How do I have to structure my code?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

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

How do I have to structure my code?

Post by Sebastian_L » Sun Feb 28, 2021 10:13 am

Hello together,

I made a litte progress, but the problem is that I got stuck with my code.

I am trying to split the game logic to the mainloop and the other parts to the NMI.

So I really need your help. I am new to programming. For you it will be simple and my questions maybe silly... But I want to learn it.

1. General question: What in my code should I change/remove/improve? If possible, please with example of my code.
2. Is the Controller Part game logic or is it NMI part? Could you please give examples of game logic?
3. Should the initialisation and MainGameLoop written before NMI?

Thank you for your answers! I hope that you don't get mad, because I ask these questions.

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


;; 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
   
   LDX #$00				;loads zero into x
;;ready to start loop
ClearMemomryLoop:
;;clear out memory etc.
   LDA #$00				;loads zero into the accumulator

   STA $0000,x			;store accumulator 0 into address $0000+x
   STA $0100,x			;store accumulator 0 into address $0100+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
   LDA #$FF				
   STA $0200,x			;store accumulator 255 into address $0200+x (sprite tiles)
   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

InitializeTitleScreen:
   ;draw title screen

LoopTitleScreen:
   jmp InitializeGameplay

InitializeGameplay:
   LDA #$80				;load decimal value 128 (=8x16)
   STA $0200			;store it to sprite 1's y address
   LDA #$80				;optional, load a different hex value for the x value
   STA $0203			;and store it to sprite 1's x address
   LDA #$00
   STA $0201
   STA $0202

LoopGameplay:
   jsr LatchController
   jmp LoopGameplay
   

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

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


;; 7. Includes and data tables
MyPalettes:
   ;backgroud
   .db $0F,$11,$17,$0a, $0F,$31,$01,$1c, $0F,$3c,$01,$14, $0F,$21,$39,$03
   ;sprites
   .db $0F,$27,$07,$30, $0F,$06,$05,$31, $0F,$2a,$2c,$16, $0F,$13,$14,$00


;; 8. 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
   
   .incbin "mario.chr"	;this includes the graphics for the sprites in the assembly of the game
I have started NES programming! - Please be kind :D

User avatar
tokumaru
Posts: 12054
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: How do I have to structure my code?

Post by tokumaru » Sun Feb 28, 2021 10:28 am

Sebastian_L wrote:
Sun Feb 28, 2021 10:13 am
1. General question: What in my code should I change/remove/improve?
I just replied in your other thread explaining the problems with the current organization of your code.
2. Is the Controller Part game logic or is it NMI part?
That depends on where you want to put your game logic. If you want the game logic in the main thread, simply read and process input there, not in the NMI handler.
3. Should the initialisation and MainGameLoop written before NMI?
The order in which they are written doesn't matter, only the execution order matters. You need the Reset code to transition into the first game mode, so once the initialization is done, you can either JMP to the initialization of the first game mode, or write it immediately after the Reset code and let the CPU flow naturally into it, like you're already doing.

As for the NMI handler, that's and independent piece of code which has well defined starting (NMI:) and ending (RTI) points, so it can be placed anywhere, just like subroutines.

Keep in mind that there is no one single correct way to organize your code, there are literally thousands of valid ways to arrange an NES program. The one I suggest is one that I think is easy to follow, and is versatile enough to be expanded into a full game.

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

Re: How do I have to structure my code?

Post by Sebastian_L » Mon Mar 01, 2021 2:54 am

tokumaru wrote:
Sun Feb 28, 2021 10:28 am
Sebastian_L wrote:
Sun Feb 28, 2021 10:13 am
1. General question: What in my code should I change/remove/improve?
I just replied in your other thread explaining the problems with the current organization of your code.
2. Is the Controller Part game logic or is it NMI part?
That depends on where you want to put your game logic. If you want the game logic in the main thread, simply read and process input there, not in the NMI handler.
3. Should the initialisation and MainGameLoop written before NMI?
The order in which they are written doesn't matter, only the execution order matters. You need the Reset code to transition into the first game mode, so once the initialization is done, you can either JMP to the initialization of the first game mode, or write it immediately after the Reset code and let the CPU flow naturally into it, like you're already doing.

As for the NMI handler, that's and independent piece of code which has well defined starting (NMI:) and ending (RTI) points, so it can be placed anywhere, just like subroutines.

Keep in mind that there is no one single correct way to organize your code, there are literally thousands of valid ways to arrange an NES program. The one I suggest is one that I think is easy to follow, and is versatile enough to be expanded into a full game.
Thank you for your answer, things get a bit clearer every time. Just answered you in the other thread.
I have started NES programming! - Please be kind :D

Post Reply