diff options
Diffstat (limited to 'src/devices/machine/psion_asic9.cpp')
-rw-r--r-- | src/devices/machine/psion_asic9.cpp | 1239 |
1 files changed, 1239 insertions, 0 deletions
diff --git a/src/devices/machine/psion_asic9.cpp b/src/devices/machine/psion_asic9.cpp new file mode 100644 index 00000000000..fc7d677bfab --- /dev/null +++ b/src/devices/machine/psion_asic9.cpp @@ -0,0 +1,1239 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/****************************************************************************** + + Psion ASIC9 + + ASIC9 is a composite chip comprising of a V30H processor, ASIC1, ASIC2 and + general I/O and PSU control logic all on one IC. ASIC9 thus integrates all + the digital logic required to produce a SIBO architecture computer less the + memory onto one chip. ASIC9 has a few additional features such as an extra + free-running clock (FRC) and a codec interface for sound. + + TODO: + - improve RAM configuration for mx machines + - set RTC timer + - ASIC9MX implements V30MX, and likely the Temic (Condor) device found in 3c/Siena + +******************************************************************************/ + +#include "emu.h" +#include "psion_asic9.h" +#include "cpu/nec/nec.h" +#include "screen.h" + + +#define VERBOSE 0 +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" + + +DEFINE_DEVICE_TYPE(PSION_ASIC9, psion_asic9_device, "psion_asic9", "Psion ASIC9 V30H") +DEFINE_DEVICE_TYPE(PSION_ASIC9MX, psion_asic9mx_device, "psion_asic9mx", "Psion ASIC9 V30MX") + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +psion_asic9_device::psion_asic9_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, type, tag, owner, clock) + , device_memory_interface(mconfig, *this) + , device_video_interface(mconfig, *this) + , m_v30(*this, "v30") + , m_ram(*this, finder_base::DUMMY_TAG) + , m_rom(*this, finder_base::DUMMY_TAG) + , m_ram_config("asic9_ram", ENDIANNESS_LITTLE, 16, 24, 0) + , m_rom_config("asic9_rom", ENDIANNESS_LITTLE, 16, 24, 0) + , m_tick_timer(nullptr) + , m_frc1_timer(nullptr) + , m_frc2_timer(nullptr) + , m_watchdog_timer(nullptr) + , m_rtc_timer(nullptr) + , m_snd_timer(nullptr) + , m_buz_cb(*this) + , m_col_cb(*this) + , m_port_ab_r(*this, 0) + , m_port_ab_w(*this) + , m_pcm_in(*this, 0) + , m_pcm_out(*this) + , m_data_r(*this, 0x00) + , m_data_w(*this) +{ +} + +psion_asic9_device::psion_asic9_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : psion_asic9_device(mconfig, PSION_ASIC9, tag, owner, clock) +{ +} + +psion_asic9mx_device::psion_asic9mx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : psion_asic9_device(mconfig, PSION_ASIC9MX, tag, owner, clock) +{ +} + + +void psion_asic9_device::mem_map(address_map &map) +{ + map(0x00000, 0xfffff).rw(FUNC(psion_asic9_device::mem_r), FUNC(psion_asic9_device::mem_w)); +} + +void psion_asic9_device::io_map(address_map &map) +{ + map(0x0000, 0x00ff).rw(FUNC(psion_asic9_device::io_r), FUNC(psion_asic9_device::io_w)); +} + + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- + +void psion_asic9_device::device_add_mconfig(machine_config &config) +{ + V30(config, m_v30, DERIVED_CLOCK(1, 1)); + m_v30->set_addrmap(AS_PROGRAM, &psion_asic9_device::mem_map); + m_v30->set_addrmap(AS_IO, &psion_asic9_device::io_map); + m_v30->set_irq_acknowledge_callback(FUNC(psion_asic9_device::inta_cb)); +} + + +//------------------------------------------------- +// memory_space_config - return a description of +// any address spaces owned by this device +//------------------------------------------------- + +device_memory_interface::space_config_vector psion_asic9_device::memory_space_config() const +{ + return space_config_vector{ + std::make_pair(AS_A9_RAM, &m_ram_config), + std::make_pair(AS_A9_ROM, &m_rom_config) + }; +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void psion_asic9_device::device_start() +{ + if (!m_ram->started()) + throw device_missing_dependencies(); + + m_ram_space = &space(AS_A9_RAM); + m_rom_space = &space(AS_A9_ROM); + + m_ram_type = get_ram_type(m_ram->size()); + + configure_ram(m_ram_type); + configure_rom(); + + m_tick_timer = timer_alloc(FUNC(psion_asic9_device::tick), this); + m_frc1_timer = timer_alloc(FUNC(psion_asic9_device::frc1), this); + m_frc2_timer = timer_alloc(FUNC(psion_asic9_device::frc2), this); + m_watchdog_timer = timer_alloc(FUNC(psion_asic9_device::watchdog), this); + m_rtc_timer = timer_alloc(FUNC(psion_asic9_device::rtc), this); + m_snd_timer = timer_alloc(FUNC(psion_asic9_device::snd), this); + m_busy_timer = timer_alloc(FUNC(psion_asic9_device::busy), this); + + m_a9_control = 0x00; + m_a9_status = 0x00; + + save_item(NAME(m_a9_control)); + save_item(NAME(m_a9_lcd_size)); + save_item(NAME(m_a9_interrupt_status)); + save_item(NAME(m_a9_interrupt_mask)); + save_item(NAME(m_a9_protection_mode)); + save_item(NAME(m_a9_protection_upper)); + save_item(NAME(m_a9_protection_lower)); + save_item(NAME(m_a9_port_ab_ddr)); + save_item(NAME(m_a9_port_c_ddr)); + save_item(NAME(m_a9_port_d_ddr)); + save_item(NAME(m_a9_psel_6000)); + save_item(NAME(m_a9_psel_7000)); + save_item(NAME(m_a9_psel_8000)); + save_item(NAME(m_a9_psel_9000)); + save_item(NAME(m_a9_control_extra)); + save_item(NAME(m_frc1_count)); + save_item(NAME(m_frc1_reload)); + save_item(NAME(m_frc2_count)); + save_item(NAME(m_frc2_reload)); + save_item(NAME(m_watchdog_count)); + save_item(NAME(m_rtc)); +} + +uint8_t psion_asic9_device::get_ram_type(uint32_t ram_size) +{ + switch (ram_size) + { + case 0x010000: case 0x020000: + return 0; + + case 0x040000: case 0x080000: + return 1; + + case 0x100000: case 0x200000: + return 2; + + case 0x400000: case 0x800000: + return 3; + } + return 0; +} + +uint8_t psion_asic9mx_device::get_ram_type(uint32_t ram_size) +{ + switch (ram_size) + { + case 0x0080000: case 0x0100000: + return 0; + + case 0x0200000: case 0x0400000: + return 1; + + case 0x0800000: case 0x1000000: + return 2; + + case 0x2000000: case 0x4000000: + return 3; + } + return 0; +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void psion_asic9_device::device_reset() +{ + m_tick_timer->adjust(attotime::from_hz(32.768), 0, attotime::from_hz(32.768)); + m_frc1_timer->adjust(attotime::from_hz(512000), 0, attotime::from_hz(512000)); + m_frc2_timer->adjust(attotime::from_hz(512000), 0, attotime::from_hz(512000)); + m_watchdog_timer->adjust(attotime::from_hz(4), 0, attotime::from_hz(4)); + m_rtc_timer->adjust(attotime::from_hz(1), 0, attotime::from_hz(1)); + m_snd_timer->adjust(attotime::from_hz(8000), 0, attotime::from_hz(8000)); // unknown data rate + + m_post = 0x00; + + m_a9_interrupt_status = 0x00; + m_a9_interrupt_mask = 0x00; + + m_frc1_count = 0; + m_frc1_reload = 0; + m_frc2_count = 0; + m_frc2_reload = 0; + m_buz_toggle = 0; + m_watchdog_count = 0; + m_a9_protection_mode = false; + m_a9_protection_lower = 0x00; + m_a9_protection_upper = 0x00; + m_a9_port_ab_ddr = 0x00; + m_a9_port_c_ddr = 0x00; + m_a9_port_d_ddr = 0x00; + m_a9_psel_6000 = 0x00; + m_a9_psel_7000 = 0x00; + m_a9_psel_8000 = 0x00; + m_a9_psel_9000 = 0x00; + m_a9_control_extra = 0x00; + m_rtc = 0; + + m_a9_status |= 0x0020; // A9MMainsPresent + m_a9_status |= 0xe000; // A9MCold + + m_a9_serial_control = 0x00; + m_a9_channel_select = 0x00; +} + + +TIMER_CALLBACK_MEMBER(psion_asic9_device::tick) +{ + m_a9_interrupt_status |= 0x02; // Timer + update_interrupts(); +} + +TIMER_CALLBACK_MEMBER(psion_asic9_device::frc1) +{ + switch (--m_frc1_count) + { + case 0x0000: + if (BIT(m_a9_control_extra, 5)) // A9MBuzzFromFrc1OrTog + { + m_buz_cb(m_buz_toggle ^= 1); + } + m_a9_interrupt_status |= 0x40; // A9MFrc1 + update_interrupts(); + break; + + case 0xffff: + if (BIT(m_a9_control, 12)) // A9MFrc1PreScale + m_frc1_count = m_frc1_reload; + break; + } +} + +TIMER_CALLBACK_MEMBER(psion_asic9_device::frc2) +{ + switch (--m_frc2_count) + { + case 0x0000: + m_a9_interrupt_status |= 0x80; // A9MFrc2 + update_interrupts(); + break; + + case 0xffff: + if (BIT(m_a9_control, 14)) // A9MFrc2PreScale + m_frc2_count = m_frc2_reload; + break; + } +} + +TIMER_CALLBACK_MEMBER(psion_asic9_device::watchdog) +{ + m_watchdog_count++; + m_watchdog_count &= 3; + + if (m_watchdog_count == 3) + { + m_a9_status |= 0x0001; // A9MWatchDogNMI + update_interrupts(); + } +} + +TIMER_CALLBACK_MEMBER(psion_asic9_device::rtc) +{ + m_rtc++; +} + +TIMER_CALLBACK_MEMBER(psion_asic9_device::snd) +{ + if (BIT(m_a9_control, 11)) // A9MSoundEnable + { + switch (BIT(m_a9_control_extra, 7)) // A9MSoundDir + { + case 0: + if (!m_snd_fifo.full()) + m_snd_fifo.enqueue(m_pcm_in()); + if (m_snd_fifo.full()) + m_a9_status |= 0x0800; + break; + + case 1: + if (!m_snd_fifo.empty()) + m_pcm_out(m_snd_fifo.dequeue()); + if (!m_snd_fifo.full()) + m_a9_status &= ~0x0800; + break; + } + m_a9_interrupt_status |= 0x01; // Sound + update_interrupts(); + } +} + +void psion_asic9_device::sds_int_w(int state) +{ + if (state) + m_a9_interrupt_status |= 0x04; // A9MSlave + else + m_a9_interrupt_status &= ~0x04; + + update_interrupts(); +} + +void psion_asic9_device::eint0_w(int state) +{ + if (state) + m_a9_interrupt_status |= 0x08; // A9MExpIntC + else + m_a9_interrupt_status &= ~0x08; + + update_interrupts(); +} + +void psion_asic9_device::eint1_w(int state) +{ + if (state) + m_a9_interrupt_status |= 0x10; // A9MExpIntA + else + m_a9_interrupt_status &= ~0x10; + + update_interrupts(); +} + +void psion_asic9_device::eint2_w(int state) +{ + if (state) + m_a9_interrupt_status |= 0x20; // A9MExpIntB + else + m_a9_interrupt_status &= ~0x20; + + update_interrupts(); +} + + +void psion_asic9_device::medchng_w(int state) +{ + if (state) + m_a9_status |= 0x04; // A9MDoorNMI + else + m_a9_status &= ~0x04; + + update_interrupts(); +} + +void psion_asic9_device::update_interrupts() +{ + bool irq = m_a9_interrupt_status & m_a9_interrupt_mask; + bool nmi = m_a9_status & 0x000f; + + m_v30->set_input_line(INPUT_LINE_IRQ0, irq ? ASSERT_LINE : CLEAR_LINE); + m_v30->set_input_line(INPUT_LINE_NMI, nmi ? ASSERT_LINE : CLEAR_LINE); +} + + +IRQ_CALLBACK_MEMBER(psion_asic9_device::inta_cb) +{ + // IRQ Vector Name Description + // 0 0x78 CSINT CODEC sound interrupt. + // 1 0x79 TINT 32 Hz tick interrupt. + // 2 0x7A SSINT Serial slave interrupt. + // 3 0x7B EINT0 External interrupt input 0. + // 4 0x7C EINT1 External interrupt input 1 (inverted). + // 5 0x7D EINT2 External interrupt input 2 (inverted). + // 6 0x7E FRC1OI FRC1 overflow interrupt. + // 7 0x7F FRC2OI FRC2 overflow interrupt. + uint8_t vector = 0x78; + for (int irq = 0; irq < 8; irq++) + { + if (m_a9_interrupt_status & m_a9_interrupt_mask & (1 << irq)) + { + vector += irq; + break; + } + } + return vector; +} + + +TIMER_CALLBACK_MEMBER(psion_asic9_device::busy) +{ + m_v30->set_input_line(NEC_INPUT_LINE_POLL, ASSERT_LINE); +} + + +//************************************************************************** +// RAM/ROM CONFIGURATION +//************************************************************************** + +uint32_t psion_asic9_device::ram_device_size(uint8_t device_type) +{ + uint32_t size = 0; + + // RAM is installed in pairs, ie. 256KBits = 2 x 32K + switch (device_type & 3) + { + case 0: size = 0x010000; break; // 256KBits + case 1: size = 0x040000; break; // 1MBits + case 2: size = 0x100000; break; // 4MBits + case 3: size = 0x400000; break; // 16MBits + } + return size; +} + +uint32_t psion_asic9mx_device::ram_device_size(uint8_t device_type) +{ + uint32_t size = 0; + + // TODO: unknown for A9MX but allow EPOC16 to correctly identify 2MB fitted to all MX machines. + switch (device_type & 3) + { + case 0: size = 0x0040000; break; + case 1: size = 0x0100000; break; + case 2: size = 0x0400000; break; + case 3: size = 0x1000000; break; + } + return size; +} + +void psion_asic9_device::configure_ram(uint8_t device_type) +{ + uint32_t device_size_actual = ram_device_size(m_ram_type); + uint32_t device_size_test = ram_device_size(device_type); + + m_ram_space->unmap_readwrite(0, 0xffffff); + + for (int i = 0; i < (m_ram->size() / device_size_actual); i++) + { + offs_t addrstart = device_size_test * i; + offs_t addrend = (device_size_test * i) + std::min(device_size_actual, device_size_test) - 1; + offs_t addrmirror = 0xffffff ^ ((device_size_test * 4) - 1); + + if (device_size_actual < device_size_test) + addrmirror ^= (device_size_test - device_size_actual); + + LOG("configure_ram: %d type %d ramsize %06x devsize %06x start %06x end %06x mirror %06x\n", i, device_type, m_ram->size(), device_size_actual, addrstart, addrend, addrmirror); + m_ram_space->install_ram(addrstart, addrend, addrmirror, m_ram->pointer() + (device_size_actual * i)); + } +} + +void psion_asic9_device::configure_rom() +{ + m_rom_space->install_rom(0x800000, 0x800000 + m_rom->bytes() - 1, 0x7fffff ^ (m_rom->bytes() - 1), m_rom->base()); +} + + +//************************************************************************** +// READ/WRITE HANDLERS +//************************************************************************** + +uint8_t psion_asic9_device::col_r() +{ + return m_a9_control_extra & 0x0f; +} + + +bool psion_asic9_device::is_protected(offs_t offset) +{ + if (m_a9_protection_mode && (offset <= m_a9_protection_lower || offset > m_a9_protection_upper)) + { + LOG("%s is_protected: %05x < %05x <= %05x\n", machine().describe_context(), m_a9_protection_lower, offset, m_a9_protection_upper); + m_a9_status |= 0x0002; // A9MProtectedModeNMI + update_interrupts(); + return true; + } + return false; +} + +offs_t psion_asic9_device::translate_address(offs_t offset) +{ + switch (offset & 0xf0000) + { + case 0x00000: case 0x10000: case 0x20000: case 0x30000: case 0x40000: case 0x50000: + break; + + case 0x60000: + offset = (m_a9_psel_6000 << 16) | (offset & 0xffff); + break; + + case 0x70000: + offset = (m_a9_psel_7000 << 16) | (offset & 0xffff); + break; + + case 0x80000: + offset = (m_a9_psel_8000 << 16) | (offset & 0xffff); + break; + + case 0x90000: + offset = (m_a9_psel_9000 << 16) | (offset & 0xffff); + break; + + case 0xa0000: case 0xb0000: case 0xc0000: case 0xd0000: case 0xe0000: case 0xf0000: + offset = 0xf00000 | offset; + break; + } + return offset; +} + +uint16_t psion_asic9_device::mem_r(offs_t offset, uint16_t mem_mask) +{ + uint16_t data = 0xffff; + + offset <<= 1; + + offs_t addr = translate_address(offset); + + switch (offset & 0x80000) + { + case 0x00000: + data = m_ram_space->read_word(addr, mem_mask); + break; + + case 0x80000: + data = m_rom_space->read_word(addr, mem_mask); + break; + } + + return data; +} + +void psion_asic9_device::mem_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + offset <<= 1; + + offs_t addr = translate_address(offset); + + if (!is_protected(addr)) + { + switch (offset & 0x80000) + { + case 0x00000: + m_ram_space->write_word(addr, data, mem_mask); + break; + } + } +} + + +uint16_t psion_asic9_device::io_r(offs_t offset, uint16_t mem_mask) +{ + uint16_t data = 0x00; + + offset <<= 1; + + switch (offset) + { + case 0x00: // A9Index - Not used + LOG("%s io_r: A9Index => %02x\n", machine().describe_context(), data); + break; + + case 0x02: // A9WControl + // b0 A9MDisableClockDivide + // b1 A9MDisableIoWait + // b2 A9MDisableMemWait + // b3-b4 A9MRamDeviceSize + // b5 A9MDisableDMADivide + // b6 A9MArmStandBy + // b7 A9MZeroIsGrayMode + // b8 A9MDoorNMIEnable + // b9 A9MLowBatNMIEnable + // b10 A9MLcdEnable + // b11 A9MSoundEnable + // b12 A9MFrc1PreScale + // b13 A9MFrc1Is512KHzOr1Hz + // b14 A9MFrc2PreScale + // b15 A9MFrc2Is512KHzOr1KHz + data = m_a9_control; // | 0x18; + LOG("%s io_r: A9WControl => %04x\n", machine().describe_context(), data); + break; + + case 0x04: // A9WStatus + // b0 A9MWatchDogNMI + // b1 A9MProtectedModeNMI + // b2 A9MDoorNMI + // b3 A9MLowBatNMI + // b4 A9MDoorSwitch + // b5 A9MMainsPresent + // b6 A9MSlaveClock + // b7 A9MKeyboard + // b8 A9MSlaveDataValid + // b9 A9MSlaveControlFrame + // b10 A9MSlaveOverrun + // b11 A9MFifoFull + // b12 A9MNoBattery + // b13 A9MReset + // b14 A9MPowerFail + // b15 A9MCold + data = m_a9_status; + if (!machine().side_effects_disabled()) + { + m_a9_status &= ~0x2000; // clear A9MReset + } + LOG("%s io_r: A9WStatus => %04x\n", machine().describe_context(), data); + break; + + case 0x06: // A9BInterruptStatus + if (ACCESSING_BITS_0_7) + { + data = m_a9_interrupt_status & m_a9_interrupt_mask; + LOG("%s io_r: A9InterruptStatus => %02x\n", machine().describe_context(), data); + } + break; + + case 0x08: // A9BInterruptMask + if (ACCESSING_BITS_0_7) + { + data = m_a9_interrupt_mask; + LOG("%s io_r: A9BInterruptMask => %02x\n", machine().describe_context(), data); + } + break; + + case 0x12: // A9WFrc1Data + data = m_frc1_count; + LOG("%s io_r: A9WFrc1Data => %04x\n", machine().describe_context(), data); + break; + + case 0x14: // A9BProtectionOff + if (ACCESSING_BITS_0_7) + { + //LOG("%s io_r: A9BProtectionOff => %02x\n", machine().describe_context(), data); + m_a9_protection_mode = false; + } + break; + + case 0x1a: // A9BSoundData + if (ACCESSING_BITS_0_7) + { + data = m_snd_fifo.dequeue(); + if (!m_snd_fifo.full()) + m_a9_status &= ~0x0800; + LOG("%s io_r: A9BSoundData => %02x\n", machine().describe_context(), data); + } + break; + + case 0x1e: // A9WFrc2Data + data = m_frc2_count; + LOG("%s io_r: A9WFrc2Data => %04x\n", machine().describe_context(), data); + break; + + case 0x20: // A9WPortABData + data = m_port_ab_r() & ~m_a9_port_ab_ddr; + LOG("%s io_r: A9WPortABData => %04x\n", machine().describe_context(), data); + break; + + case 0x22: // A9WPortABDDR + if (ACCESSING_BITS_0_7) + { + data |= m_a9_port_ab_ddr & 0x00ff; + LOG("%s io_r: A9WPortADDR => %02x\n", machine().describe_context(), data); + } + if (ACCESSING_BITS_8_15) + { + data |= m_a9_port_ab_ddr & 0xff00; + LOG("%s io_r: A9WPortBDDR => %02x\n", machine().describe_context(), data >> 8); + } + break; + + case 0x24: // A9WPortCDData + if (ACCESSING_BITS_0_7) + { + data |= 0x00; + LOG("%s io_r: A9WPortCData => %02x\n", machine().describe_context(), data); + } + if (ACCESSING_BITS_8_15) + { + data |= 0x00 << 8; + LOG("%s io_r: A9WPortDData => %02x\n", machine().describe_context(), data >> 8); + } + break; + + case 0x26: // A9BPortCDDDR + if (ACCESSING_BITS_0_7) + { + data |= m_a9_port_c_ddr; + LOG("%s io_r: A9BPortCDDR => %04x\n", machine().describe_context(), data); + } + if (ACCESSING_BITS_8_15) + { + data |= m_a9_port_d_ddr << 8; + LOG("%s io_r: A9WPortDDDR <= %02x\n", machine().describe_context(), data >> 8); + } + break; + + case 0x28: // A9BPageSelect6000 + if (ACCESSING_BITS_0_7) + { + data |= m_a9_psel_6000; + LOG("%s io_r: A9BPageSelect6000 => %02x\n", machine().describe_context(), data); + } + if (ACCESSING_BITS_8_15) + { + data |= m_a9_psel_7000 << 8; + LOG("%s io_r: A9BPageSelect7000 => %02x\n", machine().describe_context(), data >> 8); + } + break; + + case 0x2a: // A9BPageSelect8000 + if (ACCESSING_BITS_0_7) + { + data |= m_a9_psel_8000; + //LOG("%s io_r: A9BPageSelect8000 => %02x\n", machine().describe_context(), data); + } + if (ACCESSING_BITS_8_15) + { + data |= m_a9_psel_9000 << 8; + //LOG("%s io_r: A9BPageSelect9000 => %02x\n", machine().describe_context(), data >> 8); + } + break; + + case 0x2c: // A9WControlExtra + data = m_a9_control_extra; + LOG("%s io_r: A9WControlExtra => %04x\n", machine().describe_context(), data); + break; + + case 0x2e: // A9WPumpControl + LOG("%s io_r: A9WPumpControl <= %04x\n", machine().describe_context(), data); + break; + + case 0x80: // A9WRtcLSW + data = m_rtc & 0x0000ffff; + //LOG("%s io_r: A9WRtcLSW <= %04x\n", machine().describe_context(), data); + break; + + case 0x82: // A9WRtcMSW + data = (m_rtc & 0xffff0000) >> 16; + //LOG("%s io_r: A9WRtcMSW <= %04x\n", machine().describe_context(), data); + break; + + case 0x88: // A9BSlaveData + LOG("%s io_r: A9BSlaveData <= %02x\n", machine().describe_context(), data); + break; + + case 0x8a: // A9BSerialData + data = m_a9_serial_data; + if ((m_a9_serial_control & 0x10) == 0x10) + m_a9_serial_data = receive_frame(); + LOG("%s io_r: A9BSerialData => %02x\n", machine().describe_context(), data); + break; + + case 0x8e: // A9BChannelSelect + data = m_a9_channel_select; + break; + + default: + logerror("%s io_r: Unhandled register %02x => %04x\n", machine().describe_context(), offset, data); + break; + } + return data; +} + +void psion_asic9_device::io_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + offset <<= 1; + + switch (offset) + { + case 0x00: // Post + if (m_post != data) + { + switch (data) + { + case 0x10: LOG("%s io_w: Post <= %02x ROM Test\n", machine().describe_context(), data); break; + case 0x20: LOG("%s io_w: Post <= %02x System RAM Test\n", machine().describe_context(), data); break; + case 0x30: LOG("%s io_w: Post <= %02x Video RAM Test\n", machine().describe_context(), data); break; + case 0xf0: LOG("%s io_w: Post <= %02x Complete\n", machine().describe_context(), data); break; + default: LOG("%s io_w: Post <= %02x Unknown\n", machine().describe_context(), data); break; + } + m_post = data; + } + break; + + case 0x02: // A9WControl + // b0 A9MDisableClockDivide + // b1 A9MDisableIoWait + // b2 A9MDisableMemWait + // b3-b4 A9MRamDeviceSize + // b5 A9MDisableDMADivide + // b6 A9MArmStandBy + // b7 A9MZeroIsGrayMode + // b8 A9MDoorNMIEnable + // b9 A9MLowBatNMIEnable + // b10 A9MLcdEnable + // b11 A9MSoundEnable + // b12 A9MFrc1PreScale + // b13 A9MFrc1Is512KHzOr1Hz + // b14 A9MFrc2PreScale + // b15 A9MFrc2Is512KHzOr1KHz + LOG("%s io_w: A9WControl <= %04x, RamDeviceSize %s, Frc1 %s%s, Frc2 %s%s\n", machine().describe_context(), data, + BIT(data, 3,2) == 0 ? "256KBits" : (BIT(data, 3,2) == 1 ? "1MBits" : (BIT(data, 3,2) == 2 ? "4MBits" : "16MBits")), + BIT(data, 13) ? "512KHz" : "1Hz", BIT(data, 12) ? " PreScale" : "", + BIT(data, 15) ? "512KHz" : "1024Hz", BIT(data, 14) ? " PreScale" : ""); + + if (BIT(data, 3, 2) != BIT(m_a9_control, 3, 2)) + { + configure_ram(BIT(data, 3, 2)); + } + if (BIT(data, 13) != BIT(m_a9_control, 13)) + { + if (data & 0x2000) + m_frc1_timer->adjust(attotime::zero, 0, attotime::from_hz(512000)); + else + m_frc1_timer->adjust(attotime::zero, 0, attotime::from_hz(1)); + } + if (BIT(data, 15) != BIT(m_a9_control, 15)) + { + if (data & 0x8000) + m_frc2_timer->adjust(attotime::zero, 0, attotime::from_hz(512000)); + else + m_frc2_timer->adjust(attotime::zero, 0, attotime::from_hz(1024)); + } + m_a9_control = data; + break; + + case 0x04: // A9WLcdSize + // b0-b10 A9MLcdNumberOfPixels - (end of frame address / 16) - 1 + // b11-b15 A9MLcdLineLength - (number of pixels per line / 32) - 1 + LOG("%s io_w: A9WLcdSize <= %04x, Pixels in line %d, Total pixels in display %d\n", machine().describe_context(), data, (BIT(data, 11, 5) + 1) * 32, (BIT(data, 0, 11) + 1) * 128); + m_a9_lcd_size = data; + break; + + case 0x06: // A9WLcdControl + // b0-b4 LcdPixelRate - 3 + // b5-b9 LcdACLineRate - 13 + // b10-b11 LcdMode - 3 (Dual Screen mode) + LOG("%s io_w: A9WLcdControl <= %04x, Mode %d %s Page\n", machine().describe_context(), data, BIT(data, 10,2), BIT(data, 10,2) == 3 ? "Dual" : "Single"); + break; + + case 0x08: // A9BInterruptMask + if (ACCESSING_BITS_0_7) + { + // b0 A9MSound + // b1 A9MTimer + // b2 A9MSlave + // b3 A9MExpIntC + // b4 A9MExpIntA + // b5 A9MExpIntB + // b6 A9MFrc1 + // b7 A9MFrc2 + LOG("%s io_w: A9BInterruptMask <= %02x\n", machine().describe_context(), data & 0xff); + m_a9_interrupt_mask = data & 0xff; + } + if (ACCESSING_BITS_8_15) + { + LOG("%s io_w: A9BNmiClear <= %02x\n", machine().describe_context(), data >> 8); + m_a9_status &= 0xfff0; + } + update_interrupts(); + break; + + case 0x0a: // A9BNonSpecificEoi + if (ACCESSING_BITS_0_7) + { + LOG("%s io_w: A9BNonSpecificEoi <= %02x\n", machine().describe_context(), data & 0xff); + } + if (ACCESSING_BITS_8_15) + { + LOG("%s io_w: A9BStartFlagClear <= %02x\n", machine().describe_context(), data >> 8); + } + break; + + case 0x0c: // A9BTimerEoiW + if (ACCESSING_BITS_0_7) + { + LOG("%s io_w: A9BTimerEoi <= %02x\n", machine().describe_context(), data & 0xff); + m_a9_interrupt_status &= ~0x02; // Timer + } + if (ACCESSING_BITS_8_15) + { + LOG("%s io_w: A9BSerialSlaveEoi <= %02x\n", machine().describe_context(), data >> 8); + m_a9_interrupt_status &= ~0x04; // SerialSlave + } + update_interrupts(); + break; + + case 0x0e: // A9Frc1Eoi + if (ACCESSING_BITS_0_7) + { + LOG("%s io_w: A9Frc1Eoi <= %02x\n", machine().describe_context(), data & 0xff); + m_a9_interrupt_status &= ~0x40; // Frc1Expired + } + if (ACCESSING_BITS_8_15) + { + LOG("%s io_w: A9Frc2Eoi <= %02x\n", machine().describe_context(), data >> 8); + m_a9_interrupt_status &= ~0x80; // Frc2Expired + } + update_interrupts(); + break; + + case 0x10: // A9WResetWatchDog + //LOG("%s io_w: A9WResetWatchDog <= %04x\n", machine().describe_context(), data); + m_watchdog_count = 0; + break; + + case 0x12: // A9WFrc1Data + LOG("%s io_w: A9WFrc1Data <= %04x\n", machine().describe_context(), data); + m_frc1_reload = data; + m_frc1_count = data; + break; + + case 0x14: // A9BProtectionOn + if (ACCESSING_BITS_0_7) + { + //LOG("%s io_w: A9BProtectionOn <= %02x\n", machine().describe_context(), data); + m_a9_protection_mode = true; + } + if (ACCESSING_BITS_8_15) + { + //LOG("%s io_w: A9BProtectionOff <= %02x\n", machine().describe_context(), data); + m_a9_protection_mode = false; + } + break; + + case 0x16: // A9WProtectionUpper + //LOG("%s io_w: A9WProtectionUpper <= %04x\n", machine().describe_context(), data); + m_a9_protection_upper = (data << 4) | 0x0f; + break; + + case 0x18: // A9WProtectionLower + //LOG("%s io_w: A9WProtectionLower <= %04x\n", machine().describe_context(), data); + m_a9_protection_lower = data << 4; + break; + + case 0x1a: // A9BSoundData + if (ACCESSING_BITS_0_7) + { + LOG("%s io_w: A9BSoundData <= %04x\n", machine().describe_context(), data); + m_snd_fifo.enqueue(data & 0xff); + if (m_snd_fifo.full()) + m_a9_status |= 0x0800; + } + break; + + case 0x1c: // A9BSoundEoi + if (ACCESSING_BITS_0_7) + { + LOG("%s io_w: A9BSoundEoi <= %02x\n", machine().describe_context(), data); + m_a9_interrupt_status &= ~0x01; // Sound + update_interrupts(); + } + break; + + case 0x1e: // A9WFrc2Data + LOG("%s io_w: A9WFrc2Data <= %04x\n", machine().describe_context(), data); + m_frc2_reload = data; + m_frc2_count = data; + break; + + case 0x20: // A9WPortABData + if (ACCESSING_BITS_0_7) + { + LOG("%s io_w: A9WPortAData <= %02x\n", machine().describe_context(), data & 0xff); + } + if (ACCESSING_BITS_8_15) + { + LOG("%s io_w: A9WPortBData <= %02x\n", machine().describe_context(), data >> 8); + } + break; + + case 0x22: // A9WPortABDDR + if (ACCESSING_BITS_0_7) + { + LOG("%s io_w: A9WPortADDR <= %02x\n", machine().describe_context(), data & 0xff); + m_a9_port_ab_ddr = (m_a9_port_ab_ddr & 0xff00) | data; + } + if (ACCESSING_BITS_8_15) + { + LOG("%s io_w: A9WPortBDDR <= %02x\n", machine().describe_context(), data >> 8); + m_a9_port_ab_ddr = (m_a9_port_ab_ddr & 0x00ff) | (data << 8); + } + break; + + case 0x24: // A9WPortCDData + if (ACCESSING_BITS_0_7) + { + LOG("%s io_w: A9WPortCData <= %02x\n", machine().describe_context(), data & 0xff); + } + if (ACCESSING_BITS_8_15) + { + LOG("%s io_w: A9WPortDData <= %02x\n", machine().describe_context(), data >> 8); + } + break; + + case 0x26: // A9BPortCDDDR + if (ACCESSING_BITS_0_7) + { + LOG("%s io_w: A9WPortCDDR <= %02x\n", machine().describe_context(), data & 0xff); + m_a9_port_c_ddr = data & 0xff; + } + if (ACCESSING_BITS_8_15) + { + LOG("%s io_w: A9WPortDDDR <= %02x\n", machine().describe_context(), data >> 8); + m_a9_port_d_ddr = data >> 8; + } + break; + + case 0x28: // A9BPageSelect6000 + if (ACCESSING_BITS_0_7) + { + LOG("%s io_w: A9BPageSelect6000 <= %02x\n", machine().describe_context(), data & 0xff); + m_a9_psel_6000 = data & 0xff; + } + if (ACCESSING_BITS_8_15) + { + LOG("%s io_w: A9BPageSelect7000 <= %02x\n", machine().describe_context(), data >> 8); + m_a9_psel_7000 = data >> 8; + } + break; + + case 0x2a: // A9BPageSelect8000 + if (ACCESSING_BITS_0_7) + { + //LOG("%s io_w: A9BPageSelect8000 <= %02x\n", machine().describe_context(), data & 0xff); + m_a9_psel_8000 = data & 0xff; + } + if (ACCESSING_BITS_8_15) + { + //LOG("%s io_w: A9BPageSelect9000 <= %02x\n", machine().describe_context(), data >> 8); + m_a9_psel_9000 = data >> 8; + } + break; + + case 0x2c: // A9WControlExtra + // b0-b3 A9MKeyCol + // b4 A9MBuzzTog + // b5 A9MBuzzFromFrc1OrTog + // b6 A9MExonDisable + // b7 A9MSoundDir + // b8 A9MClockEnable1 + // b9 A9MClockEnable2 + // b10 A9MClockEnable3 + // b11 A9MClockEnable4 + // b12 A9MClockEnable5 + // b13-b14 A9MClkDiv + // b15 A9MSlaveIntEnable + LOG("%s io_w: A9WControlExtra <= %04x\n", machine().describe_context(), data); + m_a9_control_extra = data; + + // enable keyboard COLs + switch (data & 0xf) + { + case 0x0: m_col_cb(0xff); break; + case 0x8: m_col_cb(0x01); break; + case 0x9: m_col_cb(0x02); break; + case 0xa: m_col_cb(0x04); break; + case 0xb: m_col_cb(0x08); break; + case 0xc: m_col_cb(0x10); break; + case 0xd: m_col_cb(0x20); break; + case 0xe: m_col_cb(0x40); break; + case 0xf: m_col_cb(0x80); break; + default: m_col_cb(0x00); break; + } + + if (!BIT(data, 5)) + { + m_buz_cb(BIT(data, 4)); + } + break; + + case 0x2e: // A9WPumpControl + LOG("%s io_w: A9WPumpControl <= %04x\n", machine().describe_context(), data); + break; + + case 0x80: // A9WRtcLSW + //LOG("%s io_w: A9WRtcLSW <= %04x\n", machine().describe_context(), data); + m_rtc = (m_rtc & 0xffff0000) | data; + break; + + case 0x82: // A9WRtcMSW + //LOG("%s io_w: A9WRtcMSW <= %04x\n", machine().describe_context(), data); + m_rtc = (m_rtc & 0x0000ffff) | (data << 16); + break; + + case 0x84: // A9WNullFrame + LOG("%s io_w: A9WNullFrame <= %02x\n", machine().describe_context(), data); + transmit_frame(NULL_FRAME); + break; + + case 0x8a: // A9BSerialData + LOG("%s io_w: A9BSerialData <= %02x\n", machine().describe_context(), data & 0xff); + if ((m_a9_serial_control & 0xc0) == 0x80) + transmit_frame(DATA_FRAME | (data & 0xff)); + break; + + case 0x8c: // A9BSerialControl - Serial channel write control register + // WriteSingle 10000000b; ReadSingle 11000000b + // WriteMulti 10010000b; ReadMulti 11010000b + // Reset 00000000b; Select 01000000b + // Asic2SlaveId 001h; Asic5PackId 002h + // Asic5NormalId 003h; Asic6Id 004h + // Asic8Id 005h; Asic4Id 006h + LOG("%s io_w: A2SerialControl <= %02x\n", machine().describe_context(), data & 0xff); + m_a9_serial_control = data & 0xff; + transmit_frame(CONTROL_FRAME | m_a9_serial_control); + + if ((m_a9_serial_control & 0x40) == 0x40) + m_a9_serial_data = receive_frame(); + break; + + case 0x8e: // A9BChannelSelect - Serial channel select register + // b0 Pack1Enable - 1 to select pack 1 + // b1 Pack2Enable - 1 to select pack 2 + // b2 Pack3Enable - 1 to select pack 3 + // b3 Pack4Enable - 1 to select pack 4 + // b4 Pack5Enable - 1 to select pack 5 + // b5-b6 SerialClockRate - 0 Medium, 1 Special, 2 Slow, 3 Fast + // b7 MultiplexEnable - 1 to loop slave channel to pack channel + LOG("%s io_w: A9BChannelSelect <= %02x Channels %c %c %c %c %c\n", machine().describe_context(), data & 0xff, + BIT(data, 0) ? '0' : ' ', + BIT(data, 1) ? '1' : ' ', + BIT(data, 2) ? '2' : ' ', + BIT(data, 3) ? '3' : ' ', + BIT(data, 4) ? '4' : ' '); + m_a9_channel_select = data & 0xff; + break; + + default: + logerror("%s io_w: Unhandled register %02x <= %04x\n", machine().describe_context(), offset, data); + break; + } +} + + +//------------------------------------------------- +// SIBO Serial Protocol Controller +//------------------------------------------------- + +bool psion_asic9_device::channel_active(int channel) +{ + switch (channel) + { + case 0: case 1: case 2: case 3: case 4: + return BIT(m_a9_channel_select, channel); + } + return false; +} + +void psion_asic9_device::transmit_frame(uint16_t data) +{ + m_busy_timer->adjust(attotime::from_ticks(12, clock() / 2)); + m_v30->set_input_line(NEC_INPUT_LINE_POLL, CLEAR_LINE); + + for (int ch = 0; ch < 8; ch++) + { + if (channel_active(ch)) + { + LOG("%s Channel %d Transmit %s frame %02x\n", machine().describe_context(), ch, (data & DATA_FRAME) ? "Data" : (data & CONTROL_FRAME) ? "Control" : "Null", data & 0xff); + m_data_w[ch](data); + } + } +} + +uint8_t psion_asic9_device::receive_frame() +{ + uint8_t data = 0x00; + + m_busy_timer->adjust(attotime::from_ticks(12, clock() / 2)); + m_v30->set_input_line(NEC_INPUT_LINE_POLL, CLEAR_LINE); + + for (int ch = 0; ch < 8; ch++) + { + if (channel_active(ch)) + { + data |= m_data_r[ch](); + LOG("%s Channel %d Receive Data frame %02x\n", machine().describe_context(), ch, data); + } + } + + return data; +} + + +//------------------------------------------------- +// LCD Controller +//------------------------------------------------- + +uint32_t psion_asic9_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + if (m_a9_control & 0x0400) // LCD enable bit + { + pen_t const *const pens = screen.palette().pens(); + + int const width = (BIT(m_a9_lcd_size, 11, 5) + 1) * 32; + uint16_t const size = (BIT(m_a9_lcd_size, 0, 11) + 1) * 16; + + for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++) + { + for (int x = screen.visible_area().min_x; x <= (screen.visible_area().max_x / 8); x++) + { + uint8_t const black = m_ram_space->read_byte(0x0400 + y * (width / 8) + x); + uint8_t const grey = m_ram_space->read_byte(0x0400 + size + y * (width / 8) + x); + uint16_t *p = &bitmap.pix(y, x << 3); + for (int i = 0; i < 8; i++) + *p++ = BIT(black, i) ? pens[1] : (BIT(grey, i) ? pens[2] : pens[0]); + } + } + } + else + { + bitmap.fill(0, cliprect); + } + return 0; +} |