Hey everyone,
I was wondering if someone could help me out with an issue I'm seeing.
I'm trying to implement a horizontal-scrolling platformer. I've followed "The frame and NMIs" and kept the game logic and NMI logic separate. I implemented the scrolling the following way:
- Every 16 times the scroll is incremented I buffer two columns of background sprites (i.e. one column of 16x16 "metatiles")
- Every 32 times the scroll is incremented I buffer one column of attribute data
When I run my implementation, the screen glitches out - it kind of "jumps" up and down every now and then. It doesn't happen when I turn the PAL emulation on, so it makes me think that I'm doing too much during NMI and it causes these issues. Pretty much every 32 times the scroll is incremented I have to write 30 + 30 + 8 = 68 bytes. I should be able to do that, right? "The frame and NMIs" mentions it should be possible to write 160 bytes?
Maybe my drawing routine is too complicated, but I've really tried to make it as simple as possible (not an expert on assembly though). Here's my code:
; Input data has the following format:
; Byte 0 = length
; Byte 1 = high byte of the PPU address
; Byte 2 = low byte of the PPU address
; Byte 3 = reserved for now
; Byte 4+ = {length} bytes
;
; Repeat until length == 0 is found.
;
; Buffer starts at $0100, drawBuffer is declared as
;
; .rsset $0100
; drawBuffer .rs 160
DoDrawing:
LDX #$00
LDA $2002 ; read PPU status to reset the high/low latch
.drawLoop:
LDY drawBuffer, x ; load the length of the data to the Y register
BEQ ResetBuffer ; length equal 0 means that the drawing is done
INX ; X = 1
LDA drawBuffer, x ; load the high byte of the target address
STA $2006 ; write the high byte to PPU
INX ; X = 2
LDA drawBuffer, x ; load the low byte of the target address
STA $2006 ; write the low byte to PPU
INX ; X = 3 (reserved for now)
.setLoop:
INX ; increment X so it points to the next byte
LDA drawBuffer, x ; load a byte of the data
STA $2007 ; write it to PPU
DEY ; decrement Y
BNE .setLoop ; if Y != 0 jump to .setLoop
INX ; increment X so it points to the next segment
JMP .drawLoop ; jump back to .drawLoop
ResetBuffer:
CPX #$00
BEQ DoDrawingDone ; there was no data buffered
LDA #$00
.resetBufferLoop:
DEX ; decrement X so it points on the previous byte
STA drawBuffer, x ; reset to 0
BNE .resetBufferLoop ; X > 0 means there's more resetting to do
DoDrawingDone:
RTS
One thing that's not that great is that when buffering a column of attributes, I have to buffer them as 8 separate segments (since the address increments by 8). So in the case when I'm buffering two columns of sprites and one column of attributes, the buffer holds 108 bytes (34 bytes for sprite column 1, 34 bytes for sprite column 2, 8*5=40 bytes for attributes). But I don't know if that matters, or how it can be resolved.
I could probably fix this by buffering less stuff at one time - I could buffer one column of sprites when "scroll == (multiple of 16)", second column of sprites a frame before that, and the attributes a frame before that - but before I make it too complicated I just wanted to make sure I'm not missing anything obvious.
Any help is appreciated!
I was wondering if someone could help me out with an issue I'm seeing.
I'm trying to implement a horizontal-scrolling platformer. I've followed "The frame and NMIs" and kept the game logic and NMI logic separate. I implemented the scrolling the following way:
- Every 16 times the scroll is incremented I buffer two columns of background sprites (i.e. one column of 16x16 "metatiles")
- Every 32 times the scroll is incremented I buffer one column of attribute data
When I run my implementation, the screen glitches out - it kind of "jumps" up and down every now and then. It doesn't happen when I turn the PAL emulation on, so it makes me think that I'm doing too much during NMI and it causes these issues. Pretty much every 32 times the scroll is incremented I have to write 30 + 30 + 8 = 68 bytes. I should be able to do that, right? "The frame and NMIs" mentions it should be possible to write 160 bytes?
Maybe my drawing routine is too complicated, but I've really tried to make it as simple as possible (not an expert on assembly though). Here's my code:
Code:
; Input data has the following format:
; Byte 0 = length
; Byte 1 = high byte of the PPU address
; Byte 2 = low byte of the PPU address
; Byte 3 = reserved for now
; Byte 4+ = {length} bytes
;
; Repeat until length == 0 is found.
;
; Buffer starts at $0100, drawBuffer is declared as
;
; .rsset $0100
; drawBuffer .rs 160
DoDrawing:
LDX #$00
LDA $2002 ; read PPU status to reset the high/low latch
.drawLoop:
LDY drawBuffer, x ; load the length of the data to the Y register
BEQ ResetBuffer ; length equal 0 means that the drawing is done
INX ; X = 1
LDA drawBuffer, x ; load the high byte of the target address
STA $2006 ; write the high byte to PPU
INX ; X = 2
LDA drawBuffer, x ; load the low byte of the target address
STA $2006 ; write the low byte to PPU
INX ; X = 3 (reserved for now)
.setLoop:
INX ; increment X so it points to the next byte
LDA drawBuffer, x ; load a byte of the data
STA $2007 ; write it to PPU
DEY ; decrement Y
BNE .setLoop ; if Y != 0 jump to .setLoop
INX ; increment X so it points to the next segment
JMP .drawLoop ; jump back to .drawLoop
ResetBuffer:
CPX #$00
BEQ DoDrawingDone ; there was no data buffered
LDA #$00
.resetBufferLoop:
DEX ; decrement X so it points on the previous byte
STA drawBuffer, x ; reset to 0
BNE .resetBufferLoop ; X > 0 means there's more resetting to do
DoDrawingDone:
RTS
One thing that's not that great is that when buffering a column of attributes, I have to buffer them as 8 separate segments (since the address increments by 8). So in the case when I'm buffering two columns of sprites and one column of attributes, the buffer holds 108 bytes (34 bytes for sprite column 1, 34 bytes for sprite column 2, 8*5=40 bytes for attributes). But I don't know if that matters, or how it can be resolved.
I could probably fix this by buffering less stuff at one time - I could buffer one column of sprites when "scroll == (multiple of 16)", second column of sprites a frame before that, and the attributes a frame before that - but before I make it too complicated I just wanted to make sure I'm not missing anything obvious.
Any help is appreciated!