// license:BSD-3-Clause // copyright-holders:R. Belmont /********************************************************************* titan3plus3.cpp Implemention of the Titan "/// Plus //"" card. By R. Belmont, based on reverse-engineering by Rob Justice. This extends the Apple ///'s II emulation to include a 128K extended language card and Apple II compatible game I/O. The card enables itself if the /// is at 1 MHz and accesses the card's $CNxx ROM space. If the /// goes back to 2 MHz mode at any time, the card disables itself. Address ranges this messes with: $C05X - passed through to ///, but also used for annunciators on A2 gameio $C06X - /// doesn't see when card enabled, A2 paddles $C07X - passed through to ///, but also used for A2 paddle reset $C08X - unused by /// (language card control) $D000-$FFFF: usual (for A2) language card space. Not so usual for ///. *********************************************************************/ #include "emu.h" #include "titan3plus2.h" /*************************************************************************** PARAMETERS ***************************************************************************/ //************************************************************************** // GLOBAL VARIABLES //************************************************************************** DEFINE_DEVICE_TYPE(A2BUS_TITAN3PLUS2, a2bus_titan3plus2_device, "titan3p2", "Titan /// Plus //") /*************************************************************************** FUNCTION PROTOTYPES ***************************************************************************/ //************************************************************************** // LIVE DEVICE //************************************************************************** void a2bus_titan3plus2_device::device_add_mconfig(machine_config &config) { APPLE2_GAMEIO(config, m_gameio, apple2_gameio_device::default_options, nullptr); } a2bus_titan3plus2_device::a2bus_titan3plus2_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, const XTAL &clock) : device_t(mconfig, type, tag, owner, clock), device_a2bus_card_interface(mconfig, *this), m_gameio(*this, "gameio"), m_inh_state(0), m_last_offset(0), m_dxxx_bank(0), m_main_bank(0), m_enabled(false) { } a2bus_titan3plus2_device::a2bus_titan3plus2_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : a2bus_titan3plus2_device(mconfig, A2BUS_TITAN3PLUS2, tag, owner, clock) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void a2bus_titan3plus2_device::device_start() { memset(m_ram, 0, 128*1024); save_item(NAME(m_inh_state)); save_item(NAME(m_ram)); save_item(NAME(m_dxxx_bank)); save_item(NAME(m_main_bank)); save_item(NAME(m_last_offset)); save_item(NAME(m_enabled)); m_x_calibration = m_y_calibration = attotime::from_nsec(10800).as_double(); } void a2bus_titan3plus2_device::device_reset() { m_inh_state = INH_NONE; m_dxxx_bank = 0; m_main_bank = 0; m_last_offset = -1; } void a2bus_titan3plus2_device::do_io(int offset) { int old_inh_state = m_inh_state; switch (offset) { case 0x1: case 0x3: case 0x9: case 0xb: if (offset != m_last_offset) { m_last_offset = offset; return; } break; } m_last_offset = offset; if (offset & 4) { switch (offset) { case 0x4: m_main_bank = 0x00000; break; case 0x5: m_main_bank = 0x04000; break; case 0x6: m_main_bank = 0x08000; break; case 0x7: m_main_bank = 0x0c000; break; case 0xc: m_main_bank = 0x10000; break; case 0xd: m_main_bank = 0x14000; break; case 0xe: m_main_bank = 0x18000; break; case 0xf: m_main_bank = 0x1c000; break; } } else { m_inh_state = INH_NONE; m_dxxx_bank = 0; if (offset & 0x1) { m_inh_state |= INH_WRITE; } switch(offset & 0x03) { case 0x00: case 0x03: m_inh_state |= INH_READ; break; } if (!(offset & 8)) { m_dxxx_bank = 0x1000; } } if (m_inh_state != old_inh_state) { recalc_slot_inh(); } #if 0 printf("LC: (ofs %x) new state %c%c dxxx=%04x main=%05x\n", offset, (m_inh_state & INH_READ) ? 'R' : 'x', (m_inh_state & INH_WRITE) ? 'W' : 'x', m_dxxx_bank, m_main_bank); #endif } /*------------------------------------------------- read_c08x - called for reads from the LC switches -------------------------------------------------*/ uint8_t a2bus_titan3plus2_device::read_c08x(uint8_t offset) { if (!machine().side_effects_disabled()) { do_io(offset & 0xf); } return 0xff; } /*------------------------------------------------- write_c0nx - called for writes to the LC switches -------------------------------------------------*/ void a2bus_titan3plus2_device::write_c08x(uint8_t offset, uint8_t data) { do_io(offset & 0xf); } uint8_t a2bus_titan3plus2_device::read_inh_rom(uint16_t offset) { if (offset < 0xd000) { if ((offset >= 0xc080) && (offset <= 0xc08f)) { return read_c08x(offset & 0xf); } if ((offset >= 0xc060) && (offset <= 0xc06f)) { switch (offset & 0x7) { case 1: // button 0 return (m_gameio->sw0_r() ? 0x80 : 0); case 2: // button 1 return (m_gameio->sw1_r() ? 0x80 : 0); case 3: // button 2 return (m_gameio->sw2_r() ? 0x80 : 0); case 4: // joy 1 X axis if (!m_gameio->is_device_connected()) return 0x80; return ((machine().time().as_double() < m_joystick_x1_time) ? 0x80 : 0); case 5: // joy 1 Y axis if (!m_gameio->is_device_connected()) return 0x80; return ((machine().time().as_double() < m_joystick_y1_time) ? 0x80 : 0); case 6: // joy 2 X axis if (!m_gameio->is_device_connected()) return 0x80; return ((machine().time().as_double() < m_joystick_x2_time) ? 0x80 : 0); case 7: // joy 2 Y axis if (!m_gameio->is_device_connected()) return 0x80; return ((machine().time().as_double() < m_joystick_y2_time) ? 0x80 : 0); } } } else { if (offset < 0xe000) { return m_ram[(offset & 0xfff) + m_dxxx_bank + m_main_bank]; } return m_ram[(offset & 0x1fff) + 0x2000 + m_main_bank]; } return 0xff; } void a2bus_titan3plus2_device::write_inh_rom(uint16_t offset, uint8_t data) { if (offset < 0xd000) { if ((offset >= 0xc080) && (offset <= 0xc08f)) { write_c08x(offset & 0xf, data); return; } } else { if (offset < 0xe000) { m_ram[(offset & 0xfff) + m_dxxx_bank + m_main_bank] = data; return; } m_ram[(offset & 0x1fff) + 0x2000 + m_main_bank] = data; } } u8 a2bus_titan3plus2_device::read_cnxx(u8 offset) { m_enabled = true; return 0xff; } // returns if we want to /INH a read or write to a specific address bool a2bus_titan3plus2_device::inh_check(u16 offset, bool bIsWrite) { if (!m_enabled) { return false; } // handle LC if (offset >= 0xd000) { if ((bIsWrite) && (m_inh_state & INH_WRITE)) { return true; } if (!(bIsWrite) && (m_inh_state & INH_READ)) { return true; } } if ((offset >= 0xc080) && (offset <= 0xc08f)) { return true; } if ((offset >= 0xc070) && (offset <= 0xc07f)) { m_joystick_x1_time = machine().time().as_double() + m_x_calibration * m_gameio->pdl0_r(); m_joystick_y1_time = machine().time().as_double() + m_y_calibration * m_gameio->pdl1_r(); m_joystick_x2_time = machine().time().as_double() + m_x_calibration * m_gameio->pdl2_r(); m_joystick_y2_time = machine().time().as_double() + m_y_calibration * m_gameio->pdl3_r(); return false; // Apple /// should still handle this } if ((offset >= 0xc060) && (offset <= 0xc06f)) { return true; } if ((offset >= 0xc058) && (offset <= 0xc05f)) { switch (offset & 0xf) { case 8: case 9: m_gameio->an0_w(offset & 1); break; case 0xa: case 0xb: m_gameio->an1_w(offset & 1); break; case 0xc: case 0xd: m_gameio->an2_w(offset & 1); break; case 0xe: case 0xf: m_gameio->an3_w(offset & 1); break; } return false; // Apple /// should still see this } return false; }