From 807bdf0a062c19b81bbdbf70161e7b3b00e9c503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Larsson=20Edstr=C3=B6m?= Date: Sun, 30 Aug 2015 14:04:51 +0200 Subject: [PATCH 01/12] Added driver for Heurikon HK68K/V10 VME board Added some SCC support in the z80dart driver --- scripts/target/mame/mess.lua | 6 + src/devices/machine/z80dart.c | 168 +++++++++++++-- src/devices/machine/z80dart.h | 84 +++++++- src/mame/mess.lst | 1 + src/mess/drivers/hk68v10.c | 371 ++++++++++++++++++++++++++++++++++ 5 files changed, 613 insertions(+), 17 deletions(-) create mode 100644 src/mess/drivers/hk68v10.c diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index b25c03b8f3e..0d81b3892d2 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -736,6 +736,7 @@ function linkProjects_mame_mess(_target, _subtarget) "heathkit", "hec2hrp", "hegener", + "heurikon", "hitachi", "homebrew", "homelab", @@ -1635,6 +1636,11 @@ files { MAME_DIR .. "src/mess/drivers/interact.c", } +createMESSProjects(_target, _subtarget, "heurikon") +files { + MAME_DIR .. "src/mess/drivers/hk68v10.c", +} + createMESSProjects(_target, _subtarget, "intel") files { MAME_DIR .. "src/mess/drivers/basic52.c", diff --git a/src/devices/machine/z80dart.c b/src/devices/machine/z80dart.c index 6254d1d81f1..5a3b5439247 100644 --- a/src/devices/machine/z80dart.c +++ b/src/devices/machine/z80dart.c @@ -6,6 +6,7 @@ NEC uPD7201 Multiprotocol Serial Communications Controller emulation Z80-DART Dual Asynchronous Receiver/Transmitter emulation Z80-SIO/0/1/2/3/4 Serial Input/Output Controller emulation + Z80-SCC Serial Communications Controller emulation (experimental) The z80dart/z80sio itself is based on an older intel serial chip, the i8274 MPSC (see http://doc.chipfind.ru/pdf/intel/8274.pdf), which also has almost identical @@ -13,6 +14,10 @@ scheme which uses write register 2 on channel A, that register which is unused on the z80dart and z80sio. + The z80scc is an updated version of the z80sio, with additional support for CRC + checks and a number of data link layer protocols such as HDLC, SDLC and BiSync. + (See https://en.wikipedia.org/wiki/Zilog_SCC). + ***************************************************************************/ /* @@ -25,8 +30,9 @@ - wait/ready - 1.5 stop bits - synchronous mode (Z80-SIO/1,2) - - SDLC mode (Z80-SIO/1,2) - + - SDLC mode (Z80-SIO/1,2/SCC) + - HDLC, BiSync support (Z80-SCC) + - CRC support (Z80-SCC) */ #include "z80dart.h" @@ -60,6 +66,7 @@ const device_type Z80SIO3 = &device_creator; const device_type Z80SIO4 = &device_creator; const device_type I8274 = &device_creator; const device_type UPD7201 = &device_creator; +const device_type Z80SCC = &device_creator; //------------------------------------------------- @@ -181,6 +188,11 @@ upd7201_device::upd7201_device(const machine_config &mconfig, const char *tag, d { } +scc8530_device::scc8530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : z80dart_device(mconfig, Z80SCC, "Z80 SCC", tag, owner, clock, TYPE_Z80SCC, "z80scc", __FILE__) +{ +} + //------------------------------------------------- // device_start - device-specific startup @@ -438,6 +450,10 @@ READ8_MEMBER( z80dart_device::cd_ba_r ) int cd = BIT(offset, 1); z80dart_channel *channel = ba ? m_chanB : m_chanA; + cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC + + // LOG(("z80dart_device::cd_ba_r ba:%02x cd:%02x\n", ba, cd)); + return cd ? channel->control_read() : channel->data_read(); } @@ -452,6 +468,10 @@ WRITE8_MEMBER( z80dart_device::cd_ba_w ) int cd = BIT(offset, 1); z80dart_channel *channel = ba ? m_chanB : m_chanA; + cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC + + // LOG(("z80dart_device::cd_ba_w ba:%02x cd:%02x\n", ba, cd)); + if (cd) channel->control_write(data); else @@ -469,6 +489,10 @@ READ8_MEMBER( z80dart_device::ba_cd_r ) int cd = BIT(offset, 0); z80dart_channel *channel = ba ? m_chanB : m_chanA; + cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC + + // LOG(("z80dart_device::ba_cd_r ba:%02x cd:%02x\n", ba, cd)); + return cd ? channel->control_read() : channel->data_read(); } @@ -483,14 +507,16 @@ WRITE8_MEMBER( z80dart_device::ba_cd_w ) int cd = BIT(offset, 0); z80dart_channel *channel = ba ? m_chanB : m_chanA; + cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC + + LOG(("z80dart_device::ba_cd_w ba:%02x cd:%02x\n", ba, cd)); + if (cd) channel->control_write(data); else channel->data_write(data); } - - //************************************************************************** // DART CHANNEL //************************************************************************** @@ -518,10 +544,10 @@ z80dart_channel::z80dart_channel(const machine_config &mconfig, const char *tag, m_rts(0), m_sync(0) { - for (int i = 0; i < 3; i++) + for (int i = 0; i < sizeof(m_rr); i++) m_rr[i] = 0; - for (int i = 0; i < 6; i++) + for (int i = 0; i < sizeof(m_wr); i++) m_wr[i] = 0; for (int i = 0; i < 3; i++) @@ -540,6 +566,7 @@ void z80dart_channel::device_start() { m_uart = downcast(owner()); m_index = m_uart->get_channel_index(this); + m_ph = 0; // state saving save_item(NAME(m_rr)); @@ -560,6 +587,7 @@ void z80dart_channel::device_start() save_item(NAME(m_dtr)); save_item(NAME(m_rts)); save_item(NAME(m_sync)); + save_item(NAME(m_ph)); device_serial_interface::register_save_state(machine().save(), this); } @@ -787,13 +815,17 @@ int z80dart_channel::get_tx_word_length() UINT8 z80dart_channel::control_read() { UINT8 data = 0; + int reg = m_wr[0]; + int regmask = (WR0_REGISTER_MASK | m_ph); - int reg = m_wr[0] & WR0_REGISTER_MASK; + m_ph = 0; // The "Point High" command is only valid for one access + + reg &= regmask; if (reg != 0) { // mask out register index - m_wr[0] &= ~WR0_REGISTER_MASK; + m_wr[0] &= ~regmask; } switch (reg) @@ -808,9 +840,33 @@ UINT8 z80dart_channel::control_read() if (m_index == z80dart_device::CHANNEL_B) data = m_rr[reg]; break; + /* registers 4-7 are specific to SCC. TODO: Check variant and log/stop misuse */ + case 4: /* (ESCC and 85C30 Only) */ + /*On the ESCC, Read Register 4 reflects the contents of Write Register 4 provided the Extended + Read option is enabled. Otherwise, this register returns an image of RR0. On the NMOS/CMOS version, + a read to this location returns an image of RR0.*/ + case 5: /* (ESCC and 85C30 Only) */ + /*On the ESCC, Read Register 5 reflects the contents of Write Register 5 provided the Extended + Read option is enabled. Otherwise, this register returns an image of RR1. On the NMOS/CMOS version, + a read to this register returns an image of RR1.*/ + data = BIT(m_wr[7], 6) ? m_wr[reg] : m_rr[reg - 4]; + break; + /* registers 8-15 are specific to SCC and misuse captured by test around Point High command in control_write() */ + case 8: + data = data_read(); + break; + case 10: + data = 0; + LOG(("Z80DART Read Register 10 Misc Status Bits, SDLC related, not implemented yet.\n")); + break; + case 13: + data = m_wr[13]; + break; + default: + logerror("Z80DART \"%s\" Channel %c : Unsupported RRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); } - //LOG(("Z80DART \"%s\" Channel %c : Control Register Read '%02x'\n", m_owner->tag(), 'A' + m_index, data)); + //LOG(("Z80DART \"%s\" Channel %c : Register R%d read '%02x'\n", m_owner->tag(), 'A' + m_index, reg, data)); return data; } @@ -822,9 +878,13 @@ UINT8 z80dart_channel::control_read() void z80dart_channel::control_write(UINT8 data) { - int reg = m_wr[0] & WR0_REGISTER_MASK; + int reg = m_wr[0]; + int regmask = (WR0_REGISTER_MASK | m_ph); + + m_ph = 0; // The "Point High" command is only valid for one access + + reg &= regmask; - LOG(("Z80DART \"%s\" Channel %c : Control Register Write '%02x'\n", m_owner->tag(), 'A' + m_index, data)); // write data to selected register if (reg < 6) @@ -833,9 +893,11 @@ void z80dart_channel::control_write(UINT8 data) if (reg != 0) { // mask out register index - m_wr[0] &= ~WR0_REGISTER_MASK; + m_wr[0] &= ~regmask; } + LOG(("reg %02x, regmask %02x, WR0 %02x, data %02x\n", reg, regmask, m_wr[0], data)); + switch (reg) { case 0: @@ -845,9 +907,21 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Null\n", m_owner->tag(), 'A' + m_index)); break; + //case WR0_POINT_HIGH: // Same value so doesn't compile... case WR0_SEND_ABORT: - LOG(("Z80DART \"%s\" Channel %c : Send Abort\n", m_owner->tag(), 'A' + m_index)); - logerror("Z80DART \"%s\" Channel %c : unsupported command: Send Abort\n", m_owner->tag(), 'A' + m_index); + if (((z80dart_device *)m_owner)->m_variant == z80dart_device::TYPE_Z80SCC) + { + /* This is the Point High command for SCC, it will latch access to the high + registers for the next read or write to the control registers */ + LOG(("Z80DART \"%s\" Channel %c : Point High\n", m_owner->tag(), 'A' + m_index)); + m_ph = 8; + } + else + { + /* Send Abort is only valid for the original Z80 SIO, not the DART or SCC */ + LOG(("Z80DART \"%s\" Channel %c : Send Abort\n", m_owner->tag(), 'A' + m_index)); + logerror("Z80DART \"%s\" Channel %c : unsupported command: Send Abort\n", m_owner->tag(), 'A' + m_index); + } break; case WR0_RESET_EXT_STATUS: @@ -892,6 +966,8 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Return from Interrupt\n", m_owner->tag(), 'A' + m_index)); m_uart->z80daisy_irq_reti(); break; + default: + logerror("Z80DART \"%s\" Channel %c : Unsupported WR0 command:%02x\n", m_owner->tag(), 'A' + m_index, data & WR0_COMMAND_MASK); } break; @@ -989,6 +1065,70 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Receive Sync %02x\n", m_owner->tag(), 'A' + m_index, data)); m_sync = (data << 8) | (m_sync & 0xff); break; + /* registers 8-15 are specific to SCC and misuse captured by test around Point High command above */ + case 8: + LOG(("Z80DART \"%s\" Channel %c : Transmit Buffer read %02x\n", m_owner->tag(), 'A' + m_index, data)); + data_write(data); + break; + case 9: + switch (data & WR9_CMD_MASK) + { + case WR9_CMD_NORESET: + LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - No reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + break; + case WR9_CMD_CHNB_RESET: + LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Channel B reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_uart->m_chanB->reset(); + break; + case WR9_CMD_CHNA_RESET: + LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Channel A reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_uart->m_chanA->reset(); + break; + case WR9_CMD_HW_RESET: + LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Device reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + /*"The effects of this command are identical to those of a hardware reset, except that the Shift Right/Shift Left bit is + not changed and the MIE, Status High/Status Low and DLC bits take the programmed values that accompany this command." + The Shift Right/Shift Left bits of the WR0 is only valid on SCC8030 device hence not implemented yet, just the SCC8530 */ + if (data & (WR9_BIT_MIE | WR9_BIT_IACK | WR9_BIT_SHSL | WR9_BIT_DLC | WR9_BIT_NV)) + logerror("Z80DART: SCC Interrupt system not yet implemented, please be patient!\n"); + m_uart->device_reset(); + break; + default: + logerror("Z80DART Code is broken in WR9, please report!\n"); + } + break; + case 10: + LOG(("Z80DART \"%s\" Channel %c : unsupported command: Misc Tx/Rx Control %02x\n", m_owner->tag(), 'A' + m_index, data)); + break; + case 11: + LOG(("Z80DART \"%s\" Channel %c : incomplete command: Clock Mode Control %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_wr[11] = data; + break; + case 12: + LOG(("Z80DART \"%s\" Channel %c : incomplete command: Low Byte of Baudrate Generator Time Constant %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_wr[12] = data; + break; + case 13: + LOG(("Z80DART \"%s\" Channel %c : incomplete command: High Byte of Baudrate Generator Time Constant %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_wr[13] = data; + break; + case 14: + switch (data & WR14_DPLL_CMD_MASK) + { + case WR14_CMD_NULL: + LOG(("Z80DART \"%s\" Channel %c : Misc Control Bits: DPLL Null Command %02x\n", m_owner->tag(), 'A' + m_index, data)); + break; + default: + logerror("Z80DART \"%s\" Channel %c : incomplete command: Misc Control Bits %02x\n", m_owner->tag(), 'A' + m_index, data); + } + // TODO: Add support for clock source to Baudrate generator + m_wr[14] = data; + break; + case 15: + LOG(("Z80DART \"%s\" Channel %c : unsupported command: External/Status Control Bits %02x\n", m_owner->tag(), 'A' + m_index, data)); + break; + default: + logerror("Z80DART \"%s\" Channel %c : Unsupported WRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); } } diff --git a/src/devices/machine/z80dart.h b/src/devices/machine/z80dart.h index 2674f84758a..c4893ef90db 100644 --- a/src/devices/machine/z80dart.h +++ b/src/devices/machine/z80dart.h @@ -6,6 +6,7 @@ NEC uPD7201 Multiprotocol Serial Communications Controller emulation Z80-DART Dual Asynchronous Receiver/Transmitter emulation Z80-SIO/0/1/2/3/4 Serial Input/Output Controller emulation + Z80-SCC Serial Communications Controller emulation (experimental) **************************************************************************** _____ _____ @@ -140,6 +141,29 @@ _DCDA 19 | | 22 _DCDB CLK 20 |_____________| 21 _RESET + + _____ _____ + D1 1 |* \_/ | 40 D0 + D3 2 | | 39 D2 + D5 3 | | 38 D4 + D7 4 | | 37 D6 + _INT 5 | | 36 _RD + IEO 6 | | 35 _WR + IEI 7 | | 34 B/_A + _INTAK 8 | | 33 _CE + VCC 9 | | 32 C/_D + _W/REQA 10| Z80-SCC | 31 GND + _SYNCA 11| Z8530 | 30 _W/REQB + _RTxCA 12| | 29 _SYNCB + RxDA 13| | 28 _RTxCB + _TRxCA 14| | 27 RxDB + TxDA 15| | 26 _TRxCB + _DTR/REQA 16| | 25 TxDB + _RTSA 17| | 24 _DTR/REQB + _CTSA 18| | 23 _RTSB + _DCDA 19| | 22 _CTSB + CLK 20|_____________| 21 _DCDB + ***************************************************************************/ #ifndef __Z80DART_H__ @@ -185,6 +209,10 @@ MCFG_DEVICE_ADD(_tag, UPD7201, _clock) \ MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) +#define MCFG_Z80SCC_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \ + MCFG_DEVICE_ADD(_tag, Z80SCC, _clock) \ + MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) + #define MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) \ z80dart_device::configure_channels(*device, _rxa, _txa, _rxb, _txb); @@ -282,8 +310,8 @@ public: int m_txc; // register state - UINT8 m_rr[3]; // read register - UINT8 m_wr[6]; // write register + UINT8 m_rr[16]; // read registers, DART=3 SCC=16 + UINT8 m_wr[16]; // write register, DART=6 SCC=16 protected: enum @@ -336,6 +364,13 @@ protected: WR0_CRC_RESET_TX_UNDERRUN = 0xc0 // not supported }; + enum /* SCC specifics */ + { + WR0_REGISTER_MASK_SCC = 0x0f, + WR0_POINT_HIGH = 0x08, + + }; + enum { WR1_EXT_INT_ENABLE = 0x01, @@ -418,6 +453,34 @@ protected: WR5_DTR = 0x80 }; + /* SCC specifics */ + enum + { + WR9_CMD_MASK = 0xC0, + WR9_CMD_NORESET = 0x00, + WR9_CMD_CHNB_RESET = 0x40, + WR9_CMD_CHNA_RESET = 0x80, + WR9_CMD_HW_RESET = 0xC0, + WR9_BIT_VIS = 0x01, + WR9_BIT_NV = 0x02, + WR9_BIT_DLC = 0x04, + WR9_BIT_MIE = 0x08, + WR9_BIT_SHSL = 0x10, + WR9_BIT_IACK = 0x20 + }; + + enum + { + WR14_DPLL_CMD_MASK = 0xe0, + WR14_CMD_NULL = 0x00, + WR14_CMD_ESM = 0x20, + WR14_CMD_RMC = 0x40, + WR14_CMD_SS_BGR = 0x80, + WR14_CMD_SS_RTXC = 0xa0, + WR14_CMD_SET_FM = 0xc0, + WR14_CMD_SET_NRZI = 0xe0 + }; + void update_serial(); void set_dtr(int state); void set_rts(int state); @@ -455,6 +518,9 @@ protected: int m_index; z80dart_device *m_uart; + + // SCC specifics + int m_ph; // Point high command to access regs 08-0f }; @@ -555,7 +621,8 @@ protected: TYPE_SIO3, TYPE_SIO4, TYPE_I8274, - TYPE_UPD7201 + TYPE_UPD7201, + TYPE_Z80SCC }; enum @@ -669,6 +736,16 @@ public: }; +// ======================> scc8530_device + +class scc8530_device : public z80dart_device +{ +public : + // construction/destruction + scc8530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + + // device type definition extern const device_type Z80DART_CHANNEL; extern const device_type Z80DART; @@ -679,6 +756,7 @@ extern const device_type Z80SIO3; extern const device_type Z80SIO4; extern const device_type I8274; extern const device_type UPD7201; +extern const device_type Z80SCC; #endif diff --git a/src/mame/mess.lst b/src/mame/mess.lst index cea61196da1..ac6f4ad82d9 100644 --- a/src/mame/mess.lst +++ b/src/mame/mess.lst @@ -2614,6 +2614,7 @@ eacc argo applix mzr8105 +hk68v10 fccpu1 68ksbc lcmate2 diff --git a/src/mess/drivers/hk68v10.c b/src/mess/drivers/hk68v10.c new file mode 100644 index 00000000000..8e1c766bbe4 --- /dev/null +++ b/src/mess/drivers/hk68v10.c @@ -0,0 +1,371 @@ +// license:BSD-3-Clause +// copyright-holders:Joakim Larsson Edstrom +/*************************************************************************** + * + * Heurikon HK68/V10 6U SBC driver, initially derived from hk68v10.c + * + * 21/08/2015 + * + * I baught this board from http://www.retrotechnology.com without documentation. + * It has a Motorola 68010 CPU @ 10MHz and two 2764 EPROMS with HBUG firmware + * The board is very populated and suitable to run a real server OS supported by + * FPU,MMU and DMA controller chips. The firmware supports SCSI, Centronics/FPI + * and a serial port. I have not found so much on the FPI interface yet and it is + * possibly Heurikon specific. There is also a Heurikon Multibus board called + * HK68/M10 for which I might add support from specs, however would need a ROM + * to verify. + * + * || + * || || + * ||||--|| + * ||||--|| + * || ||____________________________________________________________ ___ + * || | | | 12 PALs 68/V-... | | | | | |_| | + * || | | 2 x | |74 |74 |74 |74 | | | | + * || | | 74273 | VS4-01 VM4-00 | 244 573 645 645 | | | + * \==||| |SCSI | | VS3-00 VM3-03 | | | | | | | | + * ||| | | | VS2-01 VM2-02 |___|___|___|___| | | | + * || |NCR | | VS1-00 VM1-01 | | | | | | | + * || | 8530| 2 x | VBX-00 VDB-00 |74 |74 |74 | | |VME| + * || | | 74245 | VI1-00 VI2-00 | 244 641 245 | | | + * || | | | (90 deg ccw) | | | | | |P1 | + * SCSI || |_____| |_____________________|___|___|___| | | | + * port || | | _________ _________________________________ | | | + * || | | | || |74 |PAL | | | | | + * || | | | || 5 x | 244 MP2| 2 x | | | | + * || |CIO | | MC68881 || 74373 | | -00| 74646| | | | + * || |paral| | FPU || | | | | | | | + * || |Z8536| |_________||_________________|_ _| | | |_| | + * ||| | | | | | | ------------ |___| + * /==||| | | | | | | | 3 PALs | | + * FSM || | | | MC68010 | MC68451 | MC68450 | | MP1-00 | | + * 1234 || | | | CPU | MMU | DMA | | MX2-00 | | + * LEDs || |-----| |_________|__________|_________|__| MX1-01 |---| + * || | | ____ ____ ---| |__________|74 | + * \==||| | | | | | | |74| 4 x | 3 PALs |145| + * ||| |SCC | | | | | |804 74245 | CS3-00 |___| + * FPI || |seria| |U12 | |U23 | | | | CS2-00 | | + * paralell |Z8530| |HBUG| |HBUG| | | | CS1-00 | |___ + * port || | | | | | | ---_---------------|__________| _| | + * || | | | | | | |MTTLDL|MDLDM| 2 x |___________ | | | + * ||| | | |____|_|____|_|____40|TTL75|74280| | | | | + * /==||| | || |19. | | | | | |_____| | | | | + * \==||| |_____||75 |6608|16 |20 |74|74| ______| 9 x 4 | | | | + * ||| +--------+|173| MHz| MHz | MHz| 74 08| | | RAM chips| | |VME| + * || | ||___|____|_____|____|__|__||74 74 | on SIL | | | | + * || | 2 x || | | |259|257 boards | | |P2 | + * || | SIL || 12 PALs 90 deg ccw|74| -______- | | | | + * || | boards || |240 | | | | | | | + * || | with 2 || BER-00 FPI-01 | | |74 74 | | | | | + * Serial|| | DIP16 || AC4-00 ARB-01 |__|___|138|257 | | | | + * port || | surf || AC3-00 INT-00 |______| | | | | + * || | mounted|| AC2-01 IAK-00 | | | | | | | + * || | chips || AC1-01 PAS-01 |74 |74| | | | | + * || | each || RFS-01 |590|257 | | | | + * ||| | || RCT-01 | | | | |_| | + * /==||| +--------+|__________________________|___|__|__________| |___| + * || ||------------------------------------------------------------+-+ + * ||||--|| + * ||||--|| + * || + * + * History of Heurikon + *--------------------- + * The company was founded 1972 as cellar company. Heurikon was aquired + * 1989 by Computer Products, 1990 by Artesyn and finally in 2005 by Emerson + * Electric who consilidated it fully by 2009 and closed the office. + * + * Misc links about Heurikon and this board: + * http://www.heurikon.com/ + * http://www.nytimes.com/1992/01/07/business/company-news-briefs.html + * http://bitsavers.informatik.uni-stuttgart.de/pdf/heurikon/brochures/HK68_V10_Brochure.pdf + * http://bitsavers.informatik.uni-stuttgart.de/pdf/heurikon/Heurikon_UNIX_System_V_and_V.2_Reference_Guide_Apr87.pdf + * + * Address Map from the UNIX Ref Guide + * -------------------------------------------------------------------------- + * Address Range Memory Space (physical) Notes + * -------------------------------------------------------------------------- + * 0xffffff (top of memory) + * 0xffc200 0xffc220 Sky FFP 68881 FPU + * 0xff8000 ... MCT Reel-to-reel tape (V10) VME + * 0xff0000 0xff0100 Tapemaster(00B0,00B1) Reel-to-reel tape (M10) Multibus + * Ethernet(0010,0011) + * 0xff0000 (base of bus I/O) + * + * 0xfea000 SCC8530 device + * 0xfe9000 offset 6 seems also to be 2 access registers, ie register offset + data pairs + * 0xfe8000 offset E reading single byte + * 0xfe4000 front LED:s? or move ROM from 0x0 to 0xfc0000... + * 0xfe2000 cleared + * 0xfe0000 HK68 On-card I/O MMU, DMAC, SCC, SCSI + * 0xfc0000 HK68 ROM (128K) Hbug monitor + * 0xf00000 (reserved) + * (shared memory and logical area + * for shared text.) + * 0x800000 (Twilight Zone) (top of /dev/mem) + * 0x7c0000 (reserved) + * 0x780000 8 Chnl serial Expn(1-4) (CDC) + * HK68 (1-15) VRTX + * MLZ-93 (1-4) CP/M Shell + * 0x700000 + * (open) + * 0x*00000 (maxmeml) + * RAM (bus) (optional) + * 0x200000 (top of 2 meg RAM) + * RAM (bus) (optional) + * 0x100000 (top of 1 meg RAM) + * User RAM + * 0x001000 UNIX Kernel + * Exception Vectors + * 0x000000 (bottom of memory) + * -------------------------------------------------------------------------- + * + * Interrupt sources M10/V10 + * ---------------------------------------------------------- + * Description Device Lvl IRQ VME board + * /Board Vector Address + * ---------------------------------------------------------- + * On board Sources M10/V10 + * + * Off board Sources (other VME boards) + * EXOS Ethernet 5 4 + * TapeMaster Reel-to-Reel Tape 3 + * MCT Reel-to-Reel Tape 5 + * CDC MB1031 Serial Expansion 2 2 + * IT 2190 SMD Interface 1 3 + * IT 3200 SMD Interface 3 + * ---------------------------------------------------------- + * + * DMAC Channel Assignments + * ---------------------------------------------------------- + * Channel M10 V10 + * ---------------------------------------------------------- + * 0 SCSI SCSI + * 1 Streamer (P3) n/a + * 2 n/a n/a + * 3 SBX-FDIO n/a + * ---------------------------------------------------------- + * + * TODO: + * - Dump the ROMs (DONE) + * - Setup a working address map (DONE) + * - Fix terminal for HBUG (DONE) + * - Add VME bus driver + * - Add DMA/MMU devices + * - Add CIO port + * - ADD SCSI controller device + * - dump PALs and describe descrete logic + * - Setup BAUD generation correctly, (eg find that x32 divider) + * - Add LED:s + * - Add Jumpers and strap areas + * - Find and Boot Heurikon Unix from a SCSI device + * - Support that optional 68881 FPU + * + ****************************************************************************/ + +#include "emu.h" +#include "cpu/m68000/m68000.h" +#include "machine/z80dart.h" +#include "bus/rs232/rs232.h" +#include "machine/clock.h" + +#define LOG(x) /* x */ + +#define BAUDGEN_CLOCK XTAL_19_6608MHz /* Raltron */ +/* + */ +#define SCC_CLOCK (BAUDGEN_CLOCK / 128) /* This will give prompt */ +//#define SCC_CLOCK (BAUDGEN_CLOCK / 4) /* This is correct */ +class hk68v10_state : public driver_device +{ +public: +hk68v10_state(const machine_config &mconfig, device_type type, const char *tag) : + driver_device (mconfig, type, tag), + m_maincpu (*this, "maincpu") + ,m_sccterm(*this, "scc") +// ,m_cart(*this, "exp_rom1") +{ +} + +DECLARE_READ16_MEMBER (bootvect_r); +DECLARE_WRITE16_MEMBER (bootvect_w); +DECLARE_READ16_MEMBER (vme_a24_r); +DECLARE_WRITE16_MEMBER (vme_a24_w); +DECLARE_READ16_MEMBER (vme_a16_r); +DECLARE_WRITE16_MEMBER (vme_a16_w); +virtual void machine_start (); +virtual void machine_reset (); +DECLARE_WRITE_LINE_MEMBER (write_sccterm_clock); + +protected: + +private: +required_device m_maincpu; +required_device m_sccterm; + +// Pointer to System ROMs needed by bootvect_r and masking RAM buffer for post reset accesses + UINT16 *m_sysrom; + UINT16 m_sysram[4]; +}; + +static ADDRESS_MAP_START (hk68v10_mem, AS_PROGRAM, 16, hk68v10_state) +ADDRESS_MAP_UNMAP_HIGH +AM_RANGE (0x000000, 0x000007) AM_ROM AM_READ (bootvect_r) /* ROM mirror just durin reset */ +AM_RANGE (0x000000, 0x000007) AM_RAM AM_WRITE (bootvect_w) /* After first write we act as RAM */ +AM_RANGE (0x000008, 0x1fffff) AM_RAM /* 2 Mb RAM */ +AM_RANGE (0xFC0000, 0xFC3fff) AM_ROM /* System EPROM Area 16Kb HBUG */ +AM_RANGE (0xFC4000, 0xFDffff) AM_ROM /* System EPROM Area an additional 112Kb for System ROM */ +AM_RANGE (0xFE9000, 0xFE9009) AM_RAM //AM_DEVREADWRITE8("scc", scc8530_device, ba_cd_r, ba_cd_w, 0xffff) /* Z80-PIO? */ +AM_RANGE (0xFEA000, 0xFEA001) AM_DEVREADWRITE8("scc", scc8530_device, ca_r, ca_w, 0xff00) /* Dual serial port Z80-SCC */ +AM_RANGE (0xFEA002, 0xFEA003) AM_DEVREADWRITE8("scc", scc8530_device, cb_r, cb_w, 0xff00) /* Dual serial port Z80-SCC */ +AM_RANGE (0xFEA004, 0xFEA005) AM_DEVREADWRITE8("scc", scc8530_device, da_r, da_w, 0xff00) /* Dual serial port Z80-SCC */ +AM_RANGE (0xFEA006, 0xFEA007) AM_DEVREADWRITE8("scc", scc8530_device, db_r, db_w, 0xff00) /* Dual serial port Z80-SCC */ +//AM_RANGE(0x100000, 0xfeffff) AM_READWRITE(vme_a24_r, vme_a24_w) /* VMEbus Rev B addresses (24 bits) - not verified */ +//AM_RANGE(0xff0000, 0xffffff) AM_READWRITE(vme_a16_r, vme_a16_w) /* VMEbus Rev B addresses (16 bits) - not verified */ +ADDRESS_MAP_END + +/* Input ports */ +static INPUT_PORTS_START (hk68v10) +INPUT_PORTS_END + +/* Start it up */ +void hk68v10_state::machine_start () +{ + LOG (logerror ("machine_start\n")); + + /* Setup pointer to bootvector in ROM for bootvector handler bootvect_r */ + m_sysrom = (UINT16*)(memregion ("maincpu")->base () + 0x0fc0000); +} + +/* Support CPU resets + + TODO: Investigate why the user need two 'softreset' commands for the below to work. Eg F3 + F11 + F3 + F11 + If only one 'softreset' is given the reset PC gets the RAM content, not the intended ROM vector. + Race conditions? Wrong call order in memory system? Debugger prefetch accesses? Better way to to this? +*/ +void hk68v10_state::machine_reset () +{ + LOG (logerror ("machine_reset\n")); + + /* Reset pointer to bootvector in ROM for bootvector handler bootvect_r */ + if (m_sysrom == &m_sysram[0]) /* Condition needed because memory map is not setup first time */ + m_sysrom = (UINT16*)(memregion ("maincpu")->base () + 0x0fc0000); +} + +/* Boot vector handler, the PCB hardwires the first 8 bytes from 0xfc0000 to 0x0 at reset*/ +/* + Right after HBUG reset the bootvector is masked by RAM: + FC001C: move.w #$700, $fe4000.l + FC0024: move.l #$0, $0.l # <- zeroing the reset vector + FC002E: move.l #$0, $4.l # There is for sure some hardware mapping going in here +*/ +READ16_MEMBER (hk68v10_state::bootvect_r){ + //LOG (logerror ("bootvect_r %s\n", m_sysrom != &m_sysram[0] ? "as reset" : "as swapped")); + return m_sysrom [offset]; +} + +WRITE16_MEMBER (hk68v10_state::bootvect_w){ + LOG (logerror("bootvect_w offset %08x, mask %08x, data %04x\n", offset, mem_mask, data)); + m_sysram[offset % sizeof(m_sysram)] &= ~mem_mask; + m_sysram[offset % sizeof(m_sysram)] |= (data & mem_mask); + m_sysrom = &m_sysram[0]; // redirect all upcomming accesses to masking RAM until reset. +} + +#if 0 +/* Dummy VME access methods until the VME bus device is ready for use */ +READ16_MEMBER (hk68v10_state::vme_a24_r){ + LOG (logerror ("vme_a24_r\n")); + return (UINT16) 0; +} + +WRITE16_MEMBER (hk68v10_state::vme_a24_w){ + LOG (logerror ("vme_a24_w\n")); +} + +READ16_MEMBER (hk68v10_state::vme_a16_r){ + LOG (logerror ("vme_16_r\n")); + return (UINT16) 0; +} + +WRITE16_MEMBER (hk68v10_state::vme_a16_w){ + LOG (logerror ("vme_a16_w\n")); +} +#endif + +/* + * Serial port clock source depends on the CPU clock and divider settings + * HBUG has a control byte that needs to be patched accordingly: + * + * CPU clock SCC clock Baud rate offset 0x0b value + * 10 Mhz 4.9152MHz 9600 15 + * 10 Mhz 4.9152MHz 19200 55 + * 12 Mhz 4.9152MHz 9600 16 + * 12 Mhz 4.9152MHz 19200 56 + * + * Configuration word detail: + * D15-D8 reserved ( = 0 ) + * D7(MSB) = 0 + * D6 = 0 for console default 9600 baud + * D6 = 1 for console default 19,200 baud + * D5,D4,D3,D2 = 0101 for 4.9152 Mhz SCC clock + * D1,DO + * 01 for 10 Mhz MPU clock + * D1,DO = 10 for 12 Mhz MPU clock + * + * Original HBUG configuration word: 0x003D = 0000 0000 0011 1101 + * + */ +WRITE_LINE_MEMBER (hk68v10_state::write_sccterm_clock){ + m_sccterm->txca_w (state); + m_sccterm->rxca_w (state); +} + +/* + * Machine configuration + */ +static MACHINE_CONFIG_START (hk68v10, hk68v10_state) +/* basic machine hardware */ +MCFG_CPU_ADD ("maincpu", M68010, XTAL_10MHz) +MCFG_CPU_PROGRAM_MAP (hk68v10_mem) + +/* Terminal Port config */ +MCFG_Z80SCC_ADD("scc", XTAL_4MHz, 0, 0, 0, 0 ) +MCFG_Z80DART_OUT_TXDA_CB(DEVWRITELINE("rs232trm", rs232_port_device, write_txd)) +MCFG_Z80DART_OUT_DTRA_CB(DEVWRITELINE("rs232trm", rs232_port_device, write_dtr)) +MCFG_Z80DART_OUT_RTSA_CB(DEVWRITELINE("rs232trm", rs232_port_device, write_rts)) + +MCFG_RS232_PORT_ADD ("rs232trm", default_rs232_devices, "terminal") +MCFG_RS232_RXD_HANDLER (DEVWRITELINE ("scc", scc8530_device, rxa_w)) +MCFG_RS232_CTS_HANDLER (DEVWRITELINE ("scc", scc8530_device, ctsa_w)) + +MCFG_DEVICE_ADD ("sccterm_clock", CLOCK, SCC_CLOCK) +MCFG_CLOCK_SIGNAL_HANDLER (WRITELINE (hk68v10_state, write_sccterm_clock)) + +MACHINE_CONFIG_END + +/* ROM definitions */ +ROM_START (hk68v10) +ROM_REGION (0x1000000, "maincpu", 0) + +ROM_LOAD16_BYTE ("hk68kv10U23.bin", 0xFC0001, 0x2000, CRC (632aa026) SHA1 (f2b1ed0cc38dfbeb1602c013e00757015400720d)) +ROM_LOAD16_BYTE ("hk68kv10U12.bin", 0xFC0000, 0x2000, CRC (f2d688e9) SHA1 (e68699965645f0ce53de47625163c3eb02c8b727)) +/* + * System ROM information + * + * The ROMs contains HBUG v1.8, known commands from different sources: + * + * 'uc' Print HK68 Configuration + * 'um' Perform RAM test + * 'dm adrs' Display Memory + * 'sb adrs' Substitute Byte at adrs + * 'c adrs' Call Routine at adrs + * 'bw' Boot from Winchester + * 'bf' Boot from floppy (MIO, SBX-FDIO) + * 'bsf' Boot from floppy (SCSI) + * + */ +ROM_END + +/* Driver */ +/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */ +COMP (1985, hk68v10, 0, 0, hk68v10, hk68v10, driver_device, 0, "Heurikon Corporation", "HK68/V10", MACHINE_NO_SOUND_HW | MACHINE_TYPE_COMPUTER ) From ba03ee9087a21f084975576e43522c8c0657c7e3 Mon Sep 17 00:00:00 2001 From: Joakim Larsson Edstrom Date: Sat, 12 Sep 2015 07:40:41 +0200 Subject: [PATCH 02/12] Added new z80sio driver derived from z80dart.c --- src/emu/machine/z80sio.c | 1393 ++++++++++++++++++++++++++++++++++++++ src/emu/machine/z80sio.h | 534 +++++++++++++++ 2 files changed, 1927 insertions(+) create mode 100644 src/emu/machine/z80sio.c create mode 100644 src/emu/machine/z80sio.h diff --git a/src/emu/machine/z80sio.c b/src/emu/machine/z80sio.c new file mode 100644 index 00000000000..6ac4af5578f --- /dev/null +++ b/src/emu/machine/z80sio.c @@ -0,0 +1,1393 @@ +// license:BSD-3-Clause +// copyright-holders:Curt Coder, Joakim Larsson Edstrom +/*************************************************************************** + + Z80-SIO Serial Input/Output emulation + + The variants in the SIO family are only different in the packaging + but has the same register features. However, since some signals are + not connected to the pins on the package or share a pin with another + signal the functionality is limited. However, this driver does not + check that an operation is invalid because of package type but relies + on the software to be adapated for the particular version. + + Package: DIP40 SIO/0, SIO/1, SIO/2, + QFP44 SIO/3 + PLCC44 SIO/4 + ------------------------------------------------------------------- + Channels / Full Duplex 2 / Y + Synch data rates 2Mhz 500Kbps + 4MHz 800Kbps + 6MHz 1200Kbps + 10MHz 2500Kbps + -- Asynchrounous features ------------------------------------------- + 5-8 bit per char Y + 1,1.5,2 stop bits Y + odd/even parity Y + x1,x16,x32,x64 Y + break det/gen Y + parity, framing & Y + overrun error det Y + -- Byte oriented synchrounous features ------------------------------- + Int/ext char sync Y + 1/2 synch chars Y + Aut synch char insertion Y + Aut CRC gen/det Y + -- SDLC/HDLC capabilities -------------------------------------------- + Abort seq gen/chk Y + Aut zero ins/det Y + Aut flag insert Y + Addr field rec Y + 1-fld resid hand Y + Valid rec msg protection Y + -- + Receiver FIFO 3 + Transmitter FIFO 1 + ------------------------------------------------------------------------- + * = Features that has been implemented n/a = features that will not +***************************************************************************/ + +#include "z80sio.h" + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + +#define VERBOSE 0 + +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) +#if VERBOSE == 2 +#define logerror printf +#endif + +#define CHANA_TAG "cha" +#define CHANB_TAG "chb" + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +// device type definition +const device_type Z80SIO = &device_creator; +const device_type Z80SIO_CHANNEL = &device_creator; + +//------------------------------------------------- +// device_mconfig_additions - +//------------------------------------------------- + +MACHINE_CONFIG_FRAGMENT( z80sio ) + MCFG_DEVICE_ADD(CHANA_TAG, Z80SIO_CHANNEL, 0) + MCFG_DEVICE_ADD(CHANB_TAG, Z80SIO_CHANNEL, 0) +MACHINE_CONFIG_END + +machine_config_constructor z80sio_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( z80sio ); +} + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// z80sio_device - constructor +//------------------------------------------------- + +z80sio_device::z80sio_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_z80daisy_interface(mconfig, *this), + m_chanA(*this, CHANA_TAG), + m_chanB(*this, CHANB_TAG), + m_rxca(0), + m_txca(0), + m_rxcb(0), + m_txcb(0), + m_out_txda_cb(*this), + m_out_dtra_cb(*this), + m_out_rtsa_cb(*this), + m_out_wrdya_cb(*this), + m_out_synca_cb(*this), + m_out_txdb_cb(*this), + m_out_dtrb_cb(*this), + m_out_rtsb_cb(*this), + m_out_wrdyb_cb(*this), + m_out_syncb_cb(*this), + m_out_int_cb(*this), + m_out_rxdrqa_cb(*this), + m_out_txdrqa_cb(*this), + m_out_rxdrqb_cb(*this), + m_out_txdrqb_cb(*this), + m_variant(variant) +{ + for (int i = 0; i < 8; i++) + m_int_state[i] = 0; +} + +z80sio_device::z80sio_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, Z80SIO, "Z80 SIO", tag, owner, clock, "z80sio", __FILE__), + device_z80daisy_interface(mconfig, *this), + m_chanA(*this, CHANA_TAG), + m_chanB(*this, CHANB_TAG), + m_rxca(0), + m_txca(0), + m_rxcb(0), + m_txcb(0), + m_out_txda_cb(*this), + m_out_dtra_cb(*this), + m_out_rtsa_cb(*this), + m_out_wrdya_cb(*this), + m_out_synca_cb(*this), + m_out_txdb_cb(*this), + m_out_dtrb_cb(*this), + m_out_rtsb_cb(*this), + m_out_wrdyb_cb(*this), + m_out_syncb_cb(*this), + m_out_int_cb(*this), + m_out_rxdrqa_cb(*this), + m_out_txdrqa_cb(*this), + m_out_rxdrqb_cb(*this), + m_out_txdrqb_cb(*this), + m_variant(TYPE_Z80SIO) +{ + for (int i = 0; i < 8; i++) + m_int_state[i] = 0; +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void z80sio_device::device_start() +{ + // resolve callbacks + m_out_txda_cb.resolve_safe(); + m_out_dtra_cb.resolve_safe(); + m_out_rtsa_cb.resolve_safe(); + m_out_wrdya_cb.resolve_safe(); + m_out_synca_cb.resolve_safe(); + m_out_txdb_cb.resolve_safe(); + m_out_dtrb_cb.resolve_safe(); + m_out_rtsb_cb.resolve_safe(); + m_out_wrdyb_cb.resolve_safe(); + m_out_syncb_cb.resolve_safe(); + m_out_int_cb.resolve_safe(); + m_out_rxdrqa_cb.resolve_safe(); + m_out_txdrqa_cb.resolve_safe(); + m_out_rxdrqb_cb.resolve_safe(); + m_out_txdrqb_cb.resolve_safe(); + + // configure channel A + m_chanA->m_rxc = m_rxca; + m_chanA->m_txc = m_txca; + + // configure channel B + m_chanB->m_rxc = m_rxcb; + m_chanB->m_txc = m_txcb; + + // state saving + save_item(NAME(m_int_state)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void z80sio_device::device_reset() +{ + LOG(("Z80SIO \"%s\" Reset\n", tag())); + + m_chanA->reset(); + m_chanB->reset(); +} + +//------------------------------------------------- +// z80daisy_irq_state - get interrupt status +//------------------------------------------------- + +int z80sio_device::z80daisy_irq_state() +{ + int state = 0; + int i; + + LOG(("Z80SIO \"%s\" : Interrupt State A:%d%d%d%d B:%d%d%d%d\n", tag(), + m_int_state[0], m_int_state[1], m_int_state[2], m_int_state[3], + m_int_state[4], m_int_state[5], m_int_state[6], m_int_state[7])); + + // loop over all interrupt sources + for (i = 0; i < 8; i++) + { + // if we're servicing a request, don't indicate more interrupts + if (m_int_state[i] & Z80_DAISY_IEO) + { + state |= Z80_DAISY_IEO; + break; + } + state |= m_int_state[i]; + } + + LOG(("Z80SIO \"%s\" : Interrupt State %u\n", tag(), state)); + + return state; +} + + +//------------------------------------------------- +// z80daisy_irq_ack - interrupt acknowledge +//------------------------------------------------- + +int z80sio_device::z80daisy_irq_ack() +{ + int i; + + LOG(("Z80SIO \"%s\" Interrupt Acknowledge\n", tag())); + + // loop over all interrupt sources + for (i = 0; i < 8; i++) + { + // find the first channel with an interrupt requested + if (m_int_state[i] & Z80_DAISY_INT) + { + // clear interrupt, switch to the IEO state, and update the IRQs + m_int_state[i] = Z80_DAISY_IEO; + m_chanA->m_rr0 &= ~z80sio_channel::RR0_INTERRUPT_PENDING; + check_interrupts(); + + LOG(("Z80SIO \"%s\" : Interrupt Acknowledge Vector %02x\n", tag(), m_chanB->m_rr2)); + + return m_chanB->m_rr2; + } + } + + //logerror("z80sio_irq_ack: failed to find an interrupt to ack!\n"); + + return m_chanB->m_rr2; +} + + +//------------------------------------------------- +// z80daisy_irq_reti - return from interrupt +//------------------------------------------------- + +void z80sio_device::z80daisy_irq_reti() +{ + int i; + + LOG(("Z80SIO \"%s\" Return from Interrupt\n", tag())); + + // loop over all interrupt sources + for (i = 0; i < 8; i++) + { + // find the first channel with an IEO pending + if (m_int_state[i] & Z80_DAISY_IEO) + { + // clear the IEO state and update the IRQs + m_int_state[i] &= ~Z80_DAISY_IEO; + check_interrupts(); + return; + } + } + + //logerror("z80sio_irq_reti: failed to find an interrupt to clear IEO on!\n"); +} + + +//------------------------------------------------- +// check_interrupts - +//------------------------------------------------- + +void z80sio_device::check_interrupts() +{ + int state = (z80daisy_irq_state() & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE; + m_out_int_cb(state); +} + + +//------------------------------------------------- +// reset_interrupts - +//------------------------------------------------- + +void z80sio_device::reset_interrupts() +{ + for (int i = 0; i < 8; i++) + { + m_int_state[i] = 0; + } + + check_interrupts(); +} + + +//------------------------------------------------- +// trigger_interrupt - TODO: needs attention for SIO +//------------------------------------------------- + +void z80sio_device::trigger_interrupt(int index, int state) +{ + UINT8 vector = m_chanB->m_wr2; + int priority; + +#if 0 + if((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201)) + { + int prio_level = 0; + switch(state) + { + case z80sio_channel::INT_TRANSMIT: + prio_level = 1; + break; + case z80sio_channel::INT_RECEIVE: + case z80sio_channel::INT_SPECIAL: + prio_level = 0; + break; + case z80sio_channel::INT_EXTERNAL: + prio_level = 2; + break; + } + + if(m_chanA->m_wr2 & z80sio_channel::WR2_PRIORITY) + { + priority = (prio_level * 2) + index; + } + else + { + priority = (prio_level == 2) ? index + 4 : ((index * 2) + prio_level); + } + if (m_chanB->m_wr1 & z80sio_channel::WR1_STATUS_VECTOR) + { + vector = (!index << 2) | state; + if((m_chanA->m_wr1 & 0x18) == z80sio_channel::WR2_MODE_8086_8088) + { + vector = (m_chanB->m_wr2 & 0xf8) | vector; + } + else + { + vector = (m_chanB->m_wr2 & 0xe3) | (vector << 2); + } + } + } + else + { +#endif + priority = (index << 2) | state; + if (m_chanB->m_wr1 & z80sio_channel::WR1_STATUS_VECTOR) + { + // status affects vector + vector = (m_chanB->m_wr2 & 0xf1) | (!index << 3) | (state << 1); + } +// } + + LOG(("Z80SIO \"%s\" Channel %c : Interrupt Request %u\n", tag(), 'A' + index, state)); + + // update vector register + m_chanB->m_rr2 = vector; + + // trigger interrupt + m_int_state[priority] |= Z80_DAISY_INT; + m_chanA->m_rr0 |= z80sio_channel::RR0_INTERRUPT_PENDING; + + // check for interrupt + check_interrupts(); +} + + +//------------------------------------------------- +// m1_r - interrupt acknowledge +//------------------------------------------------- + +int z80sio_device::m1_r() +{ + return z80daisy_irq_ack(); +} + + +//------------------------------------------------- +// cd_ba_r - +//------------------------------------------------- + +READ8_MEMBER( z80sio_device::cd_ba_r ) +{ + int ba = BIT(offset, 0); + int cd = BIT(offset, 1); + z80sio_channel *channel = ba ? m_chanB : m_chanA; + + // LOG(("z80sio_device::cd_ba_r ba:%02x cd:%02x\n", ba, cd)); + + return cd ? channel->control_read() : channel->data_read(); +} + + +//------------------------------------------------- +// cd_ba_w - +//------------------------------------------------- + +WRITE8_MEMBER( z80sio_device::cd_ba_w ) +{ + int ba = BIT(offset, 0); + int cd = BIT(offset, 1); + z80sio_channel *channel = ba ? m_chanB : m_chanA; + + // LOG(("z80sio_device::cd_ba_w ba:%02x cd:%02x\n", ba, cd)); + + if (cd) + channel->control_write(data); + else + channel->data_write(data); +} + + +//------------------------------------------------- +// ba_cd_r - +//------------------------------------------------- + +READ8_MEMBER( z80sio_device::ba_cd_r ) +{ + int ba = BIT(offset, 1); + int cd = BIT(offset, 0); + z80sio_channel *channel = ba ? m_chanB : m_chanA; + + // LOG(("z80sio_device::ba_cd_r ba:%02x cd:%02x\n", ba, cd)); + + return cd ? channel->control_read() : channel->data_read(); +} + + +//------------------------------------------------- +// ba_cd_w - +//------------------------------------------------- + +WRITE8_MEMBER( z80sio_device::ba_cd_w ) +{ + int ba = BIT(offset, 1); + int cd = BIT(offset, 0); + z80sio_channel *channel = ba ? m_chanB : m_chanA; + + LOG(("z80sio_device::ba_cd_w ba:%02x cd:%02x\n", ba, cd)); + + if (cd) + channel->control_write(data); + else + channel->data_write(data); +} + +//************************************************************************** +// SIO CHANNEL +//************************************************************************** + +//------------------------------------------------- +// z80sio_channel - constructor +//------------------------------------------------- + +z80sio_channel::z80sio_channel(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, Z80SIO_CHANNEL, "Z80 SIO channel", tag, owner, clock, "z80sio_channel", __FILE__), + device_serial_interface(mconfig, *this), + m_rx_error(0), + m_rx_fifo(-1), + m_rx_clock(0), + m_rx_first(0), + m_rx_break(0), + m_rx_rr0_latch(0), + m_rxd(0), + m_sh(0), + m_cts(0), + m_dcd(0), + m_tx_data(0), + m_tx_clock(0), + m_dtr(0), + m_rts(0), + m_sync(0) +{ + // Reset all registers + m_rr0 = m_rr1 = m_rr2 = 0; + m_wr0 = m_wr1 = m_wr2 = m_wr3 = m_wr4 = m_wr5 = m_wr6 = m_wr7 = 0; + + for (int i = 0; i < 3; i++) + { + m_rx_data_fifo[i] = 0; + m_rx_error_fifo[i] = 0; + } +} + + +//------------------------------------------------- +// start - channel startup +//------------------------------------------------- + +void z80sio_channel::device_start() +{ + m_uart = downcast(owner()); + LOG(("Z80SIO device_start m_uart:%p\n", m_uart)); + m_index = m_uart->get_channel_index(this); + + // state saving + save_item(NAME(m_rr0)); + save_item(NAME(m_rr1)); + save_item(NAME(m_rr2)); + save_item(NAME(m_wr0)); + save_item(NAME(m_wr1)); + save_item(NAME(m_wr2)); + save_item(NAME(m_wr3)); + save_item(NAME(m_wr4)); + save_item(NAME(m_wr5)); + save_item(NAME(m_wr6)); + save_item(NAME(m_wr7)); + save_item(NAME(m_rx_data_fifo)); + save_item(NAME(m_rx_error_fifo)); + save_item(NAME(m_rx_error)); + save_item(NAME(m_rx_fifo)); + save_item(NAME(m_rx_clock)); + save_item(NAME(m_rx_first)); + save_item(NAME(m_rx_break)); + save_item(NAME(m_rx_rr0_latch)); + save_item(NAME(m_sh)); + save_item(NAME(m_cts)); + save_item(NAME(m_dcd)); + save_item(NAME(m_tx_data)); + save_item(NAME(m_tx_clock)); + save_item(NAME(m_dtr)); + save_item(NAME(m_rts)); + save_item(NAME(m_sync)); + device_serial_interface::register_save_state(machine().save(), this); +} + + +//------------------------------------------------- +// reset - reset channel status +//------------------------------------------------- + +void z80sio_channel::device_reset() +{ + receive_register_reset(); + transmit_register_reset(); + + // disable receiver + m_wr3 &= ~WR3_RX_ENABLE; + + // disable transmitter + m_wr5 &= ~WR5_TX_ENABLE; + m_rr0 |= RR0_TX_BUFFER_EMPTY; + m_rr1 |= RR1_ALL_SENT; + + // reset external lines + set_rts(1); + set_dtr(1); + + // reset interrupts + if (m_index == z80sio_device::CHANNEL_A) + { + m_uart->reset_interrupts(); + } +} + +void z80sio_channel::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + device_serial_interface::device_timer(timer, id, param, ptr); +} + + +//------------------------------------------------- +// tra_callback - +//------------------------------------------------- + +void z80sio_channel::tra_callback() +{ + if (!(m_wr5 & WR5_TX_ENABLE)) + { + // transmit mark + if (m_index == z80sio_device::CHANNEL_A) + m_uart->m_out_txda_cb(1); + else + m_uart->m_out_txdb_cb(1); + } + else if (m_wr5 & WR5_SEND_BREAK) + { + // transmit break + if (m_index == z80sio_device::CHANNEL_A) + m_uart->m_out_txda_cb(0); + else + m_uart->m_out_txdb_cb(0); + } + else if (!is_transmit_register_empty()) + { + // transmit data + if (m_index == z80sio_device::CHANNEL_A) + m_uart->m_out_txda_cb(transmit_register_get_data_bit()); + else + m_uart->m_out_txdb_cb(transmit_register_get_data_bit()); + } +} + + +//------------------------------------------------- +// tra_complete - +//------------------------------------------------- + +void z80sio_channel::tra_complete() +{ + if ((m_wr5 & WR5_TX_ENABLE) && !(m_wr5 & WR5_SEND_BREAK) && !(m_rr0 & RR0_TX_BUFFER_EMPTY)) + { + LOG(("Z80SIO \"%s\" Channel %c : Transmit Data Byte '%02x'\n", m_owner->tag(), 'A' + m_index, m_tx_data)); + + transmit_register_setup(m_tx_data); + + // empty transmit buffer + m_rr0 |= RR0_TX_BUFFER_EMPTY; + + if (m_wr1 & WR1_TX_INT_ENABLE) + m_uart->trigger_interrupt(m_index, INT_TRANSMIT); + } + else if (m_wr5 & WR5_SEND_BREAK) + { + // transmit break + if (m_index == z80sio_device::CHANNEL_A) + m_uart->m_out_txda_cb(0); + else + m_uart->m_out_txdb_cb(0); + } + else + { + // transmit mark + if (m_index == z80sio_device::CHANNEL_A) + m_uart->m_out_txda_cb(1); + else + m_uart->m_out_txdb_cb(1); + } + + // if transmit buffer is empty + if (m_rr0 & RR0_TX_BUFFER_EMPTY) + { + // then all characters have been sent + m_rr1 |= RR1_ALL_SENT; + + // when the RTS bit is reset, the _RTS output goes high after the transmitter empties + if (!m_rts) + set_rts(1); + } +} + + +//------------------------------------------------- +// rcv_callback - +//------------------------------------------------- + +void z80sio_channel::rcv_callback() +{ + if (m_wr3 & WR3_RX_ENABLE) + { + receive_register_update_bit(m_rxd); + } +} + + +//------------------------------------------------- +// rcv_complete - +//------------------------------------------------- + +void z80sio_channel::rcv_complete() +{ + receive_register_extract(); + receive_data(get_received_char()); +} + + +//------------------------------------------------- +// get_clock_mode - get clock divisor +//------------------------------------------------- + +int z80sio_channel::get_clock_mode() +{ + int clocks = 1; + + switch (m_wr4 & WR4_CLOCK_RATE_MASK) + { + case WR4_CLOCK_RATE_X1: clocks = 1; break; + case WR4_CLOCK_RATE_X16: clocks = 16; break; + case WR4_CLOCK_RATE_X32: clocks = 32; break; + case WR4_CLOCK_RATE_X64: clocks = 64; break; + } + + return clocks; +} + +/* From "uPD7201/7201A MULTI PROTOCOL SERIAL COMMUNICATION CONTROLLER" by NEC: +"RTSA (Request to Send A): The state of the RTS bit (01 of the CR5 register) controls this pin. If +the RTS bit is reset in the asynchronous mode, a high level will not be output on the RTS pin until +all transmit characters are written and the all sent bit (D0 of the SR1 register) is set. In the +synchronous mode, the state of the RTS bit is used as is. That is, when the RTS bit is 0, the RTS +pin is 1. When the RTS bit is 1, the RTS pin is O." + +CR5 = m_wr5 and SR1 = m_rr1 + +*/ + +void z80sio_channel::set_rts(int state) +{ + if (m_index == z80sio_device::CHANNEL_A) + m_uart->m_out_rtsa_cb(state); + else + m_uart->m_out_rtsb_cb(state); +} + +void z80sio_channel::update_rts() +{ + if (m_wr5 & WR5_RTS) + { + // when the RTS bit is set, the _RTS output goes low + set_rts(0); + m_rts = 1; + } + else + { + // when the RTS bit is reset, the _RTS output goes high after the transmitter empties + m_rts = 0; + } + + // data terminal ready output follows the state programmed into the DTR bit*/ + set_dtr((m_wr5 & WR5_DTR) ? 0 : 1); +} + +//------------------------------------------------- +// get_stop_bits - get number of stop bits +//------------------------------------------------- + +device_serial_interface::stop_bits_t z80sio_channel::get_stop_bits() +{ + switch (m_wr4 & WR4_STOP_BITS_MASK) + { + case WR4_STOP_BITS_1: return STOP_BITS_1; + case WR4_STOP_BITS_1_5: return STOP_BITS_1_5; + case WR4_STOP_BITS_2: return STOP_BITS_2; + } + + return STOP_BITS_0; +} + + +//------------------------------------------------- +// get_rx_word_length - get receive word length +//------------------------------------------------- + +int z80sio_channel::get_rx_word_length() +{ + int bits = 5; + + switch (m_wr3 & WR3_RX_WORD_LENGTH_MASK) + { + case WR3_RX_WORD_LENGTH_5: bits = 5; break; + case WR3_RX_WORD_LENGTH_6: bits = 6; break; + case WR3_RX_WORD_LENGTH_7: bits = 7; break; + case WR3_RX_WORD_LENGTH_8: bits = 8; break; + } + + return bits; +} + + +//------------------------------------------------- +// get_tx_word_length - get transmit word length +//------------------------------------------------- + +int z80sio_channel::get_tx_word_length() +{ + int bits = 5; + + switch (m_wr5 & WR5_TX_WORD_LENGTH_MASK) + { + case WR5_TX_WORD_LENGTH_5: bits = 5; break; + case WR5_TX_WORD_LENGTH_6: bits = 6; break; + case WR5_TX_WORD_LENGTH_7: bits = 7; break; + case WR5_TX_WORD_LENGTH_8: bits = 8; break; + } + + return bits; +} + +/* + * This register contains the status of the receive and transmit buffers; the + * DCD, CTS, and SYNC inputs; the Transmit Underrun/EOM latch; and the + * Break/Abort latch. */ +UINT8 z80sio_channel::do_sioreg_rr0() +{ + return m_rr0; +} +/* + * This register contains the Special Receive condition status bits and Residue + * codes for the I-Field in the SDLC Receive Mode. */ +UINT8 z80sio_channel::do_sioreg_rr1() +{ + return m_rr1; +} +/* + * This register contains the interrupt vector written into WR2 if the Status +Affects Vector control bit is not set. If the control bit is set, it contains the +modified vector listed in the Status Affects Vector paragraph of the Write +Register 1 section. When this register is read, the vector returned is modi- +fied by the highest priority interrupting condition at the time of the read. If +no interrupts are pending, the vector is modified with V3 = 0, V2 = 1, and +V1 = 1. This register is read only through Channel B. */ +UINT8 z80sio_channel::do_sioreg_rr2() +{ + // channel B only + return m_index == z80sio_device::CHANNEL_B ? m_rr2 : 0; +} + + +//------------------------------------------------- +// control_read - read control register +//------------------------------------------------- + +UINT8 z80sio_channel::control_read() +{ + UINT8 data = 0; + UINT8 reg = m_wr0 & WR0_REGISTER_MASK; + + if (reg != 0) + { + // mask out register index + m_wr0 &= ~WR0_REGISTER_MASK; + } + + switch (reg) + { + case REG_RR0_STATUS: data = do_sioreg_rr0(); break; + case REG_RR1_SPEC_RCV_COND: data = do_sioreg_rr1(); break; + case REG_RR2_INTERRUPT_VECT: data = do_sioreg_rr2(); break; + default: + logerror("Z80SIO \"%s\" Channel %c : Unsupported RRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); + } + //LOG(("Z80SIO \"%s\" Channel %c : Register R%d read '%02x'\n", m_owner->tag(), 'A' + m_index, reg, data)); + + return data; +} + +/* SIO CRC Initialization Code handling + Handle the WR0 CRC Reset/Init bits separatelly, needed by derived devices separatelly from the commands */ +void z80sio_channel::do_sioreg_wr0_resets(UINT8 data) +{ + switch (data & WR0_CRC_RESET_CODE_MASK) + { + case WR0_CRC_RESET_NULL: + LOG(("Z80SIO \"%s\" Channel %c : CRC_RESET_NULL\n", m_owner->tag(), 'A' + m_index)); + break; + case WR0_CRC_RESET_RX: /* In Synchronous mode: all Os (zeros) (CCITT-O CRC-16) */ + LOG(("Z80SIO \"%s\" Channel %c : CRC_RESET_RX - not implemented\n", m_owner->tag(), 'A' + m_index)); + break; + case WR0_CRC_RESET_TX: /* In HDLC mode: all 1s (ones) (CCITT-1) */ + LOG(("Z80SIO \"%s\" Channel %c : CRC_RESET_TX - not implemented\n", m_owner->tag(), 'A' + m_index)); + break; + case WR0_CRC_RESET_TX_UNDERRUN: /* Resets Tx underrun/EOM bit (D6 of the SRO register) */ + LOG(("Z80SIO \"%s\" Channel %c : CRC_RESET_TX_UNDERRUN - not implemented\n", m_owner->tag(), 'A' + m_index)); + break; + default: /* Will not happen unless someone messes with the mask */ + logerror("Z80SIO \"%s\" Channel %c : %s Wrong CRC reset/init command:%02x\n", m_owner->tag(), 'A' + m_index, __func__, data & WR0_CRC_RESET_CODE_MASK); + } +} +void z80sio_channel::do_sioreg_wr0(UINT8 data) +{ + m_wr0 = data; + switch (data & WR0_COMMAND_MASK) + { + case WR0_NULL: + LOG(("Z80SIO \"%s\" Channel %c : Null\n", m_owner->tag(), 'A' + m_index)); + break; + case WR0_RESET_EXT_STATUS: + // reset external/status interrupt + m_rr0 &= ~(RR0_DCD | RR0_SYNC_HUNT | RR0_CTS | RR0_BREAK_ABORT); + // release the latch + m_rx_rr0_latch = 0; + // update register to reflect wire values TODO: Check if this will fire new interrupts + if (!m_dcd) m_rr0 |= RR0_DCD; + if (m_sync) m_rr0 |= RR0_SYNC_HUNT; + if (m_cts) m_rr0 |= RR0_CTS; + + LOG(("Z80SIO \"%s\" Channel %c : Reset External/Status Interrupt\n", m_owner->tag(), 'A' + m_index)); + break; + case WR0_CHANNEL_RESET: + // channel reset + LOG(("Z80SIO \"%s\" Channel %c : Channel Reset\n", m_owner->tag(), 'A' + m_index)); + device_reset(); + break; + case WR0_ENABLE_INT_NEXT_RX: + // enable interrupt on next receive character + LOG(("Z80SIO \"%s\" Channel %c : Enable Interrupt on Next Received Character\n", m_owner->tag(), 'A' + m_index)); + m_rx_first = 1; + break; + case WR0_RESET_TX_INT: + // reset transmitter interrupt pending + LOG(("Z80SIO \"%s\" Channel %c : Reset Transmitter Interrupt Pending\n", m_owner->tag(), 'A' + m_index)); + logerror("Z80SIO \"%s\" Channel %c : unsupported command: Reset Transmitter Interrupt Pending\n", m_owner->tag(), 'A' + m_index); + break; + case WR0_ERROR_RESET: + // error reset + LOG(("Z80SIO \"%s\" Channel %c : Error Reset\n", m_owner->tag(), 'A' + m_index)); + m_rr1 &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR); + break; + case WR0_RETURN_FROM_INT: + // return from interrupt + LOG(("Z80SIO \"%s\" Channel %c : Return from Interrupt\n", m_owner->tag(), 'A' + m_index)); + m_uart->z80daisy_irq_reti(); + break; + default: + LOG(("Z80SIO \"%s\" Channel %c : Unsupported WR0 command %02x mask %02x\n", m_owner->tag(), 'A' + m_index, data, WR0_REGISTER_MASK)); + + } + do_sioreg_wr0_resets(data); +} + +void z80sio_channel::do_sioreg_wr1(UINT8 data) +{ +/* TODO: implement vector modifications when WR1 bit D2 is changed */ + m_wr1 = data; + LOG(("Z80SIO \"%s\" Channel %c : External Interrupt Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR1_EXT_INT_ENABLE) ? 1 : 0)); + LOG(("Z80SIO \"%s\" Channel %c : Transmit Interrupt Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR1_TX_INT_ENABLE) ? 1 : 0)); + LOG(("Z80SIO \"%s\" Channel %c : Status Affects Vector %u\n", m_owner->tag(), 'A' + m_index, (data & WR1_STATUS_VECTOR) ? 1 : 0)); + LOG(("Z80SIO \"%s\" Channel %c : Wait/Ready Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR1_WRDY_ENABLE) ? 1 : 0)); + LOG(("Z80SIO \"%s\" Channel %c : Wait/Ready Function %s\n", m_owner->tag(), 'A' + m_index, (data & WR1_WRDY_FUNCTION) ? "Ready" : "Wait")); + LOG(("Z80SIO \"%s\" Channel %c : Wait/Ready on %s\n", m_owner->tag(), 'A' + m_index, (data & WR1_WRDY_ON_RX_TX) ? "Receive" : "Transmit")); + + switch (data & WR1_RX_INT_MODE_MASK) + { + case WR1_RX_INT_DISABLE: + LOG(("Z80SIO \"%s\" Channel %c : Receiver Interrupt Disabled\n", m_owner->tag(), 'A' + m_index)); + break; + + case WR1_RX_INT_FIRST: + LOG(("Z80SIO \"%s\" Channel %c : Receiver Interrupt on First Character\n", m_owner->tag(), 'A' + m_index)); + break; + + case WR1_RX_INT_ALL_PARITY: + LOG(("Z80SIO \"%s\" Channel %c : Receiver Interrupt on All Characters, Parity Affects Vector\n", m_owner->tag(), 'A' + m_index)); + break; + + case WR1_RX_INT_ALL: + LOG(("Z80SIO \"%s\" Channel %c : Receiver Interrupt on All Characters\n", m_owner->tag(), 'A' + m_index)); + break; + } +} + +void z80sio_channel::do_sioreg_wr2(UINT8 data) +{ + m_wr2 = data; + if (m_index == z80sio_device::CHANNEL_B) + { + if (m_wr1 & z80sio_channel::WR1_STATUS_VECTOR) + m_rr2 = ( m_rr2 & 0x0e ) | ( m_wr2 & 0xF1); + else + m_rr2 = m_wr2; + } + m_uart->check_interrupts(); + LOG(("Z80SIO \"%s\" Channel %c : Interrupt Vector %02x\n", m_owner->tag(), 'A' + m_index, data)); +} + +void z80sio_channel::do_sioreg_wr3(UINT8 data) +{ + m_wr3 = data; + LOG(("Z80SIO \"%s\" Channel %c : Receiver Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR3_RX_ENABLE) ? 1 : 0)); + LOG(("Z80SIO \"%s\" Channel %c : Auto Enables %u\n", m_owner->tag(), 'A' + m_index, (data & WR3_AUTO_ENABLES) ? 1 : 0)); + LOG(("Z80SIO \"%s\" Channel %c : Receiver Bits/Character %u\n", m_owner->tag(), 'A' + m_index, get_rx_word_length())); +} + +void z80sio_channel::do_sioreg_wr4(UINT8 data) +{ + m_wr4 = data; + LOG(("Z80SIO \"%s\" Channel %c : Parity Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR4_PARITY_ENABLE) ? 1 : 0)); + LOG(("Z80SIO \"%s\" Channel %c : Parity %s\n", m_owner->tag(), 'A' + m_index, (data & WR4_PARITY_EVEN) ? "Even" : "Odd")); + LOG(("Z80SIO \"%s\" Channel %c : Stop Bits %s\n", m_owner->tag(), 'A' + m_index, stop_bits_tostring(get_stop_bits()))); + LOG(("Z80SIO \"%s\" Channel %c : Clock Mode %uX\n", m_owner->tag(), 'A' + m_index, get_clock_mode())); +} + +void z80sio_channel::do_sioreg_wr5(UINT8 data) +{ + m_wr5 = data; + LOG(("Z80SIO \"%s\" Channel %c : Transmitter Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR5_TX_ENABLE) ? 1 : 0)); + LOG(("Z80SIO \"%s\" Channel %c : Transmitter Bits/Character %u\n", m_owner->tag(), 'A' + m_index, get_tx_word_length())); + LOG(("Z80SIO \"%s\" Channel %c : Send Break %u\n", m_owner->tag(), 'A' + m_index, (data & WR5_SEND_BREAK) ? 1 : 0)); + LOG(("Z80SIO \"%s\" Channel %c : Request to Send %u\n", m_owner->tag(), 'A' + m_index, (data & WR5_RTS) ? 1 : 0)); + LOG(("Z80SIO \"%s\" Channel %c : Data Terminal Ready %u\n", m_owner->tag(), 'A' + m_index, (data & WR5_DTR) ? 1 : 0)); +} + +void z80sio_channel::do_sioreg_wr6(UINT8 data) +{ + LOG(("Z80SIO \"%s\" Channel %c : Transmit Sync %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_sync = (m_sync & 0xff00) | data; +} + +void z80sio_channel::do_sioreg_wr7(UINT8 data) +{ + LOG(("Z80SIO \"%s\" Channel %c : Receive Sync %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_sync = (data << 8) | (m_sync & 0xff); +} + +//------------------------------------------------- +// control_write - write control register +//------------------------------------------------- + +void z80sio_channel::control_write(UINT8 data) +{ + UINT8 reg = m_wr0 & WR0_REGISTER_MASK; + + if (reg != 0) + { + // mask out register index + m_wr0 &= ~WR0_REGISTER_MASK; + } + + LOG(("Z80SIO control_write reg %02x, regmask %02x, WR0 %02x, data %02x\n", reg, WR0_REGISTER_MASK, m_wr0, data)); + + switch (reg) + { + case REG_WR0_COMMAND_REGPT: do_sioreg_wr0(data); break; + case REG_WR1_INT_DMA_ENABLE: do_sioreg_wr1(data); m_uart->check_interrupts(); break; + case REG_WR2_INT_VECTOR: do_sioreg_wr2(data); break; + case REG_WR3_RX_CONTROL: do_sioreg_wr3(data); update_serial(); break; + case REG_WR4_RX_TX_MODES: do_sioreg_wr4(data); update_serial(); break; + case REG_WR5_TX_CONTROL: do_sioreg_wr5(data); update_serial(); update_rts(); break; + case REG_WR6_SYNC_OR_SDLC_A: do_sioreg_wr6(data); break; + case REG_WR7_SYNC_OR_SDLC_F: do_sioreg_wr7(data); break; + default: + logerror("Z80SIO \"%s\" Channel %c : Unsupported WRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); + } +} + + +//------------------------------------------------- +// data_read - read data register +//------------------------------------------------- + +UINT8 z80sio_channel::data_read() +{ + UINT8 data = 0; + + if (m_rx_fifo >= 0) + { + // load data from the FIFO + data = m_rx_data_fifo[m_rx_fifo]; + + // load error status from the FIFO + m_rr1 = (m_rr1 & ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR)) | m_rx_error_fifo[m_rx_fifo]; + + // decrease FIFO pointer + m_rx_fifo--; + + if (m_rx_fifo < 0) + { + // no more characters available in the FIFO + m_rr0 &= ~ RR0_RX_CHAR_AVAILABLE; + } + } + + LOG(("Z80SIO \"%s\" Channel %c : Data Register Read '%02x'\n", m_owner->tag(), 'A' + m_index, data)); + + return data; +} + + +//------------------------------------------------- +// data_write - write data register +//------------------------------------------------- + +void z80sio_channel::data_write(UINT8 data) +{ + m_tx_data = data; + + if ((m_wr5 & WR5_TX_ENABLE) && is_transmit_register_empty()) + { + LOG(("Z80SIO \"%s\" Channel %c : Transmit Data Byte '%02x'\n", m_owner->tag(), 'A' + m_index, m_tx_data)); + + transmit_register_setup(m_tx_data); + + // empty transmit buffer + m_rr0 |= RR0_TX_BUFFER_EMPTY; + + if (m_wr1 & WR1_TX_INT_ENABLE) + m_uart->trigger_interrupt(m_index, INT_TRANSMIT); + } + else + { + m_rr0 &= ~RR0_TX_BUFFER_EMPTY; + } + + m_rr1 &= ~RR1_ALL_SENT; + + LOG(("Z80SIO \"%s\" Channel %c : Data Register Write '%02x'\n", m_owner->tag(), 'A' + m_index, data)); +} + + +//------------------------------------------------- +// receive_data - receive data word +//------------------------------------------------- + +void z80sio_channel::receive_data(UINT8 data) +{ + LOG(("Z80SIO \"%s\" Channel %c : Receive Data Byte '%02x'\n", m_owner->tag(), 'A' + m_index, data)); + + if (m_rx_fifo == 2) + { + // receive overrun error detected + m_rx_error |= RR1_RX_OVERRUN_ERROR; + + switch (m_wr1 & WR1_RX_INT_MODE_MASK) + { + case WR1_RX_INT_FIRST: + if (!m_rx_first) + { + m_uart->trigger_interrupt(m_index, INT_SPECIAL); + } + break; + + case WR1_RX_INT_ALL_PARITY: + case WR1_RX_INT_ALL: + m_uart->trigger_interrupt(m_index, INT_SPECIAL); + break; + } + } + else + { + m_rx_fifo++; + } + + // store received character and error status into FIFO + m_rx_data_fifo[m_rx_fifo] = data; + m_rx_error_fifo[m_rx_fifo] = m_rx_error; + + m_rr0 |= RR0_RX_CHAR_AVAILABLE; + + // receive interrupt + switch (m_wr1 & WR1_RX_INT_MODE_MASK) + { + case WR1_RX_INT_FIRST: + if (m_rx_first) + { + m_uart->trigger_interrupt(m_index, INT_RECEIVE); + + m_rx_first = 0; + } + break; + + case WR1_RX_INT_ALL_PARITY: + case WR1_RX_INT_ALL: + m_uart->trigger_interrupt(m_index, INT_RECEIVE); + break; + } +} + + +//------------------------------------------------- +// cts_w - clear to send handler +//------------------------------------------------- + +WRITE_LINE_MEMBER( z80sio_channel::cts_w ) +{ + LOG(("Z80SIO \"%s\" Channel %c : CTS %u\n", m_owner->tag(), 'A' + m_index, state)); + + if (m_cts != state) + { + // enable transmitter if in auto enables mode + if (!state) + if (m_wr3 & WR3_AUTO_ENABLES) + m_wr5 |= WR5_TX_ENABLE; + + // set clear to send + m_cts = state; + + if (!m_rx_rr0_latch) + { + if (!m_cts) + m_rr0 |= RR0_CTS; + else + m_rr0 &= ~RR0_CTS; + + // trigger interrupt + if (m_wr1 & WR1_EXT_INT_ENABLE) + { + // trigger interrupt + m_uart->trigger_interrupt(m_index, INT_EXTERNAL); + + // latch read register 0 + m_rx_rr0_latch = 1; + } + } + } +} + + +//------------------------------------------------- +// dcd_w - data carrier detected handler +//------------------------------------------------- + +WRITE_LINE_MEMBER( z80sio_channel::dcd_w ) +{ + LOG(("Z80SIO \"%s\" Channel %c : DCD %u\n", m_owner->tag(), 'A' + m_index, state)); + + if (m_dcd != state) + { + // enable receiver if in auto enables mode + if (!state) + if (m_wr3 & WR3_AUTO_ENABLES) + m_wr3 |= WR3_RX_ENABLE; + + // set data carrier detect + m_dcd = state; + + if (!m_rx_rr0_latch) + { + if (m_dcd) + m_rr0 |= RR0_DCD; + else + m_rr0 &= ~RR0_DCD; + + if (m_wr1 & WR1_EXT_INT_ENABLE) + { + // trigger interrupt + m_uart->trigger_interrupt(m_index, INT_EXTERNAL); + + // latch read register 0 + m_rx_rr0_latch = 1; + } + } + } +} + + +//------------------------------------------------- +// sh_w - Sync Hunt handler +//------------------------------------------------- + +WRITE_LINE_MEMBER( z80sio_channel::sync_w ) +{ + LOG(("Z80SIO \"%s\" Channel %c : Sync %u\n", m_owner->tag(), 'A' + m_index, state)); + + if (m_sh != state) + { + // set ring indicator state + m_sh = state; + + if (!m_rx_rr0_latch) + { + if (m_sh) + m_rr0 |= RR0_SYNC_HUNT; + else + m_rr0 &= ~RR0_SYNC_HUNT; + + if (m_wr1 & WR1_EXT_INT_ENABLE) + { + // trigger interrupt + m_uart->trigger_interrupt(m_index, INT_EXTERNAL); + + // latch read register 0 + m_rx_rr0_latch = 1; + } + } + } +} + + +//------------------------------------------------- +// rxc_w - receive clock +//------------------------------------------------- + +WRITE_LINE_MEMBER( z80sio_channel::rxc_w ) +{ + //LOG(("Z80SIO \"%s\" Channel %c : Receiver Clock Pulse\n", m_owner->tag(), m_index + 'A')); + int clocks = get_clock_mode(); + if (clocks == 1) + rx_clock_w(state); + else if(state) + { + rx_clock_w(m_rx_clock < clocks/2); + + m_rx_clock++; + if (m_rx_clock == clocks) + m_rx_clock = 0; + + } +} + + +//------------------------------------------------- +// txc_w - transmit clock +//------------------------------------------------- + +WRITE_LINE_MEMBER( z80sio_channel::txc_w ) +{ + //LOG(("Z80SIO \"%s\" Channel %c : Transmitter Clock Pulse\n", m_owner->tag(), m_index + 'A')); + int clocks = get_clock_mode(); + if (clocks == 1) + tx_clock_w(state); + else if(state) + { + tx_clock_w(m_tx_clock < clocks/2); + + m_tx_clock++; + if (m_tx_clock == clocks) + m_tx_clock = 0; + + } +} + + +//------------------------------------------------- +// update_serial - +//------------------------------------------------- +void z80sio_channel::update_serial() +{ + int data_bit_count = get_rx_word_length(); + stop_bits_t stop_bits = get_stop_bits(); + parity_t parity; + + LOG(("Z80SIO update_serial\n")); + + if (m_wr4 & WR4_PARITY_ENABLE) + { + if (m_wr4 & WR4_PARITY_EVEN) + parity = PARITY_EVEN; + else + parity = PARITY_ODD; + } + else + parity = PARITY_NONE; + + set_data_frame(1, data_bit_count, parity, stop_bits); + + int clocks = get_clock_mode(); + + if (m_rxc > 0) + { + set_rcv_rate(m_rxc / clocks); + } + + if (m_txc > 0) + { + set_tra_rate(m_txc / clocks); + } + receive_register_reset(); // if stop bits is changed from 0, receive register has to be reset +} + + +//------------------------------------------------- +// set_dtr - +//------------------------------------------------- + +void z80sio_channel::set_dtr(int state) +{ + m_dtr = state; + + if (m_index == z80sio_device::CHANNEL_A) + m_uart->m_out_dtra_cb(m_dtr); + else + m_uart->m_out_dtrb_cb(m_dtr); +} + +//------------------------------------------------- +// write_rx - +//------------------------------------------------- + +WRITE_LINE_MEMBER(z80sio_channel::write_rx) +{ + m_rxd = state; + //only use rx_w when self-clocked + if(m_rxc) + device_serial_interface::rx_w(state); +} diff --git a/src/emu/machine/z80sio.h b/src/emu/machine/z80sio.h new file mode 100644 index 00000000000..fa3471e8adb --- /dev/null +++ b/src/emu/machine/z80sio.h @@ -0,0 +1,534 @@ +// license:BSD-3-Clause +// copyright-holders:Curt Coder, Joakim Larsson Edstrom +/*************************************************************************** + + Z80-SIO Serial Input/Output + +**************************************************************************** + _____ _____ _____ _____ + D1 1|* \_/ |40 D0 _/ |40 D0 _/ |40 D0 + D3 2| |39 D2 : |39 D2 : |39 D2 + D5 3| |38 D4 : |38 D4 : |38 D4 + D7 4| |37 D6 : |37 D6 : |37 D6 + _INT 5| |36 _IORQ : |36 _IORQ : |36 _IORQ + IEO 6| |35 _CE : |35 _CE : |35 _CE + IEI 7| |34 B/_A : |34 B/_A : |34 B/_A + _M1 8| |33 C/_D : |33 C/_D : |33 C/_D + VDD 9| DIP40 |32 _RD : DIP40 |32 _RD : DIP40 |32 _RD + _W//RDYA 10| Z80 |31 GND : Z80 |31 GND : Z80 |31 GND + _SYNCA 11| SIO/0 |30 _W/_RDYB: SIO/1 |30 _W/_RDYB: SIO/2 |30 _W/_RDYB + RxDA 12| |29 _SYNCB : |29 _SYNCB : |29 _SYNCB + _RxCA 13| |28 RxDB : |28 RxDB : |28 _RxCB + _TxCA 14| |27 _RxTxCB : |27 _RxCB : |27 _TxCB + TxDA 15| |26 TxDB : |26 _TxCB : |26 TxDB + _DTRA 16| |25 _DTRB : |25 TxD_B : |25 _DTRB + _RTSA 17| |24 _RTSB : |24 _RTSB : |24 _RTSB + _CTSA 18| |23 _CTSB : |23 _CTSB : |23 _CTSB + _DCDA 19| |22 _DCDB : |22 _DCDB : |22 _DCDB + CLK 20|_____________|21 _RESET :_______|21 _RESET :_______|21 _RESET + + *I *I + *I N O *I O + N D D D D / D D D D R N D D D D D D D D R*C + T 7 5 3 1 C 0 2 4 6 Q T 7 5 3 1 0 2 4 6 Q E + +----------------------+ +----------------------+ + IEI|34 22| *CE IEI|6 5 4 3 2 1 44 42 40|B/ *A + IEO|35 21| B/ *A IEO|8 43 41 |C/ *D + *M1| | C/ *D *M1|9 37|*RD + +5v| | *RD +5V|10 36|GND +*W/ *RDYA| QFP44 | GND *W/ *RDYA|11 PLCC44 35|*W/ *RDYB + N/C| Z80 SIO/3 | N/C *SYNCA|12 Z80 SIO/4 34|*SYNCB + *SYNCA| Z804C43 | *W/ *RDYB RxDA|13 33|RxDB + RxDA| | *SYNCB *RxCA|14 32|*RxCB + *RxCA|42 | RxDB *TxCA|15 31|*TxCB + *TxCA|43 1 1 | *RxCB TxDA| 19 21 23 25 30|TxDB + TxDA`. 2 3 4 5 6 7 8 9 0 1 | *TxCB N/C|18 20 22 24 26 29|N/C + `--------------------+ +----------------------+ + *D*R*C*D C*R*D*C*R*D*T *D*R*C*D C*R*D*C*R*D N + T T T C L E C*T T T x T T T C L E C T T T / + R S S D K S D S S R D R S S D K S D S S R C + A A A A E B B B B B A A A A E B B B B + T T + +***************************************************************************/ + +#ifndef __Z80SIO_H__ +#define __Z80SIO_H__ + +#include "emu.h" +#include "cpu/z80/z80daisy.h" + +//************************************************************************** +// DEVICE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_Z80SIO_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \ + MCFG_DEVICE_ADD(_tag, Z80SIO, _clock) \ + MCFG_Z80SIO_OFFSETS(_rxa, _txa, _rxb, _txb) + +#define MCFG_Z80SIO_OFFSETS(_rxa, _txa, _rxb, _txb) \ + z80sio_device::configure_channels(*device, _rxa, _txa, _rxb, _txb); + +#define MCFG_Z80SIO_OUT_TXDA_CB(_devcb) \ + devcb = &z80sio_device::set_out_txda_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_DTRA_CB(_devcb) \ + devcb = &z80sio_device::set_out_dtra_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_RTSA_CB(_devcb) \ + devcb = &z80sio_device::set_out_rtsa_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_WRDYA_CB(_devcb) \ + devcb = &z80sio_device::set_out_wrdya_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_SYNCA_CB(_devcb) \ + devcb = &z80sio_device::set_out_synca_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_TXDB_CB(_devcb) \ + devcb = &z80sio_device::set_out_txdb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_DTRB_CB(_devcb) \ + devcb = &z80sio_device::set_out_dtrb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_RTSB_CB(_devcb) \ + devcb = &z80sio_device::set_out_rtsb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_WRDYB_CB(_devcb) \ + devcb = &z80sio_device::set_out_wrdyb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_SYNCB_CB(_devcb) \ + devcb = &z80sio_device::set_out_syncb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_INT_CB(_devcb) \ + devcb = &z80sio_device::set_out_int_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_RXDRQA_CB(_devcb) \ + devcb = &z80sio_device::set_out_rxdrqa_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_TXDRQA_CB(_devcb) \ + devcb = &z80sio_device::set_out_txdrqa_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_RXDRQB_CB(_devcb) \ + devcb = &z80sio_device::set_out_rxdrqb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SIO_OUT_TXDRQB_CB(_devcb) \ + devcb = &z80sio_device::set_out_txdrqb_callback(*device, DEVCB_##_devcb); + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> z80sio_channel + +class z80sio_device; + +class z80sio_channel : public device_t, + public device_serial_interface +{ + friend class z80sio_device; + +public: + z80sio_channel(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + + // device_serial_interface overrides + virtual void tra_callback(); + virtual void tra_complete(); + virtual void rcv_callback(); + virtual void rcv_complete(); + + // read register handlers + UINT8 do_sioreg_rr0(); + UINT8 do_sioreg_rr1(); + UINT8 do_sioreg_rr2(); + + // write register handlers + void do_sioreg_wr0(UINT8 data); + void do_sioreg_wr0_resets(UINT8 data); + void do_sioreg_wr1(UINT8 data); + void do_sioreg_wr2(UINT8 data); + void do_sioreg_wr3(UINT8 data); + void do_sioreg_wr4(UINT8 data); + void do_sioreg_wr5(UINT8 data); + void do_sioreg_wr6(UINT8 data); + void do_sioreg_wr7(UINT8 data); + + UINT8 control_read(); + void control_write(UINT8 data); + + UINT8 data_read(); + void data_write(UINT8 data); + + void receive_data(UINT8 data); + + DECLARE_WRITE_LINE_MEMBER( write_rx ); + DECLARE_WRITE_LINE_MEMBER( cts_w ); + DECLARE_WRITE_LINE_MEMBER( dcd_w ); + DECLARE_WRITE_LINE_MEMBER( ri_w ); + DECLARE_WRITE_LINE_MEMBER( rxc_w ); + DECLARE_WRITE_LINE_MEMBER( txc_w ); + DECLARE_WRITE_LINE_MEMBER( sync_w ); + + int m_rxc; + int m_txc; + + // Register state + // read registers enum + UINT8 m_rr0; // REG_RR0_STATUS + UINT8 m_rr1; // REG_RR1_SPEC_RCV_COND + UINT8 m_rr2; // REG_RR2_INTERRUPT_VECT + // write registers enum + UINT8 m_wr0; // REG_WR0_COMMAND_REGPT + UINT8 m_wr1; // REG_WR1_INT_DMA_ENABLE + UINT8 m_wr2; // REG_WR2_INT_VECTOR + UINT8 m_wr3; // REG_WR3_RX_CONTROL + UINT8 m_wr4; // REG_WR4_RX_TX_MODES + UINT8 m_wr5; // REG_WR5_TX_CONTROL + UINT8 m_wr6; // REG_WR6_SYNC_OR_SDLC_A + UINT8 m_wr7; // REG_WR7_SYNC_OR_SDLC_F + + int m_variant; // Set in device + +protected: + enum + { + INT_TRANSMIT = 0, + INT_EXTERNAL, + INT_RECEIVE, + INT_SPECIAL + }; + + // Read registers + enum + { + REG_RR0_STATUS = 0, + REG_RR1_SPEC_RCV_COND = 1, + REG_RR2_INTERRUPT_VECT = 2, + }; + + // Write registers + enum + { + REG_WR0_COMMAND_REGPT = 0, + REG_WR1_INT_DMA_ENABLE = 1, + REG_WR2_INT_VECTOR = 2, + REG_WR3_RX_CONTROL = 3, + REG_WR4_RX_TX_MODES = 4, + REG_WR5_TX_CONTROL = 5, + REG_WR6_SYNC_OR_SDLC_A = 6, + REG_WR7_SYNC_OR_SDLC_F = 7, + }; + + enum + { + RR0_RX_CHAR_AVAILABLE = 0x01, + RR0_INTERRUPT_PENDING = 0x02, + RR0_TX_BUFFER_EMPTY = 0x04, + RR0_DCD = 0x08, + RR0_SYNC_HUNT = 0x10, + RR0_CTS = 0x20, + RR0_TX_UNDERRUN = 0x40, + RR0_BREAK_ABORT = 0x80 + }; + + enum + { + RR1_ALL_SENT = 0x01, + RR1_RESIDUE_CODE_MASK = 0x0e, + RR1_PARITY_ERROR = 0x10, + RR1_RX_OVERRUN_ERROR = 0x20, + RR1_CRC_FRAMING_ERROR = 0x40, + RR1_END_OF_FRAME = 0x80 + }; + + enum + { // TODO: overload SIO functionality + RR2_INT_VECTOR_MASK = 0xff, // SCC channel A, SIO channel B (special case) + RR2_INT_VECTOR_V1 = 0x02, // SIO (special case) /SCC Channel B + RR2_INT_VECTOR_V2 = 0x04, // SIO (special case) /SCC Channel B + RR2_INT_VECTOR_V3 = 0x08 // SIO (special case) /SCC Channel B + }; + + enum + { + WR0_REGISTER_MASK = 0x07, + WR0_COMMAND_MASK = 0x38, + WR0_NULL = 0x00, + WR0_SEND_ABORT = 0x08, // not supported + WR0_RESET_EXT_STATUS = 0x10, + WR0_CHANNEL_RESET = 0x18, + WR0_ENABLE_INT_NEXT_RX = 0x20, + WR0_RESET_TX_INT = 0x28, // not supported + WR0_ERROR_RESET = 0x30, + WR0_RETURN_FROM_INT = 0x38, // not supported + WR0_CRC_RESET_CODE_MASK = 0xc0, // not supported + WR0_CRC_RESET_NULL = 0x00, // not supported + WR0_CRC_RESET_RX = 0x40, // not supported + WR0_CRC_RESET_TX = 0x80, // not supported + WR0_CRC_RESET_TX_UNDERRUN = 0xc0 // not supported + }; + + enum + { + WR1_EXT_INT_ENABLE = 0x01, + WR1_TX_INT_ENABLE = 0x02, + WR1_STATUS_VECTOR = 0x04, + WR1_RX_INT_MODE_MASK = 0x18, + WR1_RX_INT_DISABLE = 0x00, + WR1_RX_INT_FIRST = 0x08, + WR1_RX_INT_ALL_PARITY = 0x10, // not supported + WR1_RX_INT_ALL = 0x18, + WR1_WRDY_ON_RX_TX = 0x20, // not supported + WR1_WRDY_FUNCTION = 0x40, // not supported + WR1_WRDY_ENABLE = 0x80 // not supported + }; + + enum + { + WR2_DATA_XFER_INT = 0x00, // not supported + WR2_DATA_XFER_DMA_INT = 0x01, // not supported + WR2_DATA_XFER_DMA = 0x02, // not supported + WR2_DATA_XFER_ILLEGAL = 0x03, // not supported + WR2_DATA_XFER_MASK = 0x03, // not supported + WR2_PRIORITY = 0x04, // not supported + WR2_MODE_8085_1 = 0x00, // not supported + WR2_MODE_8085_2 = 0x08, // not supported + WR2_MODE_8086_8088 = 0x10, // not supported + WR2_MODE_ILLEGAL = 0x18, // not supported + WR2_MODE_MASK = 0x18, // not supported + WR2_VECTORED_INT = 0x20, // not supported + WR2_PIN10_SYNDETB_RTSB = 0x80 // not supported + }; + + enum + { + WR3_RX_ENABLE = 0x01, + WR3_SYNC_CHAR_LOAD_INHIBIT= 0x02, // not supported + WR3_ADDRESS_SEARCH_MODE = 0x04, // not supported + WR3_RX_CRC_ENABLE = 0x08, // not supported + WR3_ENTER_HUNT_PHASE = 0x10, // not supported + WR3_AUTO_ENABLES = 0x20, + WR3_RX_WORD_LENGTH_MASK = 0xc0, + WR3_RX_WORD_LENGTH_5 = 0x00, + WR3_RX_WORD_LENGTH_7 = 0x40, + WR3_RX_WORD_LENGTH_6 = 0x80, + WR3_RX_WORD_LENGTH_8 = 0xc0 + }; + + enum + { + WR4_PARITY_ENABLE = 0x01, + WR4_PARITY_EVEN = 0x02, + WR4_STOP_BITS_MASK = 0x0c, + WR4_STOP_BITS_1 = 0x04, + WR4_STOP_BITS_1_5 = 0x08, // not supported + WR4_STOP_BITS_2 = 0x0c, + WR4_SYNC_MODE_MASK = 0x30, // not supported + WR4_SYNC_MODE_8_BIT = 0x00, // not supported + WR4_SYNC_MODE_16_BIT = 0x10, // not supported + WR4_SYNC_MODE_SDLC = 0x20, // not supported + WR4_SYNC_MODE_EXT = 0x30, // not supported + WR4_CLOCK_RATE_MASK = 0xc0, + WR4_CLOCK_RATE_X1 = 0x00, + WR4_CLOCK_RATE_X16 = 0x40, + WR4_CLOCK_RATE_X32 = 0x80, + WR4_CLOCK_RATE_X64 = 0xc0 + }; + + enum + { + WR5_TX_CRC_ENABLE = 0x01, // not supported + WR5_RTS = 0x02, + WR5_CRC16 = 0x04, // not supported + WR5_TX_ENABLE = 0x08, + WR5_SEND_BREAK = 0x10, + WR5_TX_WORD_LENGTH_MASK = 0x60, + WR5_TX_WORD_LENGTH_5 = 0x00, + WR5_TX_WORD_LENGTH_6 = 0x40, + WR5_TX_WORD_LENGTH_7 = 0x20, + WR5_TX_WORD_LENGTH_8 = 0x60, + WR5_DTR = 0x80 + }; + + void update_serial(); + void update_rts(); + void set_dtr(int state); + void set_rts(int state); + + int get_clock_mode(); + stop_bits_t get_stop_bits(); + int get_rx_word_length(); + int get_tx_word_length(); + + // receiver state + UINT8 m_rx_data_fifo[3]; // receive data FIFO + UINT8 m_rx_error_fifo[3]; // receive error FIFO + UINT8 m_rx_error; // current receive error + int m_rx_fifo; // receive FIFO pointer + + int m_rx_clock; // receive clock pulse count + int m_rx_first; // first character received + int m_rx_break; // receive break condition + UINT8 m_rx_rr0_latch; // read register 0 latched + + int m_rxd; + int m_sh; // sync hunt + int m_cts; // clear to send latch + int m_dcd; // data carrier detect latch + + // transmitter state + UINT8 m_tx_data; // transmit data register + int m_tx_clock; // transmit clock pulse count + + int m_dtr; // data terminal ready + int m_rts; // request to send + + // synchronous state + UINT16 m_sync; // sync character + + int m_index; + z80sio_device *m_uart; +}; + + +// ======================> z80sio_device + +class z80sio_device : public device_t, + public device_z80daisy_interface +{ + friend class z80sio_channel; + +public: + // construction/destruction + z80sio_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source); + z80sio_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + template static devcb_base &set_out_txda_callback(device_t &device, _Object object) { return downcast(device).m_out_txda_cb.set_callback(object); } + template static devcb_base &set_out_dtra_callback(device_t &device, _Object object) { return downcast(device).m_out_dtra_cb.set_callback(object); } + template static devcb_base &set_out_rtsa_callback(device_t &device, _Object object) { return downcast(device).m_out_rtsa_cb.set_callback(object); } + template static devcb_base &set_out_wrdya_callback(device_t &device, _Object object) { return downcast(device).m_out_wrdya_cb.set_callback(object); } + template static devcb_base &set_out_synca_callback(device_t &device, _Object object) { return downcast(device).m_out_synca_cb.set_callback(object); } + template static devcb_base &set_out_txdb_callback(device_t &device, _Object object) { return downcast(device).m_out_txdb_cb.set_callback(object); } + template static devcb_base &set_out_dtrb_callback(device_t &device, _Object object) { return downcast(device).m_out_dtrb_cb.set_callback(object); } + template static devcb_base &set_out_rtsb_callback(device_t &device, _Object object) { return downcast(device).m_out_rtsb_cb.set_callback(object); } + template static devcb_base &set_out_wrdyb_callback(device_t &device, _Object object) { return downcast(device).m_out_wrdyb_cb.set_callback(object); } + template static devcb_base &set_out_syncb_callback(device_t &device, _Object object) { return downcast(device).m_out_syncb_cb.set_callback(object); } + template static devcb_base &set_out_int_callback(device_t &device, _Object object) { return downcast(device).m_out_int_cb.set_callback(object); } + template static devcb_base &set_out_rxdrqa_callback(device_t &device, _Object object) { return downcast(device).m_out_rxdrqa_cb.set_callback(object); } + template static devcb_base &set_out_txdrqa_callback(device_t &device, _Object object) { return downcast(device).m_out_txdrqa_cb.set_callback(object); } + template static devcb_base &set_out_rxdrqb_callback(device_t &device, _Object object) { return downcast(device).m_out_rxdrqb_cb.set_callback(object); } + template static devcb_base &set_out_txdrqb_callback(device_t &device, _Object object) { return downcast(device).m_out_txdrqb_cb.set_callback(object); } + + static void configure_channels(device_t &device, int rxa, int txa, int rxb, int txb) + { + z80sio_device &dev = downcast(device); + dev.m_rxca = rxa; + dev.m_txca = txa; + dev.m_rxcb = rxb; + dev.m_txcb = txb; + } + + DECLARE_READ8_MEMBER( cd_ba_r ); + DECLARE_WRITE8_MEMBER( cd_ba_w ); + DECLARE_READ8_MEMBER( ba_cd_r ); + DECLARE_WRITE8_MEMBER( ba_cd_w ); + + DECLARE_READ8_MEMBER( da_r ) { return m_chanA->data_read(); } + DECLARE_WRITE8_MEMBER( da_w ) { m_chanA->data_write(data); } + DECLARE_READ8_MEMBER( db_r ) { return m_chanB->data_read(); } + DECLARE_WRITE8_MEMBER( db_w ) { m_chanB->data_write(data); } + + DECLARE_READ8_MEMBER( ca_r ) { return m_chanA->control_read(); } + DECLARE_WRITE8_MEMBER( ca_w ) { m_chanA->control_write(data); } + DECLARE_READ8_MEMBER( cb_r ) { return m_chanB->control_read(); } + DECLARE_WRITE8_MEMBER( cb_w ) { m_chanB->control_write(data); } + + // interrupt acknowledge + int m1_r(); + + DECLARE_WRITE_LINE_MEMBER( rxa_w ) { m_chanA->write_rx(state); } + DECLARE_WRITE_LINE_MEMBER( rxb_w ) { m_chanB->write_rx(state); } + DECLARE_WRITE_LINE_MEMBER( ctsa_w ) { m_chanA->cts_w(state); } + DECLARE_WRITE_LINE_MEMBER( ctsb_w ) { m_chanB->cts_w(state); } + DECLARE_WRITE_LINE_MEMBER( dcda_w ) { m_chanA->dcd_w(state); } + DECLARE_WRITE_LINE_MEMBER( dcdb_w ) { m_chanB->dcd_w(state); } + DECLARE_WRITE_LINE_MEMBER( ria_w ) { m_chanA->ri_w(state); } + DECLARE_WRITE_LINE_MEMBER( rib_w ) { m_chanB->ri_w(state); } + DECLARE_WRITE_LINE_MEMBER( rxca_w ) { m_chanA->rxc_w(state); } + DECLARE_WRITE_LINE_MEMBER( rxcb_w ) { m_chanB->rxc_w(state); } + DECLARE_WRITE_LINE_MEMBER( txca_w ) { m_chanA->txc_w(state); } + DECLARE_WRITE_LINE_MEMBER( txcb_w ) { m_chanB->txc_w(state); } + DECLARE_WRITE_LINE_MEMBER( rxtxcb_w ) { m_chanB->rxc_w(state); m_chanB->txc_w(state); } + DECLARE_WRITE_LINE_MEMBER( synca_w ) { m_chanA->sync_w(state); } + DECLARE_WRITE_LINE_MEMBER( syncb_w ) { m_chanB->sync_w(state); } + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual machine_config_constructor device_mconfig_additions() const; + + // device_z80daisy_interface overrides + virtual int z80daisy_irq_state(); + virtual int z80daisy_irq_ack(); + virtual void z80daisy_irq_reti(); + + // internal interrupt management + void check_interrupts(); + void reset_interrupts(); + void trigger_interrupt(int index, int state); + int get_channel_index(z80sio_channel *ch) { return (ch == m_chanA) ? 0 : 1; } + + enum + { + TYPE_Z80SIO, + }; + + enum + { + CHANNEL_A = 0, + CHANNEL_B + }; + + required_device m_chanA; + required_device m_chanB; + + // internal state + int m_rxca; + int m_txca; + int m_rxcb; + int m_txcb; + + devcb_write_line m_out_txda_cb; + devcb_write_line m_out_dtra_cb; + devcb_write_line m_out_rtsa_cb; + devcb_write_line m_out_wrdya_cb; + devcb_write_line m_out_synca_cb; + + devcb_write_line m_out_txdb_cb; + devcb_write_line m_out_dtrb_cb; + devcb_write_line m_out_rtsb_cb; + devcb_write_line m_out_wrdyb_cb; + devcb_write_line m_out_syncb_cb; + + devcb_write_line m_out_int_cb; + devcb_write_line m_out_rxdrqa_cb; + devcb_write_line m_out_txdrqa_cb; + devcb_write_line m_out_rxdrqb_cb; + devcb_write_line m_out_txdrqb_cb; + + int m_int_state[8]; // interrupt state + + int m_variant; +}; + + +// device type definition +extern const device_type Z80SIO; +extern const device_type Z80SIO_CHANNEL; +#endif From c84deae9715538277c1886ee2e10c04f5f968643 Mon Sep 17 00:00:00 2001 From: Joakim Larsson Edstrom Date: Sat, 12 Sep 2015 07:43:41 +0200 Subject: [PATCH 03/12] added z80scc device, supporting just polled asynchrounous mode at the moment --- src/emu/machine/z80scc.c | 1837 ++++++++++++++++++++++++++++++++++++++ src/emu/machine/z80scc.h | 738 +++++++++++++++ 2 files changed, 2575 insertions(+) create mode 100644 src/emu/machine/z80scc.c create mode 100644 src/emu/machine/z80scc.h diff --git a/src/emu/machine/z80scc.c b/src/emu/machine/z80scc.c new file mode 100644 index 00000000000..d498df918fd --- /dev/null +++ b/src/emu/machine/z80scc.c @@ -0,0 +1,1837 @@ +// license:BSD-3-Clause +// copyright-holders:Curt Coder, Joakim Larsson Edstrom +/*************************************************************************** + + Z80-SCC Serial Communications Controller emulation + + The z80scc is an updated version of the z80sio, with additional support for CRC + checks and a number of data link layer protocols such as HDLC, SDLC and BiSync. + (See https://en.wikipedia.org/wiki/Zilog_SCC). The variants in the SCC + family are as follows: + Zbus Universal bus + NMOS Z8030 Z8530 + CMOS Z80C30 Z85C30 + ESCC Z80230 Z85230, Z8523L (L = low voltage) + EMSCC Z85233 + The difference between Zbus and Universal bus is mainly at hardware + design level and suitable for Intel oriented (Zbus) or Motorola oriented + chip designs. + +TODO: + NMOS CMOS ESCC EMSCC + ------------------------------------------------------------------- + Channels 2 FD 2 FD 2 FD 2 FD FD = Full Duplex + Synch data rates 2Mbps 4Mbps 5Mbps 5Mbps + 1Mbps (FM) + .5Mbps (NRZI) + -- asynchrounous features ------------------------------------------- + 5-8 bit per char Y Y Y Y + 1,1.5,2 stop bits Y Y Y Y + odd/even parity Y Y Y Y + x1,x16,x32,x64 Y Y Y Y + break det/gen Y Y Y Y + parity, framing & Y Y Y Y + overrun error det Y Y Y Y + -- byte oriented synchrounous features ------------------------------- + Int/ext char sync Y Y Y Y + 1/2 synch chars Y Y Y Y + Aut CRC gen/det Y Y Y Y + -- SDLC/HDLC capabilities -------------------------------------------- + Abort seq gen/chk Y Y Y Y + Aut zero ins/det Y Y Y Y + Aut flag insert Y Y Y Y + Addr field rec Y Y Y Y + I-fld resid hand Y Y Y Y + CRC gen/det Y Y Y Y + SDLC loop w EOP Y Y Y Y + -- + Receiver FIFO 3 3 8 8 + Transmitter FIFO 1 1 4 4 + NRZ, NRZI or Y Y Y Y + FM enc/dec Y Y Y Y + Manchester dec Y Y Y Y + Baud gen per chan Y Y Y Y + DPLL clock recov Y Y Y Y + -- Additional features CMOS versions ----------------------------------- + Status FIFO N Y Y Y + SWI ack feat N Y Y Y + higher bps w ext DPLL N 32Mbps 32Mbps 32Mbps + -- Additional features 85C30 ------------------------------------------- + New WR7 feat N 85C30 Y Y + Improved SDLC N 85C30 Y Y + Improved reg handl N 85C30 Y Y + Improved auto feat N 85C30 Y Y + -- Additional features ESCC ------------------------------------------- + Progrmbl FIFO int & + DMA req lev Y Y Y Y + Improved SDLC Y Y Y Y + DPLL counter as TXc Y Y Y Y + Improved reg handl Y Y Y Y + ------------------------------------------------------------------------- + * = Features that has been implemented n/a = features that will not +***************************************************************************/ + +#include "z80scc.h" + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + +#define VERBOSE 0 + +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) +#if VERBOSE == 2 +#define logerror printf +#endif + +#define CHANA_TAG "cha" +#define CHANB_TAG "chb" + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** +// device type definition +const device_type Z80SCC = &device_creator; +const device_type Z80SCC_CHANNEL = &device_creator; +const device_type SCC8030 = &device_creator; +const device_type SCC80C30 = &device_creator; +const device_type SCC80230 = &device_creator; +const device_type SCC8530 = &device_creator; +const device_type SCC85C30 = &device_creator; +const device_type SCC85230 = &device_creator; +const device_type SCC85233 = &device_creator; +const device_type SCC8523L = &device_creator; + +//------------------------------------------------- +// device_mconfig_additions - +//------------------------------------------------- +MACHINE_CONFIG_FRAGMENT( z80scc ) + MCFG_DEVICE_ADD(CHANA_TAG, Z80SCC_CHANNEL, 0) + MCFG_DEVICE_ADD(CHANB_TAG, Z80SCC_CHANNEL, 0) +MACHINE_CONFIG_END + +machine_config_constructor z80scc_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( z80scc ); +} + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// z80scc_device - constructor +//------------------------------------------------- + +z80scc_device::z80scc_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_z80daisy_interface(mconfig, *this), + m_chanA(*this, CHANA_TAG), + m_chanB(*this, CHANB_TAG), + m_rxca(0), + m_txca(0), + m_rxcb(0), + m_txcb(0), + m_out_txda_cb(*this), + m_out_dtra_cb(*this), + m_out_rtsa_cb(*this), + m_out_wrdya_cb(*this), + m_out_synca_cb(*this), + m_out_txdb_cb(*this), + m_out_dtrb_cb(*this), + m_out_rtsb_cb(*this), + m_out_wrdyb_cb(*this), + m_out_syncb_cb(*this), + m_out_int_cb(*this), + m_out_rxdrqa_cb(*this), + m_out_txdrqa_cb(*this), + m_out_rxdrqb_cb(*this), + m_out_txdrqb_cb(*this), + m_variant(variant) +{ + for (int i = 0; i < 8; i++) + m_int_state[i] = 0; +} + +z80scc_device::z80scc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, Z80SCC, "Z80 SCC", tag, owner, clock, "z80scc", __FILE__), + device_z80daisy_interface(mconfig, *this), + m_chanA(*this, CHANA_TAG), + m_chanB(*this, CHANB_TAG), + m_rxca(0), + m_txca(0), + m_rxcb(0), + m_txcb(0), + m_out_txda_cb(*this), + m_out_dtra_cb(*this), + m_out_rtsa_cb(*this), + m_out_wrdya_cb(*this), + m_out_synca_cb(*this), + m_out_txdb_cb(*this), + m_out_dtrb_cb(*this), + m_out_rtsb_cb(*this), + m_out_wrdyb_cb(*this), + m_out_syncb_cb(*this), + m_out_int_cb(*this), + m_out_rxdrqa_cb(*this), + m_out_txdrqa_cb(*this), + m_out_rxdrqb_cb(*this), + m_out_txdrqb_cb(*this), + m_variant(TYPE_Z80SCC) +{ + for (int i = 0; i < 8; i++) + m_int_state[i] = 0; +} + +scc8030_device::scc8030_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : z80scc_device(mconfig, SCC8530, "SCC 8030", tag, owner, clock, TYPE_SCC8030, "scc8030", __FILE__){ } + +scc80C30_device::scc80C30_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : z80scc_device(mconfig, SCC85C30, "SCC 80C30", tag, owner, clock, TYPE_SCC80C30, "scc80C30", __FILE__){ } + +scc80230_device::scc80230_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : z80scc_device(mconfig, SCC85230, "SCC 80230", tag, owner, clock, TYPE_SCC80230, "scc80230", __FILE__){ } + +scc8530_device::scc8530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : z80scc_device(mconfig, SCC8530, "SCC 8530", tag, owner, clock, TYPE_SCC8530, "scc8530", __FILE__){ } + +scc85C30_device::scc85C30_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : z80scc_device(mconfig, SCC85C30, "SCC 85C30", tag, owner, clock, TYPE_SCC85C30, "scc85C30", __FILE__){ } + +scc85230_device::scc85230_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : z80scc_device(mconfig, SCC85230, "SCC 85230", tag, owner, clock, TYPE_SCC85230, "scc85230", __FILE__){ } + +scc85233_device::scc85233_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : z80scc_device(mconfig, SCC85233, "SCC 85233", tag, owner, clock, TYPE_SCC85233, "scc85233", __FILE__){ } + +scc8523L_device::scc8523L_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : z80scc_device(mconfig, SCC8523L, "SCC 8523L", tag, owner, clock, TYPE_SCC8523L, "scc8523L", __FILE__){ } + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void z80scc_device::device_start() +{ + // resolve callbacks + m_out_txda_cb.resolve_safe(); + m_out_dtra_cb.resolve_safe(); + m_out_rtsa_cb.resolve_safe(); + m_out_wrdya_cb.resolve_safe(); + m_out_synca_cb.resolve_safe(); + m_out_txdb_cb.resolve_safe(); + m_out_dtrb_cb.resolve_safe(); + m_out_rtsb_cb.resolve_safe(); + m_out_wrdyb_cb.resolve_safe(); + m_out_syncb_cb.resolve_safe(); + m_out_int_cb.resolve_safe(); + m_out_rxdrqa_cb.resolve_safe(); + m_out_txdrqa_cb.resolve_safe(); + m_out_rxdrqb_cb.resolve_safe(); + m_out_txdrqb_cb.resolve_safe(); + + // configure channel A + m_chanA->m_rxc = m_rxca; + m_chanA->m_txc = m_txca; + + // configure channel B + m_chanB->m_rxc = m_rxcb; + m_chanB->m_txc = m_txcb; + + // state saving + save_item(NAME(m_int_state)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void z80scc_device::device_reset() +{ + LOG(("Z80SCC \"%s\" Reset\n", tag())); + + m_chanA->reset(); + m_chanB->reset(); +} + +//------------------------------------------------- +// z80daisy_irq_state - get interrupt status +//------------------------------------------------- + +int z80scc_device::z80daisy_irq_state() +{ + int state = 0; + int i; + + LOG(("Z80SCC \"%s\" : Interrupt State A:%d%d%d%d B:%d%d%d%d\n", tag(), + m_int_state[0], m_int_state[1], m_int_state[2], m_int_state[3], + m_int_state[4], m_int_state[5], m_int_state[6], m_int_state[7])); + + // loop over all interrupt sources + for (i = 0; i < 8; i++) + { + // if we're servicing a request, don't indicate more interrupts + if (m_int_state[i] & Z80_DAISY_IEO) + { + state |= Z80_DAISY_IEO; + break; + } + state |= m_int_state[i]; + } + + LOG(("Z80SCC \"%s\" : Interrupt State %u\n", tag(), state)); + + return state; +} + + +//------------------------------------------------- +// z80daisy_irq_ack - interrupt acknowledge +//------------------------------------------------- + +int z80scc_device::z80daisy_irq_ack() +{ + int i; + + LOG(("Z80SCC \"%s\" Interrupt Acknowledge\n", tag())); + + // loop over all interrupt sources + for (i = 0; i < 8; i++) + { + // find the first channel with an interrupt requested + if (m_int_state[i] & Z80_DAISY_INT) + { + // clear interrupt, switch to the IEO state, and update the IRQs + m_int_state[i] = Z80_DAISY_IEO; + m_chanA->m_rr0 &= ~z80scc_channel::RR0_INTERRUPT_PENDING; + check_interrupts(); + + LOG(("Z80SCC \"%s\" : Interrupt Acknowledge Vector %02x\n", tag(), m_chanB->m_rr2)); + + return m_chanB->m_rr2; + } + } + + //logerror("z80scc_irq_ack: failed to find an interrupt to ack!\n"); + + return m_chanB->m_rr2; +} + + +//------------------------------------------------- +// z80daisy_irq_reti - return from interrupt +//------------------------------------------------- + +void z80scc_device::z80daisy_irq_reti() +{ + int i; + + LOG(("Z80SCC \"%s\" Return from Interrupt\n", tag())); + + // loop over all interrupt sources + for (i = 0; i < 8; i++) + { + // find the first channel with an IEO pending + if (m_int_state[i] & Z80_DAISY_IEO) + { + // clear the IEO state and update the IRQs + m_int_state[i] &= ~Z80_DAISY_IEO; + check_interrupts(); + return; + } + } + + //logerror("z80scc_irq_reti: failed to find an interrupt to clear IEO on!\n"); +} + + +//------------------------------------------------- +// check_interrupts - +//------------------------------------------------- + +void z80scc_device::check_interrupts() +{ + LOG(("Z80SCC \"%s\" : check_interrupts\n", m_owner->tag())); + int state = (z80daisy_irq_state() & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE; + m_out_int_cb(state); +} + + +//------------------------------------------------- +// reset_interrupts - +//------------------------------------------------- + +void z80scc_device::reset_interrupts() +{ + for (int i = 0; i < 8; i++) + { + m_int_state[i] = 0; + } + + check_interrupts(); +} + + +//------------------------------------------------- +// trigger_interrupt - +//------------------------------------------------- + +void z80scc_device::trigger_interrupt(int index, int state) +{ +#if 0 // TODO: SIO code Needs to be adapted for SCC + UINT8 vector = m_chanB->m_wr2; + int priority; + + if((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201)) + { + int prio_level = 0; + switch(state) + { + case z80scc_channel::INT_TRANSMIT: + prio_level = 1; + break; + case z80scc_channel::INT_RECEIVE: + case z80scc_channel::INT_SPECIAL: + prio_level = 0; + break; + case z80scc_channel::INT_EXTERNAL: + prio_level = 2; + break; + } + + if(m_chanA->m_wr2 & z80scc_channel::WR2_PRIORITY) + { + priority = (prio_level * 2) + index; + } + else + { + priority = (prio_level == 2) ? index + 4 : ((index * 2) + prio_level); + } + if (m_chanB->m_wr1 & z80scc_channel::WR1_STATUS_VECTOR) + { + vector = (!index << 2) | state; + if((m_chanA->m_wr1 & 0x18) == z80scc_channel::WR2_MODE_8086_8088) + { + vector = (m_chanB->m_wr2 & 0xf8) | vector; + } + else + { + vector = (m_chanB->m_wr2 & 0xe3) | (vector << 2); + } + } + } + else + { + priority = (index << 2) | state; + if (m_chanB->m_wr1 & z80scc_channel::WR1_STATUS_VECTOR) + { + // status affects vector + vector = (m_chanB->m_wr2 & 0xf1) | (!index << 3) | (state << 1); + } +// } + + LOG(("Z80SCC \"%s\" Channel %c : Interrupt Request %u\n", tag(), 'A' + index, state)); + + // update vector register + m_chanB->m_rr2 = vector; + + // trigger interrupt + m_int_state[priority] |= Z80_DAISY_INT; + m_chanA->m_rr0 |= z80scc_channel::RR0_INTERRUPT_PENDING; + + // check for interrupt + check_interrupts(); +#endif +} + + +//------------------------------------------------- +// m1_r - interrupt acknowledge +//------------------------------------------------- + +int z80scc_device::m1_r() +{ + return z80daisy_irq_ack(); +} + + +//------------------------------------------------- +// cd_ba_r - Universal Bus read +//------------------------------------------------- +READ8_MEMBER( z80scc_device::cd_ba_r ) +{ + int ba = BIT(offset, 0); + int cd = BIT(offset, 1); + z80scc_channel *channel = ba ? m_chanB : m_chanA; + + /* Expell non-Universal Bus variants */ + if ( !(m_variant & SET_Z85X3X)) + { + logerror("Z80SCC cd_ba_r not supported by this device variant, you should probably use combinations of c*_r/w and d*_r/w (see z80scc.h)\n"); + return 0; + } + + // LOG(("z80scc_device::cd_ba_r ba:%02x cd:%02x\n", ba, cd)); + return cd ? channel->control_read() : channel->data_read(); +} + +//------------------------------------------------- +// cd_ba_w - Universal Bus write +//------------------------------------------------- +WRITE8_MEMBER( z80scc_device::cd_ba_w ) +{ + int ba = BIT(offset, 0); + int cd = BIT(offset, 1); + z80scc_channel *channel = ba ? m_chanB : m_chanA; + + /* Expell non-Universal Bus variants */ + if ( !(m_variant & SET_Z85X3X) ) + { + logerror("Z80SCC cd_ba_w not supported by this device variant, you should probably use combinations of c*_r/w and d*_r/w (see z80scc.h)\n"); + return; + } + + // LOG(("z80scc_device::cd_ba_w ba:%02x cd:%02x\n", ba, cd)); + if (cd) + channel->control_write(data); + else + channel->data_write(data); +} + + +//------------------------------------------------- +// ba_cd_r - Universal Bus read +//------------------------------------------------- + +READ8_MEMBER( z80scc_device::ba_cd_r ) +{ + int ba = BIT(offset, 1); + int cd = BIT(offset, 0); + z80scc_channel *channel = ba ? m_chanB : m_chanA; + + /* Expell non-Universal Bus variants */ + if ( !(m_variant & SET_Z85X3X) ) + { + logerror("Z80SCC ba_cd_r not supported by this device variant, you should probably use combinations of c*_r/w and d*_r/w (see z80scc.h)\n"); + return 0; + } + + // LOG(("z80scc_device::ba_cd_r ba:%02x cd:%02x\n", ba, cd)); + return cd ? channel->control_read() : channel->data_read(); +} + + +//------------------------------------------------- +// ba_cd_w - +//------------------------------------------------- + +WRITE8_MEMBER( z80scc_device::ba_cd_w ) +{ + int ba = BIT(offset, 1); + int cd = BIT(offset, 0); + z80scc_channel *channel = ba ? m_chanB : m_chanA; + + /* Expell non-Universal Bus variants */ + if ( !(m_variant & SET_Z85X3X) ) + { + logerror("Z80SCC ba_cd_w not supported by this device variant, you should probably use combinations of c*_r/w and d*_r/w (see z80scc.h)\n"); + return; + } + + LOG(("z80scc_device::ba_cd_w ba:%02x cd:%02x\n", ba, cd)); + if (cd) + channel->control_write(data); + else + channel->data_write(data); +} + +//************************************************************************** +// SCC CHANNEL +//************************************************************************** + +//------------------------------------------------- +// SCC_channel - constructor +//------------------------------------------------- + +z80scc_channel::z80scc_channel(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : z80sio_channel( mconfig, tag, owner, clock) +{ + // Reset all SCC specific registers; z80sio_channel:: manages the base registers + m_rr3 = m_rr4 = m_rr5 = m_rr6 = m_rr7 = m_rr8 = m_rr9 = + m_rr10 = m_rr11 = m_rr12 = m_rr13 = m_rr14 = m_rr15 = 0; + m_wr8 = m_wr9 = m_wr10 = m_wr11 = m_wr12 = m_wr13 = m_wr14 = m_wr15; + + for (int i = 0; i < 3; i++) // TODO adapt to SCC fifos + { + m_rx_data_fifo[i] = 0; + m_rx_error_fifo[i] = 0; + } +} + + +//------------------------------------------------- +// start - channel startup +//------------------------------------------------- + +void z80scc_channel::device_start() +{ + m_uart = downcast(owner()); + LOG(("Z80SCC device_start m_uart:%p\n", m_uart)); + m_index = m_uart->get_channel_index(this); + m_ph = 0; + m_variant = ((z80scc_device *)m_owner)->m_variant; + + // state saving + // m_rr0-m_rr2 is handled by the z80sio_channel driver, our base class + save_item(NAME(m_rr3)); + save_item(NAME(m_rr4)); + save_item(NAME(m_rr5)); + save_item(NAME(m_rr6)); + save_item(NAME(m_rr7)); + save_item(NAME(m_rr8)); + save_item(NAME(m_rr9)); + save_item(NAME(m_rr10)); + save_item(NAME(m_rr11)); + save_item(NAME(m_rr12)); + save_item(NAME(m_rr13)); + save_item(NAME(m_rr14)); + save_item(NAME(m_rr15)); + // m_wr0-m_wr7 is handled by the z80sio_channel driver, our base class + save_item(NAME(m_wr8)); + save_item(NAME(m_wr9)); + save_item(NAME(m_wr10)); + save_item(NAME(m_wr11)); + save_item(NAME(m_wr12)); + save_item(NAME(m_wr13)); + save_item(NAME(m_wr14)); + save_item(NAME(m_wr15)); + save_item(NAME(m_rx_data_fifo)); + save_item(NAME(m_rx_error_fifo)); + save_item(NAME(m_rx_error)); + save_item(NAME(m_rx_fifo)); + save_item(NAME(m_rx_clock)); + save_item(NAME(m_rx_first)); + save_item(NAME(m_rx_break)); + save_item(NAME(m_rx_rr0_latch)); + save_item(NAME(m_ri)); + save_item(NAME(m_cts)); + save_item(NAME(m_dcd)); + save_item(NAME(m_tx_data)); + save_item(NAME(m_tx_clock)); + save_item(NAME(m_dtr)); + save_item(NAME(m_rts)); + save_item(NAME(m_sync)); + save_item(NAME(m_ph)); + device_serial_interface::register_save_state(machine().save(), this); +} + + +//------------------------------------------------- +// reset - reset channel status +//------------------------------------------------- + +void z80scc_channel::device_reset() +{ + LOG(("Z80SCC \"%s\" Channel %c : %s\n", m_owner->tag(), 'A' + m_index, __func__)); + + receive_register_reset(); + transmit_register_reset(); + + // TODO SIO code, check SCC compliance + // disable receiver + m_wr3 &= ~WR3_RX_ENABLE; + + // disable transmitter + m_wr5 &= ~WR5_TX_ENABLE; + m_rr0 |= RR0_TX_BUFFER_EMPTY; + m_rr1 |= RR1_ALL_SENT; + + // reset external lines + set_rts(1); + set_dtr(1); + + // reset interrupts + if (m_index == z80scc_device::CHANNEL_A) + { + m_uart->reset_interrupts(); + } +} + +void z80scc_channel::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + device_serial_interface::device_timer(timer, id, param, ptr); +} + + +//------------------------------------------------- +// tra_callback - +//------------------------------------------------- + +void z80scc_channel::tra_callback() +{ + if (!(m_wr5 & WR5_TX_ENABLE)) + { + // transmit mark + if (m_index == z80scc_device::CHANNEL_A) + m_uart->m_out_txda_cb(1); + else + m_uart->m_out_txdb_cb(1); + } + else if (m_wr5 & WR5_SEND_BREAK) + { + // transmit break + if (m_index == z80scc_device::CHANNEL_A) + m_uart->m_out_txda_cb(0); + else + m_uart->m_out_txdb_cb(0); + } + else if (!is_transmit_register_empty()) + { + // transmit data + if (m_index == z80scc_device::CHANNEL_A) + m_uart->m_out_txda_cb(transmit_register_get_data_bit()); + else + m_uart->m_out_txdb_cb(transmit_register_get_data_bit()); + } +} + + +//------------------------------------------------- +// tra_complete - +//------------------------------------------------- + +void z80scc_channel::tra_complete() +{ + if ((m_wr5 & WR5_TX_ENABLE) && !(m_wr5 & WR5_SEND_BREAK) && !(m_rr0 & RR0_TX_BUFFER_EMPTY)) + { + LOG(("Z80SCC \"%s\" Channel %c : Transmit Data Byte '%02x'\n", m_owner->tag(), 'A' + m_index, m_tx_data)); + + transmit_register_setup(m_tx_data); + + // empty transmit buffer + m_rr0 |= RR0_TX_BUFFER_EMPTY; + + if (m_wr1 & WR1_TX_INT_ENABLE) + m_uart->trigger_interrupt(m_index, INT_TRANSMIT); + } + else if (m_wr5 & WR5_SEND_BREAK) + { + // transmit break + if (m_index == z80scc_device::CHANNEL_A) + m_uart->m_out_txda_cb(0); + else + m_uart->m_out_txdb_cb(0); + } + else + { + // transmit mark + if (m_index == z80scc_device::CHANNEL_A) + m_uart->m_out_txda_cb(1); + else + m_uart->m_out_txdb_cb(1); + } + + // if transmit buffer is empty + if (m_rr0 & RR0_TX_BUFFER_EMPTY) + { + // then all characters have been sent + m_rr1 |= RR1_ALL_SENT; + + // when the RTS bit is reset, the _RTS output goes high after the transmitter empties + if (!m_rts) + set_rts(1); + } +} + + +//------------------------------------------------- +// rcv_callback - +//------------------------------------------------- + +void z80scc_channel::rcv_callback() +{ + if (m_wr3 & WR3_RX_ENABLE) + { + receive_register_update_bit(m_rxd); + } +} + + +//------------------------------------------------- +// rcv_complete - +//------------------------------------------------- + +void z80scc_channel::rcv_complete() +{ + receive_register_extract(); + receive_data(get_received_char()); +} + + +//------------------------------------------------- +// get_clock_mode - get clock divisor +//------------------------------------------------- + +int z80scc_channel::get_clock_mode() +{ + int clocks = 1; + + switch (m_wr4 & WR4_CLOCK_RATE_MASK) + { + case WR4_CLOCK_RATE_X1: clocks = 1; break; + case WR4_CLOCK_RATE_X16: clocks = 16; break; + case WR4_CLOCK_RATE_X32: clocks = 32; break; + case WR4_CLOCK_RATE_X64: clocks = 64; break; + } + + return clocks; +} + +/* From Zilog SCC/ESCC USers manual, UM010902-0609: +"/RTSA, /RTSB. Request To Send (outputs, active Low). The /RTS pins can be used as generalpurpose +outputs or with the Auto Enable feature. When used with Auto Enable ON (WR3, D5=1) +in asynchronous mode, the /RTS pin goes High after the transmitter is empty. When Auto Enable +is OFF, the /RTS pins are used as general-purpose outputs, and, they strictly follow the inverse +state of WR5, bit D1. +ESCC and 85C30: In SDLC mode, the /RTS pins can be programmed to be deasserted when the closing +flag of the message clears the TxD pin, if WR7' D2 is set." +TODO: +- SDLC mode behaviour for ESCC/85C30 +*/ +void z80scc_channel::set_rts(int state) +{ + LOG(("Z80SCC \"%s\" Channel %c : %s(%d)\n", m_owner->tag(), 'A' + m_index, __func__, state)); + if (m_index == z80scc_device::CHANNEL_A) + m_uart->m_out_rtsa_cb(state); + else + m_uart->m_out_rtsb_cb(state); +} + +void z80scc_channel::update_rts() +{ + LOG(("Z80SCC \"%s\" Channel %c : %s\n", m_owner->tag(), 'A' + m_index, __func__)); + if (m_wr5 & WR5_RTS) + { + // when the RTS bit is set, the _RTS output goes low + set_rts(0); + m_rts = 1; + } + else + { + // when the RTS bit is reset, the _RTS output goes high after the transmitter empties + m_rts = 0; + } + + // data terminal ready output follows the state programmed into the DTR bit*/ + set_dtr((m_wr5 & WR5_DTR) ? 0 : 1); +} + +//------------------------------------------------- +// get_stop_bits - get number of stop bits +//------------------------------------------------- + +device_serial_interface::stop_bits_t z80scc_channel::get_stop_bits() +{ + switch (m_wr4 & WR4_STOP_BITS_MASK) + { + case WR4_STOP_BITS_1: return STOP_BITS_1; + case WR4_STOP_BITS_1_5: return STOP_BITS_1_5; + case WR4_STOP_BITS_2: return STOP_BITS_2; + } + + return STOP_BITS_0; +} + + +//------------------------------------------------- +// get_rx_word_length - get receive word length +//------------------------------------------------- + +int z80scc_channel::get_rx_word_length() +{ + int bits = 5; + + switch (m_wr3 & WR3_RX_WORD_LENGTH_MASK) + { + case WR3_RX_WORD_LENGTH_5: bits = 5; break; + case WR3_RX_WORD_LENGTH_6: bits = 6; break; + case WR3_RX_WORD_LENGTH_7: bits = 7; break; + case WR3_RX_WORD_LENGTH_8: bits = 8; break; + } + + return bits; +} + + +//------------------------------------------------- +// get_tx_word_length - get transmit word length +//------------------------------------------------- + +int z80scc_channel::get_tx_word_length() +{ + int bits = 5; + + switch (m_wr5 & WR5_TX_WORD_LENGTH_MASK) + { + case WR5_TX_WORD_LENGTH_5: bits = 5; break; + case WR5_TX_WORD_LENGTH_6: bits = 6; break; + case WR5_TX_WORD_LENGTH_7: bits = 7; break; + case WR5_TX_WORD_LENGTH_8: bits = 8; break; + } + + return bits; +} + +/* From Zilog SCC/ESCC USers manual, UM010902-0609: +"RR2 contains the interrupt vector written into WR2. When the register is accessed in Channel A, +the vector returned is the vector actually stored in WR2. When this register is accessed in Channel +B, the vector returned includes status information in bits 1, 2 and 3 or in bits 6, 5 and 4, depending +on the state of the Status High/Status Low bit in WR9 and independent of the state of the VIS bit +in WR9."*/ +UINT8 z80scc_channel::do_sccreg_rr2() +{ + LOG(("Z80SCC %s()\n", __func__)); + return m_rr2; // TODO Check that the value is maintained according to the section above for respective channel +} + +/* From Zilog SCC/ESCC USers manual, UM010902-0609: +RR3 is the interrupt Pending register. The status of each of the interrupt Pending bits in the SCC is +reported in this register. This register exists only in Channel A. If this register is accessed in Channel +B, all 0s are returned. The two unused bits are always returned as 0. Figure displays the bit positions for RR3." +*/ +UINT8 z80scc_channel::do_sccreg_rr3() +{ + LOG(("Z80SCC %s()\n", __func__)); + return m_rr3; // TODO Update all bits of this status register +} + + +/* (ESCC and 85C30 Only) */ +/*On the ESCC, Read Register 4 reflects the contents of Write Register 4 provided the Extended + Read option is enabled. Otherwise, this register returns an image of RR0. On the NMOS/CMOS version, + a read to this location returns an image of RR0.*/ +UINT8 z80scc_channel::do_sccreg_rr4() +{ + LOG(("Z80SCC %s()\n", __func__)); + if (m_variant & (SET_ESCC | z80scc_device::TYPE_SCC85C30)) + return (BIT(m_wr7, 6) ? m_wr4 : m_rr0); + else + return m_rr0; +} + + /* (ESCC and 85C30 Only) */ +/*On the ESCC, Read Register 5 reflects the contents of Write Register 5 provided the Extended + Read option is enabled. Otherwise, this register returns an image of RR1. On the NMOS/CMOS version, + a read to this register returns an image of RR1.*/ +UINT8 z80scc_channel::do_sccreg_rr5() +{ + LOG(("Z80SCC %s()\n", __func__)); + if (m_variant & (SET_ESCC | z80scc_device::TYPE_SCC85C30)) + return BIT(m_wr7, 6) ? m_wr5 : m_rr1; + else + return m_rr1; +} + +/* (not on NMOS) + On the CMOS and ESCC, Read Register 6 contains the least significant byte of the frame byte + count that is currently at the top of the Status FIFO. RR6 is displayed in Figure on page 183. This + register is readable only if the FIFO is enabled (refer to the description Write Register 15, bit D2, + and SDLC Frame Status FIFO on page 126). Otherwise, this register is an image of RR2. + On the NMOS version, a read to this register location returns an image of RR2.*/ +UINT8 z80scc_channel::do_sccreg_rr6() +{ + LOG(("Z80SCC %s()\n", __func__)); + if (!(m_variant & (SET_NMOS))) + { + logerror("Z80SCC %s() not implemented feature\n", __func__); + return 0; + } + return m_rr2; +} + +/* (not on NMOS) + On the CMOS and ESCC, Read Register 7 contains the most significant six bits of the frame byte + count that is currently at the top of the Status FIFO. Bit D7 is the FIFO Overflow Status and bit D6 + is the FIFO Data Available Status. The status indications are given in Table on page 184. RR7 is + displayed in Figure on page 183. This register is readable only if the FIFO is enabled (refer to the + description Write Register 15, bit D2). Otherwise this register is an image of RR3. Note, for proper + operation of the FIFO and byte count logic, the registers should be read in the following order: + RR7, RR6, RR1.*/ +UINT8 z80scc_channel::do_sccreg_rr7() +{ + LOG(("Z80SCC %s()\n", __func__)); + if (!(m_variant & (SET_NMOS))) + { + logerror("Z80SCC %s() not implemented feature\n", __func__); + return 0; + } + return m_rr3; +} + +#if 0 // Short cutted in control_read() +/* RR8 is the Receive Data register. */ +UINT8 z80scc_channel::do_sccreg_rr8() +{ + return data_read(): +} +#endif + +/* (ESCC and 85C30 Only) + On the ESCC, Read Register 9 reflects the contents of Write Register 3 provided the Extended + Read option has been enabled. On the NMOS/CMOS version, a read to this location returns an image + of RR13. TODO: Check what is returned if Extended Read option is turned off */ +UINT8 z80scc_channel::do_sccreg_rr9() +{ + LOG(("Z80SCC %s()\n", __func__)); + if (m_variant & (SET_ESCC | z80scc_device::TYPE_SCC85C30)) + return BIT(m_wr7, 6) ? m_wr3 : m_rr13; + else + return m_rr13; +} + +/* RR10 contains some SDLC related miscellaneous status bits. Unused bits are always 0. */ +UINT8 z80scc_channel::do_sccreg_rr10() +{ + LOG(("Z80SCC %s()\n", __func__)); + logerror("Z80SCC %s() not implemented feature\n", __func__); + return m_rr10; +} + +/* (ESCC and 85C30 Only) + On the ESCC, Read Register 11 reflects the contents of Write Register 10 provided the Extended + Read option has been enabled. Otherwise, this register returns an image of RR15. + On the NMOS/CMOS version, a read to this location returns an image of RR15.*/ +UINT8 z80scc_channel::do_sccreg_rr11() +{ + LOG(("Z80SCC %s()\n", __func__)); + if (m_variant & (SET_ESCC | z80scc_device::TYPE_SCC85C30)) + return BIT(m_wr7, 6) ? m_wr10 : m_rr15; + else + return m_rr15; +} + +/* + RR12 returns the value stored in WR12, the lower byte of the time constant, for the BRG.*/ +UINT8 z80scc_channel::do_sccreg_rr12() +{ + return m_wr12; +} + +/* + RR13 returns the value stored in WR13, the upper byte of the time constant for the BRG. */ +UINT8 z80scc_channel::do_sccreg_rr13() +{ + return m_wr13; +} + +/* (ESCC and 85C30 Only) +On the ESCC, Read Register 14 reflects the contents of Write Register 7 Prime provided the +Extended Read option has been enabled. Otherwise, this register returns an image of RR10. +On the NMOS/CMOS version, a read to this location returns an image of RR10.*/ +UINT8 z80scc_channel::do_sccreg_rr14() +{ + LOG(("Z80SCC %s()\n", __func__)); + if (m_variant & (SET_ESCC | z80scc_device::TYPE_SCC85C30)) + return BIT(m_wr7, 6) ? m_wr7 : m_rr10; + else + return m_rr10; +} + +/* + RR15 reflects the value stored in WR15, the External/Status IE bits. The two unused bits are + always returned as Os. */ +UINT8 z80scc_channel::do_sccreg_rr15() +{ + LOG(("Z80SCC %s()\n", __func__)); + logerror("Z80SCC %s() not implemented feature\n", __func__); + return m_wr15 & 0xf5; // Mask out the used bits +} + +//------------------------------------------------- +// control_read - read control register +//------------------------------------------------- +UINT8 z80scc_channel::control_read() +{ + UINT8 data = 0; + int reg = m_wr0; + int regmask = (WR0_REGISTER_MASK | m_ph); + + // LOG(("%s(%02x) reg %02x, regmask %02x, WR0 %02x\n", __func__, data, reg, regmask, m_wr0)); + + m_ph = 0; // The "Point High" command is only valid for one access + + reg &= regmask; + + if (reg != 0) + { + // mask out register index + m_wr0 &= ~regmask; + } + + /* TODO. Sort out 80X30 limitations in register access */ + switch (reg) + { + case REG_RR0_STATUS: data = do_sioreg_rr0(); break; // TODO: verify handling of SCC specific bits: D6 and D1 + case REG_RR1_SPEC_RCV_COND: data = do_sioreg_rr1(); break; + case REG_RR2_INTERRUPT_VECT: data = do_sccreg_rr2(); break; // Channel dependent and SCC specific handling compared to SIO + /* registers 3-7 are specific to SCC. TODO: Check variant and log/stop misuse */ + case REG_RR3_INTERUPPT_PEND: data = do_sccreg_rr3(); break; + case REG_RR4_WR4_OR_RR0: data = do_sccreg_rr4(); break; + case REG_RR5_WR5_OR_RR0: data = do_sccreg_rr5(); break; + case REG_RR6_LSB_OR_RR2: data = do_sccreg_rr6(); break; + case REG_RR7_MSB_OR_RR3: data = do_sccreg_rr7(); break; + /* registers 8-15 are specific to SCC */ + case REG_RR8_RECEIVE_DATA: data = data_read(); break; + case REG_RR9_WR3_OR_RR13: data = do_sccreg_rr9(); break; + case REG_RR10_MISC_STATUS: data = do_sccreg_rr10(); break; + case REG_RR11_WR10_OR_RR15: data = do_sccreg_rr11(); break; + case REG_RR12_LO_TIME_CONST: data = do_sccreg_rr12(); break; + case REG_RR13_HI_TIME_CONST: data = do_sccreg_rr13(); break; + case REG_RR14_WR7_OR_R10: data = do_sccreg_rr14(); break; + case REG_RR15_WR15_EXT_STAT: data = do_sccreg_rr15(); break; + default: + logerror("Z80SCC \"%s\" Channel %c : Unsupported RRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); + } + + //LOG(("Z80SCC \"%s\" Channel %c : Register R%d read '%02x'\n", m_owner->tag(), 'A' + m_index, reg, data)); + + return data; +} + +/**/ +void z80scc_channel::do_sccreg_wr0(UINT8 data) +{ + m_wr0 = data; + + /* Sort out SCC specific behaviours from legacy SIO behaviour */ + /* WR0_Z_* are Z80X30 specific commands */ + switch (data & WR0_COMMAND_MASK) + { + case WR0_POINT_HIGH: + /*This command effectively adds eight to the Register Pointer (D2-D0) by allowing + WR8 through WR15 to be accessed. The Point High command and the Register + Pointer bits are written simultaneously. This command is used in the Z85X30 + version of the SCC. Note that WR0 changes form depending upon the SCC version. + Register access for the Z80X30 version of the SCC is accomplished through direct + addressing*/ + if (m_variant & SET_Z85X3X) + { + LOG(("Z80SCC \"%s\" Channel %c : %s - Point High command\n", m_owner->tag(), 'A' + m_index, __func__)); + m_ph = 8; + } + else + LOG(("Z80SCC \"%s\" Channel %c : %s - NULL command 2\n", m_owner->tag(), 'A' + m_index, __func__)); + break; + case WR0_RESET_EXT_STATUS: // TODO: Take care of the Zero Count flag and the 2 slot fifo + /*After an External/Status interrupt (a change on a modem line or a break condition, + for example), the status bits in RR0 are latched. This command re-enables the bits + and allows interrupts to occur again as a result of a status change. Latching the + status bits captures short pulses until the CPU has time to read the change. + The SCC contains simple queueing logic associated with most of the external status + bits in RR0. If another External/Status condition changes while a previous condition + is still pending (Reset External/Status Interrupt has not yet been issued) and this + condition persists until after the command is issued, this second change causes another + External/Status interrupt. However, if this second status change does not persist + (there are two transitions), another interrupt is not generated. Exceptions to this + rule are detailed in the RR0 description.*/ + do_sioreg_wr0(data); + if (!m_zc) m_rr0 |= RR0_ZC; + LOG(("Z80SCC \"%s\" Channel %c : %s - Reset External/Status Interrupt\n", m_owner->tag(), 'A' + m_index, __func__)); + break; + case WR0_RESET_HIGHEST_IUS: + /* This command resets the highest priority Interrupt Under Service (IUS) bit, allowing lower + priority conditions to request interrupts. This command allows the use of the internal + daisy chain (even in systems without an external daisy chain) and is the last operation in + an interrupt service routine.TODO: Implement internal Daisychain */ + LOG(("Z80SCC \"%s\" Channel %c : Reset Highest IUS\n", m_owner->tag(), 'A' + m_index)); + break; + case WR0_SEND_ABORT: + data &= 0xef; // convert SCC SEND_ABORT command to a SIO SEND_ABORT command and fall through + /* The following commands relies on the SIO default behviour */ + case WR0_NULL: + case WR0_ENABLE_INT_NEXT_RX: + case WR0_RESET_TX_INT: + case WR0_ERROR_RESET: + default: + do_sioreg_wr0(data); + } + do_sioreg_wr0_resets(data); + if ( m_variant & SET_Z80X30) // TODO: Implement adress decoding for Z80X30 using the shift logic described below + { + /*The registers in the Z80X30 are addressed via the address on AD7-AD0 and are latched by the rising + edge of /AS. The Shift Right/Shift Left bit in the Channel B WR0 controls which bits are + decoded to form the register address. It is placed in this register to simplify programming when the + current state of the Shift Right/Shift Left bit is not known. + A hardware reset forces Shift Left mode where the address is decoded from AD5-AD1. In Shift + Right mode, the address is decoded from AD4-AD0. The Shift Right/Shift Left bit is written via a + command to make the software writing to WR0 independent of the state of the Shift Right/Shift + Left bit. + While in the Shift Left mode, the register address is placed on AD4-AD1 and the Channel Select + bit, A/B, is decoded from AD5. The register map for this case is listed in Table on page 21. In + Shift Right mode, the register address is again placed on AD4-AD1 but the channel select A/B is + decoded from AD0. The register map for this case is listed in Table on page 23. + Because the Z80X30 does not contain 16 read registers, the decoding of the read registers is not + complete; this is listed in Table on page 21 and Table on page 23 by parentheses around the register + name. These addresses may also be used to access the read registers. Also, note that the + Z80X30 contains only one WR2 and WR9; these registers may be written from either channel. + Shift Left Mode is used when Channel A and B are to be programmed differently. This allows the + software to sequence through the registers of one channel at a time. The Shift Right Mode is used + when the channels are programmed the same. By incrementing the address, the user can program + the same data value into both the Channel A and Channel B register.*/ + switch(data & WR0_Z_SHIFT_MASK) + { + case WR0_Z_SEL_SHFT_LEFT: + LOG(("Z80SCC \"%s\" Channel %c : %s - Shift Left Addressing Mode - not implemented\n", m_owner->tag(), 'A' + m_index, __func__)); + break; + case WR0_Z_SEL_SHFT_RIGHT: + LOG(("Z80SCC \"%s\" Channel %c : %s - Shift Right Addressing Mode - not implemented\n", m_owner->tag(), 'A' + m_index, __func__)); + break; + default: + break; + // LOG(("Z80SCC \"%s\" Channel %c : %s - Null commands\n", m_owner->tag(), 'A' + m_index, __func__)); + } + } +} + +/* Write Register 1 is the control register for the various SCC interrupt and Wait/Request modes.*/ +void z80scc_channel::do_sccreg_wr1(UINT8 data) +{ + LOG(("Z80SCC \"%s\" Channel %c : %s - %02x\n", m_owner->tag(), 'A' + m_index, __func__, data)); + /* TODO: Sort out SCC specific behaviours from legacy SIO behaviours: + - Channel B only bits vs + - Parity Is Special Condition, bit2 */ + do_sioreg_wr1(data & ~0x40); // Lets SIO code handle it for now but mask out dangerous bits + m_uart->check_interrupts(); +} + +/*WR2 is the interrupt vector register. Only one vector register exists in the SCC, and it can be +accessed through either channel. The interrupt vector can be modified by status information. This +is controlled by the Vector Includes Status (VIS) and the Status High/Status Low bits in WR9.*/ +void z80scc_channel::do_sccreg_wr2(UINT8 data) +{ + LOG(("Z80SCC \"%s\" Channel %c : %s - Setting the interrupt vector to: %02x \n", m_owner->tag(), 'A' + m_index, __func__, data)); + m_wr2 = data; + m_uart->m_chanA->m_rr2 = data; + m_uart->m_chanB->m_rr2 = data; /* TODO: Sort out the setting of ChanB depending on bits in WR9 */ + + m_uart->check_interrupts(); +} + +/* + * NOTE: Register WR3-WR7 are identical to the Z80SIO so handled by z80sio.c + */ + +/* WR8 is the transmit buffer register */ +void z80scc_channel::do_sccreg_wr8(UINT8 data) +{ + LOG(("Z80SCC \"%s\" Channel %c : Transmit Buffer read %02x\n", m_owner->tag(), 'A' + m_index, data)); + data_write(data); +} + +/*WR9 is the Master Interrupt Control register and contains the Reset command bits. Only one WR9 +exists in the SCC and is accessed from either channel. The Interrupt control bits are programmed +at the same time as the Reset command, because these bits are only reset by a hardware reset */ +void z80scc_channel::do_sccreg_wr9(UINT8 data) +{ + switch (data & WR9_CMD_MASK) + { + case WR9_CMD_NORESET: + LOG(("Z80SCC \"%s\" Channel %c : Master Interrupt Control - No reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + break; + case WR9_CMD_CHNB_RESET: + LOG(("Z80SCC \"%s\" Channel %c : Master Interrupt Control - Channel B reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_uart->m_chanB->reset(); + break; + case WR9_CMD_CHNA_RESET: + LOG(("Z80SCC \"%s\" Channel %c : Master Interrupt Control - Channel A reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_uart->m_chanA->reset(); + break; + case WR9_CMD_HW_RESET: + LOG(("Z80SCC \"%s\" Channel %c : Master Interrupt Control - Device reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + /*"The effects of this command are identical to those of a hardware reset, except that the Shift Right/Shift Left bit is + not changed and the MIE, Status High/Status Low and DLC bits take the programmed values that accompany this command." + The Shift Right/Shift Left bits of the WR0 is only valid on SCC8030 device hence not implemented yet, just the SCC8530 */ + if (data & (WR9_BIT_MIE | WR9_BIT_IACK | WR9_BIT_SHSL | WR9_BIT_DLC | WR9_BIT_NV)) + logerror("Z80SCC: SCC Interrupt system not yet implemented, please be patient!\n"); + m_uart->device_reset(); + default: + logerror("Z80SCC Code is broken in WR9, please report!\n"); + } +} + +/* WR10 contains miscellaneous control bits for both the receiver and the transmitter. Bit positions +for WR10 are displayed in Figure . On the ESCC and 85C30 with the Extended Read option +enabled, this register may be read as RR11.*/ +void z80scc_channel::do_sccreg_wr10(UINT8 data) +{ + m_wr10 = data; + LOG(("Z80SCC \"%s\" Channel %c : %s Misc Tx/Rx Control %02x - not implemented \n", m_owner->tag(), 'A' + m_index, __func__, data)); +} + +/* WR11 is the Clock Mode Control register. The bits in this register control the sources of both the +receive and transmit clocks, the type of signal on the /SYNC and /RTxC pins, and the direction of +the /TRxC pin.*/ +void z80scc_channel::do_sccreg_wr11(UINT8 data) +{ + LOG(("Z80SCC \"%s\" Channel %c : %s Clock Mode Control %02x - not implemented \n", m_owner->tag(), 'A' + m_index, __func__, data)); + m_wr11 = data; + /*Bit 7: This bit controls the type of input signal the SCC expects to see on the /RTxC pin. If this bit is set + to 0, the SCC expects a TTL-compatible signal as an input to this pin. If this bit is set to 1, the SCC + connects a high-gain amplifier between the /RTxC and /SYNC pins in expectation of a quartz + crystal being placed across the pins. + The output of this oscillator is available for use as a clocking source. In this mode of operation, the + /SYNC pin is unavailable for other use. The /SYNC signal is forced to zero internally. A hardware + reset forces /NO XTAL. (At least 20 ms should be allowed after this bit is set to allow the oscillator + to stabilize.)*/ + LOG((" Clock type %s\n", data & WR11_RCVCLK_TYPE ? "Crystal oscillator between RTxC and /SYNC pins" : "TTL level on RTxC pin")); + /*Bits 6 and 5: Receiver Clock select bits 1 and 0 + These bits determine the source of the receive clock as listed below. They do not + interfere with any of the modes of operation in the SCC, but simply control a multiplexer just + before the internal receive clock input. A hardware reset forces the receive clock to come from the + /RTxC pin.*/ + LOG((" Receive clock source is: ")); + switch (data & WR11_RCVCLK_SRC_MASK) + { + case WR11_RCVCLK_SRC_RTXC: LOG(("RTxC\n")); break; + case WR11_RCVCLK_SRC_TRXC: LOG(("TRxC\n")); break; + case WR11_RCVCLK_SRC_BR: LOG(("Baudrate Generator\n")); break; + case WR11_RCVCLK_SRC_DPLL: LOG(("DPLL\n")); break; + default: logerror("Wrong!\n");/* Will not happen unless someone messes with the mask */ + } + /*Bits 4 and 3: Transmit Clock select bits 1 and 0. + These bits determine the source of the transmit clock as listed in Table . They do not interfere with + any of the modes of operation of the SCC, but simply control a multiplexer just before the internal + transmit clock input. The DPLL output that is used to feed the transmitter in FM modes lags by 90 + degrees the output of the DPLL used by the receiver. This makes the received and transmitted bit + cells occur simultaneously, neglecting delays. A hardware reset selects the /TRxC pin as the + source of the transmit clocks.*/ + LOG((" Transmit clock source is: ")); + switch (data & WR11_TRACLK_SRC_MASK) + { + case WR11_TRACLK_SRC_RTXC: LOG(("RTxC\n")); break; + case WR11_TRACLK_SRC_TRXC: LOG(("TRxC\n")); break; + case WR11_TRACLK_SRC_BR: LOG(("Baudrate Generator\n")); break; + case WR11_TRACLK_SRC_DPLL: LOG(("DPLL\n")); break; + default: logerror("Wrong!\n");/* Will not happen unless someone messes with the mask */ + } + /* Bit 2: TRxC Pin I/O control bit + This bit determines the direction of the /TRxC pin. If this bit is set to 1, the /TRxC pin is an output + and carries the signal selected by D1 and D0 of this register. However, if either the receive or the + transmit clock is programmed to come from the /TRxC pin, /TRxC is an input, regardless of the + state of this bit. The /TRxC pin is also an input if this bit is set to 0. A hardware reset forces this bit + to 0.*/ + LOG((" TRxC pin is %s\n", data & WR11_TRXC_DIRECTION ? "Output" : "Input")); + /*Bits 1 and 0: /TRxC Output Source select bits 1 and 0 + These bits determine the signal to be echoed out of the SCC via the /TRxC pin as listed in Table + on page 167. No signal is produced if /TRxC has been programmed as the source of either the + receive or the transmit clock. If /TRxC O/I (bit 2) is set to 0, these bits are ignored. + If the XTAL oscillator output is programmed to be echoed, and the XTAL oscillator is not enabled, + the /TRxC pin goes High. The DPLL signal that is echoed is the DPLL signal used by the receiver. + Hardware reset selects the XTAL oscillator as the output source*/ + LOG((" TRxC clock source is: ")); + switch (data & WR11_TRXSRC_SRC_MASK) + { + case WR11_TRXSRC_SRC_XTAL: LOG(("the Oscillator\n")); break; + case WR11_TRXSRC_SRC_TRA: LOG(("Transmit clock\n")); break; + case WR11_TRXSRC_SRC_BR: LOG(("Baudrate Generator\n")); break; + case WR11_TRXSRC_SRC_DPLL: LOG(("DPLL\n")); break; + default: logerror("Wrong!\n");/* Will not happen unless someone messes with the mask */ + } +} + +/*WR12 contains the lower byte of the time constant for the baud rate generator. The time constant +can be changed at any time, but the new value does not take effect until the next time the time constant +is loaded into the down counter. No attempt is made to synchronize the loading of the time +constant into WR12 and WR13 with the clock driving the down counter. For this reason, it is +advisable to disable the baud rate generator while the new time constant is loaded into WR12 and +WR13. Ordinarily, this is done anyway to prevent a load of the down counter between the writing +of the upper and lower bytes of the time constant. +The formula for determining the appropriate time constant for a given baud is shown below, with +the desired rate in bits per second and the BR clock period in seconds. This formula is derived +because the counter decrements from N down to zero-plus-one-cycle for reloading the time constant. +This is then fed to a toggle flip-flop to make the output a square wave. + + Time Constant = Clock Frequency / (2 * Desired Rate * Baud Rate Clock Period) - 2 + +*/ +void z80scc_channel::do_sccreg_wr12(UINT8 data) +{ + m_wr12 = data; + LOG(("Z80SCC \"%s\" Channel %c : %s %02x Low byte of Time Constant for Baudrate generator - not implemented \n", m_owner->tag(), 'A' + m_index, __func__, data)); +} + +/* WR13 contains the upper byte of the time constant for the baud rate generator. */ +void z80scc_channel::do_sccreg_wr13(UINT8 data) +{ + m_wr13 = data; + LOG(("Z80SCC \"%s\" Channel %c : %s %02x High byte of Time Constant for Baudrate generator - not implemented \n", m_owner->tag(), 'A' + m_index, __func__, data)); +} + +/* + WR14 contains some miscellaneous control bits */ +void z80scc_channel::do_sccreg_wr14(UINT8 data) +{ + switch (data & WR14_DPLL_CMD_MASK) + { + case WR14_CMD_NULL: + LOG(("Z80SCC \"%s\" Channel %c : %s Misc Control Bits Null Command %02x\n", m_owner->tag(), 'A' + m_index, __func__, data)); + break; + case WR14_CMD_ESM: +/* Issuing this command causes the DPLL to enter the Search mode, where the DPLL searches for a locking edge in the + incoming data stream. The action taken by the DPLL upon receipt of this command depends on the operating mode of + the DPLL. In NRZI mode, the output of the DPLL is High while the DPLL is waiting for an edge in the incoming data + stream. After the Search mode is entered, the first edge the DPLL sees is assumed to be a valid data edge, and + the DPLL begins the clock recovery operation from that point. The DPLL clock rate must be 32x the data rate in + NRZI mode. Upon leaving the Search mode, the first sampling edge of the DPLL occurs 16 of these 32x clocks after + the first data edge, and the second sampling occurs 48 of these 32x clocks after the first data edge. Beyond + this point, the DPLL begins normal operation, adjusting the output to remain in sync with the incoming data. + In FM mode, the output of the DPLL is Low while the DPLL is waiting for an edge in the incoming data stream. + The first edge the DPLL detects is assumed to be a valid clock edge. For this to be the case, the line must + contain only clock edges; i.e. with FM1 encoding, the line must be continuous 0s. With FM0 encoding the line must + be continuous 1s, whereas Manchester encoding requires alternating 1s and 0s on the line. The DPLL clock rate must + be 16 times the data rate in FM mode. The DPLL output causes the receiver to sample the data stream in the nominal + center of the two halves of the bit to decide whether the data was a 1 or a 0. After this command is issued, as in + NRZI mode, the DPLL starts sampling immediately after the first edge is detected. (In FM mode, the DPLL examines + the clock edge of every other bit to decide what correction must be made to remain in sync.) If the DPLL does not + see an edge during the expected window, the one clock missing bit in RR10 is set. If the DPLL does not see an edge + after two successive attempts, the two clocks missing bits in RR10 are set and the DPLL automatically enters the + Search mode. This command resets both clocks missing latches.*/ + LOG(("Z80SCC \"%s\" Channel %c : %s Misc Control Bits Enter Search Mode Command - not implemented\n", m_owner->tag(), 'A' + m_index, __func__)); + break; + case WR14_CMD_RMC: +/* Issuing this command disables the DPLL, resets the clock missing latches in RR10, and forces a continuous Search mode state */ + LOG(("Z80SCC \"%s\" Channel %c : %s Misc Control Bits Reset Missing Clocks Command - not implemented\n", m_owner->tag(), 'A' + m_index, __func__)); + break; + case WR14_CMD_DISABLE_DPLL: +/* Issuing this command disables the DPLL, resets the clock missing latches in RR10, and forces a continuous Search mode state.*/ + LOG(("Z80SCC \"%s\" Channel %c : %s Misc Control Bits Disable DPLL Command - not implemented\n", m_owner->tag(), 'A' + m_index, __func__)); + break; + case WR14_CMD_SS_BGR: +/* Issuing this command forces the clock for the DPLL to come from the output of the BRG. */ + LOG(("Z80SCC \"%s\" Channel %c : %s Misc Control Bits Baudrate Generator Input DPLL Command - not implemented\n", m_owner->tag(), 'A' + m_index, __func__)); + break; + case WR14_CMD_SS_RTXC: +/* Issuing the command forces the clock for the DPLL to come from the /RTxC pin or the crystal oscillator, depending on + the state of the XTAL/no XTAL bit in WR11. This mode is selected by a channel or hardware reset*/ + LOG(("Z80SCC \"%s\" Channel %c : %s Misc Control Bits RTxC Input DPLL Command - not implemented\n", m_owner->tag(), 'A' + m_index, __func__)); + break; + case WR14_CMD_SET_FM: +/* This command forces the DPLL to operate in the FM mode and is used to recover the clock from FM or Manchester-Encoded + data. (Manchester is decoded by placing the receiver in NRZ mode while the DPLL is in FM mode.)*/ + LOG(("Z80SCC \"%s\" Channel %c : %s Misc Control Bits Set FM Mode Command - not implemented\n", m_owner->tag(), 'A' + m_index, __func__)); + break; + case WR14_CMD_SET_NRZI: +/* Issuing this command forces the DPLL to operate in the NRZI mode. This mode is also selected by a hardware or channel reset.*/ + LOG(("Z80SCC \"%s\" Channel %c : %s Mics Control Bits Set NRZI Mode Command - not implemented\n", m_owner->tag(), 'A' + m_index, __func__)); + break; + default: + logerror("Z80SCC \"%s\" Channel %c : %s Mics Control Bits command %02x - not implemented \n", m_owner->tag(), 'A' + m_index, __func__, data); + } + // TODO: Add info on the other bits of this register + m_wr14 = data; +} + +/* WR15 is the External/Status Source Control register. If the External/Status interrupts are enabled +as a group via WR1, bits in this register control which External/Status conditions cause an interrupt. +Only the External/Status conditions that occur after the controlling bit is set to 1 cause an +interrupt. This is true, even if an External/Status condition is pending at the time the bit is set*/ +void z80scc_channel::do_sccreg_wr15(UINT8 data) +{ + LOG(("Z80SCC \"%s\" Channel %c : %s External/Status Source Control Bits %02x - not implemented \n", m_owner->tag(), 'A' + m_index, __func__, data)); + m_wr15 = data; +} + + +//------------------------------------------------- +// control_write - write control register +//------------------------------------------------- + +void z80scc_channel::control_write(UINT8 data) +{ + UINT8 reg = m_wr0; + UINT8 regmask = (WR0_REGISTER_MASK | m_ph); + + m_ph = 0; // The "Point High" command is only valid for one access + + reg &= regmask; + + if (reg != 0) + { + // mask out register index + m_wr0 &= ~regmask; + } + + LOG(("%s(%02x) reg %02x, regmask %02x, WR0 %02x\n", __func__, data, reg, regmask, m_wr0)); + + /* TODO. Sort out 80X30 & other SCC variants limitations in register access */ + switch (reg) + { + case REG_WR0_COMMAND_REGPT: do_sccreg_wr0(data); break; + case REG_WR1_INT_DMA_ENABLE: do_sccreg_wr1(data); m_uart->check_interrupts(); break; + case REG_WR2_INT_VECTOR: do_sccreg_wr2(data); break; + case REG_WR3_RX_CONTROL: do_sioreg_wr3(data); update_serial(); break; + case REG_WR4_RX_TX_MODES: do_sioreg_wr4(data); update_serial(); break; + case REG_WR5_TX_CONTROL: do_sioreg_wr5(data); update_serial(); update_rts(); break; + case REG_WR6_SYNC_OR_SDLC_A: do_sioreg_wr6(data); break; + case REG_WR7_SYNC_OR_SDLC_F: do_sioreg_wr7(data); break; + case REG_WR8_TRANSMIT_DATA: do_sccreg_wr8(data); break; + case REG_WR9_MASTER_INT_CTRL: do_sccreg_wr9(data); break; + case REG_WR10_MSC_RX_TX_CTRL: do_sccreg_wr10(data); break; + case REG_WR11_CLOCK_MODES: do_sccreg_wr11(data); break; + case REG_WR12_LO_BAUD_GEN: do_sccreg_wr12(data); break; + case REG_WR13_HI_BAUD_GEN: do_sccreg_wr13(data); break; + case REG_WR14_MISC_CTRL: do_sccreg_wr14(data); break; + case REG_WR15_EXT_ST_INT_CTRL: + LOG(("Z80SCC \"%s\" Channel %c : unsupported command: External/Status Control Bits %02x\n", m_owner->tag(), 'A' + m_index, data)); + break; + default: + logerror("Z80SCC \"%s\" Channel %c : Unsupported WRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); + } +} + + +//------------------------------------------------- +// data_read - read data register +//------------------------------------------------- + +UINT8 z80scc_channel::data_read() +{ + UINT8 data = 0; + + if (m_rx_fifo >= 0) + { + // load data from the FIFO + data = m_rx_data_fifo[m_rx_fifo]; + + // load error status from the FIFO + m_rr1 = (m_rr1 & ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR)) | m_rx_error_fifo[m_rx_fifo]; + + // decrease FIFO pointer + m_rx_fifo--; + + if (m_rx_fifo < 0) + { + // no more characters available in the FIFO + m_rr0 &= ~ RR0_RX_CHAR_AVAILABLE; + } + } + + LOG(("Z80SCC \"%s\" Channel %c : Data Register Read '%02x'\n", m_owner->tag(), 'A' + m_index, data)); + + return data; +} + + +//------------------------------------------------- +// data_write - write data register +//------------------------------------------------- + +void z80scc_channel::data_write(UINT8 data) +{ + m_tx_data = data; + + if ((m_wr5 & WR5_TX_ENABLE) && is_transmit_register_empty()) + { + LOG(("Z80SCC \"%s\" Channel %c : Transmit Data Byte '%02x'\n", m_owner->tag(), 'A' + m_index, m_tx_data)); + + transmit_register_setup(m_tx_data); + + // empty transmit buffer + m_rr0 |= RR0_TX_BUFFER_EMPTY; + + if (m_wr1 & WR1_TX_INT_ENABLE) + m_uart->trigger_interrupt(m_index, INT_TRANSMIT); + } + else + { + m_rr0 &= ~RR0_TX_BUFFER_EMPTY; + } + + m_rr1 &= ~RR1_ALL_SENT; + + LOG(("Z80SCC \"%s\" Channel %c : Data Register Write '%02x'\n", m_owner->tag(), 'A' + m_index, data)); +} + + +//------------------------------------------------- +// receive_data - receive data word +//------------------------------------------------- + +void z80scc_channel::receive_data(UINT8 data) +{ + LOG(("Z80SCC \"%s\" Channel %c : Receive Data Byte '%02x'\n", m_owner->tag(), 'A' + m_index, data)); + + if (m_rx_fifo == 2) + { + // receive overrun error detected + m_rx_error |= RR1_RX_OVERRUN_ERROR; + + switch (m_wr1 & WR1_RX_INT_MODE_MASK) + { + case WR1_RX_INT_FIRST: + if (!m_rx_first) + { + m_uart->trigger_interrupt(m_index, INT_SPECIAL); + } + break; + + case WR1_RX_INT_ALL_PARITY: + case WR1_RX_INT_ALL: + m_uart->trigger_interrupt(m_index, INT_SPECIAL); + break; + } + } + else + { + m_rx_fifo++; + } + + // store received character and error status into FIFO + m_rx_data_fifo[m_rx_fifo] = data; + m_rx_error_fifo[m_rx_fifo] = m_rx_error; + + m_rr0 |= RR0_RX_CHAR_AVAILABLE; + + // receive interrupt + switch (m_wr1 & WR1_RX_INT_MODE_MASK) + { + case WR1_RX_INT_FIRST: + if (m_rx_first) + { + m_uart->trigger_interrupt(m_index, INT_RECEIVE); + + m_rx_first = 0; + } + break; + + case WR1_RX_INT_ALL_PARITY: + case WR1_RX_INT_ALL: + m_uart->trigger_interrupt(m_index, INT_RECEIVE); + break; + } +} + + +//------------------------------------------------- +// cts_w - clear to send handler +//------------------------------------------------- + +WRITE_LINE_MEMBER( z80scc_channel::cts_w ) +{ + LOG(("Z80SCC \"%s\" Channel %c : CTS %u\n", m_owner->tag(), 'A' + m_index, state)); + + if (m_cts != state) + { + // enable transmitter if in auto enables mode + if (!state) + if (m_wr3 & WR3_AUTO_ENABLES) + m_wr5 |= WR5_TX_ENABLE; + + // set clear to send + m_cts = state; + + if (!m_rx_rr0_latch) + { + if (!m_cts) + m_rr0 |= RR0_CTS; + else + m_rr0 &= ~RR0_CTS; + + // trigger interrupt + if (m_wr1 & WR1_EXT_INT_ENABLE) + { + // trigger interrupt + m_uart->trigger_interrupt(m_index, INT_EXTERNAL); + + // latch read register 0 + m_rx_rr0_latch = 1; + } + } + } +} + + +//------------------------------------------------- +// dcd_w - data carrier detected handler +//------------------------------------------------- + +WRITE_LINE_MEMBER( z80scc_channel::dcd_w ) +{ + LOG(("Z80SCC \"%s\" Channel %c : DCD %u\n", m_owner->tag(), 'A' + m_index, state)); + + if (m_dcd != state) + { + // enable receiver if in auto enables mode + if (!state) + if (m_wr3 & WR3_AUTO_ENABLES) + m_wr3 |= WR3_RX_ENABLE; + + // set data carrier detect + m_dcd = state; + + if (!m_rx_rr0_latch) + { + if (m_dcd) + m_rr0 |= RR0_DCD; + else + m_rr0 &= ~RR0_DCD; + + if (m_wr1 & WR1_EXT_INT_ENABLE) + { + // trigger interrupt + m_uart->trigger_interrupt(m_index, INT_EXTERNAL); + + // latch read register 0 + m_rx_rr0_latch = 1; + } + } + } +} + + +//------------------------------------------------- +// ri_w - ring indicator handler +//------------------------------------------------- + +WRITE_LINE_MEMBER( z80scc_channel::ri_w ) +{ + LOG(("Z80SCC \"%s\" Channel %c : RI %u\n", m_owner->tag(), 'A' + m_index, state)); + + if (m_ri != state) + { + // set ring indicator state + m_ri = state; + + if (!m_rx_rr0_latch) + { + if (m_ri) + m_rr0 |= RR0_RI; + else + m_rr0 &= ~RR0_RI; + + if (m_wr1 & WR1_EXT_INT_ENABLE) + { + // trigger interrupt + m_uart->trigger_interrupt(m_index, INT_EXTERNAL); + + // latch read register 0 + m_rx_rr0_latch = 1; + } + } + } +} + +//------------------------------------------------- +// sync_w - sync handler +//------------------------------------------------- +WRITE_LINE_MEMBER( z80scc_channel::sync_w ) +{ + LOG(("Z80SCC \"%s\" Channel %c : SYNC %u\n", m_owner->tag(), 'A' + m_index, state)); +} + +//------------------------------------------------- +// rxc_w - receive clock +//------------------------------------------------- +WRITE_LINE_MEMBER( z80scc_channel::rxc_w ) +{ + //LOG(("Z80SCC \"%s\" Channel %c : Receiver Clock Pulse\n", m_owner->tag(), m_index + 'A')); + int clocks = get_clock_mode(); + if (clocks == 1) + rx_clock_w(state); + else if(state) + { + rx_clock_w(m_rx_clock < clocks/2); + + m_rx_clock++; + if (m_rx_clock == clocks) + m_rx_clock = 0; + + } +} + +//------------------------------------------------- +// txc_w - transmit clock +//------------------------------------------------- +WRITE_LINE_MEMBER( z80scc_channel::txc_w ) +{ + //LOG(("Z80SCC \"%s\" Channel %c : Transmitter Clock Pulse\n", m_owner->tag(), m_index + 'A')); + int clocks = get_clock_mode(); + if (clocks == 1) + tx_clock_w(state); + else if(state) + { + tx_clock_w(m_tx_clock < clocks/2); + + m_tx_clock++; + if (m_tx_clock == clocks) + m_tx_clock = 0; + + } +} + +//------------------------------------------------- +// update_serial - +//------------------------------------------------- +void z80scc_channel::update_serial() +{ + int data_bit_count = get_rx_word_length(); + stop_bits_t stop_bits = get_stop_bits(); + parity_t parity; + + LOG(("Z80SCC update serial\n")); + + if (m_wr4 & WR4_PARITY_ENABLE) + { + if (m_wr4 & WR4_PARITY_EVEN) + parity = PARITY_EVEN; + else + parity = PARITY_ODD; + } + else + parity = PARITY_NONE; + + set_data_frame(1, data_bit_count, parity, stop_bits); + + int clocks = get_clock_mode(); + + if (m_rxc > 0) + { + set_rcv_rate(m_rxc / clocks); + } + + if (m_txc > 0) + { + set_tra_rate(m_txc / clocks); + } + receive_register_reset(); // if stop bits is changed from 0, receive register has to be reset +} + +//------------------------------------------------- +// set_dtr - +//------------------------------------------------- +void z80scc_channel::set_dtr(int state) +{ + m_dtr = state; + + LOG(("Z80SCC \"%s\" Channel %c : %s(%d)\n", m_owner->tag(), 'A' + m_index, __func__, state)); + if (m_index == z80scc_device::CHANNEL_A) + m_uart->m_out_dtra_cb(m_dtr); + else + m_uart->m_out_dtrb_cb(m_dtr); +} + + + +//------------------------------------------------- +// write_rx - called by terminal through rs232/diserial +// when character is sent to board +//------------------------------------------------- +WRITE_LINE_MEMBER(z80scc_channel::write_rx) +{ + // printf("%c", state ? '+' : 'o'); + m_rxd = state; + //only use rx_w when self-clocked + if(m_rxc) + device_serial_interface::rx_w(state); +} diff --git a/src/emu/machine/z80scc.h b/src/emu/machine/z80scc.h new file mode 100644 index 00000000000..110bff86a72 --- /dev/null +++ b/src/emu/machine/z80scc.h @@ -0,0 +1,738 @@ +// license:BSD-3-Clause +// copyright-holders:Joakim Larsson Edstrom +/*************************************************************************** + + Z80-SCC Serial Communications Controller emulation + +**************************************************************************** + _____ _____ _____ _____ + AD1 1|* \_/ |40 AD0 D1 1|* \_/ |40 D0 + AD3 2| |39 AD2 D3 2| |39 D2 + AD5 3| |38 AD4 D5 3| |38 D4 + AD7 4| |37 AD6 D7 4| |37 D6 + _INT 5| |36 _DS _INT 5| |36 _RD + IEO 6| |35 _AS IEO 6| |35 _WR + IEI 7| |34 R/_W IEI 7| |34 B/_A + _INTACK 8| |33 _CS0 _INTACK 8| |33 _CE + VCC 9| |32 CS1 VCC 9| |32 C/_D + _W//REQA 10| |31 GND _W//REQA 10| |31 GND + _SYNCA 11| Z8030 |30 _W/_REQ _SYNCA 11| Z8530 |30 _W/_REQB + _RTxCA 12| Z80C30 |29 _SYNCB _RTxCA 12| Z85C30 |29 _SYNCB + RxDA 13| Z80230 |28 _RTxCB RxDA 13| Z85230 |28 _RTxCB + _TRxCA 14| |27 RxDB _TRxCA 14| |27 RxDB + TxDA 15| |26 _TRxCB TxDA 15| |26 _TRxCB + _DTR//REQA 16| |25 TxDB _DTR//REQA 16| |25 TxDB + _RTSA 17| |24 _DTR/_REQB _RTSA 17| |24 _DTR/_REQB + _CTSA 18| |23 _RTSB _CTSA 18| |23 _RTSB + _DCDA 19| |22 _CTSB _DCDA 19| |22 _CTSB + PCLK 20|_____________|21 _DCDB PCLK 20|_____________|21 _DCDB + ZBUS Universal Bus + +***************************************************************************/ + +#ifndef __Z80SCC_H__ +#define __Z80SCC_H__ + +#include "emu.h" +#include "z80sio.h" +#include "cpu/z80/z80daisy.h" + +//************************************************************************** +// DEVICE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_Z80SCC_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \ + MCFG_DEVICE_ADD(_tag, Z80SCC, _clock) \ + MCFG_Z80SCC_OFFSETS(_rxa, _txa, _rxb, _txb) + +#define MCFG_SCC8530_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \ + MCFG_DEVICE_ADD(_tag, SCC8530, _clock) \ + MCFG_Z80SCC_OFFSETS(_rxa, _txa, _rxb, _txb) + +#define MCFG_Z80SCC_OFFSETS(_rxa, _txa, _rxb, _txb) \ + z80scc_device::configure_channels(*device, _rxa, _txa, _rxb, _txb); + +// Port A callbacks +#define MCFG_Z80SCC_OUT_TXDA_CB(_devcb) \ + devcb = &z80scc_device::set_out_txda_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_DTRA_CB(_devcb) \ + devcb = &z80scc_device::set_out_dtra_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_RTSA_CB(_devcb) \ + devcb = &z80scc_device::set_out_rtsa_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_WRDYA_CB(_devcb) \ + devcb = &z80scc_device::set_out_wrdya_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_SYNCA_CB(_devcb) \ + devcb = &z80scc_device::set_out_synca_callback(*device, DEVCB_##_devcb); + +// Port B callbacks +#define MCFG_Z80SCC_OUT_TXDB_CB(_devcb) \ + devcb = &z80scc_device::set_out_txdb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_DTRB_CB(_devcb) \ + devcb = &z80scc_device::set_out_dtrb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_RTSB_CB(_devcb) \ + devcb = &z80scc_device::set_out_rtsb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_WRDYB_CB(_devcb) \ + devcb = &z80scc_device::set_out_wrdyb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_SYNCB_CB(_devcb) \ + devcb = &z80scc_device::set_out_syncb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_INT_CB(_devcb) \ + devcb = &z80scc_device::set_out_int_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_RXDRQA_CB(_devcb) \ + devcb = &z80scc_device::set_out_rxdrqa_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_TXDRQA_CB(_devcb) \ + devcb = &z80scc_device::set_out_txdrqa_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_RXDRQB_CB(_devcb) \ + devcb = &z80scc_device::set_out_rxdrqb_callback(*device, DEVCB_##_devcb); + +#define MCFG_Z80SCC_OUT_TXDRQB_CB(_devcb) \ + devcb = &z80scc_device::set_out_txdrqb_callback(*device, DEVCB_##_devcb); + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> z80scc_channel + +class z80scc_device; + +class z80scc_channel : public z80sio_channel +{ + friend class z80scc_device; + +public: + z80scc_channel(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + + // device_serial_interface overrides + virtual void tra_callback(); + virtual void tra_complete(); + virtual void rcv_callback(); + virtual void rcv_complete(); + + // read register handlers + UINT8 do_sccreg_rr0(); + UINT8 do_sccreg_rr1(); + UINT8 do_sccreg_rr2(); + UINT8 do_sccreg_rr3(); + UINT8 do_sccreg_rr4(); + UINT8 do_sccreg_rr5(); + UINT8 do_sccreg_rr6(); + UINT8 do_sccreg_rr7(); + UINT8 do_sccreg_rr8(); + UINT8 do_sccreg_rr9(); + UINT8 do_sccreg_rr10(); + UINT8 do_sccreg_rr11(); + UINT8 do_sccreg_rr12(); + UINT8 do_sccreg_rr13(); + UINT8 do_sccreg_rr14(); + UINT8 do_sccreg_rr15(); + + // write register handlers + void do_sccreg_wr0(UINT8 data); + void do_sccreg_wr1(UINT8 data); + void do_sccreg_wr2(UINT8 data); + void do_sccreg_wr3(UINT8 data); + void do_sccreg_wr4(UINT8 data); + void do_sccreg_wr5(UINT8 data); + void do_sccreg_wr6(UINT8 data); + void do_sccreg_wr7(UINT8 data); + void do_sccreg_wr8(UINT8 data); + void do_sccreg_wr9(UINT8 data); + void do_sccreg_wr10(UINT8 data); + void do_sccreg_wr11(UINT8 data); + void do_sccreg_wr12(UINT8 data); + void do_sccreg_wr13(UINT8 data); + void do_sccreg_wr14(UINT8 data); + void do_sccreg_wr15(UINT8 data); + + UINT8 control_read(); + void control_write(UINT8 data); + + UINT8 data_read(); + void data_write(UINT8 data); + + void receive_data(UINT8 data); + + DECLARE_WRITE_LINE_MEMBER( write_rx ); + DECLARE_WRITE_LINE_MEMBER( cts_w ); + DECLARE_WRITE_LINE_MEMBER( dcd_w ); + DECLARE_WRITE_LINE_MEMBER( ri_w ); + DECLARE_WRITE_LINE_MEMBER( rxc_w ); + DECLARE_WRITE_LINE_MEMBER( txc_w ); + DECLARE_WRITE_LINE_MEMBER( sync_w ); + + int m_rxc; + int m_txc; + + // Register state + // read registers enum +#if 0 //defined by z80sio.h + UINT8 m_rr0; // REG_RR0_STATUS + UINT8 m_rr1; // REG_RR1_SPEC_RCV_COND + UINT8 m_rr2; // REG_RR2_INTERRUPT_VECT +#endif + UINT8 m_rr3; // REG_RR3_INTERUPPT_PEND + UINT8 m_rr4; // REG_RR4_WR4_OR_RR0 + UINT8 m_rr5; // REG_RR5_WR5_OR_RR0 + UINT8 m_rr6; // REG_RR6_LSB_OR_RR2 + UINT8 m_rr7; // REG_RR7_MSB_OR_RR3 + UINT8 m_rr8; // REG_RR8_RECEIVE_DATA + UINT8 m_rr9; // REG_RR9_WR3_OR_RR13 + UINT8 m_rr10; // REG_RR10_MISC_STATUS + UINT8 m_rr11; // REG_RR11_WR10_OR_RR15 + UINT8 m_rr12; // REG_RR12_LO_TIME_CONST + UINT8 m_rr13; // REG_RR13_HI_TIME_CONST + UINT8 m_rr14; // REG_RR14_WR7_OR_R10 + UINT8 m_rr15; // REG_RR15_WR15_EXT_STAT + + // write registers enum +#if 0 //defined by z80sio.h + UINT8 m_wr0; // REG_WR0_COMMAND_REGPT + UINT8 m_wr1; // REG_WR1_INT_DMA_ENABLE + UINT8 m_wr2; // REG_WR2_INT_VECTOR + UINT8 m_wr3; // REG_WR3_RX_CONTROL + UINT8 m_wr4; // REG_WR4_RX_TX_MODES + UINT8 m_wr5; // REG_WR5_TX_CONTROL + UINT8 m_wr6; // REG_WR6_SYNC_OR_SDLC_A + UINT8 m_wr7; // REG_WR7_SYNC_OR_SDLC_F +#endif + UINT8 m_wr8; // REG_WR8_TRANSMIT_DATA + UINT8 m_wr9; // REG_WR9_MASTER_INT_CTRL + UINT8 m_wr10; // REG_WR10_MSC_RX_TX_CTRL + UINT8 m_wr11; // REG_WR11_CLOCK_MODES + UINT8 m_wr12; // REG_WR12_LO_BAUD_GEN + UINT8 m_wr13; // REG_WR13_HI_BAUD_GEN + UINT8 m_wr14; // REG_WR14_MISC_CTRL + UINT8 m_wr15; // REG_WR15_EXT_ST_INT_CTRL + + +protected: + enum + { + INT_TRANSMIT = 0, + INT_EXTERNAL, + INT_RECEIVE, + INT_SPECIAL + }; + + // Read registers + enum + { + REG_RR0_STATUS = 0, // SIO + REG_RR1_SPEC_RCV_COND = 1, // SIO + REG_RR2_INTERRUPT_VECT = 2, // SIO + REG_RR3_INTERUPPT_PEND = 3, + REG_RR4_WR4_OR_RR0 = 4, + REG_RR5_WR5_OR_RR0 = 5, + REG_RR6_LSB_OR_RR2 = 6, + REG_RR7_MSB_OR_RR3 = 7, + REG_RR8_RECEIVE_DATA = 8, + REG_RR9_WR3_OR_RR13 = 9, + REG_RR10_MISC_STATUS = 10, + REG_RR11_WR10_OR_RR15 = 11, + REG_RR12_LO_TIME_CONST = 12, + REG_RR13_HI_TIME_CONST = 13, + REG_RR14_WR7_OR_R10 = 14, + REG_RR15_WR15_EXT_STAT = 15 + }; + + // Write registers + enum + { + REG_WR0_COMMAND_REGPT = 0, // SIO + REG_WR1_INT_DMA_ENABLE = 1, // SIO + REG_WR2_INT_VECTOR = 2, // SIO + REG_WR3_RX_CONTROL = 3, // SIO + REG_WR4_RX_TX_MODES = 4, // SIO + REG_WR5_TX_CONTROL = 5, // SIO + REG_WR6_SYNC_OR_SDLC_A = 6, // SIO + REG_WR7_SYNC_OR_SDLC_F = 7, // SIO + REG_WR8_TRANSMIT_DATA = 8, + REG_WR9_MASTER_INT_CTRL = 9, + REG_WR10_MSC_RX_TX_CTRL = 10, + REG_WR11_CLOCK_MODES = 11, + REG_WR12_LO_BAUD_GEN = 12, + REG_WR13_HI_BAUD_GEN = 13, + REG_WR14_MISC_CTRL = 14, + REG_WR15_EXT_ST_INT_CTRL= 15 + }; + + enum + { + RR0_RX_CHAR_AVAILABLE = 0x01, // SIO bit + RR0_ZC = 0x02, // SCC bit + RR0_TX_BUFFER_EMPTY = 0x04, // SIO + RR0_DCD = 0x08, // SIO + RR0_RI = 0x10, // DART bit? TODO: investigate function and remove + RR0_SYNC_HUNT = 0x10, // SIO bit, not supported + RR0_CTS = 0x20, // SIO bit + RR0_TX_UNDERRUN = 0x40, // SIO bit, not supported + RR0_BREAK_ABORT = 0x80 // SIO bit, not supported + }; + + enum + { + RR1_ALL_SENT = 0x01, // SIO/SCC bit + RR1_RESIDUE_CODE_MASK = 0x0e, // SIO/SCC bits, not supported + RR1_PARITY_ERROR = 0x10, // SIO/SCC bits + RR1_RX_OVERRUN_ERROR = 0x20, // SIO/SCC bits + RR1_CRC_FRAMING_ERROR = 0x40, // SIO/SCC bits + RR1_END_OF_FRAME = 0x80 // SIO/SCC bits, not supported + }; + + enum + { // TODO: overload SIO functionality + RR2_INT_VECTOR_MASK = 0xff, // SCC channel A, SIO channel B (special case) + RR2_INT_VECTOR_V1 = 0x02, // SIO (special case) /SCC Channel B + RR2_INT_VECTOR_V2 = 0x04, // SIO (special case) /SCC Channel B + RR2_INT_VECTOR_V3 = 0x08 // SIO (special case) /SCC Channel B + }; + + enum + { + RR3_CHANB_EXT_IP = 0x01, // SCC IP pending registers + RR3_CHANB_TX_IP = 0x02, // only read in Channel A (for both channels) + RR3_CHANB_RX_IP = 0x04, // channel B return all zero + RR3_CHANA_EXT_IP = 0x08, + RR3_CHANA_TX_IP = 0x10, + RR3_CHANA_RX_IP = 0x20 + }; + + enum // Universal Bus WR0 commands for 85X30 + { + WR0_REGISTER_MASK = 0x07, + WR0_COMMAND_MASK = 0x38, // COMMANDS + WR0_NULL = 0x00, // 0 0 0 + WR0_POINT_HIGH = 0x08, // 0 0 1 + WR0_RESET_EXT_STATUS = 0x10, // 0 1 0 + WR0_SEND_ABORT = 0x18, // 0 1 1 + WR0_ENABLE_INT_NEXT_RX = 0x20, // 1 0 0 + WR0_RESET_TX_INT = 0x28, // 1 0 1 + WR0_ERROR_RESET = 0x30, // 1 1 0 + WR0_RESET_HIGHEST_IUS = 0x38, // 1 1 1 + WR0_CRC_RESET_CODE_MASK = 0xc0, // RESET + WR0_CRC_RESET_NULL = 0x00, // 0 0 + WR0_CRC_RESET_RX = 0x40, // 0 1 + WR0_CRC_RESET_TX = 0x80, // 1 0 + WR0_CRC_RESET_TX_UNDERRUN = 0xc0 // 1 1 + }; + + enum // ZBUS WR0 commands or 80X30 + { + WR0_Z_COMMAND_MASK = 0x38, // COMMANDS + WR0_Z_NULL_1 = 0x00, // 0 0 0 + WR0_Z_NULL_2 = 0x08, // 0 0 1 + WR0_Z_RESET_EXT_STATUS = 0x10, // 0 1 0 + WR0_Z_SEND_ABORT = 0x18, // 0 1 1 + WR0_Z_ENABLE_INT_NEXT_RX = 0x20, // 1 0 0 + WR0_Z_RESET_TX_INT = 0x28, // 1 0 1 + WR0_Z_ERROR_RESET = 0x30, // 1 1 0 + WR0_Z_RESET_HIGHEST_IUS = 0x38, // 1 1 1 + WR0_Z_SHIFT_MASK = 0x03, // SHIFT mode SDLC chan B + WR0_Z_SEL_SHFT_LEFT = 0x02, // 1 0 + WR0_Z_SEL_SHFT_RIGHT = 0x03 // 1 1 + }; + + enum + { + WR1_EXT_INT_ENABLE = 0x01, + WR1_TX_INT_ENABLE = 0x02, + WR1_STATUS_VECTOR = 0x04, + WR1_RX_INT_MODE_MASK = 0x18, + WR1_RX_INT_DISABLE = 0x00, + WR1_RX_INT_FIRST = 0x08, + WR1_RX_INT_ALL_PARITY = 0x10, // not supported + WR1_RX_INT_ALL = 0x18, + WR1_WRDY_ON_RX_TX = 0x20, // not supported + WR1_WRDY_FUNCTION = 0x40, // not supported + WR1_WRDY_ENABLE = 0x80 // not supported + }; + + enum + { + WR2_DATA_XFER_INT = 0x00, // not supported + WR2_DATA_XFER_DMA_INT = 0x01, // not supported + WR2_DATA_XFER_DMA = 0x02, // not supported + WR2_DATA_XFER_ILLEGAL = 0x03, // not supported + WR2_DATA_XFER_MASK = 0x03, // not supported + WR2_PRIORITY = 0x04, // not supported + WR2_MODE_8085_1 = 0x00, // not supported + WR2_MODE_8085_2 = 0x08, // not supported + WR2_MODE_8086_8088 = 0x10, // not supported + WR2_MODE_ILLEGAL = 0x18, // not supported + WR2_MODE_MASK = 0x18, // not supported + WR2_VECTORED_INT = 0x20, // not supported + WR2_PIN10_SYNDETB_RTSB = 0x80 // not supported + }; + + enum + { + WR3_RX_ENABLE = 0x01, + WR3_SYNC_CHAR_LOAD_INHIBIT= 0x02, // not supported + WR3_ADDRESS_SEARCH_MODE = 0x04, // not supported + WR3_RX_CRC_ENABLE = 0x08, // not supported + WR3_ENTER_HUNT_PHASE = 0x10, // not supported + WR3_AUTO_ENABLES = 0x20, + WR3_RX_WORD_LENGTH_MASK = 0xc0, + WR3_RX_WORD_LENGTH_5 = 0x00, + WR3_RX_WORD_LENGTH_7 = 0x40, + WR3_RX_WORD_LENGTH_6 = 0x80, + WR3_RX_WORD_LENGTH_8 = 0xc0 + }; + + enum + { + WR4_PARITY_ENABLE = 0x01, + WR4_PARITY_EVEN = 0x02, + WR4_STOP_BITS_MASK = 0x0c, + WR4_STOP_BITS_1 = 0x04, + WR4_STOP_BITS_1_5 = 0x08, // not supported + WR4_STOP_BITS_2 = 0x0c, + WR4_SYNC_MODE_MASK = 0x30, // not supported + WR4_SYNC_MODE_8_BIT = 0x00, // not supported + WR4_SYNC_MODE_16_BIT = 0x10, // not supported + WR4_SYNC_MODE_SDLC = 0x20, // not supported + WR4_SYNC_MODE_EXT = 0x30, // not supported + WR4_CLOCK_RATE_MASK = 0xc0, + WR4_CLOCK_RATE_X1 = 0x00, + WR4_CLOCK_RATE_X16 = 0x40, + WR4_CLOCK_RATE_X32 = 0x80, + WR4_CLOCK_RATE_X64 = 0xc0 + }; + + enum + { + WR5_TX_CRC_ENABLE = 0x01, // not supported + WR5_RTS = 0x02, + WR5_CRC16 = 0x04, // not supported + WR5_TX_ENABLE = 0x08, + WR5_SEND_BREAK = 0x10, + WR5_TX_WORD_LENGTH_MASK = 0x60, + WR5_TX_WORD_LENGTH_5 = 0x00, + WR5_TX_WORD_LENGTH_6 = 0x40, + WR5_TX_WORD_LENGTH_7 = 0x20, + WR5_TX_WORD_LENGTH_8 = 0x60, + WR5_DTR = 0x80 + }; + + /* SCC specifics */ + enum + { + WR9_CMD_MASK = 0xC0, + WR9_CMD_NORESET = 0x00, + WR9_CMD_CHNB_RESET = 0x40, + WR9_CMD_CHNA_RESET = 0x80, + WR9_CMD_HW_RESET = 0xC0, + WR9_BIT_VIS = 0x01, + WR9_BIT_NV = 0x02, + WR9_BIT_DLC = 0x04, + WR9_BIT_MIE = 0x08, + WR9_BIT_SHSL = 0x10, + WR9_BIT_IACK = 0x20 + }; + + enum + { + WR11_RCVCLK_TYPE = 0x80, + WR11_RCVCLK_SRC_MASK = 0x60, // RCV CLOCK + WR11_RCVCLK_SRC_RTXC = 0x00, // 0 0 + WR11_RCVCLK_SRC_TRXC = 0x20, // 0 1 + WR11_RCVCLK_SRC_BR = 0x40, // 1 0 + WR11_RCVCLK_SRC_DPLL = 0x60, // 1 1 + WR11_TRACLK_SRC_MASK = 0x18, // TRA CLOCK + WR11_TRACLK_SRC_RTXC = 0x00, // 0 0 + WR11_TRACLK_SRC_TRXC = 0x08, // 0 1 + WR11_TRACLK_SRC_BR = 0x10, // 1 0 + WR11_TRACLK_SRC_DPLL = 0x18, // 1 1 + WR11_TRXC_DIRECTION = 0x04, + WR11_TRXSRC_SRC_MASK = 0x03, // TRXX CLOCK + WR11_TRXSRC_SRC_XTAL = 0x00, // 0 0 + WR11_TRXSRC_SRC_TRA = 0x01, // 0 1 + WR11_TRXSRC_SRC_BR = 0x02, // 1 0 + WR11_TRXSRC_SRC_DPLL = 0x03, // 1 1 + }; + + enum + { + WR14_DPLL_CMD_MASK = 0xe0, // Command + WR14_CMD_NULL = 0x00, // 0 0 0 + WR14_CMD_ESM = 0x20, // 0 0 1 + WR14_CMD_RMC = 0x40, // 0 1 0 + WR14_CMD_DISABLE_DPLL = 0x60, // 0 1 1 + WR14_CMD_SS_BGR = 0x80, // 1 0 0 + WR14_CMD_SS_RTXC = 0xa0, // 1 0 1 + WR14_CMD_SET_FM = 0xc0, // 1 1 0 + WR14_CMD_SET_NRZI = 0xe0 // 1 1 1 + }; + + void update_serial(); + void set_dtr(int state); + void set_rts(int state); + + int get_clock_mode(); + void update_rts(); + stop_bits_t get_stop_bits(); + int get_rx_word_length(); + int get_tx_word_length(); + + // receiver state + UINT8 m_rx_data_fifo[3]; // receive data FIFO + UINT8 m_rx_error_fifo[3]; // receive error FIFO + UINT8 m_rx_error; // current receive error + int m_rx_fifo; // receive FIFO pointer + + int m_rx_clock; // receive clock pulse count + int m_rx_first; // first character received + int m_rx_break; // receive break condition + UINT8 m_rx_rr0_latch; // read register 0 latched + + int m_rxd; + int m_ri; // ring indicator latch + int m_cts; // clear to send latch + int m_dcd; // data carrier detect latch + + // transmitter state + UINT8 m_tx_data; // transmit data register + int m_tx_clock; // transmit clock pulse count + + int m_dtr; // data terminal ready + int m_rts; // request to send + + // synchronous state + UINT16 m_sync; // sync character + + // int m_index; + z80scc_device *m_uart; + + // SCC specifics + int m_ph; // Point high command to access regs 08-0f + UINT8 m_zc; +}; + + +// ======================> z80scc_device + +class z80scc_device : public device_t + ,public device_z80daisy_interface +{ + friend class z80scc_channel; + +public: + // construction/destruction + z80scc_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source); + z80scc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + template static devcb_base &set_out_txda_callback(device_t &device, _Object object) { return downcast(device).m_out_txda_cb.set_callback(object); } + template static devcb_base &set_out_dtra_callback(device_t &device, _Object object) { return downcast(device).m_out_dtra_cb.set_callback(object); } + template static devcb_base &set_out_rtsa_callback(device_t &device, _Object object) { return downcast(device).m_out_rtsa_cb.set_callback(object); } + template static devcb_base &set_out_wrdya_callback(device_t &device, _Object object) { return downcast(device).m_out_wrdya_cb.set_callback(object); } + template static devcb_base &set_out_synca_callback(device_t &device, _Object object) { return downcast(device).m_out_synca_cb.set_callback(object); } + template static devcb_base &set_out_txdb_callback(device_t &device, _Object object) { return downcast(device).m_out_txdb_cb.set_callback(object); } + template static devcb_base &set_out_dtrb_callback(device_t &device, _Object object) { return downcast(device).m_out_dtrb_cb.set_callback(object); } + template static devcb_base &set_out_rtsb_callback(device_t &device, _Object object) { return downcast(device).m_out_rtsb_cb.set_callback(object); } + template static devcb_base &set_out_wrdyb_callback(device_t &device, _Object object) { return downcast(device).m_out_wrdyb_cb.set_callback(object); } + template static devcb_base &set_out_syncb_callback(device_t &device, _Object object) { return downcast(device).m_out_syncb_cb.set_callback(object); } + template static devcb_base &set_out_int_callback(device_t &device, _Object object) { return downcast(device).m_out_int_cb.set_callback(object); } + template static devcb_base &set_out_rxdrqa_callback(device_t &device, _Object object) { return downcast(device).m_out_rxdrqa_cb.set_callback(object); } + template static devcb_base &set_out_txdrqa_callback(device_t &device, _Object object) { return downcast(device).m_out_txdrqa_cb.set_callback(object); } + template static devcb_base &set_out_rxdrqb_callback(device_t &device, _Object object) { return downcast(device).m_out_rxdrqb_cb.set_callback(object); } + template static devcb_base &set_out_txdrqb_callback(device_t &device, _Object object) { return downcast(device).m_out_txdrqb_cb.set_callback(object); } + + static void configure_channels(device_t &device, int rxa, int txa, int rxb, int txb) + { + z80scc_device &dev = downcast(device); + dev.m_rxca = rxa; + dev.m_txca = txa; + dev.m_rxcb = rxb; + dev.m_txcb = txb; + } + + DECLARE_READ8_MEMBER( cd_ba_r ); + DECLARE_WRITE8_MEMBER( cd_ba_w ); + DECLARE_READ8_MEMBER( ba_cd_r ); + DECLARE_WRITE8_MEMBER( ba_cd_w ); + + DECLARE_READ8_MEMBER( da_r ) { return m_chanA->data_read(); } + DECLARE_WRITE8_MEMBER( da_w ) { m_chanA->data_write(data); } + DECLARE_READ8_MEMBER( db_r ) { return m_chanB->data_read(); } + DECLARE_WRITE8_MEMBER( db_w ) { m_chanB->data_write(data); } + + DECLARE_READ8_MEMBER( ca_r ) { return m_chanA->control_read(); } + DECLARE_WRITE8_MEMBER( ca_w ) { m_chanA->control_write(data); } + DECLARE_READ8_MEMBER( cb_r ) { return m_chanB->control_read(); } + DECLARE_WRITE8_MEMBER( cb_w ) { m_chanB->control_write(data); } + + // interrupt acknowledge + int m1_r(); + + DECLARE_WRITE_LINE_MEMBER( rxa_w ) { m_chanA->write_rx(state); } + DECLARE_WRITE_LINE_MEMBER( rxb_w ) { m_chanB->write_rx(state); } + DECLARE_WRITE_LINE_MEMBER( ctsa_w ) { m_chanA->cts_w(state); } + DECLARE_WRITE_LINE_MEMBER( ctsb_w ) { m_chanB->cts_w(state); } + DECLARE_WRITE_LINE_MEMBER( dcda_w ) { m_chanA->dcd_w(state); } + DECLARE_WRITE_LINE_MEMBER( dcdb_w ) { m_chanB->dcd_w(state); } + DECLARE_WRITE_LINE_MEMBER( ria_w ) { m_chanA->ri_w(state); } + DECLARE_WRITE_LINE_MEMBER( rib_w ) { m_chanB->ri_w(state); } + DECLARE_WRITE_LINE_MEMBER( rxca_w ) { m_chanA->rxc_w(state); } + DECLARE_WRITE_LINE_MEMBER( rxcb_w ) { m_chanB->rxc_w(state); } + DECLARE_WRITE_LINE_MEMBER( txca_w ) { m_chanA->txc_w(state); } + DECLARE_WRITE_LINE_MEMBER( txcb_w ) { m_chanB->txc_w(state); } + DECLARE_WRITE_LINE_MEMBER( rxtxcb_w ) { m_chanB->rxc_w(state); m_chanB->txc_w(state); } + DECLARE_WRITE_LINE_MEMBER( synca_w ) { m_chanA->sync_w(state); } + DECLARE_WRITE_LINE_MEMBER( syncb_w ) { m_chanB->sync_w(state); } + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual machine_config_constructor device_mconfig_additions() const; + + // device_z80daisy_interface overrides + virtual int z80daisy_irq_state(); + virtual int z80daisy_irq_ack(); + virtual void z80daisy_irq_reti(); + + // internal interrupt management + void check_interrupts(); + void reset_interrupts(); + void trigger_interrupt(int index, int state); + int get_channel_index(z80scc_channel *ch) { return (ch == m_chanA) ? 0 : 1; } + + // Variants in the SCC family + enum + { + TYPE_Z80SCC = 0x001, + TYPE_SCC8030 = 0x002, + TYPE_SCC80C30 = 0x004, + TYPE_SCC80230 = 0x008, + TYPE_SCC8530 = 0x010, + TYPE_SCC85C30 = 0x020, + TYPE_SCC85230 = 0x040, + TYPE_SCC85233 = 0x080, + TYPE_SCC8523L = 0x100 + }; + +#define SET_NMOS ( z80scc_device::TYPE_SCC8030 | z80scc_device::TYPE_SCC8530 ) +#define SET_CMOS ( z80scc_device::TYPE_SCC80C30 | z80scc_device::TYPE_SCC85C30 ) +#define SET_ESCC ( z80scc_device::TYPE_SCC80230 | z80scc_device::TYPE_SCC85230 | z80scc_device::TYPE_SCC8523L ) +#define SET_EMSCC z80scc_device::TYPE_SCC85233 +#define SET_Z80X30 ( z80scc_device::TYPE_SCC8030 | z80scc_device::TYPE_SCC80C30 | z80scc_device::TYPE_SCC80230 ) +#define SET_Z85X3X ( z80scc_device::TYPE_SCC8530 | z80scc_device::TYPE_SCC85C30 | z80scc_device::TYPE_SCC85230 \ + | z80scc_device::TYPE_SCC8523L | z80scc_device::TYPE_SCC85233 ) + + enum + { + CHANNEL_A = 0, + CHANNEL_B + }; + + required_device m_chanA; + required_device m_chanB; + + // internal state + int m_rxca; + int m_txca; + int m_rxcb; + int m_txcb; + + devcb_write_line m_out_txda_cb; + devcb_write_line m_out_dtra_cb; + devcb_write_line m_out_rtsa_cb; + devcb_write_line m_out_wrdya_cb; + devcb_write_line m_out_synca_cb; + + devcb_write_line m_out_txdb_cb; + devcb_write_line m_out_dtrb_cb; + devcb_write_line m_out_rtsb_cb; + devcb_write_line m_out_wrdyb_cb; + devcb_write_line m_out_syncb_cb; + + devcb_write_line m_out_int_cb; + devcb_write_line m_out_rxdrqa_cb; + devcb_write_line m_out_txdrqa_cb; + devcb_write_line m_out_rxdrqb_cb; + devcb_write_line m_out_txdrqb_cb; + + int m_int_state[8]; // interrupt state + + int m_variant; +}; + +class scc8030_device : public z80scc_device +{ +public : + scc8030_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +class scc80C30_device : public z80scc_device +{ +public : + scc80C30_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +class scc80230_device : public z80scc_device +{ +public : + scc80230_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +class scc8530_device : public z80scc_device +{ +public : + scc8530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +class scc85C30_device : public z80scc_device +{ +public : + scc85C30_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +class scc85230_device : public z80scc_device +{ +public : + scc85230_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +class scc85233_device : public z80scc_device +{ +public : + scc85233_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +class scc8523L_device : public z80scc_device +{ +public : + scc8523L_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +// device type definition +extern const device_type Z80SCC; +extern const device_type Z80SCC_CHANNEL; +extern const device_type SCC8030; +extern const device_type SCC80C30; +extern const device_type SCC80230; +extern const device_type SCC8530; +extern const device_type SCC85C30; +extern const device_type SCC85230; +extern const device_type SCC85233; +extern const device_type SCC8523L; + +#endif // __Z80SCC_H__ From f377fb6a2e770c8e144a09f8c8e6d1bc42e6037f Mon Sep 17 00:00:00 2001 From: Joakim Larsson Edstrom Date: Sat, 12 Sep 2015 08:25:48 +0200 Subject: [PATCH 04/12] restored z80dart.[ch] to mame0165 --- src/devices/machine/z80dart.c | 168 +++------------------------------- src/devices/machine/z80dart.h | 84 +---------------- 2 files changed, 17 insertions(+), 235 deletions(-) diff --git a/src/devices/machine/z80dart.c b/src/devices/machine/z80dart.c index 5a3b5439247..6254d1d81f1 100644 --- a/src/devices/machine/z80dart.c +++ b/src/devices/machine/z80dart.c @@ -6,7 +6,6 @@ NEC uPD7201 Multiprotocol Serial Communications Controller emulation Z80-DART Dual Asynchronous Receiver/Transmitter emulation Z80-SIO/0/1/2/3/4 Serial Input/Output Controller emulation - Z80-SCC Serial Communications Controller emulation (experimental) The z80dart/z80sio itself is based on an older intel serial chip, the i8274 MPSC (see http://doc.chipfind.ru/pdf/intel/8274.pdf), which also has almost identical @@ -14,10 +13,6 @@ scheme which uses write register 2 on channel A, that register which is unused on the z80dart and z80sio. - The z80scc is an updated version of the z80sio, with additional support for CRC - checks and a number of data link layer protocols such as HDLC, SDLC and BiSync. - (See https://en.wikipedia.org/wiki/Zilog_SCC). - ***************************************************************************/ /* @@ -30,9 +25,8 @@ - wait/ready - 1.5 stop bits - synchronous mode (Z80-SIO/1,2) - - SDLC mode (Z80-SIO/1,2/SCC) - - HDLC, BiSync support (Z80-SCC) - - CRC support (Z80-SCC) + - SDLC mode (Z80-SIO/1,2) + */ #include "z80dart.h" @@ -66,7 +60,6 @@ const device_type Z80SIO3 = &device_creator; const device_type Z80SIO4 = &device_creator; const device_type I8274 = &device_creator; const device_type UPD7201 = &device_creator; -const device_type Z80SCC = &device_creator; //------------------------------------------------- @@ -188,11 +181,6 @@ upd7201_device::upd7201_device(const machine_config &mconfig, const char *tag, d { } -scc8530_device::scc8530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : z80dart_device(mconfig, Z80SCC, "Z80 SCC", tag, owner, clock, TYPE_Z80SCC, "z80scc", __FILE__) -{ -} - //------------------------------------------------- // device_start - device-specific startup @@ -450,10 +438,6 @@ READ8_MEMBER( z80dart_device::cd_ba_r ) int cd = BIT(offset, 1); z80dart_channel *channel = ba ? m_chanB : m_chanA; - cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC - - // LOG(("z80dart_device::cd_ba_r ba:%02x cd:%02x\n", ba, cd)); - return cd ? channel->control_read() : channel->data_read(); } @@ -468,10 +452,6 @@ WRITE8_MEMBER( z80dart_device::cd_ba_w ) int cd = BIT(offset, 1); z80dart_channel *channel = ba ? m_chanB : m_chanA; - cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC - - // LOG(("z80dart_device::cd_ba_w ba:%02x cd:%02x\n", ba, cd)); - if (cd) channel->control_write(data); else @@ -489,10 +469,6 @@ READ8_MEMBER( z80dart_device::ba_cd_r ) int cd = BIT(offset, 0); z80dart_channel *channel = ba ? m_chanB : m_chanA; - cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC - - // LOG(("z80dart_device::ba_cd_r ba:%02x cd:%02x\n", ba, cd)); - return cd ? channel->control_read() : channel->data_read(); } @@ -507,16 +483,14 @@ WRITE8_MEMBER( z80dart_device::ba_cd_w ) int cd = BIT(offset, 0); z80dart_channel *channel = ba ? m_chanB : m_chanA; - cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC - - LOG(("z80dart_device::ba_cd_w ba:%02x cd:%02x\n", ba, cd)); - if (cd) channel->control_write(data); else channel->data_write(data); } + + //************************************************************************** // DART CHANNEL //************************************************************************** @@ -544,10 +518,10 @@ z80dart_channel::z80dart_channel(const machine_config &mconfig, const char *tag, m_rts(0), m_sync(0) { - for (int i = 0; i < sizeof(m_rr); i++) + for (int i = 0; i < 3; i++) m_rr[i] = 0; - for (int i = 0; i < sizeof(m_wr); i++) + for (int i = 0; i < 6; i++) m_wr[i] = 0; for (int i = 0; i < 3; i++) @@ -566,7 +540,6 @@ void z80dart_channel::device_start() { m_uart = downcast(owner()); m_index = m_uart->get_channel_index(this); - m_ph = 0; // state saving save_item(NAME(m_rr)); @@ -587,7 +560,6 @@ void z80dart_channel::device_start() save_item(NAME(m_dtr)); save_item(NAME(m_rts)); save_item(NAME(m_sync)); - save_item(NAME(m_ph)); device_serial_interface::register_save_state(machine().save(), this); } @@ -815,17 +787,13 @@ int z80dart_channel::get_tx_word_length() UINT8 z80dart_channel::control_read() { UINT8 data = 0; - int reg = m_wr[0]; - int regmask = (WR0_REGISTER_MASK | m_ph); - m_ph = 0; // The "Point High" command is only valid for one access - - reg &= regmask; + int reg = m_wr[0] & WR0_REGISTER_MASK; if (reg != 0) { // mask out register index - m_wr[0] &= ~regmask; + m_wr[0] &= ~WR0_REGISTER_MASK; } switch (reg) @@ -840,33 +808,9 @@ UINT8 z80dart_channel::control_read() if (m_index == z80dart_device::CHANNEL_B) data = m_rr[reg]; break; - /* registers 4-7 are specific to SCC. TODO: Check variant and log/stop misuse */ - case 4: /* (ESCC and 85C30 Only) */ - /*On the ESCC, Read Register 4 reflects the contents of Write Register 4 provided the Extended - Read option is enabled. Otherwise, this register returns an image of RR0. On the NMOS/CMOS version, - a read to this location returns an image of RR0.*/ - case 5: /* (ESCC and 85C30 Only) */ - /*On the ESCC, Read Register 5 reflects the contents of Write Register 5 provided the Extended - Read option is enabled. Otherwise, this register returns an image of RR1. On the NMOS/CMOS version, - a read to this register returns an image of RR1.*/ - data = BIT(m_wr[7], 6) ? m_wr[reg] : m_rr[reg - 4]; - break; - /* registers 8-15 are specific to SCC and misuse captured by test around Point High command in control_write() */ - case 8: - data = data_read(); - break; - case 10: - data = 0; - LOG(("Z80DART Read Register 10 Misc Status Bits, SDLC related, not implemented yet.\n")); - break; - case 13: - data = m_wr[13]; - break; - default: - logerror("Z80DART \"%s\" Channel %c : Unsupported RRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); } - //LOG(("Z80DART \"%s\" Channel %c : Register R%d read '%02x'\n", m_owner->tag(), 'A' + m_index, reg, data)); + //LOG(("Z80DART \"%s\" Channel %c : Control Register Read '%02x'\n", m_owner->tag(), 'A' + m_index, data)); return data; } @@ -878,13 +822,9 @@ UINT8 z80dart_channel::control_read() void z80dart_channel::control_write(UINT8 data) { - int reg = m_wr[0]; - int regmask = (WR0_REGISTER_MASK | m_ph); - - m_ph = 0; // The "Point High" command is only valid for one access - - reg &= regmask; + int reg = m_wr[0] & WR0_REGISTER_MASK; + LOG(("Z80DART \"%s\" Channel %c : Control Register Write '%02x'\n", m_owner->tag(), 'A' + m_index, data)); // write data to selected register if (reg < 6) @@ -893,11 +833,9 @@ void z80dart_channel::control_write(UINT8 data) if (reg != 0) { // mask out register index - m_wr[0] &= ~regmask; + m_wr[0] &= ~WR0_REGISTER_MASK; } - LOG(("reg %02x, regmask %02x, WR0 %02x, data %02x\n", reg, regmask, m_wr[0], data)); - switch (reg) { case 0: @@ -907,21 +845,9 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Null\n", m_owner->tag(), 'A' + m_index)); break; - //case WR0_POINT_HIGH: // Same value so doesn't compile... case WR0_SEND_ABORT: - if (((z80dart_device *)m_owner)->m_variant == z80dart_device::TYPE_Z80SCC) - { - /* This is the Point High command for SCC, it will latch access to the high - registers for the next read or write to the control registers */ - LOG(("Z80DART \"%s\" Channel %c : Point High\n", m_owner->tag(), 'A' + m_index)); - m_ph = 8; - } - else - { - /* Send Abort is only valid for the original Z80 SIO, not the DART or SCC */ - LOG(("Z80DART \"%s\" Channel %c : Send Abort\n", m_owner->tag(), 'A' + m_index)); - logerror("Z80DART \"%s\" Channel %c : unsupported command: Send Abort\n", m_owner->tag(), 'A' + m_index); - } + LOG(("Z80DART \"%s\" Channel %c : Send Abort\n", m_owner->tag(), 'A' + m_index)); + logerror("Z80DART \"%s\" Channel %c : unsupported command: Send Abort\n", m_owner->tag(), 'A' + m_index); break; case WR0_RESET_EXT_STATUS: @@ -966,8 +892,6 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Return from Interrupt\n", m_owner->tag(), 'A' + m_index)); m_uart->z80daisy_irq_reti(); break; - default: - logerror("Z80DART \"%s\" Channel %c : Unsupported WR0 command:%02x\n", m_owner->tag(), 'A' + m_index, data & WR0_COMMAND_MASK); } break; @@ -1065,70 +989,6 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Receive Sync %02x\n", m_owner->tag(), 'A' + m_index, data)); m_sync = (data << 8) | (m_sync & 0xff); break; - /* registers 8-15 are specific to SCC and misuse captured by test around Point High command above */ - case 8: - LOG(("Z80DART \"%s\" Channel %c : Transmit Buffer read %02x\n", m_owner->tag(), 'A' + m_index, data)); - data_write(data); - break; - case 9: - switch (data & WR9_CMD_MASK) - { - case WR9_CMD_NORESET: - LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - No reset %02x\n", m_owner->tag(), 'A' + m_index, data)); - break; - case WR9_CMD_CHNB_RESET: - LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Channel B reset %02x\n", m_owner->tag(), 'A' + m_index, data)); - m_uart->m_chanB->reset(); - break; - case WR9_CMD_CHNA_RESET: - LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Channel A reset %02x\n", m_owner->tag(), 'A' + m_index, data)); - m_uart->m_chanA->reset(); - break; - case WR9_CMD_HW_RESET: - LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Device reset %02x\n", m_owner->tag(), 'A' + m_index, data)); - /*"The effects of this command are identical to those of a hardware reset, except that the Shift Right/Shift Left bit is - not changed and the MIE, Status High/Status Low and DLC bits take the programmed values that accompany this command." - The Shift Right/Shift Left bits of the WR0 is only valid on SCC8030 device hence not implemented yet, just the SCC8530 */ - if (data & (WR9_BIT_MIE | WR9_BIT_IACK | WR9_BIT_SHSL | WR9_BIT_DLC | WR9_BIT_NV)) - logerror("Z80DART: SCC Interrupt system not yet implemented, please be patient!\n"); - m_uart->device_reset(); - break; - default: - logerror("Z80DART Code is broken in WR9, please report!\n"); - } - break; - case 10: - LOG(("Z80DART \"%s\" Channel %c : unsupported command: Misc Tx/Rx Control %02x\n", m_owner->tag(), 'A' + m_index, data)); - break; - case 11: - LOG(("Z80DART \"%s\" Channel %c : incomplete command: Clock Mode Control %02x\n", m_owner->tag(), 'A' + m_index, data)); - m_wr[11] = data; - break; - case 12: - LOG(("Z80DART \"%s\" Channel %c : incomplete command: Low Byte of Baudrate Generator Time Constant %02x\n", m_owner->tag(), 'A' + m_index, data)); - m_wr[12] = data; - break; - case 13: - LOG(("Z80DART \"%s\" Channel %c : incomplete command: High Byte of Baudrate Generator Time Constant %02x\n", m_owner->tag(), 'A' + m_index, data)); - m_wr[13] = data; - break; - case 14: - switch (data & WR14_DPLL_CMD_MASK) - { - case WR14_CMD_NULL: - LOG(("Z80DART \"%s\" Channel %c : Misc Control Bits: DPLL Null Command %02x\n", m_owner->tag(), 'A' + m_index, data)); - break; - default: - logerror("Z80DART \"%s\" Channel %c : incomplete command: Misc Control Bits %02x\n", m_owner->tag(), 'A' + m_index, data); - } - // TODO: Add support for clock source to Baudrate generator - m_wr[14] = data; - break; - case 15: - LOG(("Z80DART \"%s\" Channel %c : unsupported command: External/Status Control Bits %02x\n", m_owner->tag(), 'A' + m_index, data)); - break; - default: - logerror("Z80DART \"%s\" Channel %c : Unsupported WRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); } } diff --git a/src/devices/machine/z80dart.h b/src/devices/machine/z80dart.h index c4893ef90db..2674f84758a 100644 --- a/src/devices/machine/z80dart.h +++ b/src/devices/machine/z80dart.h @@ -6,7 +6,6 @@ NEC uPD7201 Multiprotocol Serial Communications Controller emulation Z80-DART Dual Asynchronous Receiver/Transmitter emulation Z80-SIO/0/1/2/3/4 Serial Input/Output Controller emulation - Z80-SCC Serial Communications Controller emulation (experimental) **************************************************************************** _____ _____ @@ -141,29 +140,6 @@ _DCDA 19 | | 22 _DCDB CLK 20 |_____________| 21 _RESET - - _____ _____ - D1 1 |* \_/ | 40 D0 - D3 2 | | 39 D2 - D5 3 | | 38 D4 - D7 4 | | 37 D6 - _INT 5 | | 36 _RD - IEO 6 | | 35 _WR - IEI 7 | | 34 B/_A - _INTAK 8 | | 33 _CE - VCC 9 | | 32 C/_D - _W/REQA 10| Z80-SCC | 31 GND - _SYNCA 11| Z8530 | 30 _W/REQB - _RTxCA 12| | 29 _SYNCB - RxDA 13| | 28 _RTxCB - _TRxCA 14| | 27 RxDB - TxDA 15| | 26 _TRxCB - _DTR/REQA 16| | 25 TxDB - _RTSA 17| | 24 _DTR/REQB - _CTSA 18| | 23 _RTSB - _DCDA 19| | 22 _CTSB - CLK 20|_____________| 21 _DCDB - ***************************************************************************/ #ifndef __Z80DART_H__ @@ -209,10 +185,6 @@ MCFG_DEVICE_ADD(_tag, UPD7201, _clock) \ MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) -#define MCFG_Z80SCC_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \ - MCFG_DEVICE_ADD(_tag, Z80SCC, _clock) \ - MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) - #define MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) \ z80dart_device::configure_channels(*device, _rxa, _txa, _rxb, _txb); @@ -310,8 +282,8 @@ public: int m_txc; // register state - UINT8 m_rr[16]; // read registers, DART=3 SCC=16 - UINT8 m_wr[16]; // write register, DART=6 SCC=16 + UINT8 m_rr[3]; // read register + UINT8 m_wr[6]; // write register protected: enum @@ -364,13 +336,6 @@ protected: WR0_CRC_RESET_TX_UNDERRUN = 0xc0 // not supported }; - enum /* SCC specifics */ - { - WR0_REGISTER_MASK_SCC = 0x0f, - WR0_POINT_HIGH = 0x08, - - }; - enum { WR1_EXT_INT_ENABLE = 0x01, @@ -453,34 +418,6 @@ protected: WR5_DTR = 0x80 }; - /* SCC specifics */ - enum - { - WR9_CMD_MASK = 0xC0, - WR9_CMD_NORESET = 0x00, - WR9_CMD_CHNB_RESET = 0x40, - WR9_CMD_CHNA_RESET = 0x80, - WR9_CMD_HW_RESET = 0xC0, - WR9_BIT_VIS = 0x01, - WR9_BIT_NV = 0x02, - WR9_BIT_DLC = 0x04, - WR9_BIT_MIE = 0x08, - WR9_BIT_SHSL = 0x10, - WR9_BIT_IACK = 0x20 - }; - - enum - { - WR14_DPLL_CMD_MASK = 0xe0, - WR14_CMD_NULL = 0x00, - WR14_CMD_ESM = 0x20, - WR14_CMD_RMC = 0x40, - WR14_CMD_SS_BGR = 0x80, - WR14_CMD_SS_RTXC = 0xa0, - WR14_CMD_SET_FM = 0xc0, - WR14_CMD_SET_NRZI = 0xe0 - }; - void update_serial(); void set_dtr(int state); void set_rts(int state); @@ -518,9 +455,6 @@ protected: int m_index; z80dart_device *m_uart; - - // SCC specifics - int m_ph; // Point high command to access regs 08-0f }; @@ -621,8 +555,7 @@ protected: TYPE_SIO3, TYPE_SIO4, TYPE_I8274, - TYPE_UPD7201, - TYPE_Z80SCC + TYPE_UPD7201 }; enum @@ -736,16 +669,6 @@ public: }; -// ======================> scc8530_device - -class scc8530_device : public z80dart_device -{ -public : - // construction/destruction - scc8530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); -}; - - // device type definition extern const device_type Z80DART_CHANNEL; extern const device_type Z80DART; @@ -756,7 +679,6 @@ extern const device_type Z80SIO3; extern const device_type Z80SIO4; extern const device_type I8274; extern const device_type UPD7201; -extern const device_type Z80SCC; #endif From cdf1498d125294f8cc5e4cc02e58c434d9520f85 Mon Sep 17 00:00:00 2001 From: Joakim Larsson Edstrom Date: Sat, 12 Sep 2015 08:28:22 +0200 Subject: [PATCH 05/12] modified to use z80scc device instead of z80dart --- src/mess/drivers/hk68v10.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mess/drivers/hk68v10.c b/src/mess/drivers/hk68v10.c index 8e1c766bbe4..646caefc50d 100644 --- a/src/mess/drivers/hk68v10.c +++ b/src/mess/drivers/hk68v10.c @@ -165,7 +165,7 @@ #include "emu.h" #include "cpu/m68000/m68000.h" -#include "machine/z80dart.h" +#include "machine/z80scc.h" #include "bus/rs232/rs232.h" #include "machine/clock.h" @@ -329,10 +329,10 @@ MCFG_CPU_ADD ("maincpu", M68010, XTAL_10MHz) MCFG_CPU_PROGRAM_MAP (hk68v10_mem) /* Terminal Port config */ -MCFG_Z80SCC_ADD("scc", XTAL_4MHz, 0, 0, 0, 0 ) -MCFG_Z80DART_OUT_TXDA_CB(DEVWRITELINE("rs232trm", rs232_port_device, write_txd)) -MCFG_Z80DART_OUT_DTRA_CB(DEVWRITELINE("rs232trm", rs232_port_device, write_dtr)) -MCFG_Z80DART_OUT_RTSA_CB(DEVWRITELINE("rs232trm", rs232_port_device, write_rts)) +MCFG_SCC8530_ADD("scc", XTAL_4MHz, 0, 0, 0, 0 ) +MCFG_Z80SCC_OUT_TXDA_CB(DEVWRITELINE("rs232trm", rs232_port_device, write_txd)) +MCFG_Z80SCC_OUT_DTRA_CB(DEVWRITELINE("rs232trm", rs232_port_device, write_dtr)) +MCFG_Z80SCC_OUT_RTSA_CB(DEVWRITELINE("rs232trm", rs232_port_device, write_rts)) MCFG_RS232_PORT_ADD ("rs232trm", default_rs232_devices, "terminal") MCFG_RS232_RXD_HANDLER (DEVWRITELINE ("scc", scc8530_device, rxa_w)) From 13bbcd283640dbf2c60e6e1777445cc8b5bdcdc0 Mon Sep 17 00:00:00 2001 From: Joakim Larsson Edstrom Date: Sat, 12 Sep 2015 08:59:46 +0200 Subject: [PATCH 06/12] Added new devices to build --- scripts/src/machine.lua | 24 ++++++++++++++++++++++++ scripts/target/mame/mess.lua | 2 ++ src/emu/machine/z80scc.c | 19 ++++++++++++++++--- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index d864e129b47..c89a11c63b4 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -2364,6 +2364,30 @@ if (MACHINES["Z80DART"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/z80sio.h,MACHINES["Z80SIO"] = true +--------------------------------------------------- + +if (MACHINES["Z80SIO"]~=null) then + files { + MAME_DIR .. "src/devices/machine/z80sio.c", + MAME_DIR .. "src/devices/machine/z80sio.h", + } +end + +--------------------------------------------------- +-- +--@src/devices/machine/z80scc.h,MACHINES["Z80SCC"] = true +--------------------------------------------------- + +if (MACHINES["Z80SCC"]~=null) then + files { + MAME_DIR .. "src/devices/machine/z80scc.c", + MAME_DIR .. "src/devices/machine/z80scc.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/z80dma.h,MACHINES["Z80DMA"] = true diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 0d81b3892d2..40f47ccd23e 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -546,6 +546,8 @@ MACHINES["X76F100"] = true MACHINES["YM2148"] = true MACHINES["Z80CTC"] = true MACHINES["Z80DART"] = true +MACHINES["Z80SIO"] = true +MACHINES["Z80SCC"] = true MACHINES["Z80DMA"] = true MACHINES["Z80PIO"] = true MACHINES["Z80STI"] = true diff --git a/src/emu/machine/z80scc.c b/src/emu/machine/z80scc.c index d498df918fd..ed84814ae0d 100644 --- a/src/emu/machine/z80scc.c +++ b/src/emu/machine/z80scc.c @@ -558,9 +558,11 @@ z80scc_channel::z80scc_channel(const machine_config &mconfig, const char *tag, d : z80sio_channel( mconfig, tag, owner, clock) { // Reset all SCC specific registers; z80sio_channel:: manages the base registers - m_rr3 = m_rr4 = m_rr5 = m_rr6 = m_rr7 = m_rr8 = m_rr9 = - m_rr10 = m_rr11 = m_rr12 = m_rr13 = m_rr14 = m_rr15 = 0; - m_wr8 = m_wr9 = m_wr10 = m_wr11 = m_wr12 = m_wr13 = m_wr14 = m_wr15; + m_rr0 = m_rr1 = m_rr2 = + m_rr3 = m_rr4 = m_rr5 = m_rr6 = m_rr7 = m_rr8 = m_rr9 = + m_rr10 = m_rr11 = m_rr12 = m_rr13 = m_rr14 = m_rr15 = 0; + m_rr0 = m_rr1 = m_rr2 = m_rr3 = m_rr4 = m_rr5 = m_rr6 = m_rr7 = + m_wr8 = m_wr9 = m_wr10 = m_wr11 = m_wr12 = m_wr13 = m_wr14 = m_wr15; for (int i = 0; i < 3; i++) // TODO adapt to SCC fifos { @@ -584,6 +586,9 @@ void z80scc_channel::device_start() // state saving // m_rr0-m_rr2 is handled by the z80sio_channel driver, our base class + save_item(NAME(m_rr0)); + save_item(NAME(m_rr1)); + save_item(NAME(m_rr2)); save_item(NAME(m_rr3)); save_item(NAME(m_rr4)); save_item(NAME(m_rr5)); @@ -598,6 +603,14 @@ void z80scc_channel::device_start() save_item(NAME(m_rr14)); save_item(NAME(m_rr15)); // m_wr0-m_wr7 is handled by the z80sio_channel driver, our base class + save_item(NAME(m_wr0)); + save_item(NAME(m_wr1)); + save_item(NAME(m_wr2)); + save_item(NAME(m_wr3)); + save_item(NAME(m_wr4)); + save_item(NAME(m_wr5)); + save_item(NAME(m_wr6)); + save_item(NAME(m_wr7)); save_item(NAME(m_wr8)); save_item(NAME(m_wr9)); save_item(NAME(m_wr10)); From 13756895313055f5e2ed6f2690b41953d2782ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Larsson=20Edstr=C3=B6m?= Date: Sun, 30 Aug 2015 14:04:51 +0200 Subject: [PATCH 07/12] Added driver for Heurikon HK68K/V10 VME board Added some SCC support in the z80dart driver --- src/devices/machine/z80dart.c | 168 +++++++++++++++++++++++++++++++--- src/devices/machine/z80dart.h | 84 ++++++++++++++++- 2 files changed, 235 insertions(+), 17 deletions(-) diff --git a/src/devices/machine/z80dart.c b/src/devices/machine/z80dart.c index 6254d1d81f1..5a3b5439247 100644 --- a/src/devices/machine/z80dart.c +++ b/src/devices/machine/z80dart.c @@ -6,6 +6,7 @@ NEC uPD7201 Multiprotocol Serial Communications Controller emulation Z80-DART Dual Asynchronous Receiver/Transmitter emulation Z80-SIO/0/1/2/3/4 Serial Input/Output Controller emulation + Z80-SCC Serial Communications Controller emulation (experimental) The z80dart/z80sio itself is based on an older intel serial chip, the i8274 MPSC (see http://doc.chipfind.ru/pdf/intel/8274.pdf), which also has almost identical @@ -13,6 +14,10 @@ scheme which uses write register 2 on channel A, that register which is unused on the z80dart and z80sio. + The z80scc is an updated version of the z80sio, with additional support for CRC + checks and a number of data link layer protocols such as HDLC, SDLC and BiSync. + (See https://en.wikipedia.org/wiki/Zilog_SCC). + ***************************************************************************/ /* @@ -25,8 +30,9 @@ - wait/ready - 1.5 stop bits - synchronous mode (Z80-SIO/1,2) - - SDLC mode (Z80-SIO/1,2) - + - SDLC mode (Z80-SIO/1,2/SCC) + - HDLC, BiSync support (Z80-SCC) + - CRC support (Z80-SCC) */ #include "z80dart.h" @@ -60,6 +66,7 @@ const device_type Z80SIO3 = &device_creator; const device_type Z80SIO4 = &device_creator; const device_type I8274 = &device_creator; const device_type UPD7201 = &device_creator; +const device_type Z80SCC = &device_creator; //------------------------------------------------- @@ -181,6 +188,11 @@ upd7201_device::upd7201_device(const machine_config &mconfig, const char *tag, d { } +scc8530_device::scc8530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : z80dart_device(mconfig, Z80SCC, "Z80 SCC", tag, owner, clock, TYPE_Z80SCC, "z80scc", __FILE__) +{ +} + //------------------------------------------------- // device_start - device-specific startup @@ -438,6 +450,10 @@ READ8_MEMBER( z80dart_device::cd_ba_r ) int cd = BIT(offset, 1); z80dart_channel *channel = ba ? m_chanB : m_chanA; + cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC + + // LOG(("z80dart_device::cd_ba_r ba:%02x cd:%02x\n", ba, cd)); + return cd ? channel->control_read() : channel->data_read(); } @@ -452,6 +468,10 @@ WRITE8_MEMBER( z80dart_device::cd_ba_w ) int cd = BIT(offset, 1); z80dart_channel *channel = ba ? m_chanB : m_chanA; + cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC + + // LOG(("z80dart_device::cd_ba_w ba:%02x cd:%02x\n", ba, cd)); + if (cd) channel->control_write(data); else @@ -469,6 +489,10 @@ READ8_MEMBER( z80dart_device::ba_cd_r ) int cd = BIT(offset, 0); z80dart_channel *channel = ba ? m_chanB : m_chanA; + cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC + + // LOG(("z80dart_device::ba_cd_r ba:%02x cd:%02x\n", ba, cd)); + return cd ? channel->control_read() : channel->data_read(); } @@ -483,14 +507,16 @@ WRITE8_MEMBER( z80dart_device::ba_cd_w ) int cd = BIT(offset, 0); z80dart_channel *channel = ba ? m_chanB : m_chanA; + cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC + + LOG(("z80dart_device::ba_cd_w ba:%02x cd:%02x\n", ba, cd)); + if (cd) channel->control_write(data); else channel->data_write(data); } - - //************************************************************************** // DART CHANNEL //************************************************************************** @@ -518,10 +544,10 @@ z80dart_channel::z80dart_channel(const machine_config &mconfig, const char *tag, m_rts(0), m_sync(0) { - for (int i = 0; i < 3; i++) + for (int i = 0; i < sizeof(m_rr); i++) m_rr[i] = 0; - for (int i = 0; i < 6; i++) + for (int i = 0; i < sizeof(m_wr); i++) m_wr[i] = 0; for (int i = 0; i < 3; i++) @@ -540,6 +566,7 @@ void z80dart_channel::device_start() { m_uart = downcast(owner()); m_index = m_uart->get_channel_index(this); + m_ph = 0; // state saving save_item(NAME(m_rr)); @@ -560,6 +587,7 @@ void z80dart_channel::device_start() save_item(NAME(m_dtr)); save_item(NAME(m_rts)); save_item(NAME(m_sync)); + save_item(NAME(m_ph)); device_serial_interface::register_save_state(machine().save(), this); } @@ -787,13 +815,17 @@ int z80dart_channel::get_tx_word_length() UINT8 z80dart_channel::control_read() { UINT8 data = 0; + int reg = m_wr[0]; + int regmask = (WR0_REGISTER_MASK | m_ph); - int reg = m_wr[0] & WR0_REGISTER_MASK; + m_ph = 0; // The "Point High" command is only valid for one access + + reg &= regmask; if (reg != 0) { // mask out register index - m_wr[0] &= ~WR0_REGISTER_MASK; + m_wr[0] &= ~regmask; } switch (reg) @@ -808,9 +840,33 @@ UINT8 z80dart_channel::control_read() if (m_index == z80dart_device::CHANNEL_B) data = m_rr[reg]; break; + /* registers 4-7 are specific to SCC. TODO: Check variant and log/stop misuse */ + case 4: /* (ESCC and 85C30 Only) */ + /*On the ESCC, Read Register 4 reflects the contents of Write Register 4 provided the Extended + Read option is enabled. Otherwise, this register returns an image of RR0. On the NMOS/CMOS version, + a read to this location returns an image of RR0.*/ + case 5: /* (ESCC and 85C30 Only) */ + /*On the ESCC, Read Register 5 reflects the contents of Write Register 5 provided the Extended + Read option is enabled. Otherwise, this register returns an image of RR1. On the NMOS/CMOS version, + a read to this register returns an image of RR1.*/ + data = BIT(m_wr[7], 6) ? m_wr[reg] : m_rr[reg - 4]; + break; + /* registers 8-15 are specific to SCC and misuse captured by test around Point High command in control_write() */ + case 8: + data = data_read(); + break; + case 10: + data = 0; + LOG(("Z80DART Read Register 10 Misc Status Bits, SDLC related, not implemented yet.\n")); + break; + case 13: + data = m_wr[13]; + break; + default: + logerror("Z80DART \"%s\" Channel %c : Unsupported RRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); } - //LOG(("Z80DART \"%s\" Channel %c : Control Register Read '%02x'\n", m_owner->tag(), 'A' + m_index, data)); + //LOG(("Z80DART \"%s\" Channel %c : Register R%d read '%02x'\n", m_owner->tag(), 'A' + m_index, reg, data)); return data; } @@ -822,9 +878,13 @@ UINT8 z80dart_channel::control_read() void z80dart_channel::control_write(UINT8 data) { - int reg = m_wr[0] & WR0_REGISTER_MASK; + int reg = m_wr[0]; + int regmask = (WR0_REGISTER_MASK | m_ph); + + m_ph = 0; // The "Point High" command is only valid for one access + + reg &= regmask; - LOG(("Z80DART \"%s\" Channel %c : Control Register Write '%02x'\n", m_owner->tag(), 'A' + m_index, data)); // write data to selected register if (reg < 6) @@ -833,9 +893,11 @@ void z80dart_channel::control_write(UINT8 data) if (reg != 0) { // mask out register index - m_wr[0] &= ~WR0_REGISTER_MASK; + m_wr[0] &= ~regmask; } + LOG(("reg %02x, regmask %02x, WR0 %02x, data %02x\n", reg, regmask, m_wr[0], data)); + switch (reg) { case 0: @@ -845,9 +907,21 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Null\n", m_owner->tag(), 'A' + m_index)); break; + //case WR0_POINT_HIGH: // Same value so doesn't compile... case WR0_SEND_ABORT: - LOG(("Z80DART \"%s\" Channel %c : Send Abort\n", m_owner->tag(), 'A' + m_index)); - logerror("Z80DART \"%s\" Channel %c : unsupported command: Send Abort\n", m_owner->tag(), 'A' + m_index); + if (((z80dart_device *)m_owner)->m_variant == z80dart_device::TYPE_Z80SCC) + { + /* This is the Point High command for SCC, it will latch access to the high + registers for the next read or write to the control registers */ + LOG(("Z80DART \"%s\" Channel %c : Point High\n", m_owner->tag(), 'A' + m_index)); + m_ph = 8; + } + else + { + /* Send Abort is only valid for the original Z80 SIO, not the DART or SCC */ + LOG(("Z80DART \"%s\" Channel %c : Send Abort\n", m_owner->tag(), 'A' + m_index)); + logerror("Z80DART \"%s\" Channel %c : unsupported command: Send Abort\n", m_owner->tag(), 'A' + m_index); + } break; case WR0_RESET_EXT_STATUS: @@ -892,6 +966,8 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Return from Interrupt\n", m_owner->tag(), 'A' + m_index)); m_uart->z80daisy_irq_reti(); break; + default: + logerror("Z80DART \"%s\" Channel %c : Unsupported WR0 command:%02x\n", m_owner->tag(), 'A' + m_index, data & WR0_COMMAND_MASK); } break; @@ -989,6 +1065,70 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Receive Sync %02x\n", m_owner->tag(), 'A' + m_index, data)); m_sync = (data << 8) | (m_sync & 0xff); break; + /* registers 8-15 are specific to SCC and misuse captured by test around Point High command above */ + case 8: + LOG(("Z80DART \"%s\" Channel %c : Transmit Buffer read %02x\n", m_owner->tag(), 'A' + m_index, data)); + data_write(data); + break; + case 9: + switch (data & WR9_CMD_MASK) + { + case WR9_CMD_NORESET: + LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - No reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + break; + case WR9_CMD_CHNB_RESET: + LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Channel B reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_uart->m_chanB->reset(); + break; + case WR9_CMD_CHNA_RESET: + LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Channel A reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_uart->m_chanA->reset(); + break; + case WR9_CMD_HW_RESET: + LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Device reset %02x\n", m_owner->tag(), 'A' + m_index, data)); + /*"The effects of this command are identical to those of a hardware reset, except that the Shift Right/Shift Left bit is + not changed and the MIE, Status High/Status Low and DLC bits take the programmed values that accompany this command." + The Shift Right/Shift Left bits of the WR0 is only valid on SCC8030 device hence not implemented yet, just the SCC8530 */ + if (data & (WR9_BIT_MIE | WR9_BIT_IACK | WR9_BIT_SHSL | WR9_BIT_DLC | WR9_BIT_NV)) + logerror("Z80DART: SCC Interrupt system not yet implemented, please be patient!\n"); + m_uart->device_reset(); + break; + default: + logerror("Z80DART Code is broken in WR9, please report!\n"); + } + break; + case 10: + LOG(("Z80DART \"%s\" Channel %c : unsupported command: Misc Tx/Rx Control %02x\n", m_owner->tag(), 'A' + m_index, data)); + break; + case 11: + LOG(("Z80DART \"%s\" Channel %c : incomplete command: Clock Mode Control %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_wr[11] = data; + break; + case 12: + LOG(("Z80DART \"%s\" Channel %c : incomplete command: Low Byte of Baudrate Generator Time Constant %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_wr[12] = data; + break; + case 13: + LOG(("Z80DART \"%s\" Channel %c : incomplete command: High Byte of Baudrate Generator Time Constant %02x\n", m_owner->tag(), 'A' + m_index, data)); + m_wr[13] = data; + break; + case 14: + switch (data & WR14_DPLL_CMD_MASK) + { + case WR14_CMD_NULL: + LOG(("Z80DART \"%s\" Channel %c : Misc Control Bits: DPLL Null Command %02x\n", m_owner->tag(), 'A' + m_index, data)); + break; + default: + logerror("Z80DART \"%s\" Channel %c : incomplete command: Misc Control Bits %02x\n", m_owner->tag(), 'A' + m_index, data); + } + // TODO: Add support for clock source to Baudrate generator + m_wr[14] = data; + break; + case 15: + LOG(("Z80DART \"%s\" Channel %c : unsupported command: External/Status Control Bits %02x\n", m_owner->tag(), 'A' + m_index, data)); + break; + default: + logerror("Z80DART \"%s\" Channel %c : Unsupported WRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); } } diff --git a/src/devices/machine/z80dart.h b/src/devices/machine/z80dart.h index 2674f84758a..c4893ef90db 100644 --- a/src/devices/machine/z80dart.h +++ b/src/devices/machine/z80dart.h @@ -6,6 +6,7 @@ NEC uPD7201 Multiprotocol Serial Communications Controller emulation Z80-DART Dual Asynchronous Receiver/Transmitter emulation Z80-SIO/0/1/2/3/4 Serial Input/Output Controller emulation + Z80-SCC Serial Communications Controller emulation (experimental) **************************************************************************** _____ _____ @@ -140,6 +141,29 @@ _DCDA 19 | | 22 _DCDB CLK 20 |_____________| 21 _RESET + + _____ _____ + D1 1 |* \_/ | 40 D0 + D3 2 | | 39 D2 + D5 3 | | 38 D4 + D7 4 | | 37 D6 + _INT 5 | | 36 _RD + IEO 6 | | 35 _WR + IEI 7 | | 34 B/_A + _INTAK 8 | | 33 _CE + VCC 9 | | 32 C/_D + _W/REQA 10| Z80-SCC | 31 GND + _SYNCA 11| Z8530 | 30 _W/REQB + _RTxCA 12| | 29 _SYNCB + RxDA 13| | 28 _RTxCB + _TRxCA 14| | 27 RxDB + TxDA 15| | 26 _TRxCB + _DTR/REQA 16| | 25 TxDB + _RTSA 17| | 24 _DTR/REQB + _CTSA 18| | 23 _RTSB + _DCDA 19| | 22 _CTSB + CLK 20|_____________| 21 _DCDB + ***************************************************************************/ #ifndef __Z80DART_H__ @@ -185,6 +209,10 @@ MCFG_DEVICE_ADD(_tag, UPD7201, _clock) \ MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) +#define MCFG_Z80SCC_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \ + MCFG_DEVICE_ADD(_tag, Z80SCC, _clock) \ + MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) + #define MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) \ z80dart_device::configure_channels(*device, _rxa, _txa, _rxb, _txb); @@ -282,8 +310,8 @@ public: int m_txc; // register state - UINT8 m_rr[3]; // read register - UINT8 m_wr[6]; // write register + UINT8 m_rr[16]; // read registers, DART=3 SCC=16 + UINT8 m_wr[16]; // write register, DART=6 SCC=16 protected: enum @@ -336,6 +364,13 @@ protected: WR0_CRC_RESET_TX_UNDERRUN = 0xc0 // not supported }; + enum /* SCC specifics */ + { + WR0_REGISTER_MASK_SCC = 0x0f, + WR0_POINT_HIGH = 0x08, + + }; + enum { WR1_EXT_INT_ENABLE = 0x01, @@ -418,6 +453,34 @@ protected: WR5_DTR = 0x80 }; + /* SCC specifics */ + enum + { + WR9_CMD_MASK = 0xC0, + WR9_CMD_NORESET = 0x00, + WR9_CMD_CHNB_RESET = 0x40, + WR9_CMD_CHNA_RESET = 0x80, + WR9_CMD_HW_RESET = 0xC0, + WR9_BIT_VIS = 0x01, + WR9_BIT_NV = 0x02, + WR9_BIT_DLC = 0x04, + WR9_BIT_MIE = 0x08, + WR9_BIT_SHSL = 0x10, + WR9_BIT_IACK = 0x20 + }; + + enum + { + WR14_DPLL_CMD_MASK = 0xe0, + WR14_CMD_NULL = 0x00, + WR14_CMD_ESM = 0x20, + WR14_CMD_RMC = 0x40, + WR14_CMD_SS_BGR = 0x80, + WR14_CMD_SS_RTXC = 0xa0, + WR14_CMD_SET_FM = 0xc0, + WR14_CMD_SET_NRZI = 0xe0 + }; + void update_serial(); void set_dtr(int state); void set_rts(int state); @@ -455,6 +518,9 @@ protected: int m_index; z80dart_device *m_uart; + + // SCC specifics + int m_ph; // Point high command to access regs 08-0f }; @@ -555,7 +621,8 @@ protected: TYPE_SIO3, TYPE_SIO4, TYPE_I8274, - TYPE_UPD7201 + TYPE_UPD7201, + TYPE_Z80SCC }; enum @@ -669,6 +736,16 @@ public: }; +// ======================> scc8530_device + +class scc8530_device : public z80dart_device +{ +public : + // construction/destruction + scc8530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + + // device type definition extern const device_type Z80DART_CHANNEL; extern const device_type Z80DART; @@ -679,6 +756,7 @@ extern const device_type Z80SIO3; extern const device_type Z80SIO4; extern const device_type I8274; extern const device_type UPD7201; +extern const device_type Z80SCC; #endif From 12a7a1c7e280238c59fbaedd60d7b4297503e72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Larsson=20Edstr=C3=B6m?= Date: Sun, 13 Sep 2015 20:19:25 +0200 Subject: [PATCH 08/12] moved z80sio and z80scc devices to src/devices --- src/{emu => devices}/machine/z80scc.c | 0 src/{emu => devices}/machine/z80scc.h | 0 src/{emu => devices}/machine/z80sio.c | 0 src/{emu => devices}/machine/z80sio.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/{emu => devices}/machine/z80scc.c (100%) rename src/{emu => devices}/machine/z80scc.h (100%) rename src/{emu => devices}/machine/z80sio.c (100%) rename src/{emu => devices}/machine/z80sio.h (100%) diff --git a/src/emu/machine/z80scc.c b/src/devices/machine/z80scc.c similarity index 100% rename from src/emu/machine/z80scc.c rename to src/devices/machine/z80scc.c diff --git a/src/emu/machine/z80scc.h b/src/devices/machine/z80scc.h similarity index 100% rename from src/emu/machine/z80scc.h rename to src/devices/machine/z80scc.h diff --git a/src/emu/machine/z80sio.c b/src/devices/machine/z80sio.c similarity index 100% rename from src/emu/machine/z80sio.c rename to src/devices/machine/z80sio.c diff --git a/src/emu/machine/z80sio.h b/src/devices/machine/z80sio.h similarity index 100% rename from src/emu/machine/z80sio.h rename to src/devices/machine/z80sio.h From fc2ce6db31fd85c7e2c2543663c46e6727b51fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Larsson=20Edstr=C3=B6m?= Date: Sun, 13 Sep 2015 20:31:22 +0200 Subject: [PATCH 09/12] reverted to version of z80dart prior this patch --- src/devices/machine/z80dart.c | 168 +++------------------------------- src/devices/machine/z80dart.h | 84 +---------------- 2 files changed, 17 insertions(+), 235 deletions(-) diff --git a/src/devices/machine/z80dart.c b/src/devices/machine/z80dart.c index 5a3b5439247..6254d1d81f1 100644 --- a/src/devices/machine/z80dart.c +++ b/src/devices/machine/z80dart.c @@ -6,7 +6,6 @@ NEC uPD7201 Multiprotocol Serial Communications Controller emulation Z80-DART Dual Asynchronous Receiver/Transmitter emulation Z80-SIO/0/1/2/3/4 Serial Input/Output Controller emulation - Z80-SCC Serial Communications Controller emulation (experimental) The z80dart/z80sio itself is based on an older intel serial chip, the i8274 MPSC (see http://doc.chipfind.ru/pdf/intel/8274.pdf), which also has almost identical @@ -14,10 +13,6 @@ scheme which uses write register 2 on channel A, that register which is unused on the z80dart and z80sio. - The z80scc is an updated version of the z80sio, with additional support for CRC - checks and a number of data link layer protocols such as HDLC, SDLC and BiSync. - (See https://en.wikipedia.org/wiki/Zilog_SCC). - ***************************************************************************/ /* @@ -30,9 +25,8 @@ - wait/ready - 1.5 stop bits - synchronous mode (Z80-SIO/1,2) - - SDLC mode (Z80-SIO/1,2/SCC) - - HDLC, BiSync support (Z80-SCC) - - CRC support (Z80-SCC) + - SDLC mode (Z80-SIO/1,2) + */ #include "z80dart.h" @@ -66,7 +60,6 @@ const device_type Z80SIO3 = &device_creator; const device_type Z80SIO4 = &device_creator; const device_type I8274 = &device_creator; const device_type UPD7201 = &device_creator; -const device_type Z80SCC = &device_creator; //------------------------------------------------- @@ -188,11 +181,6 @@ upd7201_device::upd7201_device(const machine_config &mconfig, const char *tag, d { } -scc8530_device::scc8530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : z80dart_device(mconfig, Z80SCC, "Z80 SCC", tag, owner, clock, TYPE_Z80SCC, "z80scc", __FILE__) -{ -} - //------------------------------------------------- // device_start - device-specific startup @@ -450,10 +438,6 @@ READ8_MEMBER( z80dart_device::cd_ba_r ) int cd = BIT(offset, 1); z80dart_channel *channel = ba ? m_chanB : m_chanA; - cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC - - // LOG(("z80dart_device::cd_ba_r ba:%02x cd:%02x\n", ba, cd)); - return cd ? channel->control_read() : channel->data_read(); } @@ -468,10 +452,6 @@ WRITE8_MEMBER( z80dart_device::cd_ba_w ) int cd = BIT(offset, 1); z80dart_channel *channel = ba ? m_chanB : m_chanA; - cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC - - // LOG(("z80dart_device::cd_ba_w ba:%02x cd:%02x\n", ba, cd)); - if (cd) channel->control_write(data); else @@ -489,10 +469,6 @@ READ8_MEMBER( z80dart_device::ba_cd_r ) int cd = BIT(offset, 0); z80dart_channel *channel = ba ? m_chanB : m_chanA; - cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC - - // LOG(("z80dart_device::ba_cd_r ba:%02x cd:%02x\n", ba, cd)); - return cd ? channel->control_read() : channel->data_read(); } @@ -507,16 +483,14 @@ WRITE8_MEMBER( z80dart_device::ba_cd_w ) int cd = BIT(offset, 0); z80dart_channel *channel = ba ? m_chanB : m_chanA; - cd = (m_variant == TYPE_Z80SCC) ? !cd : cd; // Inverted logic on SCC - - LOG(("z80dart_device::ba_cd_w ba:%02x cd:%02x\n", ba, cd)); - if (cd) channel->control_write(data); else channel->data_write(data); } + + //************************************************************************** // DART CHANNEL //************************************************************************** @@ -544,10 +518,10 @@ z80dart_channel::z80dart_channel(const machine_config &mconfig, const char *tag, m_rts(0), m_sync(0) { - for (int i = 0; i < sizeof(m_rr); i++) + for (int i = 0; i < 3; i++) m_rr[i] = 0; - for (int i = 0; i < sizeof(m_wr); i++) + for (int i = 0; i < 6; i++) m_wr[i] = 0; for (int i = 0; i < 3; i++) @@ -566,7 +540,6 @@ void z80dart_channel::device_start() { m_uart = downcast(owner()); m_index = m_uart->get_channel_index(this); - m_ph = 0; // state saving save_item(NAME(m_rr)); @@ -587,7 +560,6 @@ void z80dart_channel::device_start() save_item(NAME(m_dtr)); save_item(NAME(m_rts)); save_item(NAME(m_sync)); - save_item(NAME(m_ph)); device_serial_interface::register_save_state(machine().save(), this); } @@ -815,17 +787,13 @@ int z80dart_channel::get_tx_word_length() UINT8 z80dart_channel::control_read() { UINT8 data = 0; - int reg = m_wr[0]; - int regmask = (WR0_REGISTER_MASK | m_ph); - m_ph = 0; // The "Point High" command is only valid for one access - - reg &= regmask; + int reg = m_wr[0] & WR0_REGISTER_MASK; if (reg != 0) { // mask out register index - m_wr[0] &= ~regmask; + m_wr[0] &= ~WR0_REGISTER_MASK; } switch (reg) @@ -840,33 +808,9 @@ UINT8 z80dart_channel::control_read() if (m_index == z80dart_device::CHANNEL_B) data = m_rr[reg]; break; - /* registers 4-7 are specific to SCC. TODO: Check variant and log/stop misuse */ - case 4: /* (ESCC and 85C30 Only) */ - /*On the ESCC, Read Register 4 reflects the contents of Write Register 4 provided the Extended - Read option is enabled. Otherwise, this register returns an image of RR0. On the NMOS/CMOS version, - a read to this location returns an image of RR0.*/ - case 5: /* (ESCC and 85C30 Only) */ - /*On the ESCC, Read Register 5 reflects the contents of Write Register 5 provided the Extended - Read option is enabled. Otherwise, this register returns an image of RR1. On the NMOS/CMOS version, - a read to this register returns an image of RR1.*/ - data = BIT(m_wr[7], 6) ? m_wr[reg] : m_rr[reg - 4]; - break; - /* registers 8-15 are specific to SCC and misuse captured by test around Point High command in control_write() */ - case 8: - data = data_read(); - break; - case 10: - data = 0; - LOG(("Z80DART Read Register 10 Misc Status Bits, SDLC related, not implemented yet.\n")); - break; - case 13: - data = m_wr[13]; - break; - default: - logerror("Z80DART \"%s\" Channel %c : Unsupported RRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); } - //LOG(("Z80DART \"%s\" Channel %c : Register R%d read '%02x'\n", m_owner->tag(), 'A' + m_index, reg, data)); + //LOG(("Z80DART \"%s\" Channel %c : Control Register Read '%02x'\n", m_owner->tag(), 'A' + m_index, data)); return data; } @@ -878,13 +822,9 @@ UINT8 z80dart_channel::control_read() void z80dart_channel::control_write(UINT8 data) { - int reg = m_wr[0]; - int regmask = (WR0_REGISTER_MASK | m_ph); - - m_ph = 0; // The "Point High" command is only valid for one access - - reg &= regmask; + int reg = m_wr[0] & WR0_REGISTER_MASK; + LOG(("Z80DART \"%s\" Channel %c : Control Register Write '%02x'\n", m_owner->tag(), 'A' + m_index, data)); // write data to selected register if (reg < 6) @@ -893,11 +833,9 @@ void z80dart_channel::control_write(UINT8 data) if (reg != 0) { // mask out register index - m_wr[0] &= ~regmask; + m_wr[0] &= ~WR0_REGISTER_MASK; } - LOG(("reg %02x, regmask %02x, WR0 %02x, data %02x\n", reg, regmask, m_wr[0], data)); - switch (reg) { case 0: @@ -907,21 +845,9 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Null\n", m_owner->tag(), 'A' + m_index)); break; - //case WR0_POINT_HIGH: // Same value so doesn't compile... case WR0_SEND_ABORT: - if (((z80dart_device *)m_owner)->m_variant == z80dart_device::TYPE_Z80SCC) - { - /* This is the Point High command for SCC, it will latch access to the high - registers for the next read or write to the control registers */ - LOG(("Z80DART \"%s\" Channel %c : Point High\n", m_owner->tag(), 'A' + m_index)); - m_ph = 8; - } - else - { - /* Send Abort is only valid for the original Z80 SIO, not the DART or SCC */ - LOG(("Z80DART \"%s\" Channel %c : Send Abort\n", m_owner->tag(), 'A' + m_index)); - logerror("Z80DART \"%s\" Channel %c : unsupported command: Send Abort\n", m_owner->tag(), 'A' + m_index); - } + LOG(("Z80DART \"%s\" Channel %c : Send Abort\n", m_owner->tag(), 'A' + m_index)); + logerror("Z80DART \"%s\" Channel %c : unsupported command: Send Abort\n", m_owner->tag(), 'A' + m_index); break; case WR0_RESET_EXT_STATUS: @@ -966,8 +892,6 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Return from Interrupt\n", m_owner->tag(), 'A' + m_index)); m_uart->z80daisy_irq_reti(); break; - default: - logerror("Z80DART \"%s\" Channel %c : Unsupported WR0 command:%02x\n", m_owner->tag(), 'A' + m_index, data & WR0_COMMAND_MASK); } break; @@ -1065,70 +989,6 @@ void z80dart_channel::control_write(UINT8 data) LOG(("Z80DART \"%s\" Channel %c : Receive Sync %02x\n", m_owner->tag(), 'A' + m_index, data)); m_sync = (data << 8) | (m_sync & 0xff); break; - /* registers 8-15 are specific to SCC and misuse captured by test around Point High command above */ - case 8: - LOG(("Z80DART \"%s\" Channel %c : Transmit Buffer read %02x\n", m_owner->tag(), 'A' + m_index, data)); - data_write(data); - break; - case 9: - switch (data & WR9_CMD_MASK) - { - case WR9_CMD_NORESET: - LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - No reset %02x\n", m_owner->tag(), 'A' + m_index, data)); - break; - case WR9_CMD_CHNB_RESET: - LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Channel B reset %02x\n", m_owner->tag(), 'A' + m_index, data)); - m_uart->m_chanB->reset(); - break; - case WR9_CMD_CHNA_RESET: - LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Channel A reset %02x\n", m_owner->tag(), 'A' + m_index, data)); - m_uart->m_chanA->reset(); - break; - case WR9_CMD_HW_RESET: - LOG(("Z80DART \"%s\" Channel %c : Master Interrupt Control - Device reset %02x\n", m_owner->tag(), 'A' + m_index, data)); - /*"The effects of this command are identical to those of a hardware reset, except that the Shift Right/Shift Left bit is - not changed and the MIE, Status High/Status Low and DLC bits take the programmed values that accompany this command." - The Shift Right/Shift Left bits of the WR0 is only valid on SCC8030 device hence not implemented yet, just the SCC8530 */ - if (data & (WR9_BIT_MIE | WR9_BIT_IACK | WR9_BIT_SHSL | WR9_BIT_DLC | WR9_BIT_NV)) - logerror("Z80DART: SCC Interrupt system not yet implemented, please be patient!\n"); - m_uart->device_reset(); - break; - default: - logerror("Z80DART Code is broken in WR9, please report!\n"); - } - break; - case 10: - LOG(("Z80DART \"%s\" Channel %c : unsupported command: Misc Tx/Rx Control %02x\n", m_owner->tag(), 'A' + m_index, data)); - break; - case 11: - LOG(("Z80DART \"%s\" Channel %c : incomplete command: Clock Mode Control %02x\n", m_owner->tag(), 'A' + m_index, data)); - m_wr[11] = data; - break; - case 12: - LOG(("Z80DART \"%s\" Channel %c : incomplete command: Low Byte of Baudrate Generator Time Constant %02x\n", m_owner->tag(), 'A' + m_index, data)); - m_wr[12] = data; - break; - case 13: - LOG(("Z80DART \"%s\" Channel %c : incomplete command: High Byte of Baudrate Generator Time Constant %02x\n", m_owner->tag(), 'A' + m_index, data)); - m_wr[13] = data; - break; - case 14: - switch (data & WR14_DPLL_CMD_MASK) - { - case WR14_CMD_NULL: - LOG(("Z80DART \"%s\" Channel %c : Misc Control Bits: DPLL Null Command %02x\n", m_owner->tag(), 'A' + m_index, data)); - break; - default: - logerror("Z80DART \"%s\" Channel %c : incomplete command: Misc Control Bits %02x\n", m_owner->tag(), 'A' + m_index, data); - } - // TODO: Add support for clock source to Baudrate generator - m_wr[14] = data; - break; - case 15: - LOG(("Z80DART \"%s\" Channel %c : unsupported command: External/Status Control Bits %02x\n", m_owner->tag(), 'A' + m_index, data)); - break; - default: - logerror("Z80DART \"%s\" Channel %c : Unsupported WRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg); } } diff --git a/src/devices/machine/z80dart.h b/src/devices/machine/z80dart.h index c4893ef90db..2674f84758a 100644 --- a/src/devices/machine/z80dart.h +++ b/src/devices/machine/z80dart.h @@ -6,7 +6,6 @@ NEC uPD7201 Multiprotocol Serial Communications Controller emulation Z80-DART Dual Asynchronous Receiver/Transmitter emulation Z80-SIO/0/1/2/3/4 Serial Input/Output Controller emulation - Z80-SCC Serial Communications Controller emulation (experimental) **************************************************************************** _____ _____ @@ -141,29 +140,6 @@ _DCDA 19 | | 22 _DCDB CLK 20 |_____________| 21 _RESET - - _____ _____ - D1 1 |* \_/ | 40 D0 - D3 2 | | 39 D2 - D5 3 | | 38 D4 - D7 4 | | 37 D6 - _INT 5 | | 36 _RD - IEO 6 | | 35 _WR - IEI 7 | | 34 B/_A - _INTAK 8 | | 33 _CE - VCC 9 | | 32 C/_D - _W/REQA 10| Z80-SCC | 31 GND - _SYNCA 11| Z8530 | 30 _W/REQB - _RTxCA 12| | 29 _SYNCB - RxDA 13| | 28 _RTxCB - _TRxCA 14| | 27 RxDB - TxDA 15| | 26 _TRxCB - _DTR/REQA 16| | 25 TxDB - _RTSA 17| | 24 _DTR/REQB - _CTSA 18| | 23 _RTSB - _DCDA 19| | 22 _CTSB - CLK 20|_____________| 21 _DCDB - ***************************************************************************/ #ifndef __Z80DART_H__ @@ -209,10 +185,6 @@ MCFG_DEVICE_ADD(_tag, UPD7201, _clock) \ MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) -#define MCFG_Z80SCC_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \ - MCFG_DEVICE_ADD(_tag, Z80SCC, _clock) \ - MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) - #define MCFG_Z80DART_OFFSETS(_rxa, _txa, _rxb, _txb) \ z80dart_device::configure_channels(*device, _rxa, _txa, _rxb, _txb); @@ -310,8 +282,8 @@ public: int m_txc; // register state - UINT8 m_rr[16]; // read registers, DART=3 SCC=16 - UINT8 m_wr[16]; // write register, DART=6 SCC=16 + UINT8 m_rr[3]; // read register + UINT8 m_wr[6]; // write register protected: enum @@ -364,13 +336,6 @@ protected: WR0_CRC_RESET_TX_UNDERRUN = 0xc0 // not supported }; - enum /* SCC specifics */ - { - WR0_REGISTER_MASK_SCC = 0x0f, - WR0_POINT_HIGH = 0x08, - - }; - enum { WR1_EXT_INT_ENABLE = 0x01, @@ -453,34 +418,6 @@ protected: WR5_DTR = 0x80 }; - /* SCC specifics */ - enum - { - WR9_CMD_MASK = 0xC0, - WR9_CMD_NORESET = 0x00, - WR9_CMD_CHNB_RESET = 0x40, - WR9_CMD_CHNA_RESET = 0x80, - WR9_CMD_HW_RESET = 0xC0, - WR9_BIT_VIS = 0x01, - WR9_BIT_NV = 0x02, - WR9_BIT_DLC = 0x04, - WR9_BIT_MIE = 0x08, - WR9_BIT_SHSL = 0x10, - WR9_BIT_IACK = 0x20 - }; - - enum - { - WR14_DPLL_CMD_MASK = 0xe0, - WR14_CMD_NULL = 0x00, - WR14_CMD_ESM = 0x20, - WR14_CMD_RMC = 0x40, - WR14_CMD_SS_BGR = 0x80, - WR14_CMD_SS_RTXC = 0xa0, - WR14_CMD_SET_FM = 0xc0, - WR14_CMD_SET_NRZI = 0xe0 - }; - void update_serial(); void set_dtr(int state); void set_rts(int state); @@ -518,9 +455,6 @@ protected: int m_index; z80dart_device *m_uart; - - // SCC specifics - int m_ph; // Point high command to access regs 08-0f }; @@ -621,8 +555,7 @@ protected: TYPE_SIO3, TYPE_SIO4, TYPE_I8274, - TYPE_UPD7201, - TYPE_Z80SCC + TYPE_UPD7201 }; enum @@ -736,16 +669,6 @@ public: }; -// ======================> scc8530_device - -class scc8530_device : public z80dart_device -{ -public : - // construction/destruction - scc8530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); -}; - - // device type definition extern const device_type Z80DART_CHANNEL; extern const device_type Z80DART; @@ -756,7 +679,6 @@ extern const device_type Z80SIO3; extern const device_type Z80SIO4; extern const device_type I8274; extern const device_type UPD7201; -extern const device_type Z80SCC; #endif From 1211bfb33ec67559408d9679ffc324467cb8c81a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Larsson=20Edstr=C3=B6m?= Date: Sun, 13 Sep 2015 20:36:08 +0200 Subject: [PATCH 10/12] fixed cut&paste error --- src/devices/machine/z80scc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/machine/z80scc.c b/src/devices/machine/z80scc.c index ed84814ae0d..7dcb27daa40 100644 --- a/src/devices/machine/z80scc.c +++ b/src/devices/machine/z80scc.c @@ -561,7 +561,7 @@ z80scc_channel::z80scc_channel(const machine_config &mconfig, const char *tag, d m_rr0 = m_rr1 = m_rr2 = m_rr3 = m_rr4 = m_rr5 = m_rr6 = m_rr7 = m_rr8 = m_rr9 = m_rr10 = m_rr11 = m_rr12 = m_rr13 = m_rr14 = m_rr15 = 0; - m_rr0 = m_rr1 = m_rr2 = m_rr3 = m_rr4 = m_rr5 = m_rr6 = m_rr7 = + m_wr0 = m_wr1 = m_wr2 = m_wr3 = m_wr4 = m_wr5 = m_wr6 = m_wr7 = m_wr8 = m_wr9 = m_wr10 = m_wr11 = m_wr12 = m_wr13 = m_wr14 = m_wr15; for (int i = 0; i < 3; i++) // TODO adapt to SCC fifos From fe94a528feabad54e309d71c79dc16d6dc10ca98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Larsson=20Edstr=C3=B6m?= Date: Sun, 13 Sep 2015 20:56:22 +0200 Subject: [PATCH 11/12] Removed Curt from copyright holders, I am sure he doesn't want to get blamed for this device --- src/devices/machine/z80scc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/machine/z80scc.c b/src/devices/machine/z80scc.c index 7dcb27daa40..71868184e97 100644 --- a/src/devices/machine/z80scc.c +++ b/src/devices/machine/z80scc.c @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Curt Coder, Joakim Larsson Edstrom +// copyright-holders: Joakim Larsson Edstrom /*************************************************************************** Z80-SCC Serial Communications Controller emulation From 3c5965366bc78c549c3097069f5dfbab145f1b19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Larsson=20Edstr=C3=B6m?= Date: Sun, 13 Sep 2015 22:08:00 +0200 Subject: [PATCH 12/12] Fixed some type errors --- src/devices/machine/z80scc.c | 10 +++++----- src/devices/machine/z80scc.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/devices/machine/z80scc.c b/src/devices/machine/z80scc.c index 71868184e97..bcbb024ad4f 100644 --- a/src/devices/machine/z80scc.c +++ b/src/devices/machine/z80scc.c @@ -96,7 +96,7 @@ const device_type Z80SCC_CHANNEL = &device_creator; const device_type SCC8030 = &device_creator; const device_type SCC80C30 = &device_creator; const device_type SCC80230 = &device_creator; -const device_type SCC8530 = &device_creator; +const device_type SCC8530N = &device_creator; // remove trailing N when 8530scc.c is fully replaced and removed const device_type SCC85C30 = &device_creator; const device_type SCC85230 = &device_creator; const device_type SCC85233 = &device_creator; @@ -184,16 +184,16 @@ z80scc_device::z80scc_device(const machine_config &mconfig, const char *tag, dev } scc8030_device::scc8030_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : z80scc_device(mconfig, SCC8530, "SCC 8030", tag, owner, clock, TYPE_SCC8030, "scc8030", __FILE__){ } + : z80scc_device(mconfig, SCC8030, "SCC 8030", tag, owner, clock, TYPE_SCC8030, "scc8030", __FILE__){ } scc80C30_device::scc80C30_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : z80scc_device(mconfig, SCC85C30, "SCC 80C30", tag, owner, clock, TYPE_SCC80C30, "scc80C30", __FILE__){ } + : z80scc_device(mconfig, SCC80C30, "SCC 80C30", tag, owner, clock, TYPE_SCC80C30, "scc80C30", __FILE__){ } scc80230_device::scc80230_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : z80scc_device(mconfig, SCC85230, "SCC 80230", tag, owner, clock, TYPE_SCC80230, "scc80230", __FILE__){ } + : z80scc_device(mconfig, SCC80230, "SCC 80230", tag, owner, clock, TYPE_SCC80230, "scc80230", __FILE__){ } scc8530_device::scc8530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : z80scc_device(mconfig, SCC8530, "SCC 8530", tag, owner, clock, TYPE_SCC8530, "scc8530", __FILE__){ } + : z80scc_device(mconfig, SCC8530N, "SCC 8530", tag, owner, clock, TYPE_SCC8530, "scc8530", __FILE__){ } scc85C30_device::scc85C30_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : z80scc_device(mconfig, SCC85C30, "SCC 85C30", tag, owner, clock, TYPE_SCC85C30, "scc85C30", __FILE__){ } diff --git a/src/devices/machine/z80scc.h b/src/devices/machine/z80scc.h index 110bff86a72..dc278394650 100644 --- a/src/devices/machine/z80scc.h +++ b/src/devices/machine/z80scc.h @@ -46,7 +46,7 @@ MCFG_Z80SCC_OFFSETS(_rxa, _txa, _rxb, _txb) #define MCFG_SCC8530_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \ - MCFG_DEVICE_ADD(_tag, SCC8530, _clock) \ + MCFG_DEVICE_ADD(_tag, SCC8530N, _clock) \ MCFG_Z80SCC_OFFSETS(_rxa, _txa, _rxb, _txb) #define MCFG_Z80SCC_OFFSETS(_rxa, _txa, _rxb, _txb) \ @@ -729,7 +729,7 @@ extern const device_type Z80SCC_CHANNEL; extern const device_type SCC8030; extern const device_type SCC80C30; extern const device_type SCC80230; -extern const device_type SCC8530; +extern const device_type SCC8530N; // remove trailing N when 8530scc.c is fully replaced and removed extern const device_type SCC85C30; extern const device_type SCC85230; extern const device_type SCC85233;