Page 1 of 1

UNROM: Reading the current bank number

Posted: Wed Jul 26, 2017 11:00 am
by DRW
Is there a way to read the number of the currently active bank in an UNROM game? Or do I always have to save this as a separate variable?

If I have to save it as a variable: How do I make sure that an interrupt doesn't get into the situation where the bank variable is different from the actually set bank, simply because the interrupt activated right in the moment when one of the two was already set, but the other one wasn't?

Re: UNROM: Reading the current bank number

Posted: Wed Jul 26, 2017 11:01 am
by tokumaru
Put it in a constant location in every ROM bank.

Re: UNROM: Reading the current bank number

Posted: Wed Jul 26, 2017 11:04 am
by lidnariq
Is there an intrinsic way in hardware? No. Tokumaru's suggestion is it.

If you always update the shadow copy of the bank number before you change it, it can't get out of sync due to NMI or IRQ interruption. The same method works safely for MMC3 $8000.

Re: UNROM: Reading the current bank number

Posted: Wed Jul 26, 2017 11:31 am
by tokumaru
If an interrupt needs to change banks, such as the NMI handler having to map in the sound engine/data bank, just read the current bank's number from ROM and push it to the stack before bankswitching, and when you're done, pull it from the stack and map that bank back in. This should work even if multiple interrupts that need to switch banks occur on top of each other.

Re: UNROM: Reading the current bank number

Posted: Wed Jul 26, 2017 4:00 pm
by infiniteneslives
lidnariq wrote:If you always update the shadow copy of the bank number before you change it, it can't get out of sync due to NMI or IRQ interruption.
But if an interrupt were to occur in between the shadow update and bank write, the interrupt routine can't properly learn the current bank number by reading the shadow copy. Right? If the interrupt routine doesn't care about the current bank then this isn't an issue. Even if the ISR tries to restore the bank based on the shadow copy, it isn't a problem. The ISR will just align the shadow with the current bank before the main thread gets does the same swap again.

Re: UNROM: Reading the current bank number

Posted: Wed Jul 26, 2017 4:07 pm
by lidnariq
infiniteneslives wrote:But if an interrupt were to occur in between the shadow update and bank write, the interrupt routine can't properly learn the current bank number by reading the shadow copy. Right?
Right. I was assuming that the interrupt wouldn't care about the current value, only that it be saved.

Re: UNROM: Reading the current bank number

Posted: Wed Jul 26, 2017 4:22 pm
by DRW
tokumaru wrote:Put it in a constant location in every ROM bank.
Thanks. That's what I needed.
tokumaru wrote:If an interrupt needs to change banks, such as the NMI handler having to map in the sound engine/data bank, just read the current bank's number from ROM and push it to the stack before bankswitching, and when you're done, pull it from the stack and map that bank back in.
Yeah, using the stack in interrupts is of course logical. That's what I did anyway.
infiniteneslives wrote:But if an interrupt were to occur in between the shadow update and bank write, the interrupt routine can't properly learn the current bank number by reading the shadow copy. Right? If the interrupt routine doesn't care about the current bank then this isn't an issue.
Yes, if your interrupt never acually reads the original bank value and only sets and resets a new value, then writing the bank variable before setting the bank itself should be sufficient.
But I still prefer the version where each bank has its own number in the same place in ROM because this one works in every constellation, even if the interrupt tries to read the value and not just set/reset it.

Re: UNROM: Reading the current bank number

Posted: Wed Jul 26, 2017 5:50 pm
by tepples
DRW wrote:But I still prefer the version where each bank has its own number in the same place in ROM because this one works in every constellation, even if the interrupt tries to read the value and not just set/reset it.
That's fine for UNROM or MMC1, not so fine for something like MMC3 where you might load two consecutive bank numbers into adjacent windows (e.g. 25 in $8000 and 26 in $A000) and expect to read an array that continues from one to the next. Background tile streams, background map streams, and compressed sprite tile pages in The Curse of Possum Hollow are stored this way.

Re: UNROM: Reading the current bank number

Posted: Thu Jul 27, 2017 2:00 am
by AWJ
DRW wrote:How do I make sure that an interrupt doesn't get into the situation where the bank variable is different from the actually set bank, simply because the interrupt activated right in the moment when one of the two was already set, but the other one wasn't?
Make sure you always write to the shadow bank register before the hardware bank register. That way, if the interrupt fires in the middle of changing a bank, the bank number the interrupt thread sees will match the bank the main thread is about to set, which is what you want in order to restore the bank at the end of the interrupt.

If your interrupt thread needs to actually read data or execute code from a bank that was set by the main thread (I'm not sure why you'd ever need to do this), the interrupt thread can re-set the bank itself.

Re: UNROM: Reading the current bank number

Posted: Fri Aug 18, 2017 7:05 am
by sdm
I do not want to create a separate topic, so I will ask here:
Is switching a bank in UNROM (and not just in UNROM) taking up some CPU time? How many cycles / times does a bank switch take?

Re: UNROM: Reading the current bank number

Posted: Fri Aug 18, 2017 7:09 am
by Dwedit
No more cycles than any other kind of CPU write.

Re: UNROM: Reading the current bank number

Posted: Fri Aug 18, 2017 7:32 am
by tokumaru
Yeah, it takes just the time used by the instructions that write to the mapper (in the case of UNROM that'd be something like lda Bank : tax : sta $XXXX, x, or about 9 cycles - could be less if the bank is constant), there's no extra delay after the switch command, the new bank is available immediately.

Re: UNROM: Reading the current bank number

Posted: Fri Aug 18, 2017 8:08 am
by sdm
Thanks. And are MMC1 or MMC3 somehow significantly "faster" than UNROM?

Re: UNROM: Reading the current bank number

Posted: Fri Aug 18, 2017 8:29 am
by tepples
MMC1 is slower, at 28 cycles.

Code: Select all

.macro mmc1_mode_A
  sta $8000
  lsr a
  sta $8000
  lsr a
  sta $8000
  lsr a
  sta $8000
  lsr a
  sta $8000
.endmacro

.macro mmc1_prg_A
  sta $E000
  lsr a
  sta $E000
  lsr a
  sta $E000
  lsr a
  sta $E000
  lsr a
  sta $E000
.endmacro
Because it's also bigger (19 bytes), this switching is often done as a subroutine instead of inline, and it takes 12 seconds to get in and out of a subroutine. It's also a lot trickier to get correct if there's a possibility that NMI or IRQ may interrupt the switch. So I sort of treat MMC1 PRG bank switch overhead as analogous to that of a TLB miss.

Not to be confused with MMC1 is MIMIC, the mapper used in Namco 108/109/118/119. It's almost as fast as UNROM:

Code: Select all

MMC3_MODE = $00

.macro mimic_prg_8000_A
  ldx #MMC3_MODE | $06
  stx $8000
  sta $8001
.endmacro

.macro mimic_prg_A000_A
  ldx #MMC3_MODE | $07
  stx $8000
  sta $8001
.endmacro
MMC3 is an enhanced MIMIC that needs a bit more work to account for its PPU $0000/$1000 swapping and CPU $8000/$C000 swapping features. But if you use those features the same way throughout your entire game by setting these in MMC3_MODE, there's no extra overhead.

Re: UNROM: Reading the current bank number

Posted: Fri Aug 18, 2017 9:15 am
by tokumaru
The speed of bankswitching operations is dictated by the complexity of the interface used to communicate with the mapper (the more writes you have to do to "talk" to the mapper, the slower it'll be to complete a bank switch command), but once the CPU is done sending the command, the new bank is immediately available.

The reason it works like this is because mappers are constantly routing memory accesses, for every single read/write. A bank switch merely changes a route, there's no copying of data or any other slow operations involved, so the very next read/write is already routed to the newly mapped bank.