From 7bd2bb276540d6d633ac66cec55a787cb44b8a0b Mon Sep 17 00:00:00 2001 From: AJR Date: Fri, 18 Jan 2019 12:41:46 -0500 Subject: z8: Preliminary UART emulation --- src/devices/cpu/z8/z8.cpp | 186 +++++++++++++++++++++++++++++++++++++++------- src/devices/cpu/z8/z8.h | 15 ++++ 2 files changed, 174 insertions(+), 27 deletions(-) diff --git a/src/devices/cpu/z8/z8.cpp b/src/devices/cpu/z8/z8.cpp index 84ee2ea7dce..9e4ff7decbc 100644 --- a/src/devices/cpu/z8/z8.cpp +++ b/src/devices/cpu/z8/z8.cpp @@ -11,8 +11,6 @@ TODO: - strobed I/O - - timer Tin/Tout modes - - serial - instruction pipeline - internal diagnostic ROM in data space (requires high voltage reset) @@ -37,8 +35,8 @@ #define Z8_P3_IRQ1 0x08 #define Z8_P3_IRQ2 0x02 #define Z8_P3_IRQ3 0x01 -#define Z8_P3_DI 0x01 /* not supported */ -#define Z8_P3_DO 0x80 /* not supported */ +#define Z8_P3_SIN 0x01 +#define Z8_P3_SOUT 0x80 #define Z8_P3_TIN 0x02 #define Z8_P3_TOUT 0x40 #define Z8_P3_DM 0x10 /* not supported */ @@ -437,6 +435,22 @@ void z8_device::p2_write(uint8_t data) m_output_cb[2](0, data & mask, mask); } +void z8_device::p3_update_output() +{ + uint8_t output = m_output[3] & 0xf0; + + if ((m_tmr & Z8_TMR_TOUT_MASK) != Z8_TMR_TOUT_OFF) + output = (output & ~Z8_P3_TOUT) | (output ? Z8_P3_TOUT : 0); + if ((m_p3m & Z8_P3M_P3_SERIAL) != 0) + output = (output & ~Z8_P3_SOUT) | ((m_transmit_sr == 0 || BIT(m_transmit_sr, 0)) ? Z8_P3_SOUT : 0); + + if (m_p3_output != output) + { + m_output_cb[3](0, output, output ^ m_p3_output); + m_p3_output = output; + } +} + uint8_t z8_device::p3_read() { uint8_t mask = 0x0f; @@ -447,45 +461,136 @@ uint8_t z8_device::p3_read() //{ //} - if ((m_tmr & Z8_TMR_TOUT_MASK) != Z8_TMR_TOUT_OFF) - { - mask |= Z8_P3_TOUT; - if (m_tout) - inputs |= Z8_P3_TOUT; - } - - return (inputs & mask) | (m_output[3] & ~mask); + return (inputs & mask) | (m_p3_output & ~mask); } void z8_device::p3_write(uint8_t data) { - uint8_t mask = 0xf0; - - m_output[3] = data; + m_output[3] = data & 0xf0; // TODO: special port 3 modes //if (!(m_p3m & 0x7c)) //{ //} - if ((m_tmr & Z8_TMR_TOUT_MASK) != Z8_TMR_TOUT_OFF) - data = (data & ~Z8_P3_TOUT) | (m_tout ? Z8_P3_TOUT : 0); + p3_update_output(); +} - if (mask) - m_output_cb[3](0, data & mask, mask); +bool z8_device::get_serial_in() +{ + return (m_input[3] & Z8_P3_SIN) != 0; } void z8_device::sio_tick() { + if (m_receive_started) + { + m_receive_count = (m_receive_count + 1) & 15; + if (m_receive_count == 8) + { + if (m_receive_sr == 0) + { + if (!get_serial_in()) + { + // start bit validated + m_receive_sr |= 1 << 9; + m_receive_parity = false; + } + else + { + // false start bit + m_receive_started = false; + m_receive_count = 0; + } + } + else + { + // shift in data, parity or stop bit + m_receive_sr >>= 1; + if (get_serial_in()) + m_receive_sr |= 1 << 9; + + if (BIT(m_receive_sr, 0)) + { + // received full character + m_receive_buffer = (m_receive_sr & 0x1fe) >> 1; + request_interrupt(3); + m_receive_started = false; + m_receive_count = 0; + } + else + { + if (BIT(m_receive_sr, 9)) + m_receive_parity = !m_receive_parity; + + // parity replaces received bit 7 if selected + if (BIT(m_receive_sr, 1) && (m_p3m & Z8_P3M_PARITY) != 0) + { + if (m_receive_parity) + m_receive_sr |= 1 << 9; + else + m_receive_sr &= ~(1 << 9); + } + } + } + } + } + else + { + // start bit is high-low transition + m_receive_sr >>= 1; + if (get_serial_in()) + m_receive_sr |= 1 << 9; + else if (BIT(m_receive_sr, 8)) + { + m_receive_started = true; + m_receive_sr = 0; + m_receive_count = 0; + } + } + + if (m_transmit_sr != 0) + { + m_transmit_count = (m_transmit_count + 1) & 15; + if (m_transmit_count == 0) + { + m_transmit_sr >>= 1; + if (m_transmit_sr == 0) + request_interrupt(4); + else + { + // parity replaces received bit 7 if selected + if ((m_transmit_sr >> 1) == 3 && (m_p3m & Z8_P3M_PARITY) != 0) + { + if (m_transmit_parity) + m_transmit_sr |= 1; + else + m_transmit_sr &= ~1; + } + else if (BIT(m_transmit_sr, 0)) + m_transmit_parity = !m_transmit_parity; + + // serial output + logerror("Transmitting %d bit (time = %.2f usec)\n", BIT(m_transmit_sr, 0), machine().time().as_double() * 1.0E6); + p3_update_output(); + } + } + } } uint8_t z8_device::sio_read() { - return 0xff; + return m_receive_buffer; } void z8_device::sio_write(uint8_t data) { + // overwrite shift register with data + 1 start bit + 2 stop bits + m_transmit_sr = (m_transmit_sr & 1) | (uint16_t(data) << 2) | (3 << 10); + m_transmit_parity = false; + + // synchronize the shift clock + m_transmit_count = 15; } template @@ -569,13 +674,13 @@ void z8_device::t1_trigger() void z8_device::tout_init() { m_tout = true; - m_output_cb[3](0, (m_output[3] & ~Z8_P3_TOUT) | (m_tout ? Z8_P3_TOUT : 0), Z8_P3_TOUT); + p3_update_output(); } void z8_device::tout_toggle() { m_tout = !m_tout; - m_output_cb[3](0, (m_output[3] & ~Z8_P3_TOUT) | (m_tout ? Z8_P3_TOUT : 0), Z8_P3_TOUT); + p3_update_output(); } uint8_t z8_device::tmr_read() @@ -720,7 +825,16 @@ void z8_device::p2m_write(uint8_t data) void z8_device::p3m_write(uint8_t data) { + if ((data & Z8_P3M_P3_SERIAL) == 0) + { + m_transmit_sr = 0; + m_transmit_count = 0; + m_receive_started = false; + m_receive_count = 0; + } + m_p3m = data; + p3_update_output(); } void z8_device::ipr_write(uint8_t data) @@ -1115,8 +1229,17 @@ void z8_device::device_start() m_p01m = 0; m_p2m = 0; m_p3m = 0; + m_p3_output = 0; m_tmr = 0; m_tout = true; + m_transmit_sr = 0; + m_transmit_count = 0; + m_transmit_parity = false; + m_receive_buffer = 0; + m_receive_sr = 0; + m_receive_count = 0; + m_receive_parity = false; + m_receive_started = false; m_irq_taken = false; m_irq_initialized = false; @@ -1131,9 +1254,18 @@ void z8_device::device_start() save_item(NAME(m_p01m)); save_item(NAME(m_p2m)); save_item(NAME(m_p3m)); + save_item(NAME(m_p3_output)); save_item(NAME(m_tmr)); save_item(NAME(m_t)); save_item(NAME(m_tout)); + save_item(NAME(m_transmit_sr)); + save_item(NAME(m_transmit_count)); + save_item(NAME(m_transmit_parity)); + save_item(NAME(m_receive_buffer)); + save_item(NAME(m_receive_sr)); + save_item(NAME(m_receive_count)); + save_item(NAME(m_receive_parity)); + save_item(NAME(m_receive_started)); save_item(NAME(m_count)); save_item(NAME(m_pre)); save_item(NAME(m_pre_count)); @@ -1336,16 +1468,16 @@ void z8_device::device_reset() m_imr &= ~Z8_IMR_ENABLE; m_irq_initialized = false; - p01m_write(0x4d); - p2m_write(0xff); - p3m_write(0x00); - p3_write(m_output[3] | 0xf0); - m_pre[0] &= ~Z8_PRE0_COUNT_MODULO_N; m_pre[1] &= ~(Z8_PRE1_COUNT_MODULO_N | Z8_PRE1_INTERNAL_CLOCK); m_tmr = 0x00; timer_stop<0>(); timer_stop<1>(); + + m_output[3] = 0xf0; + p01m_write(0x4d); + p2m_write(0xff); + p3m_write(0x00); } diff --git a/src/devices/cpu/z8/z8.h b/src/devices/cpu/z8/z8.h index a51fb8c660e..908b7ff570d 100644 --- a/src/devices/cpu/z8/z8.h +++ b/src/devices/cpu/z8/z8.h @@ -101,6 +101,7 @@ private: uint8_t m_p01m; // port 0/1 mode uint8_t m_p2m; // port 2 mode uint8_t m_p3m; // port 3 mode + uint8_t m_p3_output; // port 3 output (alternate functions included) // timer registers uint8_t m_tmr; // timer mode @@ -110,6 +111,18 @@ private: uint8_t m_pre_count[2]; // prescaler counts bool m_tout; // toggle output + // serial transmitter registers + uint16_t m_transmit_sr; // transmitter shift register + uint8_t m_transmit_count; // counter for transmitter timing + bool m_transmit_parity; // transmitter parity calculation + + // serial receiver registers + uint8_t m_receive_buffer; // received character + uint16_t m_receive_sr; // receiver shift register + uint8_t m_receive_count; // counter for receiver timing + bool m_receive_parity; // receiver parity calculation + bool m_receive_started; // true if receiver has seen start bit + // fake registers uint8_t m_fake_r[16]; // fake working registers @@ -124,6 +137,7 @@ private: // timers emu_timer *m_internal_timer[2]; + bool get_serial_in(); void sio_tick(); template void timer_start(); @@ -147,6 +161,7 @@ private: void p2_write(uint8_t data); uint8_t p3_read(); void p3_write(uint8_t data); + void p3_update_output(); uint8_t sio_read(); void sio_write(uint8_t data); uint8_t tmr_read(); -- cgit v1.2.3