Reverse engineering funny multi-game cartridge

Discuss hardware-related topics, such as development cartridges, CopyNES, PowerPak, EPROMs, or whatever.

Moderator: Moderators

Post Reply
User avatar
krzysiobal
Posts: 1036
Joined: Sun Jun 12, 2011 12:06 pm
Location: Poland
Contact:

Reverse engineering funny multi-game cartridge

Post by krzysiobal »

I got Rinco famiclone (which looks just like Famicom) to fix it. It has one additional 60 pin cartridge socket underneath with multi-game cartridge inserted. I decided to reverse-engineer it because it looks funny (PAL, so many diodes and some mysterious pads with missing chips & resistors)
Image

The cartridge was probably projectted to be put in two-slot consoles, because it is really narrow (nothing to lock against clips in cartridge shell) and was done on 1.5 mm laminate - requires so much force to put it inside.

I dumped the menu (CPU $8000-$FFFF + PPU $0000-$1FFF), which can be done with help of kazzo to almost any multicart cartridge without knowledge how it works - except those which start messing with banks at startup.

Cartridge contains 200 000 games, but in fact, only: Mario, Lunar ball, Duck Hunt, Wild gunman, Hogans Alley, Bomber man, Tank, Sky destroyer, Binary land, Ice climber. Programmers divided the menu into 1-5000, 5001-10000, etc for easier navigation.
Image Image Image

First i desoldered chips and rev-ed all tracks
Image Image

Those glob-tobs have pins in almost same order like ordinary ROMs:
Image Image

PRG-ROM is 256k, CHR-ROM is 128 kB. CHR-ROM has only one chip enable line.

Then I rev-ed schematics:
Image

Some notes:
* There is 74273 8 bit latch (adress bits A0-A7 are latched, but A4 & A5 are not used)
* ROM is not turned off during writes at $8000-$FFFF (bus conflicts),
* There is R-C-D reset circuitry which sets latch's bits to 0 on startup,
* Two diodes + resistors were used as poor man's OR gate (ORing PPU-A13 & PPU-!RD to feed the result into CHR-ROM's !CE),
* PAL16L8's is configured to serve as 12 input & 6 output combinational logic.

Then I dumped the PAL to binary using my home-made flash reader.
Then I wrote simple program to convert the binary file into espresso text description:

Code: Select all

.i 12
.o 6
.ilb PPU-A11 PPU-A10 REG.A7 REG.A6 CPU-A14 CPU-A13 CPU-A12 REG.A2 REG.A1 REG.A0 CPU-!ROMSEL! CPU-R/!W! 
.ob REG.WR PRG-A14 PRG-A15 PRG-A16 BUF-!OE! CIRAM-A10 
000000000000 000010
000000000001 000010
000000000010 000010
000000000011 000010
000000000100 010010
000000000101 010010
000000000110 010010
000000000111 010010
...
Then I ran espresso on this file to generate logic formulas for each output:

Code: Select all

REG.WR = (CPU-A14 & not CPU-A13 & CPU-A12 & CPU-!ROMSEL! & not CPU-R/!W!);
PRG-A14 = (not REG.A6 & CPU-A14 & not REG.A1) | (not REG.A6 & CPU-A14 & not REG.A2) | (REG.A0);
PRG-A15 = (not REG.A6 & REG.A2 & REG.A0) | (not REG.A6 & CPU-A14 & not REG.A2) | (REG.A1);
PRG-A16 = (not REG.A6 & CPU-A14 & notREG.A2) | (REG.A2);
BUF-!OE! = (CPU-A14 & not CPU-A13 & CPU-A12 & CPU-!ROMSEL! & not CPU-R/!W!) | (not CPU-!ROMSEL!) | (notCPU-A12) | (CPU-A13) | (not CPU-A14);
CIRAM-A10 = (PPU-A10 & not REG.A7) | (PPU-A11 & REG.A7);
Data is latched on rising edge of REG.WR signal, which corresponds to writes at $5000-$5FFF (beginning of cycle) or $D000-$DFFF (end of cycle). In fact, first region is used because it does not produce bus conflicts with ROM.

Generally:
* PRG-A14 <- A0, PRG-A15 <- A1, PRG-A16 <- A2, PRG-A17 <= A3 (at $8000-$b000),
* A6 controls 16 KB / 32 KB banking mode,
* at $c000-$ffff there is some magic,
* A7 controls mirroring

Mysterious DIL14 chip, resistors and jumpers
In those mysterious 14 pads probably 74125 tri state buffer should be placed:
Image Image

Its four inputs are combination of GND/VCC (according to jumpers), which are placed on D0-D3 bus when BUF_!OE goes low, which corresponds to reading from $5000-$5FFF. Probably that could be used for reading some magic value and displaying different game sets, based on that (manufacturer could produce just one type of PCB with same glop-tops and create pseudo-different cartridges according to jumpers)


Kazzo script for dumping whole set would be:
for (a = 0x5000; a < 0x5010; ++a) {
cpu_w(a, 0x00);
cpu_r(0x8000, 0xbfff);
ppu_r(0x0000, 0x1fff);
}
During whole operation, m2 must have 1.7 MHz clock, otherwise register will reset.
Attachments
pal.bin
(4 KiB) Downloaded 422 times
only_menu.nes
(40.02 KiB) Downloaded 499 times
User avatar
Quietust
Posts: 1918
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Reverse engineering funny multi-game cartridge

Post by Quietust »

Probably shouldn't attach the full multicart ROM, since those games are technically copyrighted (even if the copyright text has been edited out) - the menu-only ROM is probably fine, though.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
zxbdragon
Posts: 498
Joined: Mon Dec 12, 2011 8:15 pm

Re: Reverse engineering funny multi-game cartridge

Post by zxbdragon »

krzysiobal wrote:I got Rinco famiclone (which looks just like Famicom) to fix it. It has one additional 60 pin cartridge socket underneath with multi-game cartridge inserted. I decided to reverse-engineer it because it looks funny (PAL, so many diodes and some mysterious pads with missing chips & resistors)
Image

The cartridge was probably projectted to be put in two-slot consoles, because it is really narrow (nothing to lock against clips in cartridge shell) and was done on 1.5 mm laminate - requires so much force to put it inside.

I dumped the menu (CPU $8000-$FFFF + PPU $0000-$1FFF), which can be done with help of kazzo to almost any multicart cartridge without knowledge how it works - except those which start messing with banks at startup.

Cartridge contains 200 000 games, but in fact, only: Mario, Lunar ball, Duck Hunt, Wild gunman, Hogans Alley, Bomber man, Tank, Sky destroyer, Binary land, Ice climber. Programmers divided the menu into 1-5000, 5001-10000, etc for easier navigation.
Image Image Image

First i desoldered chips and rev-ed all tracks
Image Image

Those glob-tobs have pins in almost same order like ordinary ROMs:
Image Image

PRG-ROM is 256k, CHR-ROM is 128 kB. CHR-ROM has only one chip enable line.

Then I rev-ed schematics:
Image

Some notes:
* There is 74273 8 bit latch (adress bits A0-A7 are latched, but A4 & A5 are not used)
* ROM is not turned off during writes at $8000-$FFFF (bus conflicts),
* There is R-C-D reset circuitry which sets latch's bits to 0 on startup,
* Two diodes + resistors were used as poor man's OR gate (ORing PPU-A13 & PPU-!RD to feed the result into CHR-ROM's !CE),
* PAL16L8's is configured to serve as 12 input & 6 output combinational logic.

Then I dumped the PAL to binary using my home-made flash reader.
Then I wrote simple program to convert the binary file into espresso text description:

Code: Select all

.i 12
.o 6
.ilb PPU-A11 PPU-A10 REG.A7 REG.A6 CPU-A14 CPU-A13 CPU-A12 REG.A2 REG.A1 REG.A0 CPU-!ROMSEL! CPU-R/!W! 
.ob REG.WR PRG-A14 PRG-A15 PRG-A16 BUF-!OE! CIRAM-A10 
000000000000 000010
000000000001 000010
000000000010 000010
000000000011 000010
000000000100 010010
000000000101 010010
000000000110 010010
000000000111 010010
...
Then I ran espresso on this file to generate logic formulas for each output:

Code: Select all

REG.WR = (CPU-A14 & not CPU-A13 & CPU-A12 & CPU-!ROMSEL! & not CPU-R/!W!);
PRG-A14 = (not REG.A6 & CPU-A14 & not REG.A1) | (not REG.A6 & CPU-A14 & not REG.A2) | (REG.A0);
PRG-A15 = (not REG.A6 & REG.A2 & REG.A0) | (not REG.A6 & CPU-A14 & not REG.A2) | (REG.A1);
PRG-A16 = (not REG.A6 & CPU-A14 & notREG.A2) | (REG.A2);
BUF-!OE! = (CPU-A14 & not CPU-A13 & CPU-A12 & CPU-!ROMSEL! & not CPU-R/!W!) | (not CPU-!ROMSEL!) | (notCPU-A12) | (CPU-A13) | (not CPU-A14);
CIRAM-A10 = (PPU-A10 & not REG.A7) | (PPU-A11 & REG.A7);
Data is latched on rising edge of REG.WR signal, which corresponds to writes at $5000-$5FFF (beginning of cycle) or $D000-$DFFF (end of cycle). In fact, first region is used because it does not produce bus conflicts with ROM.

Generally:
* PRG-A14 <- A0, PRG-A15 <- A1, PRG-A16 <- A2, PRG-A17 <= A3 (at $8000-$b000),
* A6 controls 16 KB / 32 KB banking mode,
* at $c000-$ffff there is some magic,
* A7 controls mirroring

Mysterious DIL14 chip, resistors and jumpers
In those mysterious 14 pads probably 74125 tri state buffer should be placed:
Image Image

Its four inputs are combination of GND/VCC (according to jumpers), which are placed on D0-D3 bus when BUF_!OE goes low, which corresponds to reading from $5000-$5FFF. Probably that could be used for reading some magic value and displaying different game sets, based on that (manufacturer could produce just one type of PCB with same glop-tops and create pseudo-different cartridges according to jumpers)


Kazzo script for dumping whole set would be:
for (a = 0x5000; a < 0x5010; ++a) {
cpu_w(a, 0x00);
cpu_r(0x8000, 0xbfff);
ppu_r(0x0000, 0x1fff);
}
During whole operation, m2 must have 1.7 MHz clock, otherwise register will reset.

dump bad! I think!
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: Reverse engineering funny multi-game cartridge

Post by lidnariq »

krzysiobal wrote:Data is latched on rising edge of REG.WR signal, which corresponds to writes at $5000-$5FFF (beginning of cycle) or $D000-$DFFF (end of cycle). In fact, first region is used because it does not produce bus conflicts with ROM.
Due to only relying on /ROMSEL (and not M2), it's actually a little weirder/better. For both addresses, /ROMSEL is high for the first half cycle, and so ROM is also not driving for the first half cycle.
The ≈20ns latency due to the 74'139 on the mainboard means that the write ends up being latched on the rising edge of the previous /ROMSEL, at which point all the other signals are stable.

When /ROMSEL falls on a write to $D000, the rising edge has already happened; there will be no bus conflicts for the latch to care about. (There'll still be a fight between the ROM and the CPU). When /ROMSEL rises again on the following cycle, the other checked values will have already changed.

I used Minilog to see if I can could a clearer explanation of what the banking logic is, but it only sorta helped.
PRG-A16 can be simplified: (/Q6 & CPUA14) | Q2
BUF-/OE can be simplified also: /R/W | //ROMSEL | /A12 | /A14 | A13.

In conclusion, the only idea I can think of is to make a table:

Code: Select all

 reg&$47 bank$8000 bank$C000
  00         0         7
  01         1         7
  02         2         7
  03         3         7
  04         4         5
  05         7         7
  06         6         3
  07         7         7
  40         0         0 ; vv- the already-identified 16K mode
  41         1         1
  42         2         2
  43         3         3
  44         4         4
  45         5         5
  46         6         6
  47         7         7
So, when Q6 is low ... there's room for one 64K UNROM game across 16K banks 0,1,2,7, and two 32K NROM games in banks 4,5, and 6,3. (in each 128K half of the 256K PRG)
User avatar
krzysiobal
Posts: 1036
Joined: Sun Jun 12, 2011 12:06 pm
Location: Poland
Contact:

Re: Reverse engineering funny multi-game cartridge

Post by krzysiobal »

The ≈20ns latency due to the 74'139 on the mainboard means that the write ends up being latched on the rising edge of the previous /ROMSEL, at which point all the other signals are stable.
There is huge 10nF capacitor on latch's clock to ground, so it probably will block such spikes.
So, when Q6 is low ... there's room for one 64K UNROM game across 16K banks 0,1,2,7, and two 32K NROM games in banks 4,5, and 6,3. (in each 128K half of the 256K PRG)
Maybe there is also Contra, because there are entries like `jungale, base1, waterfall, base2, snow field, energy zone` which corrsponds to level names (I forgot to check it, now the cart is no longer in my hands),

I think the 'jumpers' are rather intended to be used with some kind of switch, rather than jumpers (otherwise they would not put places for resistors).

The menu just before start indeed reads $5000-$5fff (even twice and copares the values, if they are not equal, it ends in infinite loop). Thanks to open bus capacitance, 0x50 is read.

Code: Select all

 01:CC80:AD 00 50  LDA $5000 = #$EA
 01:CC83:85 12     STA $0012 = #$50
 01:CC85:EA        NOP
 01:CC86:EA        NOP
>01:CC87:AD 01 50  LDA $5001 = #$EA
 01:CC8A:C5 12     CMP $0012 = #$50
 01:CC8C:D0 F2     BNE $CC80
 01:CC8E:A5 12     LDA $0012 = #$50
 01:CC90:29 0F     AND #$0F
 01:CC92:C9 01     CMP #$01
 01:CC94:90 0B     BCC $CCA1
 01:CC96:C9 03     CMP #$03
 01:CC98:90 0A     BCC $CCA4
 01:CC9A:C9 06     CMP #$06
 01:CC9C:90 0C     BCC $CCAA
 01:CC9E:4C B0 CC  JMP $CCB0
 01:CCA1:4C 00 80  JMP $8000
 01:CCA4:8D 01 50  STA $5001 = #$EA
 01:CCA7:4C 00 80  JMP $8000
 01:CCAA:8D 02 50  STA $5002 = #$EA
 01:CCAD:4C 00 80  JMP $8000
 01:CCB0:8D 03 50  STA $5003 = #$EA
 01:CCB3:4C 00 80  JMP $8000
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: Reverse engineering funny multi-game cartridge

Post by lidnariq »

krzysiobal wrote:There is huge 10nF capacitor on latch's clock to ground, so it probably will block such spikes.
Even better.

Thinking about it a little more, I realized there could be bus conflicts during the moment of transition. Adding the capacitor helps protect against that.

Code: Select all

                        v bus conflict while /ROMSEL still low and R/W low
 A0-A15 < ROM          >< $5 or $D xxx >
  D0-D7 ><HiZ ><   ROM  ◇  CPU         >
    R/W ^^^^^^^^^^^^^^^^________________
     M2 ______^^^^^^^^^^______^^^^^^^^^^
/ROMSEL _^^^^^^__________^^^^^^=========
                        >< latch would happen here without added capacitor; ROM hasn't gotten off the bus yet
I think the 'jumpers' are rather intended to be used with some kind of switch, rather than jumpers (otherwise they would not put places for resistors).
Maybe pre-populated jumpers and resistors, and the person doing the customization just needs a diagonal cutter instead of a soldering iron?

Code: Select all

 01:CC80:AD 00 50  LDA $5000 = #$EA
[...]
 01:CCA4:8D 01 50  STA $5001 = #$EA
 01:CCAA:8D 02 50  STA $5002 = #$EA
 01:CCB0:8D 03 50  STA $5003 = #$EA
So it ultimately switches between one of three different stubs, all of which have access to the same bank at $C000, depending on the value from the jumper pack. Odd.
User avatar
krzysiobal
Posts: 1036
Joined: Sun Jun 12, 2011 12:06 pm
Location: Poland
Contact:

Re: Reverse engineering funny multi-game cartridge

Post by krzysiobal »

I got this cart in possession again, so I was able to make second attempt to work on it.

Code: Select all

Pal equations:

CIRAM_A10 = (PPU_A10 & !REG_A7) | (PPU_A11 & REG_A7);
BUF_nOE = (!CPU_RnW) | (!CPU_nROMSEL) | (!CPU_A12) | (!CPU_A14) | (CPU_A13);
PRG_A16 = (!REG_A6 & CPU_A14) | (REG_A2);
PRG_A15 = (!REG_A6 & CPU_A14 & !REG_A2) | (!REG_A6 & REG_A2 & REG_A0) | (REG_A1);
PRG_A14 = (!REG_A6 & CPU_A14 & !REG_A1) | (!REG_A6 & CPU_A14 & !REG_A2) | (REG_A0);
REG_WR = (CPU_A14 & !CPU_A13 & CPU_A12 & CPU_nROMSEL & !CPU_RnW);
-------------------------------------------------------------------------------
Write register ($5000-$5fff)
A~[0101 .... mv.. Pppp]
             ||   ||||
             ||   |+++- inner PRG (16k) & CHR (8k) bank, real bank is some 
             ||   |     hash of ppp (which is aaa/bbb/ccc) depending on the v
             ||   +---- outer PRG & CHR bank
             |+-------- PRG mode
             +--------- mirroring (0=V, 1=H)

CPU memory map:        PPU memory map:      Hashes of ppp:

v | $8000 | $c000      v | $0000            ppp | aaa bbb ccc
--+-------+------      --+------            ----+-------------
0 |  Paaa | Pbbb       0 | Paaa/Pbbb (*)     0  |  4   3   4
1 |  Pccc | Pccc       1 | Pccc              1  |  5   3   5
                                             2  |  6   3   6
                                             3  |  7   3   7
                                             4  |  0   1   0
                                             5  |  3   3   1
                                             6  |  2   2   2
                                             7  |  3   3   3

(*) Take care that when v=0, CHR bank will be switched depending on which memory
region CPU is currently accessing (when A14=0 then Paaa, when A14=1 then Pbbb)
			 
-------------------------------------------------------------------------------------------------------------
Read register ($5000-$5fff)
[.... DDDD] 
 |||| ||||
 |||| ++++- value of dip switch (1=dip closed, 0=dip open)
 ++++------ open bus
 
Depending on the value returned by DDDD, game shows different versions of menu:

DDDD | Menu
-----+------------
0000 | 1KK000 IN 1
0001 | 900000 IN 1
0010 | 800000 IN 1
0011 | 700000 IN 1
0100 | 600000 IN 1
0101 | 500000 IN 1
0110 | 400000 IN 1
0111 | 300000 IN 1
1000 | 200000 IN 1
1001 | 100000 IN 1
1010 |  80000 IN 1
1011 |  60000 IN 1
1100 |  50000 IN 1
1101 |  40000 IN 1
1110 |  30000 IN 1
1111 |  10000 IN 1
	  
If there are no components soldered (74125 buffer chip and resistors), reading $5000-$5fff will return open bus, which in hardware will be high byte of read address). Menu code reads multiple times from $5000/$5001 and it exits loop inly if it finds that two last read values match, otherwise it will stay in infinite loop waiting for them to be equal.
nocash
Posts: 1405
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: Reverse engineering funny multi-game cartridge

Post by nocash »

Board name is "RM2M1M" (just mentioning in case somebody else is searching for info on that board).
The last four characters are apparently meaning 2Mbit+1Mbit for PRG+CHR.
lidnariq wrote:
I think the 'jumpers' are rather intended to be used with some kind of switch, rather than jumpers (otherwise they would not put places for resistors).
Maybe pre-populated jumpers and resistors, and the person doing the customization just needs a diagonal cutter instead of a soldering iron?
My first impression was that they tried to hide the jumpers underneath of the chip. Though, with the through-holes, they aren't hidden too well, and the feature does look quite useless no matter how it's used.
krzysiobal wrote:(*) Take care that when v=0, CHR bank will be switched depending on which memory
region CPU is currently accessing (when A14=0 then Paaa, when A14=1 then Pbbb)
That isn't so funny for PPU emulation. It's worse enough if it's affected by the CPU cartridge memory space. And for internal memory at CPU address 0000h-4xxxh, I don't even know if the CPU outputs anything (and what) on A14.
Does the software actually rely on that feature?
krzysiobal wrote:Programmers divided the menu into 1-5000, 5001-10000, etc for easier navigation.
Wonderful idea!
Post Reply