summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/konami/konami_gn676_lan.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/konami/konami_gn676_lan.cpp')
-rw-r--r--src/mame/konami/konami_gn676_lan.cpp420
1 files changed, 287 insertions, 133 deletions
diff --git a/src/mame/konami/konami_gn676_lan.cpp b/src/mame/konami/konami_gn676_lan.cpp
index bea557772fa..bfd0dd7f354 100644
--- a/src/mame/konami/konami_gn676_lan.cpp
+++ b/src/mame/konami/konami_gn676_lan.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Ville Linde
+// copyright-holders:Ville Linde, windyfairy
/*
Hardware info by Guru
@@ -22,9 +22,9 @@ Notes:
CN1 - Connector joining to CPU board CN4
CN2/3 - RCA jacks for network cable
HYC2485S - Hybrid ceramic module for RS485
- CY7C199 - 32k x8 SRAM
- XC5204 - Xilinx XC5204 FPGA
- XC5210 - Xilink XC5210 FPGA
+ CY7C199 - 32k x8 SRAM (labeled 3C)
+ XC5204 - Xilinx XC5204 FPGA (labeled 7C)
+ XC5210 - Xilink XC5210 FPGA (labeled 6F)
N676H1 - PALCE16V8Q-15 stamped 'N676H1'
Network PCB (Racing Jam 2 and Thrill Drive (NWK-TR))
@@ -44,16 +44,48 @@ MADE IN JAPAN
This pcb is the same as the A version but with one added chip:
2G - XICOR X76F041 Secure SerialFlash (SOIC8)
- Note: This chip is also present on *some* Hornet games on the GN715 CPU board at location
- 30C. The chip refreshes game and region specific serial data to the Timekeeper RAM if the
- region ID matches the timekeeper. Because Racing Jam 2 and Thrill Drive came in a conversion
- kit for Racing Jam, the two former games will first boot with a "backup data error" because
- because of the timekeeper used for the incorrect game. Pressing the test switch then sets the
- timekeeper back to factory settings for the new kitted game installed. If the region ID in
- serialflash and timekeeper do not match, the game boots with a "hardware error" message.
-TODO:
-- Add X76F041 device when dumps will be available and get rid of the work_ram hack
+FPGA Bitstreams
+---------------
+- Racing Jam (racingj)
+ - Uses type A board
+ - Firmware (CRC32 92fde8df, 29491 bytes)
+
+- Racing Jam 2 (racingj2)
+ - Can use type A or B board (x76 usage depends on detected timekeeper; see below note)
+ - Firmware (CRC32 dfc74cc9, 29491 bytes)
+
+- Thrill Drive (thrilld, thrilldb)
+ - Uses type B board with x76 chip (except thrilldg** which uses type A without the x76 chip)
+ - Firmware #1 (CRC32 3760e3ce, 29490 bytes)
+ - Used during initial device test (does not get uploaded with skip post)
+ - Tests every register and expects to be able to read back the values it wrote for every register *except* 0x05, 0x06, and 0x09 on lanc2
+ - Seems to be a stubbed version of the normal firmware with the logic for all registers stubbed except memory-related registers
+ - Firmware #2 (CRC32 a8c97a75, 29490 bytes)
+ - Uploaded after boot sequence (even with skip post)
+ - Allows usage of x76 chip
+ - Firmware #3 (CRC32 93b86e35, 29490 bytes)
+ - Uploaded after security check (boot finishes, just as it starts the actual game)
+ - x76 chip capability unknown (TODO: this should be tested on real hardware)
+
+Racing Jam 1 and 2 are both programmed to send one extra 0xff at the end of the upload sequence. The Thrill Drive a8c97a75 firmware
+and the Racing Jam 2 dfc74cc9 firmware are actually the same except for the final 0xff.
+
+Racing Jam 2 and Thrill Drive (NWK-TR) were released as ROM conversion kits for Racing Jam (and/or in the latter's case, Racing Jam 2)
+without having to replace the timekeeper. To circumvent this, Konami would use the B version network with the x76 as an additional check.
+Once installed, the system will boot with a backup data error prompting one to push the test button to reset settings to default (but keeps
+the intial string from 0x00-0x0f the same). Both Racing Jam 2 and Thrill Drive use the x76 differently.
+
+Racing Jam 2 enables x76 usage if the game detects a Racing Jam 1 timekeeper, G*676, expecting the region ID of the timekeeper and x76 to
+match and gives a "hardware error" message if not. Cabinet type is determined by the timekeeper like with Racing Jam 1 as long as checksum
+at 0x0e-0x0f is changed accordingly. Using a Racing Jam 2 timekeeper, G*888, skips the x76 check possibly allowing use of an A version
+network board as well.
+
+Thrill Drive (NWK-TR) uses the x76 to determine/force the cabinet type and region regardless of timekeeper. For example, using a
+Racing Jam or Racing Jam 2 timekeeper with only a gas, brake and up/down shifter, GQ676/GQ888, on a Thrill Drive x76 with a gas, brake,
+handbrake, clutch and 5+R shifter, JAx/ABx, will enable the missing inputs and change the shifter type. Like Racing Jam 2, it expects the
+region ID of the timekeeper and x76 to match. The Hornet versions of Thrill Drive go back to the timekeeper system to determine the
+region/cab type and aren't interchangable with the NWK-TR versions.
*/
#include "emu.h"
@@ -64,177 +96,299 @@ TODO:
#include "logmacro.h"
+#define DUMP_FIRMWARE 0
-DEFINE_DEVICE_TYPE(KONAMI_GN676_LAN, konami_gn676_lan_device, "konami_gn676_lan", "Konami GN676 Network PCB")
-konami_gn676_lan_device::konami_gn676_lan_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, KONAMI_GN676_LAN, tag, owner, clock),
- m_fpga_uploaded(false),
- m_lanc2_ram_r(0),
- m_lanc2_ram_w(0),
- m_lanc2_ram(nullptr),
- m_work_ram(*this, finder_base::DUMMY_TAG)
+DEFINE_DEVICE_TYPE(KONAMI_GN676A_LAN, konami_gn676a_lan_device, "konami_gn676a_lan", "Konami GN676-PWB(H)A Network PCB")
+DEFINE_DEVICE_TYPE(KONAMI_GN676B_LAN, konami_gn676b_lan_device, "konami_gn676b_lan", "Konami GN676-PWB(H)B Network PCB")
+
+konami_gn676_lan_device::konami_gn676_lan_device(const machine_config &mconfig, const device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock),
+ m_x76f041(*this, "eeprom"),
+ m_lanc2_ram(nullptr)
{
- std::fill(std::begin(m_lanc2_reg), std::end(m_lanc2_reg), 0);
}
+konami_gn676a_lan_device::konami_gn676a_lan_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : konami_gn676_lan_device(mconfig, KONAMI_GN676A_LAN, tag, owner, clock)
+{
+}
+
+konami_gn676b_lan_device::konami_gn676b_lan_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : konami_gn676_lan_device(mconfig, KONAMI_GN676B_LAN, tag, owner, clock)
+{
+}
+
+void konami_gn676b_lan_device::device_add_mconfig(machine_config &config)
+{
+ X76F041(config, m_x76f041);
+}
void konami_gn676_lan_device::device_start()
{
m_lanc2_ram = std::make_unique<uint8_t[]>(0x8000);
- save_item(NAME(m_fpga_uploaded));
save_item(NAME(m_lanc2_ram_r));
save_item(NAME(m_lanc2_ram_w));
save_item(NAME(m_lanc2_reg));
+ save_item(NAME(m_network_buffer_max_size));
+ save_item(NAME(m_network_id));
+ save_item(NAME(m_fpga_reset_state));
+ save_item(NAME(m_fpga_uploaded));
+ save_item(NAME(m_fpga_waiting_firmware));
+ save_item(NAME(m_fpga_receiving));
+ save_item(NAME(m_fpga_is_stubbed));
+ save_item(NAME(m_fpga_firmware_size));
+ save_item(NAME(m_fpga_firmware_crc));
+ save_item(NAME(m_x76f041_enabled));
+ save_item(NAME(m_x76f041_read_enabled));
+ save_item(NAME(m_x76f041_rst_triggered));
+
save_pointer(NAME(m_lanc2_ram), 0x8000);
}
+void konami_gn676_lan_device::device_reset()
+{
+ std::fill_n(m_lanc2_ram.get(), 0x8000, 0);
+ m_fpga_reset_state = true;
+ reset_fpga_state(false);
+}
-uint32_t konami_gn676_lan_device::lanc1_r(offs_t offset)
+uint8_t konami_gn676_lan_device::lanc1_r(offs_t offset)
{
- switch (offset)
- {
- case 0x40/4:
- {
- uint32_t r = 0;
-
- r |= (m_fpga_uploaded) ? (1 << 6) : 0;
- r |= 1 << 5;
+ uint8_t r = 0xff;
- return (r) << 24;
- }
+ if (offset == 0x40)
+ {
+ r = (m_fpga_receiving << 6) | (m_fpga_waiting_firmware << 5);
- default:
- {
- LOG("lanc1_r: %08X at %08X\n", offset, machine().describe_context());
- return 0xffffffff;
- }
+ // racingj/racingj2 polls expecting to see bit 5 set and bit 6 unset before
+ // it'll start uploading the firmware, so only set it after being polled.
+ m_fpga_receiving = true;
}
-}
-void konami_gn676_lan_device::lanc1_w(offs_t offset, uint32_t data, uint32_t mem_mask)
-{
- LOG("lanc1_w: %08X, %08X, %08X at %08X\n", data, offset, mem_mask, machine().describe_context());
+ LOG("%s: lanc1_r: read %02X from %08X\n", tag(), r, offset, machine().describe_context());
+
+ return r;
}
-uint32_t konami_gn676_lan_device::lanc2_r(offs_t offset, uint32_t mem_mask)
+void konami_gn676_lan_device::lanc1_w(offs_t offset, uint8_t data)
{
- uint32_t r = 0;
+ LOG("%s: lanc1_w: wrote %02X to %08X\n", machine().describe_context(), data, offset);
- if (offset == 0)
+ if (offset == 0x20)
{
- if (ACCESSING_BITS_0_7)
+ if (BIT(data, 7))
{
- r |= m_lanc2_ram[m_lanc2_ram_r & 0x7fff];
- m_lanc2_ram_r++;
- }
- else
- {
- r |= 0xffffff00;
+ m_fpga_uploaded = true;
+ m_fpga_waiting_firmware = false;
+ m_fpga_receiving = false;
+ m_fpga_firmware_crc = util::crc32_creator::simple(m_lanc2_ram.get(), m_fpga_firmware_size);
+
+ LOG("%s: lanc1_fpga: Found firmware with hash of %08x\n", tag(), m_fpga_firmware_crc, machine().describe_context());
+
+ m_x76f041_enabled = m_fpga_firmware_crc == 0xa8c97a75 || m_fpga_firmware_crc == 0xdfc74cc9 || m_fpga_firmware_crc == 0x93b86e35;
+ m_fpga_is_stubbed = m_fpga_firmware_crc == 0x3760e3ce;
+
+ if (DUMP_FIRMWARE)
+ {
+ // DEBUG: Dump firmware for quick comparison
+ FILE *f = fopen(util::string_format("firmware_%08x.bin", m_fpga_firmware_crc).c_str(), "wb");
+ if (f)
+ {
+ fwrite(m_lanc2_ram.get(), 1, m_fpga_firmware_size, f);
+ fclose(f);
+ }
+ }
+
+ std::fill_n(m_lanc2_ram.get(), 0x8000, 0);
}
}
- else if (offset == 1)
- {
- r |= 0x00005555; // set all other machines as disconnected
- }
- else if (offset == 3)
+}
+
+uint8_t konami_gn676_lan_device::lanc2_r(offs_t offset)
+{
+ if (m_fpga_waiting_firmware)
{
- r |= 0xffffffff;
+ if (offset == 0)
+ return 0x80; // Required to be set for firmware to be written
+ return 0;
}
- else if (offset == 4)
+
+ uint8_t r = m_lanc2_reg[offset];
+ switch (offset)
{
- if (ACCESSING_BITS_24_31)
+ case 1:
+ r = BIT(m_lanc2_ram_r, 8, 8);
+ break;
+
+ case 2:
+ r = BIT(m_lanc2_ram_r, 0, 8);
+ break;
+
+ case 3:
+ r = m_lanc2_ram[m_lanc2_ram_r];
+ m_lanc2_ram_r = (m_lanc2_ram_r + 1) & 0x7fff;
+
+ if (!m_fpga_is_stubbed)
+ r = 0xff; // HACK: network traffic isn't implemented so give dummy data
+ break;
+
+ case 6:
+ case 7:
+ if (m_fpga_is_stubbed)
+ {
+ // Thrill Drive's boot test checks for this specific logic
+ r = (m_lanc2_reg[offset] + 3) & 0x7f;
+ m_lanc2_reg[offset]++;
+ }
+ else
+ r = 0x55; // HACK: force clients to show as disconnected
+ break;
+
+ case 9:
{
- r |= 0x00000000;
+ // Thrill Drive's boot test checks for this specific logic
+ uint8_t a = m_lanc2_reg[offset] + 6;
+ uint8_t b = BIT(a, 4, 2) & BIT(a, 5, 2) & BIT(a, 6, 2);
+ r = (b << 4) | (a & 0x0f);
+
+ if (m_fpga_is_stubbed)
+ m_lanc2_reg[offset]++;
+ break;
}
+
+ case 0x0c:
+ case 0x0d:
+ // Used with reg 6/7 to determine info about clients
+ break;
+
+ case 0x10:
+ if (m_x76f041 && m_x76f041_enabled && m_x76f041_read_enabled)
+ r = m_x76f041->read_sda();
+ break;
}
- LOG("lanc2_r: %08X, %08X at %08X\n", offset, mem_mask, machine().describe_context());
+ LOG("%s: lanc2_r: read %02X from %08X\n", tag(), r, offset, machine().describe_context());
return r;
}
-void konami_gn676_lan_device::lanc2_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+void konami_gn676_lan_device::lanc2_w(offs_t offset, uint8_t data)
{
- if (offset == 0)
+ if (m_fpga_waiting_firmware)
{
- if (ACCESSING_BITS_24_31)
- {
- uint8_t value = data >> 24;
-
- value = ((value >> 7) & 0x01) |
- ((value >> 5) & 0x02) |
- ((value >> 3) & 0x04) |
- ((value >> 1) & 0x08) |
- ((value << 1) & 0x10) |
- ((value << 3) & 0x20) |
- ((value << 5) & 0x40) |
- ((value << 7) & 0x80);
+ if (offset == 0)
+ m_lanc2_ram[m_fpga_firmware_size++] = data;
+ return;
+ }
- m_fpga_uploaded = true;
- m_lanc2_reg[0] = (uint8_t)(data >> 24);
+ LOG("%s: lanc2_w: wrote %02X to %08X\n", tag(), data, offset, machine().describe_context());
- LOG("lanc2_fpga_w: %02X at %08X\n", value, machine().describe_context());
- }
- if (ACCESSING_BITS_8_15)
- {
- m_lanc2_ram_r = 0;
- m_lanc2_ram_w = 0;
- m_lanc2_reg[1] = (uint8_t)(data >> 8);
+ m_lanc2_reg[offset] = data;
- if (data & 0x1000)
- {
- // send out frame for this machine
- }
- else
+ switch (offset)
+ {
+ case 0:
+ // This shouldn't actually be used in practice but Thrill Drive's boot test explicitly writes
+ // (addr >> 16) & 1 to this register which would put it out of the available RAM space.
+ break;
+
+ case 1:
+ m_lanc2_ram_r = (m_lanc2_ram_r & 0x100ff) | (data << 8);
+ m_lanc2_ram_w = (m_lanc2_ram_w & 0x100ff) | (data << 8);
+ break;
+
+ case 2:
+ m_lanc2_ram_r = (m_lanc2_ram_r & 0x1ff00) | data;
+ m_lanc2_ram_w = (m_lanc2_ram_w & 0x1ff00) | data;
+ break;
+
+ case 3:
+ m_lanc2_ram[m_lanc2_ram_w] = data;
+ m_lanc2_ram_w = (m_lanc2_ram_w + 1) & 0x7fff;
+ break;
+
+ case 4:
+ // Network enabled flag?
+ // Set to 0 after firmware is uploaded, set to 1 when setting self network ID and similar info
+ break;
+
+ case 5:
+ m_network_id = data;
+ break;
+
+ case 0x10:
+ if (m_x76f041 && m_x76f041_enabled)
{
- // read from other machines
- //int machine_id = (m_lanc2_reg[2] >> 4) & 7;
- //int self = m_lanc2_reg[2] & 1;
-
- for (auto j = 0; j < 0x110; j++)
+ /*
+ 0x01 = x76 SDA
+ 0x02 = x76 RST
+ 0x04 = x76 CS???
+ 0x08 = x76 SCL
+ 0x10 = Controls direction of x76 SDA
+ */
+
+ if (BIT(data, 1))
+ m_x76f041_rst_triggered = true;
+
+ m_x76f041_read_enabled = BIT(data, 4);
+ m_x76f041->write_rst(BIT(data, 1));
+ m_x76f041->write_scl(BIT(data, 3));
+
+ if (!m_x76f041_read_enabled)
{
- m_lanc2_ram[j] = 0xff;
+ if (m_x76f041_rst_triggered && BIT(data, 1) == 0)
+ {
+ // HACK: RST was triggered previously and now we're exiting read mode, so reset
+ // the x76 state so we're not stuck in a loop reading the reset response.
+ // My guess is bit 2 is used in some way for this but the usage doesn't make
+ // sense (if used as CS then it'll reset the chip state entirely during ACK
+ // polling in normal usage). FPGA magic?
+ m_x76f041->write_cs(1);
+ m_x76f041->write_cs(0);
+ m_x76f041_rst_triggered = false;
+ }
+
+ m_x76f041->write_sda(BIT(data, 0));
}
}
- }
- if (ACCESSING_BITS_16_23)
- {
- m_lanc2_reg[2] = (uint8_t)(data >> 16);
- }
- if (ACCESSING_BITS_0_7)
- {
- m_lanc2_ram[m_lanc2_ram_w & 0x7fff] = data & 0xff;
- m_lanc2_ram_w++;
- }
- }
- if (offset == 4) // only type B has the chip at 2G
- {
- // TODO: HACK! The data below would normally be present on the serialflash at 2G.
+ break;
- if (strcmp(machine().system().name, "thrilld") == 0 ||
- strcmp(machine().system().name, "thrilldb") == 0 ||
- strcmp(machine().system().name, "thrilldbe") == 0)
- {
- m_work_ram[(0x3ffed0/4) + 0] = 0x472a3731; // G*71
- m_work_ram[(0x3ffed0/4) + 1] = 0x33202020; // 3
- m_work_ram[(0x3ffed0/4) + 2] = 0x2d2d2a2a; // --**
- m_work_ram[(0x3ffed0/4) + 3] = 0x2a207878; // *
-
- m_work_ram[(0x3fff40/4) + 0] = 0x47433731; // GC71
- m_work_ram[(0x3fff40/4) + 1] = 0x33000000; // 3
- m_work_ram[(0x3fff40/4) + 2] = 0x19994a41; // JA
- m_work_ram[(0x3fff40/4) + 3] = 0x4100a9b1; // A
- }
- else if (strcmp(machine().system().name, "racingj2") == 0)
- {
- m_work_ram[(0x3ffc80/4) + 0] = 0x47453838; // GE88
- m_work_ram[(0x3ffc80/4) + 1] = 0x38003030; // 8 00
- m_work_ram[(0x3ffc80/4) + 2] = 0x39374541; // 97EA
- m_work_ram[(0x3ffc80/4) + 3] = 0x410058da; // A
- }
+ case 0x1c:
+ m_network_buffer_max_size = (data << 8) | (m_network_buffer_max_size & 0xff);
+ break;
+
+ case 0x1d:
+ m_network_buffer_max_size = (m_network_buffer_max_size & 0xff00) | data;
+ break;
}
+}
+
+void konami_gn676_lan_device::reset_fpga_state(bool state)
+{
+ if (state == m_fpga_reset_state)
+ return;
+
+ // Reset state of FPGA so that it's waiting for new firmware until
+ // bit 7 of lanc1_w addr 0x20 is set.
+ m_fpga_reset_state = state;
- LOG("lanc2_w: %08X, %08X, %08X at %08X\n", data, offset, mem_mask, machine().describe_context());
+ m_fpga_waiting_firmware = true;
+ m_fpga_uploaded = false;
+ m_fpga_receiving = false;
+ m_fpga_is_stubbed = false;
+ m_fpga_firmware_size = 0;
+ m_fpga_firmware_crc = 0;
+
+ m_x76f041_enabled = false;
+ m_x76f041_read_enabled = false;
+ m_x76f041_rst_triggered = false;
+
+ m_lanc2_ram_r = m_lanc2_ram_w = 0;
+
+ m_network_buffer_max_size = 0;
+ m_network_id = 0;
+
+ std::fill(std::begin(m_lanc2_reg), std::end(m_lanc2_reg), 0);
}