- ❌ 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
#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 \
emu.cpp
HEADERS += \
+ etna.h \
wind_hw.h \
wind.h \
macros.h \
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;
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)
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)
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);
}
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
#pragma once
#include "arm.h"
#include "wind_hw.h"
+#include "etna.h"
#include <unordered_set>
class Emu {
int64_t nextTickAt = 0;
Timer tc1, tc2;
UART uart1, uart2;
+ Etna etna;
bool asleep = false;
std::unordered_set<uint32_t> _breakpoints;
--- /dev/null
+#include "etna.h"
+#include <stdio.h>
+#include <string.h>
+
+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;
+ }
+}
--- /dev/null
+#pragma once
+#include <stdint.h>
+
+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
+};
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);
-#include <stdint.h>
#pragma once
+#include <stdint.h>
const int CLOCK_SPEED = 0x9000*1000;
const int TICK_INTERVAL = CLOCK_SPEED / 64;