From fbcc6c1b37266e328cb87028e380fb29841ba590 Mon Sep 17 00:00:00 2001 From: Brandon Munger Date: Wed, 24 Oct 2018 20:16:35 -0400 Subject: r9751: Implement proper SMIOC emulation through new uart emulation (#4188) * [R9751] (Squashed) Early work on SMIOC emulation commit 9b587be762abfd576e92792c612cd205c570924d Author: Stephen Stair Date: Tue Apr 10 19:41:55 2018 -0700 Connected SMIOC DMA to 68k * SMIOC now reading bytes for serial from the 68k memory space successfully. commit 0f801a05a3fa5b78768a1a2bcb9981466e8f2b0e Author: Stephen Stair Date: Tue Mar 27 18:31:32 2018 -0700 68k now running in lockstep with SMIOC for serial * 68k now uses actual SMIOC as source of SMIOC status, so it waits for the SMIOC to complete commands before continuing. * SMIOC DMA controllers now being asked to move bytes around * "write character" (4100) commands to SMIOC are now completing and resulting in correct status being written and sent back to the 68k * Every now and then the SMIOC stalls for a bit, it seems to be due to spending some time trying to communicate with the 8051 CPU (used to communicate with another cpu on the serial breakout box) that is not currently present in the emulated device. Next Steps: * We're going to move the terminal from the r9751 into the SMIOC, and hook it up to the DMA controllers as a hacky temporary solution. * We need to emulate the 5xxxx memory range in SMIOC to proxy data to/from the 68k memory * Then we'll start work on emulating the octal uart properly and hooking it up to RS232 channels. commit ca91614fca3f43d87755d0c3439521276d7bfafb Author: Stephen Stair Date: Wed Mar 14 19:52:04 2018 -0700 Tinkering to get SMIOC further along 1) Enable 188 cpu to advance DMA immediatley if DRQ is held high when source synchronous DMA is started (It's not clear whether the DMA actually works this way yet) 2) Retrigger the DMA DRQ when a memory access is made to the DMA window (future work needed to proxy the reads/writes to the 68k's main memory) This is needed to support multibyte DMA reads/writes to the 68k window. 3) Reverse the bytes in the status register (based on current incomplete understanding of how this is encoded) 4) Connect the status register back to the r9751 on SMIOC status read Current state: SMIOC's main board DMA state machine now completes the DMA transfer from the main board (With incorrect data), but we are still getting stuck. Next steps: SMIOC is now hanging waiting on DMA to UART to complete, we need to implement some emulation of the UART and its connections to the surrounding 16 dma channels in order to make progress. commit 62c1455c97a53ed970622e08cb03a225625914fd Author: Stephen Stair Date: Sun Mar 4 20:13:32 2018 -0800 Progress on emulating SMIOC commit daee8498c5840417aee3ed9def0acfba5476335a Author: Stephen Stair Date: Sun Feb 25 20:02:32 2018 -0800 Working on bringing up SMIOC Trying to get emulated SMIOC in lockstep with the main 68k. commit 5a929fde90307040a0544682888f2f21a914b900 Author: Stephen Stair Date: Wed Feb 21 20:06:27 2018 -0800 re-enable SMIOC, Logging improvements, first steps towards emulating on-board hardware. commit f0c7a1665a18c99df5527ae45994f39f95223814 Author: Stephen Stair Date: Sun Feb 11 16:26:31 2018 -0800 Emulation fix - Terminal now responds to input commit ecff4035f3fdaa0329df46f99ccd516aa23ec8af Author: Stephen Stair Date: Tue Feb 6 19:00:35 2018 -0800 Hacky emulation of SMIOC now successfully receiving serial characters! commit 376bca90dff311d24e1f5014d8d6f0dc297bf671 Author: Stephen Stair Date: Sun Jan 28 18:31:38 2018 -0800 Experimenting with SMIOC emulation * Partial fixes for line state issues with persistent line values When the AM9517 DMA controller mode is updated to change the polarity of DACK/DREQ, it doesn't update the DACK line values / request values (respectively). This fixes this issue for one of the emulated devices in the file, but the other device has the same problem. * Add skeleton of SCC2698B UART device * Early progress on emulating SCC2698B Octal UART Core structure for IO lines and writing registers is in place. * Update address maps to new format. * SCC2698b: Add tx/rx, baud rate generation * Emulation improvements for scc2698b device * MPP output pins emulated * Improved logging for baud rate changes * Channel configuration emulated * Support for TX/RX Enable * Started integrating new features into SMIOC * Minor progress on the path to enabling new UART for SMIOC * Resolved code issues related to upstream changes * Working around an apparent problem with callbacks on sub-device devices * Terminal added for serial port attached to SCC2698B UART. * Things compile and seem to work. * SMIOC: Serial is now outputting correctly through emulated SMIOC Some glitches to work out, but this is nearly ready. * AM9517a should not forget about input line status on master reset. * Disable tracing * SMIOC: Improving diagnostics and making some progress on the emulation * Revised tracing in SMIOC and SCC2698B * Improved tracing approach in r9751 * Minor progress towards working SMIOC emulation. * [R9751] Update drivers to new code patterns after rebase Rebased on latest mame, Fixed a few issues and updated the callback code patterns based on recent changes in devcb * [R9751] Improve logging, fix interrupt race Serial no longer drops output characters * [R9751] Working on SMIOC emulation Improved SMIOC emulation accuracy, Made some progress in getting through the disktool initialization sequence, but we are still hitting a problem - It looks like disktool should be resetting the SMIOC board when it starts, but the mechanism for this has not been found. * [R9751] Improve tracing for system board registers * [R9751] Improve SMIOC emulation Still having some trouble with SMIOC but this seems to be an improvement. SMIOC is now waiting for a status to be read before applying a new status, and mandatory parameter read logic is in place. * [R9751] Incremental emulation improvements the SMIOC failure was understood and fixed, and a new approach to getting the hardware to bootstrap correctly was implemented - Still having some problems with it though. * [R9751] Fixed emulation issues * Disktool now boots to the console * UART receive only partly working, I don't think it's fully connected yet. * [R9751] Further progress on SMIOC emulation * Figured out the missing command parameter interface to the SMIOC board * Still blocked because the SMIOC interface to the breakout board through an onboard 8051 is not emulated and the disktool software depends on properties coming from it. * [R9751] Terminal is fully working for disktool * Improved the emulation of the 80188 to 451 connection in order to reliably set a status bit disktool depends on. * [R9751] Fix build after rebase * [R9751] Cleanup, tracing improvements * Remove second emulated terminal - SMIOC is now stable enough to run the terminal. * Remove older and unnecessary emulation/tracing code * r9751: Fix compile error and modify copyright holders --- scripts/src/machine.lua | 14 + scripts/target/mame/mess.lua | 1 + src/devices/cpu/i86/i186.cpp | 4 + src/devices/machine/am9517a.cpp | 25 +- src/devices/machine/pdc.cpp | 4 + src/devices/machine/pdc.h | 2 +- src/devices/machine/scc2698b.cpp | 832 +++++++++++++++++++++++++++++++++++++++ src/devices/machine/scc2698b.h | 220 +++++++++++ src/devices/machine/smioc.cpp | 600 ++++++++++++++++++++++++++-- src/devices/machine/smioc.h | 94 ++++- src/mame/drivers/r9751.cpp | 666 ++++++++++++++++++++++--------- 11 files changed, 2256 insertions(+), 206 deletions(-) create mode 100644 src/devices/machine/scc2698b.cpp create mode 100644 src/devices/machine/scc2698b.h diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index ce7c2813b8f..b88139367ad 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -3807,3 +3807,17 @@ if (MACHINES["Z8038"]~=null) then MAME_DIR .. "src/devices/machine/z8038.h", } end + + +--------------------------------------------------- +-- +--@src/devices/machine/scc2698b.h,MACHINES["SCC2698B"] = true +--------------------------------------------------- + +if (MACHINES["SCC2698B"]~=null) then + files { + MAME_DIR .. "src/devices/machine/scc2698b.cpp", + MAME_DIR .. "src/devices/machine/scc2698b.h", + } +end + diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 72c2dc1aace..addf660d6c7 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -569,6 +569,7 @@ MACHINES["S3C44B0"] = true MACHINES["SAA1043"] = true MACHINES["SATURN"] = true --MACHINES["SCSI"] = true +MACHINES["SCC2698B"] = true MACHINES["SCUDSP"] = true MACHINES["SECFLASH"] = true MACHINES["SEIBU_COP"] = true diff --git a/src/devices/cpu/i86/i186.cpp b/src/devices/cpu/i86/i186.cpp index 7738ee64d6c..e26a797f366 100644 --- a/src/devices/cpu/i86/i186.cpp +++ b/src/devices/cpu/i86/i186.cpp @@ -1777,6 +1777,10 @@ WRITE16_MEMBER(i80186_cpu_device::internal_port_w) while(m_dma[which].control & ST_STOP) drq_callback(which); } + if((m_dma[which].control & ST_STOP) == ST_STOP && (m_dma[which].control & SYNC_MASK) != 0) + { + if (m_dma[which].drq_state) drq_callback(which); + } break; case 0x7f: diff --git a/src/devices/machine/am9517a.cpp b/src/devices/machine/am9517a.cpp index cd6955b63ed..55a5cab0d8c 100644 --- a/src/devices/machine/am9517a.cpp +++ b/src/devices/machine/am9517a.cpp @@ -817,9 +817,22 @@ WRITE8_MEMBER( am9517a_device::write ) switch (offset & 0x0f) { case REGISTER_COMMAND: - m_command = data; + { + bool old_dack_active_high = COMMAND_DACK_ACTIVE_HIGH; + bool old_dreq_active_low = COMMAND_DREQ_ACTIVE_LOW; + m_command = data; + LOG("AM9517A Command Register: %02x\n", m_command); - LOG("AM9517A Command Register: %02x\n", m_command); + if (old_dack_active_high != COMMAND_DACK_ACTIVE_HIGH) + { + set_dack(); // Line values for DACK changed, update them to reflect the new state. + } + if (old_dreq_active_low != COMMAND_DREQ_ACTIVE_LOW) + { + // Invert the request bits because the interpretation of line levels has changed + m_status ^= 0xF0; + } + } break; case REGISTER_REQUEST: @@ -881,8 +894,12 @@ WRITE8_MEMBER( am9517a_device::write ) case REGISTER_MASTER_CLEAR: LOG("AM9517A Master Clear\n"); - - device_reset(); + { + // Even the master reset should not clear the state of the input lines. + int stored_status = m_status; + device_reset(); + m_status = stored_status & 0xF0; + } break; case REGISTER_CLEAR_MASK: diff --git a/src/devices/machine/pdc.cpp b/src/devices/machine/pdc.cpp index 10dc9fd0619..23e6b1b45ac 100644 --- a/src/devices/machine/pdc.cpp +++ b/src/devices/machine/pdc.cpp @@ -338,6 +338,10 @@ void pdc_device::device_start() save_item(NAME(reg_p21)); save_item(NAME(reg_p38)); save_item(NAME(fdd_68k_dma_address)); + + /* Resolve callbacks */ + m_m68k_r_cb.resolve_safe(0); + m_m68k_w_cb.resolve_safe(); } //------------------------------------------------- diff --git a/src/devices/machine/pdc.h b/src/devices/machine/pdc.h index fad97e2c724..8f2b958ce2b 100644 --- a/src/devices/machine/pdc.h +++ b/src/devices/machine/pdc.h @@ -107,7 +107,7 @@ private: /* Callbacks */ devcb_read8 m_m68k_r_cb; - devcb_write8 m_m68k_w_cb; + devcb_write8 m_m68k_w_cb; }; /* Device type */ diff --git a/src/devices/machine/scc2698b.cpp b/src/devices/machine/scc2698b.cpp new file mode 100644 index 00000000000..fce6ada6a66 --- /dev/null +++ b/src/devices/machine/scc2698b.cpp @@ -0,0 +1,832 @@ +// license:GPL-2.0+ +// copyright-holders:Brandon Munger, Stephen Stair +/********************************************************************* + + scc2698b.cpp + + Enhanced Octal Universal Asynchronous Receiver/Transmitter + +Notes: + This device is similiar to four 2681 DUART chips tied together + in a single package, with some shared resources. + The 2681 DUART is implemented in scn2681_device - but this + chip is being independently emulated seperately for mainly + educational purposes. When functionality for this device is + completed we will consider merging the devices if it's + practical. + +Quirks: + * Reading the RX Holding register will advance the HW FIFO even if + there is no data to be read. This is not currently emulated but + might be interesting to characterize in HW and emulate properly. + +*********************************************************************/ + + +#include "emu.h" +#include "scc2698b.h" + +DEFINE_DEVICE_TYPE(SCC2698B, scc2698b_device, "scc2698b", "SCC2698B Octal UART") +DEFINE_DEVICE_TYPE(SCC2698B_CHANNEL, scc2698b_channel, "scc2698b_channel", "UART channel") + +#define TRACE_ENABLE 0 +#define TRACE_CONFIG_CHANGE 0 + + +#define TRACE_REGISTER_WRITE(ofs, data, reg_name) if(TRACE_ENABLE) { log_register_access((ofs), (data), "<<", (reg_name)); } +#define TRACE_REGISTER_READ(ofs, data, reg_name) if(TRACE_ENABLE) { log_register_access((ofs), (data), ">>", (reg_name)); } +#define TRACE_CONFIG if(TRACE_CONFIG_CHANGE) logerror + +// Divider values for baud rate generation +// Expecting a crystal of 3.6864MHz, baud rate is crystal frequency / (divider value * 8) +static const int BAUD_DIVIDER_ACR7_0[16] = +{ + 9216,4189,3426,2304,1536,768,384,439,192,96,64,48,12,0,0,0 +}; +static const int BAUD_DIVIDER_ACR7_1[16] = +{ + 6144,4189,12,3072,1536,768,384,230,192,96,256,48,24,0,0,0 +}; + +void scc2698b_device::map(address_map &map) +{ + map(0x0, 0x3F).rw(FUNC(scc2698b_device::read), FUNC(scc2698b_device::write)); +} + +#define CHANA_TAG "cha" +#define CHANB_TAG "chb" +#define CHANC_TAG "chc" +#define CHAND_TAG "chd" +#define CHANE_TAG "che" +#define CHANF_TAG "chf" +#define CHANG_TAG "chg" +#define CHANH_TAG "chh" + + +scc2698b_channel::scc2698b_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, SCC2698B_CHANNEL, tag, owner, clock), + device_serial_interface(mconfig, *this) +{ + +} + +void scc2698b_channel::device_start() +{ + +} + +void scc2698b_channel::device_reset() +{ + reset_all(); + recompute_pin_output(true); +} + +void scc2698b_channel::rcv_complete() +{ + // Completed Byte Receive + receive_register_extract(); + + if (rx_enable == false) + { + // Skip receive + return; + } + + int byte = get_received_char(); + if (rx_bytecount >= SCC2698B_RX_FIFO_SIZE) + { + logerror("Warning: Received byte lost, RX FIFO Full.\n"); + } + else + { + rx_fifo[rx_bytecount++] = byte; + } + recompute_pin_output(); +} +void scc2698b_channel::tra_complete() +{ + // Completed Byte Transmit + if (tx_bytecount > 0) + { + transmit_register_setup(tx_fifo); + tx_bytecount = 0; + } + else + { + tx_transmitting = 0; + } + recompute_pin_output(); +} +void scc2698b_channel::tra_callback() +{ + // Started bit transmit - Update output line + int bit = transmit_register_get_data_bit(); + parent->write_line_tx(channel_port, bit); +} + +void scc2698b_channel::write_TXH(int txh) +{ + if (tx_enable == false) + { + logerror("Warning: TX Holding byte ignored because transmitter is disabled.\n"); + return; + } + if (tx_transmitting) + { + if (tx_bytecount == 0) + { + tx_fifo = txh; + tx_bytecount = 1; + } + else + { + // Lost byte + logerror("Warning: TX Holding byte written to full FIFO - Data lost.\n"); + } + } + else + { + if (tx_bytecount != 0) + { + logerror("Unexpected: TX FIFO should not contain bytes when UART is not transmitting\n"); + } + transmit_register_setup(txh); + tx_transmitting = 1; + } + recompute_pin_output(); +} + +int scc2698b_channel::read_RXH() +{ + if (rx_bytecount == 0) + { + logerror("Warning: RX Holding read with empty RX FIFO"); + return 0; + } + else + { + int byte = rx_fifo[0]; + for (int i = 1; i < rx_bytecount; i++) + { + rx_fifo[i - 1] = rx_fifo[i]; + } + rx_bytecount--; + recompute_pin_output(); + return byte; + } +} + + +void scc2698b_channel::reset_all() +{ + mpp1_value = -1; // Force pin update + mpp2_value = -1; + reset_tx(); + reset_rx(); +} +void scc2698b_channel::reset_tx() +{ + tx_transmitting = 0; + tx_fifo = 0; + tx_bytecount = 0; + transmit_register_reset(); + set_tx_enable(false); +} +void scc2698b_channel::reset_rx() +{ + rx_bytecount = 0; + receive_register_reset(); + set_rx_enable(false); +} + +void scc2698b_channel::set_tx_enable(bool enable) +{ + tx_enable = enable; + recompute_pin_output(); +} +void scc2698b_channel::set_rx_enable(bool enable) +{ + if (rx_enable == false && enable == true) + { + receive_register_reset(); + } + rx_enable = enable; + recompute_pin_output(); +} + + +void scc2698b_channel::update_serial_configuration() +{ + // void set_data_frame(int start_bit_count, int data_bit_count, parity_t parity, stop_bits_t stop_bits); + + // Note: unimplemented RTS/CTS control, Error mode bit. + + int start_bit_count = 1; + + int data_bit_count = (MR1 & 3) + 5; + + parity_t parity_mode = PARITY_NONE; + switch ((MR1 >> 3) & 3) + { + case 0: // Parity enabled + parity_mode = (MR1 & 4) ? PARITY_ODD : PARITY_EVEN; + break; + case 1: // Force parity (force parity bit to a constant) + parity_mode = (MR1 & 4) ? PARITY_MARK : PARITY_SPACE; + break; + case 2: // No parity + parity_mode = PARITY_NONE; + break; + default: + logerror("Warning: Unsupported special parity mode selected.\n"); + break; + } + + stop_bits_t stop_bits; + int stopbitlength = MR2 & 0x0F; + // Round up to the next highest even bit length. + // In reality there are a variety of options. + if (stopbitlength < 8) + { + stop_bits = STOP_BITS_1; + } + else + { + stop_bits = STOP_BITS_2; + } + + // Check the channel mode + switch ((MR2 >> 6) & 3) + { + case 0: // Normal + break; + case 1: // Auto-Echo (receive, and repeat RX line to TX) + case 2: // Local loopback (TX=1, RX ignored, receive bytes written to TX through internal loopback) + case 3: // Remote loop (No receiving, but repeat RX line to TX) + logerror("Warning: Unsupported channel mode selected.\n"); + } + + const char* parity_strings[] = { "None", "Odd", "Even", "Mark", "Space" }; + const char* stop_bit_strings[] = { "0","1","1.5","2" }; + TRACE_CONFIG("Reconfigured channel to %d data bits, %s Parity, %s stop bits\n", data_bit_count, parity_strings[parity_mode], stop_bit_strings[stop_bits]); + + + set_data_frame(start_bit_count, data_bit_count, parity_mode, stop_bits); +} + + + +void scc2698b_channel::set_tx_bittime(const attotime &bittime) +{ + set_tra_rate(bittime); +} +void scc2698b_channel::set_rx_bittime(const attotime &bittime) +{ + set_rcv_rate(bittime); +} + + +int scc2698b_channel::read_SR() +{ + int data = SR; + + // Compute dynamic bits of SR + data &= 0xF0; // Clear dynamic bits + + // SR(3) TxEMT Transmitter empty + if (tx_enable && tx_transmitting == 0) data |= 0x08; + // SR(2) TxRDY Transmitter Ready + if (tx_enable && tx_bytecount == 0) data |= 0x04; + // SR(1) FFULL RX Fifo Full + if (rx_bytecount == SCC2698B_RX_FIFO_SIZE) data |= 0x02; + // SR(0) RxRDY Receiver Ready (Data has been received) + if (rx_bytecount > 0) data |= 0x01; + + return data; +} + +void scc2698b_channel::set_mpp_output(bool output) +{ + mpp_is_output = output; + recompute_pin_output(true); +} + +void scc2698b_channel::recompute_pin_output(bool force) +{ + int new_mpp1 = 0, new_mpp2 = 0; + int SR = read_SR(); + if (mpp_is_output) + { + // TxRDY + new_mpp1 = (SR & 4) ? 1 : 0; + // RxRDY + new_mpp2 = (SR & 1) ? 1 : 0; + + if (new_mpp1 != mpp1_value || force) + { + if (TRACE_ENABLE) logerror("Channel %d MPP1 => %d\n", channel_port, new_mpp1); + parent->write_line_mpp1(channel_port, new_mpp1); + mpp1_value = new_mpp1; + } + if (new_mpp2 != mpp2_value || force) + { + if(TRACE_ENABLE) logerror("Channel %d MPP2 => %d\n", channel_port, new_mpp2); + parent->write_line_mpp2(channel_port, new_mpp2); + mpp2_value = new_mpp2; + } + } +} + + + +WRITE_LINE_MEMBER( scc2698b_channel::mpi0_w ) +{ + +} +WRITE_LINE_MEMBER( scc2698b_channel::mpi1_w ) +{ + +} + + +scc2698b_device::scc2698b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, SCC2698B, tag, owner, clock), + m_channel(*this, "channel_%u",1), + write_intr_A(*this), + write_intr_B(*this), + write_intr_C(*this), + write_intr_D(*this), + write_a_tx(*this), write_b_tx(*this), write_c_tx(*this), write_d_tx(*this), write_e_tx(*this), write_f_tx(*this), write_g_tx(*this), write_h_tx(*this), + write_a_mpp1(*this), write_b_mpp1(*this), write_c_mpp1(*this), write_d_mpp1(*this), write_e_mpp1(*this), write_f_mpp1(*this), write_g_mpp1(*this), write_h_mpp1(*this), + write_a_mpp2(*this), write_b_mpp2(*this), write_c_mpp2(*this), write_d_mpp2(*this), write_e_mpp2(*this), write_f_mpp2(*this), write_g_mpp2(*this), write_h_mpp2(*this), + write_a_mpo(*this), write_b_mpo(*this), write_c_mpo(*this), write_d_mpo(*this), write_e_mpo(*this), write_f_mpo(*this), write_g_mpo(*this), write_h_mpo(*this) + +{ + +} + + + +void scc2698b_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + +} + + +void scc2698b_device::device_start() +{ + write_intr_A.resolve_safe(); + write_intr_B.resolve_safe(); + write_intr_C.resolve_safe(); + write_intr_D.resolve_safe(); + + write_a_tx.resolve_safe(); + write_b_tx.resolve_safe(); + write_c_tx.resolve_safe(); + write_d_tx.resolve_safe(); + write_e_tx.resolve_safe(); + write_f_tx.resolve_safe(); + write_g_tx.resolve_safe(); + write_h_tx.resolve_safe(); + write_a_mpp1.resolve_safe(); + write_b_mpp1.resolve_safe(); + write_c_mpp1.resolve_safe(); + write_d_mpp1.resolve_safe(); + write_e_mpp1.resolve_safe(); + write_f_mpp1.resolve_safe(); + write_g_mpp1.resolve_safe(); + write_h_mpp1.resolve_safe(); + write_a_mpp2.resolve_safe(); + write_b_mpp2.resolve_safe(); + write_c_mpp2.resolve_safe(); + write_d_mpp2.resolve_safe(); + write_e_mpp2.resolve_safe(); + write_f_mpp2.resolve_safe(); + write_g_mpp2.resolve_safe(); + write_h_mpp2.resolve_safe(); + write_a_mpo.resolve_safe(); + write_b_mpo.resolve_safe(); + write_c_mpo.resolve_safe(); + write_d_mpo.resolve_safe(); + write_e_mpo.resolve_safe(); + write_f_mpo.resolve_safe(); + write_g_mpo.resolve_safe(); + write_h_mpo.resolve_safe(); + + for (int i = 0; i < 8; i++) + { + m_channel[i]->channel_port = i; + m_channel[i]->parent = this; + } + +} + + +void scc2698b_device::device_reset() +{ + +} + + +void scc2698b_device::write_line_tx(int port, int value) +{ + switch (port) + { + case 0: write_a_tx(value); break; + case 1: write_b_tx(value); break; + case 2: write_c_tx(value); break; + case 3: write_d_tx(value); break; + case 4: write_e_tx(value); break; + case 5: write_f_tx(value); break; + case 6: write_g_tx(value); break; + case 7: write_h_tx(value); break; + default: logerror("Unsupported port %d in write_line_tx\n", port); + } +} +void scc2698b_device::write_line_mpp1(int port, int value) +{ + switch (port) + { + case 0: write_a_mpp1(value); break; + case 1: write_b_mpp1(value); break; + case 2: write_c_mpp1(value); break; + case 3: write_d_mpp1(value); break; + case 4: write_e_mpp1(value); break; + case 5: write_f_mpp1(value); break; + case 6: write_g_mpp1(value); break; + case 7: write_h_mpp1(value); break; + default: logerror("Unsupported port %d in write_line_mpp1\n", port); + } +} + +void scc2698b_device::write_line_mpp2(int port, int value) +{ + switch (port) + { + case 0: write_a_mpp2(value); break; + case 1: write_b_mpp2(value); break; + case 2: write_c_mpp2(value); break; + case 3: write_d_mpp2(value); break; + case 4: write_e_mpp2(value); break; + case 5: write_f_mpp2(value); break; + case 6: write_g_mpp2(value); break; + case 7: write_h_mpp2(value); break; + default: logerror("Unsupported port %d in write_line_mpp2\n", port); + } +} +void scc2698b_device::write_line_mpo(int port, int value) +{ + switch (port) + { + case 0: write_a_mpo(value); break; + case 1: write_b_mpo(value); break; + case 2: write_c_mpo(value); break; + case 3: write_d_mpo(value); break; + case 4: write_e_mpo(value); break; + case 5: write_f_mpo(value); break; + case 6: write_g_mpo(value); break; + case 7: write_h_mpo(value); break; + default: logerror("Unsupported port %d in write_line_mpo\n", port); + } +} + + +READ8_MEMBER(scc2698b_device::read) +{ + return read_reg(offset); +} + +WRITE8_MEMBER(scc2698b_device::write) +{ + write_reg(offset, data); +} + + +void scc2698b_device::log_register_access(int offset, int value, const char* direction, const char* reg_name) +{ + logerror("[0x%02X] %s 0x%02X (%s)\n", offset, direction, value, reg_name); +} + + + +uint8_t scc2698b_device::read_reg(int offset) +{ + int device = (offset >> 4) & 3; + int reg = (offset & 15); + // Port index for port register accesses + int port = device * 2 + (reg >> 3); + int data = 0; + + switch (reg) + { + case 0: // MR1a, MR2a (Mode Register) + data = read_MR(port); + TRACE_REGISTER_READ(offset, data, "MR1a/MR2a"); + break; + case 1: // SRa (Status Register) + data = read_SR(port); + TRACE_REGISTER_READ(offset, data, "SRa"); + break; + case 2: // BRG Test + data = 0; + TRACE_REGISTER_READ(offset, data, "BRG Test"); + break; + case 3: // RHRa (Receive Holding Register) + data = read_RHR(port); + TRACE_REGISTER_READ(offset, data, "RHRa"); + break; + + case 8: // MR1b, MR2b (Mode Register) + data = read_MR(port); + TRACE_REGISTER_READ(offset, data, "MR1b/MR2b"); + break; + case 9: // SRb (Status Register) + data = read_SR(port); + TRACE_REGISTER_READ(offset, data, "SRb"); + break; + case 10: // 1X/16X Test + data = 0; + TRACE_REGISTER_READ(offset, data, "1X/16X Test"); + break; + case 11: // RHRb (Receive Holding Register) + data = read_RHR(port); + TRACE_REGISTER_READ(offset, data, "RHRb"); + break; + + default: + TRACE_REGISTER_READ(offset, data, ""); + } + return data; +} + +void scc2698b_device::write_reg(int offset, uint8_t data) +{ + int device = (offset >> 4) & 3; + int reg = (offset & 15); + // Port index for port register accesses + int port = device * 2 + (reg >> 3); + + switch (reg) + { + case 0: // MR1a, MR2a (Mode Register) + TRACE_REGISTER_WRITE(offset, data, "MR1a/MR2a"); + write_MR(port, data); + break; + case 1: // CSRa (Clock Select Register) + TRACE_REGISTER_WRITE(offset, data, "CSRa"); + write_CSR(port, data); + break; + case 2: // CRa (Command Register) + TRACE_REGISTER_WRITE(offset, data, "CRa"); + write_CR(port, data); + break; + case 3: // THRa (Transmit Holding Register) + TRACE_REGISTER_WRITE(offset, data, "THRa"); + write_THR(port, data); + break; + case 4: // ACRA + TRACE_REGISTER_WRITE(offset, data, "ACRA"); + m_blocks[device].ACR = data; + update_block_baudrate(device); + break; + case 5: // IMRA + TRACE_REGISTER_WRITE(offset, data, "IMRA"); + + break; + case 6: // CTPUA + TRACE_REGISTER_WRITE(offset, data, "CTPUA"); + + break; + case 7: // CTPLA + TRACE_REGISTER_WRITE(offset, data, "CTPLA"); + + break; + case 8: // MR1b, MR2b (Mode Register) + TRACE_REGISTER_WRITE(offset, data, "MR1b/MR2b"); + write_MR(port, data); + break; + case 9: // CSRb (Clock Select Register) + TRACE_REGISTER_WRITE(offset, data, "CSRb"); + write_CSR(port, data); + break; + case 10: // CRb (Command Register) + TRACE_REGISTER_WRITE(offset, data, "CRb"); + write_CR(port, data); + break; + case 11: // THRb (Transmit Holding Register) + TRACE_REGISTER_WRITE(offset, data, "THRb"); + write_THR(port, data); + break; + case 12: // Reserved + TRACE_REGISTER_WRITE(offset, data, "Reserved"); + break; + case 13: // OPCRA + TRACE_REGISTER_WRITE(offset, data, "OPCRA"); + // Set the MPP pin input/ouput based on OPCR bit 7 + { + scc2698b_channel* channel = get_channel(device * 2); + channel->set_mpp_output(!!(data & 0x80)); + channel = get_channel(device * 2 + 1); + channel->set_mpp_output(!!(data & 0x80)); + } + break; + case 14: // Reserved + TRACE_REGISTER_WRITE(offset, data, "Reserved"); + break; + case 15: // Reserved + TRACE_REGISTER_WRITE(offset, data, "Reserved"); + break; + } +} + +WRITE_LINE_MEMBER(scc2698b_device::port_a_rx_w) { m_channel[0]->rx_w(state); } +WRITE_LINE_MEMBER(scc2698b_device::port_b_rx_w) { m_channel[1]->rx_w(state); } +WRITE_LINE_MEMBER(scc2698b_device::port_c_rx_w) { m_channel[2]->rx_w(state); } +WRITE_LINE_MEMBER(scc2698b_device::port_d_rx_w) { m_channel[3]->rx_w(state); } +WRITE_LINE_MEMBER(scc2698b_device::port_e_rx_w) { m_channel[4]->rx_w(state); } +WRITE_LINE_MEMBER(scc2698b_device::port_f_rx_w) { m_channel[5]->rx_w(state); } +WRITE_LINE_MEMBER(scc2698b_device::port_g_rx_w) { m_channel[6]->rx_w(state); } +WRITE_LINE_MEMBER(scc2698b_device::port_h_rx_w) { m_channel[7]->rx_w(state); } + + +scc2698b_channel* scc2698b_device::get_channel(int port) +{ + return &*m_channel[port]; +} + +void scc2698b_device::reset_port(int port) +{ + reset_port_tx(port); + reset_port_rx(port); + + scc2698b_channel* channel = get_channel(port); + channel->moderegister_ptr = 0; +} +void scc2698b_device::reset_port_tx(int port) +{ + scc2698b_channel* channel = get_channel(port); + channel->reset_tx(); +} +void scc2698b_device::reset_port_rx(int port) +{ + scc2698b_channel* channel = get_channel(port); + channel->reset_rx(); +} + + +void scc2698b_device::write_MR(int port, int value) +{ + scc2698b_channel* channel = get_channel(port); + if (channel->moderegister_ptr == 0) + { + // Write MR1 + channel->MR1 = value; + } + else + { + // Write MR2 + channel->MR2 = value; + } + channel->moderegister_ptr = 1; + + channel->update_serial_configuration(); +} +void scc2698b_device::write_CSR(int port, int value) +{ + scc2698b_channel* channel = get_channel(port); + + channel->CSR = value; + update_port_baudrate(port); +} +void scc2698b_device::write_CR(int port, int value) +{ + scc2698b_channel* channel = get_channel(port); + // Todo: enable/disable TX/RX + if (value & 1) // Enable RX + { + channel->set_rx_enable(true); + } + if (value & 2) // Disable RX + { + channel->set_rx_enable(false); + } + if (value & 4) // Enable TX + { + channel->set_tx_enable(true); + } + if (value & 8) // Disable TX + { + channel->set_tx_enable(false); + } + + + switch (value >> 4) + { + case 0: // NOP + break; + case 1: // Reset MR Pointer + channel->moderegister_ptr = 0; + break; + case 2: // Reset Receiver + reset_port_rx(port); + break; + case 3: // Reset Transmitter + reset_port_tx(port); + break; + case 4: // Reset status register error bits + channel->SR &= 0x0F; + break; + default: + logerror("Unimplemented Command Register write\n"); + + } +} +void scc2698b_device::write_THR(int port, int value) +{ + scc2698b_channel* channel = get_channel(port); + channel->write_TXH(value); +} + +int scc2698b_device::read_MR(int port) +{ + scc2698b_channel* channel = get_channel(port); + int data = 0; + if (channel->moderegister_ptr == 0) + { + // Read MR1 + data = channel->MR1; + } + else + { + // Read MR2 + data = channel->MR2; + } + + channel->moderegister_ptr = 1; + return data; +} +int scc2698b_device::read_SR(int port) +{ + scc2698b_channel* channel = get_channel(port); + return channel->read_SR(); +} +int scc2698b_device::read_RHR(int port) +{ + scc2698b_channel* channel = get_channel(port); + return channel->read_RXH(); +} + +void scc2698b_device::update_block_baudrate(int block) +{ + update_port_baudrate(block * 2); + update_port_baudrate(block * 2 + 1); +} + +void scc2698b_device::update_port_baudrate(int port) +{ + scc2698b_channel* channel = get_channel(port); + channel->set_tx_bittime(generate_baudrate(port / 2, 1, channel->CSR & 15)); + channel->set_rx_bittime(generate_baudrate(port / 2, 0, channel->CSR >> 4)); +} + +attotime scc2698b_device::generate_baudrate(int block, int tx, int table_index) +{ + if (table_index < 13) + { + // Table based bit time calculation + int divider = 0; + if (m_blocks[block].ACR & 0x80) + { + divider = BAUD_DIVIDER_ACR7_1[table_index]; + } + else + { + divider = BAUD_DIVIDER_ACR7_0[table_index]; + } + + if (divider == 0) + { + logerror("Unimplemented baud rate selected. (%d)\n", table_index); + return attotime::never; + } + + int frequency = configured_clock() / (divider * 8); + TRACE_CONFIG("Set %s baud rate to %dHz (clock divider = %d)\n", tx ? "Transmit" : "Receive", frequency, divider * 8); + + return attotime::from_hz(configured_clock()) * (divider * 8); + } + else + { + // todo: Actually do timer based and more advanced baud rate generation. + logerror("Warning: Unimplemented baud rate mode"); + return attotime::never; + } +} + + + + +MACHINE_CONFIG_START(scc2698b_device::device_add_mconfig) + for (required_device &channel : m_channel) + SCC2698B_CHANNEL(config, channel, 0); +MACHINE_CONFIG_END diff --git a/src/devices/machine/scc2698b.h b/src/devices/machine/scc2698b.h new file mode 100644 index 00000000000..da9ab7ad8b2 --- /dev/null +++ b/src/devices/machine/scc2698b.h @@ -0,0 +1,220 @@ +// license:GPL-2.0+ +// copyright-holders:Brandon Munger, Stephen Stair + + +#ifndef MAME_MACHINE_SCC2698B_H +#define MAME_MACHINE_SCC2698B_H + +class scc2698b_device; + +#include "diserial.h" + +#define SCC2698B_RX_FIFO_SIZE 3 + +// _port should be a letter port index from "a" .. "h" + + + +class scc2698b_channel : public device_t, public device_serial_interface +{ + friend class scc2698b_device; +public: + scc2698b_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + DECLARE_WRITE_LINE_MEMBER(mpi0_w); + DECLARE_WRITE_LINE_MEMBER(mpi1_w); + + virtual void rcv_complete() override; // Rx completed receiving byte + virtual void tra_complete() override; // Tx completed sending byte + virtual void tra_callback() override; // Tx send bit + + void write_TXH(int txh); + int read_RXH(); + + void reset_all(); + void reset_tx(); + void reset_rx(); + + void set_tx_enable(bool enable); + void set_rx_enable(bool enable); + + void update_serial_configuration(); + + void set_tx_bittime(const attotime &bittime); + void set_rx_bittime(const attotime &bittime); + + void set_mpp_output(bool output); + + + int read_SR(); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +// devcb_write_line write_tx, write_mpp1, write_mpp2, write_mpo; + + int moderegister_ptr; + + int tx_transmitting; + int tx_fifo; + int tx_bytecount; + u8 rx_fifo[SCC2698B_RX_FIFO_SIZE]; + int rx_bytecount; + + u8 MR1, MR2, SR, CR, CSR; + + bool tx_enable; + bool rx_enable; + + void recompute_pin_output(bool force = false); + int mpp1_value, mpp2_value; + bool mpp_is_output; + + int channel_port; + scc2698b_device* parent; +}; + +class scc2698b_device : public device_t +{ +public: + scc2698b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + virtual void map(address_map &map); + + DECLARE_READ8_MEMBER(read); + DECLARE_WRITE8_MEMBER(write); + + void write_reg(int offset, uint8_t data); + uint8_t read_reg(int offset); + + template devcb_base &set_intr_A(Object &&cb) { return write_intr_A.set_callback(std::forward(cb)); } + template devcb_base &set_intr_B(Object &&cb) { return write_intr_B.set_callback(std::forward(cb)); } + template devcb_base &set_intr_C(Object &&cb) { return write_intr_C.set_callback(std::forward(cb)); } + template devcb_base &set_intr_D(Object &&cb) { return write_intr_D.set_callback(std::forward(cb)); } + + required_device_array m_channel; + + DECLARE_WRITE_LINE_MEMBER(port_a_rx_w); + DECLARE_WRITE_LINE_MEMBER(port_b_rx_w); + DECLARE_WRITE_LINE_MEMBER(port_c_rx_w); + DECLARE_WRITE_LINE_MEMBER(port_d_rx_w); + DECLARE_WRITE_LINE_MEMBER(port_e_rx_w); + DECLARE_WRITE_LINE_MEMBER(port_f_rx_w); + DECLARE_WRITE_LINE_MEMBER(port_g_rx_w); + DECLARE_WRITE_LINE_MEMBER(port_h_rx_w); + + + void write_line_tx(int port, int value); + void write_line_mpp1(int port, int value); + void write_line_mpp2(int port, int value); + void write_line_mpo(int port, int value); + + + auto tx_callback(char port) { + switch (port) { + case 'a': return write_a_tx.bind(); + case 'b': return write_b_tx.bind(); + case 'c': return write_c_tx.bind(); + case 'd': return write_d_tx.bind(); + case 'e': return write_e_tx.bind(); + case 'f': return write_f_tx.bind(); + case 'g': return write_g_tx.bind(); + case 'h': return write_h_tx.bind(); + default: + fatalerror("Wrong port for tx_callback\n"); + } + } + + auto mpp1_callback(char port) { + switch (port) { + case 'a': return write_a_mpp1.bind(); + case 'b': return write_b_mpp1.bind(); + case 'c': return write_c_mpp1.bind(); + case 'd': return write_d_mpp1.bind(); + case 'e': return write_e_mpp1.bind(); + case 'f': return write_f_mpp1.bind(); + case 'g': return write_g_mpp1.bind(); + case 'h': return write_h_mpp1.bind(); + default: + fatalerror("Wrong port for mpp1_callback\n"); + } + } + + auto mpp2_callback(char port) { + switch (port) { + case 'a': return write_a_mpp2.bind(); + case 'b': return write_b_mpp2.bind(); + case 'c': return write_c_mpp2.bind(); + case 'd': return write_d_mpp2.bind(); + case 'e': return write_e_mpp2.bind(); + case 'f': return write_f_mpp2.bind(); + case 'g': return write_g_mpp2.bind(); + case 'h': return write_h_mpp2.bind(); + default: + fatalerror("Wrong port for mpp2_callback\n"); + } + } + + auto mpo_callback(char port) { + switch (port) { + case 'a': return write_a_mpo.bind(); + case 'b': return write_b_mpo.bind(); + case 'c': return write_c_mpo.bind(); + case 'd': return write_d_mpo.bind(); + case 'e': return write_e_mpo.bind(); + case 'f': return write_f_mpo.bind(); + case 'g': return write_g_mpo.bind(); + case 'h': return write_h_mpo.bind(); + default: + fatalerror("Wrong port for mpo_callback\n"); + } + } + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + virtual void device_add_mconfig(machine_config &config) override; + + devcb_write_line write_intr_A, write_intr_B, write_intr_C, write_intr_D; + devcb_write_line write_a_tx, write_b_tx, write_c_tx, write_d_tx, write_e_tx, write_f_tx, write_g_tx, write_h_tx; + devcb_write_line write_a_mpp1, write_b_mpp1, write_c_mpp1, write_d_mpp1, write_e_mpp1, write_f_mpp1, write_g_mpp1, write_h_mpp1; + devcb_write_line write_a_mpp2, write_b_mpp2, write_c_mpp2, write_d_mpp2, write_e_mpp2, write_f_mpp2, write_g_mpp2, write_h_mpp2; + devcb_write_line write_a_mpo, write_b_mpo, write_c_mpo, write_d_mpo, write_e_mpo, write_f_mpo, write_g_mpo, write_h_mpo; + + void log_register_access(int offset, int value, const char* direction, const char* reg_name); + + scc2698b_channel* get_channel(int port); + + void reset_port(int port); + void reset_port_tx(int port); + void reset_port_rx(int port); + + void write_MR(int port, int value); + void write_CSR(int port, int value); + void write_CR(int port, int value); + void write_THR(int port, int value); + + int read_MR(int port); + int read_SR(int port); + int read_RHR(int port); + + void update_block_baudrate(int block); + void update_port_baudrate(int port); + attotime generate_baudrate(int block, int tx, int table_index); + + +private: + struct Block { + u8 ACR; + }; + + Block m_blocks[4]; + +}; + +DECLARE_DEVICE_TYPE(SCC2698B, scc2698b_device) +DECLARE_DEVICE_TYPE(SCC2698B_CHANNEL, scc2698b_channel) + +#endif // MAME_MACHINE_SCC2698B_H diff --git a/src/devices/machine/smioc.cpp b/src/devices/machine/smioc.cpp index f0407096e0b..d3adea99393 100644 --- a/src/devices/machine/smioc.cpp +++ b/src/devices/machine/smioc.cpp @@ -1,5 +1,5 @@ // license:GPL-2.0+ -// copyright-holders:Brandon Munger +// copyright-holders:Brandon Munger, Stephen Stair /********************************************************************** ROLM 9751 9005 System Monitor Input/Output Card emulation @@ -20,11 +20,11 @@ * CPU - Intel N80C188 L0450591 @ ??MHz - U23 * MCU - Signetics SC87C451CCA68 220CP079109KA 97D8641 - U70 * DMA - KS82C37A - U46, U47, U48, U49, U50 - * SCC - Signetics SCC2698BC1A84 - U67 + * SCC - Signetics SCC2698BC1A84 - U67 * Memory - NEC D43256AGU-10LL 8948A9038 SRAM 32KB - U51 * Memory - Mitsubishi M5M187AJ 046101-35 SRAM 64K X 1?? - U37 - * Memory - AT&T M79018DX-15B 2K X 9 Dual Port SRAM - U53 - * Memory - AT&T M79018DX-15B 2K X 9 Dual Port SRAM - U54 + * Memory - AT&T M79018DX-15B 2K X 9 Dual Port SRAM - U53 + * Memory - AT&T M79018DX-15B 2K X 9 Dual Port SRAM - U54 Logic: * U8 - 22V10-25JC @@ -42,23 +42,21 @@ Program Memory: * 0x00000 - 0x07FFF : SRAM D43256AGU-10LL 32KB * 0xF8000 - 0xFFFFF : ROM 27C256 PLCC32 32KB - * 0xC0080 - 0xC008F : KS82C37A - Probably RAM DMA - * 0xC0090 - 0xC009F : KS82C37A - Serial DMA (Port 1 and 2?) - * 0xC00A0 - 0xC00AF : KS82C37A - Serial DMA (Port 3 and 4?) - * 0xC00B0 - 0xC00BF : KS82C37A - Serial DMA (Port 5 and 6?) - * 0xC00C0 - 0xC00CF : KS82C37A - Serial DMA (Port 7 and 8?) + * 0xC0080 - 0xC008F : KS82C37A - Probably RAM DMA + * 0xC0090 - 0xC009F : KS82C37A - Serial DMA (Port 1 and 2?) + * 0xC00A0 - 0xC00AF : KS82C37A - Serial DMA (Port 3 and 4?) + * 0xC00B0 - 0xC00BF : KS82C37A - Serial DMA (Port 5 and 6?) + * 0xC00C0 - 0xC00CF : KS82C37A - Serial DMA (Port 7 and 8?) IO Memory: * Unknown TODO: - * Emulate SCC and hook up RS232 ports - * Hook up console to RS232 port 1 - * Hook up System Monitor II to RS232 port 2 - * Dump 87C451 rom data and emulate MCU - * Dump 87C51 on SMIOC interconnect box - * Dump all PAL chips - * Hook up status LEDs + * Hook up System Monitor II to RS232 port 2 + * Dump 87C451 rom data and emulate MCU + * Dump 87C51 on SMIOC interconnect box + * Dump all PAL chips + * Hook up status LEDs */ #include "emu.h" @@ -71,6 +69,20 @@ #define I188_TAG "smioc_i188" // U23 +/* Trace information about commands issued to SMIOC */ +#define ENABLE_LOG_COMMAND 0 +/* Trace accesses to parameter ram (used for DMA parameters from 68k) */ +#define ENABLE_LOG_PARAMETER_RAM 0 +/* Log DMA related memory movement and access to C01xx register space */ +#define ENABLE_LOG_REGISTER_ACCESS 0 +/* Trace some accesses to C01xx register space, related to command handling, command/status flow control */ +#define ENABLE_LOG_REGISTER_DETAILS 0 + +#define LOG_COMMAND if(ENABLE_LOG_COMMAND) logerror +#define LOG_PARAMETER_RAM if(ENABLE_LOG_PARAMETER_RAM) logerror +#define LOG_REGISTER_ACCESS if(ENABLE_LOG_REGISTER_ACCESS) logerror +#define LOG_REGISTER_DETAILS if(ENABLE_LOG_REGISTER_DETAILS) logerror + //************************************************************************** // DEVICE DEFINITIONS //************************************************************************** @@ -98,32 +110,80 @@ const tiny_rom_entry *smioc_device::device_rom_region() const //------------------------------------------------- // ADDRESS_MAP( smioc_mem ) //------------------------------------------------- - void smioc_device::smioc_mem(address_map &map) { map(0x00000, 0x07FFF).ram().share("smioc_ram"); + //map(0x40000, 0x4FFFF).rw(FUNC(smioc_device::ram2_mmio_r), FUNC(smioc_device::ram2_mmio_w)); + map(0x40000, 0x4FFFF).rw(FUNC(smioc_device::ram2_mmio_r), FUNC(smioc_device::ram2_mmio_w)); // 4kb of ram in the 0x4xxxx window, mainly used by the board's logic to proxy command parameters and data. + map(0x50000, 0x5FFFF).rw(FUNC(smioc_device::dma68k_r), FUNC(smioc_device::dma68k_w)); map(0xC0080, 0xC008F).rw("dma8237_1", FUNC(am9517a_device::read), FUNC(am9517a_device::write)); // Probably RAM DMA map(0xC0090, 0xC009F).rw("dma8237_2", FUNC(am9517a_device::read), FUNC(am9517a_device::write)); // Serial DMA map(0xC00A0, 0xC00AF).rw("dma8237_3", FUNC(am9517a_device::read), FUNC(am9517a_device::write)); // Serial DMA map(0xC00B0, 0xC00BF).rw("dma8237_4", FUNC(am9517a_device::read), FUNC(am9517a_device::write)); // Serial DMA map(0xC00C0, 0xC00CF).rw("dma8237_5", FUNC(am9517a_device::read), FUNC(am9517a_device::write)); // Serial DMA + map(0xC0100, 0xC01FF).rw(FUNC(smioc_device::boardlogic_mmio_r), FUNC(smioc_device::boardlogic_mmio_w)); + map(0xC0200, 0xC023F).rw("scc2698b", FUNC(scc2698b_device::read), FUNC(scc2698b_device::write)); map(0xF8000, 0xFFFFF).rom().region("rom", 0); } -MACHINE_CONFIG_START(smioc_device::device_add_mconfig) +static DEVICE_INPUT_DEFAULTS_START(terminal) + DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_9600) + DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_9600) + DEVICE_INPUT_DEFAULTS("RS232_STARTBITS", 0xff, RS232_STARTBITS_1) + DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_7) + DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_EVEN) + DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1) +DEVICE_INPUT_DEFAULTS_END + + +void smioc_device::device_add_mconfig(machine_config &config) +{ /* CPU - Intel 80C188 */ - MCFG_DEVICE_ADD(I188_TAG, I80188, XTAL(20'000'000) / 2) // Clock division unknown - MCFG_DEVICE_PROGRAM_MAP(smioc_mem) + I80188(config, m_smioccpu, XTAL(20'000'000)); // Clock division unknown + config.m_minimum_quantum = attotime::from_hz(1000); + m_smioccpu->set_addrmap(AS_PROGRAM, &smioc_device::smioc_mem); /* DMA */ for (required_device &dma : m_dma8237) AM9517A(config, dma, 20_MHz_XTAL / 4); // Clock division unknown /* RS232 */ - /* Port 1: Console */ for (required_device &rs232_port : m_rs232_p) RS232_PORT(config, rs232_port, default_rs232_devices, nullptr); -MACHINE_CONFIG_END + + m_rs232_p[0]->set_default_option("terminal"); + m_rs232_p[0]->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); + + /* SCC2698B */ + scc2698b_device &scc2698b(SCC2698B(config, "scc2698b", XTAL(3'686'400))); + scc2698b.tx_callback('a').set("rs232_p1", FUNC(rs232_port_device::write_txd)); + scc2698b.mpp1_callback('a').set("dma8237_2", FUNC(am9517a_device::dreq1_w)).invert(); + scc2698b.mpp2_callback('a').set("dma8237_2", FUNC(am9517a_device::dreq0_w)).invert(); + scc2698b.tx_callback('b').set("rs232_p2", FUNC(rs232_port_device::write_txd)); + scc2698b.mpp1_callback('b').set("dma8237_2", FUNC(am9517a_device::dreq3_w)).invert(); + scc2698b.mpp2_callback('b').set("dma8237_2", FUNC(am9517a_device::dreq2_w)).invert(); + + /* The first dma8237 is set up in cascade mode, and each of its four channels provides HREQ/HACK to the other 4 DMA controllers*/ + m_dma8237[0]->out_dack_callback<0>().set("dma8237_2", FUNC(am9517a_device::hack_w)); + m_dma8237[0]->out_dack_callback<1>().set("dma8237_3", FUNC(am9517a_device::hack_w)); + m_dma8237[0]->out_dack_callback<2>().set("dma8237_4", FUNC(am9517a_device::hack_w)); + m_dma8237[0]->out_dack_callback<3>().set("dma8237_5", FUNC(am9517a_device::hack_w)); + m_dma8237[0]->out_hreq_callback().set("dma8237_1", FUNC(am9517a_device::hack_w)); + + /* Connect base DMA controller's Hold Request to its own Hold ACK + The CPU doesn't support hold request / hold ack pins, so we will pretend that the DMA controller immediately gets what it wants every time it asks. + This will keep things moving forward, but the CPU will continue going through DMA requests rather than halting for a few cycles each time. + It shouldn't cause a problem though. + */ + + m_dma8237[1]->in_memr_callback().set(FUNC(smioc_device::dma8237_2_dmaread)); + m_dma8237[1]->out_memw_callback().set(FUNC(smioc_device::dma8237_2_dmawrite)); + m_dma8237[1]->out_hreq_callback().set("dma8237_1", FUNC(am9517a_device::dreq0_w)); + + m_rs232_p[0]->rxd_handler().set("scc2698b", FUNC(scc2698b_device::port_a_rx_w)); + m_rs232_p[1]->rxd_handler().set("scc2698b", FUNC(scc2698b_device::port_b_rx_w)); + +} //************************************************************************** // LIVE DEVICE @@ -138,8 +198,15 @@ smioc_device::smioc_device(const machine_config &mconfig, const char *tag, devic m_smioccpu(*this, I188_TAG), m_dma8237(*this, "dma8237_%u", 1), m_rs232_p(*this, "rs232_p%u", 1), - m_smioc_ram(*this, "smioc_ram") + m_scc2698b(*this, "scc2698b"), + m_smioc_ram(*this, "smioc_ram"), + m_dma_timer(nullptr), + m_451_timer(nullptr), + m_m68k_r_cb(*this), + m_m68k_w_cb(*this) { + + } //------------------------------------------------- @@ -148,6 +215,14 @@ smioc_device::smioc_device(const machine_config &mconfig, const char *tag, devic void smioc_device::device_start() { + m_dma_timer = timer_alloc(0, nullptr); + m_451_timer = timer_alloc(1, nullptr); + + /* Resolve callbacks */ + m_m68k_r_cb.resolve_safe(0); + m_m68k_w_cb.resolve_safe(); + + m_451_timer->adjust(attotime::from_msec(200), 0, attotime::from_msec(200)); } //------------------------------------------------- @@ -158,4 +233,483 @@ void smioc_device::device_reset() { /* Reset CPU */ m_smioccpu->reset(); + m_smioccpu->drq0_w(1); + m_deviceBusy = 1; + + +} + +void smioc_device::SoftReset() +{ + m_smioccpu->reset(); + m_smioccpu->drq0_w(1); + m_deviceBusy = 1; +} + + +void smioc_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr) +{ + switch (tid) + { + case 0: // DMA Timer + m_smioccpu->drq0_w(1); + break; + + case 1: // 451 emulation timer - Trigger the SMIOC to read from C0180 and store data + m_smioccpu->int1_w(CLEAR_LINE); + m_smioccpu->int1_w(HOLD_LINE); + break; + } +} + +void smioc_device::SendCommand(u16 command) +{ + LOG_COMMAND("%s SMIOC Command %04x (Address %04x, Length %04x)\n", machine().time().as_string(), command, ReadDmaParameter(smiocdma_sendaddress), ReadDmaParameter(smiocdma_sendlength)); + + // Command 0xxx is a special value that tells the hardware what channel/port it's working with for the purpose of writing DMA base / length registers + if (command < 0x1000) + { + m_activePortIndex = command & 7; + return; + } + + m_commandValue = command; + m_requestFlags_11D |= 1; + m_deviceBusy = 1; + + // Invalidate status if we hit a command. + m_status = 0; + m_statusvalid = false; + m_enable_hacky_status = false; + + m_smioccpu->int2_w(CLEAR_LINE); + m_smioccpu->int2_w(HOLD_LINE); + +} + +void smioc_device::SendCommand2(u16 command) +{ + LOG_COMMAND("%s SMIOC Command2 %04x (Address %04x, Length %04x)\n", machine().time().as_string(), command, ReadDmaParameter(smiocdma_sendaddress), ReadDmaParameter(smiocdma_sendlength)); + + m_commandValue2 = command; + m_requestFlags_11D |= 4; + m_deviceBusy = 1; + + m_smioccpu->int2_w(CLEAR_LINE); + m_smioccpu->int2_w(HOLD_LINE); + +} +void smioc_device::SetCommandParameter(u16 parameter) +{ + WriteRamParameter("SetCommandParameter", "Parameter", 0xCC6, parameter); + m_requestFlags_11D |= 0x40; + m_smioccpu->int2_w(CLEAR_LINE); + m_smioccpu->int2_w(HOLD_LINE); +} +void smioc_device::SetCommandParameter2(u16 parameter) +{ + WriteRamParameter("SetCommandParameter2", "Parameter", 0xB86, parameter); + m_requestFlags_11D |= 0x80; + m_smioccpu->int2_w(CLEAR_LINE); + m_smioccpu->int2_w(HOLD_LINE); +} + + + +u16 smioc_device::GetStatus() +{ + return m_status | 0x0008; +} +u16 smioc_device::GetStatus2() +{ + return m_status2 | 0x0008; +} + + +void smioc_device::ClearStatus() +{ + m_status = 0; + m_statusvalid = false; + if (m_statusrequest) + { + AdvanceStatus(); + } +} +void smioc_device::ClearStatus2() +{ + m_status2 = 0; + m_statusvalid2 = false; + if (m_statusrequest2) + { + AdvanceStatus2(); + } +} + +void smioc_device::ClearParameter() +{ + // Indicate to SMIOC that parameter has been read. + m_requestFlags_11D |= 0x02; // bit 1 - E2E 0x800 + m_smioccpu->int2_w(HOLD_LINE); + + m_wordcount = 0; +} +void smioc_device::ClearParameter2() +{ + // Indicate to SMIOC that alternate endpoint parameter has been read. + m_requestFlags_11D |= 0x08; // bit 3 - E2E 0x400 + m_smioccpu->int2_w(HOLD_LINE); + + m_wordcount2 = 0; +} +void smioc_device::SetDmaParameter(smioc_dma_parameter_t param, u16 value) +{ + int address = DmaParameterAddress(param); + + const char* paramNames[] = { "smiocdma_sendaddress", "smiocdma_sendlength", "smiocdma_recvaddress", "smiocdma_recvlength" }; + const char* paramName = "?"; + if (param >= 0 && param < (sizeof(paramNames) / sizeof(*paramNames))) + { + paramName = paramNames[param]; + } + + WriteRamParameter("SetDmaParameter", paramName, address, value); } + +u16 smioc_device::ReadDmaParameter(smioc_dma_parameter_t param) +{ + int address = DmaParameterAddress(param); + return m_logic_ram[address] | (m_logic_ram[address + 1] << 8); +} + +int smioc_device::DmaParameterAddress(smioc_dma_parameter_t param) +{ + int baseAddress = -1; + int p4offset = 0xC0; // The address offset from port 0 to port 4 + switch (param) + { + case smiocdma_sendaddress: // Send to SMIOC - For Serial TX data + baseAddress = 0xCD8; + break; + + case smiocdma_sendlength: + baseAddress = 0xCE8; + break; + + case smiocdma_recvaddress: // Recv from SMIOC - For Serial RX data + baseAddress = 0xCF0; + break; + + case smiocdma_recvlength: + baseAddress = 0xCF8; + break; + } + if (baseAddress != -1) + { + int portOffset = (m_activePortIndex & 1) * 4 + (m_activePortIndex & 2) + ((m_activePortIndex & 4) ? p4offset : 0); + + int address = baseAddress + portOffset; + + return address; + } + else + { + fatalerror("Invalid DMA Parameter 0x%x - unable to continue.", param); + } +} + +void smioc_device::WriteRamParameter(const char* function, const char* register_name, int address, int value) +{ + if (address < 0 || address > 0xFFE) + { + logerror("%s Invalid Parameter Ram write (%s %s %04x %04x)\n", machine().time().as_string(), function, register_name, address, value); + return; + } + m_logic_ram[address] = value & 0xFF; + m_logic_ram[address + 1] = (value >> 8) & 0xFF; + + LOG_PARAMETER_RAM("%s %s (%s) ram2[0x%04x] = %04X\n", machine().time().as_string(), function, register_name, address, value); +} + + + + +void smioc_device::update_and_log(u16& reg, u16 newValue, const char* register_name) +{ + LOG_PARAMETER_RAM("%s Update %s %04X -> %04X\n", machine().time().as_string(), register_name, reg, newValue); + reg = newValue; +} + +READ8_MEMBER(smioc_device::ram2_mmio_r) +{ + const char * description = ""; + u8 data = m_logic_ram[offset & 0xFFF]; + switch (offset) + { + + case 0xB82: // Command 2 - C011D Bit 2 + data = m_commandValue2 & 0xFF; + description = "(Command2)"; + break; + case 0xB83: + data = m_commandValue2 >> 8; + description = "(Command2)"; + break; + + case 0xB86: // Command parameter 2 - C011D Bit 7 + description = "(Command Parameter 2)"; + break; + case 0xB87: + description = "(Command Parameter 2)"; + break; + + case 0xCC2: // Command - C011D Bit 0 + data = m_commandValue & 0xFF; + description = "(Command)"; + break; + case 0xCC3: + data = m_commandValue >> 8; + description = "(Command)"; + break; + + case 0xCC6: // Command parameter - C011D Bit 6 + description = "(Command Parameter)"; + break; + case 0xCC7: + description = "(Command Parameter)"; + break; + + case 0xCD8: // DMA source address (for writing characters) - Port 0 + description = "(DMA Source)"; + break; + case 0xCD9: + description = "(DMA Source)"; + break; + + case 0xCE8: // DMA Length (for writing characters) - Port 0 + description = "(DMA Length)"; + break; + case 0xCE9: + description = "(DMA Length)"; + break; + + } + LOG_PARAMETER_RAM("ram2[%04X] => %02X %s\n", offset, data, description); + return data; +} + +WRITE8_MEMBER(smioc_device::ram2_mmio_w) +{ + const char * description = ""; + + m_logic_ram[offset & 0xFFF] = data; + + switch (offset) // Offset based on C0100 register base + { + case 0x11D: + // This value is written after the CPU resets, it may be accessible to the 68k via some register but that is not clear. + description = "(Indicate Reset)"; + break; + + case 0xC84: + //update_and_log(m_wordcount, (m_wordcount & 0xFF00) | data, "Wordcount[40C84]"); + return; + case 0xC85: + //update_and_log(m_wordcount, (m_wordcount & 0xFF) | (data<<8), "Wordcount[40C85]"); + return; + + case 0xC88: + //update_and_log(m_wordcount2, (m_wordcount2 & 0xFF00) | data, "Wordcount2[40C88]"); + return; + case 0xC89: + //update_and_log(m_wordcount2, (m_wordcount2 & 0xFF) | (data<<8), "Wordcount2[40C89]"); + return; + + case 0xB84: + update_and_log(m_shadowstatus2, (m_shadowstatus2 & 0xFF00) | (data), "Status2[40B84]"); + return; + case 0xB85: + update_and_log(m_shadowstatus2, (m_shadowstatus2 & 0xFF) | (data << 8), "Status2[40B85]"); + m_status2 |= 0x40; + return; + + case 0xCC4: + update_and_log(m_shadowstatus, (m_shadowstatus & 0xFF00) | (data), "Status[40CC4]"); + return; + case 0xCC5: + update_and_log(m_shadowstatus, (m_shadowstatus & 0xFF) | (data << 8), "Status[40CC5]"); + return; + + case 0xCC8: + update_and_log(m_wordcount, (m_wordcount & 0xFF00) | (data), "Wordcount[40CC8]"); + return; + case 0xCC9: + update_and_log(m_wordcount, (m_wordcount & 0xFF) | (data << 8), "Wordcount[40CC9]"); + return; + + case 0xB88: + update_and_log(m_wordcount, (m_wordcount & 0xFF00) | (data), "Wordcount2[40B88]"); + return; + case 0xB89: + update_and_log(m_wordcount, (m_wordcount & 0xFF) | (data << 8), "Wordcount2[40CC9]"); + return; + } + + LOG_PARAMETER_RAM("ram2[%04X] <= %02X %s\n", offset, data, description); +} + +READ8_MEMBER(smioc_device::dma68k_r) +{ + u8 data = 0; + + m_dma_timer->adjust(attotime::from_usec(10)); + + // This behavior is not currently well understood - but it seems like the address might be off by one. + // When the SMIOC starts a DMA read from the 68k memory transfer space, the first word it reads (at the right address), it throws away + // And it deliberately reads the DMA at address+1. + // One possibility is that issuing the read is more like a doorbell for the hardware on the board to fetch a data stream starting at that address + // but it's not currently clear how it works, so implementing read for the time being as reading address-1 (address - 2 bytes) + // Another (and maybe more likely) possibility is that the hardware is doing a slow fetch of the value, and the N+1 read returns data from the Nth read's address + + data = m_m68k_r_cb(offset-1); + LOG_REGISTER_ACCESS("%s dma68k[%04X] => %02X\n", machine().time().as_string(), offset, data); + return data; +} + +WRITE8_MEMBER(smioc_device::dma68k_w) +{ + + m_dma_timer->adjust(attotime::from_usec(10)); + + m_m68k_w_cb(offset, data); + + LOG_REGISTER_ACCESS("%s dma68k[%04X] <= %02X\n", machine().time().as_string(), offset, data); +} + +READ8_MEMBER(smioc_device::boardlogic_mmio_r) +{ + u8 data = 0xFF; + switch (offset) + { + + case 0x19: // Hardware revision? + // Top bit controls which set of register locations in RAM2 are polled + data = 0x7F; + break; + + case 0x1D: // C011D (HW Request flags) + data = m_requestFlags_11D; + LOG_REGISTER_DETAILS("%s C011D Read => %02X\n", machine().time().as_string(), data); + // Assume this is a clear-on-read register - It is read in one location and all set bits are acted on once it is read. + m_requestFlags_11D = 0; + + + break; + + case 0x1F: // C011F (HW Status flags?) + // 0x80, 0x40 seem to be HW request to cancel ongoing DMA requests, maybe related to board reset? + // 0x01 - When this is 0, advance some state perhaps related to talking to the 8051 + data = 0xFF; + break; + + + case 0x80: // C0180 - Related to talking to the 87C451 chip interfacing to the breakout box + data = 0x05; // Hack hack + // We need the 8051 to provide a pair of bytes with 0x05 followed by a byte with the bottom bit set. + // For now 0x05 should work to provide this condition, and might make the required status work. + break; + + } + LOG_REGISTER_ACCESS("logic[%04X] => %02X\n", offset, data); + return data; +} + +WRITE8_MEMBER(smioc_device::boardlogic_mmio_w) +{ + switch (offset) + { + case 0x10: // C0110 (Clear interrupt? This seems to happen a lot but without being related to actually completing anything.) + LOG_REGISTER_DETAILS("%s C0110 Write, DeviceBusy = %02X, RequestFlags = %02X\n", machine().time().as_string(), m_deviceBusy, m_requestFlags_11D); + m_deviceBusy = m_requestFlags_11D; + m_smioccpu->int2_w(CLEAR_LINE); + if (m_requestFlags_11D) + { + m_smioccpu->int2_w(HOLD_LINE); + } + break; + + case 0x11: // C0111 - Set to 1 after providing a status - Acknowledge by hardware by raising bit 4 in C011D (SMIOC E2E flag 0x200 + // Hypothesis: Status needs to be acknowledged before we trigger the interrupt - so moving the following code to clear status callback. + + m_statusrequest = true; + if (!m_statusvalid) + { + // Immediately complete status write. + AdvanceStatus(); + } + else + { + // Wait until host clears status to proceed. + } + + LOG_REGISTER_DETAILS("%s C0111 Write, RequestFlags = %02X\n", machine().time().as_string(), m_requestFlags_11D); + break; + + case 0x12: // C0112 - Set to 1 after providing a status(2?) - Acknowledge by hardware by raising bit 5 in C011D (SMIOC E2E flag 0x100) + m_statusrequest2 = true; + if (!m_statusvalid2) + { + AdvanceStatus2(); + } + LOG_REGISTER_DETAILS("%s C0112 Write, RequestFlags = %02X\n", machine().time().as_string(), m_requestFlags_11D); + break; + + case 0x16: // C0116 - Set to 1 after processing 11D & 0x40 + break; + case 0x17: // C0117 - Set to 1 after processing 11D & 0x80 + break; + + } + LOG_REGISTER_ACCESS("logic[%04X] <= %02X\n", offset, data); +} + +void smioc_device::AdvanceStatus() +{ + m_status = m_shadowstatus | 0x0040; + m_statusvalid = true; + m_enable_hacky_status = true; + if (m_statusrequest) + { + m_requestFlags_11D |= 0x10; // bit 4 + m_smioccpu->int2_w(CLEAR_LINE); + m_smioccpu->int2_w(HOLD_LINE); + } + m_statusrequest = false; +} +void smioc_device::AdvanceStatus2() +{ + m_status2 = m_shadowstatus2 | 0x0040; + m_statusvalid2 = true; + if (m_statusrequest2) + { + m_requestFlags_11D |= 0x20; // bit 5 + m_smioccpu->int2_w(CLEAR_LINE); + m_smioccpu->int2_w(HOLD_LINE); + } + m_statusrequest2 = false; +} + + +READ8_MEMBER(smioc_device::dma8237_2_dmaread) +{ + int data = m_smioccpu->space(AS_PROGRAM).read_byte(offset); + LOG_REGISTER_ACCESS("dma2read [0x%x] => 0x%x\n", offset, data); + m_scc2698b->write_reg(0x03, data); + return data; +} +WRITE8_MEMBER(smioc_device::dma8237_2_dmawrite) +{ + data = m_scc2698b->read_reg(0x03); + LOG_REGISTER_ACCESS("dma2write [0x%x] <= 0x%x\n", offset, data); + m_smioccpu->space(AS_PROGRAM).write_byte(offset, data); +} + diff --git a/src/devices/machine/smioc.h b/src/devices/machine/smioc.h index 39cdb5611c6..e669246f848 100644 --- a/src/devices/machine/smioc.h +++ b/src/devices/machine/smioc.h @@ -14,11 +14,20 @@ #include "cpu/i86/i186.h" #include "machine/am9517a.h" #include "bus/rs232/rs232.h" +#include "machine/scc2698b.h" //************************************************************************** // MACROS / CONSTANTS //************************************************************************** +enum smioc_dma_parameter_t +{ + smiocdma_sendaddress=0, // Send to SMIOC - For Serial TX data + smiocdma_sendlength, + smiocdma_recvaddress, // Recv from SMIOC - For Serial RX data + smiocdma_recvlength +}; + //************************************************************************** // TYPE DEFINITIONS //************************************************************************** @@ -31,6 +40,67 @@ public: /* Constructor and Destructor */ smioc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + template static devcb_base &m68k_r_callback(device_t &device, Object &&cb) { return downcast(device).m_m68k_r_cb.set_callback(std::forward(cb)); } + template static devcb_base &m68k_w_callback(device_t &device, Object &&cb) { return downcast(device).m_m68k_w_cb.set_callback(std::forward(cb)); } + + + DECLARE_READ8_MEMBER(ram2_mmio_r); + DECLARE_WRITE8_MEMBER(ram2_mmio_w); + + DECLARE_READ8_MEMBER(dma68k_r); + DECLARE_WRITE8_MEMBER(dma68k_w); + + DECLARE_READ8_MEMBER(dma8237_2_dmaread); + DECLARE_WRITE8_MEMBER(dma8237_2_dmawrite); + + DECLARE_READ8_MEMBER(boardlogic_mmio_r); + DECLARE_WRITE8_MEMBER(boardlogic_mmio_w); + + + int m_activePortIndex; + + u16 m_status; + u16 m_status2; + u16 m_shadowstatus; // RAM backing for SMIOC's status writes + u16 m_shadowstatus2; + bool m_statusvalid; // Status field has a valid value in it + bool m_statusvalid2; + bool m_statusrequest; // SMIOC has requested to queue a new status value + bool m_statusrequest2; + + u16 m_wordcount; + u16 m_wordcount2; + + u8 m_deviceBusy; + + u8 m_requestFlags_11D; + u16 m_commandValue; + u16 m_commandValue2; + + void SoftReset(); + + void SendCommand(u16 command); + void SendCommand2(u16 command); + void SetCommandParameter(u16 parameter); + void SetCommandParameter2(u16 parameter); + u16 GetStatus(); + u16 GetStatus2(); + void ClearStatus(); + void ClearStatus2(); + void ClearParameter(); + void ClearParameter2(); + + bool m_enable_hacky_status; + int m_status_hack_counter; + + void AdvanceStatus(); + void AdvanceStatus2(); + + void SetDmaParameter(smioc_dma_parameter_t param, u16 value); + + + void WriteRamParameter(const char* function, const char* register_name, int address, int value); + protected: /* Device-level overrides */ virtual void device_start() override; @@ -38,21 +108,43 @@ protected: /* Optional information overrides */ virtual void device_add_mconfig(machine_config &config) override; virtual const tiny_rom_entry *device_rom_region() const override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + + u16 ReadDmaParameter(smioc_dma_parameter_t param); + int DmaParameterAddress(smioc_dma_parameter_t param); private: /* Attached devices */ - required_device m_smioccpu; + required_device m_smioccpu; required_device_array m_dma8237; required_device_array m_rs232_p; + required_device m_scc2698b; + required_shared_ptr m_smioc_ram; + + u8 m_logic_ram[4096]; // 4kb of ram in the 0x4xxxx window, mainly used by the board's logic to proxy command parameters and data. void smioc_mem(address_map &map); + + void update_and_log(u16& reg, u16 newValue, const char* register_name); + + emu_timer *m_dma_timer; + emu_timer *m_451_timer; + + devcb_read8 m_m68k_r_cb; + devcb_write8 m_m68k_w_cb; }; /* Device type */ DECLARE_DEVICE_TYPE(SMIOC, smioc_device) +/* MCFG defines */ +#define MCFG_SMIOC_R_CB(_devcb) \ + smioc_device::m68k_r_callback(*device, DEVCB_##_devcb); +#define MCFG_SMIOC_W_CB(_devcb) \ + smioc_device::m68k_w_callback(*device, DEVCB_##_devcb); + #endif // MAME_MACHINE_SMIOC_H diff --git a/src/mame/drivers/r9751.cpp b/src/mame/drivers/r9751.cpp index eebad9ae277..db45840a84b 100644 --- a/src/mame/drivers/r9751.cpp +++ b/src/mame/drivers/r9751.cpp @@ -1,5 +1,5 @@ // license:GPL-2.0+ -// copyright-holders:Brandon Munger +// copyright-holders:Brandon Munger, Stephen Stair /****************************************************************************** * * Rolm CBX 9751 Driver @@ -59,28 +59,23 @@ #include "machine/smioc.h" #include "softlist.h" -#include - /* Log defines */ #define TRACE_FDC 0 #define TRACE_HDC 0 -#define TRACE_SMIOC 0 -#define TRACE_CPU_REG 0 #define TRACE_LED 0 #define TRACE_DMA 0 -#define TRACE_DEVICE 0x0 +/* Trace accesses to other boards in the system */ +#define ENABLE_TRACE_ALL_DEVICES 0 + +/* Trace accesses to 68k system board registers */ +#define ENABLE_TRACE_SYSTEM 0 +#define SYSTEM_TRACE_LEVEL 5 + + -#define ENABLE_TRACE_SMIOC 0 -#if ENABLE_TRACE_SMIOC -#define TRACE_SMIOC_READ(address, data, reg, text) UnifiedTrace((address),(data)," Read", "SMIOC", (reg), (text)) -#define TRACE_SMIOC_WRITE(address, data, reg, text) UnifiedTrace((address),(data),"Write", "SMIOC", (reg), (text)) -#else -#define TRACE_SMIOC_READ(address, data, reg, text) do {} while (0) -#define TRACE_SMIOC_WRITE(address, data, reg, text) do {} while (0) -#endif class r9751_state : public driver_device { @@ -91,9 +86,15 @@ public: m_pdc(*this, "pdc"), m_smioc(*this, "smioc"), m_wd33c93(*this, "wd33c93"), - m_terminal(*this, "terminal"), m_main_ram(*this, "main_ram") { + device_trace_init(); + system_trace_init(); + } + ~r9751_state() + { + device_trace_teardown(); + system_trace_teardown(); } void r9751(machine_config &config); @@ -101,7 +102,6 @@ public: void init_r9751(); private: - void kbd_put(u8 data); DECLARE_READ32_MEMBER(r9751_mmio_5ff_r); DECLARE_WRITE32_MEMBER(r9751_mmio_5ff_w); @@ -113,7 +113,9 @@ private: DECLARE_WRITE32_MEMBER(r9751_mmio_fff8_w); DECLARE_READ8_MEMBER(pdc_dma_r); - DECLARE_WRITE8_MEMBER(pdc_dma_w); + DECLARE_WRITE8_MEMBER(pdc_dma_w); + DECLARE_READ8_MEMBER(smioc_dma_r); + DECLARE_WRITE8_MEMBER(smioc_dma_w); void r9751_mem(address_map &map); @@ -121,7 +123,6 @@ private: required_device m_pdc; required_device m_smioc; required_device m_wd33c93; - required_device m_terminal; required_shared_ptr m_main_ram; m68000_base_device* ptr_m68000; @@ -131,17 +132,9 @@ private: uint32_t reg_fff80040; uint32_t fdd_dest_address; // 5FF080B0 uint32_t fdd_cmd_complete; - uint32_t smioc_out_addr; - uint32_t smioc_in_addr; uint32_t smioc_dma_bank; - uint32_t smioc_dma_w_length; - uint32_t smioc_dma_r_length; uint32_t fdd_dma_bank; attotime timer_32khz_last; - uint16_t m_serial_status2; - std::queue kbd_queue; - std::queue serial_status_queue; - bool serial_input; // End registers address_space *m_mem; @@ -154,21 +147,338 @@ private: void UnifiedTrace(u32 address, u32 data, const char* operation="Read", const char* Device="SMIOC", const char* RegisterName=nullptr, const char* extraText=nullptr); virtual void machine_reset() override; + void trace_device(int address, int data, const char* direction); + + void system_trace_init(); + void system_trace_teardown(); + void trace_system(int table, int offset, int data, const char* direction); + + uint32_t m_device_trace_enable[2]; + int m_trace_last_address[64]; + int m_trace_last_data[64]; + int m_trace_repeat_count[64]; + char const * m_trace_last_direction[64]; + char const * m_generic_command_names[256]; + char const ** m_specific_command_tables[64]; + void device_trace_init(); + void device_trace_teardown(); + void device_trace_enable_all(); + void device_trace_disable(int device); + + void* system_trace_context; +}; + +#if ENABLE_TRACE_ALL_DEVICES +// Device tracing +static const char * DeviceNames[] = { + nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,"??", // 0x00-0x07 + "??","HDD",nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, // 0x08-0x0F + nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, // 0x10-0x17 + nullptr,nullptr,nullptr,nullptr,"SMIOCb","SMIOCb?","SMIOCb?","SMIOCb?", // 0x18-0x1F - Unclear how wide the range is for the second SMIOC range. + nullptr,nullptr,nullptr,nullptr,"SMIOCa","SMIOCa?","SMIOCa","SMIOCa", // 0x20-0x27 - 0x25 SMIOC we have not observed being used yet. + "??",nullptr,nullptr,nullptr,"FDC",nullptr,nullptr,nullptr, // 0x28-0x2F + "??",nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, // 0x30-0x37 + "SMIOCc?",nullptr,"SMIOCd?",nullptr,nullptr,nullptr,nullptr,"??" // 0x38-0x3F +}; +struct GenericCommandRecord { + u32 AddressPattern; + const char * Name; +}; + +static const GenericCommandRecord GenericCommands[] = { + { 0x200, "Clear Status" }, + { 0x800, "Status" }, + { 0x1000, "Word Count / Result" }, + { 0x2800, "DMA Busy" }, + { 0x3000, "Command Result" }, + { 0x4000, "Command" }, // Also a write length. + //{ 0x8000, "various" }, // Read address, + //{ 0xC000, "various" }, // Write address, Read length +}; + +static const GenericCommandRecord SpecificCommands[] = { + { 0x5FF00198, "Special/reinit?" }, + { 0x5FF08098, "Command Parameter" }, + { 0x5FF0C098, "DMA Transmit Address" }, + { 0x5FF0409C, "DMA Transmit Length" }, + { 0x5FF0809C, "DMA Receive Address" }, + { 0x5FF0C09C, "DMA Receive Length" }, + { 0x5FF004B0, "Reset PDC" }, + { 0x5FF010B0, "Clear result?" }, + { 0x5FF041B0, "Old style Command" }, + { 0x5FF080B0, "SCSI Command Address" }, + { 0x5FF0C1B0, "DMA Receive Address" }, }; -void r9751_state::kbd_put(u8 data) + +void r9751_state::device_trace_init() +{ + for (int i = 0; i < 64; i++) + { + m_trace_last_address[i] = -1; + m_trace_last_data[i] = -1; + m_trace_repeat_count[i] = 0; + m_trace_last_direction[i] = nullptr; + m_specific_command_tables[i] = nullptr; + } + for (int i = 0; i < 256; i++) + { + m_generic_command_names[i] = "?"; + } + + for (int i = 0; i < sizeof(GenericCommands) / sizeof(GenericCommands[0]); i++) + { + int index = (GenericCommands[i].AddressPattern & 0xFF00) >> 8; + m_generic_command_names[index] = GenericCommands[i].Name; + } + + for (int i = 0; i < sizeof(SpecificCommands) / sizeof(SpecificCommands[0]); i++) + { + int deviceIndex = (SpecificCommands[i].AddressPattern & 0xFC) >> 2; + char const ** deviceTable = m_specific_command_tables[deviceIndex]; + if (deviceTable == nullptr) + { + deviceTable = new char const *[256]; + memset(deviceTable, 0, sizeof(*deviceTable) * 256); + m_specific_command_tables[deviceIndex] = deviceTable; + } + + int index = (SpecificCommands[i].AddressPattern & 0xFF00) >> 8; + + deviceTable[index] = SpecificCommands[i].Name; + } +} +void r9751_state::device_trace_teardown() +{ + for (int i = 0; i < 64; i++) + { + delete[] m_specific_command_tables[i]; + } +} + +void r9751_state::trace_device(int address, int data, const char* direction) +{ + int dev = (address & 0xFF) / 4; + char const * devName = "?"; + char const * regName = "?"; + + if (!(m_device_trace_enable[(dev >> 5)] & (1 << (dev & 31)))) + { + // Device is not enabled for tracing. + return; + } + + if ((address == m_trace_last_address[dev]) && (data == m_trace_last_data[dev]) && (direction == m_trace_last_direction[dev])) + { + m_trace_repeat_count[dev]++; + return; + } + // Compute Device and Register name + if (DeviceNames[dev] != nullptr) + { + devName = DeviceNames[dev]; + } + + if (m_trace_repeat_count[dev] > 0) + { + regName = m_generic_command_names[(m_trace_last_address[dev] & 0xFF00) >> 8]; + logerror("Previous access on Device 0x%x (%s) to [%08x] (%s) repeated %d times\n", dev, devName, m_trace_last_address[dev], regName, m_trace_repeat_count[dev]); + } + m_trace_repeat_count[dev] = 0; + m_trace_last_address[dev] = address; + m_trace_last_data[dev] = data; + m_trace_last_direction[dev] = direction; + + regName = m_generic_command_names[(address & 0xFF00) >> 8]; + if (m_specific_command_tables[dev] != nullptr) + { + int index = (address & 0xFF00) >> 8; + if (m_specific_command_tables[dev][index] != nullptr) + { + regName = m_specific_command_tables[dev][index]; + } + } + + + u32 stacktrace[4]; + u32 basepointer[2]; + u32 reg_a6 = ptr_m68000->state_int(M68K_A6); + + for (int i = 0; i<4; i++) + stacktrace[i] = 0; + for (int i = 0; i<2; i++) + basepointer[i] = 0; + + stacktrace[0] = m_maincpu->pc(); + if (reg_a6 + 4 < 0xFFFFFF) stacktrace[1] = m_maincpu->space(AS_PROGRAM).read_dword(reg_a6 + 4); + if (reg_a6 < 0xFFFFFF && reg_a6 != 0) basepointer[0] = m_maincpu->space(AS_PROGRAM).read_dword(reg_a6); + if (basepointer[0] + 4 < 0xFFFFFF && basepointer[0] != 0) stacktrace[2] = m_maincpu->space(AS_PROGRAM).read_dword(basepointer[0] + 4); + if (basepointer[0] < 0xFFFFFF && basepointer[0] != 0) basepointer[1] = m_maincpu->space(AS_PROGRAM).read_dword(basepointer[0]); + if (basepointer[1] + 4 < 0xFFFFFF && basepointer[1] != 0) stacktrace[3] = m_maincpu->space(AS_PROGRAM).read_dword(basepointer[1] + 4); + + logerror("%s Device 0x%x (%s) Register [%08X] (%s) %s %08X (%08X, %08X, %08X, %08X)\n", machine().time().as_string(), dev, devName, address, regName, direction, data, stacktrace[0], stacktrace[1], stacktrace[2], stacktrace[3]); + +} + +#else +void r9751_state::device_trace_init() {} +void r9751_state::device_trace_teardown() {} +void r9751_state::trace_device(int address, int data, const char* direction) {} +#endif + +void r9751_state::device_trace_enable_all() +{ + for (int i = 0; i < sizeof(m_device_trace_enable) / sizeof(m_device_trace_enable[0]); i++) + { + m_device_trace_enable[i] = 0xFFFFFFFF; + } +} +void r9751_state::device_trace_disable(int device) +{ + device = device & 0x3F; + m_device_trace_enable[device >> 5] &= ~(1 << (device & 0x1F)); +} + + +// System register tracing +struct SystemRegisterInfo { + int VerboseLevel; + u32 RegisterAddress; + const char * RegisterName; + const char * Description; +}; + +static const SystemRegisterInfo SystemRegisters[] = { + { 5, 0xff010000, "DB00" },{ 5, 0xff010004, "DB01" },{ 5, 0xff010008, "DB02" },{ 5, 0xff01000c, "DB03" }, + { 5, 0xff010010, "DB04" },{ 5, 0xff010014, "DB05" },{ 5, 0xff010018, "DB06" },{ 5, 0xff01001c, "DB07" }, + { 5, 0xff010020, "DB08" },{ 5, 0xff010024, "DB09" },{ 5, 0xff010028, "DB10" },{ 5, 0xff01002c, "DB11" }, + { 5, 0xff010040, "DBVR" },{ 5, 0xff010044, "DTAR" },{ 5, 0xff010048, "DESR" },{ 5, 0xff01004c, "DVR" }, + { 5, 0xff010050, "DRR" },{ 5, 0xff010054, "DDR" },{ 5, 0xff010060, "DMR" },{ 5, 0xff010064, "RCR" }, + { 5, 0xff010068, "RESR" },{ 5, 0xff01006c, "RBSR" },{ 5, 0xff010070, "RBDR" },{ 5, 0xff010074, "RBAR" }, + { 5, 0xff010078, "RIOB" },{ 5, 0xff01007c, "RPSR" },{ 5, 0xff010080, "PDR" }, + + { 5, 0xff020004, "ARSP" },{ 5, 0xff020008, "MEXC" },{ 5, 0xff02000c, "MADD" },{ 5, 0xff020010, "MDAT" }, + { 5, 0xff020014, "ADIA" },{ 5, 0xff020018, "ECCD" },{ 5, 0xff02001c, "VPDS" },{ 5, 0xff020020, "HBRK" }, + { 5, 0xff020024, "LBRK" },{ 5, 0xff020028, "REFS" },{ 5, 0xff02002c, "RCNT" },{ 5, 0xff020030, "RTMR" }, + + { 5, 0xff050004, "ROSR" },{ 5, 0xff050008, "BIVR" },{ 5, 0xff05000c, "RDDR", "LED - RAS Digit Display Register" }, + { 5, 0xff050104, "AMR" }, + { 5, 0xff050108, "MIVR" },{ 5, 0xff050200, "APCR" },{ 5, 0xff050204, "PESR" },{ 5, 0xff050208, "PEAR" }, + { 5, 0xff05020c, "PEDR" },{ 5, 0xff050210, "PEST" },{ 5, 0xff050300, "RCID" }, + { 2, 0xff050310, "APLT" }, // APLT is accessed heavily in background/timing processes. + { 2, 0xff050320, "WDT" }, // WDT use is particularly noisy + { 5, 0xff050400, "APT" },{ 5, 0xff050404, "AIVR" },{ 5, 0xff050408, "ASSR" }, + { 5, 0xff050410, "STRU" },{ 5, 0xff050414, "STRL" }, + { 5, 0xff050500, "EV00" },{ 5, 0xff050504, "EV01" },{ 5, 0xff050508, "EV02" },{ 5, 0xff05050c, "EV03" }, + { 5, 0xff050510, "EV04" },{ 5, 0xff050514, "EV05" },{ 5, 0xff050518, "EV06" },{ 5, 0xff05051c, "EV07" }, + { 5, 0xff050520, "EV08" },{ 5, 0xff050524, "EV09" },{ 5, 0xff050528, "EV10" },{ 5, 0xff05052c, "EV11" }, + { 5, 0xff050530, "EV12" },{ 5, 0xff050534, "EV13" },{ 5, 0xff050538, "EV14" },{ 5, 0xff05053c, "EV15" }, + { 5, 0xff050540, "EV16" },{ 5, 0xff050544, "EV17" },{ 5, 0xff050548, "EV18" },{ 5, 0xff05054c, "EV19" }, + { 5, 0xff050550, "EV20" },{ 5, 0xff050554, "EV21" },{ 5, 0xff050558, "EV22" },{ 5, 0xff05055c, "EV23" }, + { 5, 0xff050560, "EV24" },{ 5, 0xff050564, "EV25" }, + { 5, 0xff050580, "PISR" }, + { 2, 0xff050584, "PIMR" }, // PIMR use in the bios is noisy + { 5, 0xff050600, "CDMR" },{ 5, 0xff050604, "CDER" }, + { 5, 0xff050610, "CIV0" },{ 5, 0xff050614, "CIV1" },{ 5, 0xff050618, "CIV2" },{ 5, 0xff05061c, "CIV3" }, + { 5, 0xff050620, "CIV4" },{ 5, 0xff050624, "CIV5" },{ 5, 0xff050628, "CIV6" },{ 5, 0xff05062c, "CIV7" }, + { 5, 0xff050630, "CCV0" },{ 5, 0xff050634, "CCV1" },{ 5, 0xff050638, "CCV2" },{ 5, 0xff05063c, "CCV3" }, + { 5, 0xff050640, "CCV4" },{ 5, 0xff050644, "CCV5" },{ 5, 0xff050648, "CCV6" },{ 5, 0xff05064c, "CCV7" }, + + { 5, 0xff060014, "SCAS" },{ 5, 0xff060018, "SCRG" },{ 5, 0xff060020, "SDER" },{ 5, 0xff060024, "SDCR" }, + { 5, 0xff060028, "SESR" },{ 5, 0xff06002c, "SBSR" },{ 5, 0xff060030, "SBDR" },{ 5, 0xff060034, "SBAR" }, + { 5, 0xff060038, "SDAR" },{ 5, 0xff06003c, "SINT" },{ 5, 0xff060040, "SDXC" },{ 5, 0xff060044, "SOSR" }, + { 5, 0xff060048, "SISR" },{ 5, 0xff06004c, "SIDR" },{ 5, 0xff060050, "SDSD" }, + +}; + + +static const int MaxSystemTables = 256; // FF00xxxx through FFFFxxxx +static const int MaxSystemRegisters = 0x400; // FF0x0000 through FF0x0FFC +void r9751_state::system_trace_init() +{ + const SystemRegisterInfo*** trace_context; + + // Allocate tables for FF00 through FFFF + trace_context = new const SystemRegisterInfo**[MaxSystemTables]; + memset(trace_context, 0, sizeof(void*)*MaxSystemTables); + + // Iterate over the system register info + for (int i = 0; i < (sizeof(SystemRegisters) / sizeof(*SystemRegisters)); i++) + { + const SystemRegisterInfo* current = SystemRegisters + i; + + int table = (current->RegisterAddress & 0x00FF0000) >> 16; + int regindex = (current->RegisterAddress & 0xFFFC) >> 2; + + if (table >= MaxSystemTables || regindex >= MaxSystemRegisters) + { + logerror("system_trace_init: Register out of range and cannot be traced: 0x%x %s\n", current->RegisterAddress, current->RegisterName); + continue; + } + + // Lookup the first tier table and create it if it doesn't exist + const SystemRegisterInfo** firstTier = trace_context[table]; + if (firstTier == nullptr) + { + firstTier = new const SystemRegisterInfo*[MaxSystemRegisters]; + memset(firstTier, 0, sizeof(void*) * MaxSystemRegisters); + trace_context[table] = firstTier; + } + + // Set the content in the table + firstTier[regindex] = current; + + } + + system_trace_context = trace_context; +} + +void r9751_state::system_trace_teardown() +{ + for (int i = 0; i < MaxSystemTables; i++) + { + delete[] ((const SystemRegisterInfo***)system_trace_context)[i]; + } + delete[] (const SystemRegisterInfo***)system_trace_context; +} + +#if ENABLE_TRACE_SYSTEM +void r9751_state::trace_system(int table, int offset, int data, const char* direction) { - if(serial_input) + const SystemRegisterInfo*** trace_context = (const SystemRegisterInfo***)system_trace_context; + + const char* regName = "?"; + const char* regDescription = "?"; + int level = 10; // Undefined registers are level 10 + + if (table < MaxSystemTables && offset < MaxSystemRegisters) { - m_maincpu->space(AS_PROGRAM).write_byte(smioc_in_addr,data); - serial_status_queue.push(0x5440); - serial_input = false; + const SystemRegisterInfo** firstTier = trace_context[table]; + if (firstTier != nullptr) + { + const SystemRegisterInfo* current = firstTier[offset]; + if (current != nullptr) + { + regName = current->RegisterName; + if (current->Description != nullptr) regDescription = current->Description; + level = current->VerboseLevel; + } + } } - else + + if (level >= SYSTEM_TRACE_LEVEL) { - kbd_queue.push(data); + u32 address = 0xFF000000 | (table << 16) | (offset << 2); + logerror("%s System Register [%08X] (%s %s) %s %08X (PC=%08X)\n", machine().time().as_string(), address, regName, regDescription, direction, data, m_maincpu->pc()); } + +} +#else +void r9751_state::trace_system(int table, int offset, int data, const char* direction) +{ } +#endif + uint32_t r9751_state::swap_uint32( uint32_t val ) { @@ -215,17 +525,38 @@ void r9751_state::UnifiedTrace(u32 address, u32 data, const char* operation, con READ8_MEMBER(r9751_state::pdc_dma_r) { /* This callback function takes the value written to 0xFF01000C as the bank offset */ - uint32_t address = (fdd_dma_bank & 0x7FFFF800) + (offset&0x3FFFF); - if(TRACE_DMA) logerror("DMA READ: %08X DATA: %08X\n", address, m_maincpu->space(AS_PROGRAM).read_byte(address)); + uint32_t address = (fdd_dma_bank & 0x7FFFF800) + (offset & 0x3FFFF); + if (TRACE_DMA) logerror("DMA READ: %08X DATA: %08X\n", address, m_maincpu->space(AS_PROGRAM).read_byte(address)); return m_maincpu->space(AS_PROGRAM).read_byte(address); } WRITE8_MEMBER(r9751_state::pdc_dma_w) { /* This callback function takes the value written to 0xFF01000C as the bank offset */ - uint32_t address = (fdd_dma_bank & 0x7FFFF800) + (m_pdc->fdd_68k_dma_address&0x3FFFF); - m_maincpu->space(AS_PROGRAM).write_byte(address,data); - if(TRACE_DMA) logerror("DMA WRITE: %08X DATA: %08X\n", address,data); + uint32_t address = (fdd_dma_bank & 0x7FFFF800) + (m_pdc->fdd_68k_dma_address & 0x3FFFF); + m_maincpu->space(AS_PROGRAM).write_byte(address, data); + if (TRACE_DMA) logerror("DMA WRITE: %08X DATA: %08X\n", address, data); +} + +READ8_MEMBER(r9751_state::smioc_dma_r) +{ + /* This callback function takes the value written to 0xFF01000C as the bank offset */ + uint32_t address = (smioc_dma_bank & 0x7FFFF800) + (offset*2 & 0x3FFFF); + u16 word = m_maincpu->space(AS_PROGRAM).read_word(address); + char c = word & 0xFF; + if (c < 32 || c > 127) c = ' '; + if (TRACE_DMA) logerror("%s SMIOC DMA READ: %08X DATA: %04X (%c)\n", machine().time().as_string(), address, word, c); + return m_maincpu->space(AS_PROGRAM).read_word(address) & 0x00FF; +} + +WRITE8_MEMBER(r9751_state::smioc_dma_w) +{ + /* This callback function takes the value written to 0xFF01000C as the bank offset */ + uint32_t address = (smioc_dma_bank & 0x7FFFF800) + (offset*2 & 0x3FFFF); + m_maincpu->space(AS_PROGRAM).write_word(address, data); + char c = data & 0xFF; + if (c < 32 || c > 127) c = ' '; + if (TRACE_DMA) logerror("%s SMIOC DMA WRITE: %08X DATA: %08X (%c)\n", machine().time().as_string(), address, data, c); } void r9751_state::init_r9751() @@ -235,20 +566,21 @@ void r9751_state::init_r9751() fdd_dest_address = 0; fdd_cmd_complete = 0; fdd_dma_bank = 0; - smioc_out_addr = 0; smioc_dma_bank = 0; - smioc_dma_w_length = 0; m_mem = &m_maincpu->space(AS_PROGRAM); m_maincpu->interface(ptr_m68000); + device_trace_enable_all(); + device_trace_disable(0x07); + device_trace_disable(0x09); + /* Save states */ save_item(NAME(reg_ff050004)); save_item(NAME(reg_fff80040)); save_item(NAME(fdd_dest_address)); save_item(NAME(fdd_cmd_complete)); - save_item(NAME(smioc_out_addr)); save_item(NAME(smioc_dma_bank)); save_item(NAME(fdd_dma_bank)); save_item(NAME(timer_32khz_last)); @@ -272,66 +604,74 @@ void r9751_state::machine_reset() READ32_MEMBER( r9751_state::r9751_mmio_5ff_r ) { uint32_t data; - - if(TRACE_DEVICE) - if(((offset << 2) & 0xFF) == TRACE_DEVICE * 4) - logerror("(!) Device Read: 0x%02X - PC: %08X Register: %08X\n", TRACE_DEVICE, m_maincpu->pc(), offset << 2 | 0x5FF00000); - switch(offset << 2) { /* PDC HDD region (0x24, device 9) */ case 0x0824: /* HDD Command result code */ - return 0x10; + data = 0x10; + break; + case 0x3024: /* HDD SCSI command completed successfully */ data = 0x1; if(TRACE_HDC) logerror("SCSI HDD command completion status - Read: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000); - return data; - /* SMIOC region (0x98, device 26) */ + break; + + /* SMIOC region (0x98, device 0x26) */ case 0x0898: /* Serial status or DMA status */ - if(!serial_status_queue.empty()) - { - data = serial_status_queue.front(); - serial_status_queue.pop(); - } - else - { - data = 0; - } - if(TRACE_SMIOC) logerror("serial_status_queue = %04X \n", data | 0x8); - TRACE_SMIOC_READ(offset << 2 | 0x5FF00000, data | 0x8, "Serial Status 1", nullptr); - return data | 0x8; - case 0x0870: /* Serial status or DMA status 2 */ - if(TRACE_SMIOC) logerror("m_serial_status2 = %04X \n", m_serial_status2); - TRACE_SMIOC_READ(offset << 2 | 0x5FF00000, m_serial_status2, "Serial Status 2", nullptr); - return m_serial_status2; - /* SMIOC region (0x9C, device 27) */ + data = m_smioc->GetStatus(); + break; + + case 0x0870: /* Serial status or DMA status 2 (0x70, device 0x24) */ + data = m_smioc->GetStatus2(); + break; + /* SMIOC region (0x9C, device 0x27) */ + + case 0x1098: /* Serial word count register */ + data = m_smioc->m_wordcount; + m_smioc->ClearParameter(); + break; + + case 0x1070: /* Serial word count register (alternate) */ + data = m_smioc->m_wordcount2; + m_smioc->ClearParameter2(); + break; + + case 0x2898: /* SMIOC DMA Busy register - nonzero = busy */ + data = m_smioc->m_deviceBusy; + break; /* PDC FDD region (0xB0, device 44 */ case 0x08B0: /* FDD Command result code */ - return 0x10; + data = 0x10; + break; + case 0x10B0: /* Clear 5FF030B0 ?? */ if(TRACE_FDC) logerror("--- FDD 0x5FF010B0 READ (0)\n"); - return 0; + data = 0; + break; + + case 0x1890: /* Device offset 0x90 (Bit 7 needs to be set) */ + data = 0x80; + break; + case 0x30B0: /* FDD command completion status */ data = (m_pdc->reg_p5 << 8) + m_pdc->reg_p4; if(TRACE_FDC && data != 0) logerror("--- SCSI FDD command completion status - Read: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000); - return data; + break; + default: - if(TRACE_FDC || TRACE_HDC || TRACE_SMIOC) logerror("Unknown read address: %08X PC: %08X\n", offset << 2 | 0x5FF00000, m_maincpu->pc()); - if(((offset << 2) & 0xFF) == 0x26 * 4 || ((offset << 2) & 0xFF) == 0x27 * 4) TRACE_SMIOC_READ(offset << 2 | 0x5FF00000, 0, "Unknown", nullptr); - return 0; + data = 0; + break; } + trace_device(offset << 2 | 0x5FF00000, data, ">>"); + return data; } WRITE32_MEMBER( r9751_state::r9751_mmio_5ff_w ) { uint8_t data_b0, data_b1; - //logerror("(!!) 0x5ff Register Write - PC: %08X Register: %08X Data: %08X\n", m_maincpu->pc(), offset << 2 | 0x5FF00000, data); - - if(TRACE_DEVICE) - if(((offset << 2) & 0xFF) == TRACE_DEVICE * 4) - logerror("(!) Device Write: 0x%02X - PC: %08X Register: %08X Data: %08X\n", TRACE_DEVICE, m_maincpu->pc(), offset << 2 | 0x5FF00000, data); + trace_device(offset << 2 | 0x5FF00000, data, "<<"); /* Unknown mask */ if (mem_mask != 0xFFFFFFFF) @@ -339,103 +679,61 @@ WRITE32_MEMBER( r9751_state::r9751_mmio_5ff_w ) switch(offset << 2) { - /* PDC HDD region (0x24, device 9 */ - case 0x0224: /* HDD SCSI read command */ - if(TRACE_HDC) logerror("@@@ HDD Command: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000); - break; - case 0x8024: /* HDD SCSI read command */ - if(TRACE_HDC) logerror("@@@ HDD Command: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000); - break; - case 0xC024: /* HDD SCSI read command */ - if(TRACE_HDC) logerror("@@@ HDD Command: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000); - break; + /* PDC HDD region (0x24, device 9) */ + /* SMIOC region (0x98, device 26) - Output */ case 0x0298: - if(!serial_status_queue.empty()) - serial_status_queue.pop(); - if(TRACE_SMIOC) logerror("Serial status: %08X PC: %08X\n", data, m_maincpu->pc()); - TRACE_SMIOC_WRITE(offset << 2 | 0x5FF00000, data, "Serial Status 1", nullptr); + m_smioc->ClearStatus(); break; case 0x0270: - m_serial_status2 = data; - if(TRACE_SMIOC) logerror("Serial status2: %08X PC: %08X\n", data, m_maincpu->pc()); - TRACE_SMIOC_WRITE(offset << 2 | 0x5FF00000, data, "Serial Status 2", nullptr); + m_smioc->ClearStatus2(); break; - case 0x4090: + case 0x4070: // SMIOC upper port command (ports 4-7) + m_smioc->SendCommand2(data); + break; + case 0x8070: // SMIOC upper port parameter (ports 4-7) + m_smioc->SetCommandParameter2(data); + break; + + case 0x0198: // SMIOC soft reset? + // It's not clear what exactly this register write does. + // It isn't a soft reset of the SMIOC because the 68k does not wait long enough for the SMIOC to finish rebooting. + // Also the 68k does this twice in succession, with a status clear in between. + // The theory now is that a write to this register causes the status registers on the SMIOC to be + // set to the magic value 0x0100 (which is set after initialization is complete) - which serves as a trigger + // for the disktool software to reinitialize the SMIOC and proceed into a working state. + // This probably isn't correct, but hopefully we will determine the correct approach in the future. + + m_smioc->m_status = 0x0140; + m_smioc->m_status2 = 0x0140; + break; + case 0x4098: /* Serial DMA Command */ - switch(data) - { - case 0x1000: - serial_status_queue.push(0x0140); - m_serial_status2 = 0x0140; - if(TRACE_SMIOC) logerror("Serial DMA command 0x1000 PC: %08X\n", m_maincpu->pc()); - break; - case 0x4100: /* Send byte to serial */ - for(int i = 0; i < smioc_dma_w_length; i++) - { - if(TRACE_SMIOC) logerror("Serial byte: %02X PC: %08X\n", m_mem->read_word(smioc_out_addr+i*2), m_maincpu->pc()); - m_terminal->write(space,0,m_mem->read_word(smioc_out_addr+i*2)); - } - serial_status_queue.push(0x5140); - break; - case 0x4200: /* Serial input */ - if(kbd_queue.empty()) - { - serial_input = true; - break; - } - - for(int i = 0; i < smioc_dma_r_length && !kbd_queue.empty(); i++) - { - m_maincpu->space(AS_PROGRAM).write_byte(smioc_in_addr+i,kbd_queue.front()); - kbd_queue.pop(); - } - - serial_status_queue.push(0xE440); - break; - default: - if(TRACE_SMIOC) logerror("Unknown serial DMA command: %X\n", data); - serial_status_queue.push(0x40); - } - TRACE_SMIOC_WRITE(offset << 2 | 0x5FF00000, data, "Serial Command", nullptr); + m_smioc->SendCommand(data); + break; + case 0x8098: /* Command Parameter */ + m_smioc->SetCommandParameter(data); break; - case 0xC098: /* Serial DMA output address */ - smioc_out_addr = (smioc_dma_bank & 0x7FFFF800) | ((data&0x3FF)<<1); - if(TRACE_SMIOC) logerror("Serial output address: %08X PC: %08X\n", smioc_out_addr, m_maincpu->pc()); - TRACE_SMIOC_WRITE(offset << 2 | 0x5FF00000, data, "Serial Out Address", nullptr); + case 0xC098: /* Serial DMA Transmit data address */ + m_smioc->SetDmaParameter(smiocdma_sendaddress, data); break; + /* SMIOC region (0x9C, device 27) - Input */ case 0x409C: /* Serial DMA write length */ - smioc_dma_w_length = (~data+1) & 0xFFFF; - if(TRACE_SMIOC) logerror("Serial DMA write length: %08X PC: %08X\n", smioc_dma_w_length, m_maincpu->pc()); - if(smioc_dma_w_length > 0x400) smioc_dma_w_length = 0x400; - TRACE_SMIOC_WRITE(offset << 2 | 0x5FF00000, data, "Serial Write Length", nullptr); + m_smioc->SetDmaParameter(smiocdma_sendlength, data); break; - case 0x809C: /* Serial DMA input address */ - smioc_in_addr = (smioc_dma_bank & 0x7FFFF800) | ((data&0x3FF)<<1); - if(TRACE_SMIOC) logerror("Serial input address: %08X PC: %08X\n", smioc_out_addr, m_maincpu->pc()); - TRACE_SMIOC_WRITE(offset << 2 | 0x5FF00000, data, "Serial In Address", nullptr); + case 0x809C: /* Serial DMA Receive data address */ + m_smioc->SetDmaParameter(smiocdma_recvaddress, data); break; case 0xC09C: /* Serial DMA read length */ - smioc_dma_r_length = (~data+1) & 0xFFFF; - if(TRACE_SMIOC) logerror("Serial DMA read length: %08X PC: %08X\n", smioc_dma_r_length, m_maincpu->pc()); - if(smioc_dma_r_length > 0x400) smioc_dma_r_length = 0x400; - TRACE_SMIOC_WRITE(offset << 2 | 0x5FF00000, data, "Serial Read Length", nullptr); - break; - /* PDC FDD region (0xB0, device 44 */ - case 0x01B0: /* FDD SCSI read command */ - if(TRACE_FDC) logerror("--- FDD Command: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000); - break; - case 0x02B0: /* FDD SCSI read command */ - if(TRACE_FDC) logerror("--- FDD Command: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000); + m_smioc->SetDmaParameter(smiocdma_recvlength, data); break; + + /* PDC FDD region (0xB0, device 44) */ case 0x04B0: /* FDD RESET PDC */ if(TRACE_FDC) logerror("PDC RESET, PC: %08X DATA: %08X\n", m_maincpu->pc(), data); m_pdc->reset(); break; - case 0x08B0: /* FDD SCSI read command */ - if(TRACE_FDC) logerror("--- FDD Command: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000); - break; case 0x41B0: /* Unknown - Probably old style commands */ if(TRACE_FDC) logerror("--- FDD Command: %08X, From: %08X, Register: %08X\n", data, m_maincpu->pc(), offset << 2 | 0x5FF00000); @@ -498,8 +796,7 @@ WRITE32_MEMBER( r9751_state::r9751_mmio_5ff_w ) break; default: - if(TRACE_FDC || TRACE_HDC || TRACE_SMIOC) logerror("Unknown write address: %08X Data: %08X PC: %08X\n", offset << 2 | 0x5FF00000, data, m_maincpu->pc()); - if(((offset << 2) & 0xFF) == 0x26 * 4 || ((offset << 2) & 0xFF) == 0x27 * 4) TRACE_SMIOC_WRITE(offset << 2 | 0x5FF00000, data, "Unknown", nullptr); + break; } } @@ -508,11 +805,14 @@ WRITE32_MEMBER( r9751_state::r9751_mmio_5ff_w ) ******************************************************************************/ READ32_MEMBER( r9751_state::r9751_mmio_ff01_r ) { + u32 data; switch(offset << 2) { default: - return 0; + data = 0; } + trace_system(1, offset, data, ">>"); + return data; } WRITE32_MEMBER( r9751_state::r9751_mmio_ff01_w ) @@ -521,6 +821,8 @@ WRITE32_MEMBER( r9751_state::r9751_mmio_ff01_w ) if (mem_mask != 0xFFFFFFFF) logerror("Mask found: %08X Register: %08X PC: %08X\n", mem_mask, offset << 2 | 0xFF010000, m_maincpu->pc()); + trace_system(1, offset, data, "<<"); + switch(offset << 2) { case 0x000C: /* FDD DMA Offset */ @@ -544,22 +846,29 @@ READ32_MEMBER( r9751_state::r9751_mmio_ff05_r ) switch(offset << 2) { case 0x0004: - return reg_ff050004; + data = reg_ff050004; + break; case 0x0300: - return 0x1B | (1<<0x14); + data = 0x1B | (1<<0x14); + break; case 0x0320: /* Some type of counter */ - return (machine().time() - timer_32khz_last).as_ticks(32768) & 0xFFFF; + data = (machine().time() - timer_32khz_last).as_ticks(32768) & 0xFFFF; + break; case 0x0584: - return 0; + data = 0; + break; case 0x0610: - return 0xabacabac; + data = 0xabacabac; + break; case 0x0014: - return 0x80; + data = 0x80; + break; default: data = 0; - if(TRACE_CPU_REG) logerror("Instruction: %08x READ MMIO(%08x): %08x & %08x\n", m_maincpu->pc(), offset << 2 | 0xFF050000, data, mem_mask); - return data; + break; } + trace_system(5, offset, data, "<<"); + return data; } WRITE32_MEMBER( r9751_state::r9751_mmio_ff05_w ) @@ -568,6 +877,8 @@ WRITE32_MEMBER( r9751_state::r9751_mmio_ff05_w ) if (mem_mask != 0xFFFFFFFF) logerror("Mask found: %08X Register: %08X PC: %08X\n", mem_mask, offset << 2 | 0xFF050000, m_maincpu->pc()); + trace_system(5, offset, data, ">>"); + switch(offset << 2) { case 0x0004: @@ -580,7 +891,6 @@ WRITE32_MEMBER( r9751_state::r9751_mmio_ff05_w ) timer_32khz_last = machine().time(); return; default: - if(TRACE_CPU_REG) logerror("Instruction: %08x WRITE MMIO(%08x): %08x & %08x\n", m_maincpu->pc(), offset << 2 | 0xFF050000, data, mem_mask); return; } } @@ -592,12 +902,14 @@ READ32_MEMBER( r9751_state::r9751_mmio_fff8_r ) switch(offset << 2) { case 0x0040: - return reg_fff80040; + data = reg_fff80040; + break; default: data = 0; - if(TRACE_CPU_REG) logerror("Instruction: %08x READ MMIO(%08x): %08x & %08x\n", m_maincpu->pc(), offset << 2 | 0xFFF80000, data, mem_mask); - return data; + break; } + trace_system(5, offset, data, "<<"); + return data; } WRITE32_MEMBER( r9751_state::r9751_mmio_fff8_w ) @@ -606,13 +918,15 @@ WRITE32_MEMBER( r9751_state::r9751_mmio_fff8_w ) if (mem_mask != 0xFFFFFFFF) logerror("Mask found: %08X Register: %08X PC: %08X\n", mem_mask, offset << 2 | 0xFFF80000, m_maincpu->pc()); + trace_system(5, offset, data, ">>"); + switch(offset << 2) { case 0x0040: reg_fff80040 = data; return; default: - if(TRACE_CPU_REG) logerror("Instruction: %08x WRITE MMIO(%08x): %08x & %08x\n", m_maincpu->pc(), offset << 2 | 0xFFF80000, data, mem_mask); + return; } } @@ -646,14 +960,12 @@ MACHINE_CONFIG_START(r9751_state::r9751) /* basic machine hardware */ MCFG_DEVICE_ADD("maincpu", M68030, 20000000) MCFG_DEVICE_PROGRAM_MAP(r9751_mem) - MCFG_QUANTUM_TIME(attotime::from_hz(60)) - - /* video hardware */ - MCFG_DEVICE_ADD(m_terminal, GENERIC_TERMINAL, 0) - MCFG_GENERIC_TERMINAL_KEYBOARD_CB(PUT(r9751_state, kbd_put)) + MCFG_QUANTUM_TIME(attotime::from_hz(1000)) /* i/o hardware */ MCFG_DEVICE_ADD("smioc", SMIOC, 0) + MCFG_SMIOC_R_CB(READ8(*this, r9751_state, smioc_dma_r)) + MCFG_SMIOC_W_CB(WRITE8(*this, r9751_state, smioc_dma_w)) /* disk hardware */ PDC(config, m_pdc, 0); -- cgit v1.2.3