diff options
Diffstat (limited to 'src/devices/machine/tms9901.cpp')
-rw-r--r-- | src/devices/machine/tms9901.cpp | 111 |
1 files changed, 77 insertions, 34 deletions
diff --git a/src/devices/machine/tms9901.cpp b/src/devices/machine/tms9901.cpp index 5282dd6f9f5..4e7341feb61 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 <math.h> -#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; } } @@ -262,13 +275,13 @@ void tms9901_device::timer_reload() bit 16-31: current status of the P0-P15 pins (quits timer mode, too...) */ -READ8_MEMBER( tms9901_device::read ) +uint8_t tms9901_device::read(offs_t offset) { int answer = 0; - offset &= 0x003; + offset &= 0x01f; - switch (offset) + switch (offset >> 3) { case 0: if (m_clock_mode) @@ -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,12 +351,12 @@ 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; } - return answer; + return BIT(answer, offset & 7); } /* @@ -359,7 +372,7 @@ READ8_MEMBER( tms9901_device::read ) bit 16-31: set output state of P0-P15 (and set them as output pin) (quit timer mode, too...) */ -WRITE8_MEMBER ( tms9901_device::write ) +void tms9901_device::write(offs_t offset, uint8_t data) { data &= 1; /* clear extra bits */ offset &= 0x01F; @@ -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 */ } } @@ -500,10 +515,20 @@ void tms9901_device::device_timer(emu_timer &timer, device_timer_id id, int para { 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) |