to8, to9, to9p: Split out serial keyboards as separate devices

This commit is contained in:
AJR 2024-10-03 15:17:52 -04:00
parent d7211e631d
commit 366d6fea79
5 changed files with 1155 additions and 940 deletions

View File

@ -460,10 +460,6 @@ static INPUT_PORTS_START ( to7_keyboard )
KEY ( 6, "7 ' \302\264", 7 ) PORT_CHAR('7') PORT_CHAR('\'')
KEY ( 7, "6 &", 6 ) PORT_CHAR('6') PORT_CHAR('&')
/* unused */
PORT_START ( "keyboard.8" )
PORT_START ( "keyboard.9" )
INPUT_PORTS_END
static INPUT_PORTS_START ( to7 )
@ -1062,7 +1058,7 @@ void to9_state::to9_map(address_map &map)
map(0xe7c8, 0xe7cb).rw("pia_0", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
map(0xe7cc, 0xe7cf).rw("pia_1", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
map(0xe7da, 0xe7dd).rw(FUNC(to9_state::to9_vreg_r), FUNC(to9_state::to9_vreg_w));
map(0xe7de, 0xe7df).rw(FUNC(to9_state::to9_kbd_r), FUNC(to9_state::to9_kbd_w));
map(0xe7de, 0xe7df).rw(m_to9_kbd, FUNC(to9_keyboard_device::kbd_acia_r), FUNC(to9_keyboard_device::kbd_acia_w));
map(0xe7e4, 0xe7e7).rw(FUNC(to9_state::to9_gatearray_r), FUNC(to9_state::to9_gatearray_w));
/* map(0xe7f0, 0xe7f7).rw(FUNC(to9_state::to9_ieee_r), FUNC(to9_state::to9_ieee_w )); */
map(0xe800, 0xffff).rom(); /* system bios */
@ -1127,105 +1123,23 @@ ROM_END
/* ------------ inputs ------------ */
static INPUT_PORTS_START ( to9_keyboard )
PORT_START ( "keyboard.0" )
KEY ( 0, "F2 F7", F2 ) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F7))
KEY ( 1, "_ 6", 6 ) PORT_CHAR('_') PORT_CHAR('6')
KEY ( 2, "Y", Y ) PORT_CHAR('Y')
KEY ( 3, "H \302\250", H ) PORT_CHAR('H')
KEY ( 4, UTF8_UP, UP ) PORT_CHAR(UCHAR_MAMEKEY(UP))
KEY ( 5, UTF8_RIGHT, RIGHT ) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
KEY ( 6, "Home Clear", HOME ) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CHAR(UCHAR_MAMEKEY(ESC))
KEY ( 7, "N", N ) PORT_CHAR('N')
PORT_START ( "keyboard.1" )
KEY ( 0, "F3 F8", F3 ) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F8))
KEY ( 1, "( 5", 5 ) PORT_CHAR('(') PORT_CHAR('5')
KEY ( 2, "T", T ) PORT_CHAR('T')
KEY ( 3, "G", G ) PORT_CHAR('G')
KEY ( 4, "= +", EQUALS ) PORT_CHAR('=') PORT_CHAR('+')
KEY ( 5, UTF8_LEFT, LEFT ) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
KEY ( 6, "Insert", INSERT ) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
KEY ( 7, "B \302\264", B ) PORT_CHAR('B')
PORT_START ( "keyboard.2" )
KEY ( 0, "F4 F9", F4 ) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR(UCHAR_MAMEKEY(F9))
KEY ( 1, "' 4", 4 ) PORT_CHAR('\'') PORT_CHAR('4')
KEY ( 2, "R", R ) PORT_CHAR('R')
KEY ( 3, "F", F ) PORT_CHAR('F')
KEY ( 4, "Accent", END ) PORT_CHAR(UCHAR_MAMEKEY(END))
KEY ( 5, "Keypad 1", 1_PAD ) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
KEY ( 6, "Delete Backspace", DEL ) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE))
KEY ( 7, "V", V ) PORT_CHAR('V')
PORT_START ( "keyboard.3" )
KEY ( 0, "F5 F10", F5 ) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F10))
KEY ( 1, "\" 3", 3 ) PORT_CHAR('"') PORT_CHAR('3')
KEY ( 2, "E", E ) PORT_CHAR('E')
KEY ( 3, "D", D ) PORT_CHAR('D')
KEY ( 4, "Keypad 7", 7_PAD ) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
KEY ( 5, "Keypad 4", 4_PAD ) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
KEY ( 6, "Keypad 0", 0_PAD ) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
KEY ( 7, "C \136", C ) PORT_CHAR('C')
PORT_START ( "keyboard.4" )
KEY ( 0, "F1 F6", F1 ) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F6))
KEY ( 1, "\303\251 2", 2 ) PORT_CHAR( 0xe9 ) PORT_CHAR('2')
KEY ( 2, "Z", Z ) PORT_CHAR('Z')
KEY ( 3, "S", S ) PORT_CHAR('S')
KEY ( 4, "Keypad 8", 8_PAD ) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
KEY ( 5, "Keypad 2", 2_PAD ) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
KEY ( 6, "Keypad .", DEL_PAD ) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
KEY ( 7, "X", X ) PORT_CHAR('X')
PORT_START ( "keyboard.5" )
KEY ( 0, "# @", TILDE ) PORT_CHAR('#') PORT_CHAR('@')
KEY ( 1, "* 1", 1 ) PORT_CHAR('*') PORT_CHAR('1')
KEY ( 2, "A \140", A ) PORT_CHAR('A')
KEY ( 3, "Q", Q ) PORT_CHAR('Q')
KEY ( 4, "[ {", QUOTE ) PORT_CHAR('[') PORT_CHAR('{')
KEY ( 5, "Keypad 5", 5_PAD ) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
KEY ( 6, "Keypad 6", 6_PAD ) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
KEY ( 7, "W", W ) PORT_CHAR('W')
PORT_START ( "keyboard.6" )
KEY ( 0, "Stop", TAB ) PORT_CHAR(27)
KEY ( 1, "\303\250 7", 7 ) PORT_CHAR( 0xe8 ) PORT_CHAR('7')
KEY ( 2, "U", U ) PORT_CHAR('U')
KEY ( 3, "J", J ) PORT_CHAR('J')
KEY ( 4, "Space", SPACE ) PORT_CHAR(' ')
KEY ( 5, "Keypad 9", 9_PAD ) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
KEY ( 6, "Keypad Enter", ENTER_PAD ) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
KEY ( 7, ", ?", COMMA ) PORT_CHAR(',') PORT_CHAR('?')
PORT_START ( "keyboard.7" )
KEY ( 0, "Control", LCONTROL ) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
KEY ( 1, "! 8", 8 ) PORT_CHAR('!') PORT_CHAR('8')
KEY ( 2, "I", I ) PORT_CHAR('I')
KEY ( 3, "K", K ) PORT_CHAR('K')
KEY ( 4, "$ &", CLOSEBRACE ) PORT_CHAR('$') PORT_CHAR('&')
KEY ( 5, UTF8_DOWN, DOWN ) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
KEY ( 6, "] }", BACKSLASH ) PORT_CHAR(']') PORT_CHAR('}')
KEY ( 7, "; .", STOP ) PORT_CHAR(';') PORT_CHAR('.')
PORT_START ( "keyboard.8" )
KEY ( 0, "Caps-Lock", CAPSLOCK ) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
KEY ( 1, "\303\247 9", 9 ) PORT_CHAR( 0xe7 ) PORT_CHAR('9')
KEY ( 2, "O", O ) PORT_CHAR('O')
KEY ( 3, "L", L ) PORT_CHAR('L')
KEY ( 4, "- \\", BACKSPACE ) PORT_CHAR('-') PORT_CHAR('\\')
KEY ( 5, "\303\271 %", COLON ) PORT_CHAR( 0xf9 ) PORT_CHAR('%')
KEY ( 6, "Enter", ENTER ) PORT_CHAR(13)
KEY ( 7, ": /", SLASH ) PORT_CHAR(':') PORT_CHAR('/')
PORT_START ( "keyboard.9" )
KEY ( 0, "Shift", LSHIFT ) PORT_CODE ( KEYCODE_RSHIFT ) PORT_CHAR(UCHAR_SHIFT_1)
KEY ( 1, "\303\240 0", 0 ) PORT_CHAR( 0xe0 ) PORT_CHAR('0')
KEY ( 2, "P", P ) PORT_CHAR('P')
KEY ( 3, "M", M ) PORT_CHAR('M')
KEY ( 4, ") \302\260", MINUS ) PORT_CHAR(')') PORT_CHAR( 0xb0 )
KEY ( 5, "\342\206\221 \302\250", OPENBRACE ) PORT_CHAR('^') PORT_CHAR( 0xa8 )
KEY ( 6, "Keypad 3", 3_PAD ) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
KEY ( 7, "> <", BACKSLASH2 ) PORT_CHAR('>') PORT_CHAR('<')
INPUT_PORTS_END
static INPUT_PORTS_START ( to9 )
PORT_INCLUDE ( thom_lightpen )
PORT_INCLUDE ( thom_game_port )
PORT_INCLUDE ( to9_keyboard )
PORT_INCLUDE ( to7_config )
PORT_INCLUDE ( to7_vconfig )
PORT_START ( "config" )
PORT_BIT ( 0x01, 0x00, IPT_UNUSED )
PORT_MODIFY ( "mouse_x" )
PORT_BIT ( 0xffff, 0x00, IPT_UNUSED )
PORT_MODIFY ( "mouse_y" )
PORT_BIT ( 0xffff, 0x00, IPT_UNUSED )
PORT_MODIFY ( "mouse_button" )
PORT_BIT ( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
PORT_BIT ( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END
/* ------------ driver ------------ */
@ -1238,6 +1152,9 @@ void to9_state::to9(machine_config &config)
m_maincpu->set_addrmap(AS_PROGRAM, &to9_state::to9_map);
TO9_KEYBOARD(config, m_to9_kbd);
m_to9_kbd->irq_cb().set(m_mainirq, FUNC(input_merger_device::in_w<4>));
m_pia_sys->readpa_handler().set(FUNC(to9_state::to9_sys_porta_in));
m_pia_sys->readpb_handler().set_constant(0);
m_pia_sys->writepa_handler().set(FUNC(to9_state::to9_sys_porta_out));
@ -1412,25 +1329,10 @@ ROM_END
/* ------------ inputs ------------ */
static INPUT_PORTS_START ( to8_config )
PORT_START ( "config" )
PORT_CONFNAME ( 0x01, 0x00, "Game Port" )
PORT_CONFSETTING ( 0x00, DEF_STR( Joystick ) )
PORT_CONFSETTING ( 0x01, "Mouse" )
PORT_CONFNAME ( 0x02, 0x00, "Keyboard" )
PORT_CONFSETTING ( 0x00, "Enabled" )
PORT_CONFSETTING ( 0x02, "Disabled" )
INPUT_PORTS_END
static INPUT_PORTS_START ( to8 )
PORT_INCLUDE ( thom_lightpen )
PORT_INCLUDE ( thom_game_port )
PORT_INCLUDE ( to9_keyboard )
PORT_INCLUDE ( to8_config )
PORT_INCLUDE ( to7_config )
PORT_INCLUDE ( to7_vconfig )
INPUT_PORTS_END
@ -1449,7 +1351,8 @@ void to9_state::to8(machine_config &config)
m_maincpu->set_addrmap(AS_PROGRAM, &to9_state::to8_map);
//MC6804(config, "kbdmcu", 11_MHz_XTAL);
TO8_KEYBOARD(config, m_to8_kbd);
m_to8_kbd->data_cb().set(m_mc6846, FUNC(mc6846_device::set_input_cp1));
m_pia_sys->readpa_handler().set(FUNC(to9_state::to8_sys_porta_in));
m_pia_sys->readpb_handler().set_constant(0);
@ -1537,7 +1440,7 @@ void to9_state::to9p_map(address_map &map)
map(0xe7c8, 0xe7cb).rw("pia_0", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
map(0xe7cc, 0xe7cf).rw("pia_1", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
map(0xe7da, 0xe7dd).rw(FUNC(to9_state::to8_vreg_r), FUNC(to9_state::to8_vreg_w));
map(0xe7de, 0xe7df).rw(FUNC(to9_state::to9_kbd_r), FUNC(to9_state::to9_kbd_w));
map(0xe7de, 0xe7df).rw(m_to9_kbd, FUNC(to9_keyboard_device::kbd_acia_r), FUNC(to9_keyboard_device::kbd_acia_w));
map(0xe7e4, 0xe7e7).rw(FUNC(to9_state::to8_gatearray_r), FUNC(to9_state::to8_gatearray_w));
/* map(0xe7f0, 0xe7f7).rw(FUNC(to9_state::to9_ieee_r), FUNC(to9_state::to9_ieee_w )); */
@ -1588,7 +1491,6 @@ ROM_END
static INPUT_PORTS_START ( to9p )
PORT_INCLUDE ( thom_lightpen )
PORT_INCLUDE ( thom_game_port )
PORT_INCLUDE ( to9_keyboard )
PORT_INCLUDE ( to7_config )
PORT_INCLUDE ( to7_vconfig )
INPUT_PORTS_END
@ -1603,6 +1505,9 @@ void to9_state::to9p(machine_config &config)
m_maincpu->set_addrmap(AS_PROGRAM, &to9_state::to9p_map);
TO9P_KEYBOARD(config, m_to9_kbd);
m_to9_kbd->irq_cb().set(m_mainirq, FUNC(input_merger_device::in_w<4>));
m_pia_sys->readpa_handler().set(FUNC(to9_state::to9_sys_porta_in));
m_pia_sys->readpb_handler().set_constant(0);
m_pia_sys->writepa_handler().set(FUNC(to9_state::to9_sys_porta_out));
@ -1861,9 +1766,6 @@ static INPUT_PORTS_START ( mo6_keyboard )
KEY ( 4, "\303\271 %", COLON ) PORT_CHAR( 0xf9 ) PORT_CHAR('%')
PORT_BIT ( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED )
/* unused */
PORT_START ( "keyboard.9" )
INPUT_PORTS_END
/* QWERTY version */
@ -2169,10 +2071,6 @@ static INPUT_PORTS_START ( mo5nr_keyboard )
KEY ( 6, "Stop", TAB ) PORT_CHAR(27)
PORT_BIT ( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
/* unused */
PORT_START ( "keyboard.8" )
PORT_START ( "keyboard.9" )
INPUT_PORTS_END
static INPUT_PORTS_START ( mo5nr )

View File

@ -13,6 +13,8 @@
#pragma once
#include "to_kbd.h"
#include "cpu/m6809/m6809.h"
#include "imagedev/cassette.h"
#include "machine/6821pia.h"
@ -231,7 +233,7 @@ protected:
required_ioport m_io_lightpen_button;
required_ioport m_io_config;
required_ioport m_io_vconfig;
required_ioport_array<10> m_io_keyboard;
optional_ioport_array<9> m_io_keyboard;
required_memory_bank m_vrambank;
optional_memory_bank m_cartbank;
optional_memory_bank m_rambank;
@ -384,6 +386,8 @@ class to9_state : public thomson_state
public:
to9_state(const machine_config &mconfig, device_type type, const char *tag) :
thomson_state(mconfig, type, tag),
m_to8_kbd(*this, "to8_kbd"),
m_to9_kbd(*this, "to9_kbd"),
m_centronics(*this, "centronics"),
m_cent_data_out(*this, "cent_data_out"),
m_syslobank(*this, TO8_SYS_LO),
@ -400,6 +404,8 @@ public:
void to9p(machine_config &config);
protected:
optional_device<to8_keyboard_device> m_to8_kbd;
optional_device<to9_keyboard_device> m_to9_kbd;
optional_device<centronics_device> m_centronics;
optional_device<output_latch_device> m_cent_data_out;
@ -411,14 +417,6 @@ protected:
int m_centronics_busy = 0;
uint8_t m_to8_kbd_ack = 0; /* 1 = cpu inits / accepts transfers */
uint16_t m_to8_kbd_data = 0; /* data to transmit */
uint16_t m_to8_kbd_step = 0; /* transmission automaton state */
uint8_t m_to8_kbd_last_key = 0; /* last key (for repetition) */
uint32_t m_to8_kbd_key_count = 0; /* keypress time (for repetition) */
uint8_t m_to8_kbd_caps = 0; /* caps lock */
emu_timer* m_to8_kbd_timer = nullptr; /* bit-send */
emu_timer* m_to8_kbd_signal = nullptr; /* signal from CPU */
uint8_t m_to8_data_vpage = 0;
uint8_t m_to8_cart_vpage = 0;
uint8_t m_to8_reg_ram = 0;
@ -430,7 +428,6 @@ protected:
uint8_t m_to8_soft_bank = 0;
uint8_t m_to8_bios_bank = 0;
TIMER_CALLBACK_MEMBER( to8_kbd_timer_cb );
void to8_update_ram_bank_postload();
void to8_update_cart_bank_postload();
void to8_cartridge_w(offs_t offset, uint8_t data);
@ -456,12 +453,6 @@ protected:
void to8_data_hi_w(offs_t offset, uint8_t data);
void to8_vcart_w(offs_t offset, uint8_t data);
int to8_kbd_ktest();
int to8_kbd_get_key();
void to8_kbd_timer_func();
void to8_kbd_set_ack( int data );
void to8_kbd_reset();
void to8_kbd_init();
void to8_update_ram_bank();
void to8_update_cart_bank();
@ -475,9 +466,6 @@ protected:
void to9_cartridge_w(offs_t offset, uint8_t data);
uint8_t to9_cartridge_r(offs_t offset);
void to9_update_ram_bank_postload();
uint8_t to9_kbd_r(offs_t offset);
void to9_kbd_w(offs_t offset, uint8_t data);
TIMER_CALLBACK_MEMBER( to9_kbd_timer_cb );
uint8_t to9_sys_porta_in();
void to9_sys_porta_out(uint8_t data);
void to9_sys_portb_out(uint8_t data);
@ -496,31 +484,11 @@ protected:
uint8_t m_to9_palette_data[32]{};
uint8_t m_to9_palette_idx = 0;
uint8_t m_to9_soft_bank = 0;
uint8_t m_to9_kbd_parity = 0; /* 0=even, 1=odd, 2=no parity */
uint8_t m_to9_kbd_intr = 0; /* interrupt mode */
uint8_t m_to9_kbd_in = 0; /* data from keyboard */
uint8_t m_to9_kbd_status = 0; /* status */
uint8_t m_to9_kbd_overrun = 0; /* character lost */
uint8_t m_to9_kbd_periph = 0; /* peripheral mode */
uint8_t m_to9_kbd_byte_count = 0; /* byte-count in peripheral mode */
uint16_t m_to9_mouse_x = 0;
uint16_t m_to9_mouse_y = 0;
uint8_t m_to9_kbd_last_key = 0; /* for key repetition */
uint16_t m_to9_kbd_key_count = 0;
uint8_t m_to9_kbd_caps = 0; /* caps-lock */
uint8_t m_to9_kbd_pad = 0; /* keypad outputs special codes */
emu_timer* m_to9_kbd_timer = nullptr;
void to9_set_video_mode( uint8_t data, int style );
void to9_palette_init();
void to9_update_cart_bank();
void to9_update_ram_bank();
int to9_kbd_ktest();
void to9_kbd_update_irq();
void to9_kbd_send( uint8_t data, int parity );
int to9_kbd_get_key();
void to9_kbd_reset();
void to9_kbd_init();
};
class mo6_state : public to9_state

View File

@ -224,19 +224,6 @@ void thomson_state::thom_irq_reset()
/* ------------ 6850 defines ------------ */
#define ACIA_6850_RDRF 0x01 /* Receive data register full */
#define ACIA_6850_TDRE 0x02 /* Transmit data register empty */
#define ACIA_6850_dcd 0x04 /* Data carrier detect, active low */
#define ACIA_6850_cts 0x08 /* Clear to send, active low */
#define ACIA_6850_FE 0x10 /* Framing error */
#define ACIA_6850_OVRN 0x20 /* Receiver overrun */
#define ACIA_6850_PE 0x40 /* Parity error */
#define ACIA_6850_irq 0x80 /* Interrupt request, active low */
/***************************** TO7 / T9000 *************************/
DEVICE_IMAGE_LOAD_MEMBER( thomson_state::to7_cartridge )
@ -1689,451 +1676,11 @@ void to9_state::to9_update_ram_bank_postload()
}
/* ------------ keyboard (6850 ACIA + 6805 CPU) ------------ */
/* The 6805 chip scans the keyboard and sends ASCII codes to the 6909.
Data between the 6809 and 6805 is serialized at 9600 bauds.
On the 6809 side, a 6850 ACIA is used.
We do not emulate the seral line but pass bytes directly between the
keyboard and the 6850 registers.
Note that the keyboard protocol uses the parity bit as an extra data bit.
*/
/* normal mode: polling interval */
#define TO9_KBD_POLL_PERIOD attotime::from_msec( 10 )
/* peripheral mode: time between two bytes, and after last byte */
#define TO9_KBD_BYTE_SPACE attotime::from_usec( 300 )
#define TO9_KBD_END_SPACE attotime::from_usec( 9100 )
/* first and subsequent repeat periods, in TO9_KBD_POLL_PERIOD units */
#define TO9_KBD_REPEAT_DELAY 80 /* 800 ms */
#define TO9_KBD_REPEAT_PERIOD 7 /* 70 ms */
/* quick keyboard scan */
int to9_state::to9_kbd_ktest()
{
int line, bit;
uint8_t port;
for ( line = 0; line < 10; line++ )
{
port = m_io_keyboard[line]->read();
if ( line == 7 || line == 9 )
port |= 1; /* shift & control */
for ( bit = 0; bit < 8; bit++ )
{
if ( ! (port & (1 << bit)) )
return 1;
}
}
return 0;
}
void to9_state::to9_kbd_update_irq()
{
if ( (m_to9_kbd_intr & 4) && (m_to9_kbd_status & ACIA_6850_RDRF) )
m_to9_kbd_status |= ACIA_6850_irq; /* byte received interrupt */
if ( (m_to9_kbd_intr & 4) && (m_to9_kbd_status & ACIA_6850_OVRN) )
m_to9_kbd_status |= ACIA_6850_irq; /* overrun interrupt */
if ( (m_to9_kbd_intr & 3) == 1 && (m_to9_kbd_status & ACIA_6850_TDRE) )
m_to9_kbd_status |= ACIA_6850_irq; /* ready to transmit interrupt */
m_mainirq->in_w<3>( m_to9_kbd_status & ACIA_6850_irq );
}
uint8_t to9_state::to9_kbd_r(offs_t offset)
{
/* ACIA 6850 registers */
switch ( offset )
{
case 0: /* get status */
/* bit 0: data received */
/* bit 1: ready to transmit data (always 1) */
/* bit 2: data carrier detect (ignored) */
/* bit 3: clear to send (ignored) */
/* bit 4: framing error (ignored) */
/* bit 5: overrun */
/* bit 6: parity error */
/* bit 7: interrupt */
LOGMASKED(LOG_KBD, "$%04x %f to9_kbd_r: status $%02X (rdrf=%i, tdre=%i, ovrn=%i, pe=%i, irq=%i)\n",
m_maincpu->pc(), machine().time().as_double(), m_to9_kbd_status,
(m_to9_kbd_status & ACIA_6850_RDRF) ? 1 : 0,
(m_to9_kbd_status & ACIA_6850_TDRE) ? 1 : 0,
(m_to9_kbd_status & ACIA_6850_OVRN) ? 1 : 0,
(m_to9_kbd_status & ACIA_6850_PE) ? 1 : 0,
(m_to9_kbd_status & ACIA_6850_irq) ? 1 : 0 );
return m_to9_kbd_status;
case 1: /* get input data */
if ( !machine().side_effects_disabled() )
{
m_to9_kbd_status &= ~(ACIA_6850_irq | ACIA_6850_PE);
if ( m_to9_kbd_overrun )
m_to9_kbd_status |= ACIA_6850_OVRN;
else
m_to9_kbd_status &= ~(ACIA_6850_OVRN | ACIA_6850_RDRF);
m_to9_kbd_overrun = 0;
LOGMASKED(LOG_KBD, "$%04x %f to9_kbd_r: read data $%02X\n", m_maincpu->pc(), machine().time().as_double(), m_to9_kbd_in);
to9_kbd_update_irq();
}
return m_to9_kbd_in;
default:
LOGMASKED(LOG_ERRORS, "$%04x to9_kbd_r: invalid offset %i\n", m_maincpu->pc(), offset);
return 0;
}
}
void to9_state::to9_kbd_w(offs_t offset, uint8_t data)
{
/* ACIA 6850 registers */
switch ( offset )
{
case 0: /* set control */
/* bits 0-1: clock divide (ignored) or reset */
if ( (data & 3) == 3 )
{
/* reset */
m_to9_kbd_overrun = 0;
m_to9_kbd_status = ACIA_6850_TDRE;
m_to9_kbd_intr = 0;
LOGMASKED(LOG_KBD, "$%04x %f to9_kbd_w: reset (data=$%02X)\n", m_maincpu->pc(), machine().time().as_double(), data);
}
else
{
/* bits 2-4: parity */
if ( (data & 0x18) == 0x10 )
m_to9_kbd_parity = 2;
else
m_to9_kbd_parity = (data >> 2) & 1;
/* bits 5-6: interrupt on transmit */
/* bit 7: interrupt on receive */
m_to9_kbd_intr = data >> 5;
LOGMASKED(LOG_KBD, "$%04x %f to9_kbd_w: set control to $%02X (parity=%i, intr in=%i out=%i)\n",
m_maincpu->pc(), machine().time().as_double(),
data, m_to9_kbd_parity, m_to9_kbd_intr >> 2,
(m_to9_kbd_intr & 3) ? 1 : 0);
}
to9_kbd_update_irq();
break;
case 1: /* output data */
m_to9_kbd_status &= ~(ACIA_6850_irq | ACIA_6850_TDRE);
to9_kbd_update_irq();
/* TODO: 1 ms delay here ? */
m_to9_kbd_status |= ACIA_6850_TDRE; /* data transmit ready again */
to9_kbd_update_irq();
switch ( data )
{
case 0xF8:
/* reset */
m_to9_kbd_caps = 1;
m_to9_kbd_periph = 0;
m_to9_kbd_pad = 0;
break;
case 0xF9: m_to9_kbd_caps = 1; break;
case 0xFA: m_to9_kbd_caps = 0; break;
case 0xFB: m_to9_kbd_pad = 1; break;
case 0xFC: m_to9_kbd_pad = 0; break;
case 0xFD: m_to9_kbd_periph = 1; break;
case 0xFE: m_to9_kbd_periph = 0; break;
default:
LOGMASKED(LOG_ERRORS, "$%04x %f to9_kbd_w: unknown kbd command %02X\n", m_maincpu->pc(), machine().time().as_double(), data);
}
m_caps_led = !m_to9_kbd_caps;
LOG("$%04x %f to9_kbd_w: kbd command %02X (caps=%i, pad=%i, periph=%i)\n",
m_maincpu->pc(), machine().time().as_double(), data,
m_to9_kbd_caps, m_to9_kbd_pad, m_to9_kbd_periph);
break;
default:
LOGMASKED(LOG_ERRORS, "$%04x to9_kbd_w: invalid offset %i (data=$%02X) \n", m_maincpu->pc(), offset, data);
}
}
/* send a key to the CPU, 8-bit + parity bit (0=even, 1=odd)
note: parity is not used as a checksum but to actually transmit a 9-th bit
of information!
*/
void to9_state::to9_kbd_send( uint8_t data, int parity )
{
if ( m_to9_kbd_status & ACIA_6850_RDRF )
{
/* overrun will be set when the current valid byte is read */
m_to9_kbd_overrun = 1;
LOGMASKED(LOG_KBD, "%f to9_kbd_send: overrun => drop data=$%02X, parity=%i\n", machine().time().as_double(), data, parity);
}
else
{
/* valid byte */
m_to9_kbd_in = data;
m_to9_kbd_status |= ACIA_6850_RDRF; /* raise data received flag */
if ( m_to9_kbd_parity == 2 || m_to9_kbd_parity == parity )
m_to9_kbd_status &= ~ACIA_6850_PE; /* parity OK */
else
m_to9_kbd_status |= ACIA_6850_PE; /* parity error */
LOGMASKED(LOG_KBD, "%f to9_kbd_send: data=$%02X, parity=%i, status=$%02X\n", machine().time().as_double(), data, parity, m_to9_kbd_status);
}
to9_kbd_update_irq();
}
/* keycode => TO9 code (extended ASCII), shifted and un-shifted */
static const int to9_kbd_code[80][2] =
{
{ 145, 150 }, { '_', '6' }, { 'Y', 'Y' }, { 'H', 'H' },
{ 11, 11 }, { 9, 9 }, { 30, 12 }, { 'N', 'N' },
{ 146, 151 }, { '(', '5' }, { 'T', 'T' }, { 'G', 'G' },
{ '=', '+' }, { 8, 8 }, { 28, 28 }, { 'B', 'B' },
{ 147, 152 }, { '\'', '4' }, { 'R', 'R' }, { 'F', 'F' },
{ 22, 22 }, { 155, 155 }, { 29, 127 }, { 'V', 'V' },
{ 148, 153 }, { '"', '3' }, { 'E', 'E' }, { 'D', 'D' },
{ 161, 161 }, { 158, 158 },
{ 154, 154 }, { 'C', 'C' },
{ 144, 149 }, { 128, '2' }, { 'Z', 'Z' }, { 'S', 'S' },
{ 162, 162 }, { 156, 156 },
{ 164, 164 }, { 'X', 'X' },
{ '#', '@' }, { '*', '1' }, { 'A', 'A' }, { 'Q', 'Q' },
{ '[', '{' }, { 159, 159 }, { 160, 160 }, { 'W', 'W' },
{ 2, 2 }, { 129, '7' }, { 'U', 'U' }, { 'J', 'J' },
{ ' ', ' ' }, { 163, 163 }, { 165, 165 },
{ ',', '?' },
{ 0, 0 }, { '!', '8' }, { 'I', 'I' }, { 'K', 'K' },
{ '$', '&' }, { 10, 10 }, { ']', '}' }, { ';', '.' },
{ 0, 0 }, { 130, '9' }, { 'O', 'O' }, { 'L', 'L' },
{ '-', '\\' }, { 132, '%' }, { 13, 13 }, { ':', '/' },
{ 0, 0 }, { 131, '0' }, { 'P', 'P' }, { 'M', 'M' },
{ ')', 134 }, { '^', 133 }, { 157, 157 }, { '>', '<' }
};
/* returns the ASCII code for the key, or 0 for no key */
int to9_state::to9_kbd_get_key()
{
int control = ! (m_io_keyboard[7]->read() & 1);
int shift = ! (m_io_keyboard[9]->read() & 1);
int key = -1, line, bit;
uint8_t port;
for ( line = 0; line < 10; line++ )
{
port = m_io_keyboard[line]->read();
if ( line == 7 || line == 9 )
port |= 1; /* shift & control */
/* TODO: correct handling of simultaneous keystokes:
return the new key preferably & disable repeat
*/
for ( bit = 0; bit < 8; bit++ )
{
if ( ! (port & (1 << bit)) )
key = line * 8 + bit;
}
}
if ( key == -1 )
{
m_to9_kbd_last_key = 0xff;
m_to9_kbd_key_count = 0;
return 0;
}
else if ( key == 64 )
{
/* caps lock */
if ( m_to9_kbd_last_key == key )
return 0; /* no repeat */
m_to9_kbd_last_key = key;
m_to9_kbd_caps = !m_to9_kbd_caps;
m_caps_led = !m_to9_kbd_caps;
return 0;
}
else
{
int asc;
asc = to9_kbd_code[key][shift];
if ( ! asc ) return 0;
/* keypad */
if ( ! m_to9_kbd_pad ) {
if ( asc >= 154 && asc <= 163 )
asc += '0' - 154;
else if ( asc == 164 )
asc = '.';
else if ( asc == 165 )
asc = 13;
}
/* shifted letter */
if ( asc >= 'A' && asc <= 'Z' && ( ! m_to9_kbd_caps ) && ( ! shift ) )
asc += 'a' - 'A';
/* control */
if ( control )
asc &= ~0x40;
if ( key == m_to9_kbd_last_key )
{
/* repeat */
m_to9_kbd_key_count++;
if ( m_to9_kbd_key_count < TO9_KBD_REPEAT_DELAY || (m_to9_kbd_key_count - TO9_KBD_REPEAT_DELAY) % TO9_KBD_REPEAT_PERIOD )
return 0;
LOGMASKED(LOG_KBD, "to9_kbd_get_key: repeat key $%02X '%c'\n", asc, asc);
return asc;
}
else
{
m_to9_kbd_last_key = key;
m_to9_kbd_key_count = 0;
LOGMASKED(LOG_KBD, "to9_kbd_get_key: key down $%02X '%c'\n", asc, asc);
return asc;
}
}
}
TIMER_CALLBACK_MEMBER(to9_state::to9_kbd_timer_cb)
{
if ( m_to9_kbd_periph )
{
/* peripheral mode: every 10 ms we send 4 bytes */
switch ( m_to9_kbd_byte_count )
{
case 0: /* key */
to9_kbd_send( to9_kbd_get_key(), 0 );
break;
case 1: /* x axis */
{
int newx = m_io_mouse_x->read();
uint8_t data = ( (newx - m_to9_mouse_x) & 0xf ) - 8;
to9_kbd_send( data, 1 );
m_to9_mouse_x = newx;
break;
}
case 2: /* y axis */
{
int newy = m_io_mouse_y->read();
uint8_t data = ( (newy - m_to9_mouse_y) & 0xf ) - 8;
to9_kbd_send( data, 1 );
m_to9_mouse_y = newy;
break;
}
case 3: /* axis overflow & buttons */
{
int b = m_io_mouse_button->read();
uint8_t data = 0;
if ( b & 1 ) data |= 1;
if ( b & 2 ) data |= 4;
to9_kbd_send( data, 1 );
break;
}
}
m_to9_kbd_byte_count = ( m_to9_kbd_byte_count + 1 ) & 3;
m_to9_kbd_timer->adjust(m_to9_kbd_byte_count ? TO9_KBD_BYTE_SPACE : TO9_KBD_END_SPACE);
}
else
{
int key = to9_kbd_get_key();
/* keyboard mode: send a byte only if a key is down */
if ( key )
to9_kbd_send( key, 0 );
m_to9_kbd_timer->adjust(TO9_KBD_POLL_PERIOD);
}
}
void to9_state::to9_kbd_reset()
{
LOG("to9_kbd_reset called\n");
m_to9_kbd_overrun = 0; /* no byte lost */
m_to9_kbd_status = ACIA_6850_TDRE; /* clear to transmit */
m_to9_kbd_intr = 0; /* interrupt disabled */
m_to9_kbd_caps = 1;
m_to9_kbd_periph = 0;
m_to9_kbd_pad = 0;
m_to9_kbd_byte_count = 0;
m_caps_led = !m_to9_kbd_caps;
m_to9_kbd_key_count = 0;
m_to9_kbd_last_key = 0xff;
to9_kbd_update_irq();
m_to9_kbd_timer->adjust(TO9_KBD_POLL_PERIOD);
}
void to9_state::to9_kbd_init()
{
LOG("to9_kbd_init called\n");
m_to9_kbd_timer = timer_alloc(FUNC(to9_state::to9_kbd_timer_cb), this);
save_item(NAME(m_to9_kbd_parity));
save_item(NAME(m_to9_kbd_intr));
save_item(NAME(m_to9_kbd_in));
save_item(NAME(m_to9_kbd_status));
save_item(NAME(m_to9_kbd_overrun));
save_item(NAME(m_to9_kbd_last_key));
save_item(NAME(m_to9_kbd_key_count));
save_item(NAME(m_to9_kbd_caps));
save_item(NAME(m_to9_kbd_periph));
save_item(NAME(m_to9_kbd_pad));
save_item(NAME(m_to9_kbd_byte_count));
save_item(NAME(m_to9_mouse_x));
save_item(NAME(m_to9_mouse_y));
}
/* ------------ system PIA 6821 ------------ */
uint8_t to9_state::to9_sys_porta_in()
{
uint8_t ktest = to9_kbd_ktest();
uint8_t ktest = m_to9_kbd->ktest_r();
LOGMASKED(LOG_KBD, "to9_sys_porta_in: ktest=%i\n", ktest);
@ -2192,7 +1739,6 @@ MACHINE_RESET_MEMBER( to9_state, to9 )
/* subsystems */
thom_irq_reset();
to7_game_reset();
to9_kbd_reset();
m_extension->rom_map(m_maincpu->space(AS_PROGRAM), 0xe000, 0xe7bf);
m_extension->io_map (m_maincpu->space(AS_PROGRAM), 0xe7c0, 0xe7ff);
@ -2229,7 +1775,6 @@ MACHINE_START_MEMBER( to9_state, to9 )
/* subsystems */
to7_game_init();
to9_kbd_init();
to9_palette_init();
m_extension->rom_map(m_maincpu->space(AS_PROGRAM), 0xe000, 0xe7bf);
@ -2264,318 +1809,6 @@ MACHINE_START_MEMBER( to9_state, to9 )
/***************************** TO8 *************************/
/* ------------ keyboard (6804) ------------ */
/* The 6804 chip scans the keyboard and sends keycodes to the 6809.
Data is serialized using variable pulse length encoding.
Unlike the TO9, there is no decoding chip on the 6809 side, only
1-bit PIA ports (6821 & 6846). The 6809 does the decoding.
We do not emulate the 6804 but pass serialized data directly through the
PIA ports.
Note: if we conform to the (scarce) documentation the CPU tend to lock
waiting for keyboard input.
The protocol documentation is pretty scarce and does not account for these
behaviors!
The emulation code contains many hacks (delays, timeouts, spurious
pulses) to improve the stability.
This works well, but is not very accurate.
*/
/* polling interval */
#define TO8_KBD_POLL_PERIOD attotime::from_msec( 1 )
/* first and subsequent repeat periods, in TO8_KBD_POLL_PERIOD units */
#define TO8_KBD_REPEAT_DELAY 800 /* 800 ms */
#define TO8_KBD_REPEAT_PERIOD 70 /* 70 ms */
/* timeout waiting for CPU */
#define TO8_KBD_TIMEOUT attotime::from_msec( 100 )
/* quick keyboard scan */
int to9_state::to8_kbd_ktest()
{
int line, bit;
uint8_t port;
if ( m_io_config->read() & 2 )
return 0; /* disabled */
for ( line = 0; line < 10; line++ )
{
port = m_io_keyboard[line]->read();
if ( line == 7 || line == 9 )
port |= 1; /* shift & control */
for ( bit = 0; bit < 8; bit++ )
{
if ( ! (port & (1 << bit)) )
return 1;
}
}
return 0;
}
/* keyboard scan & return keycode (or -1) */
int to9_state::to8_kbd_get_key()
{
int control = (m_io_keyboard[7]->read() & 1) ? 0 : 0x100;
int shift = (m_io_keyboard[9]->read() & 1) ? 0 : 0x080;
int key = -1, line, bit;
uint8_t port;
if ( m_io_config->read() & 2 )
return -1; /* disabled */
for ( line = 0; line < 10; line++ )
{
port = m_io_keyboard[line]->read();
if ( line == 7 || line == 9 )
port |= 1; /* shift & control */
/* TODO: correct handling of simultaneous keystokes:
return the new key preferably & disable repeat
*/
for ( bit = 0; bit < 8; bit++ )
{
if ( ! (port & (1 << bit)) )
key = line * 8 + bit;
}
}
if ( key == -1 )
{
m_to8_kbd_last_key = 0xff;
m_to8_kbd_key_count = 0;
return -1;
}
else if ( key == 64 )
{
/* caps lock */
if ( m_to8_kbd_last_key == key )
return -1; /* no repeat */
m_to8_kbd_last_key = key;
m_to8_kbd_caps = !m_to8_kbd_caps;
if ( m_to8_kbd_caps )
key |= 0x080; /* auto-shift */
m_caps_led = !m_to8_kbd_caps;
return key;
}
else if ( key == m_to8_kbd_last_key )
{
/* repeat */
m_to8_kbd_key_count++;
if ( m_to8_kbd_key_count < TO8_KBD_REPEAT_DELAY || (m_to8_kbd_key_count - TO8_KBD_REPEAT_DELAY) % TO8_KBD_REPEAT_PERIOD )
return -1;
return key | shift | control;
}
else
{
m_to8_kbd_last_key = key;
m_to8_kbd_key_count = 0;
return key | shift | control;
}
}
/* steps:
0 = idle, key polling
1 = wait for ack to go down (key to send)
99-117 = key data transmit
91-117 = signal
255 = timeout
*/
/* keyboard automaton */
void to9_state::to8_kbd_timer_func()
{
attotime d;
LOGMASKED(LOG_KBD, "%f to8_kbd_timer_cb: step=%i ack=%i data=$%03X\n", machine().time().as_double(), m_to8_kbd_step, m_to8_kbd_ack, m_to8_kbd_data);
if( ! m_to8_kbd_step )
{
/* key polling */
int k = to8_kbd_get_key();
/* if not in transfer, send pulse from time to time
(helps avoiding CPU lock)
*/
if ( ! m_to8_kbd_ack )
m_mc6846->set_input_cp1(0);
m_mc6846->set_input_cp1(1);
if ( k == -1 )
d = TO8_KBD_POLL_PERIOD;
else
{
/* got key! */
LOGMASKED(LOG_KBD, "to8_kbd_timer_cb: got key $%03X\n", k);
m_to8_kbd_data = k;
m_to8_kbd_step = 1;
d = attotime::from_usec( 100 );
}
}
else if ( m_to8_kbd_step == 255 )
{
/* timeout */
m_to8_kbd_last_key = 0xff;
m_to8_kbd_key_count = 0;
m_to8_kbd_step = 0;
m_mc6846->set_input_cp1(1);
d = TO8_KBD_POLL_PERIOD;
}
else if ( m_to8_kbd_step == 1 )
{
/* schedule timeout waiting for ack to go down */
m_mc6846->set_input_cp1(0);
m_to8_kbd_step = 255;
d = TO8_KBD_TIMEOUT;
}
else if ( m_to8_kbd_step == 117 )
{
/* schedule timeout waiting for ack to go up */
m_mc6846->set_input_cp1(0);
m_to8_kbd_step = 255;
d = TO8_KBD_TIMEOUT;
}
else if ( m_to8_kbd_step & 1 )
{
/* send silence between bits */
m_mc6846->set_input_cp1(0);
d = attotime::from_usec( 100 );
m_to8_kbd_step++;
}
else
{
/* send bit */
int bpos = 8 - ( (m_to8_kbd_step - 100) / 2);
int bit = (m_to8_kbd_data >> bpos) & 1;
m_mc6846->set_input_cp1(1);
d = attotime::from_usec( bit ? 56 : 38 );
m_to8_kbd_step++;
}
m_to8_kbd_timer->adjust(d);
}
TIMER_CALLBACK_MEMBER(to9_state::to8_kbd_timer_cb)
{
to8_kbd_timer_func();
}
/* cpu <-> keyboard hand-check */
void to9_state::to8_kbd_set_ack( int data )
{
if ( data == m_to8_kbd_ack )
return;
m_to8_kbd_ack = data;
if ( data )
{
double len = m_to8_kbd_signal->elapsed( ).as_double() * 1000. - 2.;
LOGMASKED(LOG_KBD, "%f to8_kbd_set_ack: CPU end ack, len=%f\n", machine().time().as_double(), len);
if ( m_to8_kbd_data == 0xfff )
{
/* end signal from CPU */
if ( len >= 0.6 && len <= 0.8 )
{
LOG("%f to8_kbd_set_ack: INIT signal\n", machine().time().as_double());
m_to8_kbd_last_key = 0xff;
m_to8_kbd_key_count = 0;
m_to8_kbd_caps = 1;
/* send back signal: TODO returned codes ? */
m_to8_kbd_data = 0;
m_to8_kbd_step = 0;
m_to8_kbd_timer->adjust(attotime::from_msec( 1 ));
}
else
{
m_to8_kbd_step = 0;
m_to8_kbd_timer->adjust(TO8_KBD_POLL_PERIOD);
if ( len >= 1.2 && len <= 1.4 )
{
LOG("%f to8_kbd_set_ack: CAPS on signal\n", machine().time().as_double());
m_to8_kbd_caps = 1;
}
else if ( len >= 1.8 && len <= 2.0 )
{
LOG("%f to8_kbd_set_ack: CAPS off signal\n", machine().time().as_double());
m_to8_kbd_caps = 0;
}
}
m_caps_led = !m_to8_kbd_caps;
}
else
{
/* end key transmission */
m_to8_kbd_step = 0;
m_to8_kbd_timer->adjust(TO8_KBD_POLL_PERIOD);
}
}
else
{
if ( m_to8_kbd_step == 255 )
{
/* CPU accepts key */
m_to8_kbd_step = 99;
m_to8_kbd_timer->adjust(attotime::from_usec( 400 ));
}
else
{
/* start signal from CPU */
m_to8_kbd_data = 0xfff;
m_to8_kbd_step = 91;
m_to8_kbd_timer->adjust(attotime::from_usec( 400 ));
m_to8_kbd_signal->adjust(attotime::never);
}
LOGMASKED(LOG_KBD, "%f to8_kbd_set_ack: CPU ack, data=$%03X\n", machine().time().as_double(), m_to8_kbd_data);
}
}
void to9_state::to8_kbd_reset()
{
m_to8_kbd_last_key = 0xff;
m_to8_kbd_key_count = 0;
m_to8_kbd_step = 0;
m_to8_kbd_data = 0;
m_to8_kbd_ack = 1;
m_to8_kbd_caps = 1;
m_caps_led = !m_to8_kbd_caps;
to8_kbd_timer_func();
}
void to9_state::to8_kbd_init()
{
m_to8_kbd_timer = timer_alloc(FUNC(to9_state::to8_kbd_timer_cb), this);
m_to8_kbd_signal = machine().scheduler().timer_alloc(timer_expired_delegate());
save_item(NAME(m_to8_kbd_ack));
save_item(NAME(m_to8_kbd_data));
save_item(NAME(m_to8_kbd_step));
save_item(NAME(m_to8_kbd_last_key));
save_item(NAME(m_to8_kbd_key_count));
save_item(NAME(m_to8_kbd_caps));
}
/* ------------ RAM / ROM banking ------------ */
void to9_state::to8_update_ram_bank()
@ -3006,7 +2239,7 @@ void to9_state::to8_vreg_w(offs_t offset, uint8_t data)
uint8_t to9_state::to8_sys_porta_in()
{
int ktest = to8_kbd_ktest();
int ktest = m_to8_kbd->ktest_r();
LOGMASKED(LOG_KBD, "$%04x %f: to8_sys_porta_in ktest=%i\n", m_maincpu->pc(), machine().time().as_double(), ktest);
@ -3042,7 +2275,7 @@ uint8_t to9_state::to8_timer_port_in()
int lightpen = (m_io_lightpen_button->read() & 1) ? 2 : 0;
int cass = to7_get_cassette() ? 0x80 : 0;
int dtr = m_centronics_busy << 6;
int lock = m_to8_kbd_caps ? 0 : 8; /* undocumented! */
int lock = m_to8_kbd->caps_r() ? 0 : 8; /* undocumented! */
return lightpen | cass | dtr | lock;
}
@ -3056,7 +2289,7 @@ void to9_state::to8_timer_port_out(uint8_t data)
m_biosbank->set_entry( m_to8_bios_bank );
m_to8_soft_select = (data & 0x04) ? 1 : 0; /* bit 2: internal ROM select */
to8_update_cart_bank();
to8_kbd_set_ack(ack);
m_to8_kbd->set_ack(ack);
}
@ -3096,7 +2329,6 @@ MACHINE_RESET_MEMBER( to9_state, to8 )
/* subsystems */
thom_irq_reset();
to7_game_reset();
to8_kbd_reset();
/* gate-array */
m_to7_lightpen = 0;
@ -3141,7 +2373,6 @@ MACHINE_START_MEMBER( to9_state, to8 )
/* subsystems */
to7_game_init();
to8_kbd_init();
to9_palette_init();
m_extension->rom_map(m_maincpu->space(AS_PROGRAM), 0xe000, 0xe7bf);
@ -3241,7 +2472,6 @@ MACHINE_RESET_MEMBER( to9_state, to9p )
/* subsystems */
thom_irq_reset();
to7_game_reset();
to9_kbd_reset();
/* gate-array */
m_to7_lightpen = 0;
@ -3285,7 +2515,6 @@ MACHINE_START_MEMBER( to9_state, to9p )
/* subsystems */
to7_game_init();
to9_kbd_init();
to9_palette_init();
m_extension->rom_map(m_maincpu->space(AS_PROGRAM), 0xe000, 0xe7bf);

999
src/mame/thomson/to_kbd.cpp Normal file
View File

@ -0,0 +1,999 @@
// license:BSD-3-Clause
// copyright-holders:Antoine Mine
/**********************************************************************
Copyright (C) Antoine Mine' 2006
Thomson TO8 built-in keyboard & TO9 detached keyboard
**********************************************************************/
#include "emu.h"
#include "to_kbd.h"
#include "cpu/m6805/m68705.h"
#define LOG_KBD (1U << 1)
#define LOG_ERRORS (1U << 2)
#define VERBOSE (LOG_KBD | LOG_ERRORS)
#include "logmacro.h"
// device type definitions
DEFINE_DEVICE_TYPE(TO8_KEYBOARD, to8_keyboard_device, "to8_kbd", "Thomson TO8 keyboard")
DEFINE_DEVICE_TYPE(TO9_KEYBOARD, to9_keyboard_device, "to9_kbd", "Thomson TO9 keyboard")
DEFINE_DEVICE_TYPE(TO9P_KEYBOARD, to9p_keyboard_device, "to9p_kbd", "Thomson TO9+ keyboard")
to8_keyboard_device::to8_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, TO8_KEYBOARD, tag, owner, clock)
, m_data_cb(*this)
, m_io_keyboard(*this, "keyboard.%u", 0)
, m_caps_led(*this, "led0")
{
}
to9_keyboard_device::to9_keyboard_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, type, tag, owner, clock)
, m_irq_cb(*this)
, m_io_keyboard(*this, "keyboard.%u", 0)
, m_io_mouse_x(*this, "mouse_x")
, m_io_mouse_y(*this, "mouse_y")
, m_io_mouse_button(*this, "mouse_button")
, m_caps_led(*this, "led0")
{
}
to9_keyboard_device::to9_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: to9_keyboard_device(mconfig, TO9_KEYBOARD, tag, owner, clock)
{
}
to9p_keyboard_device::to9p_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: to9_keyboard_device(mconfig, TO9P_KEYBOARD, tag, owner, clock)
{
}
void to8_keyboard_device::device_add_mconfig(machine_config &config)
{
//MC6804P2(config, "mcu", 11_MHz_XTAL).set_disable();
}
void to9_keyboard_device::device_add_mconfig(machine_config &config)
{
M6805U2(config, "mcu", 4_MHz_XTAL).set_disable(); // 40 pins, actual model unknown
}
void to9p_keyboard_device::device_add_mconfig(machine_config &config)
{
M6805P2(config, "mcu", 4_MHz_XTAL).set_disable();
}
ROM_START(to8_kbd)
ROM_REGION(0x440, "mcu", 0)
ROM_LOAD("ef6804p2p_clav--to8.bin", 0x000, 0x440, NO_DUMP)
ROM_END
ROM_START(to9_kbd)
ROM_REGION(0x1000, "mcu", 0)
ROM_LOAD("6805.bin", 0x0000, 0x1000, NO_DUMP)
ROM_END
ROM_START(to9p_kbd)
ROM_REGION(0x800, "mcu", 0)
ROM_LOAD("6805p2.bin", 0x000, 0x800, NO_DUMP)
ROM_END
const tiny_rom_entry *to8_keyboard_device::device_rom_region() const
{
return ROM_NAME(to8_kbd);
}
const tiny_rom_entry *to9_keyboard_device::device_rom_region() const
{
return ROM_NAME(to9_kbd);
}
const tiny_rom_entry *to9p_keyboard_device::device_rom_region() const
{
return ROM_NAME(to9p_kbd);
}
/* ------------ inputs ------------ */
#define KEY(pos,name,key) \
PORT_BIT ( 1<<(pos), IP_ACTIVE_LOW, IPT_KEYBOARD ) \
PORT_NAME ( name ) \
PORT_CODE ( KEYCODE_##key )
static INPUT_PORTS_START ( to8_keyboard )
PORT_START ( "keyboard.0" )
KEY ( 0, "F2 F7", F2 ) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR(UCHAR_MAMEKEY(F7))
KEY ( 1, "_ 6", 6 ) PORT_CHAR('_') PORT_CHAR('6')
KEY ( 2, "Y", Y ) PORT_CHAR('Y')
KEY ( 3, "H", H ) PORT_CHAR('H')
KEY ( 4, u8"\u2191", UP ) PORT_CHAR(UCHAR_MAMEKEY(UP))
KEY ( 5, u8"\u2192", RIGHT ) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
KEY ( 6, "Home Clear", HOME ) PORT_CHAR(UCHAR_MAMEKEY(HOME)) PORT_CHAR(UCHAR_MAMEKEY(ESC))
KEY ( 7, "N", N ) PORT_CHAR('N')
PORT_START ( "keyboard.1" )
KEY ( 0, "F3 F8", F3 ) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR(UCHAR_MAMEKEY(F8))
KEY ( 1, "( 5", 5 ) PORT_CHAR('(') PORT_CHAR('5')
KEY ( 2, "T", T ) PORT_CHAR('T')
KEY ( 3, "G", G ) PORT_CHAR('G')
KEY ( 4, "= +", EQUALS ) PORT_CHAR('=') PORT_CHAR('+')
KEY ( 5, "\u2190", LEFT ) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
KEY ( 6, "Insert", INSERT ) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
KEY ( 7, "B", B ) PORT_CHAR('B')
PORT_START ( "keyboard.2" )
KEY ( 0, "F4 F9", F4 ) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR(UCHAR_MAMEKEY(F9))
KEY ( 1, "' 4", 4 ) PORT_CHAR('\'') PORT_CHAR('4')
KEY ( 2, "R", R ) PORT_CHAR('R')
KEY ( 3, "F", F ) PORT_CHAR('F')
KEY ( 4, "Accent", END ) PORT_CHAR(UCHAR_MAMEKEY(END))
KEY ( 5, "Keypad 1", 1_PAD ) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))
KEY ( 6, "Delete Backspace", DEL ) PORT_CHAR(8) PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE))
KEY ( 7, "V", V ) PORT_CHAR('V')
PORT_START ( "keyboard.3" )
KEY ( 0, "F5 F10", F5 ) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR(UCHAR_MAMEKEY(F10))
KEY ( 1, "\" 3", 3 ) PORT_CHAR('"') PORT_CHAR('3')
KEY ( 2, "E", E ) PORT_CHAR('E')
KEY ( 3, "D", D ) PORT_CHAR('D')
KEY ( 4, "Keypad 7", 7_PAD ) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))
KEY ( 5, "Keypad 4", 4_PAD ) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))
KEY ( 6, "Keypad 0", 0_PAD ) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))
KEY ( 7, "C \136", C ) PORT_CHAR('C')
PORT_START ( "keyboard.4" )
KEY ( 0, "F1 F6", F1 ) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR(UCHAR_MAMEKEY(F6))
KEY ( 1, u8"é 2", 2 ) PORT_CHAR( 0xe9 ) PORT_CHAR('2')
KEY ( 2, "Z", Z ) PORT_CHAR('Z')
KEY ( 3, "S", S ) PORT_CHAR('S')
KEY ( 4, "Keypad 8", 8_PAD ) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))
KEY ( 5, "Keypad 2", 2_PAD ) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))
KEY ( 6, "Keypad .", DEL_PAD ) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))
KEY ( 7, "X", X ) PORT_CHAR('X')
PORT_START ( "keyboard.5" )
KEY ( 0, "# @", TILDE ) PORT_CHAR('#') PORT_CHAR('@')
KEY ( 1, "* 1", 1 ) PORT_CHAR('*') PORT_CHAR('1')
KEY ( 2, "A \140", A ) PORT_CHAR('A')
KEY ( 3, "Q", Q ) PORT_CHAR('Q')
KEY ( 4, "[ {", QUOTE ) PORT_CHAR('[') PORT_CHAR('{')
KEY ( 5, "Keypad 5", 5_PAD ) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))
KEY ( 6, "Keypad 6", 6_PAD ) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))
KEY ( 7, "W", W ) PORT_CHAR('W')
PORT_START ( "keyboard.6" )
KEY ( 0, "Stop", TAB ) PORT_CHAR(27)
KEY ( 1, u8"è 7", 7 ) PORT_CHAR( 0xe8 ) PORT_CHAR('7')
KEY ( 2, "U", U ) PORT_CHAR('U')
KEY ( 3, "J", J ) PORT_CHAR('J')
KEY ( 4, "Space", SPACE ) PORT_CHAR(' ')
KEY ( 5, "Keypad 9", 9_PAD ) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))
KEY ( 6, "Keypad Enter", ENTER_PAD ) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD))
KEY ( 7, ", ?", COMMA ) PORT_CHAR(',') PORT_CHAR('?')
PORT_START ( "keyboard.7" )
KEY ( 0, "Control", LCONTROL ) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
KEY ( 1, "! 8", 8 ) PORT_CHAR('!') PORT_CHAR('8')
KEY ( 2, "I", I ) PORT_CHAR('I')
KEY ( 3, "K", K ) PORT_CHAR('K')
KEY ( 4, "$ &", CLOSEBRACE ) PORT_CHAR('$') PORT_CHAR('&')
KEY ( 5, u8"\u2193", DOWN ) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
KEY ( 6, "] }", BACKSLASH ) PORT_CHAR(']') PORT_CHAR('}')
KEY ( 7, "; .", STOP ) PORT_CHAR(';') PORT_CHAR('.')
PORT_START ( "keyboard.8" )
KEY ( 0, "Caps-Lock", CAPSLOCK ) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
KEY ( 1, u8"ç 9", 9 ) PORT_CHAR( 0xe7 ) PORT_CHAR('9')
KEY ( 2, "O", O ) PORT_CHAR('O')
KEY ( 3, "L", L ) PORT_CHAR('L')
KEY ( 4, "- \\", BACKSPACE ) PORT_CHAR('-') PORT_CHAR('\\')
KEY ( 5, u8"ù %", COLON ) PORT_CHAR( 0xf9 ) PORT_CHAR('%')
KEY ( 6, "Enter", ENTER ) PORT_CHAR(13)
KEY ( 7, ": /", SLASH ) PORT_CHAR(':') PORT_CHAR('/')
PORT_START ( "keyboard.9" )
KEY ( 0, "Shift", LSHIFT ) PORT_CODE ( KEYCODE_RSHIFT ) PORT_CHAR(UCHAR_SHIFT_1)
KEY ( 1, u8"à 0", 0 ) PORT_CHAR( 0xe0 ) PORT_CHAR('0')
KEY ( 2, "P", P ) PORT_CHAR('P')
KEY ( 3, "M", M ) PORT_CHAR('M')
KEY ( 4, u8") °", MINUS ) PORT_CHAR(')') PORT_CHAR( 0xb0 )
KEY ( 5, u8"^ ¨", OPENBRACE ) PORT_CHAR('^') PORT_CHAR( 0xa8 )
KEY ( 6, "Keypad 3", 3_PAD ) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))
KEY ( 7, "> <", BACKSLASH2 ) PORT_CHAR('>') PORT_CHAR('<')
INPUT_PORTS_END
static INPUT_PORTS_START ( to9_keyboard )
PORT_INCLUDE( to8_keyboard )
PORT_START ( "mouse_x" )
PORT_BIT ( 0xffff, 0x00, IPT_MOUSE_X )
PORT_NAME ( "Mouse X" )
PORT_SENSITIVITY ( 150 )
PORT_PLAYER (1)
PORT_START ( "mouse_y" )
PORT_BIT ( 0xffff, 0x00, IPT_MOUSE_Y )
PORT_NAME ( "Mouse Y" )
PORT_SENSITIVITY ( 150 )
PORT_PLAYER (1)
PORT_START ( "mouse_button" )
PORT_BIT ( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
PORT_NAME ( "Left Mouse Button" )
PORT_CODE( MOUSECODE_BUTTON1 )
PORT_BIT ( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 )
PORT_NAME ( "Right Mouse Button" )
INPUT_PORTS_END
ioport_constructor to8_keyboard_device::device_input_ports() const
{
return INPUT_PORTS_NAME(to8_keyboard);
}
ioport_constructor to9_keyboard_device::device_input_ports() const
{
return INPUT_PORTS_NAME(to9_keyboard);
}
ioport_constructor to9p_keyboard_device::device_input_ports() const
{
return INPUT_PORTS_NAME(to8_keyboard);
}
/* ------------ keyboard (6804) ------------ */
/* The 6804 chip scans the keyboard and sends keycodes to the 6809.
Data is serialized using variable pulse length encoding.
Unlike the TO9, there is no decoding chip on the 6809 side, only
1-bit PIA ports (6821 & 6846). The 6809 does the decoding.
We do not emulate the 6804 but pass serialized data directly through the
PIA ports.
Note: if we conform to the (scarce) documentation the CPU tend to lock
waiting for keyboard input.
The protocol documentation is pretty scarce and does not account for these
behaviors!
The emulation code contains many hacks (delays, timeouts, spurious
pulses) to improve the stability.
This works well, but is not very accurate.
*/
/* polling interval */
#define TO8_KBD_POLL_PERIOD attotime::from_msec( 1 )
/* first and subsequent repeat periods, in TO8_KBD_POLL_PERIOD units */
#define TO8_KBD_REPEAT_DELAY 800 /* 800 ms */
#define TO8_KBD_REPEAT_PERIOD 70 /* 70 ms */
/* timeout waiting for CPU */
#define TO8_KBD_TIMEOUT attotime::from_msec( 100 )
/* quick keyboard scan */
int to8_keyboard_device::ktest_r()
{
int line, bit;
uint8_t port;
for ( line = 0; line < 10; line++ )
{
port = m_io_keyboard[line]->read();
if ( line == 7 || line == 9 )
port |= 1; /* shift & control */
for ( bit = 0; bit < 8; bit++ )
{
if ( ! (port & (1 << bit)) )
return 1;
}
}
return 0;
}
/* keyboard scan & return keycode (or -1) */
int to8_keyboard_device::get_key()
{
int control = (m_io_keyboard[7]->read() & 1) ? 0 : 0x100;
int shift = (m_io_keyboard[9]->read() & 1) ? 0 : 0x080;
int key = -1, line, bit;
uint8_t port;
for ( line = 0; line < 10; line++ )
{
port = m_io_keyboard[line]->read();
if ( line == 7 || line == 9 )
port |= 1; /* shift & control */
/* TODO: correct handling of simultaneous keystokes:
return the new key preferably & disable repeat
*/
for ( bit = 0; bit < 8; bit++ )
{
if ( ! (port & (1 << bit)) )
key = line * 8 + bit;
}
}
if ( key == -1 )
{
m_kbd_last_key = 0xff;
m_kbd_key_count = 0;
return -1;
}
else if ( key == 64 )
{
/* caps lock */
if ( m_kbd_last_key == key )
return -1; /* no repeat */
m_kbd_last_key = key;
m_kbd_caps = !m_kbd_caps;
if ( m_kbd_caps )
key |= 0x080; /* auto-shift */
m_caps_led = !m_kbd_caps;
return key;
}
else if ( key == m_kbd_last_key )
{
/* repeat */
m_kbd_key_count++;
if ( m_kbd_key_count < TO8_KBD_REPEAT_DELAY || (m_kbd_key_count - TO8_KBD_REPEAT_DELAY) % TO8_KBD_REPEAT_PERIOD )
return -1;
return key | shift | control;
}
else
{
m_kbd_last_key = key;
m_kbd_key_count = 0;
return key | shift | control;
}
}
/* steps:
0 = idle, key polling
1 = wait for ack to go down (key to send)
99-117 = key data transmit
91-117 = signal
255 = timeout
*/
/* keyboard automaton */
void to8_keyboard_device::timer_func()
{
attotime d;
LOGMASKED(LOG_KBD, "%f timer_func: step=%i ack=%i data=$%03X\n", machine().time().as_double(), m_kbd_step, m_kbd_ack, m_kbd_data);
if( ! m_kbd_step )
{
/* key polling */
int k = get_key();
/* if not in transfer, send pulse from time to time
(helps avoiding CPU lock)
*/
if ( ! m_kbd_ack )
m_data_cb(0);
m_data_cb(1);
if ( k == -1 )
d = TO8_KBD_POLL_PERIOD;
else
{
/* got key! */
LOGMASKED(LOG_KBD, "timer_func: got key $%03X\n", k);
m_kbd_data = k;
m_kbd_step = 1;
d = attotime::from_usec( 100 );
}
}
else if ( m_kbd_step == 255 )
{
/* timeout */
m_kbd_last_key = 0xff;
m_kbd_key_count = 0;
m_kbd_step = 0;
m_data_cb(1);
d = TO8_KBD_POLL_PERIOD;
}
else if ( m_kbd_step == 1 )
{
/* schedule timeout waiting for ack to go down */
m_data_cb(0);
m_kbd_step = 255;
d = TO8_KBD_TIMEOUT;
}
else if ( m_kbd_step == 117 )
{
/* schedule timeout waiting for ack to go up */
m_data_cb(0);
m_kbd_step = 255;
d = TO8_KBD_TIMEOUT;
}
else if ( m_kbd_step & 1 )
{
/* send silence between bits */
m_data_cb(0);
d = attotime::from_usec( 100 );
m_kbd_step++;
}
else
{
/* send bit */
int bpos = 8 - ( (m_kbd_step - 100) / 2);
int bit = (m_kbd_data >> bpos) & 1;
m_data_cb(1);
d = attotime::from_usec( bit ? 56 : 38 );
m_kbd_step++;
}
m_kbd_timer->adjust(d);
}
TIMER_CALLBACK_MEMBER(to8_keyboard_device::timer_cb)
{
timer_func();
}
/* cpu <-> keyboard hand-check */
void to8_keyboard_device::set_ack( int data )
{
if ( data == m_kbd_ack )
return;
m_kbd_ack = data;
if ( data )
{
double len = m_kbd_signal->elapsed( ).as_double() * 1000. - 2.;
LOGMASKED(LOG_KBD, "%f set_ack: CPU end ack, len=%f\n", machine().time().as_double(), len);
if ( m_kbd_data == 0xfff )
{
/* end signal from CPU */
if ( len >= 0.6 && len <= 0.8 )
{
LOG("%f set_ack: INIT signal\n", machine().time().as_double());
m_kbd_last_key = 0xff;
m_kbd_key_count = 0;
m_kbd_caps = 1;
/* send back signal: TODO returned codes ? */
m_kbd_data = 0;
m_kbd_step = 0;
m_kbd_timer->adjust(attotime::from_msec( 1 ));
}
else
{
m_kbd_step = 0;
m_kbd_timer->adjust(TO8_KBD_POLL_PERIOD);
if ( len >= 1.2 && len <= 1.4 )
{
LOG("%f set_ack: CAPS on signal\n", machine().time().as_double());
m_kbd_caps = 1;
}
else if ( len >= 1.8 && len <= 2.0 )
{
LOG("%f set_ack: CAPS off signal\n", machine().time().as_double());
m_kbd_caps = 0;
}
}
m_caps_led = !m_kbd_caps;
}
else
{
/* end key transmission */
m_kbd_step = 0;
m_kbd_timer->adjust(TO8_KBD_POLL_PERIOD);
}
}
else
{
if ( m_kbd_step == 255 )
{
/* CPU accepts key */
m_kbd_step = 99;
m_kbd_timer->adjust(attotime::from_usec( 400 ));
}
else
{
/* start signal from CPU */
m_kbd_data = 0xfff;
m_kbd_step = 91;
m_kbd_timer->adjust(attotime::from_usec( 400 ));
m_kbd_signal->adjust(attotime::never);
}
LOGMASKED(LOG_KBD, "%f set_ack: CPU ack, data=$%03X\n", machine().time().as_double(), m_kbd_data);
}
}
void to8_keyboard_device::device_reset()
{
m_kbd_last_key = 0xff;
m_kbd_key_count = 0;
m_kbd_step = 0;
m_kbd_data = 0;
m_kbd_ack = 1;
m_kbd_caps = 1;
m_caps_led = !m_kbd_caps;
timer_func();
}
void to8_keyboard_device::device_start()
{
m_caps_led.resolve();
m_kbd_timer = timer_alloc(FUNC(to8_keyboard_device::timer_cb), this);
m_kbd_signal = machine().scheduler().timer_alloc(timer_expired_delegate());
save_item(NAME(m_kbd_ack));
save_item(NAME(m_kbd_data));
save_item(NAME(m_kbd_step));
save_item(NAME(m_kbd_last_key));
save_item(NAME(m_kbd_key_count));
save_item(NAME(m_kbd_caps));
}
/* ------------ 6850 defines ------------ */
#define ACIA_6850_RDRF 0x01 /* Receive data register full */
#define ACIA_6850_TDRE 0x02 /* Transmit data register empty */
#define ACIA_6850_dcd 0x04 /* Data carrier detect, active low */
#define ACIA_6850_cts 0x08 /* Clear to send, active low */
#define ACIA_6850_FE 0x10 /* Framing error */
#define ACIA_6850_OVRN 0x20 /* Receiver overrun */
#define ACIA_6850_PE 0x40 /* Parity error */
#define ACIA_6850_irq 0x80 /* Interrupt request, active low */
/* ------------ keyboard (6850 ACIA + 6805 CPU) ------------ */
/* The 6805 chip scans the keyboard and sends ASCII codes to the 6909.
Data between the 6809 and 6805 is serialized at 9600 bauds.
On the 6809 side, a 6850 ACIA is used.
We do not emulate the seral line but pass bytes directly between the
keyboard and the 6850 registers.
Note that the keyboard protocol uses the parity bit as an extra data bit.
*/
/* normal mode: polling interval */
#define TO9_KBD_POLL_PERIOD attotime::from_msec( 10 )
/* peripheral mode: time between two bytes, and after last byte */
#define TO9_KBD_BYTE_SPACE attotime::from_usec( 300 )
#define TO9_KBD_END_SPACE attotime::from_usec( 9100 )
/* first and subsequent repeat periods, in TO9_KBD_POLL_PERIOD units */
#define TO9_KBD_REPEAT_DELAY 80 /* 800 ms */
#define TO9_KBD_REPEAT_PERIOD 7 /* 70 ms */
/* quick keyboard scan */
int to9_keyboard_device::ktest_r()
{
int line, bit;
uint8_t port;
for ( line = 0; line < 10; line++ )
{
port = m_io_keyboard[line]->read();
if ( line == 7 || line == 9 )
port |= 1; /* shift & control */
for ( bit = 0; bit < 8; bit++ )
{
if ( ! (port & (1 << bit)) )
return 1;
}
}
return 0;
}
void to9_keyboard_device::update_irq()
{
if ( (m_kbd_intr & 4) && (m_kbd_status & ACIA_6850_RDRF) )
m_kbd_status |= ACIA_6850_irq; /* byte received interrupt */
if ( (m_kbd_intr & 4) && (m_kbd_status & ACIA_6850_OVRN) )
m_kbd_status |= ACIA_6850_irq; /* overrun interrupt */
if ( (m_kbd_intr & 3) == 1 && (m_kbd_status & ACIA_6850_TDRE) )
m_kbd_status |= ACIA_6850_irq; /* ready to transmit interrupt */
m_irq_cb( (m_kbd_status & ACIA_6850_irq) ? 1 : 0 );
}
uint8_t to9_keyboard_device::kbd_acia_r(offs_t offset)
{
/* ACIA 6850 registers */
switch ( offset )
{
case 0: /* get status */
/* bit 0: data received */
/* bit 1: ready to transmit data (always 1) */
/* bit 2: data carrier detect (ignored) */
/* bit 3: clear to send (ignored) */
/* bit 4: framing error (ignored) */
/* bit 5: overrun */
/* bit 6: parity error */
/* bit 7: interrupt */
LOG("%s %f kbd_acia_r: status $%02X (rdrf=%i, tdre=%i, ovrn=%i, pe=%i, irq=%i)\n",
machine().describe_context(), machine().time().as_double(), m_kbd_status,
(m_kbd_status & ACIA_6850_RDRF) ? 1 : 0,
(m_kbd_status & ACIA_6850_TDRE) ? 1 : 0,
(m_kbd_status & ACIA_6850_OVRN) ? 1 : 0,
(m_kbd_status & ACIA_6850_PE) ? 1 : 0,
(m_kbd_status & ACIA_6850_irq) ? 1 : 0 );
return m_kbd_status;
case 1: /* get input data */
if ( !machine().side_effects_disabled() )
{
m_kbd_status &= ~(ACIA_6850_irq | ACIA_6850_PE);
if ( m_kbd_overrun )
m_kbd_status |= ACIA_6850_OVRN;
else
m_kbd_status &= ~(ACIA_6850_OVRN | ACIA_6850_RDRF);
m_kbd_overrun = 0;
LOGMASKED(LOG_KBD, "%s %f kbd_acia_r: read data $%02X\n", machine().describe_context(), machine().time().as_double(), m_kbd_in);
update_irq();
}
return m_kbd_in;
default:
LOGMASKED(LOG_ERRORS, "%s kbd_acia_r: invalid offset %i\n", machine().describe_context(), offset);
return 0;
}
}
void to9_keyboard_device::kbd_acia_w(offs_t offset, uint8_t data)
{
/* ACIA 6850 registers */
switch ( offset )
{
case 0: /* set control */
/* bits 0-1: clock divide (ignored) or reset */
if ( (data & 3) == 3 )
{
/* reset */
m_kbd_overrun = 0;
m_kbd_status = ACIA_6850_TDRE;
m_kbd_intr = 0;
LOGMASKED(LOG_KBD, "%s %f kbd_acia_w: reset (data=$%02X)\n", machine().describe_context(), machine().time().as_double(), data);
}
else
{
/* bits 2-4: parity */
if ( (data & 0x18) == 0x10 )
m_kbd_parity = 2;
else
m_kbd_parity = (data >> 2) & 1;
/* bits 5-6: interrupt on transmit */
/* bit 7: interrupt on receive */
m_kbd_intr = data >> 5;
LOGMASKED(LOG_KBD, "%s %f kbd_acia_w: set control to $%02X (parity=%i, intr in=%i out=%i)\n",
machine().describe_context(), machine().time().as_double(),
data, m_kbd_parity, m_kbd_intr >> 2,
(m_kbd_intr & 3) ? 1 : 0);
}
update_irq();
break;
case 1: /* output data */
m_kbd_status &= ~(ACIA_6850_irq | ACIA_6850_TDRE);
update_irq();
/* TODO: 1 ms delay here ? */
m_kbd_status |= ACIA_6850_TDRE; /* data transmit ready again */
update_irq();
switch ( data )
{
case 0xF8:
/* reset */
m_kbd_caps = 1;
m_kbd_periph = 0;
m_kbd_pad = 0;
break;
case 0xF9: m_kbd_caps = 1; break;
case 0xFA: m_kbd_caps = 0; break;
case 0xFB: m_kbd_pad = 1; break;
case 0xFC: m_kbd_pad = 0; break;
case 0xFD: m_kbd_periph = 1; break;
case 0xFE: m_kbd_periph = 0; break;
default:
LOGMASKED(LOG_ERRORS, "%s %f kbd_acia_w: unknown kbd command %02X\n", machine().describe_context(), machine().time().as_double(), data);
}
m_caps_led = !m_kbd_caps;
LOG("%s %f kbd_acia_w: kbd command %02X (caps=%i, pad=%i, periph=%i)\n",
machine().describe_context(), machine().time().as_double(), data,
m_kbd_caps, m_kbd_pad, m_kbd_periph);
break;
default:
LOGMASKED(LOG_ERRORS, "%s kbd_acia_w: invalid offset %i (data=$%02X) \n", machine().describe_context(), offset, data);
}
}
/* send a key to the CPU, 8-bit + parity bit (0=even, 1=odd)
note: parity is not used as a checksum but to actually transmit a 9-th bit
of information!
*/
void to9_keyboard_device::send( uint8_t data, int parity )
{
if ( m_kbd_status & ACIA_6850_RDRF )
{
/* overrun will be set when the current valid byte is read */
m_kbd_overrun = 1;
LOGMASKED(LOG_KBD, "%f send: overrun => drop data=$%02X, parity=%i\n", machine().time().as_double(), data, parity);
}
else
{
/* valid byte */
m_kbd_in = data;
m_kbd_status |= ACIA_6850_RDRF; /* raise data received flag */
if ( m_kbd_parity == 2 || m_kbd_parity == parity )
m_kbd_status &= ~ACIA_6850_PE; /* parity OK */
else
m_kbd_status |= ACIA_6850_PE; /* parity error */
LOGMASKED(LOG_KBD, "%f send: data=$%02X, parity=%i, status=$%02X\n", machine().time().as_double(), data, parity, m_kbd_status);
}
update_irq();
}
/* keycode => TO9 code (extended ASCII), shifted and un-shifted */
static const int to9_kbd_code[80][2] =
{
{ 145, 150 }, { '_', '6' }, { 'Y', 'Y' }, { 'H', 'H' },
{ 11, 11 }, { 9, 9 }, { 30, 12 }, { 'N', 'N' },
{ 146, 151 }, { '(', '5' }, { 'T', 'T' }, { 'G', 'G' },
{ '=', '+' }, { 8, 8 }, { 28, 28 }, { 'B', 'B' },
{ 147, 152 }, { '\'', '4' }, { 'R', 'R' }, { 'F', 'F' },
{ 22, 22 }, { 155, 155 }, { 29, 127 }, { 'V', 'V' },
{ 148, 153 }, { '"', '3' }, { 'E', 'E' }, { 'D', 'D' },
{ 161, 161 }, { 158, 158 },
{ 154, 154 }, { 'C', 'C' },
{ 144, 149 }, { 128, '2' }, { 'Z', 'Z' }, { 'S', 'S' },
{ 162, 162 }, { 156, 156 },
{ 164, 164 }, { 'X', 'X' },
{ '#', '@' }, { '*', '1' }, { 'A', 'A' }, { 'Q', 'Q' },
{ '[', '{' }, { 159, 159 }, { 160, 160 }, { 'W', 'W' },
{ 2, 2 }, { 129, '7' }, { 'U', 'U' }, { 'J', 'J' },
{ ' ', ' ' }, { 163, 163 }, { 165, 165 },
{ ',', '?' },
{ 0, 0 }, { '!', '8' }, { 'I', 'I' }, { 'K', 'K' },
{ '$', '&' }, { 10, 10 }, { ']', '}' }, { ';', '.' },
{ 0, 0 }, { 130, '9' }, { 'O', 'O' }, { 'L', 'L' },
{ '-', '\\' }, { 132, '%' }, { 13, 13 }, { ':', '/' },
{ 0, 0 }, { 131, '0' }, { 'P', 'P' }, { 'M', 'M' },
{ ')', 134 }, { '^', 133 }, { 157, 157 }, { '>', '<' }
};
/* returns the ASCII code for the key, or 0 for no key */
int to9_keyboard_device::get_key()
{
int control = ! (m_io_keyboard[7]->read() & 1);
int shift = ! (m_io_keyboard[9]->read() & 1);
int key = -1, line, bit;
uint8_t port;
for ( line = 0; line < 10; line++ )
{
port = m_io_keyboard[line]->read();
if ( line == 7 || line == 9 )
port |= 1; /* shift & control */
/* TODO: correct handling of simultaneous keystokes:
return the new key preferably & disable repeat
*/
for ( bit = 0; bit < 8; bit++ )
{
if ( ! (port & (1 << bit)) )
key = line * 8 + bit;
}
}
if ( key == -1 )
{
m_kbd_last_key = 0xff;
m_kbd_key_count = 0;
return 0;
}
else if ( key == 64 )
{
/* caps lock */
if ( m_kbd_last_key == key )
return 0; /* no repeat */
m_kbd_last_key = key;
m_kbd_caps = !m_kbd_caps;
m_caps_led = !m_kbd_caps;
return 0;
}
else
{
int asc;
asc = to9_kbd_code[key][shift];
if ( ! asc ) return 0;
/* keypad */
if ( ! m_kbd_pad ) {
if ( asc >= 154 && asc <= 163 )
asc += '0' - 154;
else if ( asc == 164 )
asc = '.';
else if ( asc == 165 )
asc = 13;
}
/* shifted letter */
if ( asc >= 'A' && asc <= 'Z' && ( ! m_kbd_caps ) && ( ! shift ) )
asc += 'a' - 'A';
/* control */
if ( control )
asc &= ~0x40;
if ( key == m_kbd_last_key )
{
/* repeat */
m_kbd_key_count++;
if ( m_kbd_key_count < TO9_KBD_REPEAT_DELAY || (m_kbd_key_count - TO9_KBD_REPEAT_DELAY) % TO9_KBD_REPEAT_PERIOD )
return 0;
LOGMASKED(LOG_KBD, "to9_kbd_get_key: repeat key $%02X '%c'\n", asc, asc);
return asc;
}
else
{
m_kbd_last_key = key;
m_kbd_key_count = 0;
LOGMASKED(LOG_KBD, "to9_kbd_get_key: key down $%02X '%c'\n", asc, asc);
return asc;
}
}
}
TIMER_CALLBACK_MEMBER(to9_keyboard_device::timer_cb)
{
if ( m_kbd_periph )
{
/* peripheral mode: every 10 ms we send 4 bytes */
switch ( m_kbd_byte_count )
{
case 0: /* key */
send( get_key(), 0 );
break;
case 1: /* x axis */
{
int newx = m_io_mouse_x.read_safe(0);
uint8_t data = ( (newx - m_mouse_x) & 0xf ) - 8;
send( data, 1 );
m_mouse_x = newx;
break;
}
case 2: /* y axis */
{
int newy = m_io_mouse_y.read_safe(0);
uint8_t data = ( (newy - m_mouse_y) & 0xf ) - 8;
send( data, 1 );
m_mouse_y = newy;
break;
}
case 3: /* axis overflow & buttons */
{
int b = m_io_mouse_button.read_safe(~0);
uint8_t data = 0;
if ( b & 1 ) data |= 1;
if ( b & 2 ) data |= 4;
send( data, 1 );
break;
}
}
m_kbd_byte_count = ( m_kbd_byte_count + 1 ) & 3;
m_kbd_timer->adjust(m_kbd_byte_count ? TO9_KBD_BYTE_SPACE : TO9_KBD_END_SPACE);
}
else
{
int key = get_key();
/* keyboard mode: send a byte only if a key is down */
if ( key )
send( key, 0 );
m_kbd_timer->adjust(TO9_KBD_POLL_PERIOD);
}
}
void to9_keyboard_device::device_reset()
{
m_kbd_overrun = 0; /* no byte lost */
m_kbd_status = ACIA_6850_TDRE; /* clear to transmit */
m_kbd_intr = 0; /* interrupt disabled */
m_kbd_caps = 1;
m_kbd_periph = 0;
m_kbd_pad = 0;
m_kbd_byte_count = 0;
m_caps_led = !m_kbd_caps;
m_kbd_key_count = 0;
m_kbd_last_key = 0xff;
update_irq();
m_kbd_timer->adjust(TO9_KBD_POLL_PERIOD);
}
void to9_keyboard_device::device_start()
{
m_caps_led.resolve();
m_kbd_timer = timer_alloc(FUNC(to9_keyboard_device::timer_cb), this);
save_item(NAME(m_kbd_parity));
save_item(NAME(m_kbd_intr));
save_item(NAME(m_kbd_in));
save_item(NAME(m_kbd_status));
save_item(NAME(m_kbd_overrun));
save_item(NAME(m_kbd_last_key));
save_item(NAME(m_kbd_key_count));
save_item(NAME(m_kbd_caps));
save_item(NAME(m_kbd_periph));
save_item(NAME(m_kbd_pad));
save_item(NAME(m_kbd_byte_count));
save_item(NAME(m_mouse_x));
save_item(NAME(m_mouse_y));
}

121
src/mame/thomson/to_kbd.h Normal file
View File

@ -0,0 +1,121 @@
// license:BSD-3-Clause
// copyright-holders:Antoine Mine
/**********************************************************************
Copyright (C) Antoine Mine' 2006
Thomson TO8 built-in keyboard & TO9 detached keyboard
**********************************************************************/
#ifndef MAME_THOMSON_TO_KBD_H
#define MAME_THOMSON_TO_KBD_H
#pragma once
class to8_keyboard_device : public device_t
{
public:
to8_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0U);
auto data_cb() { return m_data_cb.bind(); }
int ktest_r();
int caps_r() { return m_kbd_caps; }
void set_ack( int data );
protected:
virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
virtual ioport_constructor device_input_ports() const override ATTR_COLD;
virtual void device_reset() override ATTR_COLD;
virtual void device_start() override ATTR_COLD;
private:
devcb_write_line m_data_cb;
required_ioport_array<10> m_io_keyboard;
output_finder<> m_caps_led;
uint8_t m_kbd_ack = 0; /* 1 = cpu inits / accepts transfers */
uint16_t m_kbd_data = 0; /* data to transmit */
uint16_t m_kbd_step = 0; /* transmission automaton state */
uint8_t m_kbd_last_key = 0; /* last key (for repetition) */
uint32_t m_kbd_key_count = 0; /* keypress time (for repetition) */
uint8_t m_kbd_caps = 0; /* caps lock */
emu_timer* m_kbd_timer = nullptr; /* bit-send */
emu_timer* m_kbd_signal = nullptr; /* signal from CPU */
TIMER_CALLBACK_MEMBER( timer_cb );
int get_key();
void timer_func();
};
class to9_keyboard_device : public device_t
{
public:
to9_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0U);
auto irq_cb() { return m_irq_cb.bind(); }
uint8_t kbd_acia_r(offs_t offset);
void kbd_acia_w(offs_t offset, uint8_t data);
int ktest_r();
protected:
to9_keyboard_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock = 0U);
virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
virtual ioport_constructor device_input_ports() const override ATTR_COLD;
virtual void device_reset() override ATTR_COLD;
virtual void device_start() override ATTR_COLD;
private:
devcb_write_line m_irq_cb;
required_ioport_array<10> m_io_keyboard;
optional_ioport m_io_mouse_x;
optional_ioport m_io_mouse_y;
optional_ioport m_io_mouse_button;
output_finder<> m_caps_led;
uint8_t m_kbd_parity = 0; /* 0=even, 1=odd, 2=no parity */
uint8_t m_kbd_intr = 0; /* interrupt mode */
uint8_t m_kbd_in = 0; /* data from keyboard */
uint8_t m_kbd_status = 0; /* status */
uint8_t m_kbd_overrun = 0; /* character lost */
uint8_t m_kbd_periph = 0; /* peripheral mode */
uint8_t m_kbd_byte_count = 0; /* byte-count in peripheral mode */
uint16_t m_mouse_x = 0;
uint16_t m_mouse_y = 0;
uint8_t m_kbd_last_key = 0; /* for key repetition */
uint16_t m_kbd_key_count = 0;
uint8_t m_kbd_caps = 0; /* caps-lock */
uint8_t m_kbd_pad = 0; /* keypad outputs special codes */
emu_timer* m_kbd_timer = nullptr;
TIMER_CALLBACK_MEMBER( timer_cb );
void update_irq();
void send( uint8_t data, int parity );
int get_key();
};
class to9p_keyboard_device : public to9_keyboard_device
{
public:
to9p_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0U);
protected:
virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
virtual ioport_constructor device_input_ports() const override ATTR_COLD;
};
// device type declarations
DECLARE_DEVICE_TYPE(TO8_KEYBOARD, to8_keyboard_device)
DECLARE_DEVICE_TYPE(TO9_KEYBOARD, to9_keyboard_device)
DECLARE_DEVICE_TYPE(TO9P_KEYBOARD, to9p_keyboard_device)
#endif // MAME_THOMSON_TO_KBD_H