This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Using AOROM to make a 2 in 1 multicart of NROM games

Using AOROM to make a 2 in 1 multicart of NROM games
by on (#34162)
Hey all. I've been wondering if something like this is possible. Basically, I was thinking of making a 2 in 1 cart having a menu screen so you can pick your game and then having it load the game using the roms.

I currently have this situation:
-Both NROM roms without their ines header nor their CHR data embedded within them. Just the PRG data and each PRG is stored in a different bank of the AOROM game.
-In the "master" rom, I have it grab the appropriate CHR data and write that all to RAM and then I try to bank switch out everything into the appropriate game.

This actually kinda worked, but the games don't play. One game simply loads the music and the other loads nothing.

I tried clearing zero page with 0's, but that didn't seem to work either.

I don't know if there's a way to call the reset routine of the games or what, but is what I want to ultimately do possible? Thanks.

by on (#34164)
Stick a little piece of code in RAM that selects the bank, then jumps to the reset vector.

Note that AOROM has single screen mirroring, so that means no Super Mario Bros.

by on (#34165)
Yes it's possible but you'll have to hack your games so that they work fine with single screen mirroring, which is likely hard to do. You can modify AOROM to get a fixed mirroring (aka BNROM) easily tough.

by on (#34166)
GNROM or MMC* would work better.

by on (#34171)
I made an SNROM-based multicart engine. I could probably have done it in BNROM as easily, but the theme of the compilation included an existing game that was on SNROM.

But yes, GNROM looks like it was almost made for multis. In fact, the mapper of SMB1 + Duck Hunt is just GNROM on a different board layout.

by on (#34173)
Well, it doesn't have to be AOROM. I just figured it was ideal as you can swap out everything. Neither of my games use scrolling either, so that's no loss.

I could probably do MMC1 if that's a better solution. The only thing I'm not sure of how to do is "Jumping to the reset vector in RAM".

by on (#34174)
The source code for my Forbidden Four multicart engine has an example of how to make a RAM-based trampoline. It involves copying a piece of code to RAM that does the following:
  1. Set up the mapper to copy CHR.
  2. Copy or decompress CHR to CHR RAM. (Omit this step for CHR ROM boards such as GNROM or some MMC1 configurations.)
  3. Set up the mapper to run PRG.
  4. JMP ($FFFC) to start the game from its reset handler.

by on (#34179)
Well, I've got the CHR RAM part of things. The only problem I guess lies with doing the bank swap.

I know that if I bank switch, I'm immediately in the new bank, so in theory I could be anywhere in the program.

The way I'm doing the switch is like this:
    LDA #1  ;Bank 1 is game 1.  Bank 2 is game 2.  Bank 0 is the menu.  Bank 3 is unused.
    STA bankID, X
   ;I think JMP $FFFC would need to go here...
;----------This appears later in the program----------
;Things used for bank switching
   .db $00, $01, $02, $03

The only thing is once I'm swapped, what do I do to JMP to $FFFC? Obviously JMP'ing to there before swapping will just constantly reset the game...

I guess I'm lost on the whole "copy to RAM and execute code" part of things. Do I just put code similar to what I have above somewhere in, say, $0500 and JSR there or is there more to it?

by on (#34182)
Sivak wrote:
I guess I'm lost on the whole "copy to RAM and execute code" part of things. Do I just put code similar to what I have above somewhere in, say, $0500 and JSR there

Pretty much. Except you made one mistake: it's JMP ($FFFC), using indirect addressing, not JMP $FFFC.

by on (#34220)
Ok. I got a menu and everything so you can pick a game and it switches to the appropriate bank and then performs the reset routine.

My final question is this: Whenever I use the emulator reset button, it doesn't reset back to the multicart's menu, but rather stays in the current bank as if you reset just that game. The power button of course works. Is there any good way around this or is it going to be power all the way?

by on (#34228)
You could hack each game's reset code to jump back to the multicart's menu. (This was done in SMB+Duck Hunt.) But then you wouldn't be able to use an automated tool to build multicarts because you'd have to find some space in the ROM to put the trampoline code.

by on (#34229)
Who needs rom space when you can add an extra 8k of SRAM in there?

by on (#34230)
Three things:
  1. A*ROM and B*ROM don't support SRAM without adding an extra 74HC10 chip and a bit of analog circuitry to the board.
  2. This extra SRAM, the support circuitry, and the revised PCB cost money to replicate.
  3. Multicarts including one battery-backed game need to preserve the data in SRAM when switching games.

by on (#34237)
Well, the multicart is of the first two games I made, so I have the sources and all, but what exactly would I need to do? Just some normal bank switch or is there more to it?

Also, one guy mentioned that it's possible for AOROM to start in a random bank and not always bank 0.

by on (#34247)
If you know that none of the NROM games use the last few bytes of PRG ROM for anything, you could just put identical entry point code in $FFF2-$FFFF of all banks:
  ldx #$00       ; you don't get a bus conflict if you
  stx reset + 1  ; overwrite the immediate operand
  jmp $8000
  .addr nmi_entry
  .addr reset
  .addr irq_entry

In this case, your menu starts at $8000 of bank 0:
  ; X is already 0 from the code in all banks
  stx PPUCTRL  ; disable NMI
  stx PPUMASK  ; disable rendering
  txs  ; set stack pointer to $01FF

  ; etc.

But then you'd have to copy the actual start of your game to RAM and JMP through that instead of ($FFFC).

by on (#34413)
Hey again. I finally took some time to look into this and I'm using the following method:

Basically, the code I copy to RAM are these instructions:
.bank 3
.org $F000
LDA selectedBank
STA bankID, X
JMP $FFE0          ;$FFE0 is a spot of empty space in both games where I simply put the instruction JMP RESET, which is where the standard game reset routine is.

I just store the above code all at the beginning of $0600 and then JSR $0600. That works, but I just wanted to ask if that looked bug-free or if I might be missing something.

My question was regarding:
.bank 3
.org $FFE0
;LDX #$00       ; you don't get a bus conflict if you
;STX RESET + 1  ; overwrite the immediate operand
JMP RESET      ;I guess I could also have done JMP $8000

When I have the LDX #0, and STX RESET + 1, that causes freezing and the games never load. But with those two lines commented out, the game loads fine. I also have both games' reset vectors point to a new routine (called RESET2) that simply forces bank 0 to be loaded and that works fine too.

So my question is: Are those two lines I've got commented out vital or is there no concern?

by on (#34436)
I wish someone made a 1-screen mirroring version of SMB1, That would really help

I attempted the above, But the code for the SMB1 renderer wasn't doing any of it right.

by on (#34437)
Or just abandon AOROM and use BxROM, GNROM or MMC1 instead.

by on (#34438)
Hamtaro126 wrote:
I attempted the above, But the code for the SMB1 renderer wasn't doing any of it right.

Probably because it was never designed to work with one-screen mirroring.

by on (#34444)
Yeah, I'm pretty sure that SMB1 updates WAY off screen, so any attempt to do that with one screen mirroring will result in it updating right on screen.

by on (#34455)
Celius wrote:
Yeah, I'm pretty sure that SMB1 updates WAY off screen

So do a lot of vertically mirrored games, such as Contra.

I'm with Dwedit: just use BNROM. If you have a bunch of A*ROM boards, you can rewire them to BNROM by cutting the trace to CIRAM A10 and running a wire from CIRAM A10 to PPU A10 or PPU A11.