diff options
Diffstat (limited to 'src/devices/bus/a2bus/mouse.cpp')
-rw-r--r-- | src/devices/bus/a2bus/mouse.cpp | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/src/devices/bus/a2bus/mouse.cpp b/src/devices/bus/a2bus/mouse.cpp new file mode 100644 index 00000000000..d044fbcdad2 --- /dev/null +++ b/src/devices/bus/a2bus/mouse.cpp @@ -0,0 +1,531 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/********************************************************************* + + mouse.c + + Implemention of the Apple II Mouse Card + + Apple II Mouse Interface PCB + Apple 1983 + + This is a mouse interface for the Apple II + + PCB Layout + ---------- + apple computer + MOUSE INTERFACE + 670-0030-C (C) 1983 + Printed on back side - MOUSE INTERFACE 820-0104-B (C) 1983 APPLE COMPUTER + |-----------------------------------| + | PAL16R4 6821 | + | | + | J1 | + | 74SC245 | + | 68705P3 8516 | + | 74LS74 X1 X2 | + |-------------------| |-| + |-------------| + + Notes: + J1 - 9 pin flat cable with female DB9 connector + 68705P3 - Motorola MC68705P3 microcontroller (DIP28) labelled '341-0269 (C) APPLE' + PCB printed '(C) APPLE 1983 341-0269 or 342-0285' + 8516 - Fujitsu MB8516 2k x8-bit EPROM (DIP24) labelled '341-0270-C (C) APPLE 1983' + PCB printed '(C) APPLE 1983 342-0270' + PAL16R4 - MMI PAL16R4ACN (DIP20) marked '341-0268-A' + PCB printed '(C) APPLE 1983 342-0268' + 6821 - AMI 6821 Peripheral Interface Adapter (DIP40) + X1/X2 - Jumper pads. X1 is open, X2 is closed. + + + Hookup notes: + PIA port A connects to 68705 port A in its entirety (bi-directional) + PIA PB4-PB7 connects to 68705 PC0-3 (bi-directional) + PIA PB0 is 'sync latch' + PIA PB1 is A8 on the EPROM + PIA PB2 is A9 on the EPROM + PIA PB3 is A10 on the EPROM + + 68705 PB0 is mouse X1 + 68705 PB1 is mouse X0 + 68705 PB2 is mouse Y0 + 68705 PB3 is mouse Y1 + 68705 PB4 and 5 are N/C + 68705 PB6 is IRQ for the slot + 68705 PB7 is the mouse button + + 68705 is clocked at 2M + PIA is clocked at 1M + + See the schematic at: + http://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Interface%20Cards/Digitizers/Apple%20Mouse%20Interface%20Card/Schematics/ + +*********************************************************************/ + +#include "mouse.h" + +/*************************************************************************** + PARAMETERS +***************************************************************************/ + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type A2BUS_MOUSE = &device_creator<a2bus_mouse_device>; + +#define MOUSE_ROM_REGION "a2mse_rom" +#define MOUSE_PIA_TAG "a2mse_pia" +#define MOUSE_MCU_TAG "a2mse_mcu" +#define MOUSE_MCU_ROM "a2mse_mcurom" + +#define MOUSE_BUTTON_TAG "a2mse_button" +#define MOUSE_XAXIS_TAG "a2mse_x" +#define MOUSE_YAXIS_TAG "a2mse_y" + +#define TIMER_68705 0 +#define TIMER_QUADRATURE 1 + +static ADDRESS_MAP_START( mcu_mem, AS_PROGRAM, 8, a2bus_mouse_device ) + ADDRESS_MAP_GLOBAL_MASK(0x7ff) + AM_RANGE(0x0000, 0x0000) AM_READWRITE(mcu_port_a_r, mcu_port_a_w) + AM_RANGE(0x0001, 0x0001) AM_READWRITE(mcu_port_b_r, mcu_port_b_w) + AM_RANGE(0x0002, 0x0002) AM_READWRITE(mcu_port_c_r, mcu_port_c_w) + AM_RANGE(0x0004, 0x0004) AM_WRITE(mcu_ddr_a_w) + AM_RANGE(0x0005, 0x0005) AM_WRITE(mcu_ddr_b_w) + AM_RANGE(0x0006, 0x0006) AM_WRITE(mcu_ddr_c_w) + AM_RANGE(0x0008, 0x0009) AM_READWRITE(mcu_timer_r, mcu_timer_w) + AM_RANGE(0x0010, 0x007f) AM_RAM + AM_RANGE(0x0080, 0x07ff) AM_ROM AM_REGION(MOUSE_MCU_ROM, 0x80) +ADDRESS_MAP_END + +MACHINE_CONFIG_FRAGMENT( mouse ) + MCFG_CPU_ADD(MOUSE_MCU_TAG, M68705, 2043600) + MCFG_CPU_PROGRAM_MAP(mcu_mem) + + MCFG_DEVICE_ADD(MOUSE_PIA_TAG, PIA6821, 1021800) + MCFG_PIA_READPA_HANDLER(READ8(a2bus_mouse_device, pia_in_a)) + MCFG_PIA_READPB_HANDLER(READ8(a2bus_mouse_device, pia_in_b)) + MCFG_PIA_WRITEPA_HANDLER(WRITE8(a2bus_mouse_device, pia_out_a)) + MCFG_PIA_WRITEPB_HANDLER(WRITE8(a2bus_mouse_device, pia_out_b)) + MCFG_PIA_IRQA_HANDLER(WRITELINE(a2bus_mouse_device, pia_irqa_w)) + MCFG_PIA_IRQB_HANDLER(WRITELINE(a2bus_mouse_device, pia_irqb_w)) +MACHINE_CONFIG_END + +ROM_START( mouse ) + ROM_REGION(0x800, MOUSE_ROM_REGION, 0) + ROM_LOAD( "341-0270-c.4b", 0x000000, 0x000800, CRC(0bcd1e8e) SHA1(3a9d881a8a8d30f55b9719aceebbcf717f829d6f) ) + + ROM_REGION(0x800, MOUSE_MCU_ROM, 0) + ROM_LOAD( "341-0269.2b", 0x000000, 0x000800, CRC(94067f16) SHA1(3a2baa6648efe4456d3ec3721216e57c64f7acfc) ) + + ROM_REGION(0xc00, "pal", 0) + ROM_LOAD( "mmi_pal16r4a(jedec).2a", 0x000000, 0x000b04, CRC(1d620ee5) SHA1(5aa9a515c919ff7a18878649cac5d44f0c2abf28) ) + ROM_LOAD( "mmi_pal16r4a(binary).2a", 0x000000, 0x000100, CRC(1da5c745) SHA1(ba267b69a2fda2a2348b140979ece562411bb37b) ) +ROM_END + +static INPUT_PORTS_START( mouse ) + PORT_START(MOUSE_BUTTON_TAG) /* Mouse - button */ + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Mouse Button") PORT_CODE(MOUSECODE_BUTTON1) + + PORT_START(MOUSE_XAXIS_TAG) /* Mouse - X AXIS */ + PORT_BIT( 0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(40) PORT_KEYDELTA(0) PORT_PLAYER(1) + + PORT_START(MOUSE_YAXIS_TAG) /* Mouse - Y AXIS */ + PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(40) PORT_KEYDELTA(0) PORT_PLAYER(1) +INPUT_PORTS_END + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +//------------------------------------------------- +// input_ports - device-specific input ports +//------------------------------------------------- + +ioport_constructor a2bus_mouse_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( mouse ); +} + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor a2bus_mouse_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( mouse ); +} + +//------------------------------------------------- +// rom_region - device-specific ROM region +//------------------------------------------------- + +const rom_entry *a2bus_mouse_device::device_rom_region() const +{ + return ROM_NAME( mouse ); +} + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +a2bus_mouse_device::a2bus_mouse_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : + device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_a2bus_card_interface(mconfig, *this), + m_pia(*this, MOUSE_PIA_TAG), + m_mcu(*this, MOUSE_MCU_TAG), + m_mouseb(*this, MOUSE_BUTTON_TAG), + m_mousex(*this, MOUSE_XAXIS_TAG), + m_mousey(*this, MOUSE_YAXIS_TAG) +{ + m_started = false; + m_rom_bank = 0; +} + +a2bus_mouse_device::a2bus_mouse_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, A2BUS_MOUSE, "Apple II Mouse Card", tag, owner, clock, "a2mouse", __FILE__), + device_a2bus_card_interface(mconfig, *this), + m_pia(*this, MOUSE_PIA_TAG), + m_mcu(*this, MOUSE_MCU_TAG), + m_mouseb(*this, MOUSE_BUTTON_TAG), + m_mousex(*this, MOUSE_XAXIS_TAG), + m_mousey(*this, MOUSE_YAXIS_TAG) +{ + m_started = false; + m_rom_bank = 0; +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void a2bus_mouse_device::device_start() +{ + // set_a2bus_device makes m_slot valid + set_a2bus_device(); + + m_rom = device().machine().root_device().memregion(this->subtag(MOUSE_ROM_REGION).c_str())->base(); + + // allocate two timers: one for the 68705, one for the quadrature magic + m_timer = timer_alloc(TIMER_68705, NULL); + m_read_timer = timer_alloc(TIMER_QUADRATURE, NULL); + m_timer->adjust(attotime::never, TIMER_68705); + m_read_timer->adjust(attotime::never, TIMER_QUADRATURE); + + // get 68705P3 mask option byte + m_mask_option = m_rom[0x784]; + + // register save state variables + save_item(NAME(m_ddr_a)); + save_item(NAME(m_ddr_b)); + save_item(NAME(m_ddr_c)); + save_item(NAME(m_port_a_out)); + save_item(NAME(m_port_b_out)); + save_item(NAME(m_port_c_out)); + save_item(NAME(m_port_a_in)); + save_item(NAME(m_port_b_in)); + save_item(NAME(m_port_c_in)); + save_item(NAME(m_timer_cnt)); + save_item(NAME(m_timer_ctl)); + save_item(NAME(last_mx)); + save_item(NAME(last_my)); + save_item(NAME(count_x)); + save_item(NAME(count_y)); +} + +void a2bus_mouse_device::device_reset() +{ + m_started = true; + m_rom_bank = 0; + last_mx = last_my = count_x = count_y = 0; + m_timer_cnt = 0xff; + m_timer_ctl = 0x40; // disable interrupt, everything else clear + m_port_a_in = 0; + m_port_b_in = 0x80; + m_port_c_in = 0; + + // are we emulating the mask part with a semi-programmable timer? + if (m_mask_option & 0x40) + { + m_timer_ctl |= m_mask_option & 0x17; + } + + m_read_timer->adjust(attotime::from_hz(600.0), TIMER_QUADRATURE, attotime::from_hz(600.0)); +} + +/*------------------------------------------------- + read_c0nx - called for reads from this card's c0nx space +-------------------------------------------------*/ + +UINT8 a2bus_mouse_device::read_c0nx(address_space &space, UINT8 offset) +{ + return m_pia->read(space, offset & 3); +} + + +/*------------------------------------------------- + write_c0nx - called for writes to this card's c0nx space +-------------------------------------------------*/ + +void a2bus_mouse_device::write_c0nx(address_space &space, UINT8 offset, UINT8 data) +{ + m_pia->write(space, offset & 3, data); +} + +/*------------------------------------------------- + read_cnxx - called for reads from this card's cnxx space +-------------------------------------------------*/ + +UINT8 a2bus_mouse_device::read_cnxx(address_space &space, UINT8 offset) +{ + return m_rom[offset+m_rom_bank]; +} + +READ8_MEMBER(a2bus_mouse_device::pia_in_a) +{ + return m_port_a_out; +} + +READ8_MEMBER(a2bus_mouse_device::pia_in_b) +{ + return (m_port_c_out << 4); +} + +WRITE8_MEMBER(a2bus_mouse_device::pia_out_a) +{ + m_port_a_in = data; +} + +WRITE8_MEMBER(a2bus_mouse_device::pia_out_b) +{ + m_port_c_in &= 0xf0; + m_port_c_in |= ((data >> 4) & 0xf); + + m_rom_bank = (data & 0xe) << 7; +} + +WRITE_LINE_MEMBER(a2bus_mouse_device::pia_irqa_w) +{ +} + +WRITE_LINE_MEMBER(a2bus_mouse_device::pia_irqb_w) +{ +} + +READ8_MEMBER(a2bus_mouse_device::mcu_port_a_r) +{ + return (m_port_a_out & m_ddr_a) | (m_port_a_in & ~m_ddr_a); +} + +WRITE8_MEMBER(a2bus_mouse_device::mcu_port_a_w) +{ + m_port_a_out = data; +} + +WRITE8_MEMBER(a2bus_mouse_device::mcu_ddr_a_w) +{ + m_ddr_a = data; +} + +READ8_MEMBER(a2bus_mouse_device::mcu_port_b_r) +{ + UINT8 b_in = m_port_b_in; + + // clear the gates, leave everything else alone between pulses + m_port_b_in &= 0x85; + + return (m_port_b_out & m_ddr_b) | (b_in & ~m_ddr_b); +} + +WRITE8_MEMBER(a2bus_mouse_device::mcu_port_b_w) +{ + m_port_b_out = data; + + if (!(data & 0x40)) + { + raise_slot_irq(); + } + else + { + lower_slot_irq(); + } +} + +WRITE8_MEMBER(a2bus_mouse_device::mcu_ddr_b_w) +{ + m_ddr_b = data; +} + +READ8_MEMBER(a2bus_mouse_device::mcu_port_c_r) +{ + return (m_port_c_out & m_ddr_c) | (m_port_c_in & ~m_ddr_c); +} + +WRITE8_MEMBER(a2bus_mouse_device::mcu_port_c_w) +{ + m_port_c_out = data; +} + +WRITE8_MEMBER(a2bus_mouse_device::mcu_ddr_c_w) +{ + m_ddr_c = data; +} + +READ8_MEMBER(a2bus_mouse_device::mcu_timer_r) +{ + if (offset == 1) + { + return m_timer_ctl; + } + + return m_timer_cnt; +} + +WRITE8_MEMBER(a2bus_mouse_device::mcu_timer_w) +{ + static const int prescale[8] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + bool recalc = false; + + // offset 0 = timer data (counts down) + if (offset == 0) + { + m_timer_cnt = data; + recalc = true; + } + // offset 1 = timer control: b7 = IRQ, b6 = IRQ mask (1=suppress), + // b5 = input select (0=CPU clk, 1=ext), + // b4 = enable external timer input, + // b3 = clear, b2-b0 = scaler (1/2/4/8/16/32/64/128) + else + { + // clearing the interrupt? + if ((m_timer_ctl & 0x80) && !(data & 0x80)) + { + m_mcu->set_input_line(M68705_INT_TIMER, CLEAR_LINE); + } + + if (m_mask_option & 0x40) + { + m_timer_ctl &= 0x3f; + m_timer_ctl |= (data & 0xc0); + } + else + { + // if any parameters that affect the timer changed, recalc now + if ((data & 0x3f) != (m_timer_ctl & 0x3f)) + { + recalc = true; + } + + // if prescaler reset, recalc + if (data & 0x8) + { + recalc = true; + } + + m_timer_ctl = data; + } + + } + + if (recalc) + { + // recalculate the timer now + UINT32 m_ticks = 2043600 / 4; + m_ticks /= prescale[m_timer_ctl & 7]; + m_ticks /= (int)(m_timer_cnt + 1); + m_timer->adjust(attotime::from_hz((double)m_ticks), TIMER_68705, attotime::from_hz((double)m_ticks)); + } +} + +/* + X0 = direction, 0 = left, 1 = right + X1 = gate, must go 0/1 for each pixel moved + Y0 = direction, 0 = up, 1 = down + Y1 = gate, must go 0/1 for each pixel moved + + The direction must stay constant for a given train of gate pulses or the MCU will get confused. +*/ +void a2bus_mouse_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_68705) // 68705's built-in timer + { + m_timer_ctl |= 0x80; // indicate timer expired + if (!(m_timer_ctl & 0x40)) // if interrupt not suppressed, fire! + { + m_mcu->set_input_line(M68705_INT_TIMER, ASSERT_LINE); + } + } + else if (id == TIMER_QUADRATURE) + { + int new_mx, new_my; + m_port_b_in = 0x80; + + // update button now + if (m_mouseb->read()) m_port_b_in &= ~0x80; + + // read the axes + new_mx = m_mousex->read(); + new_my = m_mousey->read(); + + // did X change? + if (new_mx != last_mx) + { + int diff = new_mx - last_mx; + + /* check for wrap */ + if (diff > 0x80) + diff = 0x100-diff; + if (diff < -0x80) + diff = -0x100-diff; + + count_x += diff; + last_mx = new_mx; + } + + // did Y change? + if (new_my != last_my) + { + int diff = new_my - last_my; + + /* check for wrap */ + if (diff > 0x80) + diff = 0x100-diff; + if (diff < -0x80) + diff = -0x100-diff; + + count_y += diff; + last_my = new_my; + } + + if (count_x) + { + if (count_x < 0) + { + count_x++; + } + else + { + count_x--; + m_port_b_in |= 0x01; // X1 + } + m_port_b_in |= 0x02; // X0 + } + else if (count_y) + { + if (count_y < 0) + { + count_y++; + } + else + { + count_y--; + m_port_b_in |= 0x04; // Y0 + } + m_port_b_in |= 0x08; // Y1 + } + } +} |