diff options
Diffstat (limited to 'src/mame/apple/apple3_m.cpp')
-rw-r--r-- | src/mame/apple/apple3_m.cpp | 1478 |
1 files changed, 1478 insertions, 0 deletions
diff --git a/src/mame/apple/apple3_m.cpp b/src/mame/apple/apple3_m.cpp new file mode 100644 index 00000000000..35a8400309a --- /dev/null +++ b/src/mame/apple/apple3_m.cpp @@ -0,0 +1,1478 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods,R. Belmont +/*************************************************************************** + + machine/apple3.c + + Apple /// + + VIA #0 (D VIA) + CA1: IRQ from the MM58167 RTC + CA2: 1 if key pressed, 0 otherwise + CB1/CB2: connected to VBL + + Port A: Environment register (all bits out) + bit 7: 1 for 1 MHz, 0 for 2 MHz + bit 6: 1 for I/O at C000-CFFF + bit 5: 1 to enable video + bit 4: 1 to enable NMI/Reset + bit 3: 1 to write-protect RAM in system bank C000-FFFF + bit 2: 1 to force primary stack at 0100-01FF + bit 1: 1 for primary ROM, 0 for secondary (Apple III doesn't have a secondary ROM, so this should always be '1' when bit 0 is) + bit 0: 1 to enable ROM in F000-FFFF + + Port B: Zero page high 8 address bits, also MM58167 RTC register select (all bits out) + + VIA #1 (E VIA) + CA1: OR of all 4 slots' IRQ status + CA2: SW1 (Open Apple key?) + CB1: SW3/SCO + CB2: SER + + Port A: + bits 0-2: bank select for $2000-$9FFF range + bit 3: n/c + bit 4: slot 4 IRQ (in) + bit 5: slot 3 IRQ (in) + bit 6: Apple II mode trap output (out) + bit 7: IRQ status (in) (0 = IRQ, 1 = no IRQ) + + Port B: + bits 0-5: 6-bit audio DAC output + bit 6: screen blank + bit 7: OR of NMI from slots + +***************************************************************************/ + +#include "emu.h" +#include "apple3.h" + +#define LOG_MEMORY (1U << 1) + +#define VERBOSE (0) +#include "logmacro.h" + + +#define ENV_SLOWSPEED (0x80) +#define ENV_IOENABLE (0x40) +#define ENV_VIDENABLE (0x20) +#define ENV_NMIENABLE (0x10) +#define ENV_WRITEPROT (0x08) +#define ENV_STACK1XX (0x04) +#define ENV_PRIMARYROM (0x02) +#define ENV_ROMENABLE (0x01) + +// 14M / 14, but with every 65th cycle stretched which we cannot reasonably emulate +static constexpr XTAL APPLE2_CLOCK(1'021'800); + +// 2 MHz mode has every ~130th cycle stretched, and there are also dram refresh cycles +// happening which slows the clock back to 1MHz for some periods. +// There is mentioned in Apple documentation the result is ~1.8MHz +// PH0 Measured on a logic analyser when running a loop in ram with the screen off +// averaged over a long capture is 1.905MHz. +static constexpr XTAL A3_2MHZ_CLOCK(1'905'000); + +uint8_t apple3_state::apple3_c0xx_r(offs_t offset) +{ + uint8_t result = 0xFF; + device_a2bus_card_interface *slotdevice; + + switch(offset) + { + /* keystrobe */ + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + result = (m_transchar & 0x7f) | m_strobe; + break; + + /* modifier keys */ + case 0x08: case 0x09: case 0x0A: case 0x0B: + case 0x0C: case 0x0D: case 0x0E: case 0x0F: + { + uint8_t tmp = m_kbspecial->read(); + + result = 0x7c | (m_transchar & 0x80); + + if (m_strobe) + { + result |= 1; + } + + if (tmp & 0x06) + { + result |= 0x02; + } + if (tmp & 0x08) + { + result &= ~0x04; + } + if (tmp & 0x01) + { + result &= ~0x08; + } + if (tmp & 0x10) + { + result &= ~0x10; + } + if (tmp & 0x20) + { + result &= ~0x20; + } + } +// printf("modifier = %02x\n", result); + break; + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1A: case 0x1B: + case 0x1C: case 0x1D: case 0x1E: case 0x1F: + m_strobe = 0; + break; + + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2A: case 0x2B: + case 0x2C: case 0x2D: case 0x2E: case 0x2F: + if (!machine().side_effects_disabled()) + { + m_cnxx_slot = -1; + } + break; + + case 0x30: case 0x31: case 0x32: case 0x33: + case 0x34: case 0x35: case 0x36: case 0x37: + case 0x38: case 0x39: case 0x3A: case 0x3B: + case 0x3C: case 0x3D: case 0x3E: case 0x3F: + m_bell_state ^= 1; + m_bell->write(m_bell_state); + break; + + case 0x40: case 0x41: case 0x42: case 0x43: + case 0x44: case 0x45: case 0x46: case 0x47: + case 0x48: case 0x49: case 0x4A: case 0x4B: + case 0x4C: case 0x4D: + m_c040_time = 200; + break; + + case 0x4E: case 0x4F: // character RAM enable/disable + break; + + case 0x50: case 0x51: case 0x52: case 0x53: + case 0x54: case 0x55: case 0x56: case 0x57: + m_screen->update_partial(m_screen->vpos()); + /* graphics softswitches */ + if (offset & 1) + m_flags |= 1 << ((offset - 0x50) / 2); + else + m_flags &= ~(1 << ((offset - 0x50) / 2)); + break; + + case 0x58: + case 0x59: + case 0x5a: + case 0x5b: + case 0x5c: + case 0x5d: + case 0x5e: + case 0x5f: + pdl_handler(offset); + break; + + case 0x60: // joystick switch 0 + case 0x68: + result = (m_joybuttons->read() & 1) ? 0x80 : 0x00; + break; + + case 0x61: // joystick switch 1 (margin switch for Silentype) + case 0x69: + result = (m_joybuttons->read() & 4) ? 0x80 : 0x00; + break; + + case 0x62: // joystick switch 2 + case 0x6a: + result = (m_joybuttons->read() & 2) ? 0x80 : 0x00; + break; + + case 0x63: // joystick switch 3 (serial clock for silentype) + case 0x6b: + result = (m_joybuttons->read() & 8) ? 0x80 : 0x00; + break; + + case 0x64: // slot 2 IRQ status (negative logic) + case 0x6c: + result = (m_a2bus->get_a2bus_irq_mask() & (1<<2)) ? 0 : 0x80; + break; + + case 0x65: // slot 1 IRQ status (negative logic) + case 0x6d: + result = (m_a2bus->get_a2bus_irq_mask() & (1<<1)) ? 0 : 0x80; + break; + + case 0x66: // paddle A/D conversion done (bit 7 = 1 while counting, 0 when done) + case 0x6e: + result = m_ramp_active ? 0x80 : 0x00; + break; + + case 0x70: case 0x71: case 0x72: case 0x73: + case 0x74: case 0x75: case 0x76: case 0x77: + case 0x78: case 0x79: case 0x7A: case 0x7B: + case 0x7C: case 0x7D: case 0x7E: case 0x7F: + result = m_rtc->read(m_via_0_b); + break; + + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + slotdevice = m_a2bus->get_a2bus_card(1); + if (slotdevice != nullptr) + { + result = slotdevice->read_c0nx(offset&0xf); + } + break; + + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + slotdevice = m_a2bus->get_a2bus_card(2); + if (slotdevice != nullptr) + { + result = slotdevice->read_c0nx(offset&0xf); + } + break; + + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + slotdevice = m_a2bus->get_a2bus_card(3); + if (slotdevice != nullptr) + { + result = slotdevice->read_c0nx(offset&0xf); + } + break; + + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + slotdevice = m_a2bus->get_a2bus_card(4); + if (slotdevice != nullptr) + { + result = slotdevice->read_c0nx(offset&0xf); + } + break; + + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + /* external drive stuff */ + m_fdc->read_c0dx(offset&0xf); + result = 0x00; + break; + + case 0xd8: case 0xd9: + m_smoothscr = offset & 1; + break; + + case 0xda: +// printf("ENCWRT off\n"); + m_charwrt = false; + break; + + case 0xdb: + m_charwrt = true; +// printf("ENCWRT on (write_charmem (r))\n"); + break; + + case 0xdc: +// printf("ENCSEL off\n"); + break; + + case 0xdd: +// printf("ENCSEL on\n"); + break; + + case 0xde: +// printf("ENSIO off\n"); + break; + + case 0xdf: +// printf("ENSIO on\n"); + break; + + case 0xe0: case 0xe1: + result = m_fdc->read(offset&0xf); + m_va = offset & 1; + break; + + case 0xe2: case 0xe3: + result = m_fdc->read(offset&0xf); + m_vb = offset & 1; + break; + + case 0xe4: case 0xe5: + result = m_fdc->read(offset&0xf); + m_vc = offset & 1; + break; + + case 0xe6: case 0xe7: case 0xe8: case 0xe9: + case 0xea: case 0xeb: case 0xec: case 0xed: + case 0xee: case 0xef: + result = m_fdc->read(offset&0xf); + break; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + result = m_acia->read(offset & 0x03); + break; + } + return result; +} + + + +void apple3_state::apple3_c0xx_w(offs_t offset, uint8_t data) +{ + device_a2bus_card_interface *slotdevice; + + switch(offset) + { + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1A: case 0x1B: + case 0x1C: case 0x1D: case 0x1E: case 0x1F: + m_strobe = 0; + break; + + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2A: case 0x2B: + case 0x2C: case 0x2D: case 0x2E: case 0x2F: + m_cnxx_slot = -1; + break; + + case 0x30: case 0x31: case 0x32: case 0x33: + case 0x34: case 0x35: case 0x36: case 0x37: + case 0x38: case 0x39: case 0x3A: case 0x3B: + case 0x3C: case 0x3D: case 0x3E: case 0x3F: + m_bell_state ^= 1; + m_bell->write(m_bell_state); + break; + + case 0x40: case 0x41: case 0x42: case 0x43: + case 0x44: case 0x45: case 0x46: case 0x47: + case 0x48: case 0x49: case 0x4A: case 0x4B: + case 0x4C: case 0x4D: + m_c040_time = 200; + break; + + case 0x4E: case 0x4F: // character RAM disable/enable + break; + + case 0x50: case 0x51: case 0x52: case 0x53: + case 0x54: case 0x55: case 0x56: case 0x57: + m_screen->update_partial(m_screen->vpos()); + /* graphics softswitches */ + if (offset & 1) + m_flags |= 1 << ((offset - 0x50) / 2); + else + m_flags &= ~(1 << ((offset - 0x50) / 2)); + break; + + case 0x58: + case 0x59: + case 0x5a: + case 0x5b: + case 0x5c: + case 0x5d: + case 0x5e: + case 0x5f: + pdl_handler(offset); + break; + + case 0x70: case 0x71: case 0x72: case 0x73: + case 0x74: case 0x75: case 0x76: case 0x77: + case 0x78: case 0x79: case 0x7A: case 0x7B: + case 0x7C: case 0x7D: case 0x7E: case 0x7F: + m_rtc->write(m_via_0_b, data); + break; + + + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + slotdevice = m_a2bus->get_a2bus_card(1); + if (slotdevice != nullptr) + { + slotdevice->write_c0nx(offset&0xf, data); + } + break; + + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + slotdevice = m_a2bus->get_a2bus_card(2); + if (slotdevice != nullptr) + { + slotdevice->write_c0nx(offset&0xf, data); + } + break; + + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + slotdevice = m_a2bus->get_a2bus_card(3); + if (slotdevice != nullptr) + { + slotdevice->write_c0nx(offset&0xf, data); + } + break; + + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + slotdevice = m_a2bus->get_a2bus_card(4); + if (slotdevice != nullptr) + { + slotdevice->write_c0nx(offset&0xf, data); + } + break; + + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + /* external drive stuff */ + m_fdc->write_c0dx(offset&0xf, data); + break; + + case 0xd8: case 0xd9: + m_smoothscr = offset & 1; + break; + + case 0xda: +// printf("ENCWRT off\n"); + m_charwrt = false; + break; + + case 0xdb: + m_charwrt = true; +// printf("ENCWRT on (write_charmem (w))\n"); + break; + + case 0xdc: +// printf("ENCSEL off\n"); + break; + + case 0xdd: +// printf("ENCSEL on\n"); + break; + + case 0xde: +// printf("ENSIO off\n"); + break; + + case 0xdf: +// printf("ENSIO on\n"); + break; + + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + m_fdc->write(offset&0xf, data); + break; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + m_acia->write(offset & 0x03, data); + break; + } +} + +void apple3_state::vbl_w(int state) +{ + // do the font upload at the end of VBL, not the start + if ((!state) && (m_charwrt)) + { + apple3_write_charmem(); + } +} + +uint8_t *apple3_state::apple3_bankaddr(uint16_t bank, offs_t offset) +{ + if (bank != (uint16_t) ~0) + { + bank %= m_ram->size() / 0x8000; + if ((bank + 1) == (m_ram->size() / 0x8000)) + bank = 0x02; + } + offset += ((offs_t) bank) * 0x8000; + offset %= m_ram->size(); + return &m_ram->pointer()[offset]; +} + +uint8_t *apple3_state::apple3_get_zpa_addr(offs_t offset) +{ + m_zpa = (((offs_t) m_via_0_b) * 0x100) + offset; + + if (m_via_0_b < 0x20) + return apple3_bankaddr(~0, m_zpa); + else if (m_via_0_b > 0x9F) + return apple3_bankaddr(~0, m_zpa - 0x8000); + else + return apple3_bankaddr(m_via_1_a, m_zpa - 0x2000); +} + +void apple3_state::apple3_update_memory() +{ + uint16_t bank; + uint8_t page; + + LOGMASKED(LOG_MEMORY, "apple3_update_memory(): via_0_b=0x%02x via_1_a=0x0x%02x\n", m_via_0_b, m_via_1_a); + + m_maincpu->set_unscaled_clock(((m_via_0_a & ENV_SLOWSPEED) ? APPLE2_CLOCK : A3_2MHZ_CLOCK), true); + + /* bank 2 (0100-01FF) */ + if (!(m_via_0_a & ENV_STACK1XX)) + { + if (m_via_0_b < 0x20) + { + bank = ~0; /* system bank */ + page = m_via_0_b ^ 0x01; + } + else if (m_via_0_b >= 0xA0) + { + bank = ~0; /* system bank */ + page = (m_via_0_b ^ 0x01) - 0x80; + } + else + { + bank = m_via_1_a; + page = (m_via_0_b ^ 0x01) - 0x20; + } + } + else + { + bank = ~0; + page = 0x01; + } + m_bank2 = apple3_bankaddr(bank, ((offs_t) page) * 0x100); + + /* bank 3 (0200-1FFF) */ + m_bank3 = apple3_bankaddr(~0, 0x0200); + + /* bank 4 (2000-9FFF) */ + m_bank4 = apple3_bankaddr(m_via_1_a, 0x0000); + + /* bank 5 (A000-BFFF) */ + m_bank5 = apple3_bankaddr(~0, 0x2000); + + /* bank 8 (C000-C0FF) */ + if (!(m_via_0_a & ENV_IOENABLE)) + { + m_bank8 = apple3_bankaddr(~0, 0x4000); + } + + /* bank 9 (C100-C4FF) */ + if (!(m_via_0_a & ENV_IOENABLE)) + { + m_bank9 = apple3_bankaddr(~0, 0x4100); + } + + /* bank 10 (C500-C7FF) */ + m_bank10 = apple3_bankaddr(~0, 0x4500); + + /* bank 11 (C800-CFFF) */ + if (!(m_via_0_a & ENV_IOENABLE)) + { + m_bank11 = apple3_bankaddr(~0, 0x4800); + } + + /* install bank 6 (D000-EFFF) */ + m_bank6 = apple3_bankaddr(~0, 0x5000); + + /* install bank 7 (F000-FFFF) */ + m_bank7wr = apple3_bankaddr(~0, 0x7000); + if (m_via_0_a & ENV_ROMENABLE) + { + m_bank7rd = memregion("maincpu")->base(); + } + else + { + m_bank7rd = m_bank7wr; + + // if we had an IRQ waiting for RAM to be paged in... + //apple3_irq_update(); + } +} + + + +void apple3_state::apple3_via_out(uint8_t *var, uint8_t data) +{ + if (*var != data) + { + *var = data; + apple3_update_memory(); + } +} + + +void apple3_state::apple3_via_0_out_a(uint8_t data) +{ + apple3_via_out(&m_via_0_a, data); +} + +void apple3_state::apple3_via_0_out_b(uint8_t data) +{ +// printf("ZP to %02x\n", data); + apple3_via_out(&m_via_0_b, data); +} + +void apple3_state::apple3_via_1_out_a(uint8_t data) +{ + apple3_via_out(&m_via_1_a, data); +} + +void apple3_state::apple3_via_1_out_b(uint8_t data) +{ + m_dac->write(data); + apple3_via_out(&m_via_1_b, data); +} + + +void apple3_state::machine_reset() +{ + m_indir_bank = 0; + m_sync = false; + m_bell_state = 0; + m_bell->write(m_bell_state); + m_c040_time = 0; + m_strobe = 0; + m_lastchar = 0x0d; + m_cnxx_slot = -1; + m_analog_sel = 0; + m_ramp_active = false; + m_charwrt = false; + m_inh_state = false; + + m_fdc->set_floppies_4(floppy0, floppy1, floppy2, floppy3); + + m_scanstart->adjust(m_screen->time_until_pos(0, 0)); + m_scanend->adjust(attotime::never); +} + +void apple3_state::a2bus_inh_w(int state) +{ + m_inh_state = state; +} + +uint8_t *apple3_state::apple3_get_indexed_addr(offs_t offset) +{ + uint8_t *result = nullptr; + + // m_indir_bank is guaranteed to be between 0x80 and 0x8f + if (m_indir_bank == 0x8f) + { + /* get at that special ram under the VIAs */ + if ((offset >= 0xFFD0) && (offset <= 0xFFEF)) + result = apple3_bankaddr(~0, offset & 0x7FFF); + else if (offset < 0x2000) + result = apple3_bankaddr(~0, offset); + else if (offset > 0x9FFF) + result = apple3_bankaddr(~0, offset - 0x8000); + else + result = &m_ram->pointer()[offset - 0x2000]; + } + else + { + result = apple3_bankaddr(m_indir_bank, offset); + } + + return result; +} + +void apple3_state::init_apple3() +{ + m_enable_mask = 0; + + m_flags = 0; + m_via_0_a = ~0; + m_via_1_a = ~0; + m_va = 0; + m_vb = 0; + m_vc = 0; + m_smoothscr = 0; + m_inh_state = false; + + // kludge round +12v pull up resistors, which after conversion will bring this low when nothing is plugged in. issue also affects dcd/dsr but those don't affect booting. + m_acia->write_cts(0); + + /* these are here to appease the Apple /// confidence tests */ + m_via[1]->write_pa0(1); + m_via[1]->write_pa1(1); + m_via[1]->write_pa2(1); + m_via[1]->write_pa3(1); + m_via[1]->write_pa4(1); + m_via[1]->write_pa5(1); + m_via[1]->write_pa6(1); + m_via[1]->write_pa7(1); + + m_via[1]->write_pb0(1); + m_via[1]->write_pb1(1); + m_via[1]->write_pb2(1); + m_via[1]->write_pb3(1); + m_via[1]->write_pb4(1); + m_via[1]->write_pb5(1); + m_via[1]->write_pb6(1); + m_via[1]->write_pb7(1); + + m_scanstart = timer_alloc(FUNC(apple3_state::scanstart_cb), this); + m_scanend = timer_alloc(FUNC(apple3_state::scanend_cb), this); + + apple3_update_memory(); + + save_item(NAME(m_via_0_a)); + save_item(NAME(m_via_0_b)); + save_item(NAME(m_via_1_a)); + save_item(NAME(m_via_1_b)); + save_item(NAME(m_zpa)); + save_item(NAME(m_last_n)); + save_item(NAME(m_sync)); + save_item(NAME(m_indir_bank)); + save_item(NAME(m_cnxx_slot)); + save_item(NAME(m_bell_state)); + save_item(NAME(m_c040_time)); + save_item(NAME(m_lastchar)); + save_item(NAME(m_strobe)); + save_item(NAME(m_transchar)); + save_item(NAME(m_flags)); + save_item(NAME(m_char_mem)); + save_item(NAME(m_analog_sel)); + save_item(NAME(m_ramp_active)); + save_item(NAME(m_pdl_charge)); + save_item(NAME(m_va)); + save_item(NAME(m_vb)); + save_item(NAME(m_vc)); + save_item(NAME(m_smoothscr)); + save_item(NAME(m_charwrt)); + save_item(NAME(m_inh_state)); +} + +void apple3_state::device_post_load() +{ + apple3_update_memory(); +} + +uint8_t apple3_state::apple3_memory_r(offs_t offset) +{ + uint8_t rv = 0xff; + + // /INH handling + for (int slot = 1; slot < 5; slot++) + { + device_a2bus_card_interface *slotdevice = m_a2bus->get_a2bus_card(slot); + if (slotdevice != nullptr) + { + if (slotdevice->inh_check(offset, false)) + { + return slotdevice->read_inh_rom(offset); + } + } + } + + // (zp), y or (zp,x) read + if (!machine().side_effects_disabled()) + { + if ((m_indir_bank & 0x80) && (offset >= 0x100)) + { + uint8_t *test; + test = apple3_get_indexed_addr(offset); + + if (test) + { + return *test; + } + } + } + + if (offset < 0x100) + { + rv = *apple3_get_zpa_addr(offset); + + if ((!m_sync) && (m_via_0_b >= 0x18) && (m_via_0_b <= 0x1F)) + { + // fetch the "X byte" + m_indir_bank = *apple3_bankaddr(~0, m_zpa ^ 0x0C00) & 0x8f; + } + } + else if (offset < 0x200) + { + rv = m_bank2[offset-0x100]; + } + else if (offset < 0x2000) + { + rv = m_bank3[offset-0x200]; + } + else if (offset < 0xa000) + { + rv = m_bank4[offset-0x2000]; + } + else if (offset < 0xc000) + { + rv = m_bank5[offset-0xa000]; + } + else if (offset < 0xc100) + { + if (m_via_0_a & ENV_IOENABLE) + { + if (!machine().side_effects_disabled()) + { + rv = apple3_c0xx_r(offset-0xc000); + } + } + else + { + rv = m_bank8[offset - 0xc000]; + } + } + else if (offset < 0xc500) + { + if (!(m_via_0_a & ENV_IOENABLE)) + { + rv = m_bank9[offset - 0xc100]; + } + else if (!machine().side_effects_disabled()) + { + /* now identify the device */ + device_a2bus_card_interface *slotdevice = m_a2bus->get_a2bus_card((offset>>8) & 0x7); + + if (slotdevice != nullptr) + { + if (slotdevice->take_c800()) + { + m_cnxx_slot = ((offset>>8) & 7); + } + + return slotdevice->read_cnxx(offset&0xff); + } + } + } + else if (offset < 0xc800) + { + rv = m_bank10[offset - 0xc500]; + } + else if (offset < 0xd000) + { + if (!(m_via_0_a & ENV_IOENABLE)) + { + rv = m_bank11[offset - 0xc800]; + } + else + { + if (!machine().side_effects_disabled()) + { + if (offset == 0xcfff) + { + m_cnxx_slot = -1; + } + } + + if (m_cnxx_slot != -1) + { + device_a2bus_card_interface *slotdevice = m_a2bus->get_a2bus_card(m_cnxx_slot); + + if (slotdevice != nullptr) + { + rv = slotdevice->read_c800(offset&0x7ff); + } + } + } + } + else if (offset < 0xf000) + { + rv = m_bank6[offset - 0xd000]; + } + else + { + if (offset >= 0xffc0 && offset <= 0xffcf) + { + rv = m_bank7wr[offset - 0xf000]; + } + else if ((offset >= 0xffd0 && offset <= 0xffdf) && (m_via_1_a & 0x40)) + { + rv = m_via[0]->read(offset); + } + else if ((offset >= 0xffe0 && offset <= 0xffef) && (m_via_1_a & 0x40)) + { + rv = m_via[1]->read(offset); + } + else + { + rv = m_bank7rd[offset - 0xf000]; + } + } + + return rv; +} + +void apple3_state::apple3_memory_w(offs_t offset, uint8_t data) +{ + if ((m_indir_bank & 0x80) && (offset >= 0x100)) + { + uint8_t *test; + test = apple3_get_indexed_addr(offset); + + if (test) + { + *test = data; + return; + } + } + + for (int slot = 1; slot < 5; slot++) + { + device_a2bus_card_interface *slotdevice = m_a2bus->get_a2bus_card(slot); + if (slotdevice != nullptr) + { + if (slotdevice->inh_check(offset, true)) + { + slotdevice->write_inh_rom(offset, data); + return; + } + } + } + + if (offset < 0x100) + { + *apple3_get_zpa_addr(offset) = data; + } + else if (offset < 0x200) + { + m_bank2[offset-0x100] = data; + } + else if (offset < 0x2000) + { + m_bank3[offset-0x200] = data; + } + else if (offset < 0xa000) + { + m_bank4[offset-0x2000] = data; + } + else if (offset < 0xc000) + { + m_bank5[offset-0xa000] = data; + } + else if (offset < 0xc100) + { + if (m_via_0_a & ENV_IOENABLE) + { + if (!machine().side_effects_disabled()) + { + apple3_c0xx_w(offset-0xc000, data); + } + } + else + { + // is this page write protected? + if (!(m_via_0_a & ENV_WRITEPROT)) + { + m_bank8[offset - 0xc000] = data; + } + } + } + else if (offset < 0xc500) + { + if (!(m_via_0_a & ENV_IOENABLE)) + { + if (!(m_via_0_a & ENV_WRITEPROT)) + { + m_bank9[offset - 0xc100] = data; + } + } + else + { + /* now identify the device */ + device_a2bus_card_interface *slotdevice = m_a2bus->get_a2bus_card((offset>>8) & 0x7); + + if (slotdevice != nullptr) + { + if (slotdevice->take_c800()) + { + m_cnxx_slot = ((offset>>8) & 7); + } + + slotdevice->write_cnxx(offset&0xff, data); + } + } + } + else if (offset < 0xc800) + { + if (!(m_via_0_a & ENV_WRITEPROT)) + { + m_bank10[offset - 0xc500] = data; + } + } + else if (offset < 0xd000) + { + if (!(m_via_0_a & ENV_IOENABLE)) + { + if (!(m_via_0_a & ENV_WRITEPROT)) + { + m_bank11[offset - 0xc800] = data; + } + } + else + { + if (offset == 0xcfff) + { + m_cnxx_slot = -1; + } + + if (m_cnxx_slot != -1) + { + device_a2bus_card_interface *slotdevice = m_a2bus->get_a2bus_card(m_cnxx_slot); + + if (slotdevice != nullptr) + { + slotdevice->write_c800(offset&0x7ff, data); + } + } + } + } + else if (offset < 0xf000) + { + if (!(m_via_0_a & ENV_WRITEPROT)) + { + m_bank6[offset - 0xd000] = data; + } + } + else + { + if (offset >= 0xffc0 && offset <= 0xffcf) + { + // does writeprot really apply to ffcx? + if (!(m_via_0_a & ENV_WRITEPROT)) + { + m_bank7wr[offset - 0xf000] = data; + } + } + else if ((offset >= 0xffd0 && offset <= 0xffdf) && (m_via_1_a & 0x40)) + { + if (!machine().side_effects_disabled()) + { + m_via[0]->write(offset, data); + } + } + else if ((offset >= 0xffe0 && offset <= 0xffef) && (m_via_1_a & 0x40)) + { + if (!machine().side_effects_disabled()) + { + m_via[1]->write(offset, data); + } + } + else + { + if (!(m_via_0_a & ENV_WRITEPROT)) + { + m_bank7wr[offset - 0xf000] = data; + } + } + } +} + +void apple3_state::apple3_sync_w(int state) +{ +// printf("sync: %d\n", state); + m_sync = (state == ASSERT_LINE) ? true : false; + + if (m_sync) + { + m_indir_bank = 0; + } +} + +TIMER_DEVICE_CALLBACK_MEMBER(apple3_state::apple3_c040_tick) +{ + if (m_c040_time > 0) + { + m_bell_state ^= 1; + m_bell->write(m_bell_state); + m_c040_time--; + } +} + +TIMER_CALLBACK_MEMBER(apple3_state::scanstart_cb) +{ + int scanline; + + scanline = m_screen->vpos(); + m_screen->update_partial(m_screen->vpos()); + + m_via[1]->write_pb6(0); + + m_scanend->adjust(m_screen->time_until_pos(scanline, 559)); + + // Drop back to 1Mhz for video memory access + if (!(m_via_0_a & ENV_SLOWSPEED) && (m_via_0_a & ENV_VIDENABLE)) + { + m_maincpu->set_unscaled_clock(APPLE2_CLOCK, true); + } +} + +TIMER_CALLBACK_MEMBER(apple3_state::scanend_cb) +{ + int scanline = m_screen->vpos(); + + m_via[1]->write_pb6(1); + + m_scanstart->adjust(m_screen->time_until_pos((scanline+1) % 192, 0)); + + // And back to 2MHz during blanking + if (!(m_via_0_a & ENV_SLOWSPEED) && (m_via_0_a & ENV_VIDENABLE)) + { + m_maincpu->set_unscaled_clock(A3_2MHZ_CLOCK, true); + } +} + +INPUT_CHANGED_MEMBER(apple3_state::keyb_special_changed) +{ + if (((m_kbspecial->read() & 0x88) == 0x88) && (m_via_0_a & ENV_NMIENABLE)) + { + // ctrl-reset pressed (RESET) + if (!m_reset_latch) + { + m_reset_latch = true; + m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); + } + } + else if ((m_kbspecial->read() & 0x80) && (m_via_0_a & ENV_NMIENABLE) && !m_reset_latch) + { + // reset key only pressed (NMI) + if (!m_nmi_latch) + { + m_nmi_latch = true; + m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE); + } + } + else + { + if (m_reset_latch) + { + m_reset_latch = false; + // allow cards to see reset + m_a2bus->reset_bus(); + m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); + } + else if (m_nmi_latch) + { + m_nmi_latch = false; + m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); + } + } +} + +int apple3_state::ay3600_shift_r() +{ + // either shift key + if (m_kbspecial->read() & 0x06) + { + return ASSERT_LINE; + } + + return CLEAR_LINE; +} + +int apple3_state::ay3600_control_r() +{ + if (m_kbspecial->read() & 0x08) + { + return ASSERT_LINE; + } + + return CLEAR_LINE; +} + +static const uint8_t key_remap[0x50][4] = +{ +/* norm shft ctrl both */ + { 0x9b,0x9b,0x9b,0x9b }, /* Escape 00 */ + { 0x31,0x21,0x31,0x31 }, /* 1 ! 01 */ + { 0x32,0x40,0x32,0x00 }, /* 2 @ 02 */ + { 0x33,0x23,0x33,0x23 }, /* 3 # 03 */ + { 0x34,0x24,0x34,0x24 }, /* 4 $ 04 */ + { 0x35,0x25,0x35,0x25 }, /* 5 % 05 */ + { 0x36,0x5e,0x35,0x53 }, /* 6 ^ 06 */ + { 0x37,0x26,0x37,0x26 }, /* 7 & 07 */ + { 0x38,0x2a,0x38,0x2a }, /* 8 * 08 */ + { 0x39,0x28,0x39,0x28 }, /* 9 ( 09 */ + { 0x89,0x89,0x89,0x89 }, /* Tab 0a */ + { 0x51,0x51,0x11,0x11 }, /* q Q 0b */ + { 0x57,0x57,0x17,0x17 }, /* w W 0c */ + { 0x45,0x45,0x05,0x05 }, /* e E 0d */ + { 0x52,0x52,0x12,0x12 }, /* r R 0e */ + { 0x54,0x54,0x14,0x14 }, /* t T 0f */ + { 0x59,0x59,0x19,0x19 }, /* y Y 10 */ + { 0x55,0x55,0x15,0x15 }, /* u U 11 */ + { 0x49,0x49,0x09,0x09 }, /* i I 12 */ + { 0x4f,0x4f,0x0f,0x0f }, /* o O 13 */ + { 0x41,0x41,0x01,0x01 }, /* a A 14 */ + { 0x53,0x53,0x13,0x13 }, /* s S 15 */ + { 0x44,0x44,0x04,0x04 }, /* d D 16 */ + { 0x46,0x46,0x06,0x06 }, /* f F 17 */ + { 0x48,0x48,0x08,0x08 }, /* h H 18 */ + { 0x47,0x47,0x07,0x07 }, /* g G 19 */ + { 0x4a,0x4a,0x0a,0x0a }, /* j J 1a */ + { 0x4b,0x4b,0x0b,0x0b }, /* k K 1b */ + { 0x3b,0x3a,0x3b,0x3a }, /* ; : 1c */ + { 0x4c,0x4c,0x0c,0x0c }, /* l L 1d */ + { 0x5a,0x5a,0x1a,0x1a }, /* z Z 1e */ + { 0x58,0x58,0x18,0x18 }, /* x X 1f */ + { 0x43,0x43,0x03,0x03 }, /* c C 20 */ + { 0x56,0x56,0x16,0x16 }, /* v V 21 */ + { 0x42,0x42,0x02,0x02 }, /* b B 22 */ + { 0x4e,0x4e,0x0e,0x0e }, /* n N 23 */ + { 0x4d,0x4d,0x0d,0x0d }, /* m M 24 */ + { 0x2c,0x3c,0x2c,0x3c }, /* , < 25 */ + { 0x2e,0x3e,0x2e,0x3e }, /* . > 26 */ + { 0x2f,0x3f,0x2f,0x3f }, /* / ? 27 */ + { 0x00,0x00,0x00,0x00 }, /* 0x28 unused */ + { 0xb9,0xb9,0xb9,0xb9 }, /* 9 (KP) 29 */ + { 0x00,0x00,0x00,0x00 }, /* 0x2a unused */ + { 0xb8,0xb8,0xb8,0xb8 }, /* 8 (KP) 2b */ + { 0x00,0x00,0x00,0x00 }, /* 0x2c unused */ + { 0xb7,0xb7,0xb7,0xb7 }, /* 7 (KP) 2d */ + { 0x5c,0x7c,0x7f,0x1c }, /* \ | 2e */ + { 0x3d,0x2b,0x3d,0x2b }, /* = + 2f */ + { 0x30,0x29,0x30,0x29 }, /* 0 ) 30 */ + { 0x2d,0x5f,0x2d,0x1f }, /* - _ 31 */ + { 0x00,0x00,0x00,0x00 }, /* 0x32 unused */ + { 0xb6,0xb6,0xb6,0xb6 }, /* 6 (KP) 33 */ + { 0x00,0x00,0x00,0x00 }, /* 0x34 unused */ + { 0xb5,0xb5,0xb5,0xb5 }, /* 5 (KP) 35 */ + { 0x00,0x00,0x00,0x00 }, /* 0x36 unused */ + { 0xb4,0xb4,0xb4,0xb4 }, /* 4 (KP) 37 */ + { 0x60,0x7e,0x60,0x7e }, /* ` ~ 38 */ + { 0x5d,0x7d,0x1d,0x1d }, /* ] } 39 */ + { 0x50,0x50,0x10,0x10 }, /* p P 3a */ + { 0x5b,0x7b,0x1b,0x1b }, /* [ { 3b */ + { 0x00,0x00,0x00,0x00 }, /* 0x3c unused */ + { 0xb3,0xb3,0xb3,0xb3 }, /* 3 (KP) 3d */ + { 0xae,0xae,0xae,0xae }, /* . (KP) 3e */ + { 0xb2,0xb2,0xb2,0xb2 }, /* 2 (KP) 3f */ + { 0xb0,0xb0,0xb0,0xb0 }, /* 0 (KP) 40 */ + { 0xb1,0xb1,0xb1,0xb1 }, /* 1 (KP) 41 */ + { 0x0d,0x0d,0x0d,0x0d }, /* Enter 42 */ + { 0x8b,0x8b,0x8b,0x8b }, /* Up 43 */ + { 0x00,0x00,0x00,0x00 }, /* 0x44 unused */ + { 0x27,0x22,0x27,0x22 }, /* ' " 45 */ + { 0x00,0x00,0x00,0x00 }, /* 0x46 unused */ + { 0x00,0x00,0x00,0x00 }, /* 0x47 unused */ + { 0x8d,0x8d,0x8d,0x8d }, /* Ent(KP) 48 */ + { 0xa0,0xa0,0xa0,0xa0 }, /* Space 49 */ + { 0x00,0x00,0x00,0x00 }, /* 0x4a unused */ + { 0xad,0xad,0xad,0xad }, /* - (KP) 4b */ + { 0x95,0x95,0x95,0x95 }, /* Right 4c */ + { 0x8a,0x8a,0x8a,0x8a }, /* Down 4d */ + { 0x88,0x88,0x88,0x88 }, /* Left 4e */ + { 0x00,0x00,0x00,0x00 } /* 0x4f unused */ +}; + +void apple3_state::ay3600_data_ready_w(int state) +{ + m_via[1]->write_ca2(state); + + if (state == ASSERT_LINE) + { + uint16_t trans; + int mod = 0; + m_lastchar = m_ay3600->b_r(); + + trans = m_lastchar & ~(0x1c0); // clear the 3600's control/shift stuff + trans |= (m_lastchar & 0x100)>>2; // bring the 0x100 bit down to the 0x40 place + + mod = (m_kbspecial->read() & 0x06) ? 0x01 : 0x00; + mod |= (m_kbspecial->read() & 0x08) ? 0x02 : 0x00; + + m_transchar = key_remap[trans][mod]; + + if (m_transchar != 0) + { + m_strobe = 0x80; +// printf("new char = %04x (%02x)\n", trans, m_transchar); + } + } +} + +void apple3_state::ay3600_ako_w(int state) +{ + m_anykeydown = (state == ASSERT_LINE) ? true : false; + + if (m_anykeydown) + { + m_repttimer->adjust(attotime::from_hz(2)); + } + else + { + m_repttimer->adjust(attotime::never); + } +} + +TIMER_DEVICE_CALLBACK_MEMBER(apple3_state::ay3600_repeat) +{ + // is the key still down? + if (m_anykeydown) + { + // Closed Apple key + if (m_kbspecial->read() & 0x20) + { + m_repttimer->adjust(attotime::from_hz(30)); + } + else + { + m_repttimer->adjust(attotime::from_hz(10)); + } + + m_strobe = 0x80; + m_via[1]->write_ca2(ASSERT_LINE); + m_via[1]->write_ca2(CLEAR_LINE); + } +} + +void apple3_state::pdl_handler(int offset) +{ + uint8_t pdlread; + + switch (offset) + { + case 0x58: + m_analog_sel &= ~1; + break; + + case 0x59: + m_analog_sel |= 1; + break; + + case 0x5a: + m_analog_sel &= ~4; + break; + + case 0x5b: + m_analog_sel |= 4; + break; + + case 0x5c: + m_ramp_active = false; + m_pdl_charge = 0; + m_pdltimer->adjust(attotime::from_hz(950000.0)); + break; + + case 0x5d: + switch (m_analog_sel) + { + case 1: + pdlread = m_joy1x->read(); + break; + + case 2: + pdlread = 255 - m_joy1y->read(); + break; + + case 4: + pdlread = m_joy2x->read(); + break; + + case 5: + pdlread = 255 - m_joy2y->read(); + break; + + default: + pdlread = 127; + break; + } + + // help the ROM self-test + if (m_pdl_charge > 82) + { + m_pdl_charge += (pdlread*7); + m_pdl_charge -= 100; + } + m_pdltimer->adjust(attotime::from_hz(950000.0)); + m_ramp_active = true; + break; + + case 0x5e: + m_analog_sel &= ~2; + break; + + case 0x5f: + m_analog_sel |= 2; + break; + } +} + +TIMER_DEVICE_CALLBACK_MEMBER(apple3_state::paddle_timer) +{ + if (m_ramp_active) + { + m_pdl_charge--; + + if (m_pdl_charge > 0) + { + m_pdltimer->adjust(attotime::from_hz(950000.0)); + } + else + { + m_pdltimer->adjust(attotime::never); + m_ramp_active = false; + } + } + else + { + m_pdl_charge++; + m_pdltimer->adjust(attotime::from_hz(950000.0)); + } +} + +void apple3_state::a2bus_irq_w(int state) +{ + uint8_t irq_mask = m_a2bus->get_a2bus_irq_mask(); + + m_via[0]->write_ca1(!state); + + if (irq_mask & (1<<4)) + { + m_via[1]->write_pa4(CLEAR_LINE); + } + else + { + m_via[1]->write_pa4(ASSERT_LINE); + } + + if (irq_mask & (1<<3)) + { + m_via[1]->write_pa5(CLEAR_LINE); + } + else + { + m_via[1]->write_pa5(ASSERT_LINE); + } +} + +void apple3_state::a2bus_nmi_w(int state) +{ + m_via[1]->write_pb7(state); + + if (m_via_0_a & ENV_NMIENABLE) + { + m_maincpu->set_input_line(INPUT_LINE_NMI, state); + } +} |