-Playable Character
-Bit Testing
-Game States
-CPU Usage
NES Programming Tutorial
Published 8/6/2010 - Download Example ROM at Bottom
Here we will continue where we left off with #1: Sprite Animations.
BETTER VARIABLE ORGINIZATION
First, we need to reorganize our variables. It’s not really necessary here, but as our program gets bigger and stronger, we will run out of variables in zero space…so, this is how we will change it.
.rsset $0000 ;only pointers here
.rsset $0100 ;stack
.rsset $0200 ;sprites
.rsset $0300 ;sound
.rsset $0400+ ;other variables
This will keep it clean and we will avoid having to change stuff around later.
NPC VARIABLE SET UP
You’ll remember from the last write up that we used strings of variables like:
Enemy_Animation .rs 4 ;Animation Counters
Now, we get creative. Currently all the graphics update routines for the enemies and our playable character are the same (as you’ll see below). So, what we’re going to do is steal the variables for enemy 0 and use them for our playable character. So, now we’ll have our Playable Character as “Enemy 0” and our enemies as “Enemy 1,2,3”. Make sense? This way we can reuse all of our code and not have to make seperate routines that do the same thing for our playable character.
Now we will modify our random direction routine. This will come in handy later when we have sprite/sprite and sprite/background collisions. Basically, every frame we are putting a random number into the “random direction” that the sprite will take on when something causes it to change direction. Not only that, but when the counter resets back to zero, we query our playable character’s direction to further jumble up the direction routine. Observe:
random_table: ;values entered at random for direction changes
.db $01,$00,$03,$02,$02,$01,$00,$00,$01,$03
random:
INC random_direction1 ;random direction counter
LDA random_direction1
CMP #$0A
BNE .next
LDA enemy_direction ;playable character’s direction
STA random_direction1
.next
LDX random_direction1
LDA random_table,X
STA random_direction ;actual direction in the movement routine
RTS
PLAYABLE CHARACTER SET UP AND BIT TESTING
This info is pretty basic, so we’ll breeze through it. If we look at the main program, we can see that the playable character routine looks like this:
LDA #$00
STA enemy_number
STA enemy_ptrnumber
JSR strobe_controllers
JSR handle_input
JSR Enemys_Animation
JSR Enemys_Sprite_Loading
JSR update_enemy_sprites
You see that this is almost exactly the same as the enemy update routine that we used last time. The only changes are “strobe_controllers” and “handle_input”. First, “strobe_controllers”. I stole this routine from MetalSlime (I think) and he does an excellent job of going through it in his tutorials, so we’ll skip it here and assume that you know what it is doing.
Next, “handle_input”. This is where the PC differs from the NPC. All it does is tells the program where to move the sprites (rather than having the automatic random movement generator do it). However, here we use a little “Bit Testing”. I can’t remember if we covered this and I’m too lazy to look, so we’ll go through it here. There are three types of bit testing, AND, ORA, and EOR. Again, I’ll credit MetalSlime for explaining it to me a while ago, and I still haven’t found a better explination of it, so we’ll use it here. (Let me know if you don’t want me to use it and I’ll remove it.)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
AND
When you AND two values together, it compares them bit by bit. If the two bits are both 1, the result will be one. If either of the bits are 0, the result will be zero:
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
Example:
00111101 AND
11100110
——–
00100100
Notice that the 1’s in the result are the bit positions where the two original values BOTH have a 1. If either has a 0 bit, the result will have a zero bit.
AND is often used to test individual bits in a bitflag. You use 0’s to clear all bits except the one you want to check. If the result is 0, the bit you were checking was 0. If the result is non-zero, the bit you were checking was 1. For example, say we have a bitflag variable and we want to check bit 2.
lda bitflag
and #%00000100 ;clear all bits except bit 2. If the result is 0, bit 2 was clear.
beq .end
;if non-zero, the bit was set.
; the only way this could be non-zero is if bit 2 was 1. Because the only way
; to get a 1 in the result is if you have 1 AND 1. Since we only put a 1 in one
; position in our AND instruction, a non-zero result must mean that bitflag also has
; a 1in that position. Make sense?
;
; … bit set here, so do something
…
.end:
rts
AND is also used to clear bits. Most often used to turn off bits in a bitflag:
lda bitflag
and #%11110111 ;clear bit 3
sta bitflag ;save the result
;bit 3 of bitflag will be cleared. All other bits will remain unchanged.
; If they were 1 before, they will remain 1 (1 AND 1 = 1). If they
; were 0 before, they will remain 0 (0 AND 1 = 0).
ORA
When you ORA two values together, it compares them bit by bit. If the two bits are 0, the result will be 0. If either of the two bits are 1, the result will be 1.
0 ORA 0 = 0
0 ORA 1 = 1
1 ORA 0 = 1
1 ORA 1 = 1
Example:
11000110 ORA
00110111
——–
11110111
Notice we only get a 0 in the result in the bit positions where the original values BOTH have 0. If either of the bits are 1, the result is 1.
ORA is usually used to set bits in a bitflag:
lda bitflag
ora #%00001000 ;set bit 3
sta bitflag ;save
;here bit 3 will be set. If it was 0 before, it will be 1 now. If it was 1 before it will still be 1.
; All other bits will remain unchanged. If they were 0 before they will remain 0 (0 ORA 0 = 0).
; If they were 1 before they will remain 1 (1 ORA 0 = 1)
EOR
EOR compares bits and results in a 0 if the bits are the same, or 1 if they are different:
0 EOR 0 = 0
0 EOR 1 = 1
1 EOR 0 = 1
1 EOR 1 = 0
Example:
11010011 EOR
01110111
——–
10100100
We get a 0 in the result in bit positions where the original values are either both 0 or both 1.
Where AND is used to clear bits and ORA is used to set bits, EOR is used to toggle bits. In the EOR chart above, pay close attention to what happens if you EOR something with 1:
0 EOR 1 = 1
1 EOR 1 = 0
Notice the result is the opposite of the original value on the left, EORing by 1 toggles the bit. Off becomes on, on becomes off.
lda bitflag
eor #%00001000 ;toggle bit 3
sta bitflag
;here bit 3 will be toggled. If it was 1 before, it will be 0 now. If it was 0 before it will be 1 now.
; other bit will remain unchanged. If they were 0 before, they will remain 0 (0 EOR 0 = 0).
; If they were 1 before, they will remain 1 (1 EOR 0 = 1)
I use EOR in my controller reading routines to determine if buttons are newly pressed (ie, pressed this frame, but were unpressed last frame).
lda buttons_old ;last frame’s button states. 1 = pressed last frame, 0 = unpressed
eor #%11111111 ;toggle all bits. Now 1 = unpressed last frame, 0 = pressed
and buttons ;AND with *this* frame’s buttons. The result will only have 1’s where the button was unpressed last frame, but pressed this frame. (1 AND 1 = 1)
sta buttons_newly_pressed
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
These instructions are VERY powerful. You need to spend some time with them because we’ll use them a lot.
PLAYABLE CHARACTER MOVEMENT
Okay, back to the program. You’ll notice that half of the button’s don’t do anything. We’ll use them later so I left them in for now. You’ll also notice the order of buttons is such that if UP is pressed, it will skip the other direction buttons. You just have to pick the order that is best for you. Since all the D-PAD buttons do the same thing, we’ll just use this as an example:
ReadRight:
LDA joypad1 ;Right
AND #%00000001
BEQ ReadRightDone
JSR RightMovement
ReadRightDone:
RTS
Here we see the use of the AND function. So, this means that we are only looking at bit 0, which is the RIGHT button as described in our strobe_controllers routine. It tells the program to JSR the RightMovement routine.
RightMovement:
LDA #$02
STA enemy_direction ;put “RIGHT” into the direction that this meta sprite is traveling
LDA sprite_RAM+3 ;load sprite X position
CLC
ADC #enemy_speed ;add the speed
STA sprite_RAM+3 ;store the x position back in memory
INC Enemy_Animation ;incriment the animation counter
RTS
Pretty simple, right? Then all we do is run the graphics and position updates, same as the NPCs, and you’re done!
OTHER STUFF
The following is kind of an afterthought. I wasn’t sure when or if I would add these tools in, but we may as well here because this is a pretty simple lesson.
First, I got this from someone, who got it from someone, who stole it from someone. We all know that the NES is a computer. Not just a computer, but a 25 year old computer. It can only do so much per cycle before you over work the processor. So…how do we know how hard we are working the processor?? Bunnyboy does a pretty good job of explaining NMI timing vs. Main Program timing, we well skip the details here (you can also look on nesdev wiki or the sound tutorials). Basically, when NMI hits, you start a new “cycle”. The NTSC systems run at 60 Hz, meaning that you have 1/60th of a second to run your program without doing something special. This is why we push/pop our registers in NMI and include the commands to skip the NMI routine if we are busy doing something. Anyway, if you’re interested in exactly how much the NES can do, nesdev wiki has a pretty good table.
So, when we start a new NMI, first we run the graphics updates (in NMI) and then we run our main program. Then it goes back to sleep until NMI wakes it up again and it starts all over. What say we stick something at the end of our main program that presents a visual indicator of how far close to the next NMI our program takes us? The closer to the top of the screen, the shorter the program. Or if the indicator is close to the bottom, you’re close to over running your program and need to do something else. How about a white line across the screen? Easy to see and impliment:
showCPUUsageBar:
ldx #%00011111 ; sprites + background + monochrome (i.e. WHITE)
stx $2001
ldy #21 ; add about 23 for each additional line (leave it on WHITE for one scan line)
.loop
dey
bne .loop
dex ; sprites + background + NO monochrome (i.e. #%00011110)
stx $2001
rts
We just call this at the end of our main program and it will work. However, this will turn the graphics back on, so we have to put some simple commands in to skip it when we are updating backgrounds. It should be noted that our program doesn’t have any background that isn’t monochrome and is pretty simple, so the usage bar won’t show up yet. But as we get more and more complex routines, it will start to bounce around the top of the screen.
GAME STATES
This was covered briefly in another NA tutorial, but we’ll impliment it here because I farted and it smells like tacos. We are going to add “PAUSE”!! How exciting.
First, to cover a few things. Indirect jumps are pretty simple. You can’t “JSR” to a pointer address. This necessitates the following pair of commands:
JSR GameStateNMIIndirect
-and-
GameStateNMIIndirect: ;indirect jump to NMI routine
JMP [NMI_Pointer]
We have to do this for both the NMI and Main Program. This leaves a RTS in the stack for us to use when we get done with the code located at the pointer address.
Next, we have to declare “Game States”. You can pick whatever you want, but we are going to use this:
;00=Main Top Down View
;01=Paused
GameStates:
.word GameState0,GameState1
GameStateNMIs:
.word GameStateNMI0,GameStateNMI1
You can see where we’re going with this. We say load 00 or 01 into our game state, then JSR to a routine that loads the appropriate address into the “NMI_Pointer” or “Main_Pointer”. Then just use our indirect jump in the NMI/Main Program routines. So, let’s write the game state pointer update routine:
GameStateUpdate: ;load the game state and NMI pointers
LDA GameState
STA GameStateOld
ASL A ;multiply by two
TAX
LDA GameStates,x ;Load the Main Program Pointer
STA Main_Pointer
LDA GameStates+1,x
STA Main_Pointer+1
LDA GameStateNMIs,x
STA NMI_Pointer
LDA GameStateNMIs+1,x
STA NMI_Pointer+1
RTS
The new variables:
“GameState” – the current game state
“GameStateOld” – if this is different than the current game state, trigger the Update routine
Pointers – pointers as described above.
Great! So, now what? Well, if we take our main program and cut all the meat out and put it in a “GameState0” routine and add a “JSR GameStateIndirect” in its place, we can use this technique. Same thing in the NMI. Now we will have a “Main_Program” and a “NMI” program AND we can have several “meat parts” of code split out into the GameState0,GameState1,etc. and GameStateNMI0,GameStateNMI1,etc. that we can call as needed any time we want to change how stuff is handled.
Next, we need to put a call for our GameStateUpdate routine into the program somewhere. If we stick the following at the end of the Main_Program, we will have an automatic way of switching game states whenever we change the “GameState” variable. i.e. the code is stand alone! We don’t have to call a routine at the time that we want to change the GS, we just wait until all of the main program is finished, then change. This way we complete all of our JMP and JSR routines and don’t leave open return to scripts which would be bad. 🙂 So, now our NMI and main programs would look like this:
;———————————————————————-
;———————–START MAIN PROGRAM—————————–
;———————————————————————-
Forever:
INC sleeping ;wait for NMI
.loop
LDA sleeping
BNE .loop ;wait for NMI to clear out the sleeping flag
LDA #$01
STA updating_background ;this is for when you are changing rooms or something, not really needed here
;it will skip the NMI updates so as not to mess with your room loading routines
JSR strobe_controllers
JSR GameStateIndirect
LDA GameState
CMP GameStateOld
BEQ .next
JSR GameStateUpdate
.next
LDA #$00
STA updating_background
JMP Forever ;jump back to Forever, and go back to sleep
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;——–THE FUCKIN’ NMI ROUTINE, RECOGNIZE BITCH!————;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NMI:
PHA ;protect the registers
TXA
PHA
TYA
PHA
nmi_start:
LDA updating_background ;check to be sure that the main program isn’t busy
BNE skip_graphics_updates
LDA #$02
STA $4014 ;set the high byte (02) of the RAM address, start the transfer
JSR GameStateNMIIndirect
LDA #$00 ;tell the ppu there is no background scrolling
STA $2005
STA $2005
LDA #%00011110 ;enable sprites, enable background, no clipping on left side
STA $2001
LDA #$00
STA sleeping ;wake up the main program
STA updating_background+1
LDA updating_background+1 ;we put this in because the usage bar TURNS ON THE BACKGROUND
BNE skip_graphics_updates
JSR showCPUUsageBar
skip_graphics_updates:
PLA ;restore the registers
TAY
PLA
TAX
PLA
RTI ;return from interrupt
Simple, easy, and clean. Now all we have to do is put in code to change the variable “GameState” and we’re set. So, in our “handle_input” subroutine, let’s change START to this:
ReadStart:
LDA joypad1_pressed ; player 1 – start
AND #%00010000 ; only look at bit 5
BEQ ReadStartDone
LDA GameState
STA GameState+1
LDA #PauseState
STA GameState
ReadStartDone:
See, all it does is change the variable “GameState”. Now that it is different than “GameStateOld”, at the end of the main program, it will automatically change to the PauseState. (Note: PauseState is just a Constant that we declare so that we can change it once and not have to hunt through the program for every use of that state. This technique will save you a lot of time. I reccomend using it a lot.) We also see “GameState+1” for the first time. This is not really needed when you only have 2 states, but when you have like 10 and want to use pause in all of them you need a way to tell the pause state which state to un-pause to. Otherwise you could pause it on your top down view and when you un-pause, the program will switch to your map screen program and crash. That said, we write our PAUSED state NMI and main routines:
;———————————————————————————-
;———————–MAIN PROGRAM GS #$01-PAUSED——————————–
;———————————————————————————-
GameState1:
;ReadStart:
LDA joypad1_pressed ; player 1 – start
AND #%00010000
BEQ .ReadStartDone
LDA GameState+1
STA GameState
.ReadStartDone:
RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;——–NMI ROUTINE, PAUSED GAME STATE #$01-PAUSED———–;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
GameStateNMI1:
RTS
Now, it is just sitting there licking its nuts waiting for you to push START again. When you do, it loads the saved state in to “GameState” and the pointers change back to the Main Top Down routines and game play continues. If you really wanted to, you could add something creative here like a giant ball sack that pops up on the screen when it’s paused, but we’re just going to have the game freeze.
AND ANOTHER THING!!
This is more of a personal preference than anything. As you get a bigger and bigger program, you will have to use the “.include” and “.incbin” commands more and more. This is all well and good, but what about when you have like 10 .include and 15 .incbin commands? There’s a different file associated with each command…that’s a lot of crap to dig through. If you are like me and like to organize your stuff into folders to keep it organized, just do this:
.include “Subfiles/spritegraphics.asm”
-or-
.incbin “CHR_Files/SpriteMovement.chr” ;include the sprite graphics data
Bitchin!
END GAME
That’s about it!! Attached are all the files that go with this program. Open it, assemble it, take it apart, and learn well because we will build from here!
Any questions, post or PM me. Until next time…8==D.