From c909f1a94382a4551ec73f810093ef76ab90fcac Mon Sep 17 00:00:00 2001 From: George Wright Date: Wed, 24 Jun 2026 20:32:47 -0700 Subject: [PATCH] series7: add initial asic14 implementation --- CMakeLists.txt | 2 + WindCore/series7/asic14.cpp | 458 ++++++++++++++++++++++++++++++++++++ WindCore/series7/asic14.h | 177 ++++++++++++++ WindCore/series7/eeprom.cpp | 76 ++++++ WindCore/series7/eeprom.h | 35 +++ WindCore/series7/sa1100.cpp | 161 ++++++++++++- 6 files changed, 907 insertions(+), 2 deletions(-) create mode 100644 WindCore/series7/asic14.cpp create mode 100644 WindCore/series7/asic14.h create mode 100644 WindCore/series7/eeprom.cpp create mode 100644 WindCore/series7/eeprom.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e1335ed..13d03ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,8 @@ set(WIND_CORE_SOURCES WindCore/osaris/clps7600.cpp WindCore/series5mx/etna.cpp WindCore/series5mx/windermere.cpp + WindCore/series7/asic14.cpp + WindCore/series7/eeprom.cpp WindCore/series7/gpio_controller.cpp WindCore/series7/interrupt_controller.cpp WindCore/series7/lcd_controller.cpp diff --git a/WindCore/series7/asic14.cpp b/WindCore/series7/asic14.cpp new file mode 100644 index 0000000..a3b4f6e --- /dev/null +++ b/WindCore/series7/asic14.cpp @@ -0,0 +1,458 @@ +#include "asic14.h" + +#include "logger.h" +#include "series7_defs.h" + +namespace Series7 { + +ASIC14::ASIC14(SA1100::GPIOController* gpioController) + : mGPIOController(gpioController) { + initRegisters(); +} + +void ASIC14::initRegisters() { + mRegisterMode = GPIORegisterMode::Data; + + mSysCon = 0; + mStatus = 0; + memset(mPortA, 0, sizeof(mPortA)); + memset(mPortB, 0, sizeof(mPortB)); + memset(mPortC, 0, sizeof(mPortC)); + memset(mPortD, 0, sizeof(mPortD)); + mSspStatus = 0; + mSspControl = 0; + mEeControl = 0; + mEeData = 0; + mAdcRxFifo = 0; + mAdcTxFifo = 0; + mBiasControl = 0; + mCcflControl = 0; + memset(mTcPreload, 0, sizeof(mTcPreload)); + memset(mTcControl, 0, sizeof(mTcControl)); + memset(mTcIrqCount, 0, sizeof(mTcIrqCount)); + memset(mFgPreload, 0, sizeof(mFgPreload)); + mIrqStatus = 0; + mIrqMask = 0; + mPcmciaControl = 0; + mKeyboard = 0; + mIrqEdge = 0; + mVolControl = 0; + mExtStat = 0; + mSpiStatus = 0; + mSpiData = 0; + mSpiFn = 0; +} + +void ASIC14::reset() { + initRegisters(); + + // Initialize GPIO 10 to HIGH (inactive state for active-low interrupt line) + mGPIOController->setPinState(kGpioSharedInterruptPin, + SA1100::GPIOController::PinState::High); + + for (int i = 0; i < 8; i++) { + mKeyboardColumns[i] = 0; + } + + mTouchX = 0; + mTouchY = 0; + mTouchDown = false; + mTouchQueue.clear(); + + mCurrentCycles = 0; + + mAdcCompleteCycle = 0; + mAdcPendingData = 0; +} + +void ASIC14::tick(int64_t passedCycles) { + mCurrentCycles = passedCycles; + + // Delayed ADC completion + if (mAdcCompleteCycle != 0 && passedCycles >= mAdcCompleteCycle) { + mAdcCompleteCycle = 0; + mSpiData = mAdcPendingData; + mSpiStatus |= SpiStatus::AdcComplete; + mIrqStatus |= IrqMask::Adc; + } + + // Process queued touch release + if (mTouchReleaseTime.has_value()) { + if (passedCycles >= mTouchReleaseTime.value()) { + mTouchDown = false; + mTouchReleaseTime.reset(); + mTouchX = 0x8000; + mTouchY = 130; + mIrqStatus |= IrqMask::Pen; + } + } + + if (!mTouchReleaseTime.has_value() && !mTouchQueue.empty()) { + auto event = mTouchQueue.front(); + mTouchQueue.pop_front(); + + bool wasDown = mTouchDown; + if (event.down) { + mTouchDown = true; + + // The OS firmware applies the following affine transform to the ADC + // values: + // Pixel_X = (13528 * ADC_X - 5684764) / 65536 + // Pixel_Y = (8924 * ADC_Y - 1868548) / 65536 + // where (0,0) is the top-left corner of the active LCD area. + + // Emulated digitiser widget coordinates include a 71-pixel left margin: + int32_t pixelX = event.x - 71; + int32_t pixelY = event.y; + + // Invert the formula to supply the exact ADC values expected by the OS: + mTouchX = (int32_t)((pixelX * 65536LL + 5684764LL) / 13528LL); + mTouchY = (int32_t)((pixelY * 65536LL + 1868548LL) / 8924LL); + + mTouchX = std::max(0, mTouchX); + mTouchX = std::min(4095, mTouchX); + mTouchY = std::max(0, mTouchY); + mTouchY = std::min(4095, mTouchY); + + if (!wasDown) { + mIrqStatus |= IrqMask::Pen; + } + } else { + if (mTouchDown) { + mTouchReleaseTime = + passedCycles + Series7::CLOCK_SPEED / 1000; // 1ms hold + } + } + } + + mGPIOController->setPinState(kGpioSharedInterruptPin, + (mIrqStatus & mIrqMask) + ? SA1100::GPIOController::PinState::Low + : SA1100::GPIOController::PinState::High); +} + +void ASIC14::setKey(uint8_t column, uint8_t rowBit, bool pressed) { + if (column < 8) { + if (pressed) { + mKeyboardColumns[column] |= rowBit; + } else { + mKeyboardColumns[column] &= ~rowBit; + } + } +} + +uint8_t ASIC14::readKeyboard() const { + uint8_t kScan = mKeyboard & 0x0f; + if (kScan >= 8) + return mKeyboardColumns[kScan - 8]; + if (kScan == 0) { + uint8_t val = 0; + for (int i = 0; i < 8; i++) + val |= mKeyboardColumns[i]; + return val; + } + return 0; +} + +uint16_t ASIC14::read16(const uint32_t address) { + switch (address) { + case SYSCON: + LOG_REG_R("A14SysCon", address, mSysCon); + return mSysCon; + case STATUS: + LOG_REG_R("A14Status", address, mStatus); + return mStatus; + case PORTA: { + // Port A = keyboard row state (PADR). Column select = KEYBOARD lower 4 bits + // (8+col = 8..15). + uint16_t val = (mRegisterMode == GPIORegisterMode::Data) + ? readKeyboard() + : mPortA[mRegisterMode]; + LOG_REG_R("A14PortA", address, val); + return val; + } + case PORTB: { + uint16_t val = mPortB[mRegisterMode]; + val = (uint16_t)(mTouchDown ? (val & ~0x80u) : (val | 0x80u)); + LOG_REG_R("A14PortB", address, val); + return val; + } + case PORTC: + LOG_REG_R("A14PortC", address, mPortC[mRegisterMode]); + return mPortC[mRegisterMode]; + case PORTD: + LOG_REG_R("A14PortD", address, mPortD[mRegisterMode]); + return mPortD[mRegisterMode]; + case SSP_STATUS: + LOG_REG_R("A14SspStatus", address, mSspStatus); + return mSspStatus; + case SSP_CONTROL: + LOG_REG_R("A14SspControl", address, mSspControl); + return mSspControl; + case EE_CONTROL: + LOG_REG_R("A14EeControl", address, mEeControl); + return mEeControl; + case EE_DATA: + LOG_REG_R("A14EeData", address, mEeData); + return mEeData; + case ADC_RXTX_FIFO: + LOG_REG_R("A14RxFifo", address, mAdcRxFifo); + return mAdcRxFifo; + case BIAS_CONTROL: + LOG_REG_R("A14BiasControl", address, mBiasControl); + return mBiasControl; + case CCFL_CONTROL: + LOG_REG_R("A14CcflControl", address, mCcflControl); + return mCcflControl; + case TC1_PRELOAD: + LOG_REG_R("A14Tc1Preload", address, mTcPreload[0]); + return mTcPreload[0]; + case TC1_CONTROL: + LOG_REG_R("A14Tc1Control", address, mTcControl[0]); + return mTcControl[0]; + case TC1_IRQ_COUNT: + LOG_REG_R("A14Tc1IrqCount", address, mTcIrqCount[0]); + return mTcIrqCount[0]; + case TC2_PRELOAD: + LOG_REG_R("A14Tc2Preload", address, mTcPreload[1]); + return mTcPreload[1]; + case TC2_CONTROL: + LOG_REG_R("A14Tc2Control", address, mTcControl[1]); + return mTcControl[1]; + case TC2_IRQ_COUNT: + LOG_REG_R("A14Tc2IrqCount", address, mTcIrqCount[1]); + return mTcIrqCount[1]; + case FG1_PRELOAD: + LOG_REG_R("A14Fg1Preload", address, mFgPreload[0]); + return mFgPreload[0]; + case FG2_PRELOAD: + LOG_REG_R("A14Fg2Preload", address, mFgPreload[1]); + return mFgPreload[1]; + case IRQ_STATUS: + LOG_REG_R("A14IrqStatus", address, mIrqStatus); + return mIrqStatus; + case IRQ_MASK: + LOG_REG_R("A14IrqMask", address, mIrqMask); + return mIrqMask; + case PCMCIA_CONTROL: + LOG_REG_R("A14PcmciaControl", address, mPcmciaControl); + return mPcmciaControl; + case KEYBOARD: + LOG_REG_R("A14Keyboard", address, mKeyboard); + return mKeyboard; + case IRQ_EDGE: + LOG_REG_R("A14IrqEdge", address, mIrqEdge); + return mIrqEdge; + case VOL_CONTROL: + LOG_REG_R("A14VolControl", address, mVolControl); + return mVolControl; + case EXT_STAT: + LOG_REG_R("A14ExtStat", address, mExtStat); + return mExtStat; + case SPI_STATUS: { + LOG_REG_R("A14SpiStatus", address, mSpiStatus); + auto status = mSpiStatus; + mSpiStatus &= ~SpiStatus::SpiReady; + mSpiStatus &= ~SpiStatus::AdcComplete; + return status; + } + case SPI_DATA: { + uint16_t value = mSpiData; + LOG_REG_R("A14SpiData", address, value); + return value; + } + case SPI_FN: + LOG_REG_R("A14SpiFn", address, mSpiFn); + return mSpiFn; + default: + LOG_REG_R("A14Unknown", address, 0); + return 0; + } +} + +void ASIC14::write16(const uint32_t address, uint16_t const value) { + switch (address) { + case SYSCON: { + LOG_REG_W("A14SysCon", address, value); + GPIORegisterMode newMode = + static_cast((value & 0x30) >> 4); + if (newMode != mRegisterMode) { + LOG("A14 GPIO Register Mode set to: %s [%d]\n", + newMode == GPIORegisterMode::Data ? "Data" + : newMode == GPIORegisterMode::Direction ? "Direction" + : newMode == GPIORegisterMode::ActiveLevel ? "ActiveLevel" + : "RunDisable", + newMode); + } + mRegisterMode = newMode; + mSysCon = value; + break; + } + case STATUS: + LOG_REG_W("A14Status", address, value); + mStatus = value; + break; + case PORTA: + LOG_REG_W("A14PortA", address, value); + mPortA[mRegisterMode] = value; + break; + case PORTB: + LOG_REG_W("A14PortB", address, value); + mPortB[mRegisterMode] = value; + break; + case PORTC: + LOG_REG_W("A14PortC", address, value); + mPortC[mRegisterMode] = value; + break; + case PORTD: + LOG_REG_W("A14PortD", address, value); + mPortD[mRegisterMode] = value; + break; + case SSP_STATUS: + LOG_REG_W("A14SspStatus", address, value); + mSspStatus = value; + break; + case SSP_CONTROL: + LOG_REG_W("A14SspControl", address, value); + mSspControl = value; + break; + case EE_CONTROL: + LOG_REG_W("A14EeControl", address, value); + mEeControl = value; + break; + case EE_DATA: + LOG_REG_W("A14EeData", address, value); + mEeData = value; + break; + case ADC_RXTX_FIFO: + LOG_REG_W("A14RxFifo", address, value); + mAdcRxFifo = value; + break; + case BIAS_CONTROL: + LOG_REG_W("A14BiasControl", address, value); + mBiasControl = value; + break; + case CCFL_CONTROL: + LOG_REG_W("A14CcflControl", address, value); + mCcflControl = value; + break; + case TC1_PRELOAD: + LOG_REG_W("A14Tc1Preload", address, value); + mTcPreload[0] = value; + break; + case TC1_CONTROL: + LOG_REG_W("A14Tc1Control", address, value); + mTcControl[0] = value; + break; + case TC1_IRQ_COUNT: + LOG_REG_W("A14Tc1IrqCount", address, value); + mTcIrqCount[0] = value; + break; + case TC2_PRELOAD: + LOG_REG_W("A14Tc2Preload", address, value); + mTcPreload[1] = value; + break; + case TC2_CONTROL: + LOG_REG_W("A14Tc2Control", address, value); + mTcControl[1] = value; + break; + case TC2_IRQ_COUNT: + LOG_REG_W("A14Tc2IrqCount", address, value); + mTcIrqCount[1] = value; + break; + case FG1_PRELOAD: + LOG_REG_W("A14Fg1Preload", address, value); + mFgPreload[0] = value; + break; + case FG2_PRELOAD: + LOG_REG_W("A14Fg2Preload", address, value); + mFgPreload[1] = value; + break; + case IRQ_STATUS: + LOG_REG_W("A14IrqStatus", address, value); + mIrqStatus &= ~value; + break; + case IRQ_MASK: + LOG_REG_W("A14IrqMask", address, value); + mIrqMask = value; + break; + case PCMCIA_CONTROL: + LOG_REG_W("A14PcmciaControl", address, value); + mPcmciaControl = value; + break; + case KEYBOARD: + LOG_REG_W("A14Keyboard", address, value); + mKeyboard = value; + break; + case IRQ_EDGE: + LOG_REG_W("A14IrqEdge", address, value); + mIrqEdge = value; + break; + case VOL_CONTROL: + LOG_REG_W("A14VolControl", address, value); + mVolControl = value; + break; + case EXT_STAT: + LOG_REG_W("A14ExtStat", address, value); + mExtStat = value; + break; + case SPI_STATUS: { + LOG_REG_W("A14SpiStatus", address, value); + mSpiStatus &= ~value; + break; + } + case SPI_DATA: { + LOG_REG_W("A14SpiData", address, value); + mSpiData = value; + break; + } + case SPI_FN: + LOG_REG_W("A14SpiFn", address, value); + mSpiFn = value; + + switch (value) { + case SpiFunction::EepromSendCommand: + mEeprom.sendCommand(mSpiData); + break; + + case SpiFunction::EepromWriteData: + case SpiFunction::EepromReadData: + mEeprom.data16(mSpiData); + break; + + case SpiFunction::AdcTransfer: { + // ADC channel being queried is stored in mSpiData's bits 4..6 + const uint16_t adcChannel = mSpiData & 0x70; + + switch (adcChannel) { + case AdcChannel::TouchscreenY: { + mAdcPendingData = mTouchDown ? mTouchY & 0xFFF : 0; + break; + } + case AdcChannel::TouchscreenX: { + mAdcPendingData = mTouchDown ? mTouchX & 0xFFF : 0; + break; + } + case AdcChannel::BatteryMain: + case AdcChannel::BatteryBackup: + default: + mAdcPendingData = 0; + break; + } + + // Schedule the ADC completion (50 microsecond physical conversion + // delay) + mAdcCompleteCycle = mCurrentCycles + Series7::CLOCK_SPEED / 20000; + } break; + } + + mSpiStatus |= SpiStatus::SpiReady; + break; + default: + LOG_REG_W("A14Unknown", address, value); + break; + } + return; +} + +} // namespace Series7 diff --git a/WindCore/series7/asic14.h b/WindCore/series7/asic14.h new file mode 100644 index 0000000..fedbf04 --- /dev/null +++ b/WindCore/series7/asic14.h @@ -0,0 +1,177 @@ +#ifndef ASIC14_H +#define ASIC14_H + +#include +#include +#include +#include + +#include "eeprom.h" +#include "gpio_controller.h" +#include "lcd_controller.h" +#include "os_timer.h" +#include "uart.h" + +// An implementation of ASIC14 from the Psion netBook. +// This is mapped into the 0x10000000 address space and +// appears to handle power/battery status, CF/PCMCIA power, +// two timers, LCD brightness/contrast and their associated +// interrupts. + +namespace Series7 { + +class ASIC14 { +public: + ASIC14(SA1100::GPIOController* gpioController); + + void reset(); + + void setKey(uint8_t column, uint8_t rowBit, bool pressed); + + uint8_t readKeyboard() const; + + void setTouch(int32_t x, int32_t y, bool down) { + mTouchQueue.push_back({x, y, down}); + } + + uint16_t read16(const uint32_t address); + void write16(const uint32_t address, const uint16_t value); + + void tick(int64_t passedCycles); + +private: + void initRegisters(); + + enum { + SYSCON = 0x10000000, + STATUS = 0x10000002, + PORTA = 0x10000004, + PORTB = 0x10000006, + PORTC = 0x10000008, + PORTD = 0x1000000A, + SSP_STATUS = 0x1000000C, + SSP_CONTROL = 0x1000000E, + EE_CONTROL = 0x10000010, + EE_DATA = 0x10000012, + ADC_RXTX_FIFO = 0x10000014, + BIAS_CONTROL = 0x10000016, + CCFL_CONTROL = 0x10000018, + TC1_PRELOAD = 0x1000001A, + TC1_CONTROL = 0x1000001C, + TC1_IRQ_COUNT = 0x1000001E, + TC2_PRELOAD = 0x10000020, + TC2_CONTROL = 0x10000022, + TC2_IRQ_COUNT = 0x10000024, + FG1_PRELOAD = 0x10000026, + FG2_PRELOAD = 0x10000028, + IRQ_STATUS = 0x1000002A, + IRQ_MASK = 0x1000002C, + PCMCIA_CONTROL = 0x1000002E, + KEYBOARD = 0x10000030, + IRQ_EDGE = 0x10000032, + VOL_CONTROL = 0x10000034, + EXT_STAT = 0x1000003E, + SPI_STATUS = 0x10000040, + SPI_DATA = 0x10000048, + SPI_FN = 0x1000004C + }; + + enum IrqMask : uint16_t { + Timer0 = (1 << 0), + Timer1 = (1 << 1), + Rx = (1 << 2), + Tx = (1 << 3), + E2prom = (1 << 4), + RxOverrun = (1 << 5), + E2promErr = (1 << 6), + Adc = (1 << 7), + Pcmcia = (1 << 9), + // Bit 8? + CfCard = (1 << 10), + // Bits 11 and 12 are expansion interrupts? + CfCardChange = (1 << 13), + PcmciaChange = (1 << 14), + Pen = (1 << 15) + }; + + enum SpiFunction : uint16_t { + AdcTransfer = 1, + EepromSendCommand = 4, + EepromWriteData = 5, + EepromReadData = 6, + }; + + enum SpiStatus : uint16_t { SpiReady = 1 << 1, AdcComplete = 1 << 2 }; + + enum AdcChannel : uint16_t { + TouchscreenY = 0x10, + TouchscreenX = 0x50, + BatteryMain = 0x20, + BatteryBackup = 0x60 + }; + + enum GPIORegisterMode : int { + Data = 0, + Direction = 1, + ActiveLevel = 2, + RunDisable = 3 + }; + + GPIORegisterMode mRegisterMode; + + uint16_t mSysCon; + uint16_t mStatus; + uint16_t mPortA[4]; + uint16_t mPortB[4]; + uint16_t mPortC[4]; + uint16_t mPortD[4]; + uint16_t mSspStatus; + uint16_t mSspControl; + uint16_t mEeControl; + uint16_t mEeData; + uint16_t mAdcRxFifo; + uint16_t mAdcTxFifo; + uint16_t mBiasControl; + uint16_t mCcflControl; + uint16_t mTcPreload[2]; + uint16_t mTcControl[2]; + uint16_t mTcIrqCount[2]; + uint16_t mFgPreload[2]; + uint16_t mIrqStatus; + uint16_t mIrqMask; + uint16_t mPcmciaControl; + uint16_t mKeyboard; + uint16_t mIrqEdge; + uint16_t mVolControl; + uint16_t mExtStat; + uint16_t mSpiStatus; + uint16_t mSpiData; + uint16_t mSpiFn; + + SA1100::GPIOController* mGPIOController; + static constexpr int kGpioSharedInterruptPin = 10; + + Eeprom mEeprom; + + bool mTouchDown = false; + int32_t mTouchX = 0; + int32_t mTouchY = 0; + std::optional mTouchReleaseTime = std::nullopt; + + struct TouchEvent { + int32_t x; + int32_t y; + bool down; + }; + std::deque mTouchQueue; + + int64_t mCurrentCycles = 0; + int64_t mAdcCompleteCycle = 0; + uint16_t mAdcPendingData = 0; + + uint8_t mKeyboardColumns[8]; +}; + +} // namespace Series7 + +#endif // ASIC14_H diff --git a/WindCore/series7/eeprom.cpp b/WindCore/series7/eeprom.cpp new file mode 100644 index 0000000..db7b84e --- /dev/null +++ b/WindCore/series7/eeprom.cpp @@ -0,0 +1,76 @@ +#include "eeprom.h" + +namespace Series7 { + +Eeprom::Eeprom() { populate(); } + +void Eeprom::populate() { + memset(mData, 0, sizeof(mData)); + + uint8_t* eepromBytes = reinterpret_cast(mData); + + // defaults expected by the touchscreen code + eepromBytes[0xA] = 20; + eepromBytes[0xB] = 20; + eepromBytes[0xC] = 20; + eepromBytes[0xD] = 30; + + // some basic stuff to begin with + // set up the Psion's unique ID + eepromBytes[0x1B] = 0xDE; + eepromBytes[0x1A] = 0xAD; + eepromBytes[0x19] = 0xBE; + eepromBytes[0x18] = 0xEF; + + // give ourselves a neat custom device name + const char* key = "PSIONPSIONPSION"; + const char* name = "Series 7"; + eepromBytes[0x28] = strlen(name); + if (eepromBytes[0x28] > 15) + eepromBytes[0x28] = 15; + for (int i = 0; i < eepromBytes[0x28]; i++) + eepromBytes[0x29 + i] = name[i] ^ key[i]; + + updateChecksum(); +} + +void Eeprom::updateChecksum() { + uint8_t* eepromBytes = reinterpret_cast(mData); + + // calculate the checksum + uint8_t chk = 0; + for (int i = 0; i < sizeof(mData) - 1; i++) + chk ^= eepromBytes[i]; + + // EPOC is expecting 66 + eepromBytes[0x7F] = chk ^ 66; +} + +void Eeprom::sendCommand(uint16_t command) { + const uint8_t opcode = (command >> 6) & 0x3; + mSeekPos = command & AddressMask; + + switch (opcode) { + case Opcode::Write: + mState = State::ReceivingData; + break; + case Opcode::Read: + mState = State::SendingData; + break; + } +} + +void Eeprom::data16(uint16_t& data) { + switch (mState) { + case State::ReceivingData: + mData[mSeekPos] = data; + break; + case State::SendingData: + data = mData[mSeekPos]; + break; + } + + mState = State::Idle; +} + +} // namespace Series7 diff --git a/WindCore/series7/eeprom.h b/WindCore/series7/eeprom.h new file mode 100644 index 0000000..94ea6c8 --- /dev/null +++ b/WindCore/series7/eeprom.h @@ -0,0 +1,35 @@ +#ifndef EEPROM_H +#define EEPROM_H + +#include +#include + +namespace Series7 { + +class Eeprom { +public: + Eeprom(); + + void sendCommand(uint16_t command); + + // There are 16 data lines going to the eeprom that are either + // driven by the eeprom (read mode) or driven by the asic (write mode). + void data16(uint16_t& data); + + void populate(); + void updateChecksum(); + +private: + enum Opcode { Write = 1, Read = 2 }; + enum class State { Idle, ReceivingData, SendingData }; + + static constexpr uint16_t AddressMask = 0x3F; + static constexpr size_t kEepromSize = 0x40; + + uint16_t mData[kEepromSize]; + uint8_t mSeekPos = 0; + State mState = State::Idle; +}; +} // namespace Series7 + +#endif // EEPROM_H diff --git a/WindCore/series7/sa1100.cpp b/WindCore/series7/sa1100.cpp index c450711..37b0a35 100644 --- a/WindCore/series7/sa1100.cpp +++ b/WindCore/series7/sa1100.cpp @@ -149,8 +149,165 @@ void Emulator::diffInterrupts(uint16_t oldval, uint16_t newval) {} uint32_t Emulator::readKeyboard() {} -void Emulator::setKeyboardKey(EpocKey key, bool value) {} +// Netbook keyboard matrix from linux-2.4.27-vrs1-kvd1-netbook +// drivers/char/netbook_keyb.c netbook_keymap[row][col] = Linux scancode. Column +// select = 8+col; Port A read = row bits (1 = pressed). +void Emulator::setKeyboardKey(EpocKey key, bool value) { + int idx = -1; +#define NETBOOK_KEY(col, row) \ + idx = ((col) << 8) | (1 << (row)); \ + break -void Emulator::updateTouchInput(int32_t x, int32_t y, bool down) {} + switch ((int)key) { + // Row 0: scancodes 28=Enter, 14=Backspace, 61=on/off, 57=Space, 58=Alt, + // 60=F1, 1=Esc + case EStdKeyEnter: + NETBOOK_KEY(0, 0); + case EStdKeyBackspace: + NETBOOK_KEY(1, 0); + case EStdKeyOff: + NETBOOK_KEY(2, 0); // 61 = on/off switch + case EStdKeySpace: + NETBOOK_KEY(4, 0); + case EStdKeyLeftAlt: + NETBOOK_KEY(5, 0); + case EStdKeyF1: + NETBOOK_KEY(6, 0); // 60 = exterior button + case EStdKeyEscape: + NETBOOK_KEY(7, 0); + + // Row 1: 77=Right, 37=K, 51=comma, 19=R, 33=F, 16=Q, 2=1 + case EStdKeyRightArrow: + NETBOOK_KEY(0, 1); + case 'K': + NETBOOK_KEY(2, 1); + case EStdKeyComma: + NETBOOK_KEY(3, 1); + case 'R': + NETBOOK_KEY(4, 1); + case 'F': + NETBOOK_KEY(5, 1); + case 'Q': + NETBOOK_KEY(6, 1); + case '1': + NETBOOK_KEY(7, 1); + + // Row 2: 15=Tab, 12=minus, 23=I, 41=quote, 5=4, 47=V, 30=A, 3=2 + case EStdKeyTab: + NETBOOK_KEY(0, 2); + case EStdKeyMinus: + NETBOOK_KEY(1, 2); + case 'I': + NETBOOK_KEY(2, 2); + case EStdKeySingleQuote: + NETBOOK_KEY(3, 2); + case '4': + NETBOOK_KEY(4, 2); + case 'V': + NETBOOK_KEY(5, 2); + case 'A': + NETBOOK_KEY(6, 2); + case '2': + NETBOOK_KEY(7, 2); + + // Row 3: 21=Y, 13=equal, 9=8, 50=M, 6=5, 46=C, 44=Z, 7=6 + case 'Y': + NETBOOK_KEY(0, 3); + case EStdKeyEquals: + NETBOOK_KEY(1, 3); + case '8': + NETBOOK_KEY(2, 3); + case 'M': + NETBOOK_KEY(3, 3); + case '5': + NETBOOK_KEY(4, 3); + case 'C': + NETBOOK_KEY(5, 3); + case 'Z': + NETBOOK_KEY(6, 3); + case '6': + NETBOOK_KEY(7, 3); + + // Row 4: 75=Left, 11=0, 10=9, 36=J, 20=T, 32=D, 31=S, 52=period + case EStdKeyLeftArrow: + NETBOOK_KEY(0, 4); + case '0': + NETBOOK_KEY(1, 4); + case '9': + NETBOOK_KEY(2, 4); + case 'J': + NETBOOK_KEY(3, 4); + case 'T': + NETBOOK_KEY(4, 4); + case 'D': + NETBOOK_KEY(5, 4); + case 'S': + NETBOOK_KEY(6, 4); + case EStdKeyFullStop: + NETBOOK_KEY(7, 4); + + // Row 5: 80=Down, 25=P, 24=O, 22=U, 34=G, 18=E, 17=W, 72=Up + case EStdKeyDownArrow: + NETBOOK_KEY(0, 5); + case 'P': + NETBOOK_KEY(1, 5); + case 'O': + NETBOOK_KEY(2, 5); + case 'U': + NETBOOK_KEY(3, 5); + case 'G': + NETBOOK_KEY(4, 5); + case 'E': + NETBOOK_KEY(5, 5); + case 'W': + NETBOOK_KEY(6, 5); + case EStdKeyUpArrow: + NETBOOK_KEY(7, 5); + + // Row 6: 49=N, 39=semicolon, 38=L, 8=7, 48=B, 4=3, 45=X, 35=H + case 'N': + NETBOOK_KEY(0, 6); + case EStdKeySemiColon: + NETBOOK_KEY(1, 6); + case 'L': + NETBOOK_KEY(2, 6); + case '7': + NETBOOK_KEY(3, 6); + case 'B': + NETBOOK_KEY(4, 6); + case '3': + NETBOOK_KEY(5, 6); + case 'X': + NETBOOK_KEY(6, 6); + case 'H': + NETBOOK_KEY(7, 6); + + // Row 7: 42=LeftShift, 54=RightShift, 29=Ctrl, 59=AltGr, 43=backslash, + // 53=slash + case EStdKeyLeftShift: + NETBOOK_KEY(0, 7); + case EStdKeyRightShift: + NETBOOK_KEY(1, 7); + case EStdKeyLeftCtrl: + NETBOOK_KEY(2, 7); + case EStdKeyRightAlt: + NETBOOK_KEY(3, 7); // 59 = AltGr + case EStdKeyMenu: + NETBOOK_KEY(3, 7); + case EStdKeyBackSlash: + NETBOOK_KEY(4, 7); + case EStdKeyForwardSlash: + NETBOOK_KEY(5, 7); + } + + if (idx >= 0) { + if (mAsic14) + mAsic14->setKey(idx >> 8, idx & 0xFF, value); + } +} + +void Emulator::updateTouchInput(int32_t x, int32_t y, bool down) { + mAsic14->setTouch(x, y, down); +} } // namespace SA1100 -- 2.45.2