Start Menu
Moderator: Moderators
Start Menu
How do you typically place your start menu in your code? My start menu is just your typical 'Press Start' so I figured I would just put a small loop at the beginning that ends when the user presses start and from then on, the real code is done unless of course the game ends in which case it all just jumps back to that loop.
What are the negatives to this approach?
What are the negatives to this approach?
Re: Start Menu
The downside is that this is not easily expandable. Most games have more screens/states than just a title screen and the main game, and not all of them are as simple as "press a button". Some screens are almost as complex as an actual game, requiring music, VBlank updates and such. It would be a good idea to implement a better "module" system, that allows you to navigate through screens without limitations.67726e wrote:What are the negatives to this approach?
In my games, each module (which is a separate ASM file) has an initialization area followed by a loop. To move from one module to the next I just jump to the initialization of the next module when required (in the case of a title screen that would be when "start" is pressed), but I also have time to some cleaning (e.g. fading out) before terminating the current module. I can also have sub-modules, which run inside a parent module (something like a menu that you can call during the game), in which case I JSR to it so I can return when it's done.
Since I want to be able to run crucial updates in case of lag frames, I want the possibility of running specialized NMIs for each module, but not all modules are CPU hogs, so these can get by with the traditional "wait for VBlank" technique. To accommodate both situations, my NMI routine can either call a specialized NMI routine if one is defined, or just signal that an NMI has happened, and each module is free to use the method that best suits it.
Just from thinking of this, have a screen uploaded to the PPU and then have a "select option" program running. I qould guess that a list of two-byte values for X-Y of the sprite that points to what your selecting would work, and then just loop through them from what the input is until it wraps around two the beginning again. You could even have it in a subroutine that returns a value in the A register to ave even more compact code, so the menus are from the subroutine, but the values returned get computed in the main games program. 
Just an idea. I have never done this though.
Just an idea. I have never done this though.
Re: Start Menu
That is actually what I was talking about doing.tokumaru wrote:In my games, each module (which is a separate ASM file) has an initialization area followed by a loop.67726e wrote:What are the negatives to this approach?
- MetalSlime
- Posts: 186
- Joined: Tue Aug 19, 2008 11:01 pm
- Location: Japan
I use a system similar to Tokumaru's. The different sections of my game (title screen, gameplay, gameover, pause, etc) are divided into "gamestates". Each gamestate lives in its own file and has an init routine and a main routine (which calls the init routine). I store the addresses of all the main routines in a table and I have a subroutine set_gamestate which takes a gamestate id in A and uses it to grab an address from the table and stick it in a pointer variable (gamestate_ptr).
My main game loop just says:
and each gamestate ends with "jmp forever", completing the loop.
So for a start menu I might have:
Having a gamestate handler like this has a little bit of overhead but it's easy for me to read and debug. Adding a new gamestate is as easy as adding a line to the address table.
My main game loop just says:
Code: Select all
forever:
jmp (gamestate_ptr)So for a start menu I might have:
Code: Select all
;table of addresses to main gamestate routines.
; set_gamestate reads from this table to set gamestate_ptr
gamestate_table:
.word startmenu
.word gameplay
;other addressesCode: Select all
;--------------startmenu.asm-----
startmenu_init:
;draw title screen
;turn screen on
;start music
jmp gamestate_init_done ;this routine adds 3 to gamestate_ptr. To skip init for future iterations
startmenu:
jsr startmenu_init
;wait a frame
;read the controller
lda controller1
and #START_BUTTON ;bitmask that tests start button bit
beq end ;if not pressed, we're done. loop back to forever
;cleanup code (e.g. play sfx, fadeout loop)
;change to gameplay gamestate
end:
jmp foreverJMP absolute on RAM over JMP Indirect on ROM to gain 2 cycles (at 1 byte of ram cost)?Memblers wrote:In some of my programs I supplied an NMI routine, in addition to the init routine and loop. You could change how your NMI routine operates, but instead I just point the vector to 3 bytes of RAM, putting $4C (JMP absolute) followed by the address, so it can be redirected easily.
Just asking, might use it on my IRQ routine.
I'm going to second what everyone else has said so far.
When I first started programming I made title screens/ intros as the outer most level of a loop per your example. However, this scales HORRIBLY and it kinda makes the code more difficult to understand. Later, when I tired to add more to the start screen, because cool intros kick ass, the loop was a huge mess of code surrounding a much smaller, better structured chunk of code.
It certainly makes sense to break it off into a separate parts or states. Even if your title screen is a typically simple, you could reuse the code with very little effort. Or later, if you want something more, you can alter it with ease.
I tend to rewrite parts of my programs often, so I find it better to break them up into smaller isolated chunks, so if I change a section I don't have to worry about how it effects the rest of the code too much.
When I first started programming I made title screens/ intros as the outer most level of a loop per your example. However, this scales HORRIBLY and it kinda makes the code more difficult to understand. Later, when I tired to add more to the start screen, because cool intros kick ass, the loop was a huge mess of code surrounding a much smaller, better structured chunk of code.
It certainly makes sense to break it off into a separate parts or states. Even if your title screen is a typically simple, you could reuse the code with very little effort. Or later, if you want something more, you can alter it with ease.
I tend to rewrite parts of my programs often, so I find it better to break them up into smaller isolated chunks, so if I change a section I don't have to worry about how it effects the rest of the code too much.
If you had an indirect JMP on ROM and an NMI fired between you setting the first and the second bytes of the address in RAM, the program would jump to an invalid location and most likely crash. To avoid that you would have to disable NMIs every time you modified the address.Wave wrote:JMP absolute on RAM over JMP Indirect on ROM to gain 2 cycles (at 1 byte of ram cost)?Memblers wrote:In some of my programs I supplied an NMI routine, in addition to the init routine and loop. You could change how your NMI routine operates, but instead I just point the vector to 3 bytes of RAM, putting $4C (JMP absolute) followed by the address, so it can be redirected easily.
The other option is to keep the whole instruction in RAM, so that you can temporally change the JMP to RTI before you modify the address, and finally change the RTI back to JMP when the address is valid. Modifying the opcode is faster than disabling/enabling NMIs through the PPU.
Well, I'm thinking about using it on IRQs, that will only be fired if loaded properly.tokumaru wrote:If you had an indirect JMP on ROM and an NMI fired between you setting the first and the second bytes of the address in RAM, the program would jump to an invalid location and most likely crash. To avoid that you would have to disable NMIs every time you modified the address.Wave wrote:JMP absolute on RAM over JMP Indirect on ROM to gain 2 cycles (at 1 byte of ram cost)?Memblers wrote:In some of my programs I supplied an NMI routine, in addition to the init routine and loop. You could change how your NMI routine operates, but instead I just point the vector to 3 bytes of RAM, putting $4C (JMP absolute) followed by the address, so it can be redirected easily.
The other option is to keep the whole instruction in RAM, so that you can temporally change the JMP to RTI before you modify the address, and finally change the RTI back to JMP when the address is valid. Modifying the opcode is faster than disabling/enabling NMIs through the PPU.
Yeah, for IRQs you can safely have just the address in RAM. Even if they fired at unpredictable times they are easily disabled and enabled with SEI and CLI.Wave wrote:Well, I'm thinking about using it on IRQs, that will only be fired if loaded properly.
Since you are planning on using IRQs for various types of effects, it makes sense to give each effect its own routine so that they can do their thing as quickly as possible.
Yeah, that redirection trick is especially helpful with IRQ (where overhead starts to matter more, if it's triggering often enough). It makes other optimizations easier too, like in many IRQ routines you only need to save/restore the accumulator.
Even if there's only one condition to check (there's usually more than that), with only one NMI/IRQ routine in the best case it's still 7 cycles minimum to load/compare/branch. Compared to just burning up 3 cycles with a JMP to a dedicated routine.
Even if there's only one condition to check (there's usually more than that), with only one NMI/IRQ routine in the best case it's still 7 cycles minimum to load/compare/branch. Compared to just burning up 3 cycles with a JMP to a dedicated routine.
Here's what the outer loop of something like Concentration Room looks like:
Code: Select all
callTitleScreenProc:
asl a
tax
lda titleScreenProcs+1,x
pha
lda titleScreenProcs,x
pha
php
rti
titleScreenProcs: .addr start1PlayerGame, start2PlayerGame, startOptions
reset:
sei
ldx #0
stx $2000
stx $2001
dex
txs
; Omitted: initialize APU ports, then wait for $2002 to be negative twice
titleloop:
jsr showTitleScreen ; this returns number of selection in A
jsr callTitleScreenProc
jmp titleloop
I'm unsure of the original quesiton, but my main loop looks like this :
Pretty straightforward. The "start menu" is handled in the "Run Player" routine, which, if the menu is to be opened, don't exit but simply executes until the menu is closed again and then the game continues normally.
I like this natural way of coding things in the order they execute.
Some people prefer having a "jmp *" style main loop (i.e. everything in NMI), and do their logic based on a series on numbers which tells the NMI routine what to do. This is more complicated for me, as you have to carefully manage that series of numbers, and this is slower/less efficient. I might be wrong though.
Code: Select all
stageLoop
jsr ResetStage
- jsr WaitNMI
jsr RunPlayer
jsr RunObjects ; Objects are enemies, items, etc...
jsr DisplaySprites
lda GameOverFlag
beq _gameOver ;Those flags breaks the main loop
lda StageBeatenFlag
bne _stageBeaten
jmp -
_gameOver
jsr FadeOut
jsr DisplayGameOverScr
jmp Reset
_stageBeaten
jsr PlayVictoryMusic
jsr FadeOut
inc StageNmr
jmp _stageLoop
I like this natural way of coding things in the order they execute.
Some people prefer having a "jmp *" style main loop (i.e. everything in NMI), and do their logic based on a series on numbers which tells the NMI routine what to do. This is more complicated for me, as you have to carefully manage that series of numbers, and this is slower/less efficient. I might be wrong though.
Useless, lumbering half-wits don't scare us.