Hello, I have a question about the pallet and the screen. Every 6 frames I update the pallete, the scroll value gets affected temporally. How can you fix this?
HERE'S THE CODE:Code:
NMI:
INC scroll
; add one to our scroll variable each frame
NTSwapCheck:
LDA scroll ; check if the scroll just wrapped from 255 to 0
BNE NTSwapCheckP2
NTSwap:
LDA nametable ; load current nametable number (0 or 1)
EOR #$01 ; exclusive OR of bit 0 will flip that bit
STA nametable ; so if nametable was 0, now 1
; if nametable was 1, now 0
NTSwapCheckP2:
NTSwapCheckP2:
LDA scrolly ; check if the scroll just wrapped from 255 to 0
BNE NTSwapCheckP3
NTSwap2:
LDA nametabley ; load current nametable number (0 or 1)
EOR #$01 ; exclusive OR of bit 0 will flip that bit
STA nametabley ; so if nametable was 0, now 1
NTSwapCheckP3:
NewAttribCheck:
LDA scroll
AND #%00011111 ; check for multiple of 32
BNE NewAttribCheckDone ; if low 5 bits = 0, time to write new attribute bytes
jsr DrawNewAttributes
NewAttribCheckDone:
NewColumnCheck:
LDA scroll
AND #%00000111 ; throw away higher bits to check for multiple of 8
BNE NewColumnCheckDone ; done if lower bits != 0
JSR DrawNewColumn ; if lower bits = 0, time for new column
lda columnNumber
CMP #$FE
BCC DT9
LDA #$00
STA scroll+2
DT9:
lda columnNumber
clc
adc #$01 ; go to next column
and #%11111111 ; only 256 columns of data, throw away top bit to wrap
sta columnNumber
lda scroll+2
clc
adc #$01
STA scroll+2
JSR Scrollcheck
NewColumnCheckDone:
LDA #$00
STA $2003
LDA #$02
STA $4014 ; sprite DMA from $0200
JSR LoadPalettes2
LDA #$00
STA $2006 ; clean up PPU address registers
STA $2006
LDA scroll
STA $2005 ; write the horizontal scroll count register
LDA scrolly ; no vertical scrolling
STA $2005
;;This is the PPU clean up section, so rendering the next frame starts properly.
LDA #%10010000 ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
ORA nametable ; select correct nametable for bit 0
STA $2000
LDA #%00011110 ; enable sprites, enable background, no clipping on left side
STA $2001
JSR Updating
JSR Gravity
JSR ReadController1
LDA #$00
STA T
LDA #$00
STA D
LDA #$00
STA D+1
LDA buttons
AND #%10000000 ; only look at bit 0
BEQ ReadADone ; branch to ReadADone if button is NOT pressed (0)
LDA JumpState
CMP #$01
BCS ReadADone
LDA #$01
STA JumpState
; save sprite X position
ReadADone:
LDA buttons
AND #%00000001 ; only look at bit 0
BEQ ReadRightDone ; branch to ReadADone if button is NOT pressed (0)
LDA #$01
STA D
LDA player
CLC
ADC #$01
STA player
LDA #$00
STA pdirection
LDA #$01
STA T
; save sprite X position
ReadRightDone:
LDA buttons
AND #%00000010 ; only look at bit 0
BEQ ReadLeftDone ; branch to ReadADone if button is NOT pressed (0)
LDA player
SEC
SBC #$01
STA player
LDA #$01
STA pdirection
LDA #$01
STA T
; save sprite X position
ReadLeftDone:
LDA buttons
AND #%00100000 ; only look at bit 0
BEQ ReadUpDone
LDA buttons
AND #%00010000
LDA gamestate
CLC
ADC #$01
STA gamestate
ReadUpDone:
; run normal game engine code here
; reading from controllers, etc
RTI ; return from interrupt
DrawNewColumn:
LDA scroll ; calculate new column address using scroll register
LSR A
LSR A
LSR A ; shift right 3 times = divide by 8
STA columnLow ; $00 to $1F, screen is 32 tiles wide
LDA nametable ; calculate new column address using current nametable
EOR #$01 ; invert low bit, A = $00 or $01
ASL A ; shift up, A = $00 or $02
ASL A ; $00 or $04
CLC
ADC #$20 ; add high byte of nametable base address ($2000) can change to lower name table $2000 = ADC #$20 $2800 = ADC #$28
STA columnHigh ; now address = $20 or $24 for nametable 0 or 1
LDA columnNumber ; column number * 32 = column data offset
ASL A
ASL A
ASL A
ASL A
ASL A
STA sourceLow
LDA columnNumber
LSR A
LSR A
LSR A
STA sourceHigh
JSR levelselect
DrawColumn:
LDA #%00000100 ; set to increment +32 mode
STA $2000
LDA $2002 ; read PPU status to reset the high/low latch
LDA columnHigh
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006 ; write the low byte of column address
LDX #$1E ; copy 30 bytes
LDY #$1E
DrawColumnLoop:
LDA [sourceLow], y
STA $2007
INY
DEX
BNE DrawColumnLoop
DrawNewColumn2:
LDA scroll ; calculate new column address using scroll register
LSR A
LSR A
LSR A ; shift right 3 times = divide by 8
STA columnLow2 ; $00 to $1F, screen is 32 tiles wide
LDA nametable ; calculate new column address using current nametable
EOR #$01 ; invert low bit, A = $00 or $01
ASL A ; shift up, A = $00 or $02
ASL A ; $00 or $04
CLC
ADC #$28 ; add high byte of nametable base address ($2000) can change to lower name table $2000 = ADC #$20 $2800 = ADC #$28 changes what loads the name table in the bottem name table in
STA columnHigh ; now address = $20 or $24 for nametable 0 or 1
LDA columnNumber2 ; column number * 32 = column data offset
ASL A
ASL A
ASL A
ASL A
ASL A
STA sourceLow2
LDA columnNumber
LSR A
LSR A
LSR A
STA sourceHigh2
LDA sourceLow ; column data start + offset = address to load column data from
CLC
ADC #$04
STA sourceLow2
LDA sourceHigh
ADC #$04
STA sourceHigh2
DrawColumn2:
LDA #%00000100 ; set to increment +32 mode
STA $2000
LDA $2002 ; read PPU status to reset the high/low latch
LDA columnHigh
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006 ; write the low byte of column address
LDX #$1E ; copy 30 bytes
LDY #$1E
DrawColumnLoop2:
LDA [sourceLow2], y
STA $2007
INY
DEX
BNE DrawColumnLoop2
RTS
DrawNewAttributes:
LDA nametable
EOR #$01 ; invert low bit, A = $00 or $01
ASL A ; shift up, A = $00 or $02
ASL A ; $00 or $04
CLC
ADC #$23 ; add high byte of attribute base address ($23C0)
STA columnHigh ; now address = $23 or $27 for nametable 0 or 1
LDA scroll
LSR A
LSR A
LSR A
LSR A
LSR A
CLC
ADC #$C0
STA columnLow ; attribute base + scroll / 32
LDA columnNumber ; (column number / 4) * 8 = column data offset
AND #%11111100
ASL A
STA sourceLow
LDA columnNumber
LSR A
LSR A
LSR A
LSR A
LSR A
LSR A
LSR A
STA sourceHigh
LDA sourceLow ; column data start + offset = address to load column data from
CLC
ADC #LOW(attribData)
STA sourceLow
LDA sourceHigh
ADC #HIGH(attribData)
STA sourceHigh
LDY #$00
LDA $2002 ; read PPU status to reset the high/low latch
DrawNewAttributesLoop
LDA columnHigh
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006 ; write the low byte of column address
LDA [sourceLow], y ; copy new attribute byte
STA $2007
INY
CPY #$08 ; copy 8 attribute bytes
BEQ DrawNewAttributesLoopDone
LDA columnLow ; next attribute byte is at address + 8
CLC
ADC #$08
STA columnLow
JMP DrawNewAttributesLoop
DrawNewAttributesLoopDone:
RTS
DrawNewAttributes2:
LDA nametable
EOR #$01 ; invert low bit, A = $00 or $01
ASL A ; shift up, A = $00 or $02
ASL A ; $00 or $04
CLC
ADC #$23 ; add high byte of attribute base address ($23C0)
STA columnHigh ; now address = $23 or $27 for nametable 0 or 1
LDA scroll
LSR A
LSR A
LSR A
LSR A
LSR A
CLC
ADC #$C0
STA columnLow ; attribute base + scroll / 32
LDA columnNumber ; (column number / 4) * 8 = column data offset
AND #%11111100
ASL A
STA sourceLow
LDA columnNumber
LSR A
LSR A
LSR A
LSR A
LSR A
LSR A
LSR A
STA sourceHigh
LDA sourceLow ; column data start + offset = address to load column data from
CLC
ADC #$04
STA sourceLow2
LDA sourceHigh
ADC #$04
STA sourceHigh2
LDY #$00
LDA $2002 ; read PPU status to reset the high/low latch
DrawNewAttributesLoop2
LDA columnHigh2
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006 ; write the low byte of column address
LDA [sourceLow2], y ; copy new attribute byte
STA $2007
INY
CPY #$08 ; copy 8 attribute bytes
BEQ DrawNewAttributesLoopDone2
LDA columnLow2 ; next attribute byte is at address + 8
CLC
ADC #$08
STA columnLow2
JMP DrawNewAttributesLoop2
DrawNewAttributesLoopDone2:
rts
; skip the update function
LoadPalettes2:
LDA #$00
STA Timer1+5
LDA #$00
STA Timer1+6
LDX #$00
LP2:
LDA #$3F
STA $2006
LDA Timer1+6
STA $2006
LDA paletteswap , x
STA $2007
INX
INC Timer1+5
INC Timer1+6
LDA Timer1+5
CMP #$20
BEQ Fin
JMP LP2
Fin:
RTS
I have also read this:
https://wiki.nesdev.com/w/index.php/The_frame_and_NMIsAnd I don't Understand what he mean to do for lda needppureg
Please first explain what the problem is before showing the code.
Thanks.
The PPU is weird. The PPU Address ($2006) shares bits with the scroll ($2005). Writing to one affects the other.
After writing to the PPU, you must set a nametable(write to $2000), and set a scroll position (2 writes to $2005)
Edit. I think you are doing what i said.
Probably a timing issue. Writing outside V-blank.
Not sure if it's the culprit, but I see that you have "PPU cleanup" twice and one of them comes after your scroll updates.
I see some other bad things as well. You have logic at the beginning of your NMI handler, logic is better to have in the main loop, or after all graphic updates (OAM, VRAM and certain register updates) in the NMI, else it will steal vblank time. The NMI handler first needs to backup registers A, X and Y to stack (unless you are doing the all-in-NMI approach) then immediately do graphic updates. The order of the graphic updates is ideally something like this:
1) Check your render flag and skip all graphic updates if clear (read bellow)
2) Read $2002 to reset high/low latch
3) Sprite updates (OAM DMA)
4) Background updates
5) Palette updates
6) $2000 and $2001 updates
7) Scroll updates ($2005)
4, 5 and 6 can probably come in any order, but OAM DMA should be done early for PAL compatibility and Scroll should be updated last because PPU register writes affects the scroll register.
After all the graphic updates, you can have things that don't have to be in vblank, like sound updates or controller updates, though I heard controller updates are better to have in your main loop to avoid possible inconsistencies. Sound is best to be in NMI though since it ensures mostly constant tempo timing.
About the needppureg flag, he uses it whenever he has made changes to $2000 or $2001 in their buffers so that the NMI only updates them when the flag is set. I don't think it's really necessary to make them conditional separately. You should make the whole graphic update block conditional though. Have a render flag that is set at the end of each main loop and cleared at the end of each NMI. If the flag is not set, jump past all the graphic updates (but not sound and everything after that). That way there is no risk for drawing partly updated frames when the game lags.
What logic is at the main loop?
Everything in your NMI before the sprite DMA looks like logic to me (it does not write to VRAM, OAM or PPU registers, so it's not graphic updates). It should be in your main loop.
Is the main loop in your NMI? In that case it should come after the graphic updates, because the first part of the NMI always happens during the vblank interval, which is limited. The article you linked to explains this too.
Definitely writing to PPU outside V-blank. I dropped your code into a blank template, and put some breakpoints on when the final scroll setting is done. About every 8 frames, you are clear down to scanline 7, occasionally more, by the time you set the scroll for the screen. [I had to make some assumptions, since you didn't provide the entire source code, I might be off by a few lines]
Eliminating the line...
jsr LoadPalettes2 (you are updating the entire palette, every frame)
-I don't know why you said "Every 6 frames". It happens every frame.
...improves this quite a bit, but still hitting scanline 1 occasionally, which would shift the entire screen by 1 pixel. Jittery. If you tightened your code a bit (made loops slightly more efficient). You could probably avoid the scroll shift entirely.
A more ideal solution, would be to do most of the calculations before hand (outside of NMI). Load all updates into a buffer (also outside of NMI), and make a much more efficient system of writing to the PPU. Then you could put the palette updates back in.
EDIT: also...
[deleted some code advice, I didn't like my wording]
You need to be careful about which direction PPU writes are going. You specifically set it to +32 in the DrawColumn / DrawColumn2 subroutines, but that comes after the attribute table subroutine, and it's still set to +1 mode from the end of the NMI write to $2000 [I guess that won't cause a problem, now that I look at it]. Further, it is still in +32 mode (sometimes) when it gets to LoadPalettes2, which needs it to be in +1 mode.
Cool. 1. Is updating the palette EVERY FRAME a bad thing and can it be done with buffering?
2 whuts buffering?
3 pla does What?
Also Would It help to give you the .nes file for reference?
Heres the new CODE:
Code:
LoadPalettes2:
LDA #%00000000 ; set to increment +32 mode
STA $2000
LDA $2002 ; read PPU status to reset the high/low latch
LDA #$00
STA Timer1+5
STA Timer1+6
LDX #$00
LP2:
LDA #$3F
STA $2006
LDA Timer1+6
STA $2006
LDA paletteswap , x
STA $2007
INX
INC Timer1+5
INC Timer1+6
LDA Timer1+5
CMP #$20
BEQ Fin
JMP LP2
Fin:
LDA #%00000100 ; set to increment +32 mode
STA $2000
RTS
NOW, to learn buffering.
1. Not a bad thing as long as it is updated in vblank, and yes buffering is the preferable way to do it. The palette isn't that big but if you are short on vblank time you could make a buffer system that only updates parts of the palette instead of the whole palette every time. You could also make a buffer system that updates nametable and palette together so that changing BG characters also changes their colours at the same time.
2. Buffering is keeping a copy of whatever you buffer (usually graphic updates) in a RAM area. You update this buffer in your game logic (main loop) and in the beginning of NMI (vblank) you just copy the contents of this buffer to VRAM/OAM etc. This way you can keep game logic and graphic updates separate. You are already buffering the OAM by using the RAM area $0200 to $02FF as an OAM buffer (or shadow OAM as they also call it). But DMA can only be used for OAM (sprite attributes), not for VRAM (nametable and palette), so you have to code a different buffer system for those. Updates to $2000 and $2001 should also be buffered in their own RAM registers so that they are also only changed in vblank.
3. PLA pulls the topmost value from the stack and puts it in the accumulator. If you have your main loop outside of NMI handler (in your RESET handler), you need to backup both the accumulator and the index registers X and Y on the stack at the start of your NMI handler and pull them out again in reverse order. PHA pushes the accumulator on to the stack but there are no instructions for pushing or pulling X or Y to or from stack, so you have to transfer X and Y into A first (using TXA/TYA when pushing and TAX/TAY after pulling):
Code:
NMI:
pha
txa
pha
tya
pha ;save A, X and Y to the stack
;(NMI code here)
pla
tay
pla
tax
pla ;restore A, X and Y from the stack
rti
This is needed because the NMI is an interrupt that could happen at any time in your main loop, and since both main and NMI uses the registers it could mess up your code. You don't need to backup the status flags however, because they are backed up automatically whenever an interrupt occurs.
The NES file would help, but the whole source code would help more.
Code:
.inesprg 1 ; 1x 16KB PRG code
.ineschr 4 ; 1x 8KB CHR data
.inesmap 4 ; mapper 0 = NROM, no bank swapping
.inesmir 2 ; background mirroring
;;;;;;;;;;;;;;;
.rsset $0000 ;;start variables at ram location 0
paletteswap .rs 32
scroll .rs 3
D .rs 2
scrolly .rs 1
gravity .rs 1
buttons .rs 1
gamestate .rs 4
progress .rs 1
buttonslay .rs 1
player .rs 2
feet .rs 1
jheight .rs 1
pdirection .rs 5
grounded .rs 1
Frame .rs 1
Fr .rs 1
Timer1 .rs 12
ANT .rs 4
T .rs 1
Srt .rs 6
Ant .rs 1
L .rs 1
BCount .rs 16
JumpState .rs 3
nametable .rs 1
nametabley .rs 1 ; ; which nametable to use, 0 or 1
columnLow .rs 1 ; low byte of new column address
columnHigh .rs 1 ; high byte of new column address
sourceLow .rs 1 ; source for column data
sourceHigh .rs 1
columnNumber .rs 1
columnNumber2 .rs 1
sourceLow2 .rs 1 ; source for column data
sourceHigh2 .rs 1
columnLow2 .rs 1 ; low byte of new column address
columnHigh2 .rs 1 ; hi
; which column of level data to
GROUND = $B1 ;
;;;;;;;;;;;;;;;
.bank 0
.org $C000
vblankwait:
BIT $2002
BPL vblankwait
RTS
RESET:
SEI ; disable IRQs
CLD ; disable decimal mode
LDX #$40
STX $4017 ; disable APU frame IRQ
LDX #$FF
TXS ; Set up stack
INX ; now X = 0
STX $2000 ; disable NMI
STX $2001 ; disable rendering
STX $4010 ; disable DMC IRQs
lda #%00000001
sta $4017 ;enable Square 1
;square 1
lda #%00000000 ;Duty 10, Length Counter Disabled, Saw Envelopes disabled, Volume F
sta $4000
lda #$C9 ;0C9 is a C# in NTSC mode
sta $4002 ;low 8 bits of period
lda #$00
sta $4003
JSR vblankwait
clrmem:
LDA #$00
STA $0000, x
STA $0100, x
STA $0200, x
STA $0400, x
STA $0500, x
STA $0600, x
STA $0700, x
LDA #$FE
STA $0300, x
INX
BNE clrmem
JSR vblankwait
LoadPalettes:
LDA $2002 ; read PPU status to reset the high/low latch
LDA #$3F
STA $2006 ; write the high byte of $3F00 address
LDA #$00
STA $2006 ; write the low byte of $3F00 address
LDX #$00
LoadPalettesLoop:
LDA palette, x ;load palette byte
STA $2007 ;write to PPU
INX ;set index to next byte
CPX #$20
BNE LoadPalettesLoop
LoadPalettesLoop1:
LDA palette, x ;load palette byte
STA paletteswap, x ;write to PPU
INX ;set index to next byte
CPX #$20
BNE LoadPalettesLoop1 ;if x = $20, 32 bytes copied, all done
LoadPSprites:
LDX #$00 ; start at 0
LoadSpritesLoopP:
LDA Playersprites, x ; load data from address (sprites + x)
STA $0200, x ; store into RAM address ($0200 + x)
INX ; X = X + 1
CPX #$08 ; Compare X to hex $20, decimal 32
BNE LoadSpritesLoopP ; Branch to LoadSpritesLoop if compare was Not Equal to zero
LoadESprites:
LDX #$00 ; start at 0
LoadSpritesLoopE:
LDA Esprites, x ; load data from address (sprites + x)
STA $0218, x ; store into RAM address ($0200 + x)
INX ; X = X + 1
CPX $FF ; Compare X to hex $20, decimal 32
BNE LoadSpritesLoopE ; Branch to LoadSpritesLoop if compare was Not Equal to zero
InitializeNametables:
LDA #$01
STA nametable
LDA #$00
STA scroll
STA columnNumber
InitializeNametablesLoop:
JSR DrawNewColumn
; draw bg column
LDA scroll ; go to next column
CLC
ADC #$08
STA scroll
INC columnNumber
LDA columnNumber ; repeat for first nametable
CMP #$20
BNE InitializeNametablesLoop
LDA #$00
STA nametable
LDA #$00
STA scroll
JSR DrawNewColumn ; draw first column of second nametable
INC columnNumber
LDA #$00 ; set back to increment +1 mode
STA $2000
InitializeNametablesDone:
InitializeAttributes:
LDA #$01
STA nametable
LDA #$00
STA scroll
STA columnNumber
InitializeAttributesLoop:
JSR DrawNewAttributes ; draw attribs
LDA scroll ; go to next column
CLC
ADC #$20
STA scroll
LDA columnNumber ; repeat for first nametable
CLC
ADC #$04
STA columnNumber
CMP #$20
BNE InitializeAttributesLoop
LDA #$00
STA nametable
LDA #$00
STA scroll
JSR DrawNewAttributes ; draw first column of second nametable
InitializeAttributesDone:
LDA #$21
STA columnNumber
LDA #$10
STA paletteswap
LDA #$00
STA JumpState
LDA #$80
STA player
LDA #$FE
STA scrolly
LDA #$80
STA player+1
LDA #$08
STA pdirection+1
LDA #$06
STA ANT
LDA #$08
STA ANT+1
LDA #$0A
STA ANT+2
LDA #$06
STA ANT+3
LDA #$06
STA Srt
LDA #$01
STA gamestate+1
LDA #%10010000 ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
STA $2000
LDA #%00011000 ; enable sprites, enable background, no clipping on left side
STA $2001
Forever:
JMP Forever ;jump back to Forever, infinite loop
NMI:
INC scroll
; add one to our scroll variable each frame
NTSwapCheck:
LDA scroll ; check if the scroll just wrapped from 255 to 0
BNE NTSwapCheckP2
NTSwap:
LDA nametable ; load current nametable number (0 or 1)
EOR #$01 ; exclusive OR of bit 0 will flip that bit
STA nametable ; so if nametable was 0, now 1
; if nametable was 1, now 0
; so if nametable was 0, now 1
NTSwapCheckP2:
LDA #$00
STA $2003
LDA #$02
STA $4014 ; sprite DMA from $0200
NewAttribCheck:
LDA scroll
AND #%00011111 ; check for multiple of 32
BNE NewAttribCheckDone ; if low 5 bits = 0, time to write new attribute bytes
jsr DrawNewAttributes
NewAttribCheckDone:
NewColumnCheck:
LDA scroll
AND #%00000111 ; throw away higher bits to check for multiple of 8
BNE NewColumnCheckDone ; done if lower bits != 0
JSR DrawNewColumn ; if lower bits = 0, time for new column
lda columnNumber
CMP #$FE
BCC DT9
LDA #$00
STA scroll+2
DT9:
lda columnNumber
clc
adc #$01 ; go to next column
and #%11111111 ; only 256 columns of data, throw away top bit to wrap
sta columnNumber
lda scroll+2
clc
adc #$01
STA scroll+2
JSR Scrollcheck
NewColumnCheckDone:
JSR LoadPalettes2
LDA #$00
STA $2006
STA $2006
;;This is the PPU clean up section, so rendering the next frame starts properly.
LDA #%10010000 ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
ORA nametable ; select correct nametable for bit 0
STA $2000
LDA #%0001100^_^_[0 ; enable sprites, enable background, no clipping on left side
STA $2001
LDA scroll
STA $2005 ; write the horizontal scroll count register
LDA scrolly ; no vertical scrolling
STA $2005
JSR Updating
JSR ReadController1
LDA #$00
STA T
LDA #$00
STA D
LDA #$00
STA D+1
LDA buttons
AND #%10000000 ; only look at bit 0
BEQ ReadADone ; branch to ReadADone if button is NOT pressed (0)
LDA JumpState
CMP #$01
BCS ReadADone
LDA #$01
STA JumpState
; save sprite X position
ReadADone:
LDA buttons
AND #%00000001 ; only look at bit 0
BEQ ReadRightDone ; branch to ReadADone if button is NOT pressed (0)
LDA #$01
STA D
LDA player
CLC
ADC #$01
STA player
LDA #$00
STA pdirection
LDA #$01
STA T
; save sprite X position
ReadRightDone:
LDA buttons
AND #%00000010 ; only look at bit 0
BEQ ReadLeftDone ; branch to ReadADone if button is NOT pressed (0)
LDA player
SEC
SBC #$01
STA player
LDA #$01
STA pdirection
LDA #$01
STA T
; save sprite X position
ReadLeftDone:
LDA buttons
AND #%00100000 ; only look at bit 0
BEQ ReadUpDone
LDA buttons
AND #%00010000
LDA gamestate
CLC
ADC #$01
STA gamestate
ReadUpDone:
; run normal game engine code here
; reading from controllers, etc
RTI ; return from interrupt
DrawNewColumn:
LDA scroll ; calculate new column address using scroll register
LSR A
LSR A
LSR A ; shift right 3 times = divide by 8
STA columnLow ; $00 to $1F, screen is 32 tiles wide
LDA nametable ; calculate new column address using current nametable
EOR #$01 ; invert low bit, A = $00 or $01
ASL A ; shift up, A = $00 or $02
ASL A ; $00 or $04
CLC
ADC #$20 ; add high byte of nametable base address ($2000) can change to lower name table $2000 = ADC #$20 $2800 = ADC #$28
STA columnHigh ; now address = $20 or $24 for nametable 0 or 1
LDA columnNumber ; column number * 32 = column data offset
ASL A
ASL A
ASL A
ASL A
ASL A
STA sourceLow
LDA columnNumber
LSR A
LSR A
LSR A
STA sourceHigh
JSR levelselect
DrawColumn:
LDA #%00000100 ; set to increment +32 mode
STA $2000
LDA $2002 ; read PPU status to reset the high/low latch
LDA columnHigh
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006 ; write the low byte of column address
LDX #$1E ; copy 30 bytes
LDY #$1E
DrawColumnLoop:
LDA [sourceLow], y
STA $2007
INY
DEX
BNE DrawColumnLoop
DrawNewColumn2:
LDA scroll ; calculate new column address using scroll register
LSR A
LSR A
LSR A ; shift right 3 times = divide by 8
STA columnLow2 ; $00 to $1F, screen is 32 tiles wide
LDA nametable ; calculate new column address using current nametable
EOR #$01 ; invert low bit, A = $00 or $01
ASL A ; shift up, A = $00 or $02
ASL A ; $00 or $04
CLC
ADC #$28 ; add high byte of nametable base address ($2000) can change to lower name table $2000 = ADC #$20 $2800 = ADC #$28 changes what loads the name table in the bottem name table in
STA columnHigh ; now address = $20 or $24 for nametable 0 or 1
LDA columnNumber2 ; column number * 32 = column data offset
ASL A
ASL A
ASL A
ASL A
ASL A
STA sourceLow2
LDA columnNumber
LSR A
LSR A
LSR A
STA sourceHigh2
LDA sourceLow ; column data start + offset = address to load column data from
CLC
ADC #$04
STA sourceLow2
LDA sourceHigh
ADC #$04
STA sourceHigh2
DrawColumn2:
LDA #%00000100 ; set to increment +32 mode
STA $2000
LDA $2002 ; read PPU status to reset the high/low latch
LDA columnHigh
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006 ; write the low byte of column address
LDX #$1E ; copy 30 bytes
LDY #$1E
DrawColumnLoop2:
LDA [sourceLow2], y
STA $2007
INY
DEX
BNE DrawColumnLoop2
RTS
DrawNewAttributes:
LDA nametable
EOR #$01 ; invert low bit, A = $00 or $01
ASL A ; shift up, A = $00 or $02
ASL A ; $00 or $04
CLC
ADC #$23 ; add high byte of attribute base address ($23C0)
STA columnHigh ; now address = $23 or $27 for nametable 0 or 1
LDA scroll
LSR A
LSR A
LSR A
LSR A
LSR A
CLC
ADC #$C0
STA columnLow ; attribute base + scroll / 32
LDA columnNumber ; (column number / 4) * 8 = column data offset
AND #%11111100
ASL A
STA sourceLow
LDA columnNumber
LSR A
LSR A
LSR A
LSR A
LSR A
LSR A
LSR A
STA sourceHigh
LDA sourceLow ; column data start + offset = address to load column data from
CLC
ADC #LOW(attribData)
STA sourceLow
LDA sourceHigh
ADC #HIGH(attribData)
STA sourceHigh
LDY #$00
LDA $2002 ; read PPU status to reset the high/low latch
DrawNewAttributesLoop
LDA columnHigh
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006 ; write the low byte of column address
LDA [sourceLow], y ; copy new attribute byte
STA $2007
INY
CPY #$08 ; copy 8 attribute bytes
BEQ DrawNewAttributesLoopDone
LDA columnLow ; next attribute byte is at address + 8
CLC
ADC #$08
STA columnLow
JMP DrawNewAttributesLoop
DrawNewAttributesLoopDone:
RTS
DrawNewAttributes2:
LDA nametable
EOR #$01 ; invert low bit, A = $00 or $01
ASL A ; shift up, A = $00 or $02
ASL A ; $00 or $04
CLC
ADC #$23 ; add high byte of attribute base address ($23C0)
STA columnHigh ; now address = $23 or $27 for nametable 0 or 1
LDA scroll
LSR A
LSR A
LSR A
LSR A
LSR A
CLC
ADC #$C0
STA columnLow ; attribute base + scroll / 32
LDA columnNumber ; (column number / 4) * 8 = column data offset
AND #%11111100
ASL A
STA sourceLow
LDA columnNumber
LSR A
LSR A
LSR A
LSR A
LSR A
LSR A
LSR A
STA sourceHigh
LDA sourceLow ; column data start + offset = address to load column data from
CLC
ADC #$04
STA sourceLow2
LDA sourceHigh
ADC #$04
STA sourceHigh2
LDY #$00
LDA $2002 ; read PPU status to reset the high/low latch
DrawNewAttributesLoop2
LDA columnHigh2
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006 ; write the low byte of column address
LDA [sourceLow2], y ; copy new attribute byte
STA $2007
INY
CPY #$08 ; copy 8 attribute bytes
BEQ DrawNewAttributesLoopDone2
LDA columnLow2 ; next attribute byte is at address + 8
CLC
ADC #$08
STA columnLow2
JMP DrawNewAttributesLoop2
DrawNewAttributesLoopDone2:
rts
levelselect:
LDA gamestate+1
CMP #$00
BEQ l00
LDA sourceLow ; column data start + offset = address to load column data from
CLC
ADC #LOW(columnDatay)
STA sourceLow
LDA sourceHigh
ADC #HIGH(columnDatay)
STA sourceHigh
RTS
l00:
LDA sourceLow ; column data start + offset = address to load column data from
CLC
ADC #LOW(columnData)
STA sourceLow
LDA sourceHigh
ADC #HIGH(columnData)
STA sourceHigh
RTS
Scrollcheck:
LDA columnNumber
CMP #$00
BCC Neve
Sc:
LDA scroll+2
CMP #$20
BEQ Endt
RTS
Neve:
LDA #$FF
STA columnNumber
Endt:
LDA columnNumber
CLC
ADC #$20
STA columnNumber
LDA #$00
STA scroll+2
RTS
RTS
;;;;;;;;;;;;;;
JMP BulletM
BulletM:
CLC
ADC #$02
RTS
Updating:
LDA Frame
CMP #$3C
BCS AniFrame
LDA #$00
STA Frame
LDA Timer1+3
CMP #$00
BEQ No
LDA #$00
STA Timer1+3
JMP FS2
No:
LDA #$01
STA Timer1+3
FS2:
LDA Timer1+11
CMP #$03
BEQ No2
LDA #$01
STA Timer1+10
JMP FSl
No2:
LDA #$01
STA Timer1+3
JMP FSl
FSl:
LDA paletteswap+17
CLC
ADC #$01
STA paletteswap+17
LDA paletteswap
CMP #$1D
BCS Flu
JMP Fly
Flu:
LDA #$10
STA paletteswap
Fly:
LDA JumpState
CMP #$02
BCS AniFrame
AniFrame:
LDA Srt
STA $0211
CLC
ADC #$01
STA $0215
LDA Srt+1
STA $0209
LDA Srt+2
STA $020D
LDA JumpState
STA $0219
LDA JumpState+1
STA $021B
LDA Timer1+1
STA $021A
Vertical:
LDA player+1
STA $0200
LDA player+1
STA $0204
LDA player+1
CLC
ADC #$08 ; A = A - 1
STA $0208
LDA player+1
CLC
ADC #$08 ; ; A = A - 1
STA $020C
LDA player+1
CLC
ADC #$0F ; A = A - 1
STA $0210
LDA player+1
CLC
ADC #$0F ; ; A = A - 1
STA $0214
Direction:
LDA pdirection
CMP #$01
BCS Right
LDA #$00
STA pdirection+1
LDA #$00
STA pdirection+3
LDA #$08
STA pdirection+2
BCC DDIR
Right:
LDA #$08
STA pdirection+1
LDA #$40
STA pdirection+3
LDA #$00
STA pdirection+2
DDIR:
LDA pdirection+3
STA $0202
STA $0206
STA $020A
STA $020E
STA $0212
STA $0216
LDA player
CLC
ADC #$04
STA $0203
LDA player
CLC
ADC pdirection+1 ; A = A - 1
STA $020B
LDA player
CLC
ADC pdirection+2 ; ; A = A - 1
STA $020F
LDA player
CLC
ADC pdirection+1 ; A = A - 1
STA $0213
LDA player
CLC
ADC pdirection+2 ; ; A = A - 1
STA $0217
Gravity:
LDA #GROUND
CLC
ADC #$01
STA feet
LDA player+1
CLC
ADC #$08
STA grounded
LDA grounded
CMP #GROUND
BEQ Fl
JMP NP1
AnimationP3:
JMP AnimationP1
Fl:
LDA JumpState
CMP #$01
BCC AnimationP3
LDA JumpState
CMP #$01
BEQ Jump
LDA JumpState
CMP #$01
BEQ Jump
LDA JumpState
CMP #$02
BEQ Delay
LDA JumpState
CMP #$03
BEQ FP1
NP1:
LDA JumpState+2
CMP #$01
BEQ Fl
LDA #$03
STA JumpState
JMP Fl
Jump:
LDA #$01
STA JumpState+2
LDA #$00
STA gravity
LDA #$01
STA jheight
LDA player+1
CMP #$80
BEQ Pa2
JMP Can
Pa2:
LDA scroll+1
CMP #$FF
BEQ Can
LDA scroll+1
CMP #$00
BEQ Can
LDA scrolly
SEC
SBC jheight
STA scrolly
Can:
LDA player+1
SEC
SBC jheight
STA player+1
LDA JumpState+1
CLC
ADC #$01
STA JumpState+1
LDA JumpState+1
CMP #$18
BCC AnimationP1
LDA #$02
STA JumpState
LDA #$00
STA JumpState+1
LDA #$00
STA JumpState+1
JMP AnimationP1
FDP1:
JMP FallDone
AnimationP1:
JMP Animation
FP1:
JMP Fall
Delay:
LDA #$00
STA gravity
LDA player+1
SEC
SBC jheight
STA player+1
LDA #$01
STA jheight
LDA JumpState+1
CLC
ADC #$01
STA JumpState+1
LDA JumpState+1
CMP #$1E
BCS P2
LDA Timer1+3
STA jheight
JMP AnimationP1
P2:
LDA JumpState+1
CMP #$25
BCS P3
LDA #$00
STA jheight
JMP AnimationP1
AnimationP2:
BCC AnimationP1
P3:
LDA #$01
STA gravity
LDA #$01
STA gravity
LDA #$00
STA jheight
LDA JumpState+1
CMP #$2A
BCS P4
LDA #$01
STA gravity
LDA #$01
STA gravity
JMP AnimationP1
P4:
LDA #$00
STA JumpState+1
LDA #$03
STA JumpState
JMP AnimationP1
Fall:
LDA grounded
CMP feet
BCS FDP1
LDA #$00
STA jheight
LDA #$01
STA gravity
LDA player+1
CLC
ADC gravity
STA player+1
LDA JumpState+1
CMP #$15
BCS MV
LDA JumpState+1
CLC
ADC #$01
STA JumpState+1
BCC AnimationP2
MV:
LDA #$02
STA gravity
JMP AnimationP2
FallDone:
LDA #$01
STA JumpState+2
LDA #$00
STA gravity
LDA #$00
STA JumpState
LDA #$00
STA JumpState+1
BCC AnimationP2
FireJump:
Animation:
LDA JumpState
CMP #$01
BEQ JAniP
LDA JumpState
CMP #$02
BEQ DAniP
LDA JumpState
CMP #$03
BEQ FAniP
LDA T
CMP #$01
BCC Rss
LDA Timer1
CMP #$0F
BCS ResP2
LDA Timer1
CLC
ADC #$01
STA Timer1
BCC SAni
ResP1:
BCC Res
ResP2:
BCS Res
Rss:
LDA #$00
STA Ant
LDA #$00
STA Timer1
LDA #$06
STA Srt
LDA #$02
STA Srt+1
LDA #$03
STA Srt+2
BCC DoneP1
Res:
LDA #$00
STA Timer1
LDA Ant
CLC
ADC #$01
STA Ant
BCC DoneP1
Rse:
LDA #$00
STA Ant
LDA #$00
STA Timer1
BCC DoneP1
JAniP:
JMP JAni
RTS
FAniP:
JMP FAni
RTS
DAniP:
JMP DAni
RTS
SAni:
LDA JumpState
CMP #$01
BEQ JAni
FrameA1:
LDA Ant
CMP #$01
BEQ FA2
LDA #$0C
STA Srt
LDA #$04
STA Srt+1
LDA #$10
STA Srt+2
BCC DoneP1
FA2:
LDA Ant
CMP #$02
BEQ FA3
LDA #$0A
STA Srt
LDA #$02
STA Srt+1
LDA #$03
STA Srt+2
BCC DoneP1
FA3:
LDA Ant
CMP #$03
BEQ FA4
LDA #$0C
STA Srt
LDA #$0E
STA Srt+1
LDA #$0F
STA Srt+2
BCC DoneP1
RsP1:
BCC Done
DoneP1:
BCC Done
FA4:
LDA Ant
CMP #$04
BEQ Rse
LDA #$0A
STA Srt
LDA #$02
STA Srt+1
LDA #$03
STA Srt+2
BCC DoneP1
LDA Ant
CMP #$03
BCS RsP1
JAni:
LDA #$00
STA Ant
LDA #$00
STA Timer1
LDA #$08
STA Srt
LDA #$04
STA Srt+1
LDA #$03
STA Srt+2
JMP Done
DAni:
LDA #$00
STA Ant
LDA #$00
STA Timer1
LDA #$25
STA Srt
LDA #$23
STA Srt+1
LDA #$03
STA Srt+2
JMP Done
FAni:
LDA #$00
STA Ant
LDA #$00
STA Timer1
LDA #$0C
STA Srt
LDA #$24
STA Srt+1
LDA #$03
STA Srt+2
JMP Done
Done:
ReadController1:
LDA #$01
STA $4016
LDA #$00
STA $4016
LDX #$08
ReadController1Loop:
LDA $4016
LSR A ; bit0 -> Carry
ROL buttons ; bit0 <- Carry
DEX
BNE ReadController1Loop
RTS
ReadController2:
LDA #$01
STA $4016
LDA #$00
STA $4016
LDX #$08
ReadController2Loop:
LDA $4017
LSR buttonslay ; bit0 -> Carry
ROL buttons ; bit0 <- Carry
DEX
BNE ReadController2Loop
RTS
RTS
LoadPalettes2:
LDA #%00000000 ; set to increment +32 mode
STA $2000
LDA $2002 ; read PPU status to reset the high/low latch
LDA #$00
STA Timer1+5
STA Timer1+6
LDX #$00
LP2:
LDA #$3F
STA $2006
LDA Timer1+6
STA $2006
LDA paletteswap , x
STA $2007
INX
INC Timer1+5
INC Timer1+6
LDA Timer1+5
CMP #$20
BEQ Fin
JMP LP2
Fin:
LDA #%00000100 ; set to increment +32 mode
STA $2000
RTS
;;;;;;;;;;;;;;
.bank 1
.org $E000
palette:
.db $02,$11,$01,$0F,$02, $04,$36,$37,$02,$39,$3A,$3B,$02,$16,$05,$0f
.db $02,$16,$20,$38,$01,$0F,$07,$37,$02,$1C,$15,$14,$02,$0F,$07,$37
palette3:
.db $02,$11,$01,$0F,$02, $04,$36,$37,$02,$39,$3A,$3B,$02,$16,$05,$0f
.db $02,$16,$20,$38,$01,$0F,$07,$37,$02,$1C,$15,$14,$02,$0F,$07,$37
Playersprites:
; Player
;vert tile attr horiz
.db $80, $00, $1C, $80 ;sprite 0
.db $88, $02, $1C, $80 ;sprite 2
.db $88, $03, $00, $88 ;sprite 3
.db $88, $03, $00, $88 ;sprite 3
.db $88, $03, $00, $88 ;sprite 3
.db $8F, $04, $00, $80 ;sprite 3
;Bullets
Bullets:
.db $8F, $1F, $04, $88
.db $8F, $1F, $04, $88
.db $8F, $1F, $04, $88
.db $8F, $FF, $04, $88
Esprites:
.db $10, $2F, $04, $88
.db $FF, $2F, $04, $FF
.db $8F, $1F, $04, $88
.db $8F, $FF, $04, $88
;sprite 3
columnData:
.incbin "KitsuneTaleslevel.bin"
columnDatay:
.incbin "KitsuneTaleslevel2.bin"
attribData:
.incbin "KitsuneTalesattrib.bin"
attribDatay:
.incbin "KitsuneTalesattrib2.bin"
.org $FFFA ;first of the three vectors starts here
.dw NMI ;when an NMI happens (once per frame if enabled) the
;processor will jump to the label NMI:
.dw RESET ;when the processor first turns on or is reset, it will jump
;to the label RESET:
.dw 0 ;external interrupt IRQ is not used in this tutorial
;;;;;;;;;;;;;;
.bank 2
.org $0000
.incbin "KitsuneTalesShiroAkai.chr"
;;;;;;;;;;;;;;;;;;;;
.bank 3
.org $A000
.incbin "Kitsunetales.chr"
;;;;;;;;;;;;;;;;;;;
.bank 4
.org $E000
What this is doing so far is that you ave a character running around with 5 8x8 sprites attached to it.
You can jump, the pallete stack $3F11 should change constantly, The nametables $20 and $28 should be both filled with a screen worth of info if a full level is loaded ala SMB3, Finally, paletteswap is the palette swapping variable.
GAME : Kitsune Tales
I see you moved out some of the logic from the beginning of the NMI, not everything though, the INC Scroll and the NTSwapCheck stuff are also logic and should be moved down after graphic updates. But now you have much more vblank time, do you still have the same problem?
I saw some other weird things. You clear RAM (at "clrmem") except page 3 that you fill with $FE:
Code:
;...
LDA #$FE
STA $0300, x
;...
You probably meant to fill page 2 with $FE to avoid stray sprites at boot since you are using that page as your OAM buffer for OAM-DMA.
And you only read bit 0 of $4016/$4017 in your controller reading routine so expansion port controllers doesn't work. You should read bit 1 as well unless it's a 4-player game.
Also I see you are doing the All-in-NMI approach. That's fine (Super Mario Bros is doing it that way) but it has its limits, and having logic in the main loop (the loop at "Forever" in your code) makes things more clear I think.
1. Yes, But listening to you des make a difference and that's nice. Wait I moved more logic around and It made a difference.
2. I Don't want the All nmi approach. I want some of the rendering to go Outside Like text, boss AI and other stuff. I heard that It boggles down speed if your not careful.
3. It's a one player game. However, If I get to demaking smash bros., I'll need that info
4. I found an article on stacks pha and pla. I just need to figure out where my buffer's going to go.
5. that was on the nerdy nights tutorial.
6. The scroll is only affected when it loads a section of the attibutes and the back ground.
7. I feel like I went too far off talking about stacks and what not. Should I continue?
If you are still having glitches, you're doing too much at once. One quick fix, would be to do the attribute table writes a frame early. It would look something like this...
NewAttribCheck:
LDA scroll
CLC ;!
ADC #1. ;!
AND #%00011111 ; check for multiple of 32
BNE NewAttribCheckDone ; if low 5 bits = 0, time to write new attribute bytes
jsr DrawNewAttributes
It may need more than just this, to make sure you write to the correct Nametable.
IMAGICA wrote:
2. I Don't want the All nmi approach. I want some of the rendering to go Outside Like text, boss AI and other stuff. I heard that It boggles down speed if your not careful.
Then just move out all your logic from the NMI and put it in your forever loop. Only keep the graphic updates (OAM, VRAM, $2000, $2001 and $2005 writes) in the NMI.
IMAGICA wrote:
3. It's a one player game. However, If I get to demaking smash bros., I'll need that info
You misunderstand me. If it's a one player game, you need to read both bit 0 and bit 1 of $4016 to your controller 1 data so that people can use both standard controllers and Famicom expansion port controllers. Nerdy Nights doesn't teach this but it's good practice to do it. Else people might not be able to use their arcade sticks and other controllers with your game.
IMAGICA wrote:
4. I found an article on stacks pha and pla. I just need to figure out where my buffer's going to go.
Your buffers goes into RAM wherever there is space. You are already buffering OAM and scroll in RAM. I like to keep my OAM buffer on RAM page 2 ($0200~$02FF), BG and palette buffers in page 3, and scroll and $2000/$2001 buffers in the zero page. The BG buffer can't be too big (there's not enough vblank time to draw the whole nametable), so you could also keep BG and palette buffers at the beginning of page 1. The stack starts in the other end of page 1 so unless you use a lot of stack there's no risk they will collide.
dougeff : Sorry, writing that slows dow the game. I need to learn buffering first
Pokun: What's bit one and how do you get it?
Bits in a binary number are traditionally numbered by their place value from right to left.
- %00000001: Ones place, 1 = 2^0, bit 0
- %00000010: Twos place, 2 = 2^1, bit 1
- %00000100: Fours place, 4 = 2^2, bit 2
- %00001000: Eights place, 8 = 2^3, bit 3
- %00010000: Sixteens place, 16 = 2^4, bit 4
- %00100000: Thirty-twos place, 32 = 2^5, bit 5
- %01000000: Sixty-fours place, 64 = 2^6, bit 6
- %10000000: One-hundred-twenty-eights place, 128 = 2^7, bit 7
Yes and if it's a one player game you read bit 0 and bit 1 of $4016 as controller 1 data so that either standard or expansion controller can be used. If it's a two player game you also read bit 0 and bit 1 of $4017 as controller 2 data. This is what (most) commercial games do.
Only if you have a three or four player game you would treat bit 0 of $4016 and $4017 as separate data (as controller 1 and 2) from bit 1 (as controller 3 and 4). In that case you would also want to support the four score.
IMAGICA wrote:
I need to learn buffering first
The Nerdy Nights sound tutorial actually has a (graphic) buffering system that is worth studying.
Can you confirm if I'm reading bit 1?
Code:
LDA #$01
STA $4016
LDA #$01
STA $4016
LDX #$08
Not there. There you are writing to the $4016 output port, which is used by the controller as a command that it's time to send its current button data to the NES. It's called a latch or strobe for the controllers, and it gets you all controllers including the expansion port controllers.
Here is where you don't read bit 1:
Code:
LDX #$08
ReadController1Loop:
LDA $4016
LSR A ; bit0 -> Carry
ROL buttons ; bit0 <- Carry
DEX
BNE ReadController1Loop
RTS
You shift bit 0 of $4016 into Carry (using LSR) and then rotate it out of Carry and into the RAM register "buttons" (using ROL). But you never touch bit 1 of $4016.
A simple way is to just add another set of LSR and ROL to get bit 1 into a temporary variable. Then you merge the two variables using OR.
Code:
LDX #$08
ReadController1Loop:
LDA $4016 ;get button data from bit 0 (con I) and bit 1 (con III)
LSR A ;shift button data from bit 0 into carry
ROL buttons ;rotate carry into a RAM register (con I buttons)
LSR A ;shift button data from bit 1 into carry
ROL temp ;rotate carry into a RAM register (con III buttons)
DEX
BNE ReadController1Loop
MergeControllers:
LDA temp
ORA buttons ;OR con III with con I to merge them
STA buttons
RTS
And if your game is using two controllers you just repeat this code but use $4017 instead to get controller II and IV, and then merge them with each other the same way.
Edit: The easiest way to test if expansion port controllers (AKA controller III and IV) are working, is to test the game in an emulator like FCEUX and enable "Famicom 4-player Adapter" or similar. Then you just enable controller III in the emulator and see if it works with your game the same as controller I.
Thanks by the way what is the advantage of 8x16 sprites are? I willing to remake my sprites if it has advantages I need.
IMAGICA wrote:
Thanks by the way what is the advantage of 8x16 sprites are? I willing to remake my sprites if it has advantages I need.
We had a recent thread on that topic:
https://forums.nesdev.com/viewtopic.php?f=2&t=16235The advantage is really just that they're bigger. You can cover more screen with them, and use all 8K of the CHR tables rather than just one 4K side.
Conversely, the disadvantage is also that they're bigger. You'll end up with more wasted space at edges, and probably more overlap problems (i.e. 8 sprites per scanline issues).
I'm Still here. I'm now fixing the vblank issue of the game.
I'm also goin to post the code for anyone to find pout the v blank issue that Pokun gave the answer to.
Pokun wrote:
IMAGICA wrote:
2. I Don't want the All nmi approach. I want some of the rendering to go Outside Like text, boss AI and other stuff. I heard that It boggles down speed if your not careful.
Then just move out all your logic from the NMI and put it in your forever loop. Only keep the graphic updates (OAM, VRAM, $2000, $2001 and $2005 writes) in the NMI.
IMAGICA wrote:
3. It's a one player game. However, If I get to demaking smash bros., I'll need that info
You misunderstand me. If it's a one player game, you need to read both bit 0 and bit 1 of $4016 to your controller 1 data so that people can use both standard controllers and Famicom expansion port controllers. Nerdy Nights doesn't teach this but it's good practice to do it. Else people might not be able to use their arcade sticks and other controllers with your game.
IMAGICA wrote:
4. I found an article on stacks pha and pla. I just need to figure out where my buffer's going to go.
Your buffers goes into RAM wherever there is space. You are already buffering OAM and scroll in RAM. I like to keep my OAM buffer on RAM page 2 ($0200~$02FF), BG and palette buffers in page 3, and scroll and $2000/$2001 buffers in the zero page. The BG buffer can't be too big (there's not enough vblank time to draw the whole nametable), so you could also keep BG and palette buffers at the beginning of page 1. The stack starts in the other end of page 1 so unless you use a lot of stack there's no risk they will collide.
The
https://wiki.nesdev.com/w/index.php/The_frame_and_NMIs explains how you can structure your code to keep graphic updates and game logic separate.
A few problems:
Code:
lda #%00000001
sta $4017 ;enable Square 1
I guess you meant $4015 here, that's where you enable/diable APU sound channels.
Code:
clrmem:
LDA #$00
STA $0000, x
STA $0100, x
STA $0200, x
STA $0400, x
STA $0500, x
STA $0600, x
STA $0700, x
LDA #$FE
STA $0300, x
INX
BNE clrmem
I guess you meant to initialize $0200 to $FF, not $0300, since you are using $0200 as OAM buffer later in your code. You should do it like this:
Code:
clrmem:
LDA #$00
STA $0000, x
STA $0100, x
STA $0300, x
STA $0400, x
STA $0500, x
STA $0600, x
STA $0700, x
LDA #$FE
STA $0200, x
INX
BNE clrmem
This makes sure all sprites are off screen at boot, hiding them.
Code:
Forever:
JMP Forever ;jump back to Forever, infinite loop
Here is your main loop, here you can put all logic (except sound which is better off in the NMI to avoid sound lag).
Mine look something like this:
Code:
main:
jsr con_read ;read controllers
jsr logic ;state machine with all game logic, like: input handlers, moving objects, collisions gravity etc
lda #$01
sta draw_flag ;allow NMI to draw, prevents incomplete buffering
nmi_wait:
lda nmi_end_flag
beq nmi_wait ;wait for NMI to finish, this limits logic to a fixed frame rate
lda #$00
sta nmi_end_flag ;clear NMI completion flag
jmp main
At the end of the main loop I wait for an NMI to finish. This makes sure there is only one NMI (graphic update) per main loop (game logic). Inside the NMI I only update graphics if the draw flag is set, otherwise I skip it. This prevents the NMI from drawing a frame if the main loop hasn't finished in time for the vblank. Also at the end of the NMI I set the NMI end flag so the main loop knows when it's time to start the next iteration.
In your NMI you first push A, X and Y to the stack so they don't mess with your logic, should an NMI happen in the middle of it. Then check for the draw flag, and skip updates if it's clear, then you fires off your OAM DMA (writing 0 to $2003 and $02 to $4014) to update sprites. After that you should do all your nametable and palette updates ($2006 and $2007) and finally scroll ($2005) and any PPU setting changes ($2000 and $2001).
After that you can do sound and anything else that you want to happen constantly at 60 Hz (or 50 Hz if PAL) without lag, but doesn't need to be in vblank (because vblank time might be up before your NMI handler has finished, that's why we put graphic updates first in the NMI).
And finally last thing before the RTI you have to pull A, X and Y from the stack again (in reverse order from that you pushed them in).
Just ask if there's something not clear enough.
Pokun wrote:
Code:
main:
jsr con_read ;read controllers
jsr logic ;state machine with all game logic, like: input handlers, moving objects, collisions gravity etc
lda #$01
sta draw_flag ;allow NMI to draw, prevents incomplete buffering
nmi_wait:
lda nmi_end_flag
beq nmi_wait ;wait for NMI to finish, this limits logic to a fixed frame rate
lda #$00
sta nmi_end_flag ;clear NMI completion flag
jmp main
how would
Code:
lda nmi_end_flag
beq nmi_wait
work in this function?
second if I were to modify the mirroring at the end, woulden't I get an error?
Heard that's only possible with mmc3.
The NMI handler interrupts the loop and sets the flag to a nonzero value.
Quote:
if I were to modify the mirroring at the end, woulden't I get an error?
Heard that's only possible with mmc3.
PPU Mirroring is hardwired into the PCB (by soldering a spot). It can't be changed except by a special mapper (such as MMC3).
Perhaps there is some other kind of mirroring you are talking about? Many of the CPU RAM addresses are mirrors of other addresses, for example.
tepples wrote:
The NMI handler interrupts the loop and sets the flag to a nonzero value.
How would that happen?
My code is stuck.
I can't just expect it to do it.
what's the "special flag" stack I need to link to it?
If you look at the wiki page on PPU registers...
http://wiki.nesdev.com/w/index.php/PPU_registersYou will see, setting bit 7 on register $2000 will tell the PPU to generate an NMI signal at the end of every frame (at the start of vertical blanking).
With NMIs on, the code will automatically jump to the NMI code (wherever the NMI vector points to), and then once it sees an RTI, will return to the main code.
The NMI code is where you should update sprites, make changes to the background, and call the music subroutine.
Or, you could just flip a NMI_flag on, and immediately RTI. In this scenario, the main code would be looking for changes in the NMI_flag, before it does those things I mentioned (sprites, BG, music)
And read the controllers, maybe.
Edit - on a side note, NMIs are not affected by SEI and CLI, hence the "non maskable" in the name.
Here's something I've learned:
Never use a NMI that is used to render 8x8 sprites to render 8x16 sprites
Also don't mess with the vblank. I am learning x and y rendering values.
... how do you push x and y?
phx and phy?
PHY and PHX don't exist.
PHY = TYA PHA
PHX = TXA PHA
If you need to preserve A, first store A to a temporary variable...
STA temp
TYA
PHA
LDA temp
dougeff wrote:
PHY and PHX don't exist.
They do exist in the 65C02 and 65816, but not in the 6502, which is the core the NES uses.
dougeff wrote:
PHY and PHX don't exist.
PHY = TYA PHA
PHX = TXA PHA
If you need to preserve A, first store A to a temporary variable...
STA temp
TYA
PHA
LDA temp
preserve A?
What's tat supposed to do?
also rendering the game with 8x16 sprites stops my game from working.
"What's that supposed to do?"
....A:80 X:40
STA temp
....A:80 X:40 temp:80
TXA
....A:40 X:40 temp:80
PHA ;A (40) pushed to stack
LDA temp
....A:80 X:40
Switching to 8x16 sprites shouldn't break your game. Perhaps you misunderstand how 8x16 sprites are organized....or made some other change.
Can you describe how it's "not working", or post a ROM?
dougeff wrote:
"What's that supposed to do?"
....A:80 X:40
STA temp
....A:80 X:40 temp:80
TXA
....A:40 X:40 temp:80
PHA ;A (40) pushed to stack
LDA temp
....A:80 X:40
Switching to 8x16 sprites shouldn't break your game. Perhaps you misunderstand how 8x16 sprites are organized....or made some other change.
Can you describe how it's "not working", or post a ROM?
Now it's not broken.
PHA and graphics are my main concerns now.
So:
PHA as I understand has to be ordered in a certain way.
In the beginning of the NMI, You're supposed to put
Code:
TXA
PHA
TYA
PHA
PHA
in any order
At the end of rendering as I know has to be Backwards of the original PHA Trio.
Code:
PHA
TYA
PHA
TXA
PHA
Now this causes a really bizarre effects on the game. It jitters up and down for a few seconds before locking in a certain position. Now, I know that my game's working. It's just not moving or registering my inputs or can't move the character at all.
Is my PHA example working? If so, Implementing the temp storage to pha will be a breeze.
2nd:
I know now that my graphics need adjustments to a 16 by 16 character. I will be adjusting it to reduce workload on the NES.
No, you need to use PLA for the end trio.
And you need to use PHA first before TXA or TYA or else you overwrite the old value of A and can't get it back.
PHA pushes the value in A to the stack.
PLA pulls the last value on the stack and puts it in A.
If you never remove things you push to the stack, your program will very likely break anytime you return (rti or rts) because rti and rts use values from the stack. That's where they get where to return to.
Code:
NMI:
;Information for RTI to work properly is the last thing on the stack.
PHA;The value of A is the last thing on the stack.
TXA;We have overwritten the value of A with the value of X
;But that's okay, because the old value of A is safe on the stack
PHA;Now what was the value of X is the last thing on the stack.
TYA;We have overwritten what was the value of X/A with the value of Y
;But that's okay, because the old value of A and the old value of X are safe on the stack.
PHA;Now what was the value of Y is the last thing on the stack.
;A, X, and Y are now all safely on the stack
;NMI Code here
PLA;The last value on the stack is removed and put into A. (The value for Y in this case)
TAY;We copy that value from A back to Y where it was before the NMI started.
;Because of that PLA now what was the value for X is the last thing on the stack.
PLA;The last value on the stack is removed and put into A. (The value for X in this case)
TAX;We copy that value from A back to X where it was before the NMI started.
;Because of that PLA now what was the value for A is the last thing on the stack.
PLA;Now the value of A is what it was before the NMI started.
;Because of that PLA now the information needed for RTI to work properly is again the last thing on the stack.
;A, X, and Y are now all safely off the stack
RTI;We can safely return now.
Alright! GOT IT TO WORRK. now to the character moving
It's called the stack because the elements in it are stacked on top of each other. Naturally if you throw a bunch of things in a stack, the last thing you threw will be on top, and when retrieving the things one by one, they will be retrieved in reverse order from the order they where thrown in.
If you always pair every push with a pull before the next RTS or RTI you will never normally have to worry about a stack overflow happening.
To reassure, EVERYTIME RTS and ARI comes in, place the PLA Trio THERE?
Quote:
To reassure, EVERYTIME RTS and [RTI] comes in, place the PLA Trio THERE?
Don't worry about standard subroutines (JSR, RTS) needing to preserve A,X,Y values.
Do worry about NMI and IRQ interrupts needing to.
NICE
now my mainproblem is tht the code in the nmi isn't working.
The reason why you need to backup the registers on the stack in interrupt handlers is that interrupts can potentially interrupt your code at any time. If it would interrupt in the middle of a calculation like:
Code:
LDA #$05
<----NMI happens here
CLC
ADC #$03 ;calculate 5+3
The 5+3 calculation will not work if the NMI clobbers A.
For normal subroutines you have full control of when they are supposed to happen so you don't need to backup the registers.
I am often "passing an argument" to a subroutine using A,X,Y registers. And subroutines often "return a value"...usually the A register (sometimes just the zero flag or the carry flag is set or reset).
For example, a subroutine that multiplies, might have the input as A and X, and return A = A * X.
So, the original value in A needs to be overwritten by the subroutine.
Me too, but if I need to preserve the registers I would just back them up on the stack or in temporary RAM registers before calling the subroutine or whatever that clobbers them, and retrieve them when I need them again.
In a nutshell it looks like this?
Code:
PHA
STA temp
LDA temp
TXA
PHA
STA temp
LDA temp
TYA
pha
LDA temp
No, it should look exactly like it was in my post.
Code:
NMI:
PHA
STA temp;You're storing a new value into temp. (A)
LDA temp;You're loading the value you just stored from A back into A. Why? The value that's there just came from A.
TXA;You are putting the value of X into A, so the above line effectively does nothing because you never used the result.
PHA
STA temp;You're storing a new value into temp. (A) This effectively makes the first store to temp above do nothing, because its value effectively wasn't used in between being written and being overwritten.
LDA temp;You're loading the value you just stored from A back into A. Why? That value is already in A.
TYA;You are putting the value of X into A, so the above line effectively does nothing.
pha
LDA temp;This line effectively does nothing
NewAttribCheck:
LDA scroll;Because of this line.
You're loading the value of temp into A, but before it is used, you load the value of scroll into A.
For understanding: The value of A doesn't change after a store, so loading from the exact place you just stored after a load usually does nothing. (Unless you're relying on flag changes, and you are not here.)
Edit: Just as another warning, you should make sure the temp RAM for your NMI isn't used anywhere else. (It's not here, but still.) For a similar reason to why you need to push A, X and Y to the stack. If the NMI interrupts code that expects the temp RAM to be a certain value, and the NMI changes the temp RAM very bad things will probably happen.
You can safely read variables in your NMI, always be careful when something is written.
Another similar piece of code:
Code:
DLs:
lda columnNumber
SEC
SBC #$01 ; go to next column
and #%11111111 ; only 256 columns of data, throw away top bit to wrap
sta columnNumber
JMP Con;This line effectively does nothing
Con:
You are jumping to Con, but the code could naturally travel there if that line wasn't there.
One of the problems is that you have several RTIs in your code. JSR Updating eventually ends up at some animation labels (FAni, etc) that jumps to done:
Code:
Done:
lda #$00
sta NMIB+2
PLA
TAY
PLA
TAX
PLA
rti
Which pulls values off the stack that weren't pushed there (that I can see), and also returns with RTI instead of RTS.
Code:
main:
JSR updating
jmp main
updating:
;code in between
rts
Anything you JSR to should end in RTS (unless you
really know what you're doing.) That is currently not true, at least for updating. One path of updating is updating->aniFrame->Vertical->Direction->DDIR->Gravity->F1->Jump->Delay->AnimationP1->Animation->scrolling->Pa2->Continue->p2->FAniP->FAni->Done->RTI
(That path may not be 100% possible since I didn't learn enough about your game state to be sure what are possible values. Still, you probably get the idea.)
You have another RTI under ReadUpDone that maybe should be an RTS, but I didn't take the time to study how your code gets there. (Or
if it gets there.) The reason it's suspicious is because it doesn't pull values from the stack, and the only interrupt start code you have
does push.
No matter how complicated the code in between updating: and the RTS is, updating still must end in RTS (unless you
really know what you're doing.) This is how you get back to the line beneath way back near the "main:" label. Anytime I create a new subroutine, I make a habit of typing its name, then the RTS underneath it, both before even adding the JSR to its name to my code. Then I write the code in between and will be safe. If I want to create another subroutine the subroutine I just created goes to, same process. label, rts, then jsr, then code under label.
I'd maybe recommend starting completely over rather than trying to band aid this code. It probably sounds scary, but I think with what you've learned you'll actually spend less time rewriting than you will chasing problems in this and you'll end up with better code as a result. I recommend writing code in smaller parts. It helps me read, but it also helps
you read.
I had to do a lot of work to find out what happened to updating. It could look something like this:
Code:
main:
jsr waitVblank
jsr updating
jmp main
waitVblank:
;Code to wait for the frame start here
rts
updating:
jsr readbuttons
jsr updateplayer
rts
readbuttons:
;code to read buttons
rts
updateplayer:
jsr updateplayerstate
jsr updateplayeranimations
rts
updateplayerstate:
rts
updateplayeranimations:
rts
Basically, the more code you have in between the start of a subroutine's label and the RTS, the more paths you have to follow to find out what it's really doing. Dividing things allows you to create code you can verify all paths of without even scrolling in your text editor. Things get way less dense when you know the problem is in the 40 line updateplayeranimations rather than somewhere in 400 line updating.
May need another Thread: Ok, Doing that in mind, and I know it's way off topic. For This game play, Should the sprites be bigger because you'll be flying via mid air dashing, shooting projectiles and wall jumping? I may need more ram than MMC3 chip for ai and other stuff. Maybe I need to understand how to compress code and to do this NMI Stuff before this challenge.
Before I fix. Is there another way to manage jumping? Look at from Jump to Animation
I wouldn't worry too much yet about using up everything the MMC3 has available. The MMC3 can handle games like Super Mario Bros 3 and Kirby's Adventure, which have lots of enemies, graphics, and moves.
IMAGICA wrote:
[color=#FF0000]
Before I fix. Is there another way to manage jumping? Look at from Jump to Animation
I think you would benefit from testing out 6502 code and stepping through it, to see how it all works.
https://skilldrick.github.io/easy6502/This is a nice online 6502 emulator. Try writing some small test code.
To answer your question, seems to be about program flow control. Right? When to JMP and when to JSR and when to branch? I'm not sure I understand your question.
EDIT - or do mean having a character jump? Like handling the animation and collision detection of a jump?
IMAGICA wrote:
May need another Thread: Ok, Doing that in mind, and I know it's way off topic. For This game play, Should the sprites be bigger because you'll be flying via mid air dashing, shooting projectiles and wall jumping? I may need more ram than MMC3 chip for ai and other stuff. Maybe I need to understand how to compress code and to do this NMI Stuff before this challenge.
Before I fix. Is there another way to manage jumping? Look at from Jump to Animation
I think you should focus on getting your core working if it isn't already. And by core I mean your main loop and NMI. If you start adding lots of extra stuff before everything works as intended it will just complicate things even further.
I doubt you will need to compress any code. Get something smaller working first, and do things one step at a time. That's how I do it anyway.
dougeff wrote:
EDIT - or do mean having a character jump? Like handling the animation and collision detection of a jump?
Character.
The basics behind platformer physics is to have horizontal and vertical speeds for each object, that you add to their coordinates every frame. When characters are stationary, both speeds are 0. Pressing left/right gradually decreases/increases the horizontal speed by the acceleration value. The vertical speed can be affected by gravity (when there's no ground below a character, increase its vertical speed by the gravity value), or by jumping. Jumping can be implemented by setting the vertical speed to a high negative number, which will cause the character to move up pretty fast, and then gravity will kick in and gradually modify the vertical speed back into a positive value, eventually causing it to fall back down.
You can comment your code to describe what it's doing. What does a 1 in JumpState+2 mean? Why are you comparing JumpState+1 to #$14? What does #$14 represent?
Is player+1 the Y position?
I don't really follow what it's doing, it needs more context.
It requires understanding of 16 bit math, but here's fixed jump height code:
Code:
;playerY is the player's Y pixel position
;playerYSub is the player's Y position in between pixels (fractional position. 1/256th of a pixel)
;playerYSpd is the player's pixel speed (pixels per frame)
;playerYSpdSub is the player's fractional pixel speed (1/256th of a pixel per frame)
;grounded: If the player is on the ground, this is non zero
GRAVITY = 8
JUMPSTRENGTH = -512
GROUNDPOSITION = 200
lda grounded;If the player is in the air, we need to
beq addgravity;add gravity
;If here, the player is grounded and can jump if they press A
lda buttons;if the player did not press A
bpl nojump;no jump happened
;If here, the player pressed A, and we should jump
lda #low(JUMPSTRENGTH);Set the player's speed
sta playerYSpdSub;to the jump strength
lda #high(JUMPSTRENGTH)
sta playerYSpd
;and make the player not grounded
lda #0
sta grounded
jmp updateyposition
addgravity:
lda #low(GRAVITY);add gravity to the player's speed
clc
adc playerYSpdSub
sta playerYSpdSub
lda #high(GRAVITY)
adc playerYSpd
sta playerYSpd
updateyposition:
;Add speed to position to update the position
lda playerYSpdSub
clc
adc playerYSub
sta playerYSub
lda playerYSpd
adc playerY
sta playerY
;If the player is not below the ground
cmp #GROUNDPOSITION
bcc jumpdone;We're done
;Otherwise, we ground them
lda #$FF
sta grounded
;set their falling speed to zero
lda #0
sta playerYSpdSub
sta playerYSpd
;Set their position to the ground
lda #GROUNDPOSITION
sta playerY
lda #0;The ground is a pixel position
sta playerYSub;so the fractional part is just zero
jumpdone:
nojump:
Here's what it looks like:
Edit: Actually that's probably not
quite what it should look like. What I hacked it into probably ran it twice per frame instead of once. Changing GRAVITY to 24 would probably get a similar enough result to the animation.
The background changed to it's 8x16 counterpart for some reason, looks freaky.
character's changed againmeaningless
reverted the pla and pha to the basicsno more STA/LDA temp for now
and now the second part of the jsr problems that Kasumi mentioned is beinfg worked on
I apologize, sincerely. Please don't take offense. This code is not good. It crashed almost immediately. Start over. Really. Blank slate. Start over. You need a stable loop before you add anything to it.
Fr .rs 1
Timer1 .rs 12
ANT .rs 4
T .rs 1
Srt .rs 6
What are these? I don't know. There's no comments, and the variable names are impossible to figure out.
NMI:
PHA
STA temp
TXA
PHA
TYA
pha
Why are you STA temp here? There's no other reference to temp.
...
RTS ?
no. The NMI routine ends in RTI. AFTER you do this stuff...
PLA
TAY
PLA
TAX
PLA
RTI
...
jsr Updating
but you have this subroutine end in...
PLA
TAY
PLA
TAX
PLA
rti
Where it should end in
RTS
This is almost certainly where it is crashing. Here or after the first NMI tries to RTS and it finds X and Y values rather than a return address.
Removed Updating until scrollCheck
there is only RTS in between
edit
Code:
Forever:
INC NMIB+2
lda #$01
sta NMIB ;allow NMI to draw, prevents incomplete buffering
nmi_wait:
lda NMIB+2
BEQ nmi_wait
lda #$00
sta NMIB
;wait for NMI to finish, this limits logic to a fixed frame rate
main:
jsr ReadController1 ;read controllers
[ Read conrtollers1:
....
RtS] jsr Updating ;state machine with all game logic, like: input handlers, moving objects, collisions gravity etc
[updating:
...
Rts]
jmp Forever
JMP Forever ;jump back to Forever, infinite loop
Just as a practice exercise, I recommend when you rewrite your code. Follow this constraint. No subroutine shall exceed 50 lines.
Then, your main routines will be just a series of subroutine calls.
JSR Read_Controllers
JSR Move_Hero
JSR Scrolling
JSR Move_Enemies
JSR Handle_Collisions
Etc.
Then, just before each subroutine, a detailed comment on what the subroutine does. If it passes any values, list what kind of value(s) it passes.
If a subroutine is longer than 50 lines, try to break it into smaller subroutines.
Every non-trivial variable should tell you what it does, just by its name.
y_scroll
x_scroll
hero_size
jump_height
And so forth.
In 6-12 months, when your code is 10,000 lines long, you will thank me.
Challenge....Subverted
loop isn't working at all
Listen to me 11 days ago, listen to dougeff. Please restart, write smaller pieces of code, don't only come to us when your code visibly doesn't work. You're clearly not understanding some of the things you're trying, and by the time it gets broken in a way you can see there is way too uncommented code for us to comb through in a reasonable amount of time.
Anytime you're not sure about something, you can just ask. Because anytime something appears to work, but you don't really understand why is a problem waiting to happen.
Where would the rti be?
Would it be after evey 50 lines or after all code before JMP Forever?
IMAGICA wrote:
Where would the rti be?
Would it be after evey 50 lines or after all code before JMP Forever?
In most of the games I programmed, there is 1 rti in the entire game code...
NMI:
inc nmi_flag
inc frame_count
IRQ:
rti
The IRQ handler being an empty routine that just returns.
My Ninja game has 2 RTIs in the entire game. One for NMI code (at the end of it) and one for the IRQ code (at the very end of it). It uses a MMC3 mapper generated IRQ (scanline counter) to do a mid-screen split scroll.
Double post... just making sure you understand.
As the 6502 executes code, it keeps track of the current address of code being executed, called program counter (PC).
JSR - "jump to subroutine", takes pushes the address-1 (2 bytes) of the next operation on to the stack, and replaces the PC with then JSR address, at which point it starts executing from the subroutine.
RTS "return from subroutine", it pops 2 bytes off the stack, and puts them into the PC, at which point it starts executing from the next line after the last JSR.
An interrupt (NMI or IRQ), will pushes three bytes onto the stack. First is the high byte of the return address, followed by the low byte, and finally the status byte from the P processor status register. Then it replaces the PC from the NMI (or IRQ) vector table at the end of the ROM, at which point it starts executing from the NMI (or IRQ) routines.
RTI -"return from interrupt" pops 3 bytes from the stack, first the processor status flags, then the return addresses, replacing the PC, at which point it will continue executing from wherever (could be anywhere) it was prior to the interrupt.
If you try to RTI before an interrupt happens, it will try to pop 3 bytes off, but not get a usable address, which would crash the game.
If you JSR, then RTI, it will push 2 bytes, but try to pop 3 bytes. It won't get the right address, since the first byte is the processor flags, and it will crash the game. (It's not even the same address pushed. JSR pushes the next line minus 1, and an interrupt pushes the next line exactly. I don't know why they engineered it that way, but it makes it doubly incompatibe).
Clear?
This is the list of It Red text shows what was deleted green shows what was added
forever:
JSR 2
RTS -2
NMI/ IRQ 3
RTI -3
JSR RC1 +2 2
RTS RC1 -2 0
JSR Player +2 2
RTS Player -2 0
RTI -3;crash
NMI +3 3
JSR DNA 2 5
RTS DNA -2 3
JSR DNC 2 5
JSR ls 2 7
RTS ls -2 5
RTS DNC -2 3
JSR SC 2 5
RTS SC -2 3
RTI -3 0
Goal 1: Get game 'running'; notes: thank you dougleff The chart above shows the info in practice.
Goal 2: Get game to stop stuttering; notes: Pokun has info about vblanking with forever.
The stack resides at $0100-$01FF. This is what happens when you press left in FCEUX:
so you still have some type of stack issue.
Is there another thing I have to record to fixit because that's something new I've encountered.
Apart from before nmi Problems
delete the scroll stack if you want that effect again
GOT IT TO WORK
Main code:
Code:
LDA $01
STA NMIB+1
LDA NMIB+1
cmp $00
BCS waitforframe
jsr ReadController1 ;read controllers
jsr Player ;moves a sprite vertically across screen
waitforframe:
lda NMIB+1
beq waitforframe
LDA $00
STA NMIB+1
lda $00 is equivalent to lda speed in your program.
cmp $00 = cmp speed.
lda $01 is equivalent to lda temp.
lda $XX reads the value from RAM location $00XX and puts that into A. lda #$XX puts the actual value $XX into A.
The only reason this code works is because your program starts by setting all RAM to zero.
Is that a problem
if yes:
How can I fix?
Pokun wrote:
Code:
Forever:
JMP Forever ;jump back to Forever, infinite loop
Here is your main loop, here you can put all logic (except sound which is better off in the NMI to avoid sound lag).
Mine look something like this:
Code:
main:
jsr con_read ;read controllers
jsr logic ;state machine with all game logic, like: input handlers, moving objects, collisions gravity etc
lda #$01
sta draw_flag ;allow NMI to draw, prevents incomplete buffering
nmi_wait:
lda nmi_end_flag
beq nmi_wait ;wait for NMI to finish, this limits logic to a fixed frame rate
lda #$00
sta nmi_end_flag ;clear NMI completion flag
jmp main
.
This is what ive referenced
Quote:
This is what ive referenced
But you copied it incorrectly. You used lda $01, that code uses #$01 and they are
very different.
First, for understanding. Here is some example code:
Code:
lda #$FF;A=#$FF
sta $00;$00 = A. A=#$FF. So $00 has the value #$FF inside.
lda $00;A=#$FF
lda #$00;A=#$00
If you want to load a number, precede it with # like so
lda #$00
If you want to load a value in ROM or RAM, don't. like so
lda $00
But usually you would never do with a number. You'd usually use named RAM like
lda speed
instead if you wanted that.
So look for places in your code where you forgot the '#' symbol. Here's one:
Code:
Player:
INC $0200
INC scroll
LDA $00;You probably want lda #$00 here.
STA scroll
RTS
Alright. So that Sta temp from the PHA could be used to load it into that stack
BTW Code stop
P.S. How ca I yse this knowledge to fix vblanking
Code:
Forever:
LDA NMIB+1
cmp #$01
BEQ waitforframe
jsr ReadController1 ;read controllers
jsr Player ;moves a sprite vertically across screen
jsr Direction
JSR AniFrame
LDA #$01
STA NMIB+1
waitforframe:
lda NMIB+1
Cmp #$01
beq waitforframe
LDA NMIB+1
cmp #$01
BEQ waitforframe
LDA #$00
STA NMIB+1
jmp Forever
Got it... I think.....
You have a
much harder problem. After the NMI happens, you have about 2270 "cycles" to safely write to parts of the PPU. A "cycle" is a bit like a measure of time. Each instruction takes a certain amount of time.
From NMI:
to
STA $4014
in your program takes 2427 cycles. You have to optimize your code for speed and that's... not an easy topic to cover. But to start with, here's two facts.
1. The NMI lets you know when a brief period of time starts that allows you to write to places like $2007 safely while the screen is being rendered.
2. In your "Forever" loop, you have quite a lot of time, but
can't write to places like $2007 safely.
The solution: Do absolutely everything you can in your forever loop, short of actually writing to $2007.
Your NMI could look something like this:
Code:
NMI:
PHA
TXA
PHA
TYA
pha
lda #$20;Always write to one nametable for the example
sta $2006
lda #$00
sta $2006
LDA #%10110000
sta $2000;increment by one
ldy #0
nmiloop:
lda $0500,y;4 cycles
sta $2007;4 cycles
iny ;2 cycles
cpy #32;2 cycles
bne nmiloop;3 cycles taken
LDA #$00
STA $2003
LDA #$02
STA $4014 ; sprite DMA from $0200
LDA #%10110000
ora nametablescroll
sta $2000
lda #$00;reset scroll to zero
sta $2005
sta $2005
INC NMIB+1
PLA
TAY
PLA
TAX
PLA
RTI ; return from interrupt
That copy loop could be made faster, but let's keep it simple for now.
This makes it so your NMI does very few things.
1. Pushes your registers (X, Y and A) to the stack. This is pretty much required.
2. Write the top row of nametable0's address to $2006
3. Reads 32 bytes from $0500-$051F and stores them to $2007.
So effectively, it copies 32 bytes from $0500 to the top row of the first nametable.
4. Sets your scroll to 0, 0.
5. increments NMIB+1 so your main loop stops waiting.
6. pulls your registers from the stack. (Also pretty much required.)
Since your NMI (in this example) is copying bytes from $0500-$051F, your next step is to ready that data in your forever loop. So code like DrawNewColumn would be run in the forever loop, but instead of storing to $2007, it'd store to $0500,y. And then when the next NMI happens, it will be read from $0500,y and written to $2007 when it's safe to do so.
This is a super simplified way to approach this, but that's the theory. Say you want to copy to a different address. No problem. Create two variables to store the address you want to write to in your forever loop, and then read that from RAM in your NMI. Say you don't want to draw a new row every frame. No problem. Create a variable that says whether your NMI should copy a new row. Set it in your Forever loop. Read it in your NMI and skip the $2007 writes if it says to. Say you want to copy new columns instead of rows. No problem. Create a new variable that specifies the type of copying to be done.
You want your NMI to be making as few decisions as possible because the time is very limited, so this approach (write to $0500 or elsewhere in RAM while you have a lot of time, then directly copy when you don't) is a good one.
Kasumi wrote:
You have a
much harder problem. After the NMI happens, you have about 2270 "cycles" to safely write to parts of the PPU. A "cycle" is a bit like a measure of time. Each instruction takes a certain amount of time.
From NMI:
to
STA $4014
in your program takes 2427 cycles. You have to optimize your code for speed and that's... not an easy topic to cover. But to start with, here's two facts.
1. The NMI lets you know when a brief period of time starts that allows you to write to places like $2007 safely while the screen is being rendered.
2. In your "Forever" loop, you have quite a lot of time, but
can't write to places like $2007 safely.
The solution: Do absolutely everything you can in your forever loop, short of actually writing to $2007.
Your NMI could look something like this:
Code:
NMI:
PHA
TXA
PHA
TYA
pha
lda #$20;Always write to one nametable for the example
sta $2006
lda #$00
sta $2006
LDA #%10110000
sta $2000;increment by one
ldy #0
nmiloop:
lda $0500,y;4 cycles
sta $2007;4 cycles
iny ;2 cycles
cpy #32;2 cycles
bne nmiloop;3 cycles taken
LDA #$00
STA $2003
LDA #$02
STA $4014 ; sprite DMA from $0200
LDA #%10110000
ora nametablescroll
sta $2000
lda #$00;reset scroll to zero
sta $2005
sta $2005
INC NMIB+1
PLA
TAY
PLA
TAX
PLA
RTI ; return from interrupt
That copy loop could be made faster, but let's keep it simple for now.
This makes it so your NMI does very few things.
1. Pushes your registers (X, Y and A) to the stack. This is pretty much required.
2. Write the top row of nametable0's address to $2006
3. Reads 32 bytes from $0500-$051F and stores them to $2007.
So effectively, it copies 32 bytes from $0500 to the top row of the first nametable.
4. Sets your scroll to 0, 0.
5. increments NMIB+1 so your main loop stops waiting.
6. pulls your registers from the stack. (Also pretty much required.)
Since your NMI (in this example) is copying bytes from $0500-$051F, your next step is to ready that data in your forever loop. So code like DrawNewColumn would be run in the forever loop, but instead of storing to $2007, it'd store to $0500,y. And then when the next NMI happens, it will be read from $0500,y and written to $2007 when it's safe to do so.
This is a super simplified way to approach this, but that's the theory. Say you want to copy to a different address. No problem. Create two variables to store the address you want to write to in your forever loop, and then read that from RAM in your NMI. Say you don't want to draw a new row every frame. No problem. Create a variable that says whether your NMI should copy a new row. Set it in your Forever loop. Read it in your NMI and skip the $2007 writes if it says to. Say you want to copy new columns instead of rows. No problem. Create a new variable that specifies the type of copying to be done.
You want your NMI to be making as few decisions as possible because the time is very limited, so this approach (write to $0500 or elsewhere in RAM while you have a lot of time, then directly copy when you don't) is a good one.
SO if I'm reading this correctly, Put Drawcolems,Drawattributes into Forever and put this in the nmi?
Also when MMC5 is enabled, black screen is inevitvlble. Is bank switching needed
also is the back ground normal?
You can't only put Drawattributes/Drawcolumns into forever because they write to $2006/$2007. You have to modify them so they write to RAM instead. Your NMI must then read from this RAM and write that data to $2007 as fast as possible.
You last ROM used mapper 4, which is MMC3 not MMC5. Has something changed? I would not recommend switching mappers right now.
The background for your ROM only appeared normal in less accurate emulators, your last write to $2007 in your NMI cannot occur as late as it does.
No just wondering. mmc question
Also one more thing.
'For the background, does this have a bad effect on it?
Code:
DrawNewColumn:
LDA scroll ; calculate new column address using scroll register
LSR A
LSR A
LSR A ; shift right 3 times = divide by 8
STA columnLow ; $00 to $1F, screen is 32 tiles wide
LDA nametable ; calculate new column address using current nametable
EOR #$01 ; invert low bit, A = $00 or $01
ASL A ; shift up, A = $00 or $02
ASL A ; $00 or $04
STA switchrender
CLC
ADC #$20 ; add high byte of nametable base address ($2000) can change to lower name table $2000 = ADC #$20 $2800 = ADC #$28
STA columnHigh ; now address = $20 or $24 for nametable 0 or 1
LDA columnNumber ; column number * 32 = column data offset
ASL A
ASL A
ASL A
ASL A
ASL A
STA sourceLow
LDA columnNumber
LSR A
LSR A
LSR A
STA sourceHigh
JSR levelselect
DrawColumn:
LDA #%00100100 ; set to increment +32 mode
STA $2000
LDA $2002 ; read PPU status to reset the high/low latch
LDA columnHigh
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006 ; write the low byte of column address
LDX #$0F ; copy 30 bytes
LDY #$1E
DrawColumnLoop:
LDA [sourceLow], y
STA $2007
CLC
ADC #$01
STA $2007
INY
DEX
BNE DrawColumnLoop
LDA switchrender
CLC
ADC #$28
STA columnHigh
LDA columnHigh
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006
LDX #$0F ; copy 30 bytes
DrawColumnLoopP2:
LDA [sourceLow], y
STA $2007
CLC
ADC #$01
STA $2007
INY
DEX
BNE DrawColumnLoopP2
RTS
I'm not sure I understand why those changes were made. You're writing the value from [sourcelow],y to $2007, then adding 1 that value and writing that to $2007. So for every byte in the address pointed to in sourcelow, you're writing two bytes. You're also not starting from zero. (LDY #$1E), which I'm not sure is correct. (Maybe you did that before? I forget)
But aside from that, your rom is smaller than it should be. (I probably should have mentioned this a while ago.) NESASM considers a bank to be 8KB always. The header for an NES rom considers PRG banks (code/data) to be 16KB and CHR banks (graphics) to be 8KB.
Your code starts like this:
Code:
.inesprg 1 ; 1x 16KB PRG code
.ineschr 4 ; 1x 8KB CHR data
16*1 (the size in KB of PRG banks)
+
4*8 (the size in KB of CHR banks)
=
48
48/8 (the size in KB of a bank to NESASM)
=
6
So you should have 6 .bank directives in your code. 2 for PRG, and 4 for chr. Your two for prg are correct.
Code:
.bank 2
.org $0000
.incbin "Electro_and_Shiro.chr"
;;;;;;;;;;;;;;;;;;;;
.bank 3
.org $0000;This might change how your rom behaved? But I don't think $A000 was right
.incbin "Kitsunetales.chr"
;;;;;;;;;;;;;;;;;;;
.bank 4
.org $0000
.bank 5
.org $0000
Answer to first question:
This is so that I can double the screen size Ala
SMB3 by using adjacent tiles to get a certain effect of doubling the screen size using one hex memory address, effectively filling both "screens" of the PPU. While my arrogance that has shown prominently during this wants to explain how I did it, I want to tell you what it was originally
Code:
DrawColumn:
LDA #%00100100 ; set to increment +32 mode
STA $2000
LDA $2002 ; read PPU status to reset the high/low latch
LDA columnHigh
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006 ; write the low byte of column address
LDX #$1E ; copy 30 bytes
LDY #$1E
DrawColumnLoop:
LDA [sourceLow], y
STA $2007
INY
DEX
BNE DrawColumnLoop
DrawNewColumn2:
LDA scroll ; calculate new column address using scroll register
LSR A
LSR A
LSR A ; shift right 3 times = divide by 8
STA columnLow2 ; $00 to $1F, screen is 32 tiles wide
LDA nametable ; calculate new column address using current nametable
EOR #$01 ; invert low bit, A = $00 or $01
ASL A ; shift up, A = $00 or $02
ASL A ; $00 or $04
CLC
ADC #$28 ; add high byte of nametable base address ($2000) can change to lower name table $2000 = ADC #$20 $2800 = ADC #$28 changes what loads the name table in the bottem name table in
STA columnHigh ; now address = $20 or $24 for nametable 0 or 1
LDA columnNumber2 ; column number * 32 = column data offset
ASL A
ASL A
ASL A
ASL A
ASL A
STA sourceLow2
LDA columnNumber
LSR A
LSR A
LSR A
STA sourceHigh2
LDA sourceLow ; column data start + offset = address to load column data from
CLC
ADC #$04
STA sourceLow2
LDA sourceHigh
ADC #$04
STA sourceHigh2
DrawColumn2:
LDA #%00100100 ; set to increment +32 mode
STA $2000
LDA $2002 ; read PPU status to reset the high/low latch
LDA columnHigh
STA $2006 ; write the high byte of column address
LDA columnLow
STA $2006 ; write the low byte of column address
LDX #$1E ; copy 30 bytes
LDY #$1E
DrawColumnLoop2:
LDA [sourceLow2], y
STA $2007
INY
DEX
BNE DrawColumnLoop2
;;;;;;;;;;
scrollCheck:
LDA player
CLC
ADC #$08
CMP #$80
BEQ ReadController1
LDA player
CLC
ADC #$08
CMP #$80
BCC Rights
LDA player
SEC
SBC #$01
STA player
LDA #$00
STA sdir
JMP ReadController1
RTS
witch took too much memory from the hex by using the next screen data, halving the game's level data.
The new code like you said
Quote:
[writes] the value from [sourcelow],y to $2007, then adding 1 that value and writing that to $2007. So for every byte in the address pointed to in sourcelow, you're writing two bytes.
Whitch keeps the level long without compensating the length.
Answer to the second question:
The game isn't finished. When I learn Bank switching and r-a-m- -s-t-o-r-a-g-e(already done) , I'll get working on the other game elements. If there's problem with it, tell me please.
Yes, there is a problem. Your ROM claims to be 48KB in the header, but it's only 40KB, which makes some emulators reject it outright. Nestopia is one such emulator. VirtuaNES is another. Adding the bank directive as I've instructed will tell NESASM to add another CHR bank (8KB) to your file so that it will be the proper size.
One thing to note is that $2000 isn't written to before (or in) DrawNewAttributes in the NMI, so when $2007 is written to in DrawNewAttributes it is incrementing by whatever was last written to $2000.
Your game crashes when you press A because of this code:
Code:
LDA JumpState;If JumpState is >= 1
CMP #$01
BCS ReadADonep1;go to ReadADonep1
LDA #$01;This makes JumpState equal to 1
STA JumpState
ReadADonep1:
LDA JumpState;If JumpState is >= 1
CMP #$01;Go to ReadADonep1
BCS ReadADonep1;Because ReadADonep1 is directly above, this is an endless loop.
Trying to write to $0700 and every time I do, It resets to $FF
Code:
DrawColumnLoop:
LDA [sourceLow], y
STA $0700,y
INY
DEX
BNE DrawColumnLoop
RTS
First: There is no ROM, nor a way to build the ROM, so I can't even see the problem you're currently talking about. Can you share the other files one needs to build the game, instead of just the .asm? "KitsuneTaleslevel.bin", "KitsuneTaleslevel2.bin", "KitsuneTalesattrib.bin", "KitsuneTalesattrib2.bin", "Electro_and_Shiro.chr" and "Kitsunetales.chr". This forum allows you to upload .zip files, so you can upload your project file as one file.
Edit:
Second: Please make the changes I suggested in my last post about CHR. I now see that you changed .ineschr to 3 instead of 4, but that... creates a different kind of problem. Potentially the problem you're describing.
The size of your CHR and PRG should be a power of two. If you multiply your .ineschr value by 8192 you'll get 24576 which is not a power of two. What I outlined as a fix was a way to avoid all of this kind of stuff. You want .ineschr 4, you want a .bank 5, and you want .org $0000 under each CHR .bank statement. (so .bank 3, .bank 4, and .bank 5 would want .org $0000 under them, instead of .org $A000 and $E000. .bank 2 is correct.) The last CHR banks can be empty, but they need to be there.
Third: Without building the ROM, that code looks like it'd do exactly what I said it'd do. That is, copy a single column from the address pointed to in sourceLow and sourceLow+1 to RAM (except at $0700+ rather than $0500+). So can you be more specific about the difference between what you are expecting to happen and what is actually happening?
Edit 2: Because it will help replicate exactly what you're seeing, what emulator are you using, and what version of that emulator? If testing on real hardware, what flash card/other type of cartridge? (Though I only own a powerpak and infiniteneslives MMC3 cart, so if it's not those then I can't see it exactly.)
Quote:
I want to program a MMC3 demake of smash bros, what's a vblank?
STOP and go read the basics immediately. Go read a decent tutorial (Nerdy Nights at a minimum, I'm sure people here have links to better reading material though) before trying to code complex stuff. You're not going anywhere if all you do is copy/paste code you barely understand even if we try our best to help you. Look at the complexity of a simple breakout game I've coded:
https://github.com/AleffCorrea/BrickBre ... lision.asmRoughly 500 lines just for background and sprite collision detection... feel free to browse the rest of the files from my game. Don't be overwhelmed by it though, trust me when I say that if you stop what you're trying to do right now and direct your efforts towards learning the underlying concepts first you'll be doing code that is 10 times better than mine in no time.
I got rid of the code under anim, and scrollCheck. It wasn't used.
I got rid of readjoy. It wasn't used.
I got rid of Loadp. It wasn't used.
I got rid of LoadPalettes2. It wasn't used.
I recommend getting rid of dead code like that, it makes for way less to sift through while looking for errors.
DrawNewColumn uses columnNumber to decide which column to draw (which address to put into sourceLow and sourceHigh). columnNumber gets incremented every frame, so most of the time it's loading from data totally beyond your level data. Since the byte that's used to pad unused space is $FF in NESASM, this is why mostly $FF gets copied. Why columnNumber gets incremented, I have no idea. It's hard for me to follow the current code. And I know how I'd find out what's wrong, exactly, but I genuinely think the time would be better spent helping you rewrite it. As I, and others, have recommended, I would rewrite this one small step at a time.
1. Write code that copies a single fixed column to RAM.
When that works (check the RAM with FCEUX's hex editor)
2. Write code that copies a single column to RAM, that you can change with the d-pad. (Say... pressing right picks the next column. Pressing left picks the previous column.)
When that works (check the RAM with FCEUX's hex editor)
3. Write code in your NMI that writes the values in the "column RAM" to $2007 fixed to one column's address.
When that works (you can just see it)
4. Write code in that sets an address for $2006 to a specific column, that is also changed with the d-pad.
When that works (check the RAM for the address for $2006 gets stored to)
5. Write code in the NMI that uses the address you set above to write to $2006 so the $2007 writes go to the right place.
When that works (you can just see the columns get written to the right place by using the d-pad)...
6. Write code that writes all columns one frame at a time without the use of the d-pad.
When that works...
7. Move the code in the NMI that writes to $2006 and $2007 to a subroutine and call it from the NMI.
When that works...
8. Disable rendering and use that subroutine in addition to your column to RAM code to write all columns in a single frame.
When that works...
9. When rendering is enabled, make it draw a new column when the screen is scrolled.
When that works...
etc.
I outline these steps to try to teach what making small testable parts is like. If you don't have a good guess of where a problem is when you're coding, you're probably writing too much before testing.
You didn't answer my question: What test setup (emulator/cartridge) are you using?
Edit: For the record I do see you are taking steps to writing small parts like this, but the reason your code is broken right now is actually stuff you wrote before. So I'm saying get rid of that stuff in addition to doing each small part.
Test Setup?:
Fceux first check value, see if my code is working, try to break code. Move on.
wanna see an example? press up and down on the dpad using the kitsune3 code . theres the test of rendering.
Ive Also learmned what vnblabnking is
I saw an example. I even told you what was wrong, just not specifically how to fix it. You're doing things to columnNumber for reasons I don't understand. If you got rid of all code that modifies columnNumber, it'd probably work. Which you could then build on, which is what I suggest. Assuming you get rid of the columnNumber modifying code, you're on step 1 in my post. Then go to step 2. etc.
Does it at least make sense why you're seeing $FFs with the explanation in the previous post?
Done, check the scrolling by pressing A, Up and down
Fix the CHR! Your file is still 40KB, which probably means you still don't have a .bank 5 and a .org $0000 under that .bank 5.
Glad it mostly works, but there's definitely still something wrong. Every 32 pixels (which I assume is when you update attribute bytes), the scroll position jumps a little vertically.
edit: To be specific: Every 32 horizontal pixels, I mean. Holding A for 32 frames.
Quuuesstion....
Before I dive into the main problem:
"Why does every time I update my 'pallete'[palette] , the scroll gets messed up?" problem,
There is something wrong with the attribute table. I'm using your (Kasumi) method of updating using nmi timing and when I update the screen data, There are background tiles missing and the attribute doesn't update correctly. Answers?
Also, Fancy a bonus?
1.[*] can you activate an IRQ any where?
2. scroll both the IRQ and the main screen in both directions at the same time? (you know like the Sonic 2 Vs. screen)? example: https://www.youtube.com/watch?v=OgIb5T9HYEc
P.S. This is for a multiplayer option I'm thinking of.
UPDATE.
Bad example: uses parallax scrolling
I didn't look at the code, but after 2 minutes of looking at the last .nes file...I see.
1.You aren't zeroing the attribute table of the PPU. Or setting it at all.
No RAM should be assumed to be zero. On a real NES, the screen (attribute table) might be filled with random palettes for the BG.
2. You are still occasionally writing to the PPU outside of v-blank. Not very far outside, but enough to cause occasional jitter while scrolling.
1. Later... When the 2nd problem is gone.
2. Moving the controller routine makes the jittering stop. Still having rendering problems though.
3. There is a block that keeps rewriting the background Improperly.
4. The "Take Full Advantage of NMI" part of
https://wiki.nesdev.com/w/index.php/The ... er_Formats is giving me info but the info given by Kasumi has around the same effect as it is
5. It seems that my Attributes are being written into the column data somehow. Fixing that is the main priority. My "hint" (or blatant truth) is that " [Vblank has a] notification [that] comes in the form of an NMI, or "non-maskable interrupt". ... " [When] the PPU starts VBlank again, the PPU will attempt to notify you that this has happened. It does this by sending an NMI to your program."
Edit: It seems regulation of the attribute table is the answer
One thing I do, when I'm working on new code (when I'm not sure of PPU behavior)... is make a very minimal test case. So, basically an empty program with just initial code, and a mostly empty nmi, and my test code.
If you are having trouble with attribute tables, perhaps it's because you are writing tiles top to bottom. It's very tricky to write attributes top to bottom.
The attribute table (nametable #0) goes like this...
23c0,23c1,23c2,23c3,23c4,23c5,23c6,23c7
23c8,23c9,23ca,23cb,23cc,23cd,23ce,23cf
23d0,...etc on through 23ff
23c0
23c8 below it
23d0 below that
etc.
So, writing top to bottom adds 32 ($20 hex) but that would skip from 23c0 to 23e0 and miss all the bytes between. You could set a new address for each write to the attribute table.
That is slow, and will probably put you past v-blank. Maybe you could draw the column on 1 frame and draw the attribute on the next frame.
Ok... Got a question.
I've gotten one of my scrolling bugs fixed (The columnNumber byte was incrementing without the scroll being correct) and the scrolling goes both left and right.
The problem with my system is that if I tap the scrolling, the scroll wont update correctly.
Try it.
P.S. I've labled the needed functions