diff options
Diffstat (limited to 'src/mame/machine/imds2ioc.cpp')
-rw-r--r-- | src/mame/machine/imds2ioc.cpp | 690 |
1 files changed, 690 insertions, 0 deletions
diff --git a/src/mame/machine/imds2ioc.cpp b/src/mame/machine/imds2ioc.cpp new file mode 100644 index 00000000000..8295d431e03 --- /dev/null +++ b/src/mame/machine/imds2ioc.cpp @@ -0,0 +1,690 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi +// I/O controller for Intel Intellec MDS series-II +// +// NOTE: +// Firmware running on PIO is NOT original because a dump is not available at the moment. +// Emulator runs a version of PIO firmware that was specifically developed by me to implement +// line printer output. +// +// TODO: +// - Find a dump of the original PIO firmware +// - Adjust speed of processors. Wait states are not accounted for yet. + +#include "emu.h" +#include "machine/imds2ioc.h" +#include "screen.h" +#include "speaker.h" + +// Main oscillator of IOC board: 22.032 MHz +#define IOC_XTAL_Y2 22.032_MHz_XTAL + +// FDC oscillator of IOC board: 8 MHz +#define IOC_XTAL_Y1 8_MHz_XTAL + +// PIO oscillator: 6 MHz +#define IOC_XTAL_Y3 6_MHz_XTAL + +// Frequency of beeper +#define IOC_BEEP_FREQ 3300 + +// device type definition +DEFINE_DEVICE_TYPE(IMDS2IOC, imds2ioc_device, "imds2ioc", "Intellec Series II Input/Output Controller") + +void imds2ioc_device::mem_map(address_map &map) +{ + map.unmap_value_high(); + map(0x0000, 0x1fff).rom(); + map(0x4000, 0x5fff).ram(); +} + +void imds2ioc_device::io_map(address_map &map) +{ + map.unmap_value_high(); + map(0x00, 0x0f).w(FUNC(imds2ioc_device::ioc_dbbout_w)); + map(0x20, 0x2f).w(FUNC(imds2ioc_device::ioc_f0_w)); + map(0x30, 0x3f).w(FUNC(imds2ioc_device::ioc_set_f1_w)); + map(0x40, 0x4f).w(FUNC(imds2ioc_device::ioc_reset_f1_w)); + map(0x50, 0x5f).w(FUNC(imds2ioc_device::start_timer_w)); + map(0x60, 0x6f).w(FUNC(imds2ioc_device::miscout_w)); + map(0x80, 0x8f).r(FUNC(imds2ioc_device::miscin_r)); + map(0x90, 0x9f).r(FUNC(imds2ioc_device::kb_read)); + map(0xa0, 0xaf).r(FUNC(imds2ioc_device::ioc_status_r)); + map(0xb0, 0xbf).r(FUNC(imds2ioc_device::ioc_dbbin_r)); + map(0xc0, 0xcf).m(m_iocfdc, FUNC(i8271_device::map)); + map(0xd0, 0xdf).rw(m_ioccrtc, FUNC(i8275_device::read), FUNC(i8275_device::write)); + map(0xe0, 0xef).rw(m_ioctimer, FUNC(pit8253_device::read), FUNC(pit8253_device::write)); +// DMA controller range doesn't extend to 0xff because register 0xfd needs to be read as 0xff +// This register is used by IOC firmware to detect DMA controller model (either 8237 or 8257) + map(0xf0, 0xf8).rw(m_iocdma, FUNC(i8257_device::read), FUNC(i8257_device::write)); +} + +imds2ioc_device::imds2ioc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, IMDS2IOC, tag, owner, clock), + m_ioccpu(*this, "ioccpu"), + m_iocdma(*this, "iocdma"), + m_ioccrtc(*this, "ioccrtc"), + m_iocbeep(*this, "iocbeep"), + m_ioctimer(*this, "ioctimer"), + m_iocfdc(*this, "iocfdc"), + m_flop0(*this, "iocfdc:0"), + m_iocpio(*this, "iocpio"), + m_kbcpu(*this, "kbcpu"), + m_palette(*this, "palette"), + m_gfxdecode(*this, "gfxdecode"), + m_centronics(*this, "centronics"), + m_io_key(*this, "KEY%u", 0U), + m_ioc_options(*this, "IOC_OPTS"), + m_master_intr_cb(*this), + m_parallel_int_cb(*this), + m_chargen(*this, "gfx1"), + m_device_status_byte(0xff) +{ +} + + +WRITE8_MEMBER(imds2ioc_device::miscout_w) +{ + m_miscout = data; + update_beeper(); + // Send INTR to IPC + m_master_intr_cb(BIT(m_miscout, 1)); +} + +READ8_MEMBER(imds2ioc_device::miscin_r) +{ + uint8_t res = m_ioc_options->read(); + return res | ((m_beeper_timer == 0) << 2); +} + +WRITE_LINE_MEMBER(imds2ioc_device::beep_timer_w) +{ + m_beeper_timer = state; + update_beeper(); +} + +WRITE8_MEMBER(imds2ioc_device::start_timer_w) +{ + // Trigger timer 2 of ioctimer + m_ioctimer->write_gate2(0); + m_ioctimer->write_gate2(1); +} + +READ8_MEMBER(imds2ioc_device::kb_read) +{ + return m_kbcpu->upi41_master_r(space, (offset & 2) >> 1); +} + +READ8_MEMBER(imds2ioc_device::kb_port_p2_r) +{ + if ((m_kb_p1 & 3) == 0) { + // Row selected + // Row number is encoded on bits P15..P12, they are "backwards" (P15 is LSB) and keyboard rows are encoded starting with value 2 on these bits (see A4, pg 56 of schematic) + unsigned row = (m_kb_p1 >> 2) & 0x0f; + ioport_value data; + + switch (row) { + case 4: + // Row 0 + data = m_io_key[0]->read(); + break; + + case 12: + // Row 1 + data = m_io_key[1]->read(); + break; + + case 2: + // Row 2 + data = m_io_key[2]->read(); + break; + + case 10: + // Row 3 + data = m_io_key[3]->read(); + break; + + case 6: + // Row 4 + data = m_io_key[4]->read(); + break; + + case 14: + // Row 5 + data = m_io_key[5]->read(); + break; + + case 1: + // Row 6 + data = m_io_key[6]->read(); + break; + + case 9: + // Row 7 + data = m_io_key[7]->read(); + break; + + default: + data = 0xff; + break; + } + return data & 0xff; + } else { + // No row selected + return 0xff; + } +} + +WRITE8_MEMBER(imds2ioc_device::kb_port_p1_w) +{ + m_kb_p1 = data; +} + +READ_LINE_MEMBER(imds2ioc_device::kb_port_t0_r) +{ + // T0 tied low + // It appears to be some kind of strapping option on kb hw + return 0; +} + +READ_LINE_MEMBER(imds2ioc_device::kb_port_t1_r) +{ + // T1 tied low + // It appears to be some kind of strapping option on kb hw + return 0; +} + +WRITE8_MEMBER(imds2ioc_device::ioc_dbbout_w) +{ + m_ioc_obf = ~data; + // Set/reset OBF flag (b0) + m_ipc_ioc_status = ((offset & 1) == 0) | (m_ipc_ioc_status & ~0x01); +} + +WRITE8_MEMBER(imds2ioc_device::ioc_f0_w) +{ + // Set/reset F0 flag (b2) + m_ipc_ioc_status = ((offset & 1) << 2) | (m_ipc_ioc_status & ~0x04); +} + +WRITE8_MEMBER(imds2ioc_device::ioc_set_f1_w) +{ + // Set F1 flag (b3) + m_ipc_ioc_status |= 0x08; +} + +WRITE8_MEMBER(imds2ioc_device::ioc_reset_f1_w) +{ + // Reset F1 flag (b3) + m_ipc_ioc_status &= ~0x08; +} + +READ8_MEMBER(imds2ioc_device::ioc_status_r) +{ + return ~m_ipc_ioc_status; +} + +READ8_MEMBER(imds2ioc_device::ioc_dbbin_r) +{ + // Reset IBF flag (b1) + if (!machine().side_effects_disabled()) + m_ipc_ioc_status &= ~0x02; + return ~m_ioc_ibf; +} + +READ8_MEMBER(imds2ioc_device::dbb_master_r) +{ + // Read status register + if (BIT(offset, 0)) + return m_ipc_ioc_status; + else + { + // Reset OBF flag (b0) + if (!machine().side_effects_disabled()) + m_ipc_ioc_status &= ~0x01; + + // Read output buffer + return m_ioc_obf; + } +} + +WRITE8_MEMBER(imds2ioc_device::dbb_master_w) +{ + // Set IBF flag (b1) + m_ipc_ioc_status |= 0x02; + + // Command/Data = Master A0 + if (BIT(offset, 0)) + { + // Set F1 flag (b3) + m_ipc_ioc_status |= 0x08; + } + else + { + // Reset F1 flag (b3) + m_ipc_ioc_status &= ~0x08; + } + + // Set input buffer + m_ioc_ibf = data; +} + +WRITE_LINE_MEMBER(imds2ioc_device::hrq_w) +{ + // Should be propagated to HOLD input of IOC CPU + m_iocdma->hlda_w(state); +} + +READ8_MEMBER(imds2ioc_device::ioc_mem_r) +{ + address_space& prog_space = m_ioccpu->space(AS_PROGRAM); + return prog_space.read_byte(offset); +} + +WRITE8_MEMBER(imds2ioc_device::ioc_mem_w) +{ + address_space& prog_space = m_ioccpu->space(AS_PROGRAM); + return prog_space.write_byte(offset, data); +} + +READ8_MEMBER(imds2ioc_device::pio_port_p1_r) +{ + // If STATUS ENABLE/ == 0 return inverted device status byte, else return 0xff + // STATUS ENABLE/ == 0 when P23-P20 == 12 & P24 == 0 & P25 = 1 & P26 = 1 + if ((m_pio_port2 & 0x7f) == 0x6c) { + return ~m_device_status_byte; + } else { + return 0xff; +} +} + +WRITE8_MEMBER(imds2ioc_device::pio_port_p1_w) +{ + m_pio_port1 = data; + update_printer(); +} + +READ8_MEMBER(imds2ioc_device::pio_port_p2_r) +{ + return m_pio_port2; +} + +WRITE8_MEMBER(imds2ioc_device::pio_port_p2_w) +{ + m_pio_port2 = data; + update_printer(); + // Send INTR to IPC + m_parallel_int_cb(BIT(data, 7)); +} + +WRITE_LINE_MEMBER(imds2ioc_device::pio_lpt_ack_w) +{ + if (state) { + m_device_status_byte |= 0x20; + } else { + m_device_status_byte &= ~0x20; + } +} + +WRITE_LINE_MEMBER(imds2ioc_device::pio_lpt_busy_w) +{ + // Busy is active high in centronics_device whereas it's active low in MDS + if (!state) { + m_device_status_byte |= 0x10; + } else { + m_device_status_byte &= ~0x10; + } +} + +WRITE_LINE_MEMBER(imds2ioc_device::pio_lpt_select_w) +{ + if (state) { + m_device_status_byte |= 0x40; + } else { + m_device_status_byte &= ~0x40; + } +} + +I8275_DRAW_CHARACTER_MEMBER(imds2ioc_device::crtc_display_pixels) +{ + unsigned i; + const rgb_t *palette = m_palette->palette()->entry_list_raw(); + uint8_t chargen_byte = m_chargen[ (linecount & 7) | ((unsigned)charcode << 3) ]; + uint16_t pixels; + + if (lten) { + pixels = ~0; + } else if (vsp != 0 || (linecount & 8) != 0) { + pixels = 0; // VSP is gated with LC3 + } else { + // See hardware ref. manual, pg 58 for the very peculiar way of generating character images + // Here each half-pixel is translated into a full pixel + uint16_t exp_pix_l; + uint16_t exp_pix_r; + + exp_pix_l = (uint16_t)chargen_byte; + exp_pix_l = ((exp_pix_l & 0x80) << 5) | + ((exp_pix_l & 0x40) << 4) | + ((exp_pix_l & 0x20) << 3) | + ((exp_pix_l & 0x10) << 2) | + ((exp_pix_l & 0x08) << 1) | + (exp_pix_l & 0x04); + exp_pix_l |= (exp_pix_l << 1); + exp_pix_r = exp_pix_l; + + // Layout of exp_pix_l/r: + // Bit # : F E D C B A 9 8 7 6 5 4 3 2 1 0 + // Bit of chargen_byte: 0 0 b7 b7 b6 b6 b5 b5 b4 b4 b3 b3 b2 b2 0 0 + if ((chargen_byte & 2) == 0) { + exp_pix_l >>= 1; + } + exp_pix_l &= 0x3fc0; + + if ((chargen_byte & 1) == 0) { + exp_pix_r >>= 1; + } + exp_pix_r &= 0x003e; + + pixels = exp_pix_l | exp_pix_r; + } + + if (rvv) { + pixels = ~pixels; + } + + for (i = 0; i < 14; i++) { + bitmap.pix32(y, x + i) = palette[ (pixels & (1U << (13 - i))) != 0 ]; + } +} + +READ8_MEMBER(imds2ioc_device::pio_master_r) +{ + return m_iocpio->upi41_master_r(space, offset); +} + +WRITE8_MEMBER(imds2ioc_device::pio_master_w) +{ + m_iocpio->upi41_master_w(space, offset, data); +} + +void imds2ioc_device::device_resolve_objects() +{ + m_master_intr_cb.resolve_safe(); + m_parallel_int_cb.resolve_safe(); +} + +void imds2ioc_device::device_start() +{ + m_iocfdc->set_ready_line_connected(true); +} + +void imds2ioc_device::device_reset() +{ + m_ipc_ioc_status = 0x0f; + + m_iocfdc->set_rate(500000); // The IMD images show a rate of 500kbps +} + +void imds2ioc_device::update_beeper() +{ + m_iocbeep->set_state(m_beeper_timer == 0 && BIT(m_miscout, 0) == 0); +} + +void imds2ioc_device::update_printer() +{ + // Data to printer is ~P1 when STATUS ENABLE/==1, else 0xff (assuming pull-ups on printer) + uint8_t printer_data; + if ((m_pio_port2 & 0x7f) == 0x6c) { + printer_data = 0xff; + } else { + printer_data = ~m_pio_port1; + } + m_centronics->write_data0(BIT(printer_data, 0)); + m_centronics->write_data1(BIT(printer_data, 1)); + m_centronics->write_data2(BIT(printer_data, 2)); + m_centronics->write_data3(BIT(printer_data, 3)); + m_centronics->write_data4(BIT(printer_data, 4)); + m_centronics->write_data5(BIT(printer_data, 5)); + m_centronics->write_data6(BIT(printer_data, 6)); + m_centronics->write_data7(BIT(printer_data, 7)); + + // LPT DATA STROBE/ == 0 when P23-P20 == 9 & P24 == 0 + m_centronics->write_strobe((m_pio_port2 & 0x1f) != 0x09); +} + +static INPUT_PORTS_START(imds2ioc) + // See schematic, pg 56 for key matrix layout + // See schematic, pg 57 for keyboard layout + PORT_START("KEY0") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t') // OK + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR('@') PORT_CHAR('`') // OK + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') // OK + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) // OK + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') // OK + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(':') PORT_CHAR('*') // OK + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') // OK + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') // OK + + PORT_START("KEY1") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') // OK + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') // OK + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') // OK + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') // OK + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') // OK + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') // OK + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') // OK + + PORT_START("KEY2") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('~') // OK + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{') // OK + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') // OK + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') // OK + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')') // OK + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=') // OK + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') // OK + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(';') PORT_CHAR('+') // OK + + PORT_START("KEY3") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') // OK + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') // OK + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') // OK + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') // OK + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') // OK + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') // OK + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') // OK + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') // OK + + PORT_START("KEY4") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') // OK + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') // OK + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') // OK + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') // OK + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') // OK + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') // OK + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') // OK + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') // OK + + PORT_START("KEY5") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"') // OK + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') // OK + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(') // OK + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') // OK + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') // OK + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') // OK + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'') // OK + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&') // OK + + PORT_START("KEY6") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) // BS + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME)) // OK + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('|') // OK + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) // OK + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2) // OK + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) // OK + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}') // OK + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) // OK + + PORT_START("KEY7") + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) // OK + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) // OK + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('_') PORT_CHAR('^') // OK + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) // OK + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT)) // OK + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED) + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED) + + // Options on IOC: see schematic, pg 40 + PORT_START("IOC_OPTS") + PORT_DIPNAME(0x80, 0x80, "Keyboard present") + PORT_DIPSETTING(0x80, DEF_STR(Yes)) + PORT_DIPSETTING(0x00, DEF_STR(No)) + PORT_DIPNAME(0x28, 0x00, "IOC mode") + PORT_DIPSETTING(0x00, "On line") + PORT_DIPSETTING(0x08, "Local") + PORT_DIPSETTING(0x20, "Diagnostic") + PORT_DIPNAME(0x02, 0x00, "Floppy present") + PORT_DIPSETTING(0x02, DEF_STR(Yes)) + PORT_DIPSETTING(0x00, DEF_STR(No)) + PORT_DIPNAME(0x01, 0x01, "CRT frame frequency") + PORT_DIPSETTING(0x01, "50 Hz") + PORT_DIPSETTING(0x00, "60 Hz") +INPUT_PORTS_END + +static GFXLAYOUT_RAW(imds2_charlayout, 8, 8, 8, 64) + +static GFXDECODE_START(gfx_imds2) + GFXDECODE_ENTRY("gfx1", 0x0000, imds2_charlayout, 0, 1) +GFXDECODE_END + +static void imds2_floppies(device_slot_interface &device) +{ + device.option_add("8sssd", FLOPPY_8_SSSD); +} + +MACHINE_CONFIG_START(imds2ioc_device::device_add_mconfig) + I8080A(config, m_ioccpu, IOC_XTAL_Y2 / 18); // 2.448 MHz but running at 50% (due to wait states & DMA usage of bus) + m_ioccpu->set_addrmap(AS_PROGRAM, &imds2ioc_device::mem_map); + m_ioccpu->set_addrmap(AS_IO, &imds2ioc_device::io_map); + MCFG_QUANTUM_TIME(attotime::from_hz(100)) + + // The IOC CRT hw is a bit complex, as the character clock (CCLK) to i8275 + // is varied according to the part of the video frame being scanned and according to + // the 50/60 Hz option jumper (W8). + // The basic clock (BCLK) runs at 22.032 MHz. + // CCLK = BCLK / 14 when in the active region of video + // CCLK = BCLK / 12 when in horizontal retrace (HRTC=1) + // CCLK = BCLK / 10 when in horizontal retrace of "short scan lines" (50 Hz only) + // + // ***** 50 Hz timings ***** + // 80 chars/row, 26 chars/h. retrace, 11 scan lines/row, 25 active rows, 3 vertical retrace rows + // Scan line: 80 chars * 14 BCLK + 26 chars * 12 BCLK = 1432 BCLK (64.996 usec) + // Scan row: 11 * scan lines = 15752 BCLK (714.960 usec) + // "Short" scan line: 80 chars * 14 BCLK + 26 chars * 10 BCLK = 1380 BCLK (62.636 usec) + // Frame: 28 scan rows (8 scan lines of 27th row are short): 27 * scan row + 3 * scan line + 8 * short scan line: 440640 BCLK (20 msec) + // + // ***** 60 Hz timings ***** + // 80 chars/row, 20 chars/h. retrace, 10 scan lines/row, 25 active rows, 2 vertical retrace rows + // Scan line: 80 chars * 14 BCLK + 20 chars * 12 BCLK = 1360 BCLK (61.728 usec) + // Scan row: 10 * scan lines = 13600 BCLK (617.284 usec) + // Frame: 27 scan rows : 367200 BCLK (16.667 msec) + // + // Clock here is semi-bogus: it gives the correct frame frequency at 50 Hz (with the incorrect + // assumption that CCLK is fixed at BCLK / 14) + I8275(config, m_ioccrtc, 22853600 / 14); + m_ioccrtc->set_character_width(14); + m_ioccrtc->set_display_callback(FUNC(imds2ioc_device::crtc_display_pixels), this); + m_ioccrtc->drq_wr_callback().set(m_iocdma, FUNC(i8257_device::dreq2_w)); + m_ioccrtc->irq_wr_callback().set_inputline(m_ioccpu, I8085_INTR_LINE); + m_ioccrtc->set_screen("screen"); + + screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); + screen.set_screen_update("ioccrtc", FUNC(i8275_device::screen_update)); + screen.set_refresh_hz(50); + GFXDECODE(config, "gfxdecode", "palette", gfx_imds2); + MCFG_PALETTE_ADD_MONOCHROME("palette") + + SPEAKER(config, "mono").front_center(); + BEEP(config, m_iocbeep, IOC_BEEP_FREQ).add_route(ALL_OUTPUTS, "mono", 1.00); + + I8257(config, m_iocdma, IOC_XTAL_Y2 / 9); + m_iocdma->out_hrq_cb().set(FUNC(imds2ioc_device::hrq_w)); + m_iocdma->in_memr_cb().set(FUNC(imds2ioc_device::ioc_mem_r)); + m_iocdma->out_memw_cb().set(FUNC(imds2ioc_device::ioc_mem_w)); + m_iocdma->in_ior_cb<1>().set("iocfdc", FUNC(i8271_device::data_r)); + m_iocdma->out_iow_cb<1>().set("iocfdc", FUNC(i8271_device::data_w)); + m_iocdma->out_iow_cb<2>().set(m_ioccrtc, FUNC(i8275_device::dack_w)); + + PIT8253(config, m_ioctimer, 0); + m_ioctimer->set_clk<0>(IOC_XTAL_Y1 / 4); + m_ioctimer->out_handler<0>().set(m_ioctimer, FUNC(pit8253_device::write_clk2)); + m_ioctimer->out_handler<2>().set(FUNC(imds2ioc_device::beep_timer_w)); + + I8271(config, m_iocfdc, IOC_XTAL_Y1 / 2); + m_iocfdc->drq_wr_callback().set(m_iocdma, FUNC(i8257_device::dreq1_w)); + MCFG_FLOPPY_DRIVE_ADD("iocfdc:0", imds2_floppies, "8sssd", floppy_image_device::default_floppy_formats) + MCFG_SLOT_FIXED(true) + + I8041(config, m_iocpio, IOC_XTAL_Y3); + m_iocpio->p1_in_cb().set(FUNC(imds2ioc_device::pio_port_p1_r)); + m_iocpio->p1_out_cb().set(FUNC(imds2ioc_device::pio_port_p1_w)); + m_iocpio->p2_in_cb().set(FUNC(imds2ioc_device::pio_port_p2_r)); + m_iocpio->p2_out_cb().set(FUNC(imds2ioc_device::pio_port_p2_w)); + + I8741(config, m_kbcpu, 3.579545_MHz_XTAL); + m_kbcpu->p1_out_cb().set(FUNC(imds2ioc_device::kb_port_p1_w)); + m_kbcpu->p2_in_cb().set(FUNC(imds2ioc_device::kb_port_p2_r)); + m_kbcpu->t0_in_cb().set(FUNC(imds2ioc_device::kb_port_t0_r)); + m_kbcpu->t1_in_cb().set(FUNC(imds2ioc_device::kb_port_t1_r)); + + CENTRONICS(config, m_centronics, centronics_devices, "printer"); + m_centronics->ack_handler().set(FUNC(imds2ioc_device::pio_lpt_ack_w)); + m_centronics->busy_handler().set(FUNC(imds2ioc_device::pio_lpt_busy_w)); + m_centronics->perror_handler().set(FUNC(imds2ioc_device::pio_lpt_select_w)); +MACHINE_CONFIG_END + +ROM_START(imds2ioc) + // ROM definition of IOC cpu (8080A) + ROM_REGION(0x2000, "ioccpu", 0) + ROM_DEFAULT_BIOS("v15") + ROM_SYSTEM_BIOS(0, "v15", "Series II IOC Diagnostic v1.5") // "CORP INTEL corp.1981-83" + ROMX_LOAD("ioc_a50_104692-001.bin", 0x0000, 0x0800, CRC(d9f926a1) SHA1(bd9d0f7458acc2806120a6dbaab9c48be315b060), ROM_BIOS(0)) + ROMX_LOAD("ioc_a51_104692-002.bin", 0x0800, 0x0800, CRC(6aa2f86c) SHA1(d3a5314d86e3366545b4c97b29e323dfab383d5f), ROM_BIOS(0)) + ROMX_LOAD("ioc_a52_104692-003.bin", 0x1000, 0x0800, CRC(b88a38d5) SHA1(934716a1daec852f4d1f846510f42408df0c9584), ROM_BIOS(0)) + ROMX_LOAD("ioc_a53_104692-004.bin", 0x1800, 0x0800, CRC(c8df4bb9) SHA1(2dfb921e94ae7033a7182457b2f00657674d1b77), ROM_BIOS(0)) + ROM_SYSTEM_BIOS(1, "v14", "Series II IOC Diagnostic v1.4") // "(C) Intel Corp. 1981,1982" + ROMX_LOAD("a62_2716.bin", 0x0000, 0x0800, CRC(86a55b2f) SHA1(21033f7abb2c3e08028613e0c35ffecb703ff4f1), ROM_BIOS(1)) + ROMX_LOAD("a51_2716.bin", 0x0800, 0x0800, CRC(ee55c448) SHA1(16c2f7e3b5baeb398adcc59603943910813e6a9b), ROM_BIOS(1)) + ROMX_LOAD("a52_2716.bin", 0x1000, 0x0800, CRC(8db1b33e) SHA1(6fc5e438009636dd6d7185071b152b0491d3baeb), ROM_BIOS(1)) + ROMX_LOAD("a53_2716.bin", 0x1800, 0x0800, CRC(01690f4f) SHA1(eadef30a3797f41e08d28e7691f8de44c0f3b8ea), ROM_BIOS(1)) + ROM_SYSTEM_BIOS(2, "v13", "Series II IOC Diagnostic v1.3") // no copyright string; accompanying installation instructions © 1980 Intel Corporation + ROMX_LOAD("104593-001.a50", 0x0000, 0x0800, CRC(801de7ad) SHA1(291b7db8a163b970e9082bdb637d7dee436c7a62), ROM_BIOS(2)) + ROMX_LOAD("104593-002.a51", 0x0800, 0x0800, CRC(6c8e21b6) SHA1(b3e63b2846a79866eb74ab354001996424ec22a0), ROM_BIOS(2)) + ROMX_LOAD("104593-003.a52", 0x1000, 0x0800, CRC(134c8115) SHA1(cd8c566344ee7a9cda78eea5fd2efdaca5a2e540), ROM_BIOS(2)) + ROMX_LOAD("104593-004.a53", 0x1800, 0x0800, CRC(04407e2a) SHA1(ecd65e4337d2bb7f5f6fdaae95696c9207ba57ee), ROM_BIOS(2)) + + // ROM definition of PIO controller (8041A) + // For the time being a specially developed PIO firmware is used until a dump of the original PIO is + // available. + ROM_REGION(0x400, "iocpio", 0) + ROM_LOAD("pio_a72.bin", 0, 0x400, BAD_DUMP CRC(8c8e740b) SHA1(9b9333a9dc9585aa8f630721d13e551a5c87defc)) + + // ROM definition of keyboard controller (8741) + ROM_REGION(0x400, "kbcpu", 0) + ROM_LOAD("kbd_a3_104675-001.bin", 0, 0x400, CRC(ba7c4303) SHA1(19899af732d0ae1247bfc79979b1ee5f339ee5cf)) + + // ROM definition of character generator (2708, A19 on IOC) + ROM_REGION(0x400, "gfx1", 0) + ROM_LOAD("ioc_a19_101539-001.bin", 0x0000, 0x0400, CRC(47487d0f) SHA1(0ed98f9f06622949ee3cc2ffc572fb9702db0f81)) +ROM_END + +ioport_constructor imds2ioc_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(imds2ioc); +} + +const tiny_rom_entry *imds2ioc_device::device_rom_region() const +{ + return ROM_NAME(imds2ioc); +} |