From 0ed437c6d3c0226e04149118a895ed51fea53e3c Mon Sep 17 00:00:00 2001 From: Ash Wolf Date: Fri, 20 Dec 2019 23:46:22 +0000 Subject: [PATCH] add skeleton support for the Etna chip, including PROM support --- README.md | 2 +- WindCore/WindCore.pro | 2 + WindCore/emu.cpp | 18 +++++- WindCore/emu.h | 2 + WindCore/etna.cpp | 145 ++++++++++++++++++++++++++++++++++++++++++ WindCore/etna.h | 24 +++++++ WindCore/wind.cpp | 5 +- WindCore/wind.h | 2 +- 8 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 WindCore/etna.cpp create mode 100644 WindCore/etna.h diff --git a/README.md b/README.md index afe5db4..50c3539 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Hardware features: - ❌ Touch panel: not implemented - ❌ Audio: not implemented - ❌ Serial/UART support: stubbed out -- ❌ ETNA (CompactFlash): not implemented +- ❌ ETNA (PCMCIA/CompactFlash): mostly stubbed out - ✅ RTC: implemented - ❌ RTC alarm: not implemented - ❌ Standby mode: not implemented diff --git a/WindCore/WindCore.pro b/WindCore/WindCore.pro index 23b825a..74c4a85 100644 --- a/WindCore/WindCore.pro +++ b/WindCore/WindCore.pro @@ -22,6 +22,7 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ + etna.cpp \ wind.cpp \ isa-arm.c \ decoder.c \ @@ -30,6 +31,7 @@ SOURCES += \ emu.cpp HEADERS += \ + etna.h \ wind_hw.h \ wind.h \ macros.h \ diff --git a/WindCore/emu.cpp b/WindCore/emu.cpp index dd861cf..a37fb14 100644 --- a/WindCore/emu.cpp +++ b/WindCore/emu.cpp @@ -108,7 +108,13 @@ void Emu::writeReg8(uint32_t reg, uint8_t value) { uint32_t oldPorts = portValues; portValues &= 0xFF00FFFF; portValues |= (uint32_t)value << 16; - diffPorts(oldPorts, portValues); + if ((portValues & 0x10000) && !(oldPorts & 0x10000)) + etna.setPromBit0High(); + else if (!(portValues & 0x10000) && (oldPorts & 0x10000)) + etna.setPromBit0Low(); + if ((portValues & 0x20000) && !(oldPorts & 0x20000)) + etna.setPromBit1High(); + diffPorts(oldPorts, portValues); } else if (reg == PCDR) { uint32_t oldPorts = portValues; portValues &= 0xFFFF00FF; @@ -200,6 +206,8 @@ uint32_t Emu::readPhys8(uint32_t physAddress) { uint8_t region = (physAddress >> 24) & 0xF1; if (region == 0) result = ROM[physAddress & 0xFFFFFF]; + else if (region == 0x20 && physAddress <= 0x20000FFF) + result = etna.readReg8(physAddress & 0xFFF); else if (region == 0x80 && physAddress <= 0x80000FFF) result = readReg8(physAddress & 0xFFF); else if (region == 0xC0) @@ -244,6 +252,8 @@ uint32_t Emu::readPhys32(uint32_t physAddress) { uint8_t region = (physAddress >> 24) & 0xF1; if (region == 0) LOAD_32LE(result, physAddress & 0xFFFFFF, ROM); + else if (region == 0x20 && physAddress <= 0x20000FFF) + result = etna.readReg32(physAddress & 0xFFF); else if (region == 0x80 && physAddress <= 0x80000FFF) result = readReg32(physAddress & 0xFFF); else if (region == 0xC0) @@ -277,8 +287,10 @@ void Emu::writePhys8(uint32_t physAddress, uint8_t value) { else if (region == 0xD1) MemoryBlockD1[physAddress & MemoryBlockMask] = (uint8_t)value; #endif + else if (region == 0x20 && physAddress <= 0x20000FFF) + etna.writeReg8(physAddress & 0xFFF, value); else if (region == 0x80 && physAddress <= 0x80000FFF) - writeReg8(physAddress & 0xFFF, value); + writeReg8(physAddress & 0xFFF, value); // else // printf("<%08x> unmapped write8 addr p:%08x :: %02x\n", cpu.gprs[ARM_PC] - 4, physAddress, value); } @@ -313,6 +325,8 @@ void Emu::writePhys32(uint32_t physAddress, uint32_t value) { else if (region == 0xD1) STORE_32LE(value, physAddress & MemoryBlockMask, MemoryBlockD1); #endif + else if (region == 0x20 && physAddress <= 0x20000FFF) + etna.writeReg32(physAddress & 0xFFF, value); else if (region == 0x80 && physAddress <= 0x80000FFF) writeReg32(physAddress & 0xFFF, value); // else diff --git a/WindCore/emu.h b/WindCore/emu.h index 5a96df6..0962fc7 100644 --- a/WindCore/emu.h +++ b/WindCore/emu.h @@ -1,6 +1,7 @@ #pragma once #include "arm.h" #include "wind_hw.h" +#include "etna.h" #include class Emu { @@ -27,6 +28,7 @@ class Emu { int64_t nextTickAt = 0; Timer tc1, tc2; UART uart1, uart2; + Etna etna; bool asleep = false; std::unordered_set _breakpoints; diff --git a/WindCore/etna.cpp b/WindCore/etna.cpp new file mode 100644 index 0000000..5e15d2f --- /dev/null +++ b/WindCore/etna.cpp @@ -0,0 +1,145 @@ +#include "etna.h" +#include +#include + +enum EtnaReg { + regUnk0 = 0, + regUnk1 = 1, + regUartIntStatus = 2, + regUartIntMask = 3, + regUartBaudRateLo8 = 4, + regUartBaudRateHi4 = 5, + regPcCdIntStatus = 6, + regPcCdIntMask = 7, + regIntClear = 8, + regSktVarA0 = 9, + regSktVarA1 = 0xA, + regSktCtrl = 0xB, + regWake1 = 0xC, + regSktVarB0 = 0xD, + regSktVarB1 = 0xE, + regWake2 = 0xF +}; + +static const char *nameReg(uint32_t reg) { + switch (reg) { + case regUnk0: return "unk0"; + case regUnk1: return "unk1"; + case regUartIntStatus: return "UartIntStatus"; + case regUartIntMask: return "UartIntMask"; + case regUartBaudRateLo8: return "UartBaudRateLo8"; + case regUartBaudRateHi4: return "UartBaudRateHi4"; + case regPcCdIntStatus: return "PcCdIntStatus"; + case regPcCdIntMask: return "PcCdIntMask"; + case regIntClear: return "IntClear"; + case regSktVarA0: return "SktVarA0"; + case regSktVarA1: return "SktVarA1"; + case regSktCtrl: return "SktCtrl"; + case regWake1: return "wake1"; + case regSktVarB0: return "SktVarB0"; + case regSktVarB1: return "SktVarB1"; + case regWake2: return "wake2"; + } + return nullptr; +} + + +Etna::Etna() { + for (int i = 0; i < 0x80; i++) + prom[i] = 0; + + // some basic stuff to begin with + // set up the Psion's unique ID + prom[0x1B] = 0xDE; + prom[0x1A] = 0xAD; + prom[0x19] = 0xBE; + prom[0x18] = 0xEF; + + // give ourselves a neat custom device name + const char *key = "PSIONPSIONPSION"; + const char *name = "WindEmu!"; + prom[0x28] = strlen(name); + if (prom[0x28] > 15) + prom[0x28] = 15; + for (int i = 0; i < prom[0x28]; i++) + prom[0x29 + i] = name[i] ^ key[i]; + + // calculate the checksum + uint8_t chk = 0; + for (int i = 0; i < 0x7F; i++) + chk ^= prom[i]; + + // EPOC is expecting 66 + prom[0x7F] = chk ^ 66; +} + + +uint32_t Etna::readReg8(uint32_t reg) +{ + if (!promReadActive) + printf("ETNA readReg8: reg=%s\n", nameReg(reg)); + switch (reg) { + case regSktVarA0: return 0; // will store some status flags + case regSktVarA1: return 0; // will store some more status flags + case regWake1: return wake1; + case regWake2: return wake2; + } + return 0xFF; +} + +uint32_t Etna::readReg32(uint32_t reg) +{ + // may be able to remove this, p. sure Etna is byte addressing only + printf("ETNA readReg32: reg=%x\n", reg); + return 0xFFFFFFFF; +} + +void Etna::writeReg8(uint32_t reg, uint8_t value) +{ + if (!promReadActive) + printf("ETNA writeReg8: reg=%s value=%02x\n", nameReg(reg), value); + switch (reg) { + case regWake1: wake1 = value; break; + case regWake2: wake2 = value; break; + } +} + +void Etna::writeReg32(uint32_t reg, uint32_t value) +{ + // may be able to remove this, p. sure Etna is byte addressing only + printf("ETNA writeReg32: reg=%x value=%08x\n", reg, value); +} + +void Etna::setPromBit0High() +{ + // begin reading a word + promReadAddress = 0; + promReadValue = 0; + promAddressBitsReceived = 0; + promReadActive = true; +} + +void Etna::setPromBit0Low() +{ + promReadActive = false; +} + +void Etna::setPromBit1High() +{ + if (promAddressBitsReceived < 10) { + // we're still receiving the address + promReadAddress <<= 1; + promReadAddress |= ((wake1 & 4) >> 2); + if (++promAddressBitsReceived == 10) { + // we can fetch the value now + int addressInBytes = promReadAddress * 2; + addressInBytes %= sizeof(prom); + promReadValue = prom[addressInBytes] | (prom[addressInBytes + 1] << 8); + } + } else { + wake1 &= ~8; + if (promReadValue & 0x8000) + wake1 |= 8; + promReadValue <<= 1; + } +} diff --git a/WindCore/etna.h b/WindCore/etna.h new file mode 100644 index 0000000..7e83a4f --- /dev/null +++ b/WindCore/etna.h @@ -0,0 +1,24 @@ +#pragma once +#include + +class Etna { + uint8_t prom[0x80] = {}; + uint16_t promReadAddress = 0, promReadValue = 0; + bool promReadActive = false; + int promAddressBitsReceived = 0; + + uint8_t wake1 = 0, wake2 = 0; + +public: + Etna(); + + uint32_t readReg8(uint32_t reg); + uint32_t readReg32(uint32_t reg); + void writeReg8(uint32_t reg, uint8_t value); + void writeReg32(uint32_t reg, uint32_t value); + + // PROM + void setPromBit0High(); // port B, bit 0 + void setPromBit0Low(); // port B, bit 0 + void setPromBit1High(); // port B, bit 1 +}; diff --git a/WindCore/wind.cpp b/WindCore/wind.cpp index d8cbbe3..851e4f2 100644 --- a/WindCore/wind.cpp +++ b/WindCore/wind.cpp @@ -18,8 +18,9 @@ void diffPorts(uint32_t oldval, uint32_t newval) { if (changes & 0x1000) printf("PRT lcd backlight: %d\n", newval&0x1000); if (changes & 0x2000) printf("PRT enable uart0: %d\n", newval&0x2000); if (changes & 0x4000) printf("PRT dictaphone: %d\n", newval&0x4000); - if (changes & 0x10000) printf("PRT EECS: %d\n", newval&0x10000); - if (changes & 0x20000) printf("PRT EECLK: %d\n", newval&0x20000); +// PROM read process makes this super spammy in stdout +// if (changes & 0x10000) printf("PRT EECS: %d\n", newval&0x10000); +// if (changes & 0x20000) printf("PRT EECLK: %d\n", newval&0x20000); if (changes & 0x40000) printf("PRT contrast0: %d\n", newval&0x40000); if (changes & 0x80000) printf("PRT contrast1: %d\n", newval&0x80000); if (changes & 0x100000) printf("PRT contrast2: %d\n", newval&0x100000); diff --git a/WindCore/wind.h b/WindCore/wind.h index 18fe7ab..593d87d 100644 --- a/WindCore/wind.h +++ b/WindCore/wind.h @@ -1,5 +1,5 @@ -#include #pragma once +#include const int CLOCK_SPEED = 0x9000*1000; const int TICK_INTERVAL = CLOCK_SPEED / 64;