From d1b046cf34b89ad47b3875360ea8ed1ee023f9e2 Mon Sep 17 00:00:00 2001 From: Michael Zapf Date: Sat, 23 Apr 2022 15:25:29 +0200 Subject: ti99: Added the S0-triggered clock update to TMS9901. --- src/devices/bus/ti99/internal/998board.cpp | 5 +++ src/devices/bus/ti99/internal/998board.h | 4 ++ src/devices/bus/ti99/internal/datamux.cpp | 5 +++ src/devices/bus/ti99/internal/datamux.h | 5 +++ src/devices/machine/tms9901.cpp | 64 ++++++++++++++++++++---------- src/devices/machine/tms9901.h | 2 + src/mame/drivers/geneve.cpp | 4 ++ src/mame/drivers/ti99_4p.cpp | 4 ++ src/mame/drivers/ti99_4x.cpp | 1 - 9 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/devices/bus/ti99/internal/998board.cpp b/src/devices/bus/ti99/internal/998board.cpp index 49533d97c29..bebd7762ec4 100644 --- a/src/devices/bus/ti99/internal/998board.cpp +++ b/src/devices/bus/ti99/internal/998board.cpp @@ -189,6 +189,7 @@ mainboard8_device::mainboard8_device(const machine_config &mconfig, const char * m_p3grom0(*owner, TI998_GLIB30_TAG), m_p3grom1(*owner, TI998_GLIB31_TAG), m_p3grom2(*owner, TI998_GLIB32_TAG), + m_tms9901(*owner, TI998_TMS9901_TAG), m_sgrom_idle(true), m_tsgrom_idle(true), m_p8grom_idle(true), @@ -383,6 +384,10 @@ void mainboard8_device::setaddress(offs_t offset, uint8_t busctrl) m_logical_address = offset; m_physical_address = 0; + // Trigger the 9901's clock if S0=1 + if ((offset & 0x0020) != 0) + m_tms9901->update_clock(); + // In TI's bit order, A14 is the second line from the right side (2^1) m_A14_set = ((m_logical_address & 2)!=0); // Needed for clock_in diff --git a/src/devices/bus/ti99/internal/998board.h b/src/devices/bus/ti99/internal/998board.h index 86c5da12787..f699b8d02cf 100644 --- a/src/devices/bus/ti99/internal/998board.h +++ b/src/devices/bus/ti99/internal/998board.h @@ -24,6 +24,7 @@ #include "bus/ti99/internal/ioport.h" #include "machine/ram.h" #include "machine/tmc0430.h" +#include "machine/tms9901.h" #include "sound/sn76496.h" #include "sound/tms5220.h" #include "video/tms9928a.h" @@ -685,6 +686,9 @@ private: required_device m_p3grom1; required_device m_p3grom2; + // Link to the 9901 + required_device m_tms9901; + // Idle flags for GROMs bool m_sgrom_idle = true; bool m_tsgrom_idle = true; diff --git a/src/devices/bus/ti99/internal/datamux.cpp b/src/devices/bus/ti99/internal/datamux.cpp index d56d8573822..95412e296e9 100644 --- a/src/devices/bus/ti99/internal/datamux.cpp +++ b/src/devices/bus/ti99/internal/datamux.cpp @@ -101,6 +101,7 @@ datamux_device::datamux_device(const machine_config &mconfig, const char *tag, d m_grom0(*owner, TI99_GROM0_TAG), m_grom1(*owner, TI99_GROM1_TAG), m_grom2(*owner, TI99_GROM2_TAG), + m_tms9901(*owner, TI99_TMS9901_TAG), m_ready(*this), m_addr_buf(0), m_dbin(CLEAR_LINE), @@ -436,6 +437,10 @@ void datamux_device::setaddress(offs_t offset, uint16_t busctrl) LOGMASKED(LOG_ADDRESS, "Set address %04x\n", m_addr_buf); + // Trigger the TMS9901 clock when A10 is 1 + if ((m_addr_buf & 0x0020) != 0) + m_tms9901->update_clock(); + if ((m_addr_buf & 0xe000) == 0x0000) { return; // console ROM diff --git a/src/devices/bus/ti99/internal/datamux.h b/src/devices/bus/ti99/internal/datamux.h index db1c9d18035..8fb9a7a8c08 100644 --- a/src/devices/bus/ti99/internal/datamux.h +++ b/src/devices/bus/ti99/internal/datamux.h @@ -22,6 +22,7 @@ #include "sound/sn76496.h" #include "video/tms9928a.h" #include "machine/ram.h" +#include "machine/tms9901.h" #define TI99_DATAMUX_TAG "datamux_16_8" #define TI99_GROM0_TAG "console_grom_0" @@ -32,6 +33,7 @@ #define TI99_CONSOLEROM "console_rom" #define TI99_SOUNDCHIP_TAG "soundchip" #define TI99_VDP_TAG "vdp" +#define TI99_TMS9901_TAG "tms9901" namespace bus::ti99::internal { @@ -91,6 +93,9 @@ private: required_device m_grom1; required_device m_grom2; + // Link to 9901 + required_device m_tms9901; + // Common read routine void read_all(uint16_t addr, uint8_t *target); diff --git a/src/devices/machine/tms9901.cpp b/src/devices/machine/tms9901.cpp index c05cffad2a7..518d2281fb2 100644 --- a/src/devices/machine/tms9901.cpp +++ b/src/devices/machine/tms9901.cpp @@ -33,7 +33,8 @@ Reference: [1] TMS9901 Programmable Systems Interface Data Manual, July 1977 Overview: TMS9901 is a support chip for TMS9900. It handles interrupts, provides several I/O pins, and a timer, which is a register which - decrements regularly and can generate an interrupt when it reaches 0. + decrements continuously and can be set to generate an interrupt when it + reaches 0. It communicates with the TMS9900 with the CRU bus, and with the rest of the world with a number of parallel I/O pins. @@ -79,7 +80,8 @@ Interrupt inputs (group 1 and 2) The interrupt inputs (INT1*-INT15*) are sampled on each falling edge of the phi* clock. An interrupt mask is applied to mask away levels that shall not trigger an interrupt. The mask can be set using the SBO/SBZ - commands (1=arm, 0=disarm) on each of the 15 bits. + commands (1=arm, 0=disarm) on each of the 15 bits. A disarmed interrupt + input can still be read like a normal input port via CRU access. After each clock cycle, the TMS9901 latches the state of INT1*-INT15* (except those pins which are set as output pins). @@ -98,8 +100,7 @@ Group 2 pins (shared I/O and INT*) and triggers an interrupt at level 7 when asserted. In contrast, when writing to bit 31, P15 (same pin) is configured as an - output, and the written value appears on the pin. When the port is set - as output, the interrupt input on the shared pin is deactivated. + output, and the written value appears on the pin. According to [1], the interrupt mask should be set to 0 for those group 2 pins that are used as input/output pins so that no unwanted interrupts are @@ -108,16 +109,20 @@ Group 2 pins (shared I/O and INT*) Clock mode: The "clock mode" is entered by setting bit 0 to 1; setting to 0 enters "interrupt mode". The internal clock is a 14-bit decrementer that - counts down by 1 every 64 clock ticks. On entering clock mode, the current - value of the decrementer is copied to the clock read register and can be - read by the CRU bits 1 to 14. Writing to these CRU bits modifies the - respective bit of the clock register that serves as the start value. Every - time a bit is written, the decrementer is loaded with the current clock - register value. + counts down by 1 every 64 clock ticks. On every update, the value is copied + into the read register, but only in interrupt mode. In clock mode, the read + register is locked so that it can be read without being changed. + Whenever the counter reaches 0, it is reloaded from the clock register on + the next update. + Setting the clock register is possible via CRU addresses 1 to 14 in clock + mode, with bit 1 being the LSB and bit 14 being the MSB. On each bit write + operation, the current state of the clock register is copied into the counter. + + Interrupt ^ | - [Clock register] -> [Decrementer] -> [Clock read register] + [Clock register] -> [Decrementer] -> [Read register] ^ | | v +--<--- CRU write CRU read---<---+ @@ -126,7 +131,7 @@ Clock mode: and "the clock is disabled by RST1* or by writing a zero value into the clock register". Tests show that when a 0 has been written, the chip still counts down from 0x3FFF to 0. However, no interrupt is raised when reaching 0, so "enable" - or "disable" most likely refer to the interrupt. + or "disable" most likely refers to the interrupt. When enabled, the clock raises an interrupt level 3 when reaching 0, overriding the input from the INT3* input. CRU bit 3 is the mask bit for @@ -394,14 +399,6 @@ void tms9901_device::write_bit(int offset, bool data) // Write to control bit (CB) m_clock_mode = (data!=0); LOGMASKED(LOG_MODE, "Enter %s mode\n", m_clock_mode? "clock" : "interrupt"); - - if (m_clock_mode) - { - // we are switching to clock mode: latch the current value of - // the decrementer register - m_clock_read_register = m_decrementer_value; - LOGMASKED(LOG_MODE, "Clock setting = %d\n", m_clock_read_register); - } break; case 15: @@ -448,6 +445,26 @@ void tms9901_device::write_bit(int offset, bool data) } } +/* + Update clock line. This is not a real connection to the 9901; it represents + the effect of setting selection line S0 to 1. Since we use a separate + I/O address space, and in the real machine, the same address lines as for + memory access are used, and one of the address lines is connected to S0, + we have settings of S0 even in situations when there is no I/O access but + ordinary memory access. + + Offering a method explicitly for S0 looks inconsistent with the way of + addressing the bits in this chip (that is, we should then offer S1 to S4 as + well). + + Drivers may use this line for higher emulation precision concerning the + clock. +*/ +void tms9901_device::update_clock() +{ + m_clock_read_register = m_decrementer_value; +} + /* Timer callback Decrementer counts down the value set in clock mode; when it reaches 0, @@ -467,6 +484,10 @@ void tms9901_device::timer_clock_in(line_state clk) if (clk == ASSERT_LINE) { m_decrementer_value = (m_decrementer_value - 1) & 0x3FFF; + + if (!m_clock_mode) + m_clock_read_register = m_decrementer_value; + LOGMASKED(LOG_CLOCK, "Clock = %04x\n", m_decrementer_value); if (m_decrementer_value==0) { @@ -496,6 +517,9 @@ void tms9901_device::phi_line(int state) { timer_clock_in(ASSERT_LINE); + if (!m_clock_mode) + m_clock_read_register = m_decrementer_value; + // We signal the interrupt in sync with the clock line signal_int(); diff --git a/src/devices/machine/tms9901.h b/src/devices/machine/tms9901.h index fd0d68c01cb..68a8b1cd4cd 100644 --- a/src/devices/machine/tms9901.h +++ b/src/devices/machine/tms9901.h @@ -86,6 +86,8 @@ public: void set_poll_int_lines(bool poll) { m_poll_lines = poll; } + void update_clock(); + private: static constexpr device_timer_id DECREMENTER = 0; diff --git a/src/mame/drivers/geneve.cpp b/src/mame/drivers/geneve.cpp index 6f8651aa470..dd4c6b9cbfd 100644 --- a/src/mame/drivers/geneve.cpp +++ b/src/mame/drivers/geneve.cpp @@ -496,6 +496,10 @@ void geneve_state::setaddress_debug(bool debug, offs_t address, uint8_t busctrl) // See V9938 specs m_pal->csw_in(m_gatearray->csw_out()); m_pal->csr_in(m_gatearray->csr_out()); + + // Trigger the 9901 clock when A10=1 + if ((address & 0x0020) != 0) + m_tms9901->update_clock(); } // Going to the box diff --git a/src/mame/drivers/ti99_4p.cpp b/src/mame/drivers/ti99_4p.cpp index af2a49e020d..de3926ca18a 100644 --- a/src/mame/drivers/ti99_4p.cpp +++ b/src/mame/drivers/ti99_4p.cpp @@ -443,6 +443,10 @@ void ti99_4p_state::setaddress(offs_t address, uint16_t busctrl) m_decode = SGCPU_NONE; m_muxready = true; + // Trigger the TMS9901 clock when A10 is 1 + if ((m_addr_buf & 0x0020) != 0) + m_tms9901->update_clock(); + m_decode = decode_address(m_addr_buf); if (m_decode == SGCPU_NONE) diff --git a/src/mame/drivers/ti99_4x.cpp b/src/mame/drivers/ti99_4x.cpp index 7b3ab550c02..463c2498bff 100644 --- a/src/mame/drivers/ti99_4x.cpp +++ b/src/mame/drivers/ti99_4x.cpp @@ -56,7 +56,6 @@ #include "speaker.h" #define TI99_CONSOLEGROM "cons_grom" -#define TI99_TMS9901_TAG "tms9901" #define TI99_SCREEN_TAG "screen" // Debugging -- cgit v1.2.3