diff options
Diffstat (limited to 'src/devices/machine/i8256.cpp')
| -rw-r--r-- | src/devices/machine/i8256.cpp | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/src/devices/machine/i8256.cpp b/src/devices/machine/i8256.cpp new file mode 100644 index 00000000000..ae934677534 --- /dev/null +++ b/src/devices/machine/i8256.cpp @@ -0,0 +1,512 @@ +#include "emu.h" +#include "i8256.h" + +#define VERBOSE 1 +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(I8256, i8256_device, "intel_8256", "Intel 8256AH Multifunction microprocessor support controller") + +i8256_device::i8256_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, I8256, tag, owner, clock), + device_serial_interface(mconfig, *this), + m_in_inta_cb(*this, 0), + m_out_int_cb(*this), + m_in_extint_cb(*this, 0), + m_rxc(0), + m_rxd(1), + m_cts(1), + m_txc(0), + m_txd_handler(*this), + m_in_p2_cb(*this, 0), + m_out_p2_cb(*this), + m_in_p1_cb(*this, 0), + m_out_p1_cb(*this), + + m_timer(nullptr) +{ +} + +void i8256_device::device_start() +{ + save_item(NAME(m_command1)); + save_item(NAME(m_command2)); + save_item(NAME(m_command3)); + save_item(NAME(m_mode)); + save_item(NAME(m_port1_control)); + save_item(NAME(m_interrupts)); + save_item(NAME(m_current_interrupt_level)); + save_item(NAME(m_rx_buffer)); + save_item(NAME(m_tx_buffer)); + save_item(NAME(m_port1_int)); + save_item(NAME(m_port2_int)); + save_item(NAME(m_timers[0])); + save_item(NAME(m_timers[1])); + save_item(NAME(m_timers[2])); + save_item(NAME(m_timers[3])); + save_item(NAME(m_timers[4])); + save_item(NAME(m_status)); + + m_timer = timer_alloc(FUNC(i8256_device::timer_check), this); +} + +void i8256_device::device_reset() +{ + m_command1 = 0; + m_command2 = 0; + m_command3 = 0; + m_mode = 0; + m_port1_control = 0; + m_interrupts = 0; + + m_tx_buffer = 0; + m_rx_buffer = 0; + + m_port1_int = 0; + m_port2_int = 0; + + m_timers[0] = 0; + m_timers[1] = 0; + m_timers[2] = 0; + m_timers[3] = 0; + m_timers[4] = 0; + + m_status = 0x30; // TRE and TBE + + m_timer->adjust(attotime::from_hz(16000), 0, attotime::from_hz(16000)); +} + +TIMER_CALLBACK_MEMBER(i8256_device::timer_check) +{ + for (int i = 0; i < 5; ++i) { + if (m_timers[i] > 0) { + m_timers[i]--; + if (m_timers[i] == 0 && BIT(m_interrupts,timer_interrupt[i])) { // If the interrupt is enabled + m_current_interrupt_level = timer_interrupt[i]; + m_out_int_cb(1); // it occurs when the counter changes from 1 to 0. + } + } + } +} + +uint8_t i8256_device::read(offs_t offset) +{ + // In the 8-bit mode, AD0-AD3 are used to select the proper register, while AD1-AD4 are used in the 16-bit mode. + // AD4 in the 8-bit mote is ignored as an address, while AD0 in the 16-bit mode is used as a second chip select, active low. + if (BIT(m_command1,I8256_CMD1_8086)) + { + offset = offset >> 1; + } + + u8 reg = offset & 0x0f; + + switch (reg) + { + case I8256_REG_CMD1: + return m_command1; + case I8256_REG_CMD2: + return m_command2; + case I8256_REG_CMD3: + return m_command3 & 0x76; // When command Register 3 is read, bits 0, 3, and 7 will always be zero. + case I8256_REG_MODE: + return m_mode; + case I8256_REG_PORT1C: + return m_port1_control; + case I8256_REG_INTEN: + return m_interrupts; + case I8256_REG_INTAD: + m_out_int_cb(0); + return m_current_interrupt_level*4; + case I8256_REG_BUFFER: + return m_rx_buffer; + case I8256_REG_PORT1: + return m_port1_int; + case I8256_REG_PORT2: + return m_port2_int; + case I8256_REG_TIMER1: + case I8256_REG_TIMER2: + case I8256_REG_TIMER3: + case I8256_REG_TIMER4: + case I8256_REG_TIMER5: + return m_timers[reg-10]; + case I8256_REG_STATUS: + return m_status; + default: + LOG("I8256 Read unmapped register: %u\n", reg); + return 0xff; + }; +} + +void i8256_device::write(offs_t offset, u8 data) +{ + + u8 reg = offset & 0x0f; + + // In the 8-bit mode, AD0-AD3 are used to select the proper register, while AD1-AD4 are used in the 16-bit mode. + // AD4 in the 8-bit mote is ignored as an address. + + if (BIT(m_command1,I8256_CMD1_8086)) + { + if (!BIT(offset,0)) // AD0 in the 16-bit mode is used as a second chip select, active low. + { + reg = (offset >> 1) & 0x0f; + } else { + return; + } + } + + switch (reg) + { + case I8256_REG_CMD1: + if (m_command1 != data) { + m_command1 = data; + + if (BIT(m_command1,I8256_CMD1_FRQ)) + { + m_timer->adjust(attotime::from_hz(1000), 0, attotime::from_hz(1000)); + } else { + m_timer->adjust(attotime::from_hz(16000), 0, attotime::from_hz(16000)); + } + + if (BIT(m_command1,I8256_CMD1_8086)) + { + LOG("I8256 Enabled 8086 mode\n"); + } + + m_data_bits_count = 8-((BIT(m_command1, I8256_CMD1_L0)) | (BIT(m_command1, I8256_CMD1_L1) << 1)); + m_stop_bits = stopBits[(BIT(m_command1, I8256_CMD1_S0)) | (BIT(m_command1, I8256_CMD1_S1) << 1)]; + + set_data_frame(1, m_data_bits_count, m_parity, m_stop_bits); + } + break; + case I8256_REG_CMD2: + if (m_command2 != data) { + m_command2 = data; + + set_rate(baudRates[m_command2 & 0x0f]); + + if (BIT(m_command2,I8256_CMD2_PARITY_ENABLE)) + m_parity = BIT(m_command2,I8256_CMD2_EVEN_PARITY) ? PARITY_EVEN : PARITY_ODD; + else + m_parity = PARITY_NONE; + + set_data_frame(1, m_data_bits_count, m_parity, m_stop_bits); + + LOG("I8256 Clock Scale: %u\n", sysclockDivider[(m_command2 & 0x30 >> 4)]); + if((clock() / sysclockDivider[(m_command2 & 0x30 >> 4)])!=1024000) + { + LOG("I8256 Internal Clock should be 1024000, calculated: %u\n", (clock() / sysclockDivider[(m_command2 & 0x30 >> 4)])); + } + } + break; + case I8256_REG_CMD3: + m_command3 = data; + if (BIT(m_command3,I8256_CMD3_RST)) { + m_interrupts = 0; + m_status = 0x30; + } + break; + case I8256_REG_MODE: + m_mode = data; + break; + case I8256_REG_PORT1C: + m_port1_control = data; + break; + case I8256_REG_INTEN: + m_interrupts = m_interrupts | data; + break; + case I8256_REG_INTAD: // reset interrupt + m_interrupts = m_interrupts & ~data; + break; + case I8256_REG_BUFFER: + LOG("I8256 write serial: %u\n", data); + m_tx_buffer = data; + break; + case I8256_REG_PORT1: + m_port1_int = data; + break; + case I8256_REG_PORT2: + m_port2_int = data; + break; + case I8256_REG_TIMER1: + case I8256_REG_TIMER2: + case I8256_REG_TIMER3: + case I8256_REG_TIMER4: + case I8256_REG_TIMER5: + m_timers[reg-10] = data; + break; + case I8256_REG_STATUS: + m_modification = data; + break; + default: + LOG("I8256 Unmapped write %02x to %02x\n", data, reg); + break; + }; +} + +uint8_t i8256_device::p1_r() +{ + // if control bit is 0 (input), read from callback else use output latch + uint8_t input = m_in_p1_cb(0); + uint8_t result = 0; + for (int i = 0; i < 8; i++) { + if (BIT(m_port1_control, i)) // output + result |= (m_port1_int & (1 << i)); + else // input + result |= (input & (1 << i)); + } + return result; +} + +void i8256_device::p1_w(uint8_t data) +{ + m_port1_int = (m_port1_int & ~m_port1_control) | (data & m_port1_control); + m_out_p1_cb(0, m_port1_int & m_port1_control); +} + +uint8_t i8256_device::p2_r() +{ + uint8_t p2c = m_mode & 0x03; + if (p2c == I8256_PORT2C_II || p2c == I8256_PORT2C_IO) + return m_in_p2_cb(0); + else + return m_port2_int; +} + +void i8256_device::p2_w(uint8_t data) +{ + uint8_t p2c = m_mode & 0x03; + m_port2_int = data; + uint8_t port2_data = 0; + switch (p2c) + { + case I8256_PORT2C_IO: port2_data = m_port2_int & 0x0f; break; + case I8256_PORT2C_OI: port2_data = m_port2_int & 0xf0; break; + case I8256_PORT2C_OO: port2_data = m_port2_int; break; + default: port2_data = 0; break; + } + if (p2c == I8256_PORT2C_IO || p2c == I8256_PORT2C_OI || p2c == I8256_PORT2C_OO) + m_out_p2_cb(0, port2_data); +} + +/*------------------------------------------------- + receive_clock +-------------------------------------------------*/ + +void i8256_device::receive_clock() +{ + // receive enable? + if (BIT(m_command3, I8256_CMD3_RxE)) + { + const bool SYNC = is_receive_register_synchronized(); + if (SYNC) + { + --m_rxc_count; + if (m_rxc_count) + return; + } + + //logerror("i8256\n"); + // get bit received from other side and update receive register + //LOGBITS("8256: Rx Sampled %d\n", m_rxd); + receive_register_update_bit(m_rxd); + if (is_receive_register_synchronized()) + m_rxc_count = SYNC ? m_br_factor : (3 * m_br_factor / 2); + + if (is_receive_register_full()) + { + receive_register_extract(); + if (is_receive_parity_error()) + m_status |= I8256_STATUS_PARITY_ERROR; + if (is_receive_framing_error()) + m_status |= I8256_STATUS_FRAMING_ERROR; + receive_character(get_received_char()); + } + } +} + +bool i8256_device::calc_parity(u8 ch) +{ + bool data = 0; + for (u8 b = 0; b < 8; b++) + data ^= BIT(ch, b); + return data; +} + +void i8256_device::sync1_rxc() +{ + // is rx enabled? + if (!BIT(m_command3, I8256_CMD3_RxE)) + return; + + u8 need_parity = BIT(m_command2, I8256_CMD2_PARITY_ENABLE); + + // see about parity + if (need_parity && (m_rxd_bits == m_data_bits_count)) + { + if (calc_parity(m_sync1) != m_rxd) + m_status |= I8256_STATUS_PARITY_ERROR; + // and then continue on as if everything was ok + } + else + { + // add bit to byte + m_sync1 = (m_sync1 >> 1) | (m_rxd << (m_data_bits_count-1)); + } + + // is byte complete? if not, quit + m_rxd_bits++; + if (m_rxd_bits < (m_data_bits_count + need_parity)) + return; + + // now we have a synchronised byte, and parity has been dealt with + + // copy byte to rx buffer + receive_character(m_sync1); + + m_rxd_bits = 0; + m_sync1 = 0; +} + +void i8256_device::sync2_rxc() +{ + // is rx enabled? + if (!BIT(m_command3, I8256_CMD3_RxE)) + return; + + u8 need_parity = BIT(m_command2, I8256_CMD2_PARITY_ENABLE); + + // see about parity + if (need_parity && (m_rxd_bits == m_data_bits_count)) + { + if (calc_parity(m_sync1) != m_rxd) + m_status |= I8256_STATUS_PARITY_ERROR; + // and then continue on as if everything was ok + } + else + { + // add bit to byte + m_sync1 = (m_sync1 >> 1) | (m_rxd << (m_data_bits_count-1)); + m_sync2 = (m_sync2 >> 1) | (m_rxd << (m_data_bits_count*2-1)); + } + + // is byte complete? if not, quit + m_rxd_bits++; + if (m_rxd_bits < (m_data_bits_count + need_parity)) + return; + + // now we have a synchronised byte, and parity has been dealt with + + // copy byte to rx buffer + receive_character(m_sync1); + + m_rxd_bits = 0; + m_sync1 = 0; + m_sync2 = 0; +} + +/*------------------------------------------------- + check_for_tx_start +-------------------------------------------------*/ +void i8256_device::check_for_tx_start() +{ + if (!BIT(m_status,I8256_STATUS_TR_EMPTY)) + { + start_tx(); + } +} + +/*------------------------------------------------- + start_tx +-------------------------------------------------*/ +void i8256_device::start_tx() +{ + LOG("start_tx %02x\n", m_tx_data); + transmit_register_setup(m_tx_data); + m_status &= ~I8256_STATUS_TR_EMPTY; +} + +/*------------------------------------------------- + transmit_clock +-------------------------------------------------*/ + +void i8256_device::transmit_clock() +{ + m_txc_count++; + if (m_txc_count != m_br_factor) + return; + + m_txc_count = 0; + + if (is_transmit_register_empty()) + { + start_tx(); + + } + + // if diserial has bits to send, make them so + if (!is_transmit_register_empty()) + { + uint8_t data = transmit_register_get_data_bit(); + LOG("I8256: Tx Present a %d\n", data); + m_txd_handler(data); + } +} + +void i8256_device::receive_character(uint8_t ch) +{ + LOG("I8256: receive_character %02x\n", ch); + + m_rx_data = ch; + + LOG("status RX READY test %02x\n", m_status); + // char has not been read and another has arrived! + if (BIT(m_status, I8256_STATUS_RB_FULL)) + { + m_status |= I8256_STATUS_OVERRUN_ERROR; + LOG("status overrun set\n"); + } +} + +void i8256_device::write_rxd(int state) +{ + m_rxd = state; + LOG("I8256: Presented a %d\n", m_rxd); + // device_serial_interface::rx_w(state); +} + +void i8256_device::write_cts(int state) +{ + m_cts = state; + + if (started()) + { + check_for_tx_start(); + } +} + +void i8256_device::write_rxc(int state) +{ + if (!m_rxc && state) + { + if (m_sync_byte_count == 1) + sync1_rxc(); + else + if (m_sync_byte_count == 2) + sync2_rxc(); + else + receive_clock(); + } + + m_rxc = state; +} + +void i8256_device::write_txc(int state) +{ + if (m_txc != state) + { + m_txc = state; + + if (!m_txc) + transmit_clock(); + } +} |
