I've been watching NES dev videos on YT for more than a year to satisfy my curiosity on how games actually worked. Once I got some confidence with assembly I started tearing down the famicom game, convoy no nazo.
Basically, I'm learning everything at once and it's going great. Today I'm starting to understand how to load a new column of tiles to the nametable while scrolling.
One thing I'm not sure I understand is I'm finding useless loops happening; is this because it's waiting for the next vblank since there's nothing else to process?
Did you know? You get a 1-Up at 10000, 30000, 50000... points
Newb decompiling first game; Convoy No Nazo
Moderator: Moderators
-
- Posts: 1565
- Joined: Tue Feb 07, 2017 2:03 am
Re: Newb decompiling first game; Convoy No Nazo
wait for next frame is very common. I.e if you want to slowly fade, or you want to transition the screen, you wait for the next frame. Sometimes you just want to pause the game for a while to let them read the text or something. So you just wait for X vblanks were every 60 = 1 second.
Re: Newb decompiling first game; Convoy No Nazo
Waiting each frame ensures a consistent frame-rate. Otherwise the game logic would run as fast as possible, and the action would slow down when more stuff is being calculated.
Re: Newb decompiling first game; Convoy No Nazo
In Contra, once the game logic is executed and the rti instruction is called to exit from the interrupt, there is a forever loop that repeatedly adds the "frame counter" (a value incremented every frame) to an value used as a random number. By adding the frame counter repeatedly without worrying about any overflow, you essentially have a random number to use in game logic, especially because the number of cpu operations is different each vertical blanking interval.
Code: Select all
RANDOM_NUM = $34
FRAME_COUNTER = $1a
; run between NMI interrupts after interrupt code finishes
; loop forever updating RANDOM_NUM
forever_loop:
lda FRAME_COUNTER ; load frame counter
adc RANDOM_NUM ; add the frame number to RANDOM_NUM
sta RANDOM_NUM ; update RANDOM_NUM to new result
jmp forever_loop
nmi_start:
...
[game code]
...
rti ; return to forever_loop until nmi is triggered again
; rti pops the processor flags and then the program counter
; then starts executing at that location
- jeffythedragonslayer
- Posts: 344
- Joined: Thu Dec 09, 2021 12:29 pm
Re: Newb decompiling first game; Convoy No Nazo
Are fades/transitions on the NES usually done in their own little special game-loop-with-nested-wait-for-vblank loop inside it or by special "game mode" enum variables that temporarily disable controller input until the fade/transition is complete?Oziphantom wrote: ↑Mon Jan 30, 2023 10:30 pm wait for next frame is very common. I.e if you want to slowly fade, or you want to transition the screen, you wait for the next frame. Sometimes you just want to pause the game for a while to let them read the text or something. So you just wait for X vblanks were every 60 = 1 second.
Re: Newb decompiling first game; Convoy No Nazo
Dunno about "usually", but Full Quiet and Garbage Pail Kids put fades in the ordinary game loop using four variables.
1. Current fade level: $00 for fully dark, $40 for normal, $80 for fully light
2. Fade target level: same value domain
3. Fade rate: number of units per frame
4. Dirty things: bitmask of things that need to be redrawn sometime once there's space in the VRAM buffer
Each frame, if the target level is less than the current level, add the fade rate to the current level. Otherwise, if the target level is greater than the current level, subtract the fade rate from the current level. If this causes the high nibble to change, set dirty things to schedule re-uploading the palette. Disregarding controller input is separate. Some actors' state machines wait for the level to reach some target before doing something.
1. Current fade level: $00 for fully dark, $40 for normal, $80 for fully light
2. Fade target level: same value domain
3. Fade rate: number of units per frame
4. Dirty things: bitmask of things that need to be redrawn sometime once there's space in the VRAM buffer
Each frame, if the target level is less than the current level, add the fade rate to the current level. Otherwise, if the target level is greater than the current level, subtract the fade rate from the current level. If this causes the high nibble to change, set dirty things to schedule re-uploading the palette. Disregarding controller input is separate. Some actors' state machines wait for the level to reach some target before doing something.