diff options
Diffstat (limited to 'src/devices/bus/ti99/internal/992board.cpp')
-rw-r--r-- | src/devices/bus/ti99/internal/992board.cpp | 332 |
1 files changed, 324 insertions, 8 deletions
diff --git a/src/devices/bus/ti99/internal/992board.cpp b/src/devices/bus/ti99/internal/992board.cpp index e31c7e256d8..66e96eb984c 100644 --- a/src/devices/bus/ti99/internal/992board.cpp +++ b/src/devices/bus/ti99/internal/992board.cpp @@ -7,12 +7,26 @@ This component implements the custom video controller and interface chip from the TI-99/2 console. + May 2018 + ***************************************************************************/ #include "emu.h" +#include "998board.h" + +#define LOG_WARN (1U<<1) // Warnings +#define LOG_CRU (1U<<2) // CRU logging +#define LOG_CASSETTE (1U<<3) // Cassette logging +#define LOG_HEXBUS (1U<<4) // Hexbus logging +#define LOG_BANK (1U<<5) // Change ROM banks +#define LOG_KEYBOARD (1U<<6) // Keyboard operation + +#define VERBOSE ( LOG_GENERAL | LOG_WARN ) + +#include "logmacro.h" #include "992board.h" -/** +/* Emulation of the CRT Gate Array of the TI-99/2 Video display controller @@ -41,7 +55,6 @@ CRU Bit VIDENA: disables the scan line generation; blank white screen Clock: 10.7 MHz - Divided by 2 for 9995 CLKIN Scanline refresh: - Pull down HOLD @@ -52,7 +65,7 @@ - Push the byte to the shift register - Get the bits for the scanline from the register - EF00: Control byte + EF00: Control byte +--+--+--+--+--+--+--+--+ |- |- |- |- |- |T |B |S | +--+--+--+--+--+--+--+--+ @@ -94,7 +107,9 @@ 279..304: Hor sync ------------------------------ - Later versions define a "bitmap mode" [2] + Later versions define a "bitmap mode". [2] + There are no known consoles with this capability, and it would require at + least 6 KiB of RAM. EF00: Control byte +--+--+--+--+--+--+--+--+ @@ -109,11 +124,11 @@ [2] VDC Controller CF40052 */ -#include "emu.h" -#include "992board.h" - DEFINE_DEVICE_TYPE_NS(VIDEO99224, bus::ti99::internal, video992_24_device, "video992_24", "TI-99/2 CRT Controller 24K version") DEFINE_DEVICE_TYPE_NS(VIDEO99232, bus::ti99::internal, video992_32_device, "video992_32", "TI-99/2 CRT Controller 32K version") +DEFINE_DEVICE_TYPE_NS(IO99224, bus::ti99::internal, io992_24_device, "io992_24", "TI-99/2 I/O controller 24K version") +DEFINE_DEVICE_TYPE_NS(IO99232, bus::ti99::internal, io992_32_device, "io992_32", "TI-99/2 I/O controller 32K version") + namespace bus { namespace ti99 { namespace internal { @@ -170,6 +185,11 @@ void video992_device::device_timer(emu_timer &timer, device_timer_id id, int par if (m_videna) { // Hold the CPU + // We should expect the HOLDA (HOLD acknowledge) line to go to the + // same circuit that issued the HOLD, but this is not the case. + // The HOLDA line goes to the I/O circuit, which (obviously) allows + // access to the RAM at that point. There is no + // indication of any other effect on the video controller. m_hold_time = machine().time(); m_hold_cb(ASSERT_LINE); } @@ -262,7 +282,7 @@ uint32_t video992_device::screen_update( screen_device &screen, bitmap_rgb32 &bi } /* - VIDENA pin, negative logic + VIDENA pin, positive logic */ WRITE_LINE_MEMBER( video992_device::videna ) { @@ -292,6 +312,302 @@ void video992_device::device_reset() m_free_timer->adjust(screen().time_until_pos(0, HORZ_DISPLAY_START)); } +/* + Emulation of the I/O Gate Array of the TI-99/2 [3] + + The I/O controller is a TAL004 Low Power Schottky-TTL bipolar Gate Array + that provides the interface between the CPU, the keyboard, the Hexbus, + and the cassette interface. It delivers memory select lines for use by the + CPU and by the VDC. It also offers a synchronized RESET signal and a + divider for the CPU clock (which is seemingly not used in the real + machines). + + It is mapped into the CRU I/O address space at addresses E000 and E800: + + I/O map (CRU map) + ----------------- + 0000 - 1DFE: unmapped + 1E00 - 1EFE: TMS9995-internal CRU addresses + 1F00 - 1FD8: unmapped + 1FDA: TMS9995 MID flag + 1FDC - 1FFF: unmapped + 2000 - DFFE: unmapped + E000 - E00E: Read: Keyboard column input + E000 - E00A: Write: Keyboard row selection + E00C: Write: unmapped + E00E: Write: Video enable (VIDENA) + E010 - E7FE: Mirrors of the above + E800 - E80C: Hexbus + E800 - E806: Data lines + E808: HSK line + E80A: BAV line + E80C: Inhibit (Write only) + E80E: Cassette + + [3] I/O Controller CF40051, Preliminary specification, Texas Instruments +*/ + +io992_device::io992_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : bus::hexbus::hexbus_chained_device(mconfig, type, tag, owner, clock), + m_set_rom_bank(*this), + m_key_row(0), + m_latch_out(0xd7), + m_latch_in(0xd7), + m_communication_disable(true) +{ +} + +io992_24_device::io992_24_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : io992_device(mconfig, IO99224, tag, owner, clock) +{ + m_have_banked_rom = false; +} + +io992_32_device::io992_32_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : io992_device(mconfig, IO99232, tag, owner, clock) +{ + m_have_banked_rom = true; +} + +/* + 54-key keyboard +*/ +static INPUT_PORTS_START( keys992 ) + + PORT_START("LINE0") /* col 0 */ + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 ! DEL") PORT_CODE(KEYCODE_1) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 @ INS") PORT_CODE(KEYCODE_2) + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 $ CLEAR") PORT_CODE(KEYCODE_4) + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 % BEGIN") PORT_CODE(KEYCODE_5) + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 ^ PROC'D") PORT_CODE(KEYCODE_6) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 & AID") PORT_CODE(KEYCODE_7) + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 * REDO") PORT_CODE(KEYCODE_8) + + PORT_START("LINE1") /* col 1 */ + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("q Q") PORT_CODE(KEYCODE_Q) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("w W ~") PORT_CODE(KEYCODE_W) + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("e E (UP)") PORT_CODE(KEYCODE_E) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("r R [") PORT_CODE(KEYCODE_R) + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("t T ]") PORT_CODE(KEYCODE_T) + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("y Y") PORT_CODE(KEYCODE_Y) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("i I ?") PORT_CODE(KEYCODE_I) + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 ( BACK") PORT_CODE(KEYCODE_9) + + PORT_START("LINE2") /* col 2 */ + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("a A") PORT_CODE(KEYCODE_A) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("s S (LEFT)") PORT_CODE(KEYCODE_S) + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("d D (RIGHT)") PORT_CODE(KEYCODE_D) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f F {") PORT_CODE(KEYCODE_F) + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("h H") PORT_CODE(KEYCODE_H) + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("u U _") PORT_CODE(KEYCODE_U) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("o O '") PORT_CODE(KEYCODE_O) + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 )") PORT_CODE(KEYCODE_0) + + PORT_START("LINE3") /* col 3 */ + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("z Z \\") PORT_CODE(KEYCODE_Z) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("x X (DOWN)") PORT_CODE(KEYCODE_X) + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("c C `") PORT_CODE(KEYCODE_C) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("g G }") PORT_CODE(KEYCODE_G) + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("j J") PORT_CODE(KEYCODE_J) + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("k K") PORT_CODE(KEYCODE_K) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("p P \"") PORT_CODE(KEYCODE_P) + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("= + QUIT") PORT_CODE(KEYCODE_EQUALS) + + PORT_START("LINE4") /* col 4 */ + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("v V") PORT_CODE(KEYCODE_V) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("n N") PORT_CODE(KEYCODE_N) + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("l L") PORT_CODE(KEYCODE_L) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; :") PORT_CODE(KEYCODE_COLON) + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/ -") PORT_CODE(KEYCODE_SLASH) + + PORT_START("LINE5") /* col 5 */ + PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BREAK") PORT_CODE(KEYCODE_ESC) + PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(SPACE)") PORT_CODE(KEYCODE_SPACE) + PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b B") PORT_CODE(KEYCODE_B) + PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("m M") PORT_CODE(KEYCODE_M) + PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) + PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FCTN") PORT_CODE(KEYCODE_LALT) + PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_RSHIFT) + PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) + + PORT_START("LINE6") /* col 6 */ + PORT_BIT(0xFF, IP_ACTIVE_LOW, IPT_UNUSED) + + PORT_START("LINE7") /* col 7 */ + PORT_BIT(0xFF, IP_ACTIVE_LOW, IPT_UNUSED) + +INPUT_PORTS_END + +void io992_device::device_start() +{ + m_hexbus_outbound = dynamic_cast<bus::hexbus::hexbus_device*>(machine().device(TI_HEXBUS_TAG)); + m_cassette = dynamic_cast<cassette_image_device*>(machine().device(TI_CASSETTE)); + m_videoctrl = dynamic_cast<video992_device*>(machine().device(TI992_VDC_TAG)); + + // Establish callback for inbound propagations + m_hexbus_outbound->set_chain_element(this); + + m_set_rom_bank.resolve(); +} + +READ8_MEMBER(io992_device::cruread) +{ + static const char *const keynames[] = { "LINE0", "LINE1", "LINE2", "LINE3", "LINE4", "LINE5", "LINE6", "LINE7" }; + + int address = offset << 4; + uint8_t value = 0x7f; // All Hexbus lines high + double inp = 0; + int i; + uint8_t bit = 1; + + switch (address) + { + case 0xe000: + // CRU E000-E7fE: Keyboard + // Read: 0000 1110 0*** **** (mirror 007f) + value = ioport(keynames[m_key_row])->read(); + break; + case 0xe800: + // CRU E800-EFFE: Hexbus and other functions + // Read: 0000 1110 1*** **** (mirror 007f) + for (i=0; i < 6; i++) + { + if ((m_latch_in & m_hexbval[i])==0) value &= ~bit; + bit <<= 1; + } + + inp = m_cassette->input(); + if (inp > 0) + value |= 0x80; + LOGMASKED(LOG_CASSETTE, "value=%f\n", inp); + break; + default: + LOGMASKED(LOG_CRU, "Unknown access to %04x\n", address); + } + + LOGMASKED(LOG_CRU, "CRU %04x -> %02x\n", address, value); + + return value; +} + +WRITE8_MEMBER(io992_device::cruwrite) +{ + int address = (offset << 1) & 0xf80e; + + LOGMASKED(LOG_CRU, "CRU %04x <- %1x\n", address, data); + + uint8_t olddata = m_latch_out; + + switch (address) + { + // Select the current keyboard row. Also, bit 0 is used to switch the + // ROM bank. I guess that means we won't be able to read the keyboard + // when processing that particular ROM area. + // CRU E000-E7fE: Keyboard + // Write: 1110 0*** **** XXX0 (mirror 07f0) + case 0xe000: + if (m_have_banked_rom) + { + LOGMASKED(LOG_BANK, "set bank = %d\n", data); + m_set_rom_bank(data==1); + } + // no break + case 0xe002: + case 0xe004: + case 0xe006: + case 0xe008: + case 0xe00a: + if (data == 0) m_key_row = offset&7; + break; + case 0xe00c: + LOGMASKED(LOG_WARN, "Unmapped CRU write to address e00c\n"); + break; + case 0xe00e: + LOGMASKED(LOG_CRU, "VIDENA = %d\n", data); + m_videoctrl->videna(data); + break; + + // Write: 1110 1*** **** XXX0 (mirror 07f0) + case 0xe800: // ID0 + case 0xe802: // ID1 + case 0xe804: // ID2 + case 0xe806: // ID3 + if (data != 0) m_latch_out |= m_hexbval[offset & 0x07]; + else m_latch_out &= ~m_hexbval[offset & 0x07]; + LOGMASKED(LOG_HEXBUS, "Hexbus latch out = %02x\n", m_latch_out); + break; + case 0xe808: // HSK + case 0xe80a: // BAV + if (data != 0) m_latch_out |= m_hexbval[offset & 0x07]; + else m_latch_out &= ~m_hexbval[offset & 0x07]; + + if (m_latch_out != olddata) + { + LOGMASKED(LOG_HEXBUS, "%s %s\n", (address==0xe808)? "HSK*" : "BAV*", (data==0)? "assert" : "clear"); + if (m_communication_disable) + { + // This is not explicitly stated in the specs, but since they + // also claim that communication is disabled on power-up, + // we must turn it on here; the ROM fails to do it. + LOGMASKED(LOG_HEXBUS, "Enabling Hexbus\n"); + m_communication_disable = false; + } + hexbus_write(m_latch_out); + // Check how the bus has changed. This depends on the states of all + // connected peripherals + m_latch_in = hexbus_read(); + } + break; + case 0xe80c: + LOGMASKED(LOG_HEXBUS, "Hexbus inhibit = %d\n", data); + if (data == 1) + { + m_latch_in = 0xd7; + m_communication_disable = true; + } + else m_communication_disable = false; + break; + case 0xe80e: + LOGMASKED(LOG_CRU, "Cassette output = %d\n", data); + // Tape output. See also ti99_4x.cpp. + m_cassette->output((data==1)? +1 : -1); + break; + } +} + +/* + Input from the Hexbus. Since it cannot trigger any interrupt on the + CPU, it must be polled via CRU. + + Line state received via the Hexbus + +------+------+------+------+------+------+------+------+ + | ID3 | ID2 | - | HSK* | 0 | BAV* | ID1 | ID0 | + +------+------+------+------+------+------+------+------+ + +*/ +void io992_device::hexbus_value_changed(uint8_t data) +{ + // Only latch the incoming data when BAV* is asserted and the Hexbus + // is not inhibited + bool bav_asserted = ((m_latch_out & bus::hexbus::HEXBUS_LINE_BAV)==0); + if (!m_communication_disable && bav_asserted) + { + LOGMASKED(LOG_HEXBUS, "Hexbus changed and latched: %02x\n", data); + m_latch_in = data; + } + else + LOGMASKED(LOG_HEXBUS, "Ignoring Hexbus change (to %02x), latch=%s, BAV*=%d\n", data, m_communication_disable? "inhibit":"enabled", bav_asserted? 0:1); +} + +ioport_constructor io992_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( keys992 ); +} + } } } |