summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Michael Zapf <Michael.Zapf@mizapf.de>2019-05-15 22:43:40 +0200
committer Michael Zapf <Michael.Zapf@mizapf.de>2019-05-15 22:43:40 +0200
commit13c3ec0f0079a1a5e6e77640b8d503a6dd6e6cf9 (patch)
treeb6e03a5fa917f98a334187684a0d3ec3d0a5c501
parentc9b625bb2a1b36c2527ac6c438ee1f6bcb08576e (diff)
tms9901: Changed input line reading from 8-bit units to single bits, adapted clients. Decrementer now continues counting even when initial value is 0, clearing up a misunderstanding of specs.
-rw-r--r--src/devices/machine/tms9901.cpp754
-rw-r--r--src/devices/machine/tms9901.h134
-rw-r--r--src/mame/drivers/geneve.cpp163
-rw-r--r--src/mame/drivers/si5500.cpp25
-rw-r--r--src/mame/drivers/ti99_4p.cpp189
-rw-r--r--src/mame/drivers/ti99_4x.cpp312
-rw-r--r--src/mame/drivers/ti99_8.cpp136
-rw-r--r--src/mame/drivers/tm990189.cpp66
8 files changed, 975 insertions, 804 deletions
diff --git a/src/devices/machine/tms9901.cpp b/src/devices/machine/tms9901.cpp
index 4e7341feb61..7f85114128a 100644
--- a/src/devices/machine/tms9901.cpp
+++ b/src/devices/machine/tms9901.cpp
@@ -27,109 +27,121 @@
P5 |20 21| P4
+--------------+
+Reference: [1] TMS9901 Programmable Systems Interface Data Manual, July 1977
+ [2] A. Osborne, G. Kane: Osborne 16-bit microprocessor handbook
+
Overview:
TMS9901 is a support chip for TMS9900. It handles interrupts, provides
- several I/O pins, and a timer (a.k.a. clock: it is merely a register which
- decrements regularly and can generate an interrupt when it reaches 0).
+ several I/O pins, and a timer, which is a register which
+ decrements regularly and can 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.
I/O and timer functions should work with any other 990/99xx/99xxx CPU.
- On the other hand, interrupt handling was primarily designed for tms9900
+ On the other hand, interrupt handling was primarily designed for TMS9900
and 99000 based systems: other CPUs can support interrupts, but not the 16
distinct interrupt vectors.
+ For the 9980A, the IC1-IC3 lines are connected to the IC0-IC2 lines of the
+ 9980A.
+
Pins:
Vcc, Vss: power supply
- Phi*: system clock (connected to TMS9900 Phi3* or TMS9980 CLKOUT*)
- RST1*: reset input
- CRUIN, CRUOUT, CRUCLK, CE*, S0-S4: CRU bus (CPU interface)
- INTREQ*, IC0-IC3: interrupt bus (CPU interface)
- INT*1-INT*6: used as interrupt/input pins.
- P0-P6: used as input/output pins.
- INT*7/P15-INT*15/P7: used as either interrupt/input or input/output pins.
- Note that a pin cannot be used simultaneously as output and as interrupt.
- (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
- INT3* with an internal timer interrupt flag. Then it inverts the value and
- performs a bit-wise AND with the interrupt mask.
+ Phi* : system clock (connected to TMS9900 Phi3* or TMS9980A CLKOUT*)
+ RST1*: Reset input
+ CRUIN,
+ CRUOUT,
+ CRUCLK: CRU bus
+ CE*: Chip enable; typically driven by a decoder for the CRU address
+ S0-S4: CRU access bits (0..31; S0 is MSB)
+ INTREQ*: Interrupt request; active (0) when an interrupt is signaled to the CPU
+ IC0-IC3: Interrupt level (0..15, IC0 is MSB)
+
+ Three groups of I/O pins:
+
+ Group 1: INT1*-INT6*: Interrupt inputs.
+ Group 2: INT7*_P15 - INT15*_P7: Interrupt inputs or I/O ports
+ Group 3: P0-P6: I/O ports
+
+ In group 2, the interrupt inputs and I/O ports share their pins, which
+ leads to mirroring in the CRU address space.
+
+Input/Output ports:
+ P0 to P15 are preconfigured as input ports. By writing a value to a port,
+ it is configured as an output. Caution must be taken that the pin is not
+ fed with some logic level when setting it as output, because this may
+ damage the port. To reconfigure it as an input, the chip must be reset
+ by the hard RST1* line or the soft RST2* operation (setting bit 15 to 0
+ in clock mode). Output pins can be read and return the currently set value.
+
+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.
+
+ After each clock cycle, the TMS9901 latches the state of INT1*-INT15*
+ (except those pins which are set as output pins).
If there are some unmasked interrupt bits, INTREQ* is asserted and the code
- of the lowest active interrupt is placed on IC0-IC3. If these pins are
- duly connected to the tms9900 INTREQ* and IC0-IC3 pins, the result is that
- asserting an INTn* on tms9901 will cause a level-n interrupt request on the
- tms9900, provided that this interrupt pin is not masked in tms9901, and
- that no unmasked higher-priority (i.e. lower-level) interrupt pin is set.
-
- This interrupt request lasts for as long as the interrupt pin and the
- relevant bit in the interrupt mask are set (level-triggered interrupts).
- (The request may be shadowed by a higher-priority interrupt request, but
- it will resume when the higher-priority request ends.)
-
- TIMER interrupts are kind of an exception, since they are not associated
- with an external interrupt pin. I think there is an internal timer
- interrupt flag that is set when the decrementer reaches 0, and is cleared
- by a write to the 9901 int*3 enable bit ("SBO 3" in interrupt mode).
-
-TODO:
- * Emulate the RST1* input. Note that RST1* active (low) makes INTREQ*
- inactive (high) with IC0-IC3 = 0.
- * the clock read register is updated every time the timer decrements when
- the TMS9901 is not in clock mode. This probably implies that if the
- clock mode is cleared and re-asserted immediately, the tms9901 may fail
- to update the clock read register: this is not emulated.
- * The clock mode is entered when a 1 is written to the control bit. It is
- exited when a 0 is written to the control bit or the a tms9901 select bit
- greater than 15 is accessed. According to the data sheet, "when CE* is
- inactive (HIGH), the PSI is not disabled from seeing the select lines.
- As the CPU is accessing memory, A10-A14 could very easily have a value of
- 15 or greater" (this is assuming that S0-S4 are connected to A10-A14,
- which makes sense with most tms9900 family members). There is no way
- this "feature" (I would call it a hardware bug) can be emulated
- efficiently, as we would need to watch every memory access.
-
-MZ: According to the description in
- A. Osborne, G. Kane: Osborne 16-bit microprocessor handbook
- page 3-81
- the 9901 only temporarily leaves the timer mode as long as S0 is set to 1.
- In the meantime the timer function continues but cannot be queried. This
- makes it possible to continue using the chip as a timer while working with
- its I/O pins. Thus I believe the above TODO concering the exit of the timer
- mode is not applicable.
- The problem is that the original 9901 specification is not clear about this.
-
-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
-- Check whether the clock_read_register is updated whenever clock mode is exited
- (in particular when S0=1, i.e. A10=1 -> addresses xxxx xxxx xx1x xxxx
- requires to write a program that fits into 32 bytes; workspace elsewhere)
+ of the lowest active interrupt is placed on IC0-IC3. The interrupt line
+ and the level lines should be connected to the respective inputs of the
+ TMS9900 or TMS9980A.
+
+Group 2 pins (shared I/O and INT*)
+ Pins of group 2 are shared between the I/O ports and the interrupt inputs.
+ Internally, they are treated as different signals: There are interrupt
+ mask bits for INT7*..INT15*, and there is a CRU bit for each of the I/O
+ ports. For example, INT7* can be read by bit 7, and the same pin can be
+ read as P15 via bit 31. When setting bit 7 to 1, the INT7* input is armed
+ 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.
+
+ 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
+ triggered.
+
+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.
+ Interrupt
+ ^
+ |
+ [Clock register] -> [Decrementer] -> [Clock read register]
+ ^ |
+ | v
+ +--<--- CRU write CRU read---<---+
+
+ The specs somewhat ambiguously say that "writing a non-zero value enables the clock"
+ 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.
+
+ 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
+ both clock and INT3* input. Writing any value to it changes the mask bit,
+ and as a side effect it also clears the clock interrupt.
+
+ According to [2], the clock mode is temporarily left when the CRU bits of
+ the I/O ports (bits 16-31) are accessed. Thus, the 9901 can control its
+ I/O ports even when it has been set to clock mode before. (Keep in mind
+ that clock mode simply means to access the clock register; the clock is
+ counting all the time.)
Raphael Nabet, 2000-2004
Michael Zapf
- February 2012: Rewritten as class
-
*****************************************************************************/
#include "emu.h"
@@ -139,12 +151,12 @@ TODO: Tests on a real machine
#define LOG_GENERAL (1U << 0)
#define LOG_PINS (1U << 1)
-#define LOG_CONFIG (1U << 2)
+#define LOG_MASK (1U << 2)
#define LOG_MODE (1U << 3)
#define LOG_INT (1U << 4)
-#define LOG_DECVALUE (1U << 5)
+#define LOG_CLOCK (1U << 5)
-#define VERBOSE ( 0 )
+#define VERBOSE ( LOG_GENERAL )
#include "logmacro.h"
/*
@@ -152,109 +164,126 @@ TODO: Tests on a real machine
*/
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_int_level(0),
+ m_last_level(0),
+ m_int_mask(0),
+ m_int_pending(false),
+ m_poll_lines(false),
m_clockdiv(0),
- m_read_block(*this),
+ m_timer_int_pending(false),
+ m_read_port(*this),
m_write_p{{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this},{*this}},
m_interrupt(*this)
{
}
/*
- should be called after any change to int_state or enabled_ints.
+ Determine the most significant interrupt (lowest number)
*/
-void tms9901_device::field_interrupts()
+void tms9901_device::prioritize_interrupts()
{
- int current_ints;
+ // Prioritizer: Search for the interrupt with the highest level
+ bool found = false;
+
+ // Skip the rightmost bit
+ uint16_t masked_ints = m_int_line;
- // m_int_state: inverted state of lines INT1*-INT15*. Bits are set by set_single_int only.
- current_ints = m_int_state;
- if (m_clock_register != 0)
+ // Do we have a timer interrupt?
+ if (m_clock_register != 0 && m_timer_int_pending)
{
- // if timer is enabled, INT3 pin is overridden by timer
- if (m_timer_int_pending)
- {
- LOGMASKED(LOG_INT, "INT3 (timer) asserted\n");
- current_ints |= INT3;
- }
+ masked_ints |= (1<<INT3);
+ LOGMASKED(LOG_INT, "INT3 (timer) asserted\n");
+ }
+
+ m_int_level = 1;
+ masked_ints = (masked_ints & m_int_mask)>>1;
+
+ while ((masked_ints!=0) && !found)
+ {
+ // If INTn is set, stop searching. Consider, however, that
+ // within INT7-INT15, those pins configured as outputs are not sampled
+ // (shared pins with P15-P7)
+
+ if ((masked_ints & 1) && ((m_int_level < 7) || !is_output(22-m_int_level)))
+ found = true;
else
{
- LOGMASKED(LOG_INT, "INT3 (timer) cleared\n");
- current_ints &= ~INT3;
+ m_int_level++;
+ masked_ints >>= 1;
}
}
- // enabled_ints: enabled interrupts
- // Remove all settings from pins that are set as outputs (INT7*-INT15* share the same pins as P15-P7)
- current_ints &= m_enabled_ints & (~m_pio_direction_mirror);
+ if (!found) m_int_level = 15;
- // Check whether we have a new state. For systems that use level-triggered
- // interrupts it should not do any harm if the line is re-asserted
- // but we may as well avoid this.
- if (current_ints == m_old_int_state)
+ m_int_pending = found;
+
+ // Only for asynchronous emulation
+ if (clock()!=0) signal_int();
+}
+
+bool tms9901_device::is_output(int p)
+{
+ return BIT(m_pio_direction, p);
+}
+
+bool tms9901_device::output_value(int p)
+{
+ return BIT(m_pio_output, p);
+}
+
+void tms9901_device::set_and_latch_output(int p, bool val)
+{
+ set_bit(m_pio_direction, p, true);
+ set_bit(m_pio_output, p, val);
+ m_write_p[p](val);
+}
+
+void tms9901_device::set_bit(uint16_t& bitfield, int pos, bool val)
+{
+ if (val) bitfield |= (1<<pos);
+ else bitfield &= ~(1<<pos);
+}
+
+void tms9901_device::signal_int()
+{
+ if (m_int_level == m_last_level)
return;
- m_old_int_state = current_ints;
+ m_last_level = m_int_level;
- if (current_ints != 0)
+ if (m_int_pending)
{
- // find which interrupt tripped us:
- // the number of the first (i.e. least significant) non-zero bit among
- // the 16 first bits
- // we simply look for the first bit set to 1 in current_ints...
- int level = 0;
-
- while ((current_ints & 1)==0)
- {
- current_ints >>= 1; /* try next bit */
- level++;
- }
- LOGMASKED(LOG_INT, "Triggering interrupt, level %d\n", level);
- m_int_pending = true;
+ LOGMASKED(LOG_INT, "Triggering interrupt, level %d\n", m_int_level);
if (!m_interrupt.isnull())
- m_interrupt(level, 1, 0xff); // the offset carries the IC0-3 level
+ m_interrupt(ASSERT_LINE);
}
else
{
- m_int_pending = false;
+ LOGMASKED(LOG_INT, "Clear all interrupts\n");
if (!m_interrupt.isnull())
- m_interrupt(0xf, 0, 0xff); //Spec: INTREQ*=1 <=> IC0,1,2,3 = 1111
+ m_interrupt(CLEAR_LINE); //Spec: INTREQ*=1 <=> IC0,1,2,3 = 1111
}
}
/*
- function which should be called by the driver when the state of an INTn*
- pin changes (only required if the pin is set up as an interrupt pin)
+ Signal an interrupt line change to the 9901.
+ The real circuit samples all active interrupt inputs on every falling edge
+ of the phi* clock, which is very inefficient to emulate.
+
+ Accordingly, we let the interrupt producer calls this method and
+ so push the interrupt line change.
state == CLEAR_LINE: INTn* is inactive (high)
state == ASSERT_LINE: INTn* is active (low)
- 0<=pin_number<=15
-*/
-void tms9901_device::set_single_int(int pin_number, int state)
-{
- /* remember new state of INTn* pin state */
- if (state==ASSERT_LINE)
- m_int_state |= 1 << pin_number;
- else
- m_int_state &= ~(1 << pin_number);
-
- field_interrupts();
-}
-
-/*
- load the content of m_clock_register into the decrementer
+ n=1..15
*/
-void tms9901_device::timer_reload()
+void tms9901_device::set_int_line(int n, int state)
{
- if (m_clock_register != 0)
- { /* reset clock interval */
- m_decrementer_value = m_clock_register;
- m_clock_active = true;
- }
- else
- { /* clock interval == 0 -> no timer */
- m_clock_active = false;
+ if ((n >= 1) && (n <= 15))
+ {
+ set_bit(m_int_line, n, state==ASSERT_LINE);
+ prioritize_interrupts();
}
}
@@ -263,245 +292,143 @@ void tms9901_device::timer_reload()
----------------------------------------------------------------*/
/*
- Read a 8 bit chunk from tms9901.
+ Read a bit from tms9901.
+
+ Bit Meaning
+ ---------------
+ 0 Control bit (0=Interrupt mode, 1=Clock mode)
+ 1..14 /INTn (int mode), CLKn (clock mode)
+ 15 /INT15 (int mode), /INTREQ (clock mode)
+ 16..31 P0...P15 input
- signification:
- bit 0: m_clock_mode
- if (m_clock_mode == false)
- bit 1-15: current status of the INT1*-INT15* pins
- else
- bit 1-14: current timer value
- bit 15: value of the INTREQ* (interrupt request to TMS9900) pin.
+ Reading an output port delivers the latched output value.
- bit 16-31: current status of the P0-P15 pins (quits timer mode, too...)
+ Ports P7 to P15 share pins with the interrupt inputs /INT15 to /INT7
+ (in this order). When configured as outputs, reading returns the latched
+ values.
*/
-uint8_t tms9901_device::read(offs_t offset)
+READ8_MEMBER( tms9901_device::read )
{
- int answer = 0;
+ int crubit = offset & 0x01f;
- offset &= 0x01f;
+ if (crubit == 0)
+ return m_clock_mode? 1 : 0;
- switch (offset >> 3)
+ if (crubit > 15)
{
- case 0:
- if (m_clock_mode)
- {
- // Clock mode. The LSB reflects the CB bit which is set to 1 for clock mode.
- answer = ((m_clock_read_register & 0x7F) << 1) | 0x01;
- }
+ // I/O lines
+ if (is_output(crubit-16))
+ return output_value(crubit-16);
else
{
- // Interrupt mode
- // Note that we rely on the read function to deliver the same
- // INTx levels that have been signaled via the set_single_int method.
- // This may mean that those levels must be latched by the callee.
- if (!m_read_block.isnull())
- answer |= m_read_block(CB_INT7);
-
- // Remove the bits that are set as outputs (can only be INT7*)
- answer &= ~m_pio_direction_mirror;
-
- // Set those bits here
- answer |= (m_pio_output_mirror & m_pio_direction_mirror) & 0xFF;
+ // Positive logic; should be 0 if there is no connection.
+ if (m_read_port.isnull()) return 0;
+ return m_read_port((crubit<=P6)? crubit : P6+P0-crubit);
}
- LOGMASKED(LOG_PINS, "Input on lines INT7..CB = %02x\n", answer);
- break;
- case 1:
- if (m_clock_mode)
- {
- // clock mode
- answer = (m_clock_read_register & 0x3F80) >> 7;
- if (!m_int_pending)
- answer |= 0x80;
- }
- else
- {
- // See above concerning the INT levels.
- if (!m_read_block.isnull())
- answer |= m_read_block(INT8_INT15);
-
- // Remove the bits that are set as outputs (can be any line)
- answer &= ~(m_pio_direction_mirror >> 8);
- answer |= (m_pio_output_mirror & m_pio_direction_mirror) >> 8;
- }
- LOGMASKED(LOG_PINS, "Input on lines INT15..INT8 = %02x\n", answer);
- break;
- case 2:
- /* exit timer mode */
- // MZ: See comments at the beginning. I'm sure that we do not quit clock mode.
- // m_clock_mode = false;
-
- if (!m_read_block.isnull())
- answer = m_read_block(P0_P7);
- else
- answer = 0;
+ }
- answer &= ~m_pio_direction;
- answer |= (m_pio_output & m_pio_direction) & 0xFF;
- LOGMASKED(LOG_PINS, "Input on lines P7..P0 = %02x\n", answer);
+ // If we are here, crubit=1..15
+ if (m_clock_mode)
+ {
+ if (crubit == 15) // bit 15 in clock mode = /INTREQ
+ return (m_int_pending)? 0 : 1;
- break;
- case 3:
- // MZ: see above
- // m_clock_mode = false;
- if (!m_read_block.isnull())
- answer = m_read_block(P8_P15);
+ return BIT(m_clock_read_register, crubit-1);
+ }
+ else
+ {
+ // We trust the read_port method to deliver the same INTx levels that
+ // have been signaled via the set_int_line method.
+ // Thus, those levels must be latched by the component that hosts
+ // this 9901. Alternatively, use the interrupt line polling
+ // which has a bad impact on performance.
+ if (crubit>INT6 && is_output(22-crubit))
+ return output_value(22-crubit);
else
- answer = 0;
-
- answer &= ~(m_pio_direction >> 8);
- answer |= (m_pio_output & m_pio_direction) >> 8;
- LOGMASKED(LOG_PINS, "Input on lines P15..P8 = %02x\n", answer);
-
- break;
+ return m_read_port.isnull()? 1 : m_read_port(crubit);
}
-
- return BIT(answer, offset & 7);
}
-/*
- Write 1 bit to tms9901.
-
- signification:
- bit 0: write m_clock_mode
- if (!m_clock_mode)
- bit 1-15: write interrupt mask register
- else
- bit 1-14: write timer period
- bit 15: if written value == 0, soft reset (just resets all I/O pins as input)
- bit 16-31: set output state of P0-P15 (and set them as output pin) (quit timer mode, too...)
+/*
+ Write one bit to the tms9901.
+
+ Bit Meaning
+ ----------------------------------------------------------
+ 0 0=Interrupt mode, 1=Clock mode
+ 1..14 Clock mode: Set CLKn; Interrupt mode: Set Mask n
+ 15 Clock mode: /RST2; Interrupt mode: Set Mask 15
+ 16..31 Set P(n-16) as output, latch value, and output it
*/
-void tms9901_device::write(offs_t offset, uint8_t data)
+WRITE8_MEMBER( tms9901_device::write )
{
- data &= 1; /* clear extra bits */
- offset &= 0x01F;
+ data &= 1; // clear extra bits
+ int crubit = offset & 0x001f;
- if (offset >= 0x10)
+ if (crubit >= 16)
{
- int pin = offset & 0x0F;
- LOGMASKED(LOG_PINS, "Output on P%d = %d\n", pin, data);
-
- int bit = (1 << pin);
-
- // MZ: see above - I think this is wrong
- // m_clock_mode = false; // exit timer mode
-
- // Once a value is written to a pin, the pin remains in output mode
- // until the chip is reset
- m_pio_direction |= bit;
-
- // Latch the value
- if (data)
- m_pio_output |= bit;
- else
- m_pio_output &= ~bit;
-
- if (pin >= 7)
- {
- // pins P7-P15 are mirrored as INT15*-INT7*,
- // also using the same pins in the package
- int mirror_bit = (1 << (22 - pin));
-
- // See above
- m_pio_direction_mirror |= mirror_bit;
-
- if (data)
- m_pio_output_mirror |= mirror_bit;
- else
- m_pio_output_mirror &= ~mirror_bit;
- }
-
- m_write_p[offset - 0x10](data);
+ LOGMASKED(LOG_PINS, "Output on P%d = %d\n", crubit-16, data);
+ set_and_latch_output(crubit-P0, data);
+ return;
}
- else if (offset == 0)
+
+ switch (crubit)
{
+ case 0:
// Write to control bit (CB)
- if (data == 0)
- {
- // Switch to interrupt mode; quit clock mode
- m_clock_mode = false;
- LOGMASKED(LOG_MODE, "Enter interrupt mode\n");
- }
- else
+ m_clock_mode = (data!=0);
+ LOGMASKED(LOG_MODE, "Enter %s mode\n", m_clock_mode? "clock" : "interrupt");
+
+ if (m_clock_mode)
{
- m_clock_mode = true;
- 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);
+ m_clock_read_register = m_decrementer_value;
+ LOGMASKED(LOG_MODE, "Clock setting = %d\n", m_clock_read_register);
}
- }
- else if (offset == 0x0f)
- {
+ break;
+
+ case 15:
if (m_clock_mode)
- { /* in clock mode this is the soft reset bit */
- if (!data)
- { // TMS9901 soft reset (RST2*)
- // Spec: "Writing a 0 to bit 15 while in the clock mode executes a soft reset on the I/O pins.
- // [...] RST2* will program all ports to the input mode"
- m_pio_direction = 0;
- m_pio_direction_mirror = 0;
-
- // "RST1* (power-up reset) will reset all mask bits low."
- // 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;
- LOGMASKED(LOG_MODE, "Soft reset (RST2*)\n");
- }
+ {
+ // In clock mode, bit 15 is /RST2
+ if (data == 0) soft_reset();
}
else
- { /* modify interrupt enable mask */
- if (data)
- m_enabled_ints |= 0x4000; /* set bit */
- else
- m_enabled_ints &= ~0x4000; /* unset bit */
-
- LOGMASKED(LOG_CONFIG, "Enabled interrupts = %04x\n", m_enabled_ints);
- field_interrupts(); /* changed interrupt state */
+ {
+ set_bit(m_int_mask, 15, data!=0);
+ LOGMASKED(LOG_MASK, "/INT15 is %s\n", data? "enabled" : "disabled");
+ prioritize_interrupts(); // changed interrupt state
}
- }
- else
- {
- // write one bit to 9901 (bits 1-14)
- //
- // m_clock_mode==false ? Disable/Enable an interrupt
- // : Bit in clock interval
- //
- // offset is the index of the modified bit of register (-> interrupt number -1)
+ break;
+
+ default:
+ // Bits 1..14
if (m_clock_mode)
- { /* modify clock interval */
- int bit = 1 << ((offset & 0x0F) - 1); /* corresponding mask */
+ {
+ // Modify clock interval
+ set_bit(m_clock_register, crubit-1, data!=0);
- if (data)
- m_clock_register |= bit; /* set bit */
- else
- m_clock_register &= ~bit; /* clear bit */
+ // Reset clock timer (page 8)
+ m_decrementer_value = m_clock_register;
- /* reset clock timer (page 8) */
- LOGMASKED(LOG_CONFIG, "Clock register = %04x\n", m_clock_register);
- timer_reload();
+ LOGMASKED(LOG_CLOCK, "Clock register = %04x\n", m_clock_register);
}
else
- { /* modify interrupt enable mask */
- int bit = 1 << (offset & 0x0F); /* corresponding mask */
-
- if (data)
- m_enabled_ints |= bit; /* set bit */
- else
- m_enabled_ints &= ~bit; /* unset bit */
+ {
+ // Modify interrupt enable mask
+ set_bit(m_int_mask, crubit, data!=0);
- if (offset == 3)
- m_timer_int_pending = false; /* SBO 3 clears pending timer interrupt (??) */
+ // [1] sect 2.5: "When the clock interrupt is active, the clock mask
+ // (mask bit 3) must be written into (with either a "0" or "1")
+ // to clear the interrupt."
+ if (crubit == 3)
+ m_timer_int_pending = false;
- LOGMASKED(LOG_CONFIG, "Enabled interrupts = %04x\n", m_enabled_ints);
- field_interrupts(); /* changed interrupt state */
+ LOGMASKED(LOG_MASK, "/INT%d is %s\n", crubit, data? "enabled" : "disabled");
+ prioritize_interrupts();
}
+ break;
}
}
@@ -509,28 +436,30 @@ void tms9901_device::write(offs_t offset, uint8_t data)
Timer callback
Decrementer counts down the value set in clock mode; when it reaches 0,
raises an interrupt and resets to the start value
- The decrementer works as long as the clock_register contains a non-zero value.
*/
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);
+ timer_clock_in(ASSERT_LINE);
+ timer_clock_in(CLEAR_LINE);
}
}
-void tms9901_device::clock_in(line_state clk)
+void tms9901_device::timer_clock_in(line_state clk)
{
- if (m_clock_active && clk == ASSERT_LINE)
+ if (clk == ASSERT_LINE)
{
- m_decrementer_value--;
- LOGMASKED(LOG_DECVALUE, "Decrementer = %d\n", m_decrementer_value);
- if (m_decrementer_value<=0)
+ m_decrementer_value = (m_decrementer_value - 1) & 0x3FFF;
+ LOGMASKED(LOG_CLOCK, "Clock = %04x\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();
+ if (m_clock_register != 0)
+ {
+ LOGMASKED(LOG_INT, "Timer expired\n");
+ m_timer_int_pending = true; // decrementer interrupt requested
+ prioritize_interrupts();
+ }
m_decrementer_value = m_clock_register;
}
}
@@ -543,12 +472,69 @@ void tms9901_device::clock_in(line_state clk)
*/
WRITE_LINE_MEMBER( tms9901_device::phi_line )
{
- // Divider by 64
if (state==ASSERT_LINE)
- m_clockdiv = (m_clockdiv+1) % 0x40;
+ {
+ // Divider by 64
+ m_clockdiv = (m_clockdiv+1) & 0x3f;
+ if (m_clockdiv==0)
+ {
+ timer_clock_in(ASSERT_LINE);
+
+ // We signal the interrupt in sync with the clock line
+ signal_int();
- if (m_clockdiv==0)
- clock_in((line_state)state);
+ // For the next phi assert
+ // MZ: This costs a lot of performance for a minimum of benefit.
+ if (m_poll_lines) sample_interrupt_inputs();
+ }
+ else
+ {
+ if (m_clockdiv==32)
+ timer_clock_in(CLEAR_LINE);
+ }
+ }
+}
+
+/*
+ All unmasked interrupt ports are sampled at the rising edge of phi.
+ Doing it this way (also for performance issues): For each mask bit 1,
+ fetch the pin level. Stop at the first asserted INT line.
+
+ Good idea in terms of emulation, bad in terms of performance. The Geneve
+ 9640 bench dropped from 680% to 180%. Not recommended to use, except in
+ very special situations. Enable by calling set_poll_int_lines(true).
+*/
+void tms9901_device::sample_interrupt_inputs()
+{
+ int mask = m_int_mask;
+ m_int_level = 0;
+ m_int_pending = false;
+
+ while (mask != 0 && !m_int_pending)
+ {
+ m_int_level++;
+ if ((mask & 1)!=0)
+ {
+ // Negative logic
+ if (m_read_port(m_int_level)==0)
+ m_int_pending = true;
+ }
+ mask >>= 1;
+ }
+}
+
+void tms9901_device::soft_reset()
+{
+ // TMS9901 soft reset (RST2*)
+ // Spec: "Writing a 0 to bit 15 while in the clock mode executes a soft reset on the I/O pins.
+ // [...] RST2* will program all ports to the input mode"
+ m_pio_direction = 0;
+ m_pio_output = 0;
+
+ // We assume that the interrupt mask is also reset.
+ m_int_mask = 0;
+
+ LOGMASKED(LOG_MODE, "Soft reset (RST2*)\n");
}
/*-------------------------------------------------
@@ -579,23 +565,16 @@ WRITE_LINE_MEMBER( tms9901_device::rst1_line )
void tms9901_device::do_reset()
{
m_timer_int_pending = false;
- m_enabled_ints = 0;
-
- m_pio_direction = 0;
- m_pio_direction_mirror = 0;
- m_pio_output = m_pio_output_mirror = 0;
+ soft_reset();
// This is an interrupt level latch, positive logic (bit 0 = no int)
// The inputs are negative logic (INTx*)
- m_int_state = 0;
+ m_int_line = 0;
- m_old_int_state = -1;
- field_interrupts();
+ prioritize_interrupts();
m_clock_mode = false;
-
- m_clock_register = 0;
- timer_reload();
+ m_decrementer_value = m_clock_register = 0;
}
@@ -612,22 +591,21 @@ void tms9901_device::device_start()
m_decrementer->adjust(attotime::from_hz(clock() / 64.), 0, attotime::from_hz(clock() / 64.));
}
- m_read_block.resolve();
+ m_read_port.resolve();
for (auto &cb : m_write_p)
cb.resolve_safe();
m_interrupt.resolve();
m_clock_register = 0;
- save_item(NAME(m_int_state));
- save_item(NAME(m_old_int_state));
- save_item(NAME(m_enabled_ints));
+ save_item(NAME(m_int_line));
+ save_item(NAME(m_pio_output));
+ save_item(NAME(m_pio_direction));
+ save_item(NAME(m_int_level));
+ save_item(NAME(m_int_mask));
+ save_item(NAME(m_last_level));
save_item(NAME(m_int_pending));
save_item(NAME(m_timer_int_pending));
- save_item(NAME(m_pio_direction));
- save_item(NAME(m_pio_output));
- save_item(NAME(m_pio_direction_mirror));
- save_item(NAME(m_pio_output_mirror));
save_item(NAME(m_clock_mode));
save_item(NAME(m_clock_register));
save_item(NAME(m_decrementer_value));
diff --git a/src/devices/machine/tms9901.h b/src/devices/machine/tms9901.h
index eda01f253ad..a962ceb28f7 100644
--- a/src/devices/machine/tms9901.h
+++ b/src/devices/machine/tms9901.h
@@ -29,55 +29,65 @@ DECLARE_DEVICE_TYPE(TMS9901, tms9901_device)
class tms9901_device : public device_t
{
public:
- // Masks for the interrupts levels available on TMS9901
- static constexpr int INT1 = 0x0002;
- static constexpr int INT2 = 0x0004;
- static constexpr int INT3 = 0x0008; // overridden by the timer interrupt
- static constexpr int INT4 = 0x0010;
- static constexpr int INT5 = 0x0020;
- static constexpr int INT6 = 0x0040;
- static constexpr int INT7 = 0x0080;
- static constexpr int INT8 = 0x0100;
- static constexpr int INT9 = 0x0200;
- static constexpr int INTA = 0x0400;
- static constexpr int INTB = 0x0800;
- static constexpr int INTC = 0x1000;
- static constexpr int INTD = 0x2000;
- static constexpr int INTE = 0x4000;
- static constexpr int INTF = 0x8000;
-
+ // I/O pins
enum
{
- CB_INT7 = 0,
- INT8_INT15 = 1,
- P0_P7 = 2,
- P8_P15 = 3
- };
+ INT1=1,
+ INT2,
+ INT3,
+ INT4,
+ INT5,
+ INT6,
+ INT7_P15,
+ INT8_P14,
+ INT9_P13,
+ INT10_P12,
+ INT11_P11,
+ INT12_P10,
+ INT13_P9,
+ INT14_P8,
+ INT15_P7,
+ P0,
+ P1,
+ P2,
+ P3,
+ P4,
+ P5,
+ P6
+ } pins;
tms9901_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
- void set_single_int(int pin_number, int state);
+ void set_int_line(int pin_number, int state);
DECLARE_WRITE_LINE_MEMBER( rst1_line );
// Synchronous clock input
DECLARE_WRITE_LINE_MEMBER( phi_line );
- uint8_t read(offs_t offset);
- void write(offs_t offset, uint8_t data);
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
auto p_out_cb(int n) { return m_write_p[n].bind(); }
- auto read_cb() { return m_read_block.bind(); }
- auto intlevel_cb() { return m_interrupt.bind(); }
+ auto read_cb() { return m_read_port.bind(); }
+ auto intreq_cb() { return m_interrupt.bind(); }
+
+ // Pins IC3...IC0
+ // When no interrupt is active, the IC lines are all set to 1
+ // The difference to /INT15 is that INTREQ is cleared.
+ int get_int_level() { return m_int_pending? m_int_level : 15; }
+ // Return PIO all outputs; ports configured as inputs return 1
+ // Used by si5500
uint16_t pio_outputs() const { return m_pio_output | ~m_pio_direction; }
+ void set_poll_int_lines(bool poll) { m_poll_lines = poll; }
+
private:
static constexpr device_timer_id DECREMENTER = 0;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
- void timer_reload();
- void field_interrupts();
+ void soft_reset();
void device_start() override;
void device_stop() override;
@@ -87,62 +97,76 @@ private:
void do_reset();
// Common clock handling
- void clock_in(line_state clk);
+ void timer_clock_in(line_state clk);
+
+ // Direction and value of P pins
+ bool is_output(int p);
+ bool output_value(int p);
+ void set_and_latch_output(int p, bool val);
+ void set_bit(uint16_t& bitfield, int pos, bool val);
+
+ int m_int_level;
+ int m_last_level;
// 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.
- int m_int_state;
- int m_old_int_state; // stores the previous value to avoid useless INT line assertions
- int m_enabled_ints; // interrupt enable mask
+ // may require to latch the int levels on the caller's side.
+ uint16_t m_int_line;
+ uint16_t m_int_mask;
+ bool m_int_pending;
+ bool m_poll_lines;
- 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)
+ // Find the interrupt with the lowest level (most important)
+ void prioritize_interrupts();
- bool m_clock_active;
- int m_clockdiv; // Clock divider counter (for synchronous clock)
+ // Outgoing INTREQ* line
+ void signal_int();
- // PIO registers
- int m_pio_direction; // direction register for PIO
+ // Sample the interrupt inputs.
+ void sample_interrupt_inputs();
- // current PIO output (to be masked with pio_direction)
- int m_pio_output;
+ // P15..P0; 1=output H, 0=output L
+ uint16_t m_pio_output;
- // mirrors used for INT7*-INT15*
- int m_pio_direction_mirror;
- int m_pio_output_mirror;
+ // For P15..P0; 1=output, 0=input
+ // Once set to 1, a reset is necessary to return the pin to input
+ uint16_t m_pio_direction;
// =======================================================================
// TMS9901 clock mode
- // false = so-called interrupt mode (read interrupt state, write interrupt enable mask)
+ // false = interrupt mode (read interrupt state, write interrupt enable mask)
// true = clock mode (read/write clock interval)
bool m_clock_mode;
+ // Clock divider
+ int m_clockdiv;
+
+ // Clock has reached 0
+ bool m_timer_int_pending;
+
// Timer, used to emulate the decrementer register
emu_timer *m_decrementer;
- // clock interval, loaded in decrementer when it reaches 0.
- // 0 means decrementer off
- int m_clock_register;
+ // Clock interval, loaded in decrementer when it reaches 0.
+ uint16_t m_clock_register;
// Current decrementer value
- int m_decrementer_value;
+ uint16_t m_decrementer_value;
// when we go into timer mode, the decrementer is copied there to allow to read it reliably
- int m_clock_read_register;
+ uint16_t m_clock_read_register;
// =======================================================================
// Read callback.
- devcb_read8 m_read_block;
+ devcb_read8 m_read_port;
// I/O lines, used for output. When used as inputs, the levels are delivered via the m_read_block
devcb_write_line m_write_p[16];
- // The invocation corresponds to the INTREQ signal (with the level passed as data)
- // and the address delivers the interrupt level (0-15)
- devcb_write8 m_interrupt;
+ // INTREQ pin
+ devcb_write_line m_interrupt;
};
#endif // MAME_MACHINE_TMS9901_H
diff --git a/src/mame/drivers/geneve.cpp b/src/mame/drivers/geneve.cpp
index 84f3f28e12b..44ac3436f68 100644
--- a/src/mame/drivers/geneve.cpp
+++ b/src/mame/drivers/geneve.cpp
@@ -203,7 +203,7 @@
#define LOG_CRU (1U<<4)
// Minimum log should be settings and warnings
-#define VERBOSE (LOG_GENERAL | LOG_WARN)
+#define VERBOSE ( LOG_GENERAL | LOG_WARN )
#include "logmacro.h"
@@ -234,7 +234,7 @@ private:
void cruwrite(offs_t offset, uint8_t data);
// Connections with the system interface TMS9901
- uint8_t read_by_9901(offs_t offset);
+ uint8_t psi_input(offs_t offset);
DECLARE_WRITE_LINE_MEMBER(peripheral_bus_reset);
DECLARE_WRITE_LINE_MEMBER(VDP_reset);
DECLARE_WRITE_LINE_MEMBER(joystick_select);
@@ -453,66 +453,105 @@ uint8_t geneve_state::cruread(offs_t offset)
CRU callbacks
***********************************************************************/
-uint8_t geneve_state::read_by_9901(offs_t offset)
+uint8_t geneve_state::psi_input(offs_t offset)
{
- int answer = 0;
-
- switch (offset & 0x03)
+ switch (offset)
{
- case tms9901_device::CB_INT7:
- //
- // Read pins INT3*-INT7* of Geneve's 9901.
- // bit 1: INTA status
- // bit 2: INT2 status
- // bit 3-7: joystick status
- //
- // |K|K|K|K|K|I2|I1|C|
- // negative logic
- if (m_inta==CLEAR_LINE) answer |= 0x02;
- if (m_int2==CLEAR_LINE) answer |= 0x04;
- answer |= m_joyport->read_port()<<3;
- break;
-
- case tms9901_device::INT8_INT15:
- // Read pins int8_t*-INT15* of Geneve 9901.
- //
- // bit 0: keyboard interrupt
- // bit 1: unused
- // bit 2: mouse left button
- // (bit 3: clock interrupt)
- // bit 4: INTB from PE-bus
- // bit 5 & 7: used as output
- // bit 6: unused
- if (m_keyint==CLEAR_LINE) answer |= 0x01;
- if (m_colorbus->left_button()==CLEAR_LINE) answer |= 0x04;
- // TODO: add clock interrupt
- if (m_intb==CLEAR_LINE) answer |= 0x10;
- if (m_video_wait==ASSERT_LINE) answer |= 0x20;
- // TODO: PAL pin 5
- LOGMASKED(LOG_LINES, "INT15-8 = %02x\n", answer);
- break;
-
- case tms9901_device::P0_P7:
- // Read pins P0-P7 of TMS9901. All pins are configured as outputs, so nothing here.
- break;
-
- case tms9901_device::P8_P15:
- // Read pins P8-P15 of TMS 9901.
- // bit 4: mouse left button
- // video wait is an output; no input possible here
- if (m_intb==CLEAR_LINE) answer |= 0x04; // mirror from above
- // TODO: 0x08 = real-time clock int
- if (m_colorbus->left_button()==CLEAR_LINE) answer |= 0x10; // mirror from above
- if (m_keyint==CLEAR_LINE) answer |= 0x40;
-
- // Joystick up (mirror of bit 7)
- if ((m_joyport->read_port() & 0x10)==0) answer |= 0x80;
- break;
+ // External interrupt (INTA)
+ case tms9901_device::INT1:
+ return (m_inta==CLEAR_LINE)? 1 : 0;
+
+ // Video interrupt
+ case tms9901_device::INT2:
+ return (m_int2==CLEAR_LINE)? 1 : 0;
+
+ // Joystick port
+ case tms9901_device::INT3:
+ case tms9901_device::INT4:
+ case tms9901_device::INT5:
+ case tms9901_device::INT6:
+ case tms9901_device::INT7_P15:
+ return BIT(m_joyport->read_port(), offset-tms9901_device::INT3);
+
+ // Keyboard interrupt
+ case tms9901_device::INT8_P14:
+ return (m_keyint==CLEAR_LINE)? 1 : 0;
+
+ // Left mouse button
+ case tms9901_device::INT10_P12:
+ LOG("Mouse button = %d\n", m_colorbus->left_button());
+ return (m_colorbus->left_button()==CLEAR_LINE)? 1 : 0;
+
+ // TODO: Real time clock interrupt
+ case tms9901_device::INT11_P11:
+ return 1;
+
+ // INTB interrupt
+ case tms9901_device::INT12_P10:
+ return (m_intb==CLEAR_LINE)? 1 : 0;
+
+ default:
+ LOG("Unknown pin %d\n", offset);
+ return 1;
}
- return answer;
}
/*
+ switch (offset & 0x03)
+ {
+ case tms9901_device::CB_INT7:
+ //
+ // Read pins INT3*-INT7* of Geneve's 9901.
+ // bit 1: INTA status
+ // bit 2: INT2 status
+ // bit 3-7: joystick status
+ //
+ // |K|K|K|K|K|I2|I1|C|
+ // negative logic
+ if (m_inta==CLEAR_LINE) answer |= 0x02;
+ if (m_int2==CLEAR_LINE) answer |= 0x04;
+ answer |= m_joyport->read_port()<<3;
+ break;
+
+ case tms9901_device::INT8_INT15:
+ // Read pins int8_t*-INT15* of Geneve 9901.
+ //
+ // bit 0: keyboard interrupt
+ // bit 1: unused
+ // bit 2: mouse left button
+ // (bit 3: clock interrupt)
+ // bit 4: INTB from PE-bus
+ // bit 5 & 7: used as output
+ // bit 6: unused
+ if (m_keyint==CLEAR_LINE) answer |= 0x01;
+ if (m_colorbus->left_button()==CLEAR_LINE) answer |= 0x04;
+ // TODO: add clock interrupt
+ if (m_intb==CLEAR_LINE) answer |= 0x10;
+ if (m_video_wait==ASSERT_LINE) answer |= 0x20;
+ // TODO: PAL pin 5
+ LOGMASKED(LOG_LINES, "INT15-8 = %02x\n", answer);
+ break;
+
+ case tms9901_device::P0_P7:
+ // Read pins P0-P7 of TMS9901. All pins are configured as outputs, so nothing here.
+ break;
+
+ case tms9901_device::P8_P15:
+ // Read pins P8-P15 of TMS 9901.
+ // bit 4: mouse left button
+ // video wait is an output; no input possible here
+ if (m_intb==CLEAR_LINE) answer |= 0x04; // mirror from above
+ // TODO: 0x08 = real-time clock int
+ if (m_colorbus->left_button()==CLEAR_LINE) answer |= 0x10; // mirror from above
+ if (m_keyint==CLEAR_LINE) answer |= 0x40;
+
+ // Joystick up (mirror of bit 7)
+ if ((m_joyport->read_port() & 0x10)==0) answer |= 0x80;
+ break;
+ }
+ */
+
+/*
Write PE bus reset line
*/
WRITE_LINE_MEMBER( geneve_state::peripheral_bus_reset )
@@ -577,7 +616,7 @@ void geneve_state::tms9901_interrupt(offs_t offset, uint8_t data)
WRITE_LINE_MEMBER( geneve_state::inta )
{
m_inta = (state!=0)? ASSERT_LINE : CLEAR_LINE;
- m_tms9901->set_single_int(1, state);
+ m_tms9901->set_int_line(1, state);
m_cpu->set_input_line(INT_9995_INT4, state);
}
@@ -587,7 +626,7 @@ WRITE_LINE_MEMBER( geneve_state::inta )
WRITE_LINE_MEMBER( geneve_state::intb )
{
m_intb = (state!=0)? ASSERT_LINE : CLEAR_LINE;
- m_tms9901->set_single_int(12, state);
+ m_tms9901->set_int_line(12, state);
}
WRITE_LINE_MEMBER( geneve_state::ext_ready )
@@ -614,7 +653,7 @@ WRITE_LINE_MEMBER(geneve_state::set_tms9901_INT2_from_v9938)
if (state != m_int2)
{
m_int2 = (state!=0)? ASSERT_LINE : CLEAR_LINE;
- m_tms9901->set_single_int(2, state);
+ m_tms9901->set_int_line(2, state);
if (state!=0)
{
m_colorbus->poll();
@@ -628,7 +667,7 @@ WRITE_LINE_MEMBER(geneve_state::set_tms9901_INT2_from_v9938)
WRITE_LINE_MEMBER( geneve_state::keyboard_interrupt )
{
m_keyint = (state!=0)? ASSERT_LINE : CLEAR_LINE;
- m_tms9901->set_single_int(8, state);
+ m_tms9901->set_int_line(8, state);
}
void geneve_state::external_operation(offs_t offset, uint8_t data)
@@ -741,7 +780,7 @@ void geneve_state::geneve_common(machine_config &config)
// Main board components
TMS9901(config, m_tms9901, 0);
- m_tms9901->read_cb().set(FUNC(geneve_state::read_by_9901));
+ m_tms9901->read_cb().set(FUNC(geneve_state::psi_input));
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));
m_tms9901->p_out_cb(2).set(FUNC(geneve_state::joystick_select));
@@ -751,7 +790,7 @@ void geneve_state::geneve_common(machine_config &config)
m_tms9901->p_out_cb(7).set(FUNC(geneve_state::extbus_wait_states));
m_tms9901->p_out_cb(9).set(FUNC(geneve_state::video_wait_states));
m_tms9901->p_out_cb(13).set(GENEVE_MAPPER_TAG, FUNC(bus::ti99::internal::geneve_mapper_device::pfm_select_msb));
- m_tms9901->intlevel_cb().set(FUNC(geneve_state::tms9901_interrupt));
+ m_tms9901->intreq_cb().set(FUNC(geneve_state::tms9901_interrupt));
// Clock
MM58274C(config, GENEVE_CLOCK_TAG, 0).set_mode_and_day(1, 0); // 24h, sunday
diff --git a/src/mame/drivers/si5500.cpp b/src/mame/drivers/si5500.cpp
index 605466f5a07..e9ed91ab180 100644
--- a/src/mame/drivers/si5500.cpp
+++ b/src/mame/drivers/si5500.cpp
@@ -72,20 +72,31 @@ void si5500_state::mainic_w(offs_t offset, u8 data)
WRITE_LINE_MEMBER(si5500_state::gpib_int_w)
{
- m_mainpsi->set_single_int(4, state);
+ m_mainpsi->set_int_line(4, state);
}
WRITE_LINE_MEMBER(si5500_state::acc_int_w)
{
- m_mainpsi->set_single_int(5, state);
+ m_mainpsi->set_int_line(5, state);
}
u8 si5500_state::gpibpsi_input_r(offs_t offset)
{
- if (offset == tms9901_device::P0_P7)
- return m_gpib_data;
-
- return 0xff;
+ switch (offset)
+ {
+ case tms9901_device::P0:
+ case tms9901_device::P1:
+ case tms9901_device::P2:
+ case tms9901_device::P3:
+ case tms9901_device::P4:
+ case tms9901_device::P5:
+ case tms9901_device::P6:
+ return BIT(m_gpib_data, offset-tms9901_device::P0);
+ case tms9901_device::INT15_P7:
+ return BIT(m_gpib_data, 8);
+ default:
+ return 1;
+ }
}
WRITE_LINE_MEMBER(si5500_state::gpibc_we_w)
@@ -175,7 +186,7 @@ void si5500_state::si5500(machine_config &config)
m_maincpu->set_addrmap(AS_IO, &si5500_state::cru_map);
TMS9901(config, m_mainpsi, 10_MHz_XTAL / 4);
- m_mainpsi->intlevel_cb().set(FUNC(si5500_state::mainic_w));
+ m_mainpsi->intreq_cb().set(FUNC(si5500_state::mainic_w));
tms9902_device &acc(TMS9902(config, "acc", 10_MHz_XTAL / 4));
acc.int_cb().set(FUNC(si5500_state::acc_int_w));
diff --git a/src/mame/drivers/ti99_4p.cpp b/src/mame/drivers/ti99_4p.cpp
index 3b0be43a5b4..215916f68e6 100644
--- a/src/mame/drivers/ti99_4p.cpp
+++ b/src/mame/drivers/ti99_4p.cpp
@@ -132,12 +132,12 @@
#define LOG_WARN (1U<<1) // Warnings
#define LOG_ILLWRITE (1U<<2)
#define LOG_READY (1U<<3)
-#define LOG_INT (1U<<4)
+#define LOG_INTERRUPTS (1U<<4)
#define LOG_ADDRESS (1U<<5)
#define LOG_MEM (1U<<6)
#define LOG_MUX (1U<<7)
-#define VERBOSE ( LOG_WARN )
+#define VERBOSE ( LOG_GENERAL | LOG_WARN )
#include "logmacro.h"
@@ -177,7 +177,7 @@ private:
// CRU (Communication Register Unit) handling
uint8_t cruread(offs_t offset);
void cruwrite(offs_t offset, uint8_t data);
- uint8_t read_by_9901(offs_t offset);
+ uint8_t psi_input(offs_t offset);
DECLARE_WRITE_LINE_MEMBER(keyC0);
DECLARE_WRITE_LINE_MEMBER(keyC1);
DECLARE_WRITE_LINE_MEMBER(keyC2);
@@ -195,6 +195,10 @@ private:
void datamux_clock_in(int clock);
+ // Latch for 9901 INT1 and INT2 lines
+ int m_int1;
+ int m_int2;
+
// Devices
required_device<tms9900_device> m_cpu;
required_device<tms9901_device> m_tms9901;
@@ -217,7 +221,7 @@ private:
uint16_t *m_rom;
// First joystick. 6 for TI-99/4A
- int m_firstjoy;
+ static constexpr int FIRSTJOY=6;
int m_keyboard_column;
int m_check_alphalock;
@@ -274,10 +278,6 @@ private:
// Mapper registers
uint8_t m_mapper[16];
- // Latch for 9901 INT2, INT1 lines
- int m_9901_int;
- void set_9901_int(int line, line_state state);
-
int m_ready_prev; // for debugging purposes only
};
@@ -377,7 +377,7 @@ static INPUT_PORTS_START(ti99_4p)
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('-')
PORT_START("ALPHA") /* one more port for Alpha line */
- PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Alpha Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alpha Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
INPUT_PORTS_END
@@ -725,61 +725,103 @@ uint8_t ti99_4p_state::cruread(offs_t offset)
Keyboard/tape control
****************************************************************************/
-uint8_t ti99_4p_state::read_by_9901(offs_t offset)
+uint8_t ti99_4p_state::psi_input(offs_t offset)
{
- int answer=0;
-
- switch (offset & 0x03)
+ switch (offset)
{
- case tms9901_device::CB_INT7:
- // Read pins INT3*-INT7* of TI99's 9901.
- // bit 1: INT1 status
- // bit 2: INT2 status
- // bit 3-7: keyboard status bits 0 to 4
- //
- // |K|K|K|K|K|I2|I1|C|
- //
- if (m_keyboard_column >= m_firstjoy) // joy 1 and 2
- {
- answer = m_joyport->read_port();
- }
+ case tms9901_device::INT1:
+ return (m_int1==CLEAR_LINE)? 1 : 0;
+ case tms9901_device::INT2:
+ return (m_int2==CLEAR_LINE)? 1 : 0;
+ case tms9901_device::INT3:
+ case tms9901_device::INT4:
+ case tms9901_device::INT5:
+ case tms9901_device::INT6:
+ // Keyboard ACTIVE_LOW (s.o.)
+ // Joysticks ACTIVE_LOW (handset.cpp)
+ if (m_keyboard_column >= 6)
+ return BIT(m_joyport->read_port(), offset-tms9901_device::INT3);
+ else
+ return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
+
+ case tms9901_device::INT7_P15:
+ if (m_keyboard_column >= 6) // Joysticks
+ return BIT(m_joyport->read_port(), offset-tms9901_device::INT3);
else
{
- answer = m_keyboard[m_keyboard_column]->read();
- }
- if (m_check_alphalock)
- {
- answer &= ~(m_alpha->read());
+ if (m_check_alphalock)
+ {
+ return BIT(m_alpha->read(), offset-tms9901_device::INT3);
+ }
+ else
+ return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
}
- answer = (answer << 3) | m_9901_int;
- break;
-
- case tms9901_device::INT8_INT15:
- // Read pins int8_t*-INT15* of TI99's 9901.
- // bit 0-2: keyboard status bits 5 to 7
- // bit 3: tape input mirror
- // bit 5-7: weird, not emulated
- // |1|1|1|1|0|K|K|K|
- if (m_keyboard_column >= m_firstjoy) answer = 0x07;
- else answer = ((m_keyboard[m_keyboard_column]->read())>>5) & 0x07;
- answer |= 0xf0;
- break;
-
- case tms9901_device::P0_P7:
- break;
-
- case tms9901_device::P8_P15:
- // Read pins P8-P15 of TI99's 9901.
- // bit 26: high
- // bit 27: tape input
- answer = 4;
- if (m_cassette->input() > 0) answer |= 8;
- break;
+ case tms9901_device::INT8_P14:
+ case tms9901_device::INT9_P13:
+ case tms9901_device::INT10_P12:
+ if (m_keyboard_column >= FIRSTJOY) // no joystick lines after /INT7
+ return 1;
+ else
+ return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
+ case tms9901_device::INT11_P11:
+ // CS2 is write-only
+ return (m_cassette->input() > 0);
+ default:
+ return 1;
}
- return answer;
}
+/* switch (offset & 0x03)
+ {
+ case tms9901_device::CB_INT7:
+ // Read pins INT3*-INT7* of TI99's 9901.
+ // bit 1: INT1 status
+ // bit 2: INT2 status
+ // bit 3-7: keyboard status bits 0 to 4
+ //
+ // |K|K|K|K|K|I2|I1|C|
+ //
+ if (m_keyboard_column >= FIRSTJOY) // joy 1 and 2
+ {
+ answer = m_joyport->read_port();
+ }
+ else
+ {
+ answer = m_keyboard[m_keyboard_column]->read();
+ }
+ if (m_check_alphalock)
+ {
+ answer &= ~(m_alpha->read());
+ }
+ answer = (answer << 3) | m_9901_int;
+ break;
+
+ case tms9901_device::INT8_INT15:
+ // Read pins int8_t*-INT15* of TI99's 9901.
+ // bit 0-2: keyboard status bits 5 to 7
+ // bit 3: tape input mirror
+ // bit 5-7: weird, not emulated
+
+ // |1|1|1|1|0|K|K|K|
+ if (m_keyboard_column >= FIRSTJOY) answer = 0x07;
+ else answer = ((m_keyboard[m_keyboard_column]->read())>>5) & 0x07;
+ answer |= 0xf0;
+ break;
+
+ case tms9901_device::P0_P7:
+ break;
+
+ case tms9901_device::P8_P15:
+ // Read pins P8-P15 of TI99's 9901.
+ // bit 26: high
+ // bit 27: tape input
+ answer = 4;
+ if (m_cassette->input() > 0) answer |= 8;
+ break;
+ }
+ */
+
/*
WRITE key column select (P2-P4)
*/
@@ -788,9 +830,9 @@ void ti99_4p_state::set_keyboard_column(int number, int data)
if (data!=0) m_keyboard_column |= 1 << number;
else m_keyboard_column &= ~(1 << number);
- if (m_keyboard_column >= m_firstjoy)
+ if (m_keyboard_column >= FIRSTJOY)
{
- m_joyport->write_port(m_keyboard_column - m_firstjoy + 1);
+ m_joyport->write_port(m_keyboard_column - FIRSTJOY + 1);
}
}
@@ -874,23 +916,16 @@ WRITE_LINE_MEMBER( ti99_4p_state::ready_line )
ready_join();
}
-void ti99_4p_state::set_9901_int( int line, line_state state)
-{
- m_tms9901->set_single_int(line, state);
- // We latch the value for the read operation. Mind the negative logic.
- if (state==CLEAR_LINE) m_9901_int |= (1<<line);
- else m_9901_int &= ~(1<<line);
-}
-
WRITE_LINE_MEMBER( ti99_4p_state::extint )
{
- LOGMASKED(LOG_INT, "EXTINT level = %02x\n", state);
- set_9901_int(1, (line_state)state);
+ LOGMASKED(LOG_INTERRUPTS, "EXTINT level = %02x\n", state);
+ m_int1 = (line_state)state;
+ m_tms9901->set_int_line(1, state);
}
WRITE_LINE_MEMBER( ti99_4p_state::notconnected )
{
- LOGMASKED(LOG_INT, "Setting a not connected line ... ignored\n");
+ LOGMASKED(LOG_INTERRUPTS, "Setting a not connected line ... ignored\n");
}
/*
@@ -931,14 +966,13 @@ void ti99_4p_state::driver_start()
m_peribox->senila(CLEAR_LINE);
m_peribox->senilb(CLEAR_LINE);
- m_firstjoy = 6;
-
m_sysready = ASSERT_LINE;
m_muxready = true;
m_rom = (uint16_t*)(memregion("maincpu")->base());
- save_item(NAME(m_firstjoy));
+ save_item(NAME(m_int1));
+ save_item(NAME(m_int2));
save_item(NAME(m_keyboard_column));
save_item(NAME(m_check_alphalock));
save_item(NAME(m_internal_dsr));
@@ -960,7 +994,6 @@ void ti99_4p_state::driver_start()
save_item(NAME(m_highbyte));
save_item(NAME(m_latch));
save_pointer(NAME(m_mapper),16);
- save_item(NAME(m_9901_int));
}
/*
@@ -968,7 +1001,9 @@ void ti99_4p_state::driver_start()
*/
WRITE_LINE_MEMBER(ti99_4p_state::video_interrupt_in)
{
- set_9901_int(2, (line_state)state);
+ LOGMASKED(LOG_INTERRUPTS, "VDP INT2 from EVPC on tms9901, level=%d\n", state);
+ m_int2 = (line_state)state;
+ m_tms9901->set_int_line(2, state);
}
/*
@@ -976,11 +1011,9 @@ WRITE_LINE_MEMBER(ti99_4p_state::video_interrupt_in)
*/
void ti99_4p_state::driver_reset()
{
- set_9901_int(12, CLEAR_LINE);
-
m_cpu->set_ready(ASSERT_LINE);
m_cpu->set_hold(CLEAR_LINE);
- m_9901_int = 0x03; // INT2* and INT1* set to 1, i.e. inactive
+ m_int1 = m_int2 = CLEAR_LINE;
}
/*
@@ -1000,7 +1033,7 @@ void ti99_4p_state::ti99_4p_60hz(machine_config& config)
// tms9901
TMS9901(config, m_tms9901, 0);
- m_tms9901->read_cb().set(FUNC(ti99_4p_state::read_by_9901));
+ m_tms9901->read_cb().set(FUNC(ti99_4p_state::psi_input));
m_tms9901->p_out_cb(2).set(FUNC(ti99_4p_state::keyC0));
m_tms9901->p_out_cb(3).set(FUNC(ti99_4p_state::keyC1));
m_tms9901->p_out_cb(4).set(FUNC(ti99_4p_state::keyC2));
@@ -1008,7 +1041,7 @@ void ti99_4p_state::ti99_4p_60hz(machine_config& config)
m_tms9901->p_out_cb(6).set(FUNC(ti99_4p_state::cs_motor));
m_tms9901->p_out_cb(8).set(FUNC(ti99_4p_state::audio_gate));
m_tms9901->p_out_cb(9).set(FUNC(ti99_4p_state::cassette_output));
- m_tms9901->intlevel_cb().set(FUNC(ti99_4p_state::tms9901_interrupt));
+ m_tms9901->intreq_cb().set(FUNC(ti99_4p_state::tms9901_interrupt));
// Peripheral expansion box (SGCPU composition)
TI99_PERIBOX_SG(config, m_peribox, 0);
diff --git a/src/mame/drivers/ti99_4x.cpp b/src/mame/drivers/ti99_4x.cpp
index 6a7bf511754..8091947f556 100644
--- a/src/mame/drivers/ti99_4x.cpp
+++ b/src/mame/drivers/ti99_4x.cpp
@@ -65,7 +65,7 @@
#define LOG_CRUREAD (1U<<6)
#define LOG_RESETLOAD (1U<<7)
-#define VERBOSE ( LOG_CONFIG | LOG_WARN | LOG_RESETLOAD )
+#define VERBOSE ( LOG_GENERAL | LOG_CONFIG | LOG_WARN | LOG_RESETLOAD )
#include "logmacro.h"
@@ -145,14 +145,15 @@ private:
DECLARE_WRITE_LINE_MEMBER( handset_interrupt_in );
// Connections with the system interface TMS9901
- uint8_t read_by_9901(offs_t offset);
+ DECLARE_READ8_MEMBER(psi_input_4);
+ DECLARE_READ8_MEMBER(psi_input_4a);
DECLARE_WRITE_LINE_MEMBER(keyC0);
DECLARE_WRITE_LINE_MEMBER(keyC1);
DECLARE_WRITE_LINE_MEMBER(keyC2);
DECLARE_WRITE_LINE_MEMBER(cs1_motor);
DECLARE_WRITE_LINE_MEMBER(audio_gate);
DECLARE_WRITE_LINE_MEMBER(cassette_output);
- void tms9901_interrupt(offs_t offset, uint8_t data);
+ DECLARE_WRITE_LINE_MEMBER(tms9901_interrupt);
DECLARE_WRITE_LINE_MEMBER(handset_ack);
DECLARE_WRITE_LINE_MEMBER(cs2_motor);
DECLARE_WRITE_LINE_MEMBER(alphaW);
@@ -387,13 +388,13 @@ static INPUT_PORTS_START(ti99_4a)
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('-')
PORT_START("ALPHA") /* one more port for Alpha line */
- PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Alpha Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alpha Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE
/* another version of Alpha Lock which is non-toggling; this is useful when we want to attach
a real TI keyboard for input. For home computers, the Alpha Lock / Shift Lock was a physically
locking key. */
PORT_START("ALPHA1")
- PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Alpha Lock non-toggle") PORT_CODE(KEYCODE_RWIN)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Alpha Lock non-toggle") PORT_CODE(KEYCODE_RWIN)
INPUT_PORTS_END
@@ -436,101 +437,208 @@ void ti99_4x_state::external_operation(offs_t offset, uint8_t data)
/***************************************************************************
TI99/4x-specific tms9901 I/O handlers
- See mess/machine/tms9901.c for generic tms9901 CRU handlers.
-
- TMS9901 interrupt handling on a TI99/4(a).
-
- TI99/4(a) uses the following interrupts:
- INT1: external interrupt (used by RS232 controller, for instance)
- INT2: VDP interrupt
- TMS9901 timer interrupt (overrides INT3)
- INT12: handset interrupt (only on a TI-99/4 with the handset prototypes)
-
- Three (occasionally four) interrupts are used by the system (INT1, INT2,
- timer, and INT12 on a TI-99/4 with remote handset prototypes), out of 15/16
- possible interrupts. Keyboard pins can be used as interrupt pins, too, but
- this is not emulated (it's a trick, anyway, and I don't know any program
- which uses it).
-
- When an interrupt line is set (and the corresponding bit in the interrupt mask is set),
- a level 1 interrupt is requested from the TMS9900. This interrupt request lasts as long as
- the interrupt pin and the relevant bit in the interrupt mask are set.
-
+ Bit Meaning
+ 0 - (control) -
+ 1 /INT1 (input) EXTINT
+ 2 /INT2 (input) VDP
+ 3 /INT3 (input) Keyboard = line / Joystick button
+ 4 /INT4 (input) Keyboard Space line / Joystick left
+ 5 /INT5 (input) Keyboard Enter line / Joystick right
+ 6 /INT6 (input) Keyboard 0 line / Joystick down
+ 7 31 /INT7 P15 (input) Keyboard Fctn line / Joystick up / AlphaLock
+
+ 8 30 /INT8 P14 (input) Keyboard Shift line
+ 9 29 /INT9 P13 (input) Keyboard Ctrl line
+ 10 28 /INT10 P12 (input) Keyboard Z line
+ 11 27 /INT11 P11 (input) Cassette audio
+ 12 26 /INT12 P10 (input) 99/4: Handset, 99/4A: 1
+ 13 25 /INT13 P9 (output) Cassette audio (1?)
+ 14 24 /INT14 P8 (output) Audio Gate (1?)
+ 15 23 /INT15 P7 (output) Motor CS2 (1?)
+
+ 16 P0 (output) 99/4: Handset ack (1?)
+ 17 P1 (input) 99/4: Handset (1?)
+ 18 P2 (output) Key col 0 (1?)
+ 19 P3 (output) Key col 1 (1?)
+ 20 P4 (output) Key col 2 (1?)
+ 21 P5 (output) AlphaLock select (1)
+ 22 P6 (output) Motor CS1 (1?)
+
+ enum { INT1, ... INT7_P15, INT8_P14, ..., P5, P6 }
+
+ The hardware bug of the TI-99/4A keyboard: You have to release the
+ AlphaLock key when using joysticks.
+ The AlphaLock key was obviously added to the 99/4 matrix in a quite adhoc
+ way; a separate 9901 line (P5) is used to deliver the 0 level to be routed
+ through the switch. When AlphaLock is depressed, it connects the /INT7
+ line via two 470 ohm resistors to P5. When the AlphaLock key is not
+ scanned, P5 is 1, pulling up the /INT7 line. Moving the joystick lever up
+ should pull it down, but due to the additional resistance in the long
+ cable in the joystick, the sum resistance becomes too high to safely
+ pull down the level, and the 9901 does not sense a 0 on its /INT7 input.
+
+ Alpha
+ P5 -----[470]-----/ +
+ | Joy up
+ /INT7--+--[470]-----+----[xxx]-----/ ---[280]--- 0 (if column=110 or 111)
+ |
+ +---[10k]--- 1
+ pull-up
+
+ The typical fix was to insert a diode at the Alphalock key.
***************************************************************************/
+READ8_MEMBER( ti99_4x_state::psi_input_4)
+{
+ switch (offset)
+ {
+ case tms9901_device::INT1:
+ return (m_int1==CLEAR_LINE)? 1 : 0;
+ case tms9901_device::INT2:
+ return (m_int2==CLEAR_LINE)? 1 : 0;
+ case tms9901_device::INT3:
+ case tms9901_device::INT4:
+ case tms9901_device::INT5:
+ case tms9901_device::INT6:
+ case tms9901_device::INT7_P15:
+ // Keyboard ACTIVE_LOW, Joysticks ACTIVE_LOW
+ if (m_keyboard_column >= 5)
+ return BIT(m_joyport->read_port(), offset-tms9901_device::INT3);
+ else
+ return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
+ case tms9901_device::INT8_P14:
+ case tms9901_device::INT9_P13:
+ case tms9901_device::INT10_P12:
+ if (m_keyboard_column >= 5) // no joystick lines after /INT7
+ return 1;
+ else
+ return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
+ case tms9901_device::INT11_P11:
+ return (m_cassette1->input() > 0);
+ case tms9901_device::INT12_P10:
+ return (m_int12==CLEAR_LINE)? 1 : 0;
+ case tms9901_device::P1:
+ return BIT(m_joyport->read_port(), 5); // 0x20
+ default:
+ return 1;
+ }
+}
-uint8_t ti99_4x_state::read_by_9901(offs_t offset)
+READ8_MEMBER( ti99_4x_state::psi_input_4a )
{
- int answer=0;
+ int alphabias=0;
- switch (offset & 0x03)
+ switch (offset)
{
- case tms9901_device::CB_INT7:
- //
- // Read pins INT3*-INT7* of TI99's 9901.
- // bit 1: INT1 status
- // bit 2: INT2 status
- // bit 3-7: keyboard status bits 0 to 4
- //
- // |K|K|K|K|K|I2|I1|C|
- //
- if (m_keyboard_column >= (m_model==MODEL_4? 5:6)) // joy 1, 2, handset
- {
- answer = m_joyport->read_port();
- // The hardware bug of the TI-99/4A: you have to release the
- // Alphalock key when using joysticks. This is a maldesign of the
- // board: When none of the other keyboard lines are selected the
- // depressed Alphalock key pulls up the /INT7 line which is also
- // used for joystick up. The joystick switch then fails to lower
- // the line enough to make the TMS9901 sense the low level.
- // A reported, feasible fix was to cut the line and insert a diode
- // below the Alphalock key.
- if ((m_model!=MODEL_4) && (m_alphabug->read()!=0) ) answer |= (m_alpha->read() | m_alpha1->read());
- }
+ case tms9901_device::INT1:
+ return (m_int1==CLEAR_LINE)? 1 : 0;
+ case tms9901_device::INT2:
+ return (m_int2==CLEAR_LINE)? 1 : 0;
+ case tms9901_device::INT3:
+ case tms9901_device::INT4:
+ case tms9901_device::INT5:
+ case tms9901_device::INT6:
+ // Keyboard ACTIVE_LOW (s.o.)
+ // Joysticks ACTIVE_LOW (handset.cpp)
+ if (m_keyboard_column >= 6)
+ return BIT(m_joyport->read_port(), offset-tms9901_device::INT3);
else
+ return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
+
+ case tms9901_device::INT7_P15:
+ if (m_keyboard_column >= 6) // Joysticks
{
- answer = m_keyboard[m_keyboard_column]->read();
- }
- if (m_check_alphalock) // never true for TI-99/4
- {
- answer &= ~(m_alpha->read() | m_alpha1->read());
+ // If the Alpha Lock bug is not fixed
+ if (m_alphabug->read()!=0)
+ alphabias = ~(m_alpha->read() & m_alpha1->read());
+
+ return BIT(m_joyport->read_port() | alphabias, offset-tms9901_device::INT3);
}
- answer = (answer << 3);
- if (m_int1 == CLEAR_LINE) answer |= 0x02;
- if (m_int2 == CLEAR_LINE) answer |= 0x04;
-
- break;
-
- case tms9901_device::INT8_INT15:
- // |1|1|1|INT12|0|K|K|K|
- if (m_keyboard_column >= (m_model==MODEL_4? 5:6)) answer = 0x07;
- else answer = ((m_keyboard[m_keyboard_column]->read())>>5) & 0x07;
- answer |= 0xe0;
- if (m_model != MODEL_4 || m_int12==CLEAR_LINE) answer |= 0x10;
- break;
-
- case tms9901_device::P0_P7:
- // Required for the handset (only on TI-99/4)
- if ((m_joyport->read_port() & 0x20)!=0) answer |= 2;
- break;
-
- case tms9901_device::P8_P15:
- // Preset to 1
- answer = 4;
-
- // Interrupt pin of the handset (only on TI-99/4)
- // Negative logic (interrupt pulls line down)
- if ((m_joyport->read_port() & 0x40)==0) answer = 0;
-
- // we don't take CS2 into account, as CS2 is a write-only unit
- if (m_cassette1->input() > 0)
+ else
{
- answer |= 8;
+ if (m_check_alphalock)
+ {
+ return BIT(m_alpha->read() & m_alpha1->read(), offset-tms9901_device::INT3);
+ }
+ else
+ return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
}
- break;
+
+ case tms9901_device::INT8_P14:
+ case tms9901_device::INT9_P13:
+ case tms9901_device::INT10_P12:
+ if (m_keyboard_column >= 6) // no joystick lines after /INT7
+ return 1;
+ else
+ return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT3);
+ case tms9901_device::INT11_P11:
+ // CS2 is write-only
+ return (m_cassette1->input()>0);
+ default:
+ return 1;
}
- return answer;
}
+/* switch (offset & 0x03)
+ {
+ case tms9901_device::CB_INT7:
+ //
+ // Read pins INT3*-INT7* of TI99's 9901.
+ // bit 1: INT1 status
+ // bit 2: INT2 status
+ // bit 3-7: keyboard status bits 0 to 4
+ //
+ // |K|K|K|K|K|I2|I1|C|
+ //
+ if (m_keyboard_column >= (m_model==MODEL_4? 5:6)) // joy 1, 2, handset
+ {
+ answer = m_joyport->read_port();
+
+ if ((m_model!=MODEL_4) && (m_alphabug->read()!=0) ) answer |= (m_alpha->read() | m_alpha1->read());
+ }
+ else
+ {
+ answer = m_keyboard[m_keyboard_column]->read();
+ }
+ if (m_check_alphalock) // never true for TI-99/4
+ {
+ answer &= ~(m_alpha->read() | m_alpha1->read());
+ }
+ answer = (answer << 3);
+ if (m_int1 == CLEAR_LINE) answer |= 0x02;
+ if (m_int2 == CLEAR_LINE) answer |= 0x04;
+
+ break;
+
+ case tms9901_device::INT8_INT15:
+ // |1|1|1|INT12|0|K|K|K|
+ if (m_keyboard_column >= (m_model==MODEL_4? 5:6)) answer = 0x07;
+ else answer = ((m_keyboard[m_keyboard_column]->read())>>5) & 0x07;
+ answer |= 0xe0;
+ if (m_model != MODEL_4 || m_int12==CLEAR_LINE) answer |= 0x10;
+ break;
+
+ case tms9901_device::P0_P7:
+ // Required for the handset (only on TI-99/4)
+ if ((m_joyport->read_port() & 0x20)!=0) answer |= 2;
+ break;
+
+ case tms9901_device::P8_P15:
+ // Preset to 1
+ answer = 4;
+
+ // Interrupt pin of the handset (only on TI-99/4)
+ // Negative logic (interrupt pulls line down)
+ if ((m_joyport->read_port() & 0x40)==0) answer = 0;
+
+ // we don't take CS2 into account, as CS2 is a write-only unit
+ if (m_cassette1->input() > 0)
+ {
+ answer |= 8;
+ }
+ break;
+ }
+ */
+
/*
Handler for TMS9901 P0 pin (handset data acknowledge); only for 99/4
@@ -628,17 +736,15 @@ WRITE_LINE_MEMBER( ti99_4x_state::cassette_output )
m_cassette2->output(state==ASSERT_LINE? +1 : -1);
}
-void ti99_4x_state::tms9901_interrupt(offs_t offset, uint8_t data)
+WRITE_LINE_MEMBER( ti99_4x_state::tms9901_interrupt )
{
- // offset contains the interrupt level (0-15)
- // However, the TI board just ignores that level and hardwires it to 1
- // See below (interrupt_level)
- m_cpu->set_input_line(INT_9900_INTREQ, data);
+ m_cpu->set_input_line(INT_9900_INTREQ, state);
}
uint8_t ti99_4x_state::interrupt_level()
{
- // On the TI-99 systems these IC lines are not used; the input lines
+ // The interrupt level must be fetched from the 9901;
+ // on the TI-99 systems these IC lines are not used; the input lines
// at the CPU are hardwired to level 1.
return 1;
}
@@ -680,7 +786,7 @@ WRITE_LINE_MEMBER( ti99_4x_state::video_interrupt_evpc_in )
{
LOGMASKED(LOG_INTERRUPTS, "VDP INT2 from EVPC on tms9901, level=%d\n", state);
m_int2 = (line_state)state;
- m_tms9901->set_single_int(2, state);
+ m_tms9901->set_int_line(2, state);
}
/*
@@ -688,13 +794,11 @@ WRITE_LINE_MEMBER( ti99_4x_state::video_interrupt_evpc_in )
*/
WRITE_LINE_MEMBER( ti99_4x_state::video_interrupt_in )
{
- LOGMASKED(LOG_INTERRUPTS, "VDP INT2 on tms9901, level=%d\n", state);
-
+ LOGMASKED(LOG_INTERRUPTS, "VDP %s /INT2 on TMS9901\n", (state==ASSERT_LINE)? "asserts" : "clears");
+ m_int2 = (line_state)state;
+ m_tms9901->set_int_line(2, state);
// Pulse for the handset
if (m_model == MODEL_4) m_joyport->pulse_clock();
-
- m_int2 = (line_state)state;
- m_tms9901->set_single_int(2, state);
}
/*
@@ -704,7 +808,7 @@ WRITE_LINE_MEMBER( ti99_4x_state::handset_interrupt_in)
{
LOGMASKED(LOG_INTERRUPTS, "joyport INT12 on tms9901, level=%d\n", state);
m_int12 = (line_state)state;
- m_tms9901->set_single_int(12, state);
+ m_tms9901->set_int_line(12, state);
}
/*
@@ -788,7 +892,7 @@ WRITE_LINE_MEMBER( ti99_4x_state::extint )
{
LOGMASKED(LOG_INTERRUPTS, "EXTINT level = %02x\n", state);
m_int1 = (line_state)state;
- m_tms9901->set_single_int(1, state);
+ m_tms9901->set_int_line(1, state);
}
WRITE_LINE_MEMBER( ti99_4x_state::notconnected )
@@ -845,7 +949,6 @@ void ti99_4x_state::ti99_4_common(machine_config& config)
// 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));
m_tms9901->p_out_cb(4).set(FUNC(ti99_4x_state::keyC2));
@@ -853,7 +956,7 @@ void ti99_4x_state::ti99_4_common(machine_config& config)
m_tms9901->p_out_cb(7).set(FUNC(ti99_4x_state::cs2_motor));
m_tms9901->p_out_cb(8).set(FUNC(ti99_4x_state::audio_gate));
m_tms9901->p_out_cb(9).set(FUNC(ti99_4x_state::cassette_output));
- m_tms9901->intlevel_cb().set(FUNC(ti99_4x_state::tms9901_interrupt));
+ m_tms9901->intreq_cb().set(FUNC(ti99_4x_state::tms9901_interrupt));
// Databus multiplexer
TI99_DATAMUX(config, m_datamux, 0).ready_cb().set(FUNC(ti99_4x_state::console_ready_dmux));
@@ -896,6 +999,7 @@ void ti99_4x_state::ti99_4(machine_config& config)
// Main board
// Add handset interrupt to 9901
m_tms9901->p_out_cb(0).set(FUNC(ti99_4x_state::handset_ack));
+ m_tms9901->read_cb().set(FUNC(ti99_4x_state::psi_input_4)); // use a separate one for 99/4
// Input/output port: normal config
TI99_IOPORT(config, m_ioport, 0, ti99_ioport_options_plain, nullptr);
@@ -956,6 +1060,7 @@ void ti99_4x_state::ti99_4a(machine_config& config)
// Main board
// Add Alphalock to 9901
m_tms9901->p_out_cb(5).set(FUNC(ti99_4x_state::alphaW));
+ m_tms9901->read_cb().set(FUNC(ti99_4x_state::psi_input_4a));
// Input/output port: Normal config
TI99_IOPORT(config, m_ioport, 0, ti99_ioport_options_plain, nullptr);
@@ -1046,6 +1151,7 @@ void ti99_4x_state::ti99_4ev_60hz(machine_config& config)
// Main board
// Add Alphalock
m_tms9901->p_out_cb(5).set(FUNC(ti99_4x_state::alphaW));
+ m_tms9901->read_cb().set(FUNC(ti99_4x_state::psi_input_4a));
// EVPC connector
// This is needed for delivering the video interrupt from the
diff --git a/src/mame/drivers/ti99_8.cpp b/src/mame/drivers/ti99_8.cpp
index ce6cacc6b39..083e5c924b2 100644
--- a/src/mame/drivers/ti99_8.cpp
+++ b/src/mame/drivers/ti99_8.cpp
@@ -133,31 +133,22 @@
November 2013: Included new dumps [Michael Zapf]
===========================================================================
-Known Issues (MZ, 2010-11-07)
+Known Issues (MZ, 2019-05-10)
KEEP IN MIND THAT TEXAS INSTRUMENTS NEVER RELEASED THE TI-99/8 AND THAT
THERE ARE ONLY A FEW PROTOTYPES OF THE TI-99/8 AVAILABLE. ALL SOFTWARE
MUST BE ASSUMED TO HAVE REMAINED IN A PRELIMINARY STATE.
-- Extended Basic II does not start when a floppy controller is present. This is
- a problem of the prototypical XB II which we cannot solve. It seems as if only
- hexbus devices are properly supported, but we currently do not have an
- emulation for those. Thus you can currently only use cassette to load and
- save programs. You MUST not plug in any floppy controller when you intend to
- start XB II. Other cartridges (like Editor/Assembler)
- seem to be unaffected by this problem and can make use of the floppy
- controllers.
- Technical detail: The designers of XB II seem to have decided to put PABs
- (Peripheral access block; contains pointers to buffers, the file name, and
- the access modes) into CPU RAM instead of the traditional storage in VDP
- RAM. The existing peripheral cards are hard-coded to interpret the given
- pointer to the PAB as pointing to a VDP RAM address. That is, as soon as
- the card is found, control is passed to the DSR (device service routine),
- the file name will not be found, and control returns with an error. It seems
- as if XB II does not properly handle this situation and may lock up
- (sometimes it starts up, but file access is still not possible).
-
- TODO: Emulate a Hexbus floppy.
+- TI-99/4A disk controllers cannot be used with the TI-99/8 in Extended Basic II.
+ In the 99/8, the peripheral access block (PAB, set of data defining the
+ access to the device, like floppy) may be located in CPU RAM, while the
+ controllers of the 99/4A expect the PAB to be in video RAM only. Exbasic II
+ sets up the PAB in CPU RAM, which leads to a crash. Other cartridges from
+ the 99/4A certainly use video RAM, and so the disk controller works.
+ Therefore, the Hexbus floppy drive HX5102 is recommended for use with the
+ TI-99/8. You do not even need to attach the Peripheral Box.
+
+ mame ti99_8 -hexbus hx5102 -flop1 somedisk.dsk
- Multiple cartridges are not shown in the startup screen; only one
cartridge is presented. You have to manually select the cartridges with the
@@ -266,7 +257,7 @@ private:
DECLARE_WRITE_LINE_MEMBER( video_interrupt );
// Connections with the system interface TMS9901
- uint8_t read_by_9901(offs_t offset);
+ uint8_t psi_input(offs_t offset);
DECLARE_WRITE_LINE_MEMBER(keyC0);
DECLARE_WRITE_LINE_MEMBER(keyC1);
DECLARE_WRITE_LINE_MEMBER(keyC2);
@@ -464,75 +455,44 @@ void ti99_8_state::cruwrite(offs_t offset, uint8_t data)
keyboard column selection.)
***************************************************************************/
-uint8_t ti99_8_state::read_by_9901(offs_t offset)
+uint8_t ti99_8_state::psi_input(offs_t offset)
{
- int answer=0;
- uint8_t joyst;
- switch (offset & 0x03)
+ switch (offset)
{
- case tms9901_device::CB_INT7:
- // Read pins INT3*-INT7* of TI99's 9901.
- //
- // bit 1: INT1 status
- // bit 2: INT2 status
- // bits 3-4: unused?
- // bit 5: ???
- // bit 6-7: keyboard status bits 0 through 1
-
- // |K|K|-|-|-|I2|I1|C|
+ case tms9901_device::INT1:
+ return (m_int1==CLEAR_LINE)? 1 : 0;
+ case tms9901_device::INT2:
+ return (m_int2==CLEAR_LINE)? 1 : 0;
+
+ case tms9901_device::INT6:
+ if (m_keyboard_column >= 14)
+ return BIT(m_joyport->read_port(),0);
+
+ case tms9901_device::INT7_P15:
+ if (m_keyboard_column >= 14)
+ return BIT(m_joyport->read_port(),4);
+
+ case tms9901_device::INT8_P14:
if (m_keyboard_column >= 14)
- {
- // TI-99/8's wiring differs from the TI-99/4A
- joyst = m_joyport->read_port();
- answer = (joyst & 0x01) | ((joyst & 0x10)>>3);
- }
- else
- {
- answer = m_keyboard[m_keyboard_column]->read();
- }
- answer = (answer << 6);
- if (m_int1 == CLEAR_LINE) answer |= 0x02;
- if (m_int2 == CLEAR_LINE) answer |= 0x04;
-
- break;
-
- case tms9901_device::INT8_INT15:
- // Read pins int8_t*-INT15* of TI99's 9901.
- //
- // bit 0-2: keyboard status bits 2 to 4
- // bit 3: tape input mirror
- // bit 4: unused
- // bit 5-7: weird, not emulated
-
- // |0|0|0|0|0|K|K|K|
+ return BIT(m_joyport->read_port(),1);
+ case tms9901_device::INT9_P13:
if (m_keyboard_column >= 14)
- {
- joyst = m_joyport->read_port();
- answer = joyst << 1;
- }
- else
- {
- answer = m_keyboard[m_keyboard_column]->read();
- }
- answer = (answer >> 2) & 0x07;
- break;
-
- case tms9901_device::P0_P7:
- // Read pins P0-P7 of TI99's 9901. None here.
- break;
-
- case tms9901_device::P8_P15:
- // Read pins P8-P15 of TI99's 9901. (TI-99/8)
- //
- // bit 26: high
- // bit 27: tape input
- answer = 4;
- if (m_cassette->input() > 0)
- answer |= 8;
- break;
+ return BIT(m_joyport->read_port(),2);
+
+ case tms9901_device::INT10_P12:
+ if (m_keyboard_column >= 14)
+ return BIT(m_joyport->read_port(),3);
+
+ // return for last 5 cases if column<14
+ return BIT(m_keyboard[m_keyboard_column]->read(), offset-tms9901_device::INT6);
+
+ case tms9901_device::INT11_P11:
+ return (m_cassette->input() > 0);
+
+ default:
+ return 1;
}
- return answer;
}
/*
@@ -613,7 +573,7 @@ WRITE_LINE_MEMBER( ti99_8_state::video_interrupt )
{
LOGMASKED(LOG_INTERRUPTS, "VDP int 2 on tms9901, level=%02x\n", state);
m_int2 = (line_state)state;
- m_tms9901->set_single_int(2, state);
+ m_tms9901->set_int_line(2, state);
}
/***********************************************************
@@ -665,7 +625,7 @@ WRITE_LINE_MEMBER( ti99_8_state::extint )
{
LOGMASKED(LOG_INTERRUPTS, "EXTINT level = %02x\n", state);
m_int1 = (line_state)state;
- m_tms9901->set_single_int(1, state);
+ m_tms9901->set_int_line(1, state);
}
WRITE_LINE_MEMBER( ti99_8_state::notconnected )
@@ -735,7 +695,7 @@ void ti99_8_state::ti99_8(machine_config& config)
// 9901 configuration
TMS9901(config, m_tms9901, 0);
- m_tms9901->read_cb().set(FUNC(ti99_8_state::read_by_9901));
+ m_tms9901->read_cb().set(FUNC(ti99_8_state::psi_input));
m_tms9901->p_out_cb(0).set(FUNC(ti99_8_state::keyC0));
m_tms9901->p_out_cb(1).set(FUNC(ti99_8_state::keyC1));
m_tms9901->p_out_cb(2).set(FUNC(ti99_8_state::keyC2));
@@ -745,7 +705,7 @@ void ti99_8_state::ti99_8(machine_config& config)
m_tms9901->p_out_cb(6).set(FUNC(ti99_8_state::cassette_motor));
m_tms9901->p_out_cb(8).set(FUNC(ti99_8_state::audio_gate));
m_tms9901->p_out_cb(9).set(FUNC(ti99_8_state::cassette_output));
- m_tms9901->intlevel_cb().set(FUNC(ti99_8_state::tms9901_interrupt));
+ m_tms9901->intreq_cb().set(FUNC(ti99_8_state::tms9901_interrupt));
// Mainboard with custom chips
TI99_MAINBOARD8(config, m_mainboard, 0);
diff --git a/src/mame/drivers/tm990189.cpp b/src/mame/drivers/tm990189.cpp
index 154f456b9cb..4aa4acbb96e 100644
--- a/src/mame/drivers/tm990189.cpp
+++ b/src/mame/drivers/tm990189.cpp
@@ -112,9 +112,9 @@ private:
DECLARE_WRITE_LINE_MEMBER(usr9901_led1_w);
DECLARE_WRITE_LINE_MEMBER(usr9901_led2_w);
DECLARE_WRITE_LINE_MEMBER(usr9901_led3_w);
- DECLARE_WRITE8_MEMBER(usr9901_interrupt_callback);
+ DECLARE_WRITE_LINE_MEMBER(usr9901_interrupt_callback);
- DECLARE_WRITE8_MEMBER(sys9901_interrupt_callback);
+ DECLARE_WRITE_LINE_MEMBER(sys9901_interrupt_callback);
DECLARE_READ8_MEMBER(sys9901_r);
DECLARE_WRITE_LINE_MEMBER(sys9901_digitsel0_w);
DECLARE_WRITE_LINE_MEMBER(sys9901_digitsel1_w);
@@ -282,13 +282,13 @@ TIMER_DEVICE_CALLBACK_MEMBER(tm990189_state::display_callback)
tms9901 code
*/
-WRITE8_MEMBER( tm990189_state::usr9901_interrupt_callback )
+WRITE_LINE_MEMBER( tm990189_state::usr9901_interrupt_callback )
{
// Triggered by internal timer (set by ROM to 1.6 ms cycle) on level 3
// or by keyboard interrupt (level 6)
if (!m_load_state)
{
- m_tms9980a->set_input_line(offset & 7, ASSERT_LINE);
+ m_tms9980a->set_input_line(m_tms9901_usr->get_int_level() & 7, ASSERT_LINE);
}
}
@@ -320,31 +320,51 @@ WRITE_LINE_MEMBER( tm990189_state::usr9901_led3_w )
led_set(3, state);
}
-WRITE8_MEMBER( tm990189_state::sys9901_interrupt_callback )
+WRITE_LINE_MEMBER( tm990189_state::sys9901_interrupt_callback )
{
// TODO: Check this
- m_tms9901_usr->set_single_int(5, (data!=0)? ASSERT_LINE:CLEAR_LINE);
+ m_tms9901_usr->set_int_line(5, state);
}
READ8_MEMBER( tm990189_state::sys9901_r )
{
- uint8_t data = 0;
- if (offset == tms9901_device::CB_INT7)
- {
- static const char *const keynames[] = { "LINE0", "LINE1", "LINE2", "LINE3", "LINE4", "LINE5", "LINE6", "LINE7", "LINE8" };
-
- /* keyboard read */
- if (m_digitsel < 9)
- data |= ioport(keynames[m_digitsel])->read() << 1;
+ // |-|Cass|K|K|K|K|K|C|
+ static const char *const keynames[] = { "LINE0", "LINE1", "LINE2", "LINE3", "LINE4", "LINE5", "LINE6", "LINE7", "LINE8" };
- /* tape input */
- if (m_cass->input() > 0.0)
- data |= 0x40;
+ offset &= 0x0F;
+ switch (offset)
+ {
+ case tms9901_device::INT1:
+ case tms9901_device::INT2:
+ case tms9901_device::INT3:
+ case tms9901_device::INT4:
+ case tms9901_device::INT5:
+ if (m_digitsel < 9)
+ return BIT(ioport(keynames[m_digitsel])->read(), offset-tms9901_device::INT1);
+ else return 0;
+
+ case tms9901_device::INT6:
+ return (m_cass->input() > 0);
+ default:
+ return 0;
}
-
- return data;
}
+/*
+ uint8_t data = 0;
+ if (offset == tms9901_device::CB_INT7)
+ {
+ static const char *const keynames[] = { "LINE0", "LINE1", "LINE2", "LINE3", "LINE4", "LINE5", "LINE6", "LINE7", "LINE8" };
+ // keyboard read
+ if (m_digitsel < 9)
+ data |= ioport(keynames[m_digitsel])->read() << 1;
+ // tape input
+ if (m_cass->input() > 0.0)
+ data |= 0x40;
+ }
+ return data;
+*/
+
void tm990189_state::digitsel(int offset, bool state)
{
if (state)
@@ -843,7 +863,7 @@ void tm990189_state::tm990_189(machine_config &config)
m_tms9901_usr->p_out_cb(1).set(FUNC(tm990189_state::usr9901_led1_w));
m_tms9901_usr->p_out_cb(2).set(FUNC(tm990189_state::usr9901_led2_w));
m_tms9901_usr->p_out_cb(3).set(FUNC(tm990189_state::usr9901_led3_w));
- m_tms9901_usr->intlevel_cb().set(FUNC(tm990189_state::usr9901_interrupt_callback));
+ m_tms9901_usr->intreq_cb().set(FUNC(tm990189_state::usr9901_interrupt_callback));
TMS9901(config, m_tms9901_sys, 8_MHz_XTAL / 4);
m_tms9901_sys->read_cb().set(FUNC(tm990189_state::sys9901_r));
@@ -863,7 +883,7 @@ void tm990189_state::tm990_189(machine_config &config)
m_tms9901_sys->p_out_cb(13).set(FUNC(tm990189_state::sys9901_shiftlight_w));
m_tms9901_sys->p_out_cb(14).set(FUNC(tm990189_state::sys9901_spkrdrive_w));
m_tms9901_sys->p_out_cb(15).set(FUNC(tm990189_state::sys9901_tapewdata_w));
- m_tms9901_sys->intlevel_cb().set(FUNC(tm990189_state::sys9901_interrupt_callback));
+ m_tms9901_sys->intreq_cb().set(FUNC(tm990189_state::sys9901_interrupt_callback));
TMS9902(config, m_tms9902, 8_MHz_XTAL / 4);
m_tms9902->xmit_cb().set(FUNC(tm990189_state::xmit_callback)); // called when a character is transmitted
@@ -907,7 +927,7 @@ void tm990189_state::tm990_189_v(machine_config &config)
m_tms9901_usr->p_out_cb(1).set(FUNC(tm990189_state::usr9901_led1_w));
m_tms9901_usr->p_out_cb(2).set(FUNC(tm990189_state::usr9901_led2_w));
m_tms9901_usr->p_out_cb(3).set(FUNC(tm990189_state::usr9901_led3_w));
- m_tms9901_usr->intlevel_cb().set(FUNC(tm990189_state::usr9901_interrupt_callback));
+ m_tms9901_usr->intreq_cb().set(FUNC(tm990189_state::usr9901_interrupt_callback));
TMS9901(config, m_tms9901_sys, 8_MHz_XTAL / 4);
m_tms9901_sys->read_cb().set(FUNC(tm990189_state::sys9901_r));
@@ -927,7 +947,7 @@ void tm990189_state::tm990_189_v(machine_config &config)
m_tms9901_sys->p_out_cb(13).set(FUNC(tm990189_state::sys9901_shiftlight_w));
m_tms9901_sys->p_out_cb(14).set(FUNC(tm990189_state::sys9901_spkrdrive_w));
m_tms9901_sys->p_out_cb(15).set(FUNC(tm990189_state::sys9901_tapewdata_w));
- m_tms9901_sys->intlevel_cb().set(FUNC(tm990189_state::sys9901_interrupt_callback));
+ m_tms9901_sys->intreq_cb().set(FUNC(tm990189_state::sys9901_interrupt_callback));
TMS9902(config, m_tms9902, 8_MHz_XTAL / 4);
m_tms9902->xmit_cb().set(FUNC(tm990189_state::xmit_callback)); // called when a character is transmitted;