Thomas Jentzsch Posted December 11, 2018 Share Posted December 11, 2018 (edited) Atari's bank switching (F8, F6, F4) is pretty straight forward. Nevertheless it can become quite complicated if you want to switch between banks a lot. Therefore I wrote myself some macros, which make things much easier. Here is the basic version, just to get the idea: ; TJ's Atari standard bankswitching macros, here tailored for F4 (adapt for F8 or F6) BANK_SIZE = $1000 ; Use at the start of each bank, required by JMP_LBL macro MAC START_BANK ; bank number BANK_NUM SET {1} BANK_ORG SET $8000 + BANK_NUM*BANK_SIZE BANK_RORG SET $1000 + BANK_NUM*BANK_SIZE*2 SEG code ORG BANK_ORG, $55 RORG BANK_RORG ECHO "Start of bank", [BANK_NUM]d, ", ORG", BANK_ORG, ", RORG", BANK_RORG ; verify start addresses: START{1} = (START & $fff) | BANK_RORG ; optional Space for RAM+ ; DS.B $100, "#" ENDM ; a convenience macro, put at the end of every bank MAC END_BANK RORG $ff4 ds 8, 0 ; .word (START & $fff) | BANK_RORG ; RESET .word 0 ; BRK (unused) ENDM ; Insert code for executing the bankswitch, ; should be put at the very end of each bank, right before the hotspots MAC SWITCH_BANK ORG BANK_ORG + $ff4 - BANKSWITCH_SIZE RORG BANK_RORG + $ff4 - BANKSWITCH_SIZE SwitchBank = (. & $fff) | $1000 pha txa pha lda $fff4,y rts BANKSWITCH_SIZE = ((. & $fff) | $1000) - SwitchBank ENDM ; Define a label which can be used by JMP_LBL macro ;Example: ; DEF_LBL Foo MAC DEF_LBL ; address {1} {1}_BANK = BANK_NUM ENDM ; Jump to a label in other or same bank. The assembler will take care if the ; code has to bankswitch or not. ; Example: ; JMP_LBL Foo MAC JMP_LBL ; address IF {1}_BANK != BANK_NUM ; convenience IF ldy #{1}_BANK lda #>({1}-1) ldx #<({1}-1) jmp SwitchBank ; always at the same address in each bank ELSE jmp {1} ENDIF ENDM So if you put these macros into your code and use DEF_LBL to define the labels you want to call, you can jump with JMP_LBL to these labels without having to care for bankswitching.Disadvantages: a bit slower than dedicated bank switching code uses A, X and Y registers, so you cannot transfer parameters (but with more code this can be reduced to one register). BTW: This could be adapted to JSR calls to, but I never really needed those. Edited December 11, 2018 by Thomas Jentzsch 10 1 Quote Link to comment Share on other sites More sharing options...
+Karl G Posted December 11, 2018 Share Posted December 11, 2018 Thank you for sharing these! Quote Link to comment Share on other sites More sharing options...
Just Jeff Posted December 23, 2018 Share Posted December 23, 2018 Well I started out understanding none of this and now I think I understand about half of it. Anybody care to check or correct my comments? ; TJ's Atari standard bankswitching macros, here tailored for F4 (adapt for F8 or F6) BANK_SIZE = $1000 ;j Define a constant,(4K bank size here) ; Use at the start of each bank, required by JMP_LBL macro MAC START_BANK ; bank number. j example: start each bank with with argument ;j START_BANK 1, START_BANK 2, etc BANK_NUM SET {1} ;j Transfer the START_BANK argument to BANK_NUM BANK_ORG SET $8000 + BANK_NUM*BANK_SIZE ;j $8000 skips over the first potential 8 ;j (4K) banks then sets the start address (ORiGin) of the ;j bank that was set in the argument. BANK_RORG SET $1000 + BANK_NUM*BANK_SIZE*2 ;j ??? If bank 2, for example, BANK_RORG ;j would be $5000. Why? SEG code ;j Start the code segment ORG BANK_ORG, $55 ;j Put $55 at the start of a bank? RORG BANK_RORG ;j Put the relocatable code in the calculated location? ECHO "Start of bank", [BANK_NUM]d, ", ORG", BANK_ORG, ", RORG", BANK_RORG ;j print stuff ; verify start addresses: START{1} = (START & $fff) | BANK_RORG ;j Define START of the bank as this location ;j with the left nibble stripped off and replaced with the ;j left nibble of BANK_RORG? ; optional Space for RAM+ ; DS.B $100, "#" ENDM ; a convenience macro, put at the end of every bank MAC END_BANK RORG $ff4 ds 8, 0 ;j Put zeros in the last 8 bytes for the hot spots before the ; reset and break vectors .word (START & $fff) | BANK_RORG ; RESET .word 0 ; BRK (unused) ENDM ; Insert code for executing the bankswitch, ; should be put at the very end of each bank, right before the hotspots MAC SWITCH_BANK ORG BANK_ORG + $ff4 - BANKSWITCH_SIZE ;j Calculate where the hot spots ;j go in each bank RORG BANK_RORG + $ff4 - BANKSWITCH_SIZE ;j Calculate where the hot spots ;j go in the moveable code? SwitchBank = (. & $fff) | $1000 ;j take this location and replace the left ;j nibble with a 1? pha ;j MAC JMP_LBL jumps to here. The accumulator was loaded with one txa ;j nibble and x was loaded with the other. Move it to the accumulator pha ;j so it can be pushed on the stack. Setting up a return address. lda $fff4,y ;j Load the accumulator with one of the hot spots, causing ;j a bankswitch. rts ;j The bankswitch occurred, so this executes in the new bank ;j The rts is to an address that was calculated in JMP_LBL BANKSWITCH_SIZE = ((. & $fff) | $1000) - SwitchBank ;j Calculates the size of the ;j SwitchBank code. Then take this address and limit it to a range of ;j %00001111 11111111 to ;j %00011111 11111111 .... Why the limit? ENDM ; Define a label which can be used by JMP_LBL macro ;Example: ; DEF_LBL Foo ;j Insert this in the place where you want to be able to jump to. MAC DEF_LBL ; address ;j Figure out where we are. {1} ;j Argument is Foo. Labels the code? {1}_BANK = BANK_NUM ;j Foo_BANK is the bank number we are currently in. ENDM ; Jump to a label in other or same bank. The assembler will take care if the ; code has to bankswitch or not. ; Example: ; JMP_LBL Foo MAC JMP_LBL ; address IF {1}_BANK != BANK_NUM ; convenience IF ;j If Foo_BANK is not current bank ldy #{1}_BANK ;j Load y with the hot spot offset lda #>({1}-1) ;j Load MSB of Foo's address ldx #<({1}-1) ;j Load LSB of Foo's address jmp SwitchBank ; always at the same address in each bank ELSE ;j If we're already in the same bank jmp {1} ;j then just jump Foo ENDIF ENDM 1 Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted December 24, 2018 Author Share Posted December 24, 2018 Well I started out understanding none of this and now I think I understand about half of it. Anybody care to check or correct my comments? ; TJ's Atari standard bankswitching macros, here tailored for F4 (adapt for F8 or F6) BANK_SIZE = $1000 ;j Define a constant,(4K bank size here) ; Use at the start of each bank, required by JMP_LBL macro MAC START_BANK ; bank number. j example: start each bank with with argument ;j START_BANK 1, START_BANK 2, etc TJ: for F4 you should start with 0, else the following variables will overrun BANK_NUM SET {1} ;j Transfer the START_BANK argument to BANK_NUM BANK_ORG SET $8000 + BANK_NUM*BANK_SIZE ;j $8000 skips over the first potential 8 ;j (4K) banks then sets the start address (ORiGin) of the ;j bank that was set in the argument. BANK_RORG SET $1000 + BANK_NUM*BANK_SIZE*2 ;j ??? If bank 2, for example, BANK_RORG ;j would be $5000. Why? TJ: Because we need an odd first nibble here SEG code ;j Start the code segment ORG BANK_ORG, $55 ;j Put $55 at the start of a bank? TJ: This is the address of the bank in the ROM. These can be anything, but they must be consecutive overall. $55 is just redefining the default fill value for unused areas RORG BANK_RORG ;j Put the relocatable code in the calculated location? TJ: The origin used by the code in this bank. Different origins per bank make debugging easier. ECHO "Start of bank", [BANK_NUM]d, ", ORG", BANK_ORG, ", RORG", BANK_RORG ;j print stuff ; verify start addresses: START{1} = (START & $fff) | BANK_RORG ;j Define START of the bank as this location ;j with the left nibble stripped off and replaced with the ;j left nibble of BANK_RORG? TJ: Startup address of the bank, this code just verifies that all banks use the place in their logical address space ; optional Space for RAM+ ; DS.B $100, "#" ENDM ; a convenience macro, put at the end of every bank MAC END_BANK RORG $ff4 ds 8, 0 ;j Put zeros in the last 8 bytes for the hot spots before the ; reset and break vectors .word (START & $fff) | BANK_RORG ; RESET .word 0 ; BRK (unused) ENDM ; Insert code for executing the bankswitch, ; should be put at the very end of each bank, right before the hotspots MAC SWITCH_BANK ORG BANK_ORG + $ff4 - BANKSWITCH_SIZE ;j Calculate where the hot spots ;j go in each bank RORG BANK_RORG + $ff4 - BANKSWITCH_SIZE ;j Calculate where the hot spots ;j go in the moveable code? TJ: You have to update ORG and ROGR in pairs when using bankswitching, because they are different. SwitchBank = (. & $fff) | $1000 ;j take this location and replace the left ;j nibble with a 1? TJ: Just like START, this puts all bank switch code at the same spot in address space for each bank pha ;j MAC JMP_LBL jumps to here. The accumulator was loaded with one txa ;j nibble and x was loaded with the other. Move it to the accumulator pha ;j so it can be pushed on the stack. Setting up a return address. lda $fff4,y ;j Load the accumulator with one of the hot spots, causing ;j a bankswitch. rts ;j The bankswitch occurred, so this executes in the new bank ;j The rts is to an address that was calculated in JMP_LBL BANKSWITCH_SIZE = ((. & $fff) | $1000) - SwitchBank ;j Calculates the size of the ;j SwitchBank code. Then take this address and limit it to a range of ;j %00001111 11111111 to ;j %00011111 11111111 .... Why the limit? TJ: The limit comes from the 4K address space we are using. ENDM ; Define a label which can be used by JMP_LBL macro ;Example: ; DEF_LBL Foo ;j Insert this in the place where you want to be able to jump to. MAC DEF_LBL ; address ;j Figure out where we are. {1} ;j Argument is Foo. Labels the code? TJ: Yes, required to have an address {1}_BANK = BANK_NUM ;j Foo_BANK is the bank number we are currently in. ENDM ; Jump to a label in other or same bank. The assembler will take care if the ; code has to bankswitch or not. ; Example: ; JMP_LBL Foo MAC JMP_LBL ; address IF {1}_BANK != BANK_NUM ; convenience IF ;j If Foo_BANK is not current bank ldy #{1}_BANK ;j Load y with the hot spot offset lda #>({1}-1) ;j Load MSB of Foo's address ldx #<({1}-1) ;j Load LSB of Foo's address jmp SwitchBank ; always at the same address in each bank ELSE ;j If we're already in the same bank jmp {1} ;j then just jump Foo ENDIF ENDM My comments above (TJ...) 2 Quote Link to comment Share on other sites More sharing options...
Just Jeff Posted December 30, 2018 Share Posted December 30, 2018 Thanks A lot! I think I understand enough of it to use it now Quote Link to comment Share on other sites More sharing options...
KevKelley Posted January 1, 2019 Share Posted January 1, 2019 Curious. I am still learning but Is there a method of getting the same amount pf space without bankswitching? Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted January 1, 2019 Author Share Posted January 1, 2019 Sorry, I don't get your question. Quote Link to comment Share on other sites More sharing options...
KevKelley Posted January 4, 2019 Share Posted January 4, 2019 Sorry. I was wondering if information can only be accessed in 4k increments or is there a way to have more without bankswitching? Not sure if I am fully understanding it or not or if my question makes much sense at all... Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted January 4, 2019 Author Share Posted January 4, 2019 The Atari can only access up to 4K ROM without bankswitching. If you need more ROM space you need bankswitching. Quote Link to comment Share on other sites More sharing options...
KevKelley Posted January 4, 2019 Share Posted January 4, 2019 Okay. Thanks. That's what I thought from all the reading but wasn't sure if I missed anything. Quote Link to comment Share on other sites More sharing options...
Dionoid Posted April 8, 2019 Share Posted April 8, 2019 Hi Thomas, I see in your macros that bank 0 will use: ORG $8000 RORG $1000 ...and bank 1 will use: ORG $9000 RORG $3000 Can you explain why this makes debugging easier than if you would just use RORG $F000 for each bank? Thanks! Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted April 8, 2019 Share Posted April 8, 2019 (edited) Because when one bank references labels from another bank, you can tell by the MSB which bank the labels actually exist in. Tho it's true that it's not much an advantage over using $1000 for all banks IMO, where the upper 3 bits for all MSB's can be repurposed for other data...so if it IS used, you could truncate the label addresses to be 13 bit whenever you use need them. Edited April 8, 2019 by Nukey Shay Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted April 9, 2019 Author Share Posted April 9, 2019 Actually it is a huge advantage when using Stella's debugger. Else you code will be cluttered by labels from all banks. And the correct label may not even show, if the same 13 bit address is used in another bank. 1 Quote Link to comment Share on other sites More sharing options...
Dionoid Posted April 9, 2019 Share Posted April 9, 2019 Actually it is a huge advantage when using Stella's debugger. Else you code will be cluttered by labels from all banks. And the correct label may not even show, if the same 13 bit address is used in another bank. For some reason the Stella 6.0 debugger doesn't show me labels for Bank 1, but only for Bank 0. In Stella I do see that: Bank 0 is at $1000 - $1fff Bank 1 is at $3000 - $3fff And the symbols dump file also contains the correct labels for Bank 1 in that $3000 - $3fff range. But when I step through the debugger from Bank 0 into Bank 1, I see the addresses left to the assembly code stay in the '1xxx' range instead of switching to the '3xxx' range. Also, in Bank 1 it doesn't show any of the labels from that bank. So while Stella seems to know that Bank 1 is in the $3000 - $3fff range, it doesn't use that correctly in the debugger and for matching label names. Am I doing something wrong maybe? Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted April 9, 2019 Author Share Posted April 9, 2019 (edited) The problem is, that the PC is still using the old bank's upper bits. For debugging, use a jump after entering a new bank. Afterwards, you should be fine. Edited April 9, 2019 by Thomas Jentzsch Quote Link to comment Share on other sites More sharing options...
Dionoid Posted April 9, 2019 Share Posted April 9, 2019 The problem is, that the PC is still using the old bank's upper bits. For debugging, use a jump after entering a new bank. Afterwards, you should be fine. Weird, I do see the PC is showing the correct upper bits after bank switching, but the debugger doesn't seem to reflect the new bank addresses. Adding a dummy jmp *+3 as first instruction after entering the new bank doesn't help. Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted April 9, 2019 Share Posted April 9, 2019 Actually it is a huge advantage when using Stella's debugger. Else you code will be cluttered by labels from all banks. That is why I edited the reply to scratch out "use" and put "need" there instead. It's an extra 3 bits of Ram from any variable holding MSB data. Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted April 9, 2019 Author Share Posted April 9, 2019 Weird, I do see the PC is showing the correct upper bits after bank switching, but the debugger doesn't seem to reflect the new bank addresses. Adding a dummy jmp *+3 as first instruction after entering the new bank doesn't help. Hm, I suppose I have to see the code (or an example code) do understand what is going on. Quote Link to comment Share on other sites More sharing options...
Dionoid Posted April 9, 2019 Share Posted April 9, 2019 (edited) Hm, I suppose I have to see the code (or an example code) do understand what is going on. Hi Thomas, I extracted the bank-switching code into a very basic example showing rainbow colors and doing a single F8 bank switch (from bank 0 to bank 1, and back again). If you run this example and in the Stella debugger set a breakpoint on the 'BankSwitch' label, you can step through the code and see the problem that after the bank-switch the addresses inside the debugger are not updated to reflect the correct addresses (i.e. they should be in the $3000 - $3fff range, but they keep showing addresses in the $1000 - $1fff range). Maybe I'm doing something wrong, but maybe it's a bug in Stella 6.0. Thanks! ;=============================================================================== ; F8 Bank Switch sample ;=============================================================================== processor 6502 include "vcs.h" include "macro.h" MAC COMMON_BANKSWITCH_CODE ;make sure we always start at label InitSystem in Bank 0 lda #>(InitSystem-1) ldy #<(InitSystem-1) ldx #$ff txs ; init stack pointer inx ; X = 0, because InitSystem is in Bank 0 BankSwitch = (. & $fff) | $1000 ; pha ; push hi byte tya ; Y -> A pha ; push lo byte lda $1FF8,x ; do the bank switch rts ; return to target ENDM ;=============================================================================== ; BANK 0 ;=============================================================================== SEG Code ORG $8000 RORG $1000 COMMON_BANKSWITCH_CODE ;=============================================================================== ; Init ;=============================================================================== InitSystem: CLEAN_START ; initialize zero page and stack GameLoop: VERTICAL_SYNC ;Now switch to code in bank 1 lda #>(LabelInBank1-1) ldy #<(LabelInBank1-1) ldx #1 jmp BankSwitch ReturnFromBankSwitch: ; 36 scanlines of vblank... REPEAT 36 sta WSYNC REPEND lda #0 sta VBLANK ; stop vertical blank ;------------------------------------------------------------------------------- ; Now display a screen of rainbow colors ;------------------------------------------------------------------------------- DisplayKernel: ; 192 scanlines of picture... ldx #0 REPEAT 192 ; scanlines inx stx COLUBK sta WSYNC REPEND ;------------------------------------------------------------------------------- ; VerticalBlank - enter blanking ;------------------------------------------------------------------------------- OverScan: ; end of screen - enter blanking lda #%01000010 sta VBLANK ; 30 scanlines of overscan... REPEAT 30 sta WSYNC REPEND ; back to game loop jmp GameLoop ;ending vectors ORG $8ff8 RORG $1ff8 ds 4, 0 .word $1000 ; RESET .word 0 ; BRK ;=============================================================================== ; BANK 1 ;=============================================================================== SEG Code ORG $9000 RORG $3000 COMMON_BANKSWITCH_CODE LabelInBank1: jmp *+3 ; jump to next instruction (maybe this fixes PC issue?) ; switch back to bank 0 lda #>(ReturnFromBankSwitch-1) ldy #<(ReturnFromBankSwitch-1) ldx #0 jmp BankSwitch ;ending vectors ORG $9ff8 RORG $3ff8 ds 4, 0 .word $3000 ; RESET .word 0 ; BRK Edited April 10, 2019 by Dionoid Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted April 10, 2019 Author Share Posted April 10, 2019 You are right, your code doesn't disassemble correct. I have no idea why similar code works, but your's doesn't. I'll create an issue for it, but it may take quite some time to get to it. Quote Link to comment Share on other sites More sharing options...
Dionoid Posted April 11, 2019 Share Posted April 11, 2019 (edited) You are right, your code doesn't disassemble correct. I have no idea why similar code works, but your's doesn't. I'll create an issue for it, but it may take quite some time to get to it. Hi @Thomas Jentzsch, do you have a small bank-switching example where the Stella debugger actually shows the correct addresses after switching to the non-startup bank? From that I think I can figure out the differences. I tried changing my code by moving the common bank-switch code to the end of the banks (just before the hotspots starting at $fff4) but that didn't fix the issue with the missing debug labels. Edited April 11, 2019 by Dionoid Quote Link to comment Share on other sites More sharing options...
Dionoid Posted June 2, 2019 Share Posted June 2, 2019 (edited) Note that I found a specific combination of Stella settings that seems to solve the F8 debugging issues for me, see this post. Edited June 2, 2019 by Dionoid 2 Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted April 11, 2022 Author Share Posted April 11, 2022 (edited) On 6/2/2019 at 4:20 PM, Dionoid said: Note that I found a specific combination of Stella settings that seems to solve the F8 debugging issues for me, see this post. @Dionoid I finally got back to this and fixed the issue. The current Stella only considers the current PC's origin for the bank's origin. This can be wrong when switching banks. With the next Stella release, the code will consider the origin of all identified bank labels too. The most frequent origin will be used as bank origin. Edited April 11, 2022 by Thomas Jentzsch 1 1 Quote Link to comment Share on other sites More sharing options...
Dionoid Posted April 11, 2022 Share Posted April 11, 2022 Great news! Thanks! Quote Link to comment Share on other sites More sharing options...
vdub_bobby Posted October 16, 2023 Share Posted October 16, 2023 (edited) Hi. I just started seeing this issue. I moved a bunch of code from one bank to another and now Stella is acting like both banks are RORGed to the same location ($3000-$3FFF) I tried the tips in this post without much luck. Wondering if anybody has any other tips to try Edited October 16, 2023 by vdub_bobby 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.