diff options
Diffstat (limited to 'src/devices/bus/ti99/internal/998board.cpp')
-rw-r--r-- | src/devices/bus/ti99/internal/998board.cpp | 292 |
1 files changed, 276 insertions, 16 deletions
diff --git a/src/devices/bus/ti99/internal/998board.cpp b/src/devices/bus/ti99/internal/998board.cpp index f6eaf940e4d..0e3d7663684 100644 --- a/src/devices/bus/ti99/internal/998board.cpp +++ b/src/devices/bus/ti99/internal/998board.cpp @@ -2243,15 +2243,34 @@ enum CR7 = 0x01 }; +/* Line */ +enum +{ + LINE_HSK = 0x10, + LINE_BAV = 0x04, + LINE_BIT32 = 0xc0, + LINE_BIT10 = 0x03 +}; + oso_device::oso_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : bus::hexbus::hexbus_chained_device(mconfig, TI99_OSO, tag, owner, clock), + m_int(*this), m_data(0), m_status(0xff), m_control(0), - m_xmit(0), - m_clkcount(0), - m_xmit_send(0) -{ + m_xmit(0), m_lasthxvalue(0x01), + m_bav(false), m_sbav(false), m_sbavold(false), m_bavhold(false), + m_hsk(false), m_shsk(false), m_shskold(false), m_hskhold(false), + m_wq1(false), m_wq1old(false), m_wq2(false), m_wq2old(false), + m_wnp(false), m_wbusyold(false), m_sendbyte(false), + m_wrset(false), m_counting(false), m_clkcount(0), + m_rq1(false), m_rq2(false), m_rq2old(false), + m_rnib(false), m_rnibcold(false), + m_rdset(false), m_rdsetold(false), + m_msns(false), m_lsns(false), + m_rhsus(false) +{ + (void)m_shskold; m_hexbus_inbound = nullptr; m_hexbus_outbound = nullptr; } @@ -2266,11 +2285,17 @@ READ8_MEMBER( oso_device::read ) // read 5FF8: read data register if (TRACE_OSO) logerror("Read data register = %02x\n", value); value = m_data; + // Release the handshake + m_rhsus = false; break; case 1: // read 5FFA: read status register value = m_status; - if (TRACE_OSO) logerror("Read status %02x\n", value); + clear_int_status(); + if (TRACE_OSO) logerror("Read status %02x (HSKWT=%d,HSKRD=%d,BAVIAS=%d,BAVAIS=%d,SBAV=%d,WBUSY=%d,RBUSY=%d,SHSK=%d)\n", value, + (value&HSKWT)? 1:0, (value&HSKRD)? 1:0, (value&BAVIAS)? 1:0, + (value&BAVAIS)? 1:0, (value&SBAV)? 1:0, (value&WBUSY)? 1:0, + (value&RBUSY)? 1:0,(value&SHSK)? 1:0); break; case 2: // read 5FFC: read control register @@ -2294,14 +2319,33 @@ WRITE8_MEMBER( oso_device::write ) case 0: // write 5FF8: write transmit register if (TRACE_OSO) logerror("Write transmit register %02x\n", data); + + // trigger some actions in the write subsystem + m_sendbyte = true; + if (!m_wq1) + { + m_wbusyold = true; + set_status(WBUSY, true); + } + m_xmit = data; - m_xmit_send = 2; - m_status |= HSKWT; break; case 1: // write 5FFA: write control register - if (TRACE_OSO) logerror("Write control register %02x\n", data); + if (TRACE_OSO) logerror("Write control register %02x (WIEN=%d, RIEN=%d, BAVIAEN=%d, BAVAIEN=%d, BAVC=%d, WEN=%d, REN=%d)\n", + data, (data & WIEN)? 1:0, (data & RIEN)? 1:0, (data&BAVIAEN)? 1:0, (data&BAVAIEN)? 1:0, + (data & BAVC)? 1:0, (data & WEN)? 1:0, (data & REN)? 1:0); m_control = data; + + // Reset some flipflops in the write/read timing section + if (!control_bit(WEN)) + { + m_wq1 = m_wq2 = m_wrset = false; + } + if (!control_bit(REN)) + { + m_rq1 = m_rq2 = m_rdset = false; + } break; default: // write 5FFC, 5FFE: undefined @@ -2310,9 +2354,24 @@ WRITE8_MEMBER( oso_device::write ) } } +void oso_device::clear_int_status() +{ + m_status &= ~(HSKWT | HSKRD | BAVIAS | BAVAIS); + m_int(CLEAR_LINE); +} + void oso_device::hexbus_value_changed(uint8_t data) { if (TRACE_OSO) logerror("Hexbus value changed to %02x\n", data); + + m_bav = ((data & LINE_BAV)==0) | control_bit(BAVC); + + m_hsk = (data & LINE_HSK)==0; + int nibble = ((data & LINE_BIT32)>>4) | (data & LINE_BIT10); + if (m_msns) + m_data = (m_data & 0x0f) | (nibble<<4); + if (m_lsns) + m_data = (m_data & 0xf0) | nibble; } /* @@ -2320,28 +2379,229 @@ void oso_device::hexbus_value_changed(uint8_t data) */ WRITE_LINE_MEMBER( oso_device::clock_in ) { - if (state==ASSERT_LINE) m_clkcount++; - if (m_clkcount > 30 && ((m_control & WEN)!=0) && (m_xmit_send > 0)) + if (state==ASSERT_LINE) + { + // Control lines SHSK, SBAV + // When BAV/HSK is 0/1 for two rising edges of Phi3*, SBAV/SHSK goes to + // 0/1 at the following falling edge of Phi3*. + // Page 5 + m_sbav = m_bavhold && m_bav; // could mean "stable BAV" + m_bavhold = m_bav; + m_shsk = m_hskhold && m_hsk; + m_hskhold = m_hsk; + set_status(SHSK, m_shsk); + set_status(SBAV, m_sbav); + + // Raising edge of SBAV* + if (m_sbav == true && m_sbavold == false) + set_status(BAVIAS, true); + // Falling edge of SBAV* + if (m_sbav == false && m_sbavold == true) + set_status(BAVAIS, true); + m_sbavold = m_sbav; + + // Implement the write timing logic + // This subcircuit in the OSO chip autonomously runs the Hexbus + // protocol. After loading a byte into the transmit register, it sends + // both nibbles (little-endian) one after another over the Hexbus, + // pausing for 30 cycles, and checking the HSK line. + + // The schematics show some fascinating signal line spaghetti with + // embedded JK* flipflops which may give you some major headaches. + // Compared to that, the lines below are a true relief. + + if (control_bit(WEN)) // Nothing happens without WEN + { + if (TRACE_OSO) if (!m_wrset && m_sendbyte) logerror("Starting write process\n"); + // Page 3: Write timing + // Note: First pass counts to 30, second to 31 + bool cnt30 = ((m_clkcount & 0x1e) == 30); + bool cont = (m_wrset && !m_wq2 && !m_wq1) || (cnt30 && m_wq2 && !m_wq1) + || (cnt30 && !m_wq2 && m_wq1) || (m_shsk && m_wq2 && m_wq1); + + bool jwq1 = cont && m_wq2; + bool kwq1 = !((cont && !m_wq2) || (!cont && m_wq2 && m_wnp)); + + bool jwq2 = cont; + bool kwq2 = !(m_wq1 && !cont); + + if (m_wq1 == m_wq2) m_clkcount = 0; + + // Reset "byte loaded" flipflop during the second phase + if (m_wq1 == true) + m_sendbyte = false; + + // logerror("sendbyte=%d, wq1=%d, wq2=%d, jwq1=%d, kwq1=%d, jwq2=%d, kwq2=%d, shsk=%d\n", m_sendbyte, m_wq1, m_wq2, jwq1, kwq1, jwq2, kwq2, m_shsk); + // WBUSY is asserted on byte load, during phase 1, and phase 2. + bool wbusy = m_sendbyte || m_wq1 || m_wq2; + + // Set status bits and raise interrupt (p. 4) + set_status(WBUSY, wbusy); + + // Raising edge of wbusy* + if (m_wbusyold == true && wbusy == false) + set_status(HSKWT, true); + m_wbusyold = wbusy; + + // Operate flipflops + // Write phases + // 74LS109: J-K* flipflop (inverted K) + if (jwq1) + { + if (!kwq1) m_wq1 = !m_wq1; + else m_wq1 = true; + } + else + if (!kwq1) m_wq1 = false; + + if (jwq2) + { + if (!kwq2) m_wq2 = !m_wq2; + else m_wq2 = true; + } + else + if (!kwq2) m_wq2 = false; + + // Set WNP on rising edge of WQ2* + if (m_wq2 != m_wq2old) + { + if (!m_wq2) + m_wnp = true; + + m_wq2old = m_wq2; + } + m_wq1old = m_wq1; + + // Reset WNP if phases are done + if (!m_wq2 && !m_wq1) + { + m_wnp = false; + } + } + + // This is the reading behavior. In this case, the master (this + // component) pulls down BAV*, then the slave sets the data lines + // with the back nibble, pulls down HSK*, then releases HSK*, + // puts the front nibble on the data lines, pulls down HSK* again, + // releases it, and this continues until the master releases BAV* + + if (control_bit(REN)) + { + bool rbusy = m_rq1 || m_rq2; + set_status(RBUSY, rbusy); + + // Flipflop resets + if (!rbusy) m_rnib = false; + if (m_rq2) m_rdset = false; + + bool rdsetin = !control_bit(WBUSY) && m_sbav && m_shsk; + bool next = (m_rdset && !m_rq2) || (m_shsk && m_rq2); + bool drq1 = (next && m_rq2) || (m_rq2 && m_rq1 && !m_rnib); + bool jrq2 = next && !m_rq1; + bool krq2 = m_rq1 && !m_rnib; + m_msns = m_rnib && !m_rq1 && m_rq2; + m_lsns = !m_rnib && !m_rq1 && m_rq2; + bool rnibc = m_rq1; + + // Next state + if (!m_rdsetold && rdsetin) m_rdset = true; // raising edge + m_rdsetold = rdsetin; + m_rq1 = drq1; + + if (jrq2) + { + if (!krq2) m_rq2 = !m_rq2; + else m_rq2 = true; + } + else + if (!krq2) m_rq2 = false; + + if (m_rnibcold == false && rnibc == true) m_rnib = !m_rnib; // raising edge + m_rnibcold = rnibc; + + // Raising edge of RQ2* + if (m_rq2old == true && m_rq2 == false) + { + set_status(HSKRD, true); + m_rhsus = true; // byte is available for reading + } + m_rq2old = m_rq2; + } + else + { + m_rhsus = false; + } + + // Handshake control + // Set HSK (Page 6, RHSUS*) + bool hskwrite = !m_wq1 && m_wq2; + + // We can simplify this to a single flag because the CPU read operation + // is atomic here (starts and immediately terminates) + m_hsk = hskwrite || m_rhsus; + update_hexbus(); + } + // Actions that occur for Phi3=0 + else + { + m_wrset = m_sendbyte; + // Only count when one phase is active + m_counting = !(m_wq1==m_wq2); + + if (m_counting) + m_clkcount++; + else + m_clkcount = 0; // Reset when not counting + } + + // Flipflop resets (not related to clock) + if (!control_bit(WEN)) { - if (TRACE_OSO) logerror("Write nibble %d\n", 3-m_xmit_send); - hexbus_write(((m_xmit & 0x0c)<<4) | (m_xmit & 0x03)); - m_xmit >>= 4; + m_wq1 = m_wq2 = m_wrset = m_counting = false; m_clkcount = 0; - m_xmit_send--; + } + if (!control_bit(REN)) + { + m_rq1 = m_rq2 = m_rdset = false; + } + + // Raise interrupt + if ((control_bit(WIEN) && status_bit(HSKWT)) + || (control_bit(RIEN) && status_bit(HSKRD)) + || (control_bit(BAVAIEN) && status_bit(BAVAIS)) + || (control_bit(BAVIAEN) && status_bit(BAVIAS))) + { + m_int(ASSERT_LINE); } } +void oso_device::update_hexbus() +{ + uint8_t value = 0x00; + uint8_t nibble = m_xmit; + if (m_wnp) nibble >>= 4; + + value = ((m_xmit & 0x0c)<<4) | (m_xmit & 0x03); + if (!m_hsk) value |= 0x10; + if (!control_bit(BAVC)) value |= 0x04; + if (value != m_lasthxvalue) + { + if (TRACE_OSO) logerror("Set hexbus = %02x (BAV*=%d, HSK*=%d, data=%01x)\n", value, (value & 0x04)? 1:0, (value & 0x10)? 1:0, ((value>>4)&0x0c) | (value&0x03)); + hexbus_write(value); + } + m_lasthxvalue = value; +} + void oso_device::device_start() { logerror("Starting\n"); m_status = m_xmit = m_control = m_data = 0; + m_int.resolve_safe(); m_hexbus_outbound = dynamic_cast<bus::hexbus::hexbus_device*>(machine().device(TI_HEXBUS_TAG)); // Establish callback for inbound propagations m_hexbus_outbound->set_chain_element(this); - // Establish callback - m_hexbus_outbound->set_chain_element(this); save_item(NAME(m_data)); save_item(NAME(m_status)); |