Skip to content

Commit

Permalink
Add mapper #53 MAPPER_GG_Real_24_in_1_FFFE_0000_FFFF for "Real 24 i…
Browse files Browse the repository at this point in the history
…n 1 [Sonic II] (Unl)"

I've been calling it "Real 24 in 1 [Sonic II] (Unl)" but the correct name is not obvious

The label says:

REAL 24 IN 1
NEW GAME

The label also has a game listing in English and Traditional Chinese. Transcription of the English part:

```
 1.SONIC Ⅱ
 2.BARE KNUCKLE
 3.SHINOBI Ⅱ
 4.SUPER MONACO GP
 5.MAGIC ADVENTURE
 6.NINJA GAIDEN
 7.PAC MAN
 8.WOODY POP
 9.COLUMAS
10.PENGO
11.TEDDYBOY-BLUES
12.ASTRD FLASH O
13.ACTION BOY
14.BANK PANIC
15.MACHINEGUN
16.HANG-ON
17.GHOST HOUSE
18.SPY VS SPY
19.SUPER TENNIS
20.SATELLITE 7
21.GREAT SOCCER
22.GREAT BASEBALL 2
23.ROAD FIGHTER
24.PIT POT
```

Chinese label text as transcribed by Google:
```
 1.音速小子二代
 2.格闘3人組
 3.GG忍 二代
 4.超級摩洛哥GP
 5.魔法颱風
 6.忍者外傳
 7.小精靈
 8.玩具磚塊
 9.魔法寶石
10.企鵝推冰
11.泰迪男孩
12.空中戰機
13.青春英雄
14.銀行大盗
15.機關槍
16.摩托賽車
17.鬼屋
18.諜對諜
19.網球
20.衛星七號
21.足球
22.棒球
23.路十六
24.陷阱
```

The cartridge plastic is molded with the word "TIME" where you might expect to see "SEGA" instead

The label also includes art for G.G. Shinobi II and a gradient background (yellow at the top, light green at the bottom)

This is a 2MB ROM containing a menu and 24 games, including 10 Game Gear games, 13 SMS games, and one MSX game with an MSX BIOS replacement. The games: GG: Pengo, Columns, Woody Pop, Pac-Man, Ninja Gaiden, Magical Taruruuto-kun, Super Monaco GP, The GG Shinobi II, Bare Knuckle, Sonic the Hedgehog 2; SMS: Pit Pot, Great Baseball, Great Soccer, Satellite-7, Super Tennis, Spy vs Spy, Ghost House, Hang-On, Machine Gun Joe, Bank Panic, Seishun Scandal, Astro Flash, Teddyboy Blues; MSX: Road Fighter

Dumping this was a bit painful. This is the script I used to dump each of the parts, disconnecting and reconnecting the cartridge in between parts:
```
// Real 24 in 1 [Sonic II] (Unl)
//
// |Size| ID | Name                | ID | Name                |
// |----|----|---------------------|----|---------------------|
// | 32K|0x00|Menu                 |0x01|Pit Pot              |
// |    |0x02|MSX Road Fighter     |0x03|Great Baseball       |
// |    |0x04|Great Soccer         |0x05|Satellite-7          |
// |    |0x06|Super Tennis         |0x07|Spy vs Spy           |
// |    |0x08|Ghost House          |0x09|Hang-On              |
// |    |0x0A|Machine Gun Joe      |0x0B|Bank Panic           |
// |    |0x0C|Seishun Scandal      |0x0D|Astro Flash          |
// |    |0x0E|Teddyboy Blues       |0x0F|Pengo                |
// |    |0x10|Columns              |0x11|Woody Pop            |
// |----|----|---------------------|----|---------------------|
// | 64K|0x12|Pac-Man              |    |                     |
// |----|----|---------------------|----|---------------------|
// |128K|0x14|Ninja Gaiden         |0x18|Magical Taruruuto-kun|
// |    |0x1C|Super Monaco GP      |    |                     |
// |----|----|---------------------|----|---------------------|
// |256K|0x20|The GG Shinobi II    |0x28|Bare Knuckle         |
// |----|----|---------------------|----|---------------------|
// |512K|0x30|Sonic the Hedgehog 2 |    |                     |
//
// NOTE: You will need to disconnect and reconnect between
// game_id values. I have not found a way to reliably
// reconfigure the mapper multiple times without power-cycling
local game_id = 0x00;

// seems the hardware does not work reliably with this
// dumping setup unless you repeat yourself a bunch...
local outer_page_attempts = 3;
local quiesce_retries = 2; // for writes to similar-addressed RAM
local write_retries = 5;

// this allows us to avoid multiple identical (slow!)
// reconfigurations after the configuration has already been
// used for reading
local previous_outer_page = -1;
for (local page = game_id; (address_scale_factor * (page - game_id) * 0x8000) < rom_size; page += 1) {

    local extra_bits =
        ((page >= 0x12) ? 0x81 : 0x40) // >= 64K with mapper, otherwise SMS-GG mode
        | ((page >= 0x14) ? 0x02 : 0x00) // >= 128K
        | ((page >= 0x20) ? 0x04 : 0x00) // >= 256K
        | ((page >= 0x30) ? 0x08 : 0x00) // 512K
        | 0;

    local outer_page = page | extra_bits;

    if (outer_page != previous_outer_page) {
        for (local outer_page_attempt = 0; outer_page_attempt < 3; outer_page_attempt += 1) {
            for (local quiesce_retry = 0; quiesce_retry < quiesce_retries; quiesce_retry += 1) {
                cpu_write(d, address_scale_factor * 0xC000, outer_page);
            }
            for (local write_retry = 0; write_retry < write_retries; write_retry += 1) {
                cpu_write(d, address_scale_factor * 0x0000, outer_page);
            }
            for (local quiesce_retry = 0; quiesce_retry < quiesce_retries; quiesce_retry += 1) {
                cpu_write(d, address_scale_factor * 0xC000, outer_page);
            }
        }
        previous_outer_page = outer_page;
    }

    local inner_page = page & 0x0F & ~extra_bits;

    if (inner_page == 0x00) {
        // possibly missing Sega mapper: use 0x0000..0x7FFF
        for (local quiesce_retry = 0; quiesce_retry < quiesce_retries; quiesce_retry += 1) {
            cpu_write(d, address_scale_factor * 0xDFFE, (inner_page << 1) | 1);
        }
        for (local write_retry = 0; write_retry < write_retries; write_retry += 1) {
            cpu_write(d, address_scale_factor * 0xFFFE, (inner_page << 1) | 1);
        }
        for (local quiesce_retry = 0; quiesce_retry < quiesce_retries; quiesce_retry += 1) {
            cpu_write(d, address_scale_factor * 0xDFFE, (inner_page << 1) | 1);
        }
        cpu_read (d, address_scale_factor * 0x0000, address_scale_factor * 0x8000);
    } else {
        // Sega mapper: use 0x8000..0xBFFF twice
        for (local offset = 0; offset < 2; offset += 1) {
            for (local quiesce_retry = 0; quiesce_retry < quiesce_retries; quiesce_retry += 1) {
                cpu_write(d, address_scale_factor * 0xDFFF, (inner_page << 1) | offset);
            }
            for (local write_retry = 0; write_retry < write_retries; write_retry += 1) {
                cpu_write(d, address_scale_factor * 0xFFFF, (inner_page << 1) | offset);
            }
            for (local quiesce_retry = 0; quiesce_retry < quiesce_retries; quiesce_retry += 1) {
                cpu_write(d, address_scale_factor * 0xDFFF, (inner_page << 1) | offset);
            }
            cpu_read (d, address_scale_factor * 0x8000, address_scale_factor * 0x4000);
        }
    }
}
```

The menu is based on Pengo code, but that copy of Pengo is no longer playable. However there is a separate playable copy of Pengo elsewhere in the ROM. At startup the menu writes:

[0xFFFE=0xB0]

The "➡" arrow cursor is movable and shown below in its initial position. Unlike many Game Gear multicart menus, this one preserves cursor offset while switching screens

Menu screen 1:

```

 ┌────────────────┐
 │  REAL 24 IN 1  │
 └────────────────┘

➡ 1 SONIC Ⅱ          [0x0000=0xBF]; it's [GG] part-30-sonic-the-hedgehog-2-512k.gg
  2 BARE KNUCKLE     [0x0000=0xAF]; it's [GG] part-28-bare-knuckle-256k.gg
  3 SHINOBI Ⅱ        [0x0000=0xA7]; it's [GG] part-20-the-gg-shinobi-ii-256k.gg
  4 SUPER MONACO GP  [0x0000=0x1F]; it's [GG] part-1c-super-monaco-gp-128k.gg
  5 MAGIC ADVENTURE  [0x0000=0x1B]; it's [GG] part-18-magical-taruruuto-kun-128k.gg
  6 NINJA GAIDEN     [0x0000=0x17]; it's [GG] part-14-ninja-gaiden-128k.gg
  7 PAC MAN          [0x0000=0x13]; it's [GG] part-12-pac-man-64k.gg
  8 WOODY POP        [0x0000=0x11]; it's [GG] part-11-woody-pop-32k.gg
  9 COLUMNS          [0x0000=0x10]; it's [GG] part-10-columns-32k.gg
 10 PENGO            [0x0000=0x0F]; it's [GG] part-0f-pengo-32k.gg
 11 TEDDYBOY-BLUES   [0x0000=0x4E]; it's [SMS-GG] part-0e-teddyboy-blues-32k.sms
 12 ASTRO FLASH      [0x0000=0x4D]; it's [SMS-GG] part-0d-astro-flash-32k.sms
```

Menu screen 2:

```

 ┌────────────────┐
 │  REAL 24 IN 1  │
 └────────────────┘

➡13 ACTION BOY       [0x0000=0x4C]; it's [SMS-GG] part-0c-seishun-scandal-32k.sms
 14 BANK PANIC       [0x0000=0x4B]; it's [SMS-GG] part-0b-bank-panic-32k.sms
 15 MACHINEGUN       [0x0000=0x4A]; it's [SMS-GG] part-0a-machine-gun-joe-32k.sms
 16 HANG-ON          [0x0000=0x49]; it's [SMS-GG] part-09-hang-on-32k.sms
 17 GHOST HOUSE      [0x0000=0x48]; it's [SMS-GG] part-08-ghost-house-32k.sms
 18 SPY VS SPY       [0x0000=0x47]; it's [SMS-GG] part-07-spy-vs-spy-32k.sms
 19 SUPER TENNIS     [0x0000=0x46]; it's [SMS-GG] part-06-super-tennis-32k.sms
 20 SATELLITE·7      [0x0000=0x45]; it's [SMS-GG] part-05-satellite-7-32k.sms
 21 GREAT SOCCER     [0x0000=0x44]; it's [SMS-GG] part-04-great-soccer-32k.sms
 22 GREAT BASEBALL   [0x0000=0x43]; it's [SMS-GG] part-03-great-baseball-32k.sms
 23 ROAD FIGHTER     [0x0000=0x42]; it's [SMS-GG] part-02-msx-bios-and-road-fighter-32k.sms
 24 PIT POT          [0x0000=0x41]; it's [SMS-GG] part-01-pit-pot-32k.sms
```

The mapper registers seem to be 0x0000, 0xFFFE, and 0xFFFF. Register 0x0000 is used for outer paging and SMS-GG mode selection. The other two are used for Sega-style paging.

Inferred mapper behavior, not yet tested exhaustively:

0x0000:
- bit 0x80: set when mapping the second megabyte, clear otherwise
- bit 0x40: set when activating SMS-GG mode, clear otherwise
- bits 0x3F: 32KB base page for outer page selection

For paging to work, the base page must be in an eligible region and
all paging-modifiable bits of the 32KB base page must be set

16KB paging mask bits:
- 0x10: when base page bit 0x08 is set and base page > 0x30
- 0x08: when base page bit 0x04 is set and base page > 0x20
- 0x04: when base page bit 0x02 is set and base page > 0x14
- 0x02: when base page bit 0x01 is set and base page > 0x12
- 0x01: always available

0xFFFE: 16KB paging offset for region 0x4000..0x7FFF (restricted by 16KB paging mask)
- 0xB0 is a special value and its meaning is unknown; it leaves the page set to 0x01

0xFFFF: 16KB paging offset for region 0x8000..0xBFFF (restricted by 16KB paging mask)

ROM fingerprint info:

2.0M Real 24 in 1 [Sonic II] (Unl).gg crc32:de6471e8 mekacrc:0E01DB0FE65FBA08 md5:d8cadcb912565e410f4278c516f561e8 sha1:763a3cdbfac247a90009f396bf9691ca6024b9de sha256:3054ad4c1d9ba02ba53e5b14d2f476c6c3525ff9fda339ef0d49383d6fd959d0

32K part-00-menu-32k.gg crc32:d80fb38b mekacrc:A19A21C2FEB5CC63 md5:e0ee46da13c7b89ffbcc40dc86a70b47 sha1:482e3563dde051e57bf40a60e5b84e208b134f65 sha256:218106d54268cf7a39a4d1f9c4317b5556df7645e7a16e8fa866b3bfa3a6c11d

32K part-01-pit-pot-32k.sms crc32:f81ab71e mekacrc:CA42846BB2AE8B1A md5:b3e6b1285bf8b820ca1344c7c4519efa sha1:55036c365d7d4496c5c5e0bfd01c13dfdb1a79f1 sha256:6679ea24710cf51b3aaa5fbee043e5ed6c446912cbc4729ab3fe4668424d1877

32K part-02-msx-bios-and-road-fighter-32k.sms crc32:e8d8e99c mekacrc:3395440A4DC7983E md5:7e82a5af56942c64c288c54050ea16fd sha1:9ed1d924035d94c7dad56644b58de88bc7d45612 sha256:01fd02d70e1672e085496c479945a91dc82362a1c7435fd05476c76538a3e0c3

32K part-03-great-baseball-32k.sms crc32:aafafd51 mekacrc:B478FD7132EF8ABB md5:ab3e87f4981bda8c486392719471ef08 sha1:9133fb22586ab10ee142192541ceb368b9b02540 sha256:8afd26d8aa991fee9e755d53cd0d2ee6f18a058580d44b5240a4c70d8997dd0e

32K part-04-great-soccer-32k.sms crc32:be711818 mekacrc:7CB0BCEF29C11827 md5:cecccff2d09d3cc705d848b28037e267 sha1:41d55da9ed00124c570d9964494b791e09d93af8 sha256:b48ce6a09946941fdf62e8ebf611fc1706344e26ea230bb206ba98cf04e9fbd2

32K part-05-satellite-7-32k.sms crc32:eb9a0359 mekacrc:C30BB9170D4D8E7A md5:7cb52be125ef66daa5e7a6e971a2a97f sha1:be0ace7ed8f1f1ab6fa51dfaa3ba2dd167ab3863 sha256:3adcd01767ee06949a930b88b76f9e2d8c89031c41e289af9d2e4290455ee5c1

32K part-06-super-tennis-32k.sms crc32:95d4c673 mekacrc:FEA3F1C357520CF6 md5:6d135ff05b6d440fa329cc060e559244 sha1:62b323b7b3188b2e0557e3e6d799edc71813c82e sha256:aa07700850080a8276bc518183e09035449a73e9c697415555bc14eca80e539b

32K part-07-spy-vs-spy-32k.sms crc32:f13a28c0 mekacrc:98EC9FA8477AEC88 md5:324741e68234ae80ff3df8bc3f411557 sha1:fd7d1bee1ca42c6d962780f21d5ad138b792db59 sha256:bcba12678775542b59694fc8fe3945530490cdd92a2bfafb07f74b189fa03ede

32K part-08-ghost-house-32k.sms crc32:317451a3 mekacrc:A5771FE4EC52960D md5:343210de693491444b452df7b665921d sha1:9917f8f06ed4c99b71d58d8a03fbea13330c84a1 sha256:c690ae4bff9711c0244e9a2ab18a579a429b29191dcc993f0ed5a496b472cfd3

32K part-09-hang-on-32k.sms crc32:40cedfd9 mekacrc:02A62976104874ED md5:5c25b6b38963fc513958a26e64c591d4 sha1:5726c7f7948612ca42daed96ded45745393bd80a sha256:5a2ecca3d78ae9aa8a6ac2c1775122cfdce3744898032ce5763c0017350e58ce

32K part-0a-machine-gun-joe-32k.sms crc32:9a94c7cc mekacrc:5C858AC9BD4714B4 md5:02906381c66601fb49b10bed123455d4 sha1:03a8a50b3b9e97ea5e10d7e094d287b51ca477af sha256:3981649682a895772012a769e8296e24e660ac4fc4102ecfbb747d53d18cee3e

32K part-0b-bank-panic-32k.sms crc32:46e1c722 mekacrc:EBA230C857CA76E4 md5:00a1f4b983ac30d05dd7cfb9bdbb2ee7 sha1:b05ac9fd2478d69f8f3bdd96ad86bbec482541f1 sha256:5c6e1f75255de9d1a7fc7d10db0ea9126633eedd018873597f78b312d2a80032

32K part-0c-seishun-scandal-32k.sms crc32:637dbbef mekacrc:D41A76F3C3411E87 md5:c021bce5a58cfbf513af253a25bc29bd sha1:2c39fdf814e223e2dc14646941a276879ae14c64 sha256:3a1df738527b578e248b6650c9a1498abaf9bef6da813a172dfae0a63be4e746

32K part-0d-astro-flash-32k.sms crc32:b1267d52 mekacrc:13171118D3218C2D md5:4c13f02b69c266ffea80fd7aba160bb2 sha1:b4e2483fbfe9e4c23a6fb14e755e5aa8c6ecece0 sha256:514484c1d4156fc77e4cefcd39cffd71f3e126c708420100ebb07e4972dc3486

32K part-0e-teddyboy-blues-32k.sms crc32:ed8dee80 mekacrc:E805AC40FA83F3B7 md5:acb4e818db613dc6f4737ef21302dc7c sha1:19d03cefeca768b08e708a161d3d6f5398450f49 sha256:e99eb0859de5801fddcf88a7d87651a6209bfe5df4f75bbd85c0b311edf6704c

32K part-0f-pengo-32k.gg crc32:6bd56e95 mekacrc:6C3C703170EBD587 md5:69bf00297cad6ee0bb6046c8084e5e37 sha1:126654feb2975e3a52431d29cefcafbd37bb2ef5 sha256:1c221bcb0b7ef67e644a695b78cb4dd851b800202ee13fd8d03fb099e533c46a

32K part-10-columns-32k.gg crc32:7316423e mekacrc:1DE54704572F032A md5:bfcf0e46ec8df8a51776bd2315b859fc sha1:26d407e8389a5564b836f0a68585a935ca019802 sha256:73f6012c1a92bcc96497668cdecfb3b592162c6b39a8c7efb8b9dc3931380561

32K part-11-woody-pop-32k.gg crc32:0c6e4769 mekacrc:9D2A47DDDF12FD27 md5:3885c64be1dc990918adebcbe9675855 sha1:58516c23f1e1b87daebda1b971fab278a6e7ce59 sha256:0c23fccffa3160b6754fa54ecbf08d3828f5e60a64dbe4203109715644ad94ce

64K part-12-pac-man-64k.gg crc32:dd001845 mekacrc:730EF79FF63F971D md5:fa8940099182cb57dc7eaa3466f43859 sha1:7d4521b55386eb7f06fed6b143c0e97bdfab264b sha256:d5b2c6fc05041802db1e5506ba9df8edf5415bbfe7c5852620cdc5f8bbb261f9

128K part-14-ninja-gaiden-128k.gg crc32:7ca9e75e mekacrc:430F39097D9A3025 md5:d9201f5ced95468d893e0f1b24438eea sha1:86681e978b3c0088d077afe850f4f9d278f5fa30 sha256:dc574770c07c5fc58b9cac371ac16307166a000df9a4015d0a2286626c2e9a72

128K part-18-magical-taruruuto-kun-128k.gg crc32:11d11451 mekacrc:1062032CD75EE446 md5:04fc405128c80de8358903a2a3b967d9 sha1:96fa1d2a82e5d0ccc3f2b7b6f1c85a3e098d048a sha256:bbcb5abea2e18b8d9e4a852b32f0b65e6aea4556f3deb70540dccb91d01b3c1b

128K part-1c-super-monaco-gp-128k.gg crc32:08ebcea1 mekacrc:9D34492A4095AD3A md5:1737583c8a857c84798ff254167025d9 sha1:69ffd99a1589bb77ca9943cbf08e07996dd0e8da sha256:08f11a8927879b681e2b23c7f3bb103002dd5468b3a58e1d6cff7bc0789af4ed

256K part-20-the-gg-shinobi-ii-256k.gg crc32:1f8f7d7c mekacrc:5312EB420F84AA31 md5:735c85911371d3c8fb18c33093307718 sha1:68e2f2b58ecbd038a4c8a9d004f2b9dee8a3b88d sha256:545d4b460b486d29d9f42029e91896eef5b0331b6cb9ea86df0ccc5e42d27de5

256K part-28-bare-knuckle-256k.gg crc32:dacc9061 mekacrc:73DB350D2119AB8B md5:80257cab0a9ed965520beeaba5bd5c9f sha1:815a1c910d016d374fff02604b521f147f529291 sha256:a027321ec909bb69ca44486a8e04193915cf733c88974a7dbc7d8551841f4461

512K part-30-sonic-the-hedgehog-2-512k.gg crc32:cda97bb3 mekacrc:DB692161E347F020 md5:6685be3154439062030746a179589090 sha1:9bd6690330a35e6d3ad6c0230c9d8984a2e2de0c sha256:8c4b4207d8c6be2c5a9d9f1ab0d3df5c76d65ce9e26ae6c9e1c6b45babf3a9b7
  • Loading branch information
bsittler committed Jun 10, 2023
1 parent 1566428 commit 16af322
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 4 deletions.
3 changes: 2 additions & 1 deletion meka/compat.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,7 @@
Quiz Gear Fight!!, The (JP) Ok
Rastan Saga [SMS-GG] (JP) Ok
R.C. Grand Prix [SMS-GG] Ok
Real 24 in 1 [Sonic II] *Ok
Revenge of Drancon Ok
Riddick Rowe Boxing (US) Ok
Riddick Rowe Boxing (JP) Ok
Expand Down Expand Up @@ -1502,7 +1503,7 @@
Zoop (US) Ok
Zoop [Proto] (US) Ok
-----------------------------------------------------------------------------
517 games tested - 506 are "Ok" - Compatibility rate: 97.63%
518 games tested - 507 are "Ok" - Compatibility rate: 97.88%
-----------------------------------------------------------------------------

-----------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions meka/meka.nam
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,7 @@ GG 8e54ee04 E463A36AE4D522D1 Puzzle Bobble/COUNTRY=JP/PRODUCT_NO=T-11047
GG 6c451ee1 2A50C584823D710D Quest for the Shaven Yak Starring Ren Hoëk & Stimpy/COUNTRY=US,EU/PRODUCT_NO=2519
GG 736cdb76 70BA6E9D99C81B4F Quiz Gear Fight!!, The/COUNTRY=JP/PRODUCT_NO=G-3367
GG 6dc3295e D56E2790AD1DEE4E R.B.I. Baseball '94/COUNTRY=US/PRODUCT_NO=T-48168 301073-0161
GG de6471e8 0E01DB0FE65FBA08 Real 24 in 1 [Sonic II]/EMU_MAPPER=53
GG 03e9c607 EACA76A0EF9F1D8B Revenge of Drancon/NAME_BR=Wonder Boy/COUNTRY=US,BR/PRODUCT_NO=2302,012080/COMMENT=US version of "Wonder Boy", with a different title.
GG 38d8ec56 46EF1611B14E079E Riddick Bowe Boxing/COUNTRY=US/PRODUCT_NO=T-22028
GG a45fffb7 45EF1710B24D079F Riddick Bowe Boxing/COUNTRY=JP/PRODUCT_NO=T-22027
Expand Down
20 changes: 20 additions & 0 deletions meka/srcs/machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ void Machine_Set_Handler_MemRW(void)
case MAPPER_SMS_Korean_MSX_SMS_8000:
WrZ80 = WrZ80_NoHook = Write_Mapper_SMS_Korean_MSX_SMS_8000;
break;
case MAPPER_GG_Real_24_in_1_FFFE_0000_FFFF:
WrZ80 = WrZ80_NoHook = Write_Mapper_GG_Real_24_in_1_FFFE_0000_FFFF;
break;
}
}

Expand Down Expand Up @@ -502,6 +505,23 @@ void Machine_Set_Mapping (void)
g_machine.mapper_regs[i] = 0;
break;

case MAPPER_GG_Real_24_in_1_FFFE_0000_FFFF:
Map_8k_ROM(0, 0 & tsms.Pages_Mask_8k);
Map_8k_ROM(1, 1 & tsms.Pages_Mask_8k);
Map_8k_ROM(2, 2 & tsms.Pages_Mask_8k);
Map_8k_ROM(3, 3 & tsms.Pages_Mask_8k);
Map_8k_ROM(4, 4 & tsms.Pages_Mask_8k);
Map_8k_ROM(5, 5 & tsms.Pages_Mask_8k);
Map_8k_RAM(6, 0);
Map_8k_RAM(7, 0);
g_machine.mapper_regs_count = 3;
for (int i = 0; i != MAPPER_REGS_MAX; i++)
g_machine.mapper_regs[i] = 0;
g_machine.mapper_regs[1] = 2;
g_machine.mapper_regs[2] = 1;
drv_set(DRV_GG);
break;

case MAPPER_SC3000_Survivors_Multicart:
g_machine.mapper_regs_count = 1;
for (int i = 0; i != MAPPER_REGS_MAX; i++)
Expand Down
80 changes: 80 additions & 0 deletions meka/srcs/mappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
#include "shared.h"
#include "mappers.h"
#include "eeprom.h"
#include "vdp.h"
#include "video.h"
#include "app_game.h"

//-----------------------------------------------------------------------------
// Data
Expand Down Expand Up @@ -989,6 +992,83 @@ WRITE_FUNC (Write_Mapper_SMS_Korean_MSX_SMS_8000)
Write_Error (Addr, Value);
}

// Mapper #53
// Real 24 in 1 [Sonic II] (Unl)
WRITE_FUNC (Write_Mapper_GG_Real_24_in_1_FFFE_0000_FFFF) {
if ((Addr == 0x0000) || (Addr == 0xFFFE) || (Addr == 0xFFFF)) { // Configurable segment -----------------------------------------------
// mapper register allocation:
// 0: 0x0000: outer paging configuration and SMS-GG mode
// - bit 0x80: set when mapping the second megabyte, clear otherwise
// - bit 0x40: set when activating SMS-GG mode, clear otherwise
// - bits 0x3F: 32KB base page for outer page selection
//
// For paging to work, the base page must be in an eligible region and
// all paging-modifiable bits of the 32KB base page must be set
//
// 16KB paging mask bits:
// - 0x10: when base page bit 0x08 is set and base page > 0x30
// - 0x08: when base page bit 0x04 is set and base page > 0x20
// - 0x04: when base page bit 0x02 is set and base page > 0x14
// - 0x02: when base page bit 0x01 is set and base page > 0x12
// - 0x01: always available
//
// 1: 0xFFFF: 16KB paging offset for region 0x8000..0xBFFF (restricted by 16KB paging mask)
// 2: 0xFFFE: 16KB paging offset for region 0x4000..0x7FFF (restricted by 16KB paging mask)

if (Addr == 0x0000) {
g_machine.mapper_regs[0] |= Value;
bool sms_gg_mode = (g_machine.mapper_regs[0] & 0x40) ? true : false;
if (sms_gg_mode) {
drv_set(DRV_SMS);
} else {
drv_set(DRV_GG);
}
gamebox_resize_all();
VDP_UpdateLineLimits();
Video_GameMode_UpdateBounds();
} else if (Addr == 0xFFFF) {
g_machine.mapper_regs[1] = Value;
} else if (Addr == 0xFFFE) {
if (Value & 0x80) {
// 0xB0 is written at startup, no idea what it means
} else {
g_machine.mapper_regs[2] = Value;
}
}

unsigned int base_page_32k = (g_machine.mapper_regs[0] & 0x3F) | ((g_machine.mapper_regs[0] & 0x80) >> 2);
unsigned int paging_mask_16k = 0x01 | (2 * (
((base_page_32k > 0x12) ? (base_page_32k & 0x01) : 0x00)
| ((base_page_32k > 0x14) ? (base_page_32k & 0x02) : 0x00)
| ((base_page_32k > 0x20) ? (base_page_32k & 0x04) : 0x00)
| ((base_page_32k > 0x30) ? (base_page_32k & 0x08) : 0x00)));
unsigned int base_page_16k = (2 * base_page_32k) & ~paging_mask_16k;
unsigned int page_8000_offset_16k = g_machine.mapper_regs[1] & paging_mask_16k;
unsigned int page_4000_offset_16k = g_machine.mapper_regs[2] & paging_mask_16k;

Map_8k_ROM(0, ((base_page_16k * 2) | 0) & tsms.Pages_Mask_8k);
Map_8k_ROM(1, ((base_page_16k * 2) | 1) & tsms.Pages_Mask_8k);
Map_8k_ROM(2, (((base_page_16k | page_4000_offset_16k) * 2) | 0) & tsms.Pages_Mask_8k);
Map_8k_ROM(3, (((base_page_16k | page_4000_offset_16k) * 2) | 1) & tsms.Pages_Mask_8k);
Map_8k_ROM(4, (((base_page_16k | page_8000_offset_16k) * 2) | 0) & tsms.Pages_Mask_8k);
Map_8k_ROM(5, (((base_page_16k | page_8000_offset_16k) * 2) | 1) & tsms.Pages_Mask_8k);

if (Addr == 0x0000) {
// no RAM mirroring for register 0x0000
return;
}
}

switch (Addr >> 13)
{
// RAM [0xC000] = [0xE000] ------------------------------------------------
case 6: Mem_Pages[6][Addr] = Value; return;
case 7: Mem_Pages[7][Addr] = Value; return;
}

Write_Error (Addr, Value);
}

// Based on MSX ASCII 8KB mapper? http://bifi.msxnet.org/msxnet/tech/megaroms.html#ascii8
// - This mapper requires 4 registers to save bank switching state.
// However, all other mappers so far used only 3 registers, stored as 3 bytes.
Expand Down
2 changes: 2 additions & 0 deletions meka/srcs/mappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#define MAPPER_SMS_Korean_MD_FFFA (26) // Registers at 0xFFFA and 0xFFFF (Game Jiphap 30 Hap [SMS-MD])
#define MAPPER_SMS_Korean_MSX_32KB_2000 (27) // Register at 0x2000 (2 Hap in 1 (Moai-ui bomul, David-2))
#define MAPPER_SMS_Korean_MSX_SMS_8000 (40) // Register at 0x8000 with 8KB granularity and both MSX and SMS game support (Zemina Best 88 [MISSING 64K])
#define MAPPER_GG_Real_24_in_1_FFFE_0000_FFFF (53) // Registers at 0xFFFE, 0x0000, 0xFFFF (Real 24 in 1 [Sonic II])

#define READ_FUNC(_NAME) u8 _NAME(register u16 Addr)
#define WRITE_FUNC(_NAME) void _NAME(register u16 Addr, register u8 Value)
Expand Down Expand Up @@ -98,6 +99,7 @@ WRITE_FUNC (Write_Mapper_SMS_Korean_MD_FFF5);
WRITE_FUNC (Write_Mapper_SMS_Korean_MD_FFFA);
WRITE_FUNC (Write_Mapper_SMS_Korean_MSX_32KB_2000);
WRITE_FUNC (Write_Mapper_SMS_Korean_MSX_SMS_8000);
WRITE_FUNC (Write_Mapper_GG_Real_24_in_1_FFFE_0000_FFFF);
//-----------------------------------------------------------------------------
void Out_SC3000_SurvivorsMulticarts_DataWrite(u8 v);

Expand Down
19 changes: 16 additions & 3 deletions meka/srcs/saves.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include "vmachine.h"
#include "sound/fmunit.h"
#include "sound/psg.h"
#include "video.h"
#include "app_game.h"

//-----------------------------------------------------------------------------
// Functions
Expand All @@ -26,6 +28,7 @@ void Load_Game_Fixup(void)
{
int i;
u8 b;
bool sms_gg_mode_in_mapper = false;

// CPU
#ifdef MARAT_Z80
Expand Down Expand Up @@ -151,13 +154,21 @@ void Load_Game_Fixup(void)
WrZ80_NoHook(0x8000, mapper_page);
}
break;
case MAPPER_GG_Real_24_in_1_FFFE_0000_FFFF:
// this will restore every other piece of mapper state by side-effect;
// this will also configure SMS-GG mode if needed
WrZ80_NoHook(0x0000, g_machine.mapper_regs[0]);
sms_gg_mode_in_mapper = true;
break;
}
}

// VDP/Graphic related
tsms.VDP_Video_Change |= VDP_VIDEO_CHANGE_ALL;
VDP_UpdateLineLimits();
// FALSE!!! // tsms.VDP_Line = 224;
if (!sms_gg_mode_in_mapper) {
tsms.VDP_Video_Change |= VDP_VIDEO_CHANGE_ALL;
VDP_UpdateLineLimits();
// FALSE!!! // tsms.VDP_Line = 224;
}

// Rewrite all VDP registers (we can do that since it has zero side-effect)
for (i = 0; i < 16; i ++)
Expand Down Expand Up @@ -347,6 +358,7 @@ int Save_Game_MSV(FILE *f)
case MAPPER_SMS_Korean_MD_FFFA:
case MAPPER_SMS_Korean_MSX_32KB_2000:
case MAPPER_SMS_Korean_MSX_SMS_8000:
case MAPPER_GG_Real_24_in_1_FFFE_0000_FFFF:
default:
fwrite (RAM, 0x2000, 1, f); // Do not use g_driver->ram because of g_driver video mode change
break;
Expand Down Expand Up @@ -527,6 +539,7 @@ int Load_Game_MSV(FILE *f)
case MAPPER_SMS_Korean_MD_FFFA:
case MAPPER_SMS_Korean_MSX_32KB_2000:
case MAPPER_SMS_Korean_MSX_SMS_8000:
case MAPPER_GG_Real_24_in_1_FFFE_0000_FFFF:
default:
fread (RAM, 0x2000, 1, f); // Do not use g_driver->ram because of g_driver video mode change
break;
Expand Down

0 comments on commit 16af322

Please sign in to comment.