How to organize code? ("good code")

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

Sebastian_L
Posts: 17
Joined: Tue Feb 16, 2021 12:31 pm

How to organize code? ("good code")

Post by Sebastian_L »

Hello,

for this question I have the content of 2 asm files. The first one is from Nerdy Nights, the second one is from Joe Granato.

My questions are as following:

1. Is the order within a LABEL important or is it just, that you have to have all parts in it?
2. What registers (X,Y and A) should be used in what label? (e.g. one used ldx and one lda in RESET label)
3. The second code by Joe Granato uses PHA, TXA, PHA and TYA in the NMI label, is there an equivalent in Nerdy Nights code?
4. What should exactly happen in RESET, NMI, Clearmemory? (especially if comparing both codes)
5. Where in the code from Joe Granato is said that the green $0a is used? How would I change it (not the color palette itself)?

I look forward for your answers.
Thank you in advance.

Here is the code:

Nerdy Nights:

Code: Select all

;; 1. The iNES Header
   .db "NES", $1a		;INES identifier
   .db $01				;number of PRG-Rom blocks the game will have
   .db $01				;number of CHR-Rom blocks the game will have
   .db $00, $01			;control bytes
   .db $00, $00, $00, $00, $00, $00, $00, $00		;filler
  


;; 2. Constants and Variables
   .enum $0000			
   ;variables will eventually go here
   .ende

    
;;;;;;;;;;;;;;;


  .org $C000 
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

vblankwait1:       ; First wait for vblank to make sure PPU is ready
  BIT $2002
  BPL vblankwait1

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
   
vblankwait2:      ; Second wait for vblank, PPU is ready after this
  BIT $2002
  BPL vblankwait2


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              ; start out at 0
LoadPalettesLoop:
  LDA palette, x        ; load data from address (palette + the value in x)
                          ; 1st time through loop it will load palette+0
                          ; 2nd time through loop it will load palette+1
                          ; 3rd time through loop it will load palette+2
                          ; etc
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$20              ; Compare X to hex $10, decimal 16 - copying 16 bytes = 4 sprites
  BNE LoadPalettesLoop  ; Branch to LoadPalettesLoop if compare was Not Equal to zero
                        ; if compare was equal to 32, keep going down



LoadSprites:
  LDX #$00              ; start at 0
LoadSpritesLoop:
  LDA sprites, x        ; load data from address (sprites +  x)
  STA $0200, x          ; store into RAM address ($0200 + x)
  INX                   ; X = X + 1
  CPX #$10              ; Compare X to hex $10, decimal 16
  BNE LoadSpritesLoop   ; Branch to LoadSpritesLoop if compare was Not Equal to zero
                        ; if compare was equal to 16, keep going down
              
              
              
LoadBackground:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$20
  STA $2006             ; write the high byte of $2000 address
  LDA #$00
  STA $2006             ; write the low byte of $2000 address
  LDX #$00              ; start out at 0
LoadBackgroundLoop:
  LDA background, x     ; load data from address (background + the value in x)
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$80              ; Compare X to hex $80, decimal 128 - copying 128 bytes
  BNE LoadBackgroundLoop  ; Branch to LoadBackgroundLoop if compare was Not Equal to zero
                        ; if compare was equal to 128, keep going down
              
              
LoadAttribute:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$23
  STA $2006             ; write the high byte of $23C0 address
  LDA #$C0
  STA $2006             ; write the low byte of $23C0 address
  LDX #$00              ; start out at 0
LoadAttributeLoop:
  LDA attribute, x      ; load data from address (attribute + the value in x)
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$08              ; Compare X to hex $08, decimal 8 - copying 8 bytes
  BNE LoadAttributeLoop  ; Branch to LoadAttributeLoop if compare was Not Equal to zero
                        ; if compare was equal to 128, keep going down


              
              
              
  LDA #%10010000   ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
  STA $2000

  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001

Forever:
  JMP Forever     ;jump back to Forever, infinite loop
  
 

NMI:
  LDA #$00
  STA $2003       ; set the low byte (00) of the RAM address
  LDA #$02
  STA $4014       ; set the high byte (02) of the RAM address, start the transfer


LatchController:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016       ; tell both the controllers to latch buttons


ReadA: 
  LDA $4016       ; player 1 - A
  AND #%00000001  ; only look at bit 0
  BEQ ReadADone   ; branch to ReadADone if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  CLC             ; make sure the carry flag is clear
  ADC #$01        ; A = A + 1
  STA $0203       ; save sprite X position
ReadADone:        ; handling this button is done
  

ReadB: 
  LDA $4016       ; player 1 - B
  AND #%00000001  ; only look at bit 0
  BEQ ReadBDone   ; branch to ReadBDone if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  SEC             ; make sure carry flag is set
  SBC #$01        ; A = A - 1
  STA $0203       ; save sprite X position
ReadBDone:        ; handling this button is done


  ;;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
  STA $2000
  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001
  LDA #$00        ;;tell the ppu there is no background scrolling
  STA $2005
  STA $2005
  
  RTI             ; return from interrupt
 
;;;;;;;;;;;;;;  
  
  

  .org $E000
palette:
  .db $22,$29,$1A,$0F,  $22,$36,$17,$0F,  $22,$30,$21,$0F,  $22,$27,$17,$0F   ;;background palette
  .db $22,$1C,$15,$14,  $22,$02,$38,$3C,  $22,$1C,$15,$14,  $22,$02,$38,$3C   ;;sprite palette

sprites:
     ;vert tile attr horiz
  .db $80, $32, $00, $80   ;sprite 0
  .db $80, $33, $00, $88   ;sprite 1
  .db $88, $34, $00, $80   ;sprite 2
  .db $88, $35, $00, $88   ;sprite 3


background:
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;row 1
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;all sky

  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;row 2
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;all sky

  .db $24,$24,$24,$24,$45,$45,$24,$24,$45,$45,$45,$45,$45,$45,$24,$24  ;;row 3
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$53,$54,$24,$24  ;;some brick tops

  .db $24,$24,$24,$24,$47,$47,$24,$24,$47,$47,$47,$47,$47,$47,$24,$24  ;;row 4
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$55,$56,$24,$24  ;;brick bottoms

attribute:
  .db %00000000, %00010000, %01010000, %00010000, %00000000, %00000000, %00000000, %00110000

  .db $24,$24,$24,$24, $47,$47,$24,$24 ,$47,$47,$47,$47, $47,$47,$24,$24 ,$24,$24,$24,$24 ,$24,$24,$24,$24, $24,$24,$24,$24, $55,$56,$24,$24  ;;brick bottoms



  .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
  
  
;;;;;;;;;;;;;;  
  
  

  .incbin "mario.chr"   ;includes 8KB graphics file from SMB1

Joe Granato:

Code: Select all

  .inesprg 1   ; 1x 16KB PRG code
  .ineschr 1   ; 1x  8KB CHR data
  .inesmap 0   ; mapper 0 = NROM, no bank swapping
  .inesmir 1   ; background mirroring
  

   .bank 0
;; 3. Set the code starting point
   .org $C000			;this starts the code at the address $C000


;; 4. The RESET routine
;;turn things off and initialize

RESET:
   SEI					;SEI tells the code to ignore interrupts for the routine
   LDA #$00				;load 0 into the accumulator
   STA $2000			;disable the NMI
   STA $2001			;disable rendering
   STA $4010
   STA $4015
   LDA #$40				;loads HEX value 40 which is dec value 64
   STA $4017
   CLD					;disable decimal mode
   LDX #$FF				;loads value 255
   TXS					;initialize the stack
   bit $2002
vBlankWait1:
   bit $2002
   BPL vBlankWait1
   
;;clear out memory etc.
   LDA #$00				;loads zero into the accumulator
   LDX #$00				;loads zero into x
;;ready to start loop
ClearMemomryLoop:
   STA $0000,x			;store accumulator 0 into address $0000+x
   STA $0100,x			;store accumulator 0 into address $0100+x
   STA $0200,x			;store accumulator 0 into address $0200+x
   STA $0300,x			;store accumulator 0 into address $0300+x
   STA $0400,x			;store accumulator 0 into address $0400+x
   STA $0500,x			;store accumulator 0 into address $0500+x
   STA $0600,x			;store accumulator 0 into address $0600+x
   STA $0700,x			;store accumulator 0 into address $0700+x
   INX					;x goes up by one, so all of those + x's at the end that were zero the first time through are increased
   BNE ClearMemomryLoop	;loop again until all adresses + x (#$FF = 255) become 0


vBlankWait2:
   bit $2002
   BPL vBlankWait2

;;turn things back on now that we're set up
   LDA #%10010000		;loads this binary number to accumulator = 144 decimal
   STA $2000			;storing it here, turns NMI back on
   LDA #%00011110		;loads this binary number to accumulator = 30 decimal
   STA $2001			;enables rendering
   
   JMP MainGameLoop


;; 5. The NMI
NMI:
   ;PUSH registers to the stack to preserve them
   PHA					;this pushes the accumulator to the stack, it is the first thing there.
   TXA					;this loads whatever is in X into the accumulator
   PHA					;and pushes it into the accumulator, now the old 'A' is on the bottom and X is on top of it
   TYA					;this loads whatever is in Y into the accumulator
   PHA					;and pushes the accumulator to the stack, now Y is on top, X is in the middle and A is in the bottom
   
   
;;do NMI things here
;;transfer sprites to PPU
   LDA #$00				;puts 0 into the accumulator (low byte)
   STA $2003			;sets the low byte of the sprite RAM address
   LDA #$02				;puts 02 into the accumulator (high byte)
   STA $4014			;sets high byte of the RAM address and starts transfer
   ;read backwards: RAM address we loaded 00
   ;then loaded 02 (this has loaded low byte of 00, high byte 02 = it is a 16-bit address)
   ;high+low (02+00 or $0200), this the address for our sprite data
   
   ;load the palettes
   LDA $2002
   LDA #$3F
   STA $2006
   LDA #$00
   STA $2006
   LDX #$00
   
   ;ready to start loop
LoadPaletteLoop:
   LDA MyPalettes,x		;load value from the table that x equals to
   STA $2007			;store it to the address that handles palettes
   INX					;increase x
   CPX #$20				;compare it to hex 20 = 32 decimal (8 banks of 4 colors), so 32-times the loop
   BNE LoadPaletteLoop	;if 32-x is not 0, then start loop again
   
   
   
   
   LDA #%10010000		;turns on NMI, like in RESET = 144 decimal
   STA $2000
   LDA #%00011110		;turns on rendering, like in RESET = 30 decimal   
   STA $2001



   ;PULL registers from the stack and restore them
   ;first thing to pull is the top, then go down
   PLA					;pulls the top stack and put it in the accumulator
   TAY					;puts that value into Y, now Y is restored to what it was before NMI
   PLA					;pulls second stack value and puts it in accumulator
   TAX					;puts that value into X, now X is restored to what it was before NMI
   PLA					;pulls third stack value and puts it in accumulator, now Y, X and A are all restored
   RTI					;at the end of NMI, return from the interrupt

;; 6. The Main Game Loop
MainGameLoop:
   ;game logic will go here
   JMP MainGameLoop		;jumps back to MainGameLoop, create an infinite loop


;; 7. Sub Routines


   .bank 1
   .org $E000
;; 8. Includes and data tables
MyPalettes:
   ;backgroud
   .db $0F,$11,$17,$0a, $28,$31,$01,$1c, $05,$3c,$01,$14, $08,$21,$39,$03
   ;sprites
   .db $0F,$31,$28,$1b, $13,$06,$05,$31, $39,$2a,$2c,$16, $17,$13,$14,$00


;; 9. The Vectors
   .org $fffa			;sets up at the very end of the code
   .dw NMI				;now the NMI points to our label NMI
   .dw RESET			;now the RESET points to our label RESET
   .dw 00
I have started NES programming! - Please be kind :D
stan423321
Posts: 53
Joined: Wed Sep 09, 2020 3:08 am

Re: How to organize code? ("good code")

Post by stan423321 »

I'm not experienced in NES specifically, but as for the questions involving general assembly stuff:

1. The CPU will run things in order, so in general order of opcodes between labels does matter.
2. There's no general answer to this. Some things can be done easily with A, some with X. Some can be done with both, but you may want to keep one if the values.
3. No, you're correct. The first sample destroys values in registers when NMI handler starts. The second sample saves them on stack and pulls them back before RTI. The latter is more reasonable. The former breaks when you do anything outside of NMI handler except for waiting for NMI handler and expect to continue it afterwards.
Pokun
Posts: 2675
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: How to organize code? ("good code")

Post by Pokun »

1) By "LABEL" I think you mean interrupt handler. No the order of them is totally up to you. The order of the interrupt vectors at $FFFA however is important. They go in the order NMI, RESET and IRQ.

2) You should expect that you will use all of them in any interrupt handler. They are used all the time. There are no conventions that you have to use one or the other or anything like that.

3) Joe Granato does that because he uses the NMI handler for graphics update and the RESET handler for logic code (inside the Forever loop). Nerdy Nights just runs the init code in RESET then the entire program loop runs in NMI. This is a more limited approach but it works and there is no need to backup the registers using PHA, TXA etc. The reason is explained very well in this article.

4) Clearmem is part of the init code where he clears RAM. This is the same for both codes as far as I can see. You need to set every RAM register to 0 sometime in your init code or else RAM will have unpredictable values that can vary every time you start the NES. You want the system to be as predictable as possible to prevent bugs, that's why you have to initialize everything to a known state. The whole of VRAM should also be initialized.

5) It looks like Joe Granato doesn't touch the palette in his init code, but he does update the palette every NMI (which is every vblank). He loads the colors from the location labeled "MyPalettes" and it includes the color $0A.
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: How to organize code? ("good code")

Post by DRW »

Pokun wrote: Wed Feb 17, 2021 12:14 pm Nerdy Nights just runs the init code in RESET then the entire program loop runs in NMI. This is a more limited approach but it works and there is no need to backup the registers using PHA, TXA etc.
Doesn't the backup of the registers need to be done anyway? What if the NMI hits while you're inside the NMI? In this case, you at least have to read some flag variable to decide whether you immediately exit NMI again. Hence, you at least have to do PHA and PLA to be safe, don't you?
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Pokun
Posts: 2675
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: How to organize code? ("good code")

Post by Pokun »

Yes you are right. Unless you can guarantee that nested NMIs doesn't happen you need some kind of protection from that. The Nerdy Night programs are of course so short that this isn't a problem yet, and Bunny Boy probably didn't want to make the code more complicated than absolutely necessary.

Nerdy Night's approach is used by some games like Super Mario Bros, but the approach used by Joe Granato is more popular and the better solution of the two. They are both explained here.

BTW, the init code that every NES program needs is explained in detail here. Though you also need to do a few other things after this code, like clearing the screen (VRAM).
I also recommend to look at Rainwarrior's and Tepple's examples at some point.
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: How to organize code? ("good code")

Post by Garth »

Sebastian_L, I think you're confusing "label" with "routine." A label is not a whole routine, or even part of one. It's just a way for the programmer to refer to an address in memory, often a routine's starting address.

Interrupt handlers, also called "interrupt-service routines" (ISRs for short) must always save the processor registers they use so they can return the registers to their previous values upon exit so the interrupted background program can resume where it left off, and not get messed up. I see the one routine there is saving Y, but it doesn't use Y; so although saving Y doesn't hurt anything, it does take unnecessary extra bytes and execution time. Nested NMI ISRs is no problem as long as the registers are saved on the stack. The only way an IRQ will cut in on an ISR, either an IRQ or an NMI ISR, is if you have a CLI instruction in the ISR. There is sometimes a place for this, but it's not common.

The status register is saved in the interrupt sequence and restored by RTI, so there's no need to do PHP and PLP in the ISR. The interrupt sequence also sets the interrupt-disable flag, so there's no need for an SEI instruction in the ISR.

The reset routine has an SEI in it. The reset sequence already sets the interrupt-disable flag, and if the reset signal also goes to the I/O ICs, they'll be reset so they won't produce interrupts until the software tells them to anyway; so the only reason to have an SEI in the reset routine is if you jump to it rather than reaching it from a hardware reset.

All of this and lots more is covered in the general-purpose (not games-specific) 6502 interrupts primer. (Enjoy my very outdated cartoons!)
http://WilsonMinesCo.com/ lots of 6502 resources
Sebastian_L
Posts: 17
Joined: Tue Feb 16, 2021 12:31 pm

Re: How to organize code? ("good code")

Post by Sebastian_L »

Thank you all for your answers!

Now I have some new questions:

1. I have 2 code parts from the complete codes. These parts have nearly the same content, but in different order. In the first is INX and in the second is bit $2002. What does bit $2002 mean? And if I call INX for the first time it is 0? Is the order important? I ask just to be clear.

2. This "RESET:" is called an interrupt?

3. What would happen if VRAM is not cleared? And how would I clear it?

4. What are nested NMI? And how would they look like?

5. Yes, I saw the value for the green screen in MyPalette, too. But why is the screen green? Where is the color called?

It might be some easy questions for you, but I am learning.

Thank you in advance and look forward to your answers!

Code: Select all

.org $C000 
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

Code: Select all

RESET:
   SEI					;SEI tells the code to ignore interrupts for the routine
   LDA #$00				;load 0 into the accumulator
   STA $2000			;disable the NMI
   STA $2001			;disable rendering
   STA $4010
   STA $4015
   LDA #$40				;loads HEX value 40 which is dec value 64
   STA $4017
   CLD					;disable decimal mode
   LDX #$FF				;loads value 255
   TXS					;initialize the stack
   bit $2002
I have started NES programming! - Please be kind :D
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: How to organize code? ("good code")

Post by Garth »

2. This "RESET:" is called an interrupt?
The reset signal is a type of interrupt. The reset routine is not an interrupt, but is instead there to service the interrupt.

4. What are nested NMI? And how would they look like?
I can't really think of a good reason to have an NMI interrupt the servicing of another NMI. However, on my workbench computer (sorry it isn't game-related), I always have NMI's interrupting the servicing of regular maskable interrupts. As long as all ISRs save, on the stack, any registers they use, and restore them before exiting, there's no problem. The 6502 interrupts primer addresses this too. If you're unsure about the function of stacks, go through the first few pages of my 6502 stacks treatise.
http://WilsonMinesCo.com/ lots of 6502 resources
Pokun
Posts: 2675
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: How to organize code? ("good code")

Post by Pokun »

Sebastian_L wrote: Thu Feb 18, 2021 2:44 am 1. I have 2 code parts from the complete codes. These parts have nearly the same content, but in different order. In the first is INX and in the second is bit $2002. What does bit $2002 mean? And if I call INX for the first time it is 0? Is the order important? I ask just to be clear.

2. This "RESET:" is called an interrupt?

3. What would happen if VRAM is not cleared? And how would I clear it?

4. What are nested NMI? And how would they look like?

5. Yes, I saw the value for the green screen in MyPalette, too. But why is the screen green? Where is the color called?

1. No INX just increases X by 1. X can be anything between $00-$FF when you turn on power. If you look carefully at the code you can see that X is loaded with $FF before the INX and is still $FF at the time INX is executed (the TXS between those doesn't change X). If you increment $FF by 1, it will wrap around back to $00. You could just as well use LDX $00 to load X with 0, or you could use A or Y instead. The only reason they do it in a strange way like this is to save ROM space with shorter instructions. It has a tendency to confuse newbies.

BIT is a pretty weird instruction that you don't have to worry too much about now, but a short description can be found here. Instead of BIT you might as well just use LDA, it doesn't matter in this case. It just have become common by NES programmers to use BIT with $2002. What does it do? When you read $2002 (using any read instruction including LDA, LDX, LDY or BIT) the PPU's vblank (bit 7 of $2002) flag is copied to the 6502's Negative flag (bit 7 of the processor register "P") which can be branched by BPL or BMI. This is used to see if you are in vblank or not in the init code. The first "BIT $2002" is used to clear the vblank flag, since just reading $2002 will cause the flag to be cleared. This is necessary because the flag could be either set or cleared when you power on the NES, and it needs to be cleared.

2. No that part in the quotes is a label. The code after it is the RESET interrupt handler or RESET interrupt-service routine (ISR) as Garth said. The way you used "LABEL" in your first question, sounded more like you were referring to the code, not the label on top of it.

3. You will have garbage on screen. Don't worry too much about that now, and follow the Nerdy Nights tutorial. But you can keep in mind later that you need to initialize all of VRAM to avoid garbage on screen. Nothing will break if you don't though.

4. By nested NMI you mean an NMI that happens while the PC still is in the NMI handler (NMI ISR).

5. Right after the "LoadPaletteLoop" label.


Garth wrote: Wed Feb 17, 2021 5:16 pm Nested NMI ISRs is no problem as long as the registers are saved on the stack. The only way an IRQ will cut in on an ISR, either an IRQ or an NMI ISR, is if you have a CLI instruction in the ISR. There is sometimes a place for this, but it's not common.

The status register is saved in the interrupt sequence and restored by RTI, so there's no need to do PHP and PLP in the ISR. The interrupt sequence also sets the interrupt-disable flag, so there's no need for an SEI instruction in the ISR.

The reset routine has an SEI in it. The reset sequence already sets the interrupt-disable flag, and if the reset signal also goes to the I/O ICs, they'll be reset so they won't produce interrupts until the software tells them to anyway; so the only reason to have an SEI in the reset routine is if you jump to it rather than reaching it from a hardware reset.
This is the first time I hear that a SEI isn't needed in the RESET ISR (but it makes sense since I recently learned that RESET works exactly like any other interrupt). Most NES games seems to use SEI in the init code. I guess it's still a good idea to put it there as you would need it if you jump to RESET as a means of software resetting.
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: How to organize code? ("good code")

Post by Oziphantom »

Pokun wrote: Thu Feb 18, 2021 6:37 am
Garth wrote: Wed Feb 17, 2021 5:16 pm Nested NMI ISRs is no problem as long as the registers are saved on the stack. The only way an IRQ will cut in on an ISR, either an IRQ or an NMI ISR, is if you have a CLI instruction in the ISR. There is sometimes a place for this, but it's not common.

The status register is saved in the interrupt sequence and restored by RTI, so there's no need to do PHP and PLP in the ISR. The interrupt sequence also sets the interrupt-disable flag, so there's no need for an SEI instruction in the ISR.

The reset routine has an SEI in it. The reset sequence already sets the interrupt-disable flag, and if the reset signal also goes to the I/O ICs, they'll be reset so they won't produce interrupts until the software tells them to anyway; so the only reason to have an SEI in the reset routine is if you jump to it rather than reaching it from a hardware reset.
This is the first time I hear that a SEI isn't needed in the RESET ISR (but it makes sense since I recently learned that RESET works exactly like any other interrupt). Most NES games seems to use SEI in the init code. I guess it's still a good idea to put it there as you would need it if you jump to RESET as a means of software resetting.
Well nobody learnt how to program the 6502 by reading a "Programming the 6502 for the NES", they will have either read a Commodore book or an Apple book. To which they have OS which will set up IRQs and hence when you start they will teach you to set the interrupt disable flag.
Also they will have had devkits which will need to run ROM first, to download to cart RAM etc, then switch and reset. So you would SEI in case those left IRQs enabled before they did a "RESET" jump. Also they probably used the FDS which has a BIOS it boots for dev and help.
Last edited by Oziphantom on Fri Feb 19, 2021 12:51 am, edited 1 time in total.
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: How to organize code? ("good code")

Post by Garth »

hence when you start they will teach you to clear the SEI flag.
Um...SEI is not a flag, but an instruction to SEt the Interrupt-disable flag I.
http://WilsonMinesCo.com/ lots of 6502 resources
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: How to organize code? ("good code")

Post by Oziphantom »

fair, my wording is poor.
Sebastian_L
Posts: 17
Joined: Tue Feb 16, 2021 12:31 pm

Re: How to organize code? ("good code")

Post by Sebastian_L »

Thank you all for your answers! They help me a lot. And so I can get every day better in programming the NES!
I have started NES programming! - Please be kind :D
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: How to organize code? ("good code")

Post by puppydrum64 »

I think the better reason for using INX to wrap around to 0 is to waste time to wait for the PPU to warm up, since INX takes 5 CPU cycles compared to just 2 for LDX
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: How to organize code? ("good code")

Post by Oziphantom »

inx takes 2 clocks, it saves a byte over LDX #00
Post Reply