diff options
Diffstat (limited to 'src/devices/machine/psion_asic1.cpp')
-rw-r--r-- | src/devices/machine/psion_asic1.cpp | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/src/devices/machine/psion_asic1.cpp b/src/devices/machine/psion_asic1.cpp new file mode 100644 index 00000000000..f733c55a590 --- /dev/null +++ b/src/devices/machine/psion_asic1.cpp @@ -0,0 +1,484 @@ +// license:BSD-3-Clause +// copyright-holders:Nigel Barnes +/****************************************************************************** + + Psion ASIC1 + + ASIC1 is the main system controller chip for the SIBO architecture. It connects + directly to the 8086-based processor (i.e. the V30H) controlling all bus cycles + to and from the processor. This configuration effectively forms a micro-controller + like device that executes 8086 instruction codes. ASIC 1 is made up of a number of + functional blocks including a bus controller, a programmable timer, an eight input + interrupt controller, an LCD controller and the memory decoding circuitry. + +******************************************************************************/ + +#include "emu.h" +#include "psion_asic1.h" +#include "screen.h" + + +#define VERBOSE 0 +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" + + +DEFINE_DEVICE_TYPE(PSION_ASIC1, psion_asic1_device, "psion_asic1", "Psion ASIC1") + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +psion_asic1_device::psion_asic1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, PSION_ASIC1, tag, owner, clock) + , device_memory_interface(mconfig, *this) + , device_video_interface(mconfig, *this) + , m_space_config("program", ENDIANNESS_LITTLE, 16, 20, 0) + , m_tick_timer(nullptr) + , m_frc_timer(nullptr) + , m_watchdog_timer(nullptr) + , m_int_cb(*this) + , m_nmi_cb(*this) + , m_frcovl_cb(*this) + , m_laptop_mode(false) +{ +} + + +//------------------------------------------------- +// memory_space_config - return a description of +// any address spaces owned by this device +//------------------------------------------------- + +device_memory_interface::space_config_vector psion_asic1_device::memory_space_config() const +{ + return space_config_vector{ + std::make_pair(0, &m_space_config) + }; +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void psion_asic1_device::device_start() +{ + m_space = &space(); + + m_tick_timer = timer_alloc(FUNC(psion_asic1_device::tick), this); + m_frc_timer = timer_alloc(FUNC(psion_asic1_device::frc), this); + m_watchdog_timer = timer_alloc(FUNC(psion_asic1_device::watchdog), this); + + m_a1_status = 0x00; + + save_item(NAME(m_a1_status)); + save_item(NAME(m_a1_lcd_size)); + save_item(NAME(m_a1_lcd_control)); + save_item(NAME(m_a1_interrupt_status)); + save_item(NAME(m_a1_interrupt_mask)); + save_item(NAME(m_a1_protection_mode)); + save_item(NAME(m_a1_protection_lower)); + save_item(NAME(m_a1_protection_upper)); + save_item(NAME(m_frc_count)); + save_item(NAME(m_frc_reload)); + save_item(NAME(m_watchdog_count)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void psion_asic1_device::device_reset() +{ + m_tick_timer->adjust(attotime::from_hz(4), 0, attotime::from_hz(4)); + m_frc_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_frc_count = 0; + m_frc_reload = 0; + m_frc_ovl = 0; + m_watchdog_count = 0; + + m_a1_interrupt_status = 0x00; + m_a1_interrupt_mask = 0x00; + + m_a1_protection_mode = false; + m_a1_protection_lower = 0x00; + m_a1_protection_upper = 0x00; +} + + +TIMER_CALLBACK_MEMBER(psion_asic1_device::tick) +{ + m_a1_interrupt_status |= 0x01; // Timer + update_interrupts(); +} + +TIMER_CALLBACK_MEMBER(psion_asic1_device::frc) +{ + switch (--m_frc_count) + { + case 0x0000: + m_frcovl_cb(m_frc_ovl ^= 1); + + m_a1_interrupt_status |= 0x20; // FrcExpired + update_interrupts(); + break; + + case 0xffff: + if (BIT(m_a1_status, 0)) // FrcMode + m_frc_count = m_frc_reload; + break; + } +} + +TIMER_CALLBACK_MEMBER(psion_asic1_device::watchdog) +{ + m_watchdog_count++; + m_watchdog_count &= 3; + + if (m_watchdog_count == 3) + { + m_a1_status |= 0x0100; // WatchDogNmi + update_interrupts(); + } +} + +void psion_asic1_device::eint1_w(int state) +{ + if (state) + m_a1_interrupt_status |= 0x04; // ExpIntRightB + else + m_a1_interrupt_status &= ~0x04; + + update_interrupts(); +} + +void psion_asic1_device::eint2_w(int state) +{ + if (state) + m_a1_interrupt_status |= 0x08; // ExpIntLeftA + else + m_a1_interrupt_status &= ~0x08; + + update_interrupts(); +} + +void psion_asic1_device::eint3_w(int state) +{ + if (state) + m_a1_interrupt_status |= 0x10; // Asic2Int + else + m_a1_interrupt_status &= ~0x10; + + update_interrupts(); +} + +void psion_asic1_device::enmi_w(int state) +{ + if (state) + m_a1_status |= 0x0200; // ExternalNmi + else + m_a1_status &= ~0x0200; + + update_interrupts(); +} + +void psion_asic1_device::update_interrupts(bool address_trap) +{ + bool irq = m_a1_interrupt_status & m_a1_interrupt_mask; + bool nmi = (m_a1_status & 0x0300) || address_trap; + + m_int_cb(irq ? ASSERT_LINE : CLEAR_LINE); + m_nmi_cb(nmi ? ASSERT_LINE : CLEAR_LINE); +} + + +IRQ_CALLBACK_MEMBER(psion_asic1_device::inta_cb) +{ + // IRQ Vector Name Description + // 0 0x78 TINT Tick interrupt at 2 or 32 Hz + // 1 0x79 EINT0 External interrupt usually connected to mains detect bit + // 2 0x7A EINT1 External interrupt from expansion port one + // 3 0x7B EINT2 External interrupt from expansion port two + // 4 0x7C EINT3 External interrupt from ASIC2 + // 5 0x7D OVINT Timer overflow interrupt + // 6 0x7E SRXI SLD sound receive interrupt + // 7 0x7F STXI SLD sound transmit interrupt + uint8_t vector = 0x78; + for (int irq = 0; irq < 8; irq++) + { + if (m_a1_interrupt_status & m_a1_interrupt_mask & (1 << irq)) + { + vector += irq; + break; + } + } + return vector; +} + + +//************************************************************************** +// READ/WRITE HANDLERS +//************************************************************************** + +bool psion_asic1_device::is_protected(offs_t offset) +{ + if (m_a1_protection_mode && (offset <= m_a1_protection_lower || offset > m_a1_protection_upper)) + { + LOG("%s is_protected: %05x < %05x <= %05x\n", machine().describe_context(), m_a1_protection_lower, offset, m_a1_protection_upper); + update_interrupts(true); + return true; + } + return false; +} + + +uint16_t psion_asic1_device::mem_r(offs_t offset, uint16_t mem_mask) +{ + return m_space->read_word(offset << 1, mem_mask); +} + +void psion_asic1_device::mem_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + if (!is_protected(offset << 1)) + m_space->write_word(offset << 1, data, mem_mask); +} + + +uint16_t psion_asic1_device::io_r(offs_t offset, uint16_t mem_mask) +{ + uint16_t data = 0x00; + + switch (offset << 1) + { + case 0x02: // A1Status + // b0 FrcMode + // b1 TickRate + // b2 FrcSource + // b3 Ram128 + // b4 Ram512 + // b5 LcdEnable + // b6 A1SldEnable + // b7 SldTx + // b8 WatchDogNmi + // b9 ExternalNmi + // b10 Rtc32Hz + // b11 ComboBusy + // b12 SldMsw + // b13 Rtc4Hz + // b14-b15 LcdData - LCD id as follows: + // 0 = Lcd640X400 + // 1 = Lcd640X200Small + // 2 = Lcd640X200Big + // 3 = Lcd720X348 + // 4 = Lcd160X80 + data = m_a1_status; + data |= lcd_type() << 14; + LOG("%s io_r: A1Status => %04x\n", machine().describe_context(), data); + break; + + case 0x06: // A1InterruptStatus + data = m_a1_interrupt_status & m_a1_interrupt_mask; + LOG("%s io_r: A1InterruptStatus => %02x\n", machine().describe_context(), data); + break; + + case 0x08: // A1InterruptMask + data = m_a1_interrupt_mask; + LOG("%s io_r: A1InterruptMask => %02x\n", machine().describe_context(), data); + break; + + case 0x12: // A1FrcControl + data = m_frc_count; + LOG("%s io_r: A1FrcControl => %04x\n", machine().describe_context(), data); + break; + + case 0x14: // A1ProtectionOff + if (!machine().side_effects_disabled()) + { + //LOG("%s io_r: A1ProtectionOff => %04x\n", machine().describe_context(), data); + m_a1_protection_mode = false; + } + break; + + default: + data = 0xffff; + LOG("%s io_r: Unhandled register %02x => %04x\n", machine().describe_context(), offset << 1, data); + break; + } + return data; +} + +void psion_asic1_device::io_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + switch (offset << 1) + { + case 0x02: // A1Control + // b0 FrcMode + // b1 TickRate + // b2 FrcSource + // b3 Ram128 + // b4 Ram512 + // b5 LcdEnable + // b6 A1SldEnable + // b7 SldTx + LOG("%s io_w: A1Control <= %04x\n", machine().describe_context(), data); + if (BIT(data, 1) != BIT(m_a1_status, 1)) + { + if (data & 0x02) // TickRate + m_tick_timer->adjust(attotime::zero, 0, attotime::from_hz(32.768)); // RTC from PS34 + else + m_tick_timer->adjust(attotime::zero, 0, attotime::from_hz(4)); + } + m_a1_status = (m_a1_status & 0xff00) | (data & 0xff); + break; + + case 0x04: // A1LcdSize + // b0-b9 LcdEndOfFrame - (Total pixels in display / 128) - 1 + // b10-b14 LcdNumberOfPixels - (No. pixels in line / 32) - 1 + // b15 LcdMLineEnable - 1 to enable the M line magic. + LOG("%s io_w: A1LcdSize <= %04x, Pixels in line %d, Total pixels in display %d\n", machine().describe_context(), data, (BIT(data, 10, 5) + 1) * 32, (BIT(data, 0, 10) + 1) * 128); + m_a1_lcd_size = data; + break; + + case 0x06: // A1LcdControl + // b0-b4 LcdRate - LCDCLK = SYSCLK / (2*(n+1)) + // b5-b9 LcdMLineRate - 13 + // b10-b11 LcdMode - 3 (Dual Screen mode) + LOG("%s io_w: A1LcdControl <= %04x, LCD clock %dHz, %s\n", machine().describe_context(), data, clock() * 4 / (screen().width() * screen().height() * 2 * (BIT(data, 0, 5) + 1)), BIT(data, 10, 2) == 3 ? "Dual screen" : "Single screen"); + m_a1_lcd_control = data; + break; + + case 0x08: // A1InterruptMask + // b0 Timer + // b1 Mains + // b2 ExpIntRightB + // b3 ExpIntLeftA + // b4 Asic2Int + // b5 FrcExpired + // b6 SldReceive + // b7 SldTransmit + LOG("%s io_w: A1InterruptMask <= %02x\n", machine().describe_context(), data); + m_a1_interrupt_mask = data & 0xff; + update_interrupts(); + break; + + case 0x0a: // A1NonSpecificEoi + LOG("%s io_w: A1NonSpecificEoi <= %04x\n", machine().describe_context(), data); + break; + + case 0x0c: // A1TimerEoi + LOG("%s io_w: A1TimerEoi <= %04x\n", machine().describe_context(), data); + m_a1_interrupt_status &= ~0x01; // Timer + update_interrupts(); + break; + + case 0x0e: // A1FrcEoi + LOG("%s io_w: A1FrcEoi <= %04x\n", machine().describe_context(), data); + m_a1_interrupt_status &= ~0x20; // FrcExpired + update_interrupts(); + break; + + case 0x10: // A1ResetWatchDog + //LOG("% io_w: A1ResetWatchDog <= %04x\n", machine().describe_context(), data); + m_watchdog_count = 0; + break; + + case 0x12: // A1FrcControl + LOG("%s io_w: A1FrcControl <= %04x\n", machine().describe_context(), data); + m_frc_reload = data; + m_frc_count = data; + break; + + case 0x14: // A1ProtectionOn + //LOG("%s io_w: A1ProtectionOn <= %04x\n", machine().describe_context(), data); + m_a1_protection_mode = true; + break; + + case 0x16: // A1ProtectionUpper + //LOG("%s io_w: A1ProtectionUpper <= %04x\n", machine().describe_context(), data); + m_a1_protection_upper = (data << 4) | 0x0f; + break; + + case 0x18: // A1ProtectionLower + //LOG("%s io_w: A1ProtectionLower <= %04x\n", machine().describe_context(), data); + m_a1_protection_lower = data << 4; + break; + + case 0x1a: // A1SoundLsw + LOG("%s io_w: A1SoundLsw <= %04x\n", machine().describe_context(), data); + break; + + case 0x1c: // A1SoundMsw + LOG("%s io_w: A1SoundMsw <= %04x\n", machine().describe_context(), data); + break; + + case 0x1e: // A1SoundControl + LOG("%s io_w: A1SoundControl <= %04x\n", machine().describe_context(), data); + break; + + default: + LOG("%s io_w: Unhandled register %02x <= %04x\n", machine().describe_context(), offset << 1, data); + break; + } +} + + +//------------------------------------------------- +// LCD Controller +//------------------------------------------------- + +uint8_t psion_asic1_device::lcd_type() +{ + if (m_laptop_mode) + { + switch (screen().height()) + { + case 400: return 0; + case 200: return 1; + } + } + return 0; +} + +uint32_t psion_asic1_device::screen_update_single(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + return screen_update(screen, bitmap, cliprect, 1); +} + +uint32_t psion_asic1_device::screen_update_dual(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + return screen_update(screen, bitmap, cliprect, 2); +} + +uint32_t psion_asic1_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int screens) +{ + if (m_a1_status & 0x0020) // LCD enable bit + { + pen_t const *const pens = screen.palette().pens(); + + offs_t videoram = m_laptop_mode ? 0xb8000 : 0x00400; + int const width = (BIT(m_a1_lcd_size, 10, 5) + 1) * 32; + int const height = screen.height() / screens; + + for (int vmap = 0; vmap < screens; vmap++) + { + for (int y = screen.visible_area().min_y; y <= screen.visible_area().max_y / screens; y++) + { + for (int x = screen.visible_area().min_x; x <= (screen.visible_area().max_x / 8); x++) + { + uint8_t const pixels = m_space->read_byte(videoram + (vmap << 14) + (y * (width / 8)) + x); + uint16_t *p = &bitmap.pix((vmap * height) + y, x << 3); + for (int i = 0; i < 8; i++) + *p++ = pens[BIT(pixels, i)]; + } + } + } + } + else + { + bitmap.fill(0, cliprect); + } + return 0; +} |