UNROM: Reading the current bank number

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

UNROM: Reading the current bank number

Post 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?
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: UNROM: Reading the current bank number

Post by tokumaru »

Put it in a constant location in every ROM bank.
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: UNROM: Reading the current bank number

Post 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.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: UNROM: Reading the current bank number

Post 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.
User avatar
infiniteneslives
Posts: 2104
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Re: UNROM: Reading the current bank number

Post 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.
If you're gonna play the Game Boy, you gotta learn to play it right. -Kenny Rogers
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: UNROM: Reading the current bank number

Post 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.
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: UNROM: Reading the current bank number

Post 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.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: UNROM: Reading the current bank number

Post 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.
AWJ
Posts: 433
Joined: Mon Nov 10, 2008 3:09 pm

Re: UNROM: Reading the current bank number

Post 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.
sdm
Posts: 410
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: UNROM: Reading the current bank number

Post 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?
User avatar
Dwedit
Posts: 4921
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: UNROM: Reading the current bank number

Post by Dwedit »

No more cycles than any other kind of CPU write.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: UNROM: Reading the current bank number

Post 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.
sdm
Posts: 410
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: UNROM: Reading the current bank number

Post by sdm »

Thanks. And are MMC1 or MMC3 somehow significantly "faster" than UNROM?
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: UNROM: Reading the current bank number

Post 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.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: UNROM: Reading the current bank number

Post 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.
Post Reply