--- /dev/null
+#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<GPIORegisterMode>((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
--- /dev/null
+#ifndef ASIC14_H
+#define ASIC14_H
+
+#include <cstdint>
+#include <deque>
+#include <optional>
+#include <vector>
+
+#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<int64_t> mTouchReleaseTime = std::nullopt;
+
+ struct TouchEvent {
+ int32_t x;
+ int32_t y;
+ bool down;
+ };
+ std::deque<TouchEvent> mTouchQueue;
+
+ int64_t mCurrentCycles = 0;
+ int64_t mAdcCompleteCycle = 0;
+ uint16_t mAdcPendingData = 0;
+
+ uint8_t mKeyboardColumns[8];
+};
+
+} // namespace Series7
+
+#endif // ASIC14_H
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