From 16af322ab94868c7093d7510ecfa0eab2022453f Mon Sep 17 00:00:00 2001 From: "Benjamin C. Wiley Sittler" Date: Fri, 9 Jun 2023 23:16:30 -0700 Subject: [PATCH] Add mapper `#53` MAPPER_GG_Real_24_in_1_FFFE_0000_FFFF for "Real 24 in 1 [Sonic II] (Unl)" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- meka/compat.txt | 3 +- meka/meka.nam | 1 + meka/srcs/machine.cpp | 20 +++++++++++ meka/srcs/mappers.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++ meka/srcs/mappers.h | 2 ++ meka/srcs/saves.cpp | 19 ++++++++-- 6 files changed, 121 insertions(+), 4 deletions(-) diff --git a/meka/compat.txt b/meka/compat.txt index 306b6a89..c2dffd1b 100644 --- a/meka/compat.txt +++ b/meka/compat.txt @@ -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 @@ -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% ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- diff --git a/meka/meka.nam b/meka/meka.nam index 35d87c35..532d8cd0 100644 --- a/meka/meka.nam +++ b/meka/meka.nam @@ -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 diff --git a/meka/srcs/machine.cpp b/meka/srcs/machine.cpp index f6adcfdb..21c02be6 100644 --- a/meka/srcs/machine.cpp +++ b/meka/srcs/machine.cpp @@ -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; } } @@ -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++) diff --git a/meka/srcs/mappers.cpp b/meka/srcs/mappers.cpp index 7610fbee..481e3883 100644 --- a/meka/srcs/mappers.cpp +++ b/meka/srcs/mappers.cpp @@ -14,6 +14,9 @@ #include "shared.h" #include "mappers.h" #include "eeprom.h" +#include "vdp.h" +#include "video.h" +#include "app_game.h" //----------------------------------------------------------------------------- // Data @@ -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. diff --git a/meka/srcs/mappers.h b/meka/srcs/mappers.h index 0ea32122..a011cdc6 100644 --- a/meka/srcs/mappers.h +++ b/meka/srcs/mappers.h @@ -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) @@ -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); diff --git a/meka/srcs/saves.cpp b/meka/srcs/saves.cpp index 34012e4e..674405fb 100644 --- a/meka/srcs/saves.cpp +++ b/meka/srcs/saves.cpp @@ -16,6 +16,8 @@ #include "vmachine.h" #include "sound/fmunit.h" #include "sound/psg.h" +#include "video.h" +#include "app_game.h" //----------------------------------------------------------------------------- // Functions @@ -26,6 +28,7 @@ void Load_Game_Fixup(void) { int i; u8 b; + bool sms_gg_mode_in_mapper = false; // CPU #ifdef MARAT_Z80 @@ -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 ++) @@ -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; @@ -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;