Page 1 of 2
Problems Loading a NameTable to show Game Over Screen
Posted: Mon Jan 16, 2017 3:50 pm
by hundonostudy
Hi All,
So in my pong game, I'm trying to learn how to transition from one background screen to another. I'm able to load a title screen, and switch from the title screen to my playfield screen without issues. The code I use to do that is here.
Code: Select all
LDA #%00000000 ;Turn Screen Off
STA $2001
LoadPlayScreen: ; load playfield screen
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
LDA #$00
STA pointerLo ; put the low byte of the address of background into pointer
LDA #HIGH(playfield)
STA pointerHi ; put the high byte of the address into pointer
LDX #$00 ; start at pointer + 0
LDY #$00
PlayFieldOutsideLoop:
PlayFieldInsideLoop:
LDA [pointerLo], y ; copy one background byte from address in pointer plus Y
STA $2007 ; this runs 256 * 4 times
INY ; inside loop counter
CPY #$00
BNE PlayFieldInsideLoop ; run the inside loop 256 times before continuing down
INC pointerHi ; low byte went 0 to 256, so high byte needs to be changed now
INX
CPX #$04
BNE PlayFieldOutsideLoop ; run the outside loop 256 times before continuing down
But when I use basically the same code to switch from playfield screen to the gameover screen (after a winning score has been achieved) the code doesn't function properly. The screen goes blank and stays blank. Now I expect it to go blank because I turn the screen off to allow time to draw the background,
But the screen doesn't come back on. My NMI handler should turn it back on because it has a "clean up" piece of code that re-enables everything.
Here is the code I use to draw the game over screen. Again basically the same code I use to draw the playfield after I switch from title. Allthough the screen stays blank, if I check FCEUX's Nametable viewer, it shows that screen has indeed drawn
Code: Select all
LDA #%00000000 ;Turn Screen Off
STA $2001
LoadGameOverScreen: ;Load Game Over Screen
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
LDA #$00
STA pointerLo ; put the low byte of the address of background into pointer
LDA #HIGH(gameover)
STA pointerHi ; put the high byte of the address into pointer
LDX #$00 ; start at pointer + 0
LDY #$00
GameOverOutsideLoop:
GameOverInsideLoop:
LDA [pointerLo], y ; copy one background byte from address in pointer plus Y
STA $2007 ; this runs 256 * 4 times
INY ; inside loop counter
CPY #$00
BNE GameOverInsideLoop ; run the inside loop 256 times before continuing down
INC pointerHi ; low byte went 0 to 256, so high byte needs to be changed now
INX
CPX #$04
BNE GameOverOutsideLoop ; run the outside loop 256 times before continuing down
And here is my NMI code
Code: Select all
NMI:
PHA ; back up registers (important)
TXA
PHA
TYA
PHA
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
Score:
LDA gamestate
CMP #STATETITLE
BEQ CleanUp
JSR DrawScore
CleanUp:
LDA #%10010000 ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1 - ;;PPU clean up section, so rendering the next frame starts properly.
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 ;;;all graphics updates done by here, run game engine
JSR ReadController1 ;;get the current button data for player 1
JSR ReadController2 ;;get the current button data for player 2
GameEngine:
StateTitle:
LDA gamestate
CMP #STATETITLE
BNE StateGameOver ;;game is displaying title screen
JMP EngineTitle
StateGameOver:
LDA gamestate
CMP #STATEGAMEOVER
BNE StatePlaying ;;game is displaying ending screen
JMP EngineGameOver
StatePlaying:
LDA gamestate
CMP #STATEPLAYING
BNE GameEngineDone
JMP EnginePlaying ;;game is playing
GameEngineDone:
JSR UpdateSprites ;;set ball/paddle sprites from positions
PLA ; restore regs and exit
TAY
PLA
TAX
PLA
RTI
Any thoughts on this would be greatly appreciated here. I'm really banging my head on this. Seeing as how switching screens is pretty important I really want to understand the concept well. Also, I have attached the full code of my PONG game to this post as well if anyone would like to see it to help me determine my problem. There is also a screen shot in there of the nametable viewer in FCEUX showing the GameOver screen.
Thank You So Much
Hundo
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Mon Jan 16, 2017 4:00 pm
by tokumaru
Have you used FCEUX's debugger to trace the code? If something isn't behaving as expected, the best thing to do is set a breakpoint and step through the code to see what's actually happening.
I normally set breakpoints on writes to $ff (a memory position that's hardly ever used in my programs), so I can just put an sta $ff right before the thing I want to debug.
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Mon Jan 16, 2017 4:01 pm
by koitsu
Gut feeling says this is being caused by $2000/2002/2005/2006 manipulation in such an order that certain internal VRAM addresses are pointing to a location different than you expect, especially if you're seeing the screen in FCEUX's nametable viewer but it isn't visibly rendering on-screen. This is a very common problem for homebrewers and complex to understand, which is why it's one of the largest pages on the wiki (do not let the name of the wiki page make you think it specifically has to do with "screen panning" -- it's more complicated than that). I'm sorry to say you'll need to read it (not skim it), although someone on the forum may be a able to write a more terse version for you:
https://wiki.nesdev.com/w/index.php/PPU_scrolling
Edit: add $2000 too.
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Mon Jan 16, 2017 4:14 pm
by hundonostudy
tokumaru wrote:Have you used FCEUX's debugger to trace the code? If something isn't behaving as expected, the best thing to do is set a breakpoint and step through the code to see what's actually happening.
I normally set breakpoints on writes to $ff (a memory position that's hardly ever used in my programs), so I can just put an sta $ff right before the thing I want to debug.
I do use the debuger, however I've never thought to use $ff to set a marker to make the debugger easier to use. Thank You! That is a super helpful tip.
I stepped through the code with the debugger and it looks to be behaving as it should be. So I'm still confused.
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Mon Jan 16, 2017 4:23 pm
by tokumaru
I think I found the problem. You have 3 states, but they don't behave the same way. The title screen state works because it waits for the time to change the background, and when it does change the background it switches to the gameplay state, meaning the background is drawn only once. But the game over state is different, it immediately draws a new background, and doesn't change states. Since your NMI handler calls the current state every frame, the game over state is redrawing the background over and over again. The NMI tries to enable rendering, but when this state is called it turns rendering off again and draws the background again... that's why the screen is blank.
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Mon Jan 16, 2017 4:39 pm
by tokumaru
You should try to format all game states in a similar way, because currently they're kinda all over the place and don't follow a set pattern. The title screen is drawn during reset, and its handler draws the gameplay background. The gameplay handler has the game logic but doesn't draw any backgrounds, and the game over handler only draws its background over and over.
I suggest you have a reset routine for each state, that draws its background sets the necessary variables, and then sets the variable that indicates the active state. Another important thing to do is disable NMIs (or have them don't do anything, which's better) during state transitions, otherwise the NMI handler might interrupt the transitions and screw everything up.
the different states could follow a standard like this:
Code: Select all
ResetTitle:
;draw background
;set state to 'TITLE'
UpdateTitle:
;wait for start to jump to ResetGameplay
jsr GameEngineDone
ResetGameplay:
;draw background
;set state to 'GAMEPLAY'
UpdateGameplay:
;run game logic
;jump to ResetGameOver on death
jsr GameEngineDone
ResetGameOver:
;draw background
;set state to 'GAMEOVER'
UpdateGameOver:
;wait for timer to expire before jumping to ResetTitle
jsr GameEngineDone
To prevent the NMI from doing anything that could ruin the loading process of the states, you could maybe have a 'TRANSITIONING' state, that when active cause the NMI handler to not do a sprite DMA or touch any PPU registers. Playing audio is OK.
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Mon Jan 16, 2017 5:56 pm
by hundonostudy
tokumaru wrote:I think I found the problem. You have 3 states, but they don't behave the same way. The title screen state works because it waits for the time to change the background, and when it does change the background it switches to the gameplay state, meaning the background is drawn only once. But the game over state is different, it immediately draws a new background, and doesn't change states. Since your NMI handler calls the current state every frame, the game over state is redrawing the background over and over again. The NMI tries to enable rendering, but when this state is called it turns rendering off again and draws the background again... that's why the screen is blank.
Thank you Tokumaru! That was the problem. Not sure why I missed it. I think my brain fried out a bit after staring at the code for 2 hours. With your suggestion I fixed the problem very quickly!
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Mon Jan 16, 2017 6:00 pm
by hundonostudy
tokumaru wrote:You should try to format all game states in a similar way, because currently they're kinda all over the place and don't follow a set pattern. The title screen is drawn during reset, and its handler draws the gameplay background. The gameplay handler has the game logic but doesn't draw any backgrounds, and the game over handler only draws its background over and over.
I suggest you have a reset routine for each state, that draws its background sets the necessary variables, and then sets the variable that indicates the active state. Another important thing to do is disable NMIs (or have them don't do anything, which's better) during state transitions, otherwise the NMI handler might interrupt the transitions and screw everything up.
the different states could follow a standard like this:
Code: Select all
ResetTitle:
;draw background
;set state to 'TITLE'
UpdateTitle:
;wait for start to jump to ResetGameplay
jsr GameEngineDone
ResetGameplay:
;draw background
;set state to 'GAMEPLAY'
UpdateGameplay:
;run game logic
;jump to ResetGameOver on death
jsr GameEngineDone
ResetGameOver:
;draw background
;set state to 'GAMEOVER'
UpdateGameOver:
;wait for timer to expire before jumping to ResetTitle
jsr GameEngineDone
To prevent the NMI from doing anything that could ruin the loading process of the states, you could maybe have a 'TRANSITIONING' state, that when active cause the NMI handler to not do a sprite DMA or touch any PPU registers. Playing audio is OK.
I am totally going to rewrite my code for this little pong game. As the general pieces of it started to come together I realized it was something of a hacked together mess. I was also right on the verge of having it work, so I figured I may as well press on despite it being a shit show. Now that its done and all the major pieces are working I'm going to re-write it following the guidelines you have suggested. I'm sure it will be a good learning exercise. Also after browsing through the literature here, I've come to the conclusion that handling the logic in the NMI as the original tutuorial suggests is a bad thing, so I'm going to try to change that as well. I'll do my best, but I'm sure you guys will be hearing from me : ) Thanks again for the help!
Hundo
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Mon Jan 16, 2017 6:03 pm
by hundonostudy
koitsu wrote:Gut feeling says this is being caused by $2000/2002/2005/2006 manipulation in such an order that certain internal VRAM addresses are pointing to a location different than you expect, especially if you're seeing the screen in FCEUX's nametable viewer but it isn't visibly rendering on-screen. This is a very common problem for homebrewers and complex to understand, which is why it's one of the largest pages on the wiki (do not let the name of the wiki page make you think it specifically has to do with "screen panning" -- it's more complicated than that). I'm sorry to say you'll need to read it (not skim it), although someone on the forum may be a able to write a more terse version for you:
https://wiki.nesdev.com/w/index.php/PPU_scrolling
Edit: add $2000 too.
Thank you Koitsu for the suggestion. I will be sure to read it. Currently in school trying to become a Doctor, so I'll fit this paper in between Neuroanatomy and Biochemistry. Lol this classic NES stuff is WAY to interesting and addicting ; )
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Tue Jan 17, 2017 8:13 am
by Pokun
hundonostudy wrote:
after browsing through the literature here, I've come to the conclusion that handling the logic in the NMI as the original tutuorial suggests is a bad thing, so I'm going to try to change that as well.
Hundo
The All in NMI way is a valid approach that some comercial games use (like Super Mario Bros). But I think that keeping logic in the Reset handler and graphic updates in the NMI is a clean way to separate the two.
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Tue Jan 17, 2017 8:40 am
by tokumaru
Pokun wrote:The All in NMI way is a valid approach that some comercial games use (like Super Mario Bros).
Not without some undesirable side effects, such as music slowdown and raster effect glitches on lag frames. I heard that in some cases the game can even crash (due to a missed sprite 0 hit, I'm assuming).
But I think that keeping logic in the Reset handler and graphic updates in the NMI is a clean way to separate the two.
It's the most robust setup when it comes to handling lag frames, because you get to prioritize some tasks that must run at 60Hz even in case of slowdown, while with the other methods (all in NMI or all in Reset) everything slows down ("slowing down" being an euphemism when it comes to raster effects, since when slowed down they can cause severe visual glitches).
I'd only recommend the "all in X" methods for games that are guaranteed to never slow down, which is normally only the case of single screen or screen by screen games with a controlled number of active objects. Anything more dynamic than that is bound to run into lag frames every once in a while.
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Tue Jan 17, 2017 6:25 pm
by hundonostudy
tokumaru wrote:You should try to format all game states in a similar way, because currently they're kinda all over the place and don't follow a set pattern. The title screen is drawn during reset, and its handler draws the gameplay background. The gameplay handler has the game logic but doesn't draw any backgrounds, and the game over handler only draws its background over and over.
I suggest you have a reset routine for each state, that draws its background sets the necessary variables, and then sets the variable that indicates the active state. Another important thing to do is disable NMIs (or have them don't do anything, which's better) during state transitions, otherwise the NMI handler might interrupt the transitions and screw everything up.
the different states could follow a standard like this:
Code: Select all
ResetTitle:
;draw background
;set state to 'TITLE'
UpdateTitle:
;wait for start to jump to ResetGameplay
jsr GameEngineDone
ResetGameplay:
;draw background
;set state to 'GAMEPLAY'
UpdateGameplay:
;run game logic
;jump to ResetGameOver on death
jsr GameEngineDone
ResetGameOver:
;draw background
;set state to 'GAMEOVER'
UpdateGameOver:
;wait for timer to expire before jumping to ResetTitle
jsr GameEngineDone
To prevent the NMI from doing anything that could ruin the loading process of the states, you could maybe have a 'TRANSITIONING' state, that when active cause the NMI handler to not do a sprite DMA or touch any PPU registers. Playing audio is OK.
Hi Tokumaru! I'm about to start re-writing this monstrosity! Just a quick question before I start. Would you recommend putting these "Reset Routines" Before the Infinite Game Loop so that they're not infinitely repeating? Just looking for some best practices to work with before I get started.
Thanks!
Hundo
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Tue Jan 17, 2017 7:41 pm
by tokumaru
hundonostudy wrote:Just a quick question before I start. Would you recommend putting these "Reset Routines" Before the Infinite Game Loop so that they're not infinitely repeating?
How they're laid out in the ROM doesn't really matter, as long as they're out of the flow. You could have something like this:
Code: Select all
Reset:
;- clear RAM, initialize the PPU, APU, sub-systems, etc.
jsr InitializeTitle
;- clear 'frame ready' flag
;- enable NMIs
MainLoop:
;- do tasks common to all modes
;- call the 'UpdateXXXXX' routine for the current mode (or none if in 'TRANSITION')
;- set 'frame ready' flag and wait for it to be cleared by the NMI handler
jmp MainLoop
;----------------------------------------------------------------
NMI:
;- if the game mode is 'TRANSITION', do absolutely nothing to the PPU
;- if the game mode is any other, and the 'frame ready' flag is set,
;- process pending VRAM updates, enable rendering and clear the 'frame ready' flag
;- update audio
rti
;----------------------------------------------------------------
InitializeTitle:
;- set game mode to 'TRANSITION'
;- disable rendering and draw background
;- initialize title screen variables
;- set game mode to 'TITLE'
rts
UpdateTitle:
;- if 'start' is pressed, call 'InitializeGameplay'
rts
;----------------------------------------------------------------
InitializeGameplay:
;- set game mode to 'TRANSITION'
;- disable rendering and draw background
;- initialize gameplay variables
;- set game mode to 'GAMEPLAY'
rts
UpdateGameplay:
;- process input
;- move game objects
;- buffer VRAM updates
;- in case of death, call 'InitializeGameOver'
rts
Just looking for some best practices to work with before I get started.
The structure above is just a suggestion (I myself don't do it quite like that), you have to create a structure you feel comfortable with, as this can be done a million different ways and each programmer prefers to do things differently. The important thing is that you're consistent in your choices. ASM gives us a lot of freedom, there are no rules on how to implement subroutines, pass parameters, declare variables, initialize stuff, and so on... For this reason, it's important that you consistently follow a set of rules of your liking to do these basic things, so you can easily follow the structure of your program even when it grows to tens of thousands of lines.
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Wed Jan 18, 2017 1:39 pm
by hundonostudy
tokumaru wrote:hundonostudy wrote:Just a quick question before I start. Would you recommend putting these "Reset Routines" Before the Infinite Game Loop so that they're not infinitely repeating?
How they're laid out in the ROM doesn't really matter, as long as they're out of the flow. You could have something like this:
Code: Select all
Reset:
;- clear RAM, initialize the PPU, APU, sub-systems, etc.
jsr InitializeTitle
;- clear 'frame ready' flag
;- enable NMIs
MainLoop:
;- do tasks common to all modes
;- call the 'UpdateXXXXX' routine for the current mode (or none if in 'TRANSITION')
;- set 'frame ready' flag and wait for it to be cleared by the NMI handler
jmp MainLoop
;----------------------------------------------------------------
NMI:
;- if the game mode is 'TRANSITION', do absolutely nothing to the PPU
;- if the game mode is any other, and the 'frame ready' flag is set,
;- process pending VRAM updates, enable rendering and clear the 'frame ready' flag
;- update audio
rti
;----------------------------------------------------------------
InitializeTitle:
;- set game mode to 'TRANSITION'
;- disable rendering and draw background
;- initialize title screen variables
;- set game mode to 'TITLE'
rts
UpdateTitle:
;- if 'start' is pressed, call 'InitializeGameplay'
rts
;----------------------------------------------------------------
InitializeGameplay:
;- set game mode to 'TRANSITION'
;- disable rendering and draw background
;- initialize gameplay variables
;- set game mode to 'GAMEPLAY'
rts
UpdateGameplay:
;- process input
;- move game objects
;- buffer VRAM updates
;- in case of death, call 'InitializeGameOver'
rts
Just looking for some best practices to work with before I get started.
The structure above is just a suggestion (I myself don't do it quite like that), you have to create a structure you feel comfortable with, as this can be done a million different ways and each programmer prefers to do things differently. The important thing is that you're consistent in your choices. ASM gives us a lot of freedom, there are no rules on how to implement subroutines, pass parameters, declare variables, initialize stuff, and so on... For this reason, it's important that you consistently follow a set of rules of your liking to do these basic things, so you can easily follow the structure of your program even when it grows to tens of thousands of lines.
Thank you for the suggestion Toku. Can you help me out a little with the frame ready flag? Not sure what you want me to write there. Should I write a piece of code that is Incrementing a variable to use as a wait counter for example, or should I simply create a variable that is set to #$00 when not ready and have the NMI update that flag to #$01 at the beggining of NMI and #$00 at the end of NMI to use as an indicator for when NMI starts and stops?
Re: Problems Loading a NameTable to show Game Over Screen
Posted: Wed Jan 18, 2017 4:56 pm
by Pokun
You could go with either a frame counter (counts NMIs) or a flag. In Tokumaru's example he is using a flag. Setting and then clearing the flag within the NMI is pointless though. Once NMI has happened it won't go back to the main loop until it reaches the rti instruction.
You set the flag in the main loop and clear it in the NMI. You also need to wait (by looping) in main until the flag is cleared again (which indicates an NMI has happened). In the NMI handler you clear it at the end of NMI to indicate that an NMI has happened.
Waiting like this ensures that there are no more than one iteration of the main loop on one NMI.
In the example it looks like the flag also indicates that there are graphic updates to be done in vblank, so you can make graphic updates in the NMI conditional based on the state of the flag (if it's set, you update, else you skip updates that NMI). This ensures that if the NMI happens before the main loop has finished (i.e. has not set the flag) the NMI won't do any graphical updates. So it won't draw partly done screens that are still being processed in the main loop logic.
Edit: Fixed mixups.