Pallete affects scroll value

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
User avatar
IMAGICA
Posts: 61
Joined: Thu Jul 13, 2017 5:17 pm
Contact:

Pallete affects scroll value

Post by IMAGICA »

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: Select all

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_NMIs
And I don't Understand what he mean to do for lda needppureg
Please first explain what the problem is before showing the code.
Thanks.
In Progress:
Tengu Tales (MMC3 Test first)[360 degress bullet direction math]
Baseball Brawlers (MMC5 full features, "Yoshi's Island")[Square root table, SPG ]
Smash ("port",MMC5 )[Character Movement]
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: Pallete affects scroll value

Post by dougeff »

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.
nesdoug.com -- blog/tutorial on programming for the NES
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Pallete affects scroll value

Post by Pokun »

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.
User avatar
IMAGICA
Posts: 61
Joined: Thu Jul 13, 2017 5:17 pm
Contact:

Re: Pallete affects scroll value

Post by IMAGICA »

What logic is at the main loop?
In Progress:
Tengu Tales (MMC3 Test first)[360 degress bullet direction math]
Baseball Brawlers (MMC5 full features, "Yoshi's Island")[Square root table, SPG ]
Smash ("port",MMC5 )[Character Movement]
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Pallete affects scroll value

Post by Pokun »

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.
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: Pallete affects scroll value

Post by dougeff »

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.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
IMAGICA
Posts: 61
Joined: Thu Jul 13, 2017 5:17 pm
Contact:

Re: Pallete affects scroll value

Post by IMAGICA »

Cool. :mrgreen:
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: Select all

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.
In Progress:
Tengu Tales (MMC3 Test first)[360 degress bullet direction math]
Baseball Brawlers (MMC5 full features, "Yoshi's Island")[Square root table, SPG ]
Smash ("port",MMC5 )[Character Movement]
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Pallete affects scroll value

Post by Pokun »

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: Select all

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.
User avatar
IMAGICA
Posts: 61
Joined: Thu Jul 13, 2017 5:17 pm
Contact:

Re: Pallete affects scroll value

Post by IMAGICA »

Code: Select all

  .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
In Progress:
Tengu Tales (MMC3 Test first)[360 degress bullet direction math]
Baseball Brawlers (MMC5 full features, "Yoshi's Island")[Square root table, SPG ]
Smash ("port",MMC5 )[Character Movement]
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Pallete affects scroll value

Post by Pokun »

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: Select all

  ;...
  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.
User avatar
IMAGICA
Posts: 61
Joined: Thu Jul 13, 2017 5:17 pm
Contact:

Re: Pallete affects scroll value

Post by IMAGICA »

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?
Last edited by IMAGICA on Fri Jul 21, 2017 6:53 pm, edited 1 time in total.
In Progress:
Tengu Tales (MMC3 Test first)[360 degress bullet direction math]
Baseball Brawlers (MMC5 full features, "Yoshi's Island")[Square root table, SPG ]
Smash ("port",MMC5 )[Character Movement]
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: Pallete affects scroll value

Post by dougeff »

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.
nesdoug.com -- blog/tutorial on programming for the NES
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Pallete affects scroll value

Post by Pokun »

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.
User avatar
IMAGICA
Posts: 61
Joined: Thu Jul 13, 2017 5:17 pm
Contact:

Re: Pallete affects scroll value

Post by IMAGICA »

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?
In Progress:
Tengu Tales (MMC3 Test first)[360 degress bullet direction math]
Baseball Brawlers (MMC5 full features, "Yoshi's Island")[Square root table, SPG ]
Smash ("port",MMC5 )[Character Movement]
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Pallete affects scroll value

Post by tepples »

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
Post Reply