summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/ti99/internal/998board.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/ti99/internal/998board.cpp')
-rw-r--r--src/devices/bus/ti99/internal/998board.cpp292
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));