Bank Switching
Moderator: Moderators
For clarity, you usually want to write only to $8000, $A000, $C000, and $E000, unless you want to obfuscate your crazy bankswitching for copy-protection, but that's an advanced topic.
-
- Posts: 2158
- Joined: Sun Jun 05, 2005 2:04 pm
- Location: Minneapolis, Minnesota, United States
- Contact:
Okay, I am currently having much trouble doing a simple bankswitch. Look here at my code:
The bank is actually switching into $8000-$BFFF. The data will work correctly if you don't switch banks, so it's not something wrong with the code in bank 2. For some reason, the data just isn't being read. It's sitting there in bank 2. Nice working data, not being read. What's the deal here? A blank screen shows up, and it's just supposed to display "Hello World!" on the screen, but it's just a blank screen. If I take the bankswitch out of there, it works just fine. But for some reason, I do the bankswitch and nothing happens. The point of the demo is so I can understand how to bankswitch. Obviosly the bankswitch isn't neccissary, but you get the point. What's the deal, man?
EDIT: Hmm, the code works as soon as I do a soft reset, but when I do a hard reset, it doesn't work. What is up with that? Does it have something to do with the reset bit in $8000?
Code: Select all
.8bit
.bank 0 slot 3
.section "reset" FREE
reset:
cld
sei
ldx #$FF
txs
lda #$02
sta $E000
lsr a
sta $E000
lsr a
sta $E000
lsr a
sta $E000
lsr a
sta $E000
.ends
.bank 2 slot 3
.section "bott" FREE
lda #$00
sta $2000
sta $2001
....
.bank 4 slot 5
.section "graphics" FREE
.incbin "finalfantasyvii.chr"
.ends
.bank 3 slot 4
.orga $FFFA
.section "vectors" FORCE
.dw 0
.dw reset
.dw 0
.ends
EDIT: Hmm, the code works as soon as I do a soft reset, but when I do a hard reset, it doesn't work. What is up with that? Does it have something to do with the reset bit in $8000?
Yeah, you really better reset it first. I don't know if that's the exact problem, but on the real system I'd reset it and configure all the registers at start-up.Celius wrote: EDIT: Hmm, the code works as soon as I do a soft reset, but when I do a hard reset, it doesn't work. What is up with that? Does it have something to do with the reset bit in $8000?
-
- Posts: 2158
- Joined: Sun Jun 05, 2005 2:04 pm
- Location: Minneapolis, Minnesota, United States
- Contact:
Yeah, it still makes me reset it. Does something wierd happen to the Program Counter? What exactly happens on soft reset that doesn't happen on hard reset? I know that the variables in RAM don't change from what they were when the system was running, unless you clear them at the beggining of your reset routine. And also pretty much whatever you stored in anything doesn't change unless you clear/change it at the beggining of your routine. Does anything wierd happen to the PC when you do a hard reset and switch banks?
EDIT: I have to say that this is really odd, because the data is being read, but nothing's happening. There are no graphics or anything, but the PC is at $8049, where the endless loop is, so it has to be doing something. When you do a soft reset, the graphics show up, and the pallete shows up, and "Hello World!" shows up in the middle of the screen. So it's like the endless loop works, but the pallete won't show up, and the graphics aren't there. This really stumps me. I have NO idea what's wrong.
EDIT: I have to say that this is really odd, because the data is being read, but nothing's happening. There are no graphics or anything, but the PC is at $8049, where the endless loop is, so it has to be doing something. When you do a soft reset, the graphics show up, and the pallete shows up, and "Hello World!" shows up in the middle of the screen. So it's like the endless loop works, but the pallete won't show up, and the graphics aren't there. This really stumps me. I have NO idea what's wrong.
Last edited by Celius on Sun Feb 26, 2006 4:43 pm, edited 1 time in total.
It would help a bit if you described exactly where each code block was located in memory.
Is your "reset" section at $8000-$BFFF or $C000-$FFFF? If $C000-$FFFF, it must be in the same bank as the vectors - on powerup/reset, the MMC1 drops the last 16KB of PRG ROM at $C000-$FFFF and a random (?) bank at $8000-$BFFF. If your startup code is located within $8000-$BFFF, it would not surprise me one bit if it worked in an emulator but crashed on a real cartridge.
Is your "reset" section at $8000-$BFFF or $C000-$FFFF? If $C000-$FFFF, it must be in the same bank as the vectors - on powerup/reset, the MMC1 drops the last 16KB of PRG ROM at $C000-$FFFF and a random (?) bank at $8000-$BFFF. If your startup code is located within $8000-$BFFF, it would not surprise me one bit if it worked in an emulator but crashed on a real cartridge.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
P.S. If you don't get this note, let me know and I'll write you another.
-
- Posts: 2158
- Joined: Sun Jun 05, 2005 2:04 pm
- Location: Minneapolis, Minnesota, United States
- Contact:
Okay, I still have to get it to work, and I'm wondering if you just do this:
Where does the PC go? Because if I do that in my code, the PC goes to $0000. I'm obviously missing something here. Can someone tell me what's wrong here? What am I missing?
Code: Select all
.bank 3
.org $C000
.section "reset" FREE
reset:
cld
sei
ldx #$FF
txs
lda #$00
sta $E000
sta $E000
sta $E000
sta $E000
sta $E000
.ends
....
.bank 0
.org $8000
.section "continue" FREE
blah blah blah reset routine code
-
- Posts: 2158
- Joined: Sun Jun 05, 2005 2:04 pm
- Location: Minneapolis, Minnesota, United States
- Contact:
Hey! Sweet! All I had to do was put jmp $8000 after the switch to set the PC to $8000 after the switch in $C000-$FFFF. Thanks for all your help. I have one question though. Say your in bank 1 at $8342. When you switch to bank 2, would the PC still be at $8342? Or does it reset itself?
EDIT: Also, tepples, I use WLA-DX, so I tell the assembler to go by 16k PRG banks. I was fed up with NESASM's shit banks.
EDIT: Also, tepples, I use WLA-DX, so I tell the assembler to go by 16k PRG banks. I was fed up with NESASM's shit banks.
Yes. The program counter is a register internal to the CPU, which doesn't know nor care about banks. For an example of code that exploits this fact heavily, see The Wonderful World of NES/Fami Cart Copy Protection.Celius wrote:Say your in bank 1 at $8342. When you switch to bank 2, would the PC still be at $8342?
-
- Posts: 2158
- Joined: Sun Jun 05, 2005 2:04 pm
- Location: Minneapolis, Minnesota, United States
- Contact:
So would you have to have the bankswitching routine in the very beggining of each bank, and just jump past that if the PC is $8000? Like this:
Would it be safe to have that at the beggining of every bank? If you want to switch to bank 2, you could just jump there, and switch to bank 2, and the PC would still be the same, and you'd have it jmp the the "past" lable, which is the lable that defines the beggining of the useful bank data. And does MMC1 only allow 256k of PRG? Because as I can see, there are only $0F banks you can switch to with MMC1. How does SUROM work then?
Code: Select all
jmp past
switch1:
;switch to bank 1
jmp past
switch2:
;switch to bank 2
jmp past
switch3:
;switch to bank 3
....
switch16:
;switch to bank 16
past:
......
Why would you need a unique routine for each bank switch? You could just have one routine which takes the desired bank number via the accumulator.
Generally speaking, you shouldn't ever need to switch banks from the code you're currently executing (unless you're using a mapper which only supports 32KB PRG ROM banks).
All code+data related to a particular aspect of the game should reside in its own bank OR in the 'permanent' bank (if there's enough room) - if an aspect has more than 1 bank worth of data, then put its code in the permanent bank (so you can switch in the desired data) OR put a copy of the code in each switchable bank along with as much data as you can fit.
If, for some reason, you do need to switch banks from within a swappable code block, you should either have a copy of your current routine in the destination bank OR copy a code stub into RAM which performs the bankswitch and then jumps into the desired entrypoint in the new bank.
Generally speaking, you shouldn't ever need to switch banks from the code you're currently executing (unless you're using a mapper which only supports 32KB PRG ROM banks).
All code+data related to a particular aspect of the game should reside in its own bank OR in the 'permanent' bank (if there's enough room) - if an aspect has more than 1 bank worth of data, then put its code in the permanent bank (so you can switch in the desired data) OR put a copy of the code in each switchable bank along with as much data as you can fit.
If, for some reason, you do need to switch banks from within a swappable code block, you should either have a copy of your current routine in the destination bank OR copy a code stub into RAM which performs the bankswitch and then jumps into the desired entrypoint in the new bank.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
P.S. If you don't get this note, let me know and I'll write you another.