From 5755f55af4e85c8c5bb974689dcd150516711a1c Mon Sep 17 00:00:00 2001 From: Michael Zapf Date: Tue, 12 Feb 2019 01:41:33 +0100 Subject: ti99: Fixed cassette instabilities, caused by improper timer usage; introduced synchronous clock input for 9901 --- src/devices/bus/ti99/joyport/handset.cpp | 1 - src/devices/machine/tms9901.cpp | 101 ++++++++++++++++++++++--------- src/devices/machine/tms9901.h | 11 +++- src/mame/drivers/geneve.cpp | 3 +- src/mame/drivers/ti99_2.cpp | 1 - src/mame/drivers/ti99_4p.cpp | 3 +- src/mame/drivers/ti99_4x.cpp | 5 +- src/mame/drivers/ti99_8.cpp | 3 +- 8 files changed, 91 insertions(+), 37 deletions(-) diff --git a/src/devices/bus/ti99/joyport/handset.cpp b/src/devices/bus/ti99/joyport/handset.cpp index 304cdffe239..829b5bca087 100644 --- a/src/devices/bus/ti99/joyport/handset.cpp +++ b/src/devices/bus/ti99/joyport/handset.cpp @@ -38,7 +38,6 @@ #include "emu.h" #include "handset.h" -#include "machine/tms9901.h" #define LOG_WARN (1U<<1) // Warnings #define LOG_CONFIG (1U<<2) // Configuration diff --git a/src/devices/machine/tms9901.cpp b/src/devices/machine/tms9901.cpp index 5282dd6f9f5..af01c6ac7fe 100644 --- a/src/devices/machine/tms9901.cpp +++ b/src/devices/machine/tms9901.cpp @@ -53,6 +53,16 @@ Pins: (This is mostly obvious, but it implies that you cannot trigger an interrupt by setting the output state of a pin, which is not SO obvious.) +Clock mode: + The "clock mode" is entered by setting bit 0 to 1. This means that the + clock register becomes accessible to changes and inspection. The clock + itself runs in the interrupt mode. Accordingly, the typical setup + involves first setting bit 0 to 1, then loading some or all of the + clock register bits, and then switching to interrupt mode again. From then + on, INT3 is asserted whenever the clock reaches 0, and is cleared by + writing 0 or 1 to bit 3. The clock can only be stopped by setting the + register to 0 or by a reset. + Interrupt handling: After each clock cycle, TMS9901 latches the state of INT1*-INT15* (except pins which are set as output pins). If the clock is enabled, it replaces @@ -105,6 +115,9 @@ MZ: According to the description in MZ: Turned to class (January 2012) +MZ: Added a synchronous clock input (Phi line) as an alternative to the + emu_timer. + TODO: Tests on a real machine - Set an interrupt input (e.g. keyboard for Geneve), trigger RST2*, check whether interrupt mask has been reset @@ -124,24 +137,23 @@ TODO: Tests on a real machine #include -#define LOG_GENERAL (1U << 0) -#define LOG_PINS (1U << 1) -#define LOG_CLOCK (1U << 2) -#define LOG_MODE (1U << 3) +#define LOG_GENERAL (1U << 0) +#define LOG_PINS (1U << 1) +#define LOG_CONFIG (1U << 2) +#define LOG_MODE (1U << 3) +#define LOG_INT (1U << 4) +#define LOG_DECVALUE (1U << 5) -//#define VERBOSE (LOG_PINS | LOG_CLOCK | LOG_MODE) +#define VERBOSE ( 0 ) #include "logmacro.h" -#define LOGPINS(...) LOGMASKED(LOG_PINS, __VA_ARGS__) -#define LOGCLOCK(...) LOGMASKED(LOG_CLOCK, __VA_ARGS__) -#define LOGMODE(...) LOGMASKED(LOG_MODE, __VA_ARGS__) - - /* Constructor */ tms9901_device::tms9901_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, TMS9901, tag, owner, clock), + m_clock_active(false), + m_clockdiv(0), m_read_block(*this), m_write_p{{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this}}, m_interrupt(*this) @@ -162,12 +174,12 @@ void tms9901_device::field_interrupts() // if timer is enabled, INT3 pin is overridden by timer if (m_timer_int_pending) { - LOGCLOCK("timer fires\n"); + LOGMASKED(LOG_INT, "INT3 (timer) asserted\n"); current_ints |= INT3; } else { - LOGCLOCK("timer clear\n"); + LOGMASKED(LOG_INT, "INT3 (timer) cleared\n"); current_ints &= ~INT3; } } @@ -197,6 +209,7 @@ void tms9901_device::field_interrupts() current_ints >>= 1; /* try next bit */ level++; } + LOGMASKED(LOG_INT, "Triggering interrupt, level %d\n", level); m_int_pending = true; if (!m_interrupt.isnull()) m_interrupt(level, 1, 0xff); // the offset carries the IC0-3 level @@ -237,11 +250,11 @@ void tms9901_device::timer_reload() if (m_clock_register != 0) { /* reset clock interval */ m_decrementer_value = m_clock_register; - m_decrementer->enable(true); + m_clock_active = true; } else { /* clock interval == 0 -> no timer */ - m_decrementer->enable(false); + m_clock_active = false; } } @@ -291,7 +304,7 @@ READ8_MEMBER( tms9901_device::read ) // Set those bits here answer |= (m_pio_output_mirror & m_pio_direction_mirror) & 0xFF; } - LOGPINS("input on lines INT7..CB = %02x\n", answer); + LOGMASKED(LOG_PINS, "Input on lines INT7..CB = %02x\n", answer); break; case 1: if (m_clock_mode) @@ -311,7 +324,7 @@ READ8_MEMBER( tms9901_device::read ) answer &= ~(m_pio_direction_mirror >> 8); answer |= (m_pio_output_mirror & m_pio_direction_mirror) >> 8; } - LOGPINS("input on lines INT15..INT8 = %02x\n", answer); + LOGMASKED(LOG_PINS, "Input on lines INT15..INT8 = %02x\n", answer); break; case 2: /* exit timer mode */ @@ -325,7 +338,7 @@ READ8_MEMBER( tms9901_device::read ) answer &= ~m_pio_direction; answer |= (m_pio_output & m_pio_direction) & 0xFF; - LOGPINS("input on lines P7..P0 = %02x\n", answer); + LOGMASKED(LOG_PINS, "Input on lines P7..P0 = %02x\n", answer); break; case 3: @@ -338,7 +351,7 @@ READ8_MEMBER( tms9901_device::read ) answer &= ~(m_pio_direction >> 8); answer |= (m_pio_output & m_pio_direction) >> 8; - LOGPINS("input on lines P15..P8 = %02x\n", answer); + LOGMASKED(LOG_PINS, "Input on lines P15..P8 = %02x\n", answer); break; } @@ -367,7 +380,7 @@ WRITE8_MEMBER ( tms9901_device::write ) if (offset >= 0x10) { int pin = offset & 0x0F; - LOGPINS("output on P%d = %d\n", pin, data); + LOGMASKED(LOG_PINS, "Output on P%d = %d\n", pin, data); int bit = (1 << pin); @@ -408,18 +421,20 @@ WRITE8_MEMBER ( tms9901_device::write ) { // Switch to interrupt mode; quit clock mode m_clock_mode = false; - LOGMODE("int mode\n"); + LOGMASKED(LOG_MODE, "Enter interrupt mode\n"); } else { m_clock_mode = true; - LOGMODE("clock mode\n"); + LOGMASKED(LOG_MODE, "Enter clock mode\n"); // we are switching to clock mode: latch the current value of // the decrementer register if (m_clock_register != 0) m_clock_read_register = m_decrementer_value; else m_clock_read_register = 0; /* timer inactive... */ + + LOGMASKED(LOG_CONFIG, "Clock setting = %d\n", m_clock_read_register); } } else if (offset == 0x0f) @@ -437,7 +452,7 @@ WRITE8_MEMBER ( tms9901_device::write ) // Spec is not clear on whether the mask bits are also reset by RST2* // TODO: Check on a real machine. (I'd guess from the text they are not touched) m_enabled_ints = 0; - LOGMODE("Soft reset (RST2*)\n"); + LOGMASKED(LOG_MODE, "Soft reset (RST2*)\n"); } } else @@ -447,7 +462,7 @@ WRITE8_MEMBER ( tms9901_device::write ) else m_enabled_ints &= ~0x4000; /* unset bit */ - LOGMODE("interrupts = %04x\n", m_enabled_ints); + LOGMASKED(LOG_CONFIG, "Enabled interrupts = %04x\n", m_enabled_ints); field_interrupts(); /* changed interrupt state */ } } @@ -469,7 +484,7 @@ WRITE8_MEMBER ( tms9901_device::write ) m_clock_register &= ~bit; /* clear bit */ /* reset clock timer (page 8) */ - LOGCLOCK("clock register = %04x\n", m_clock_register); + LOGMASKED(LOG_CONFIG, "Clock register = %04x\n", m_clock_register); timer_reload(); } else @@ -484,7 +499,7 @@ WRITE8_MEMBER ( tms9901_device::write ) if (offset == 3) m_timer_int_pending = false; /* SBO 3 clears pending timer interrupt (??) */ - LOGMODE("enabled interrupts = %04x\n"); + LOGMASKED(LOG_CONFIG, "Enabled interrupts = %04x\n", m_enabled_ints); field_interrupts(); /* changed interrupt state */ } } @@ -499,11 +514,21 @@ WRITE8_MEMBER ( tms9901_device::write ) void tms9901_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { if (id==DECREMENTER) // we have only that one + { + clock_in(ASSERT_LINE); + clock_in(CLEAR_LINE); + } +} + +void tms9901_device::clock_in(line_state clk) +{ + if (m_clock_active && clk == ASSERT_LINE) { m_decrementer_value--; - LOGCLOCK("decrementer = %d\n", m_decrementer_value); + LOGMASKED(LOG_DECVALUE, "Decrementer = %d\n", m_decrementer_value); if (m_decrementer_value<=0) { + LOGMASKED(LOG_INT, "Timer expired\n"); m_timer_int_pending = true; // decrementer interrupt requested field_interrupts(); m_decrementer_value = m_clock_register; @@ -511,6 +536,21 @@ void tms9901_device::device_timer(emu_timer &timer, device_timer_id id, int para } } +/* + Synchronous clock input. This may be used for systems which have + a CLK line controlled by the CPU, like the TMS99xx systems. + In that case, clock is set to 0. +*/ +WRITE_LINE_MEMBER( tms9901_device::phi_line ) +{ + // Divider by 64 + if (state==ASSERT_LINE) + m_clockdiv = (m_clockdiv+1) % 0x40; + + if (m_clockdiv==0) + clock_in((line_state)state); +} + /*------------------------------------------------- device_stop - device-specific stop -------------------------------------------------*/ @@ -565,9 +605,12 @@ void tms9901_device::do_reset() void tms9901_device::device_start() { - m_decrementer = timer_alloc(DECREMENTER); - m_decrementer->adjust(attotime::from_hz(clock() / 64.), 0, attotime::from_hz(clock() / 64.)); - m_decrementer->enable(false); + // Allow for using asynchronous and synchronous clocks + if (clock() != 0) + { + m_decrementer = timer_alloc(DECREMENTER); + m_decrementer->adjust(attotime::from_hz(clock() / 64.), 0, attotime::from_hz(clock() / 64.)); + } m_read_block.resolve(); for (auto &cb : m_write_p) diff --git a/src/devices/machine/tms9901.h b/src/devices/machine/tms9901.h index 86a2c6852e3..31a1f79a3d1 100644 --- a/src/devices/machine/tms9901.h +++ b/src/devices/machine/tms9901.h @@ -60,6 +60,9 @@ public: DECLARE_WRITE_LINE_MEMBER( rst1_line ); + // Synchronous clock input + DECLARE_WRITE_LINE_MEMBER( phi_line ); + DECLARE_READ8_MEMBER( read ); DECLARE_WRITE8_MEMBER( write ); @@ -83,6 +86,9 @@ private: // Common method for device_reset and rst1_line void do_reset(); + // Common clock handling + void clock_in(line_state clk); + // State of the INT1-INT15 lines (must be inverted when queried) // Note that the levels must also be delivered when reading the pins, which // may require to latch the int levels. @@ -93,6 +99,9 @@ private: bool m_int_pending; // status of the int* pin (connected to TMS9900) bool m_timer_int_pending; // timer int pending (overrides int3 pin if timer enabled) + bool m_clock_active; + int m_clockdiv; // Clock divider counter (for synchronous clock) + // PIO registers int m_pio_direction; // direction register for PIO @@ -110,7 +119,7 @@ private: // true = clock mode (read/write clock interval) bool m_clock_mode; - // MESS timer, used to emulate the decrementer register + // Timer, used to emulate the decrementer register emu_timer *m_decrementer; // clock interval, loaded in decrementer when it reaches 0. diff --git a/src/mame/drivers/geneve.cpp b/src/mame/drivers/geneve.cpp index d0849d802be..aee10998539 100644 --- a/src/mame/drivers/geneve.cpp +++ b/src/mame/drivers/geneve.cpp @@ -647,6 +647,7 @@ WRITE8_MEMBER( geneve_state::external_operation ) */ WRITE_LINE_MEMBER( geneve_state::clock_out ) { + m_tms9901->phi_line(state); m_mapper->clock_in(state); } @@ -749,7 +750,7 @@ MACHINE_CONFIG_START(geneve_state::geneve_common) screen.set_screen_update(TI_VDP_TAG, FUNC(v99x8_device::screen_update)); // Main board components - TMS9901(config, m_tms9901, 3000000); + TMS9901(config, m_tms9901, 0); m_tms9901->read_cb().set(FUNC(geneve_state::read_by_9901)); m_tms9901->p_out_cb(0).set(FUNC(geneve_state::peripheral_bus_reset)); m_tms9901->p_out_cb(1).set(FUNC(geneve_state::VDP_reset)); diff --git a/src/mame/drivers/ti99_2.cpp b/src/mame/drivers/ti99_2.cpp index d910d0327ef..3a60b4ebf5d 100644 --- a/src/mame/drivers/ti99_2.cpp +++ b/src/mame/drivers/ti99_2.cpp @@ -164,7 +164,6 @@ #include "emu.h" #include "bus/ti99/ti99defs.h" -#include "machine/tms9901.h" #include "cpu/tms9900/tms9995.h" #include "bus/ti99/internal/992board.h" #include "machine/ram.h" diff --git a/src/mame/drivers/ti99_4p.cpp b/src/mame/drivers/ti99_4p.cpp index 00774139e37..49f90ad6a60 100644 --- a/src/mame/drivers/ti99_4p.cpp +++ b/src/mame/drivers/ti99_4p.cpp @@ -919,6 +919,7 @@ WRITE_LINE_MEMBER( ti99_4p_state::notconnected ) */ WRITE_LINE_MEMBER( ti99_4p_state::clock_out ) { + m_tms9901->phi_line(state); datamux_clock_in(state); m_peribox->clock_in(state); } @@ -1020,7 +1021,7 @@ void ti99_4p_state::ti99_4p_60hz(machine_config& config) m_cpu->dbin_cb().set(FUNC(ti99_4p_state::dbin_line)); // tms9901 - TMS9901(config, m_tms9901, 3000000); + TMS9901(config, m_tms9901, 0); m_tms9901->read_cb().set(FUNC(ti99_4p_state::read_by_9901)); m_tms9901->p_out_cb(2).set(FUNC(ti99_4p_state::keyC0)); m_tms9901->p_out_cb(3).set(FUNC(ti99_4p_state::keyC1)); diff --git a/src/mame/drivers/ti99_4x.cpp b/src/mame/drivers/ti99_4x.cpp index 2e0028ee537..eaab596de3d 100644 --- a/src/mame/drivers/ti99_4x.cpp +++ b/src/mame/drivers/ti99_4x.cpp @@ -664,6 +664,7 @@ READ8_MEMBER( ti99_4x_state::interrupt_level ) */ WRITE_LINE_MEMBER( ti99_4x_state::clock_out ) { + m_tms9901->phi_line(state); m_datamux->clock_in(state); m_ioport->clock_in(state); } @@ -867,8 +868,8 @@ void ti99_4x_state::ti99_4_common(machine_config& config) m_cpu->clkout_cb().set(FUNC(ti99_4x_state::clock_out)); m_cpu->dbin_cb().set(FUNC(ti99_4x_state::dbin_line)); - // Main board - TMS9901(config, m_tms9901, 3000000); + // Programmable system interface (driven by CLKOUT) + TMS9901(config, m_tms9901, 0); m_tms9901->read_cb().set(FUNC(ti99_4x_state::read_by_9901)); m_tms9901->p_out_cb(2).set(FUNC(ti99_4x_state::keyC0)); m_tms9901->p_out_cb(3).set(FUNC(ti99_4x_state::keyC1)); diff --git a/src/mame/drivers/ti99_8.cpp b/src/mame/drivers/ti99_8.cpp index 619d54a3931..016dc17a284 100644 --- a/src/mame/drivers/ti99_8.cpp +++ b/src/mame/drivers/ti99_8.cpp @@ -691,6 +691,7 @@ WRITE8_MEMBER( ti99_8_state::external_operation ) */ WRITE_LINE_MEMBER( ti99_8_state::clock_out ) { + m_tms9901->phi_line(state); m_mainboard->clock_in(state); } @@ -747,7 +748,7 @@ void ti99_8_state::ti99_8(machine_config& config) m_cpu->holda_cb().set(TI998_MAINBOARD_TAG, FUNC(mainboard8_device::holda_line)); // 9901 configuration - TMS9901(config, m_tms9901, XTAL(10'738'635)/4.0); + TMS9901(config, m_tms9901, 0); m_tms9901->read_cb().set(FUNC(ti99_8_state::read_by_9901)); m_tms9901->p_out_cb(0).set(FUNC(ti99_8_state::keyC0)); m_tms9901->p_out_cb(1).set(FUNC(ti99_8_state::keyC1)); -- cgit v1.2.3