Jump to content
IGNORED

Transitioning from Title Screen To Game Mode


Mallard Games

Recommended Posts

Back again,

 

I have some good news progress has been slow but, steady. Some minor bug fixes have been made that should improve the code a bit. Now comes the hard part, getting the game to transition from the title screen to the game mode. Here is the code I am using:

CheckState
	lda GameState							; check the gamestate
	cmp #STATE_TITLESCREEN					; has it changed
	beq	StateCheckDone						; if not then branch
	cmp SelectBank2							; bandswitch to bank 2
	nop
	nop
	nop
	jmp GameMode							; jump to game mode
StateCheckDone
	rts

It's almost exactly like the bankswitching code except it's a jump instead of jump to subroutine and there are check to see if we need to perform the jump based on what the current state of the game is. The issue is when I try to perform the bankswitch the game crashes on Stella with "Illegal Instruction". My guess is the 2600 doesn't like me abandoning the CheckState routine altogether if the game state is changed, but at that point the subroutine doesn't need to finish since the rest of the code doesn't need to execute at all. I must say in all seriousness though this is starting to hurt my head.

 

homer simpson rubbing head GIF

 

Source: https://www.dropbox.com/sh/ku4ip913q718h0n/AABpPC0h8maEuBAL4U7dtl3Oa?dl=0

Link to comment
Share on other sites

I'm not sure what the point of the second comparison and the nop instructions are, but if you want to jump from a subroutine without a rts, you need to clear the address the jsr added to the stack first. Before your "jmp GameMode" add two of the "pla" instruction to clear off both bytes added to the stack from the jsr:

 

    pla
    pla
    jmp GameMode

 

Link to comment
Share on other sites

11 hours ago, Karl G said:

I'm not sure what the point of the second comparison and the nop instructions are, but if you want to jump from a subroutine without a rts, you need to clear the address the jsr added to the stack first. Before your "jmp GameMode" add two of the "pla" instruction to clear off both bytes added to the stack from the jsr:

 


    pla
    pla
    jmp GameMode

 

If your talking about

	cmp SelectBank2							; bankswitch to bank 2

It bankswitches to the second bank where the game mode code is. The bankswitching takes so time to happen so I added the 3 nop instructions to pass the time. I have removed them since.

Link to comment
Share on other sites

When you switch bank, the program counter doesn't change, so the CPU keeps executing code from the next address (in the new bank). So you must ensure to switch to the correct bank and at the correct address too.

 

In the rom you posted above, after bankswitching you are at address $10f8 which happens to be in the middle of another subroutine in that bank.
When you reach the "rts" at $111d, the return address is obviously wrong. From then on, the program flow is all messed up and the game eventually crashes.

01.png.d7575bcfb59001fa110cda5858f9a956.png

02.png.c2b98827ef8a1084c82e53a6ad8d93b2.png

Link to comment
Share on other sites

13 hours ago, alex_79 said:

When you switch bank, the program counter doesn't change, so the CPU keeps executing code from the next address (in the new bank). So you must ensure to switch to the correct bank and at the correct address too.

 

In the rom you posted above, after bankswitching you are at address $10f8 which happens to be in the middle of another subroutine in that bank.
When you reach the "rts" at $111d, the return address is obviously wrong. From then on, the program flow is all messed up and the game eventually crashes.

01.png.d7575bcfb59001fa110cda5858f9a956.png

02.png.c2b98827ef8a1084c82e53a6ad8d93b2.png

Fixed the RTS not being cancelled.

CheckState
	lda GameState							; check the gamestate
	cmp #STATE_TITLESCREEN					; has it changed
	beq	StateCheckDone						; if not then branch
	cmp SelectBank2							; bankswitch to bank 2
	pla										; clear low byte of jump address from stack
	pla										; clear high byte of jump address from stack
	jmp GameMode							; jump to game mode
StateCheckDone
	rts

Tried moving that bit of code so it is right above reset, and it stops the crashing but now it just sticks to the title screen.

; ***********************************************************************************
; Start of Bank 1
; Title Screen Bank
; ***********************************************************************************
    seg Bank_1                                                                  ; end of uninitialized segment - start of ROM Bank 1
    org $1000                                                                   ; start ROM at $1000
    rorg $1000                                                                   ; start ROM at $1000
	JUMP_TABLE

CheckState
	lda GameState							; check the gamestate
	cmp #STATE_TITLESCREEN					; has it changed
	beq	StateCheckDone						; if not then branch
	cmp SelectBank2							; bankswitch to bank 2
	pla										; clear low byte of jump address from stack
	pla										; clear high byte of jump address from stack
	jmp GameMode							; jump to game mode
StateCheckDone
	rts

	; we'll call the start of our program "Reset".
Reset

	sei										; Disable Any Interrupts
	cld										; Clear BCD math bit.
	ldx #$FF								; put X to the top...
	txs										; ...and use it reset the stack pointer
											; Clear RAM and all TIA registers
	lda #0									; Put Zero into A, X is at $FF
Clear
	sta 0,x									; Now, this doesn't mean what you think...
	dex										; decrement X (decrease X by one)
	bne Clear								; if the last command resulted in something
											; that's "N"ot "Equal" to Zero, branch back
											; to "Clear"

	; **************************************************************************
	; One time initializations
	; **************************************************************************
	lda #BLUE
	sta COLUBK								; set the background color to sky blue
	lda #BROWN
	sta COLUPF								; set the playfield color to brown
	lda #WHITE
	sta COLUP1								; set crosshair color to white
	lda INTIM								; get a random value from INTIM
	sta Random								; use it to seed the random number generator

	lda #STATE_TITLESCREEN					; 0 = title screen state
	sta GameState							; store it

AttarctMode

TitleScreenLoop
	jsr VerticalSync						; perform vertical sync
	jsr CheckJoystick						; check for joystick input
	jsr CheckState							; check if the gamestate has changed
	jsr TitleScreen_Kernal					; jump to title screen kernal
	jsr OverScan							; (3 13) jump to OverScan Routine
	jmp TitleScreenLoop						; jump to start of loop

TitleScreen_Kernal
WaitForVblankEnd_TitleScreen
	bit TIMINT								; load timer...
	bpl WaitForVblankEnd_TitleScreen		; killing time if the timer's not yet zero
	ldy #31									; 16 scanline of padding on the top
TopPaddingLoop:
    sta WSYNC								; wait for sync
    dey										; decrement y
    bne TopPaddingLoop						; loop until the top of the screen is padded 16 scanlines
	ldy #64									; Y is going to hold how many lines we have to do
											; ...we're going to count scanlines here. theoretically
											; since this example is ass simple, we could just repeat
											; the timer trick, but often its important to know
											; just what scan line we're at.
	ldx #02									; interlaced
	sta WSYNC								; We do a WSYNC just before that so we don't turn on
	sta VBLANK								; End the VBLANK period with the zero
	;*********************** Scan line Loop
ScanLoop_TitleScreen
	sta WSYNC								; (3 0) Wait for the previous line to finish
	lda pf0dataleft-1,y						; (4 4) load left pf0 data into the acumulator
	sta PF0									; (3 7) and store it
	lda pf1dataleft-1,y						; (4 11) load left pf1 data into the acumulator
	sta PF1									; (3 14) and store it
	lda pf2dataleft-1,y						; (4 17) load left pf2 data into the acumulator
	sta PF2									; (3 21) and store it
	lda pf0dataright-1,y					; (4 25) load right pf0 data into the acumulator
	sta PF0									; (3 28) and store it *MUST OCCUR AFTER CYCLE 28*
	SLEEP 3									; (3 31) sleep for 3 cycles
	lda pf1dataright-1,y					; (4 35) load right pf1 data into the acumulator
	sta PF1									; (3 38) and store it *MUST OCCUR AFTER CYCLE 38*
	SLEEP 5									; (5 43) sleep for 5 cycles
	lda pf2dataright-1,y					; (4 47) load right pf2 data into the acumulator
	sta PF2									; (3 50) and store it *MUST OCCUR AFTER CYCLE 50*
	dex										; (2 52) decrement x
	bne ScanLoop_TitleScreen				; (2/3 54/55) and repeat if we're not finished with all the scanlines.
	ldx #02									; (2 56/57) load 2 into x register
	dey										; (2 58/59) subtract one off the line counter thingy
	bne ScanLoop_TitleScreen				; (2/3 60/61 61/62) and repeat if we're not finished with all the scanlines.
	sty PF0									; clear PF0 register to prevent garbage from appearing on the screen
	sty PF1									; and PF1...
	sty PF2									; and PF2 just in case
	ldy #31									; (2 62/63 63/64) 16 scanlines of padding on the bottom
BottomPaddingLoop:
    sta WSYNC								; (3 0) wait for sync
    dey										; (2 2) decrement y
    bne BottomPaddingLoop					; (2/3 4/5) loop until the bottom of the screen is padded 16 scanlines
	rts
    ; *************************************
    ; Data Section Follows
    ; *************************************

Source Code: https://www.dropbox.com/sh/zjbuabjg4i0p7c9/AAAXbjqu6ODRiGrQqi8_sR5Aa?dl=0 

Link to comment
Share on other sites

21 hours ago, Mallard Games said:

Fixed the RTS not being cancelled.


CheckState
	lda GameState							; check the gamestate
	cmp #STATE_TITLESCREEN					; has it changed
	beq	StateCheckDone						; if not then branch
	cmp SelectBank2							; bankswitch to bank 2
	pla										; clear low byte of jump address from stack
	pla										; clear high byte of jump address from stack
	jmp GameMode							; jump to game mode
StateCheckDone
	rts

Not really: as your code comment says, as soon as the "cmp SelectBank2" instruction is executed, the second bank is switched in. The following 3 instructions in that "CheckState" routine are never executed. (as they're in the first bank which has been switched out and is not visible anymore by the cpu) Instead, the cpu will execute whatever is at the same address in the second bank.

 

So whatever you want to be executed after the bankswitching, you must put it in the destination bank and aligned so to be exactly at the address the cpu will fetch the next instruction from.

 

  • Like 1
Link to comment
Share on other sites

1 hour ago, alex_79 said:

Not really: as your code comment says, as soon as the "cmp SelectBank2" instruction is executed, the second bank is switched in. The following 3 instructions in that "CheckState" routine are never executed. (as they're in the first bank which has been switched out and is not visible anymore by the cpu) Instead, the cpu will execute whatever is at the same address in the second bank.

 

So whatever you want to be executed after the bankswitching, you must put it in the destination bank and aligned so to be exactly at the address the cpu will fetch the next instruction from.

 

The question is how do I align the code so it's "exactly at the address the cpu will fetch the next instruction from". Also I moved the 2 pla's up so they do execute and removed the jmp that will never execute or succeed if it does.

CheckState
	lda GameState							; check the gamestate
	cmp #STATE_TITLESCREEN					; has it changed
	beq	StateCheckDone						; if not then branch
	pla										; clear low byte of jump address from stack
	pla										; clear high byte of jump address from stack
	cmp SelectBank2							; bankswitch to bank 2
StateCheckDone
	rts

 

Link to comment
Share on other sites

Just make sure the CMP SelectBank2 code exists at the same Rom locations in both the source (bank1) and destination (bank2).  Since the Bankswitch macro is the same length in both banks, you can put it there.  And since GameMode exists just after the Bankswitch macro in bank2, it flows right to it.

 


    seg Bank_1
    org $1000
    rorg $1000
    JUMP_TABLE

Exit_Title_Screen
    cmp SelectBank2

CheckState
    lda GameState
    cmp #STATE_TITLESCREEN
    beq    StateCheckDone
    pla
    pla
    jmp Exit_Title_Screen
StateCheckDone
    rts

 


    seg Bank_2
    org $2000
    rorg $3000
    JUMP_TABLE2

    cmp SelectBank2
;    ;---------------------
;    ; Start New Game
GameMode
;    jsr StartNewGame

 

Link to comment
Share on other sites

21 hours ago, Nukey Shay said:

... And since GameMode exists just after the Bankswitch macro in bank2, it flows right to it.

You still need a "jmp GameMode" to adjust the program counter MSB, as the macros in "bankswitch.h" rely on that.

seg Bank_2
    org $2000
    rorg $3000
    JUMP_TABLE2

    cmp SelectBank2
    jmp GameMode
;    ;---------------------
;    ; Start New Game
GameMode
;    jsr StartNewGame

 

Link to comment
Share on other sites

  • 2 weeks later...
On 9/8/2019 at 4:59 AM, alex_79 said:

You still need a "jmp GameMode" to adjust the program counter MSB, as the macros in "bankswitch.h" rely on that.


seg Bank_2
    org $2000
    rorg $3000
    JUMP_TABLE2

    cmp SelectBank2
    jmp GameMode
;    ;---------------------
;    ; Start New Game
GameMode
;    jsr StartNewGame

 

What do you mean by

Quote

program counter MSB

By the way moved the subroutine into bank 3 and added it to the macro. Still am having issues.

 

Source: https://www.dropbox.com/sh/d65mqk4crtc4xr3/AACJb9jisk6PcyJu-Ndw4gZja?dl=0

Link to comment
Share on other sites

The PC ("program counter") is the 16-bit address that the 6507 processor is gathering the next program instruction from.  This 16-bit address is created from 2 8-bit bytes...the MSB ("most-significant byte"), and the LSB ("least-significant byte").

 

Example: address $301B has a MSB of $30 and a LSB of $1B.

 

When a JMP instruction is executed, the 6507 simply changes the PC to the 16-bit argument specified...and program flow continues from there.

 

Alex is correct...

Because your bankswitch macro is using the return address pushed to the stack to gather the specific bank # to return to, a JMP must be executed some time beforehand so that the program counter had been corrected to whatever base address had been used for each banks' RORG.  To the hobbled 6507, the upper 3 bits of MSB are invisible...the exact reason that the 2600 can only "see" a maximum of 4k Rom memory at any time.

 

But the bankswitch macro is depending on those 3 bits that the 6507 cannot see.

Link to comment
Share on other sites

On 9/22/2019 at 12:40 AM, Nukey Shay said:

The PC ("program counter") is the 16-bit address that the 6507 processor is gathering the next program instruction from.  This 16-bit address is created from 2 8-bit bytes...the MSB ("most-significant byte"), and the LSB ("least-significant byte").

 

Example: address $301B has a MSB of $30 and a LSB of $1B.

 

When a JMP instruction is executed, the 6507 simply changes the PC to the 16-bit argument specified...and program flow continues from there.

 

Alex is correct...

Because your bankswitch macro is using the return address pushed to the stack to gather the specific bank # to return to, a JMP must be executed some time beforehand so that the program counter had been corrected to whatever base address had been used for each banks' RORG.  To the hobbled 6507, the upper 3 bits of MSB are invisible...the exact reason that the 2600 can only "see" a maximum of 4k Rom memory at any time.

 

But the bankswitch macro is depending on those 3 bits that the 6507 cannot see.

I did everything you stated and still can't get it to transition. What's strange is despite the macro ensuring everything lines up correctly it still jumps to what seems like the middle of the input subroutine. What's strange is the pc is at 5xxx which doesn't line up what what the listing file. 

 

Source Code: https://www.dropbox.com/sh/5snzytblleo9zzi/AABhxkPUVp2V_GZmItcrLZOIa?dl=0

Link to comment
Share on other sites

11 hours ago, Mallard Games said:

I did everything you stated and still can't get it to transition. What's strange is despite the macro ensuring everything lines up correctly it still jumps to what seems like the middle of the input subroutine. What's strange is the pc is at 5xxx which doesn't line up what what the listing file. 

 

Source Code: https://www.dropbox.com/sh/5snzytblleo9zzi/AABhxkPUVp2V_GZmItcrLZOIa?dl=0

 

Have you single-stepped with the debugger to watch what is happening?  That usually gives you a big clue as to what is wrong.

  • Like 1
Link to comment
Share on other sites

On 9/25/2019 at 11:13 PM, Andrew Davie said:

 

Have you single-stepped with the debugger to watch what is happening?  That usually gives you a big clue as to what is wrong.

 

On 9/25/2019 at 11:47 AM, Mallard Games said:

What's strange is despite the macro ensuring everything lines up correctly it still jumps to what seems like the middle of the input subroutine. What's strange is the pc is at 5xxx which doesn't line up what what the listing file. 

Yes, that's how I came to the conclusion above. Funny thing is if you look into the listing file the very last used address is:

      8  4ffe		       00 10		      .word.w	InitSystem

I was exhausted today so I couldn't look into further. Hopefully tomorrow I can do some more testing and see if I can figure it out.

Link to comment
Share on other sites

Finally got a chance to look at the source code again and could not see any glaring issues. What I thought was the code jumping into no mans land was actually a mistake on my part. In fact it is jumping into one of the Vertical Sync code. It then jumps back into bank 1. What's is very strange to me is despite the code setting the state correctly, it never jumps to the proper bank despite the bankswitching code being there like it's completely ignoring it.

Link to comment
Share on other sites

7 hours ago, Mallard Games said:

Finally got a chance to look at the source code again and could not see any glaring issues. What I thought was the code jumping into no mans land was actually a mistake on my part. In fact it is jumping into one of the Vertical Sync code. It then jumps back into bank 1. What's is very strange to me is despite the code setting the state correctly, it never jumps to the proper bank despite the bankswitching code being there like it's completely ignoring it.

Step through the code with the debugger and watch what it is actually doing.

 

Link to comment
Share on other sites

2 minutes ago, Mallard Games said:

Question how do I emulate the fire button being pressed on player 1's controller while in the debugger?

put a breakpoint at the button-reading code, then press stella's fire button (space for me) and click single-step with the mouse.
Or, just step through the button-reading code and modify the accumulator manually to set the value to "fire button pressed".

 

Link to comment
Share on other sites

1 hour ago, Andrew Davie said:

put a breakpoint at the button-reading code, then press stella's fire button (space for me) and click single-step with the mouse.
Or, just step through the button-reading code and modify the accumulator manually to set the value to "fire button pressed".

 

Or go to the I/O tab and click the appropriate action on the relevant controller.

Link to comment
Share on other sites

  • 2 weeks later...

How do i make a .sym file, the tutorial states:

Quote

dasm kernel.asm -lkernel.txt -f3 -v5 -okernel.bin

Which only creates a list file and Stella is complaining about there being no symbol file to load... ?

 

UPDATE: rc7e helped me in the ZeroPage stream with getting a sym file created. The answer for anyone else who needs it is to append -s like so:

dasm kernel.asm -lkernel.lst -skernel.sym -f3 -v5 -okernel.bin

Now here's what I've figured out, after the button is pressed at address 5116:

	bmi CheckFireDone

it jumps to address 506e:

506e	lsr
506f	lsr
5070	lsr
5071	tax
5072	cmp $1ff6,x
5075

Which is actually in the middle of my bankswitching code...

Return_Code
        tsx
        lda $02,x
        lsr
        lsr
        lsr
        lsr
        lsr
        tax
	cmp SelectBank1,x							; bank 1
        rts

So it does try to bankswitch but then ends up in 109a

10a9	TitleScreen_Kernal
10a9	WaitForVblankEnd_TitleScreen
10a9	bit	TIMINT
10ac	bpl	WaitForVblankEnd_TitleScreen
10ae	ldy	#31
10b0	TopPaddingLoop
10b0	sta	WSYNC
10b2	dey

Which makes absolutely no sense to me...

 

UPDATE: scratch that i think i have an idea of what's going on. Short version: Super Epic Fail on my part!

Oh Brother Reaction GIF by reactionseditor

Now the problem is how can i change it so the jumps inside CheckState in bank 3 work? or just how do i refactor my code??? ☠️?☠️

Edited by Mallard Games
Link to comment
Share on other sites

  • 2 months later...

Hi,

 

Back from a long hiatus, and now am refreshed and ready to start working on my project again. I reworked the code so all the bankswitching now works. The only issue now is for some strange reason now moving left and right is borked! Here is what I have so far, I don't see any jumps that would skip that bit of code. Any ideas?

Source-12_25_19.zip

Link to comment
Share on other sites

Sub_PositionObjects

You are repositioning the duck sprite, but the crosshair never gets a chance to be repositioned.  Leaping between banks in this manner means you only have 1 return address.

 

So in your main loop, you could try JSR PositionDuck followed by JSR PositionCrosshair.

Then change Sub_PositionObjects to look like this:

Sub_PositionDuck

    lda DuckX
    ldx #0
    jmp PositionSprite

 

Sub_PositionCrosshair
    sta WSYNC
    lda CrosshairX
    ldx #1
    jmp PositionSprite

 

Each object has it's own return vector.  Keep in mind that RTS instructions are not used in your subroutines when switching banks, the return code in the bankswitch macro leaps back to where the JSR was originally called from (i.e. the main game loop in this case).  So the crosshair positioning was never executed.  The duck was repositioned, then the return code sent it back to the main game loop.

Link to comment
Share on other sites

BTW you -really- ought to keep an eye on the debugger.  By single-stepping through the joystick routine (you can select which directions are pressed in the debugger), you'd see that bit of code was working fine.  Therefore, the problem was something else...in this case, the repositioning subroutine.

Link to comment
Share on other sites

In your Sub_PositionObjects routine, you are doing a jmp to PositionSprite, when it should be jsr. When I changed both jmp's to jsr's, right and left movement worked fine for me.

 

Sub_PositionObjects
	lda DuckX								; (2 114) load the duck's x position into the accumulator
	ldx #0									; (2 101) set the sprite to be positioned as player 0
	jmp PositionSprite						; (3 104)jump to our Position Sprite Subroutine to position player 0
	sta WSYNC								; (2 106)wait for sync
	lda CrosshairX							; (2 108) load the crosshair's x position into the accumulator
	ldx #1									; (2 110) set the sprite to be positioned as player 1
	jmp PositionSprite						; (3 113) jump to our Position Sprite Subroutine to position player 0
	rts

 

Link to comment
Share on other sites

What Nukey said about using the debugger to find out where your code is reaching. it can be a bit tricky with multibank projects to get the hang of, but for illustration purposes, here is how I was able to tell your joystick positioning code was working correctly.

 

First, I added a label "MoveRight" to the line after the right movement detection would have been skipped to see if it is reaching that code:

 

SkipMoveLeft
	lda #%10000000							; (2 81) was the joystick pushed right?
	bit SWCHA								; (3 84)
	bne SkipMoveRight						; (3 87) if it wasn't skip to SkipMoveRight
MoveRight
	lda CrosshairX							; (2 89) load the crosshairs x position
	cmp #RIGHTBOUNDRY						; (3 92) does it intersect the right boundry?
	bcs SkipMoveRight						; (3 95) if the crosshair doesn't intersect the right boundry
	inc CrosshairX							; (2 97) then increment the crosshairs x position

 

Next, I set it as a breakpoint in the debugger by specifying both the label and the bank. note that what you think of as bank 3 Stella knows as bank 2, since numbering starts at 0:

 

breakif {_bank==2 && pc==MoveRight}

 

I then hit the backtick (`) to exit the debugger, and pressed the right arrow. This brought up the debugger again at my breakpoint. Stepping through the code from there, you can see that your CrossHairX variable gets incremented as expected.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...