8x16 and whatever else unreg wants to know
Moderator: Moderators
-
- Posts: 1318
- Joined: Thu Apr 23, 2009 11:21 pm
- Location: cypress, texas
tepples, thanks! I guess it was kind of A. I put your code in the middle of a subroutine. So it had the first part of the first subroutine missing an RTS at the end.... liketepples wrote:Most of the time, bad opcode means either A. you forgot the RTS at the end of a subroutine, B. you messed up a bankswitch, or C. you did RTS on a messed-up stack.
Code: Select all
[subroutineA...[subroutineB...rts]...rts]
-
- Posts: 1318
- Joined: Thu Apr 23, 2009 11:21 pm
- Location: cypress, texas
Code: Select all
draw_sprite:
; t0-t2 are temporary zero page locations holding
; the address of a particular sprite layout and the
; number of sprites left to draw
lda sprite_layouts_lo, x
sta t0
lda sprite_layouts_hi, x
sta t0+1
lda sprite_layouts_count, x
sta t2
ldy #0
; oamIndex is a variable tracking how far you've written
; into shadow OAM (customarily at $0200-$02FF)
ldx oamIndex
@loop:
; If you have the address of an array in a zero page pointer,
; you use the (d),y addressing mode and increase Y to go
; to the next byte.
lda (t0), y
iny
;ect. start
sta sprite+4;+oamIndex
inx
inc oamIndex
;end ect.
dec t2
bne @loop
stx oamIndex
rts
Code: Select all
sta sprite+oamIndex
-
- Posts: 1318
- Joined: Thu Apr 23, 2009 11:21 pm
- Location: cypress, texas
-
- Posts: 1318
- Joined: Thu Apr 23, 2009 11:21 pm
- Location: cypress, texas
I think I've found the problem... put tepples' code inside the code that runs before the .
Inside the NES 101 Tutorial the only code that runs after cli is the endless loop transfering control to the vblank interrupt, i think. Where do you think tepples' code should go? I'm asking beccause my skill with rearanging others' code is very poor; guess i could keep try ing though.
Code: Select all
cli
Inside the NES 101 Tutorial the only code that runs after cli is the endless loop transfering control to the vblank interrupt, i think. Where do you think tepples' code should go? I'm asking beccause my skill with rearanging others' code is very poor; guess i could keep try ing though.
-
- Posts: 1318
- Joined: Thu Apr 23, 2009 11:21 pm
- Location: cypress, texas
Yes, thank you; I agree.Jaffe wrote:The code tepples posted is a subroutine.
Well, i think i ment both... My first problem was that I placed it in the middle of some other code. I've moved theJaffe wrote:You can place it wherever you want (of course not in the middle of some other code). Was this what you meant, or do you mean where to place the actual calls (jsr) to the routine?
Code: Select all
; Background data
bg:
.incbin "charSelect1.nam"
; Attribute table
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0
.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
;sprite data
hero_frame1:
.db aY, $80, $00, aX, aY, $81, $00, aX+8
.db aY+8, $90, $00, aX, aY+8, $91, $00, aX+8
.db aY+16, $a0, $00, aX, aY+16, $a1, $00, aX+8
.db aY+24, $b0, $00, aX, aY+24, $b1, $00, aX+8
hero_frame2:
.db aY, $80, $00, aX, aY, $81, $00, aX+8
.db aY+8, $90, $00, aX, aY+8, $91, $00, aX+8
.db aY+16, $a0, $00, aX, aY+16, $a1, $00, aX+8
.db aY+24, $b0, $00, aX, aY+24, $b1, $00, aX+8
hero_frame3:
.db aY, $80, $00, aX, aY, $81, $00, aX+8
.db aY+8, $90, $00, aX, aY+8, $91, $00, aX+8
.db aY+16, $a0, $00, aX, aY+16, $a1, $00, aX+8
.db aY+24, $b0, $00, aX, aY+24, $b1, $00, aX+8
hero_frame4:
.db aY, $80, $00, aX, aY, $81, $00, aX+8
.db aY+8, $90, $00, aX, aY+8, $91, $00, aX+8
.db aY+16, $a0, $00, aX, aY+16, $a1, $00, aX+8
.db aY+24, $b0, $00, aX, aY+24, $b1, $00, aX+8
;Operator < produces the low byte of an address
sprite_layouts_lo:
.db <hero_frame1, <hero_frame2, <hero_frame3, <hero_frame4
;Operator > produces the high byte of an address
sprite_layouts_hi:
.db >hero_frame1, >hero_frame2, >hero_frame3, >hero_frame4
;Number of hardware sprites in each layout
sprite_layouts_count:
.db 8, 8, 8, 8
After many ideas and hours of reading I have decided to place the jsr in my vblank code because vblank is the period where you write to the sprite memory, right?Jaffe wrote:...or do you mean where to place the actual calls (jsr) to the routine?
Thank you Jaffe for helping me!
This code has not worked for me, yet... now, it draws the first sprite in the correct spot on the screen; but only the first sprite. Back to my assembly book.
3
edit: forgot to post my code
Code: Select all
draw_sprite:
...
ldx oamIndex
@loop:
; If you have the address of an array in a zero page pointer,
; you use the (d),y addressing mode and increase Y to go
; to the next byte.
lda (t0), y
iny
;ect. start
sta sprite, x
inx
;end ect. THIS IS THE END OF MY CODE.
dec t2
bne @loop
stx oamIndex
rts
I have a bit of a hard time getting an overview of your code. Could you post it in its entirety?
The subroutine should not be called during vblank time, at least not before all PPU updates are done. You'll want the subroutine to store sprite data in a page of RAM, and let the contents of this page be copied into OAM by a DMA transfer during vblank (from reading this thread it seems you did this before?) That way you can use all of the non-vblank time to do complex calculations etc. to compute what objects are to be shown in the next frame, where they should be placed, etc. Then when vblank comes, you'll have the sprite data ready to be copied into OAM.
I assume your game has some kind of a main loop that just runs over and over? This is usually where you'd like to place your call to the routine, but that requires that you have some way of controlling how often the loop runs. You could also place it at the end of your vblank code, after you're sure that all code accessing the PPU has run. If you just want to show one object now as a test, you can just as well call it once before your code enters the main infinite loop.
The subroutine should not be called during vblank time, at least not before all PPU updates are done. You'll want the subroutine to store sprite data in a page of RAM, and let the contents of this page be copied into OAM by a DMA transfer during vblank (from reading this thread it seems you did this before?) That way you can use all of the non-vblank time to do complex calculations etc. to compute what objects are to be shown in the next frame, where they should be placed, etc. Then when vblank comes, you'll have the sprite data ready to be copied into OAM.
I assume your game has some kind of a main loop that just runs over and over? This is usually where you'd like to place your call to the routine, but that requires that you have some way of controlling how often the loop runs. You could also place it at the end of your vblank code, after you're sure that all code accessing the PPU has run. If you just want to show one object now as a test, you can just as well call it once before your code enters the main infinite loop.
-
- Posts: 1318
- Joined: Thu Apr 23, 2009 11:21 pm
- Location: cypress, texas
It's not really my code yet; it is really the code of Michael Martin in the NES 101 Tutorial and the code of tepples in his post on page 4, i think, of this thread.Jaffe wrote:I have a bit of a hard time getting an overview of your code. Could you post it in its entirety?
Code: Select all
; Transfer control to the VBLANK routines.
loop: jmp loop
;80 81
;90 91
;a0 a1
;b0 b1
; .db aY, $80, $00, aX, aY, $81, $00, aX+8,
; aY+8, $90, $00, aX, aY+8, $91, $00, aX+8,
; aY+16, $a0, $00, aX, aY+16, $a1, $00, aX+8,
; aY+24, $b0, $00, aX, aY+24, $b1, $00, aX+8
draw_sprite:
; t0-t2 are temporary zero page locations holding
; the address of a particular sprite layout and the
; number of sprites left to draw
lda sprite_layouts_lo, x
sta t0
lda sprite_layouts_hi, x
sta t0+1
lda sprite_layouts_count, x
sta t2
ldy #0
; oamIndex is a variable tracking how far you've written
; into shadow OAM (customarily at $0200-$02FF)
ldx oamIndex
@loop:
; If you have the address of an array in a zero page pointer,
; you use the (d),y addressing mode and increase Y to go
; to the next byte.
lda (t0), y
iny
;ect. start
sta sprite, x
inx
;end ect.
dec t2
bne @loop
stx oamIndex
rts
Yes, I remember doing that from before in this thread. Right now, my file for vblank looks like:
Code: Select all
jsr scroll_screen
jsr update_sprite
jsr react_to_input
rti
scroll_screen:
ldx #$00 ; Reset VRAM
stx $2006
stx $2006
ldx scroll ; Do we need to scroll at all?
beq no_scroll
dex
stx scroll
lda #$00
sta $2005 ; Write 0 for Horiz. Scroll value
stx $2005 ; Write the value of 'scroll' for Vert. Scroll value
no_scroll:
rts
update_sprite:
ldx #3
jsr draw_sprite
lda #>sprite
sta $4014 ;OAM_DMA register ; Jam page $200-$2FF into SPR-RAM
;takes 513 cycles.
react_to_input:
lda #$01 ; strobe joypad
sta $4016
lda #$00
sta $4016
lda $4016 ; Is the A button down?
and #1
beq @b
jsr low_c
@b: lda $4016 ;Is the B button down?
and #1
beq not_dn
jsr high_c
not_dn: rts ; Ignore left and right, we don't use 'em
low_c:
pha
lda #$84
sta $4000
lda #$AA
sta $4002
lda #$09
sta $4003
pla
rts
high_c:
pha
lda #$86
sta $4000 ;audio
lda #$69
sta $4002
lda #$08
sta $4003
pla
rts
So the first thing in vblank should be the copying of page 2 to OAM? And then is it right to do all the updating of page 2 after vblank ends? At vblank we are suspossed to make our changes known to OAM first and writes to page 2 are for between Vblank times. That's what im learning right now, thank you.Jaffe wrote: That way you can use all of the non-vblank time to do complex calculations etc. to compute what objects are to be shown in the next frame, where they should be placed, etc. Then when vblank comes, you'll have the sprite data ready to be copied into OAM.
What is VRAM?tokumaru wrote:The start of VBlank is an important event, so it makes sense to interrupt whatever the PPU is doing so that the full duration of VBlank can be used for VRAM updates.
Ok, thanks! I changed it, but have the poblem again... it's only drawing the first of the 8 sprite object/meta-sprite. It's in the right place though and it's the right sprite.Jaffe wrote:If you just want to show one object now as a test, you can just as well call it once before your code enters the main infinite loop.
Yes, you can update the CPU's copy of OAM outside of vblank. In fact, you're supposed to.
VRAM is any RAM mapped into PPU $0000-$2FFF. It includes CIRAM, the memory inside the NES used for nametables, and CHR RAM, the memory inside the cartridge used for pattern tables. Some cartridges have CHR ROM instead of CHR RAM; for these, the only VRAM is CIRAM.
VRAM is any RAM mapped into PPU $0000-$2FFF. It includes CIRAM, the memory inside the NES used for nametables, and CHR RAM, the memory inside the cartridge used for pattern tables. Some cartridges have CHR ROM instead of CHR RAM; for these, the only VRAM is CIRAM.
Are you using the exact code you pasted here? You know it's incomplete, right? The sprite drawing routine is not copying all the bytes it's supposed to from the meta-sprite definitions, and it's not adding the coordinates to the object's coordinates either. That routine was provided as an example, not as something you can copy & paste.
I advise you start writing your own code, or at least read all the code you use very carefully so that you know exactly what it's doing. Making Frankenstein programs (copying blocks of code from different sources) is a terrible way to learn, because you have no idea of what is actually going on in the program and are only hoping that it will somehow work.
I advise you start writing your own code, or at least read all the code you use very carefully so that you know exactly what it's doing. Making Frankenstein programs (copying blocks of code from different sources) is a terrible way to learn, because you have no idea of what is actually going on in the program and are only hoping that it will somehow work.
-
- Posts: 1318
- Joined: Thu Apr 23, 2009 11:21 pm
- Location: cypress, texas
Thank you. Would testing the input be ok for during vblank? No, testing the input hasn't anything to do with VRAM... right?tepples wrote:Yes, you can update the CPU's copy of OAM outside of vblank. In fact, you're supposed to.
Thanks!tepples wrote:VRAM is any RAM mapped into PPU $0000-$2FFF. It includes CIRAM, the memory inside the NES used for nametables, and CHR RAM, the memory inside the cartridge used for pattern tables. Some cartridges have CHR ROM instead of CHR RAM; for these, the only VRAM is CIRAM.
Yes ok, you are right on all points. Thank you for your help. God really helped me with it and so it kind of works now! The code... it's able to print out the entire meta-sprite now using tepples' code and my code! Now i'm trying to figure out how to move it with the control pad... and I think maybe callingtokumaru wrote:Are you using the exact code you pasted here? You know it's incomplete, right? The sprite drawing routine is not copying all the bytes it's supposed to from the meta-sprite definitions, and it's not adding the coordinates to the object's coordinates either. That routine was provided as an example, not as something you can copy & paste.
I advise you start writing your own code, or at least read all the code you use very carefully so that you know exactly what it's doing. Making Frankenstein programs (copying blocks of code from different sources) is a terrible way to learn, because you have no idea of what is actually going on in the program and are only hoping that it will somehow work.
Code: Select all
ldx #3
jsr draw_sprite
Here's code that appplies to this question.
Code: Select all
lda #%00011110
sta $2001
;matthew's init
cli
;----------------------------END OF RESET----------------------------------
; Transfer control to the VBLANK routines.
ldx #3
jsr draw_sprite
loop: jmp loop
;80 81
;90 91
;a0 a1
;b0 b1
; .db aY, $80, $00, aX, aY, $81, $00, aX+8,
; aY+8, $90, $00, aX, aY+8, $91, $00, aX+8,
; aY+16, $a0, $00, aX, aY+16, $a1, $00, aX+8,
; aY+24, $b0, $00, aX, aY+24, $b1, $00, aX+8
draw_sprite:
; t0-t2 are temporary zero page locations holding
; the address of a particular sprite layout and the
Is that the only place I can put it? (Because the rest of the file is subroutines and then data.) I'm missing knowlege about a way to organize my code with files. I remember reading some post by tokumaru that's about organizing code with different files... will search for that tomorrow. Good night.
It depends. There are 3 common ways to organize your frame logic in NES programs in order to make sure it will run 60 times per second (as it should):unregistered wrote:Would jsr draw_sprite be ok to include inside of the loop: jmp loop?
1. Put the game logic after VRAM-related operations in your NMI. The NMI routine is automatically called at the start of VBlank, and once the VRAM updates are finished, you are free to do other kinds of operations. Since the NES runs at 60fps, there are 60 VBlanks in a second, so your game logic will also run at 60 Hz. Many commercial games do this, including SMB and Final Fantasy. There are some problems with this method though, which make lag frames (these happen when the frame logic takes longer than a frame to complete) difficult to manage. SMB suffers from this when there are too many enemies on screen (apparently the status bar "jumps" and the music slows down). Most tutorials appear to use this.
Code: Select all
Loop:
jmp Loop
NMI:
;DO PPU UPDATES HERE;
;RUN THE GAME LOGIC HERE;
rti
Code: Select all
Loop:
;RUN THE GAME LOGIC HERE;
;WAIT FOR THE NMI;
;DO PPU UPDATES HERE;
jmp Loop
NMI:
;INDICATE THE START OF VBLANK;
rti
Code: Select all
Loop:
;RUN THE GAME LOGIC HERE;
;WAIT FOR THE PPU UPDATES TO COMPLETE;
jmp Loop
NMI:
;DO PPU UPDATES HERE;
rti
How you organize your files doesn't make any difference in the program... Separating files and using includes is something that's supposed to help you keep track of what's where and make your code more easily maintainable, but in the end the assemble still sees all the code as if it were in a single huge file. What matters for NES programs is the order in which things are processed, which doesn't necessarily match the order in which they are written.I remember reading some post by tokumaru that's about organizing code with different files...
Don't Squaresoft games such as Final Fantasy do it this way?tokumaru wrote:2. Use the NMI just for signaling the start of VBlank, and both the frame logic and the VRAM-updating in the main thread (the "loop: jmp loop" part). [...] AFAIK, no commercial games did it like this.
I'll have to see if Nintendulator and NESICIDE can measure /NMI-to-RTI time.