diff options
Diffstat (limited to 'src/mame/konami/k573mcal.cpp')
-rw-r--r-- | src/mame/konami/k573mcal.cpp | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/src/mame/konami/k573mcal.cpp b/src/mame/konami/k573mcal.cpp new file mode 100644 index 00000000000..3fa74763fe3 --- /dev/null +++ b/src/mame/konami/k573mcal.cpp @@ -0,0 +1,211 @@ +// license:BSD-3-Clause +// copyright-holders:windyfairy +/* + * Konami 573 Master Calendar + * + * This device was made for development/factory use only. + * It will override the game when connected and always boot into the master calendar-specific code. + * Every game has a master calendar-specific boot sequence but a few that don't have code to initialize security cartridges. + * + * The only games that this does not work on that have a security cart are ddr2mc2, ddr2ml, and ddr2mla (all variants of 885jaa02). + * Those games will boot into a screen that shows the game code, clock, and date with nothing else. + * For these games it's possible to set Sys573 DIPSW 1 with the master calendar connected and it will do a checksum of the game's data + * and attempt to write it to "c:/tmp/chksum.dat" on the host debugger PC but will crash in MAME. + * + * DIPSW 2 and 3 on the System 573 directly are also used to specify the "spec" of the game. + * For example, setting DIPSW allows you to switch between GN and GE specs in earlier games. + * + * Some games require you to hold service/F2 (and set Sys573 DIPSW 3?) during boot to initialize the installation cartridge. + * + */ + +#include "emu.h" +#include "k573mcal.h" + +#include "machine/timehelp.h" + +#include "multibyte.h" + +k573mcal_device::k573mcal_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + jvs_device(mconfig, KONAMI_573_MASTER_CALENDAR, tag, owner, clock), + m_in1(*this, "IN1"), + seconds(0), + mainId(0), + subId(0) +{ +} + +void k573mcal_device::device_start() +{ + jvs_device::device_start(); +} + +void k573mcal_device::device_reset() +{ + seconds = 0; + + // Randomly picked values + // Is rendered in-game as x41-6379 where the x is generated based on the value of the main ID + mainId = 41; // valid range 0 - 99 + subId = 6379; // valid range 0 - 9999 + + jvs_device::device_reset(); +} + +const char *k573mcal_device::device_id() +{ + return "KONAMI CO.,LTD.;Master Calendar;Ver1.0;"; +} + +uint8_t k573mcal_device::command_format_version() +{ + return 0x11; +} + +uint8_t k573mcal_device::jvs_standard_version() +{ + return 0x20; +} + +uint8_t k573mcal_device::comm_method_version() +{ + return 0x10; +} + +int k573mcal_device::handle_message(const uint8_t* send_buffer, uint32_t send_size, uint8_t*& recv_buffer) +{ + switch (send_buffer[0]) { + case 0xf0: + // msg: f0 d9 + device_reset(); + break; + + case 0x70: { + // msg: 70 + // Writes to RTC chip + + system_time systime; + machine().base_datetime(systime); + + *recv_buffer++ = 0x01; // status, must be 1 + *recv_buffer++ = uint8_t(systime.local_time.year % 100); + *recv_buffer++ = uint8_t(systime.local_time.month + 1); + *recv_buffer++ = systime.local_time.mday; + *recv_buffer++ = systime.local_time.weekday; + *recv_buffer++ = systime.local_time.hour; + *recv_buffer++ = systime.local_time.minute; + *recv_buffer++ = seconds; // Can't be the same value twice in a row + seconds = (seconds + 1) % 60; + + return 1; + } + + case 0x71: { + // msg: 71 00 00 80 -> Offset = 0x0000, requested size = 0x80 bytes + // msg: 71 00 20 10 -> Offset = 0x0020, requested size = 0x10 bytes + // "Master Calendar ROM". Contents are specific to the game being initialized. + // Only encountered and tested on Hornet games. + + // msg: 71 ff ff 01 -> Offset = 0xffff, requested size = 0x01 bytes, special request for the area specification flag? Used by Sys573 + *recv_buffer++ = 0x01; // status, must be 1 + *recv_buffer++ = uint8_t(m_in1->read() & 0x0f); // Area specification + + return 4; + } + + case 0x7c: { + // msg: 7c 7f 00 04 + const uint16_t val = get_u16be(&send_buffer[1]); + + if (val == 0x7f00) { + // Return main ID + *recv_buffer++ = 0x01; // status, must be 1 + put_u32be(recv_buffer, mainId); + recv_buffer += 4; + } + else if (val == 0x8000) { + using namespace std::literals; + static const std::string_view init_complete = "<INIT COMPLETE!>"sv; + + // Return sub ID + *recv_buffer++ = 0x01; // status, must be 1 + memcpy(recv_buffer, init_complete.data(), init_complete.size()); + recv_buffer += init_complete.size(); + put_u32be(recv_buffer, subId); + recv_buffer += 4; + put_u32be(recv_buffer, ~subId); + recv_buffer += 4; + } + + return 4; + } + + case 0x7d: { + // msg: 7d 80 10 08 00 00 00 01 ff ff ff fe + const uint16_t val = get_u16be(&send_buffer[1]); + + if (val == 0x8010) { + // Set next sub ID + subId = get_u32be(&send_buffer[4]); + + *recv_buffer++ = 0x01; // status, must be 1 + } + + return 12; + } + + case 0x7e: { + // This builds some buffer that creates data like this: @2B0001:020304050607:BC9A78563412:000000000000B5 + // 2B0001 is ??? + // 020304050607 is the machine SID + // BC9A78563412 is the machine XID + // 000000000000B5 is ??? + + // msg: 7e xx + // status: 0x01 - Breaks loop, sends next byte + // 0x04 - Resends byte + *recv_buffer++ = 0x01; + + return 2; + } + + case 0x7f: + // TODO: Where is this used? + // The command existed in the command list for a few games (starting at Drummania?) but was never referenced and then disappeared around DDR 3rd mix. + break; + } + + // Command not recognized, pass it off to the base message handler + return jvs_device::handle_message(send_buffer, send_size, recv_buffer); +} + +INPUT_PORTS_START( k573mcal ) + PORT_START("IN1") + // Default the area to 3 because it's unused and will force you to actively select the region to initialize. + // For all but the earliest games it will show a message saying "this game only supports regions x, y, z". + // This is also a good way to discover new bootable variants that exist on the disc but were previously unknown. + PORT_DIPNAME(0x0f, 0x03, "Area") + PORT_DIPSETTING(0x00, "JA") + PORT_DIPSETTING(0x01, "UA") + PORT_DIPSETTING(0x02, "EA") + PORT_DIPSETTING(0x03, "3") // Unused + PORT_DIPSETTING(0x04, "AA") + PORT_DIPSETTING(0x05, "KA") + PORT_DIPSETTING(0x06, "JY/AY") + PORT_DIPSETTING(0x07, "JR") + PORT_DIPSETTING(0x08, "JB") + PORT_DIPSETTING(0x09, "UB") + PORT_DIPSETTING(0x0a, "EB") + PORT_DIPSETTING(0x0b, "11") // Unused + PORT_DIPSETTING(0x0c, "AB") + PORT_DIPSETTING(0x0d, "KB") + PORT_DIPSETTING(0x0e, "JZ/AZ") + PORT_DIPSETTING(0x0f, "JS") +INPUT_PORTS_END + +ioport_constructor k573mcal_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(k573mcal); +} + +DEFINE_DEVICE_TYPE(KONAMI_573_MASTER_CALENDAR, k573mcal_device, "k573mcal", "Konami 573 Master Calendar") |