ti99: Added the S0-triggered clock update to TMS9901.

This commit is contained in:
Michael Zapf 2022-04-23 15:25:29 +02:00
parent 9fbaa3862a
commit d1b046cf34
9 changed files with 73 additions and 21 deletions

View File

@ -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

View File

@ -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<tmc0430_device> m_p3grom1;
required_device<tmc0430_device> m_p3grom2;
// Link to the 9901
required_device<tms9901_device> m_tms9901;
// Idle flags for GROMs
bool m_sgrom_idle = true;
bool m_tsgrom_idle = true;

View File

@ -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

View File

@ -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<tmc0430_device> m_grom1;
required_device<tmc0430_device> m_grom2;
// Link to 9901
required_device<tms9901_device> m_tms9901;
// Common read routine
void read_all(uint16_t addr, uint8_t *target);

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -56,7 +56,6 @@
#include "speaker.h"
#define TI99_CONSOLEGROM "cons_grom"
#define TI99_TMS9901_TAG "tms9901"
#define TI99_SCREEN_TAG "screen"
// Debugging