summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/tms9901.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/tms9901.cpp')
-rw-r--r--src/devices/machine/tms9901.cpp111
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)