Mallard Games Posted September 4, 2019 Share Posted September 4, 2019 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. Source: https://www.dropbox.com/sh/ku4ip913q718h0n/AABpPC0h8maEuBAL4U7dtl3Oa?dl=0 Quote Link to comment Share on other sites More sharing options...
+Karl G Posted September 4, 2019 Share Posted September 4, 2019 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 Quote Link to comment Share on other sites More sharing options...
Mallard Games Posted September 5, 2019 Author Share Posted September 5, 2019 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. Quote Link to comment Share on other sites More sharing options...
alex_79 Posted September 5, 2019 Share Posted September 5, 2019 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. Quote Link to comment Share on other sites More sharing options...
Mallard Games Posted September 5, 2019 Author Share Posted September 5, 2019 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. 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 Quote Link to comment Share on other sites More sharing options...
alex_79 Posted September 6, 2019 Share Posted September 6, 2019 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. 1 Quote Link to comment Share on other sites More sharing options...
Mallard Games Posted September 6, 2019 Author Share Posted September 6, 2019 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 Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 7, 2019 Share Posted September 7, 2019 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 Quote Link to comment Share on other sites More sharing options...
alex_79 Posted September 8, 2019 Share Posted September 8, 2019 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 Quote Link to comment Share on other sites More sharing options...
Mallard Games Posted September 22, 2019 Author Share Posted September 22, 2019 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 Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 22, 2019 Share Posted September 22, 2019 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. Quote Link to comment Share on other sites More sharing options...
Mallard Games Posted September 25, 2019 Author Share Posted September 25, 2019 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 Quote Link to comment Share on other sites More sharing options...
+Andrew Davie Posted September 26, 2019 Share Posted September 26, 2019 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. 1 Quote Link to comment Share on other sites More sharing options...
Mallard Games Posted September 27, 2019 Author Share Posted September 27, 2019 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. Quote Link to comment Share on other sites More sharing options...
Mallard Games Posted October 2, 2019 Author Share Posted October 2, 2019 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. Quote Link to comment Share on other sites More sharing options...
+Andrew Davie Posted October 3, 2019 Share Posted October 3, 2019 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. Quote Link to comment Share on other sites More sharing options...
Mallard Games Posted October 3, 2019 Author Share Posted October 3, 2019 7 hours ago, Andrew Davie said: Step through the code with the debugger and watch what it is actually doing. Question how do I emulate the fire button being pressed on player 1's controller while in the debugger? Quote Link to comment Share on other sites More sharing options...
+Andrew Davie Posted October 3, 2019 Share Posted October 3, 2019 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". Quote Link to comment Share on other sites More sharing options...
+stephena Posted October 3, 2019 Share Posted October 3, 2019 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. Quote Link to comment Share on other sites More sharing options...
Mallard Games Posted October 12, 2019 Author Share Posted October 12, 2019 (edited) 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! 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 October 12, 2019 by Mallard Games Quote Link to comment Share on other sites More sharing options...
Mallard Games Posted December 25, 2019 Author Share Posted December 25, 2019 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 Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted December 26, 2019 Share Posted December 26, 2019 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. Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted December 26, 2019 Share Posted December 26, 2019 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. Quote Link to comment Share on other sites More sharing options...
+Karl G Posted December 26, 2019 Share Posted December 26, 2019 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 Quote Link to comment Share on other sites More sharing options...
+Karl G Posted December 26, 2019 Share Posted December 26, 2019 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. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.