void ARM710::switchMode(Mode newMode) {
auto oldMode = currentMode();
if (newMode != oldMode) {
-// log("Switching mode! %x", newMode);
+ log("Switching mode! %x", newMode);
switchBank(modeToBank[newMode & 0xF]);
CPSR &= ~CPSR_ModeMask;
void ARM710::raiseException(Mode mode, uint32_t savedPC, uint32_t newPC) {
auto bankIndex = modeToBank[mode & 0xF];
-// log("Raising exception mode %x, saving PC %08x, CPSR %08x", mode, savedPC, CPSR);
+ log("Raising exception mode %x, saving PC %08x, CPSR %08x", mode, savedPC, CPSR);
SPSRs[bankIndex] = CPSR;
switchMode(mode);
uint32_t ARM710::executeInstruction(uint32_t i) {
uint32_t cycles = 1;
-// log("executing insn %08x @ %08x", i, GPRs[15] - 0xC);
+ log("executing insn %08x @ %08x", i, GPRs[15] - 0xC);
// a big old dispatch thing here
// but first, conditions!
// Output-less opcodes: special behaviour
if (S) {
CPSR = (CPSR & ~CPSR_FlagMask) | flags;
-// log("CPSR setflags=%08x results in CPSR=%08x", flags, CPSR);
+ log("CPSR setflags=%08x results in CPSR=%08x", flags, CPSR);
} else if (Opcode == 8) {
// MRS, CPSR -> Reg
GPRs[Rd] = CPSR;
-// log("r%d <- CPSR(%08x)", Rd, GPRs[Rd]);
+ log("r%d <- CPSR(%08x)", Rd, GPRs[Rd]);
} else if (Opcode == 9) {
// MSR, Reg -> CPSR
bool canChangeMode = extract1(Rn, 0);
auto newCPSR = GPRs[extract(Operand2, 3, 0)];
switchMode(modeFromCPSR(newCPSR));
CPSR = newCPSR;
-// log("CPSR change privileged: %08x", CPSR);
+ log("CPSR change privileged: %08x", CPSR);
} else {
// for the flag-only version, immediates are allowed
// so we just re-use what was calculated earlier...
auto newFlag = I ? op2 : GPRs[extract(Operand2, 3, 0)];
CPSR &= ~CPSR_FlagMask;
CPSR |= (newFlag & CPSR_FlagMask);
-// log("CPSR change unprivileged: new=%08x result=%08x", newFlag, CPSR);
+ log("CPSR change unprivileged: new=%08x result=%08x", newFlag, CPSR);
}
} else if (Opcode == 0xA) {
// MRS, SPSR -> Reg
if (isPrivileged()) {
GPRs[Rd] = SPSRs[currentBank()];
-// log("r%d <- SPSR(%08x)", Rd, GPRs[Rd]);
+ log("r%d <- SPSR(%08x)", Rd, GPRs[Rd]);
}
} else /*if (Opcode == 0xB)*/ {
bool canChangeMode = extract1(Rn, 0);
if (isPrivileged()) {
if (canChangeMode) {
SPSRs[currentBank()] = GPRs[extract(Operand2, 3, 0)];
-// log("SPSR change privileged: %08x", SPSRs[currentBank()]);
+ log("SPSR change privileged: %08x", SPSRs[currentBank()]);
} else {
// same hat
auto newFlag = I ? op2 : GPRs[extract(Operand2, 3, 0)];
SPSRs[currentBank()] &= ~CPSR_FlagMask;
SPSRs[currentBank()] |= (newFlag & CPSR_FlagMask);
-// log("SPSR change unprivileged: new=%08x result=%08x", newFlag, SPSRs[currentBank()]);
+ log("SPSR change unprivileged: new=%08x result=%08x", newFlag, SPSRs[currentBank()]);
}
}
}
auto saved = SPSRs[currentBank()];
switchMode(modeFromCPSR(saved));
CPSR = saved;
-// log("dataproc restore CPSR: %08x", saved);
+ log("dataproc restore CPSR: %08x", saved);
}
} else if (S) {
CPSR = (CPSR & ~CPSR_FlagMask) | flags;
-// log("dataproc flag change: flags=%08x CPSR=%08x", flags, CPSR);
+ log("dataproc flag change: flags=%08x CPSR=%08x", flags, CPSR);
}
}
auto saved = SPSRs[currentBank()];
switchMode(modeFromCPSR(saved));
CPSR = saved;
-// log("reloading saved SPSR: %08x", saved);
+ log("reloading saved SPSR: %08x", saved);
}
}
void ARM710::log(const char *format, ...) {
- if (logger) {
- char buffer[1024];
-
va_list vaList;
va_start(vaList, format);
- vsnprintf(buffer, sizeof(buffer), format, vaList);
+ printf(format, vaList);
va_end(vaList);
-
- logger(buffer);
- }
+ printf("\n");
}
void ARM710::logPcHistory() {
--- /dev/null
+#include "sa1100.h"
+#include "hardware.h"
+#include <time.h>
+#include "common.h"
+
+namespace SA1100 {
+
+Emulator::Emulator() : EmuBase(true) {
+}
+
+uint32_t Emulator::getRTC() {
+ return time(nullptr) - 946684800;
+}
+
+MaybeU32 Emulator::readPhysical(uint32_t physAddr, ValueSize valueSize) {
+ //printf("readPhysical:: pc=%08x addr=%03x\n", getGPR(15)-4, physAddr);
+ if (valueSize == V8) {
+ return readPhysical8(physAddr);
+ } else {
+ return readPhysical32(physAddr);
+ }
+}
+
+uint8_t Emulator::readPCM8(uint32_t physAddr) {
+ // Peripheral Control Modules
+ return 0x0;
+}
+
+uint8_t Emulator::readSCM8(uint32_t physAddr) {
+ // System Control Modules
+ return 0x0;
+}
+
+uint8_t Emulator::readPhysical8(uint32_t physAddr) {
+ uint8_t region = (physAddr >> 24) & 0xFF;
+
+ switch (region) {
+ case 0x00:
+ return ROM[physAddr & 0xFFFFFF];
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ // PCMCIA slot 0
+ return 0x0;
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ // PCMCIA slot 1
+ return 0x0;
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5C:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ // Reserved, causes a data abort exception
+ return 0x0;
+ case 0x80:
+ // Peripheral Control Modules
+ return readPCM8(physAddr);
+ case 0x90:
+ // System Control Modules
+ return readSCM8(physAddr);
+ case 0xA0:
+ // Memory Control Registers
+ // Not valid in byte access
+ return 0x0;
+ case 0xB0:
+ // LCD/DMA Control Registers
+ return 0x0;
+ case 0xC0:
+ case 0xC1:
+ case 0xC2:
+ case 0xC3:
+ case 0xC4:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ return MemoryBlockC0[physAddr & MemoryBlockMask];
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ return MemoryBlockC8[physAddr & MemoryBlockMask];
+ default:
+ return 0x0; // just throw accesses to unmapped RAM away
+ }
+}
+
+uint32_t Emulator::readPCM32(uint32_t physAddr) {
+ // Peripheral Control Modules
+ return 0x0;
+}
+
+uint32_t Emulator::readSCM32(uint32_t physAddr) {
+ // System Control Modules
+ return 0x0;
+}
+
+uint32_t Emulator::readPhysical32(uint32_t physAddr) {
+ uint8_t region = (physAddr >> 24) & 0xFF;
+ uint32_t result;
+
+ switch (region) {
+ case 0x00:
+ LOAD_32LE(result, physAddr & 0xFFFFFF, ROM);
+ break;
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ // PCMCIA slot 0
+ result = 0x0;
+ break;
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ // PCMCIA slot 1
+ result = 0x0;
+ break;
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5C:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ // Reserved, causes a data abort exception
+ result = 0x0;
+ break;
+ case 0x80:
+ // Peripheral Control Modules
+ result = readPCM32(physAddr);
+ break;
+ case 0x90:
+ // System Control Modules
+ result = readSCM32(physAddr);
+ break;
+ case 0xA0:
+ // Memory Control Registers
+ result = memoryConfig.get_data(physAddr);
+ break;
+ case 0xB0:
+ // LCD/DMA Control Registers
+ result = 0x0;
+ break;
+ case 0xC0:
+ case 0xC1:
+ case 0xC2:
+ case 0xC3:
+ case 0xC4:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ LOAD_32LE(result, physAddr & MemoryBlockMask, MemoryBlockC0);
+ break;
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ LOAD_32LE(result, physAddr & MemoryBlockMask, MemoryBlockC8);
+ break;
+ default:
+ return 0x0; // just throw accesses to unmapped RAM away
+ }
+
+ return result;
+}
+
+bool Emulator::writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) {
+ printf("writePhysical:: pc=%08x addr=%03x value=%08x\n", getGPR(15)-4, physAddr, value);
+ uint8_t region = (physAddr >> 24) & 0xFF;
+ if (valueSize == V8) {
+ return writePhysical8((uint8_t)value, physAddr);
+ } else {
+ return writePhysical32(value, physAddr);
+ }
+}
+
+bool Emulator::writePCM8(uint8_t value, uint32_t physAddr) {
+ return true;
+}
+
+bool Emulator::writeSCM8(uint8_t value, uint32_t physAddr) {
+ return true;
+}
+
+bool Emulator::writePhysical8(uint8_t value, uint32_t physAddr) {
+ uint8_t region = (physAddr >> 24) & 0xFF;
+
+ switch (region) {
+ case 0x00:
+ // Read-only
+ return false;
+ break;
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ // PCMCIA slot 0
+ return true;
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ // PCMCIA slot 1
+ return true;
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5C:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ // Reserved, causes a data abort exception
+ return false;
+ case 0x80:
+ // Peripheral Control Modules
+ return writePCM8(value, physAddr);
+ case 0x90:
+ // System Control Modules
+ return writeSCM8(value, physAddr);
+ case 0xA0:
+ // Memory Control Registers
+ // Invalid to write to with bytes
+ return false;
+ case 0xB0:
+ // LCD/DMA Control Registers
+ return true;
+ case 0xC0:
+ case 0xC1:
+ case 0xC2:
+ case 0xC3:
+ case 0xC4:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ MemoryBlockC0[physAddr & MemoryBlockMask] = value;
+ return true;
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ MemoryBlockC8[physAddr & MemoryBlockMask] = value;
+ return true;
+ default:
+ // just throw accesses to unmapped RAM away
+ return true;
+ }
+}
+
+bool Emulator::writePCM32(uint32_t value, uint32_t physAddr) {
+ return true;
+}
+
+bool Emulator::writeSCM32(uint32_t value, uint32_t physAddr) {
+ return true;
+}
+
+bool Emulator::writePhysical32(uint32_t value, uint32_t physAddr) {
+ uint8_t region = (physAddr >> 24) & 0xFF;
+
+ switch (region) {
+ case 0x00:
+ // Read-only
+ return false;
+ break;
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ // PCMCIA slot 0
+ return true;
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ // PCMCIA slot 1
+ return true;
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5C:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ // Reserved, causes a data abort exception
+ return false;
+ case 0x80:
+ // Peripheral Control Modules
+ return writePCM32(value, physAddr);
+ case 0x90:
+ // System Control Modules
+ return writeSCM32(value, physAddr);
+ case 0xA0:
+ // Memory Control Registers
+ memoryConfig.put_data(physAddr, value);
+ return false;
+ case 0xB0:
+ // LCD/DMA Control Registers
+ return true;
+ case 0xC0:
+ case 0xC1:
+ case 0xC2:
+ case 0xC3:
+ case 0xC4:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ STORE_32LE(value, physAddr & MemoryBlockMask, MemoryBlockC0);
+ return true;
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ STORE_32LE(value, physAddr & MemoryBlockMask, MemoryBlockC8);
+ return true;
+ default:
+ // just throw accesses to unmapped RAM away
+ return true;
+ }
+}
+
+void Emulator::configure() {
+ if (configured) return;
+ configured = true;
+
+ srand(1000);
+
+ uart1.cpu = this;
+ uart2.cpu = this;
+ memset(&tc1, 0, sizeof(tc1));
+ memset(&tc2, 0, sizeof(tc1));
+ tc1.clockSpeed = CLOCK_SPEED;
+ tc2.clockSpeed = CLOCK_SPEED;
+
+ nextTickAt = TICK_INTERVAL;
+ tc1.nextTickAt = tc1.tickInterval();
+ tc2.nextTickAt = tc2.tickInterval();
+ rtc = getRTC();
+ memoryConfig.reset();
+
+ reset();
+}
+
+uint8_t *Emulator::getROMBuffer() {
+ return ROM;
+}
+size_t Emulator::getROMSize() {
+ return sizeof(ROM);
+}
+void Emulator::loadROM(uint8_t *osimg, size_t osimgSize, uint8_t *bootloader, size_t bootloaderSize) {
+ memcpy(ROM, bootloader, min(bootloaderSize, sizeof(ROM)));
+ memcpy(MemoryBlockC8, osimg, min(osimgSize, sizeof(MemoryBlockC8)));
+}
+
+void Emulator::executeUntil(int64_t cycles) {
+ if (!configured)
+ configure();
+
+ while (!asleep && passedCycles < cycles) {
+ if (passedCycles >= nextTickAt) {
+ // increment RTCDIV
+ if ((pwrsr & 0x3F) == 0x3F) {
+ rtc++;
+ pwrsr &= ~0x3F;
+ } else {
+ pwrsr++;
+ }
+
+ nextTickAt += TICK_INTERVAL;
+ pendingInterrupts |= (1<<TINT);
+ }
+ if (tc1.tick(passedCycles))
+ pendingInterrupts |= (1<<TC1OI);
+ if (tc2.tick(passedCycles))
+ pendingInterrupts |= (1<<TC2OI);
+
+ if ((pendingInterrupts & interruptMask & FIQ_INTERRUPTS) != 0 && canAcceptFIQ()) {
+ requestFIQ();
+ halted = false;
+ }
+ if ((pendingInterrupts & interruptMask & IRQ_INTERRUPTS) != 0 && canAcceptIRQ()) {
+ requestIRQ();
+ halted = false;
+ }
+
+ // what's running?
+ if (halted) {
+ // keep the clock moving
+ // when does the next earliest thing happen?
+ // this stops us from spinning needlessly
+ int64_t nextEvent = nextTickAt;
+ if (tc1.nextTickAt < nextEvent) nextEvent = tc1.nextTickAt;
+ if (tc2.nextTickAt < nextEvent) nextEvent = tc2.nextTickAt;
+ if (cycles < nextEvent) nextEvent = cycles;
+ passedCycles = nextEvent;
+ } else {
+ if (auto v = virtToPhys(getGPR(15) - 0xC); v.has_value() && instructionReady())
+ debugPC(v.value());
+ passedCycles += tick();
+
+#ifndef __EMSCRIPTEN__
+ uint32_t new_pc = getGPR(15) - 0xC;
+ if (_breakpoints.find(new_pc) != _breakpoints.end()) {
+ log("⚠️ Breakpoint triggered at %08x!", new_pc);
+ return;
+ }
+#endif
+ }
+ }
+}
+
+
+const char *Emulator::identifyObjectCon(uint32_t ptr) {
+ if (ptr == readVirtualDebug(0x80000980, V32).value()) return "process";
+ if (ptr == readVirtualDebug(0x80000984, V32).value()) return "thread";
+ if (ptr == readVirtualDebug(0x80000988, V32).value()) return "chunk";
+// if (ptr == readVirtualDebug(0x8000098C, V32).value()) return "semaphore";
+// if (ptr == readVirtualDebug(0x80000990, V32).value()) return "mutex";
+ if (ptr == readVirtualDebug(0x80000994, V32).value()) return "logicaldevice";
+ if (ptr == readVirtualDebug(0x80000998, V32).value()) return "physicaldevice";
+ if (ptr == readVirtualDebug(0x8000099C, V32).value()) return "channel";
+ if (ptr == readVirtualDebug(0x800009A0, V32).value()) return "server";
+// if (ptr == readVirtualDebug(0x800009A4, V32).value()) return "unk9A4"; // name always null
+ if (ptr == readVirtualDebug(0x800009AC, V32).value()) return "library";
+// if (ptr == readVirtualDebug(0x800009B0, V32).value()) return "unk9B0"; // name always null
+// if (ptr == readVirtualDebug(0x800009B4, V32).value()) return "unk9B4"; // name always null
+ return NULL;
+}
+
+void Emulator::fetchStr(uint32_t str, char *buf) {
+ if (str == 0) {
+ strcpy(buf, "<NULL>");
+ return;
+ }
+ int size = readVirtualDebug(str, V32).value();
+ for (int i = 0; i < size; i++) {
+ buf[i] = readVirtualDebug(str + 4 + i, V8).value();
+ }
+ buf[size] = 0;
+}
+
+void Emulator::fetchName(uint32_t obj, char *buf) {
+ fetchStr(readVirtualDebug(obj + 0x10, V32).value(), buf);
+}
+
+void Emulator::fetchProcessFilename(uint32_t obj, char *buf) {
+ fetchStr(readVirtualDebug(obj + 0x3C, V32).value(), buf);
+}
+
+void Emulator::debugPC(uint32_t pc) {
+ char objName[1000];
+ if (pc == 0x2CBC4) {
+ // CObjectCon::AddL()
+ uint32_t container = getGPR(0);
+ uint32_t obj = getGPR(1);
+ const char *wut = identifyObjectCon(container);
+ if (wut) {
+ fetchName(obj, objName);
+ if (strcmp(wut, "process") == 0) {
+ char procName[1000];
+ fetchProcessFilename(obj, procName);
+ log("OBJS: added %s at %08x <%s> <%s>", wut, obj, objName, procName);
+ } else {
+ log("OBJS: added %s at %08x <%s>", wut, obj, objName);
+ }
+ }
+ }
+
+ if (pc == 0x6D8) {
+ uint32_t virtAddr = getGPR(0);
+ uint32_t physAddr = getGPR(1);
+ uint32_t btIndex = getGPR(2);
+ uint32_t regionSize = getGPR(3);
+ log("KERNEL MMU SECTION: v:%08x p:%08x size:%08x idx:%02x",
+ virtAddr, physAddr, regionSize, btIndex);
+ }
+ if (pc == 0x710) {
+ uint32_t virtAddr = getGPR(0);
+ uint32_t physAddr = getGPR(1);
+ uint32_t btIndex = getGPR(2);
+ uint32_t regionSize = getGPR(3);
+ uint32_t pageTableA = getGPR(4);
+ uint32_t pageTableB = getGPR(5);
+ log("KERNEL MMU PAGES: v:%08x p:%08x size:%08x idx:%02x tableA:%08x tableB:%08x",
+ virtAddr, physAddr, regionSize, btIndex, pageTableA, pageTableB);
+ }
+
+ if (pc == 0x1576C) {
+ uint32_t rawEvent = getGPR(0);
+ uint32_t evtType = readVirtualDebug(rawEvent, V32).value_or(0);
+ uint32_t evtTick = readVirtualDebug(rawEvent + 4, V32).value_or(0);
+ uint32_t evtParamA = readVirtualDebug(rawEvent + 8, V32).value_or(0);
+ uint32_t evtParamB = readVirtualDebug(rawEvent + 0xC, V32).value_or(0);
+ const char *n = "???";
+ switch (evtType) {
+ case 0: n = "ENone"; break;
+ case 1: n = "EPointerMove"; break;
+ case 2: n = "EPointerSwitchOn"; break;
+ case 3: n = "EKeyDown"; break;
+ case 4: n = "EKeyUp"; break;
+ case 5: n = "ERedraw"; break;
+ case 6: n = "ESwitchOn"; break;
+ case 7: n = "EActive"; break;
+ case 8: n = "EInactive"; break;
+ case 9: n = "EUpdateModifiers"; break;
+ case 10: n = "EButton1Down"; break;
+ case 11: n = "EButton1Up"; break;
+ case 12: n = "EButton2Down"; break;
+ case 13: n = "EButton2Up"; break;
+ case 14: n = "EButton3Down"; break;
+ case 15: n = "EButton3Up"; break;
+ case 16: n = "ESwitchOff"; break;
+ }
+ log("EVENT %s: tick=%d params=%d,%d", n, evtTick, evtParamA, evtParamB);
+ }
+}
+
+
+const char *Emulator::getDeviceName() const { return "Series 5mx"; }
+int Emulator::getDigitiserWidth() const { return 695; }
+int Emulator::getDigitiserHeight() const { return 520; }
+int Emulator::getLCDOffsetX() const { return 45; }
+int Emulator::getLCDOffsetY() const { return 5; }
+int Emulator::getLCDWidth() const { return 640; }
+int Emulator::getLCDHeight() const { return 480; }
+
+// TODO move this elsewhere
+static bool initRgbValues = false;
+static uint32_t rgbValues[16];
+
+void Emulator::readLCDIntoBuffer(uint8_t **lines, bool is32BitOutput) const {
+ if (!initRgbValues) {
+ initRgbValues = true;
+ for (int i = 0; i < 16; i++) {
+ int r = (0x99 * i) / 15;
+ int g = (0xAA * i) / 15;
+ int b = (0x88 * i) / 15;
+ rgbValues[15 - i] = r | (g << 8) | (b << 16) | 0xFF000000;
+ }
+ }
+
+ if ((lcdAddress >> 24) == 0xC0) {
+ const uint8_t *lcdBuf = &MemoryBlockC0[lcdAddress & MemoryBlockMask];
+ int width = 640, height = 480;
+
+ // fetch palette
+ int bpp = 1 << (lcdBuf[1] >> 4);
+ int ppb = 8 / bpp;
+ uint16_t palette[16];
+ for (int i = 0; i < 16; i++)
+ palette[i] = lcdBuf[i*2] | ((lcdBuf[i*2+1] << 8) & 0xF00);
+
+ // build our image out
+ int lineWidth = (width * bpp) / 8;
+ for (int y = 0; y < height; y++) {
+ int lineOffs = 0x20 + (lineWidth * y);
+ for (int x = 0; x < width; x++) {
+ uint8_t byte = lcdBuf[lineOffs + (x / ppb)];
+ int shift = (x & (ppb - 1)) * bpp;
+ int mask = (1 << bpp) - 1;
+ int palIdx = (byte >> shift) & mask;
+ int palValue = palette[palIdx];
+
+ if (is32BitOutput) {
+ auto line = (uint32_t *)lines[y];
+ line[x] = rgbValues[palValue];
+ } else {
+ palValue |= (palValue << 4);
+ lines[y][x] = palValue ^ 0xFF;
+ }
+ }
+ }
+ }
+}
+
+
+void Emulator::diffPorts(uint32_t oldval, uint32_t newval) {
+ uint32_t changes = oldval ^ newval;
+ if (changes & 1) log("PRT codec enable: %d", newval&1);
+ if (changes & 2) log("PRT audio amp enable: %d", newval&2);
+ if (changes & 4) log("PRT lcd power: %d", newval&4);
+ if (changes & 8) log("PRT etna door: %d", newval&8);
+ if (changes & 0x10) log("PRT sled: %d", newval&0x10);
+ if (changes & 0x20) log("PRT pump pwr2: %d", newval&0x20);
+ if (changes & 0x40) log("PRT pump pwr1: %d", newval&0x40);
+ if (changes & 0x80) log("PRT etna err: %d", newval&0x80);
+ if (changes & 0x100) log("PRT rs-232 rts: %d", newval&0x100);
+ if (changes & 0x200) log("PRT rs-232 dtr toggle: %d", newval&0x200);
+ if (changes & 0x400) log("PRT disable power led: %d", newval&0x400);
+ if (changes & 0x800) log("PRT enable uart1: %d", newval&0x800);
+ if (changes & 0x1000) log("PRT lcd backlight: %d", newval&0x1000);
+ if (changes & 0x2000) log("PRT enable uart0: %d", newval&0x2000);
+ if (changes & 0x4000) log("PRT dictaphone: %d", newval&0x4000);
+// PROM read process makes this super spammy in stdout
+// if (changes & 0x10000) log("PRT EECS: %d", newval&0x10000);
+// if (changes & 0x20000) log("PRT EECLK: %d", newval&0x20000);
+ if (changes & 0x40000) log("PRT contrast0: %d", newval&0x40000);
+ if (changes & 0x80000) log("PRT contrast1: %d", newval&0x80000);
+ if (changes & 0x100000) log("PRT contrast2: %d", newval&0x100000);
+ if (changes & 0x200000) log("PRT contrast3: %d", newval&0x200000);
+ if (changes & 0x400000) log("PRT case open: %d", newval&0x400000);
+ if (changes & 0x800000) log("PRT etna cf power: %d", newval&0x800000);
+}
+
+void Emulator::diffInterrupts(uint16_t oldval, uint16_t newval) {
+ uint16_t changes = oldval ^ newval;
+ if (changes & 1) log("INTCHG external=%d", newval & 1);
+ if (changes & 2) log("INTCHG lowbat=%d", newval & 2);
+ if (changes & 4) log("INTCHG watchdog=%d", newval & 4);
+ if (changes & 8) log("INTCHG mediachg=%d", newval & 8);
+ if (changes & 0x10) log("INTCHG codec=%d", newval & 0x10);
+ if (changes & 0x20) log("INTCHG ext1=%d", newval & 0x20);
+ if (changes & 0x40) log("INTCHG ext2=%d", newval & 0x40);
+ if (changes & 0x80) log("INTCHG ext3=%d", newval & 0x80);
+ if (changes & 0x100) log("INTCHG timer1=%d", newval & 0x100);
+ if (changes & 0x200) log("INTCHG timer2=%d", newval & 0x200);
+ if (changes & 0x400) log("INTCHG rtcmatch=%d", newval & 0x400);
+ if (changes & 0x800) log("INTCHG tick=%d", newval & 0x800);
+ if (changes & 0x1000) log("INTCHG uart1=%d", newval & 0x1000);
+ if (changes & 0x2000) log("INTCHG uart2=%d", newval & 0x2000);
+ if (changes & 0x4000) log("INTCHG lcd=%d", newval & 0x4000);
+ if (changes & 0x8000) log("INTCHG spi=%d", newval & 0x8000);
+}
+
+
+uint32_t Emulator::readKeyboard() {
+ if (kScan & 8) {
+ // Select one keyboard
+ return keyboardColumns[kScan & 7];
+ } else if (kScan == 0) {
+ // Report all columns combined
+ uint8_t val = 0;
+ for (int i = 0; i < 8; i++)
+ val |= keyboardColumns[i];
+ return val;
+ } else {
+ return 0;
+ }
+}
+
+void Emulator::setKeyboardKey(EpocKey key, bool value) {
+ int idx = -1;
+#define KEY(column, bit) idx = (column << 8) | (1 << bit); break
+
+ switch ((int)key) {
+ case EStdKeyDictaphoneRecord: KEY(0, 6);
+ case '1': KEY(0, 5);
+ case '2': KEY(0, 4);
+ case '3': KEY(0, 3);
+ case '4': KEY(0, 2);
+ case '5': KEY(0, 1);
+ case '6': KEY(0, 0);
+
+ case EStdKeyDictaphonePlay: KEY(1, 6);
+ case '7': KEY(1, 5);
+ case '8': KEY(1, 4);
+ case '9': KEY(1, 3);
+ case '0': KEY(1, 2);
+ case EStdKeyBackspace: KEY(1, 1);
+ case EStdKeySingleQuote: KEY(1, 0);
+
+ case EStdKeyEscape: KEY(2, 6);
+ case 'Q': KEY(2, 5);
+ case 'W': KEY(2, 4);
+ case 'E': KEY(2, 3);
+ case 'R': KEY(2, 2);
+ case 'T': KEY(2, 1);
+ case 'Y': KEY(2, 0);
+
+ case EStdKeyMenu: KEY(3, 6);
+ case 'U': KEY(3, 5);
+ case 'I': KEY(3, 4);
+ case 'O': KEY(3, 3);
+ case 'P': KEY(3, 2);
+ case 'L': KEY(3, 1);
+ case EStdKeyEnter: KEY(3, 0);
+
+ case EStdKeyLeftCtrl: KEY(4, 6);
+ case EStdKeyTab: KEY(4, 5);
+ case 'A': KEY(4, 4);
+ case 'S': KEY(4, 3);
+ case 'D': KEY(4, 2);
+ case 'F': KEY(4, 1);
+ case 'G': KEY(4, 0);
+
+ case EStdKeyLeftFunc: KEY(5, 6);
+ case 'H': KEY(5, 5);
+ case 'J': KEY(5, 4);
+ case 'K': KEY(5, 3);
+ case 'M': KEY(5, 2);
+ case EStdKeyFullStop: KEY(5, 1);
+ case EStdKeyDownArrow: KEY(5, 0);
+
+ case EStdKeyRightShift: KEY(6, 6);
+ case 'Z': KEY(6, 5);
+ case 'X': KEY(6, 4);
+ case 'C': KEY(6, 3);
+ case 'V': KEY(6, 2);
+ case 'B': KEY(6, 1);
+ case 'N': KEY(6, 0);
+
+ case EStdKeyLeftShift: KEY(7, 6);
+ case EStdKeyDictaphoneStop: KEY(7, 5);
+ case EStdKeySpace: KEY(7, 4);
+ case EStdKeyUpArrow: KEY(7, 3);
+ case EStdKeyComma: KEY(7, 2);
+ case EStdKeyLeftArrow: KEY(7, 1);
+ case EStdKeyRightArrow: KEY(7, 0);
+ }
+
+ if (idx >= 0) {
+ if (value)
+ keyboardColumns[idx >> 8] |= (idx & 0xFF);
+ else
+ keyboardColumns[idx >> 8] &= ~(idx & 0xFF);
+ }
+}
+
+void Emulator::updateTouchInput(int32_t x, int32_t y, bool down) {
+ pendingInterrupts &= ~(1 << EINT3);
+ if (down)
+ pendingInterrupts |= (1 << EINT3);
+ touchX = x;
+ touchY = y;
+}
+
+}
--- /dev/null
+#ifndef SA1100_H
+#define SA1100_H
+
+#include "emubase.h"
+#include "sa1100_defs.h"
+#include "hardware.h"
+#include "sa1100/memory_conf.h"
+
+namespace SA1100 {
+
+// Relatively simple implementation of a StrongARM SA-1100 CPU
+//
+// The following resources are invaluable to determining how the SA-1100 operates:
+//
+// Technical documentation: https://courses.cs.washington.edu/courses/cse466/00au/sa1100dev.pdf
+// Address space layout: https://people.kth.se/~maguire/badge3/memorymap.html
+// ARMware emulator: https://github.com/Halajohn/ARMware
+// bookboot: https://web.archive.org/web/20060716045807/http://linux-7110.sourceforge.net/files/People/Klaasjan/netbook/
+
+class Emulator : public EmuBase {
+public:
+ // 16MB ROM file
+ uint8_t ROM[0x1000000];
+ // 16MB RAM/bank0, mapped at base addr 0xC0000000
+ uint8_t MemoryBlockC0[0x1000000];
+ // 16MB RAM/bank1, mapped at base addr 0xC8000000
+ // On the netBook, the OS.img minus the 256-byte wrapper is copied to this RAM
+ // See the bookboot README file (linked above)
+ uint8_t MemoryBlockC8[0x1000000];
+ enum { MemoryBlockMask = 0x0FFFFFF };
+
+private:
+ uint16_t pendingInterrupts = 0;
+ uint16_t interruptMask = 0;
+ uint32_t portValues = 0;
+ uint32_t portDirections = 0;
+ uint32_t pwrsr = 0x00002000; // cold start flag
+ uint32_t lcdControl = 0;
+ uint32_t lcdAddress = 0;
+ uint32_t rtc = 0;
+ uint16_t lastSSIRequest = 0;
+ int ssiReadCounter = 0;
+
+ uint32_t kScan = 0;
+ uint8_t keyboardColumns[8] = {0,0,0,0,0,0,0};
+ int32_t touchX = 0, touchY = 0;
+
+ Timer tc1, tc2;
+ UART uart1, uart2;
+ MemoryConf memoryConfig;
+
+ bool halted = false, asleep = false;
+
+ uint32_t getRTC();
+
+public:
+ MaybeU32 readPhysical(uint32_t physAddr, ValueSize valueSize) override;
+ bool writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) override;
+
+private:
+ uint8_t readPhysical8(uint32_t physAddr);
+ uint32_t readPhysical32(uint32_t physAddr);
+
+ bool writePhysical8(uint8_t value, uint32_t physAddr);
+ bool writePhysical32(uint32_t value, uint32_t physAddr);
+
+ uint8_t readPCM8(uint32_t physAddr);
+ uint32_t readPCM32(uint32_t physAddr);
+
+ uint8_t readSCM8(uint32_t physAddr);
+ uint32_t readSCM32(uint32_t physAddr);
+
+ bool writePCM8(uint8_t value, uint32_t physAddr);
+ bool writePCM32(uint32_t value, uint32_t physAddr);
+
+ bool writeSCM8(uint8_t value, uint32_t physAddr);
+ bool writeSCM32(uint32_t value, uint32_t physAddr);
+
+ bool configured = false;
+ void configure();
+
+ const char *identifyObjectCon(uint32_t ptr);
+ void fetchStr(uint32_t str, char *buf);
+ void fetchName(uint32_t obj, char *buf);
+ void fetchProcessFilename(uint32_t obj, char *buf);
+ void debugPC(uint32_t pc);
+ void diffPorts(uint32_t oldval, uint32_t newval);
+ void diffInterrupts(uint16_t oldval, uint16_t newval);
+ uint32_t readKeyboard();
+
+public:
+ Emulator();
+ uint8_t *getROMBuffer() override;
+ size_t getROMSize() override;
+ void loadROM(uint8_t *osimg, size_t osimgSize, uint8_t *bootloader, size_t bootloaderSize) override;
+ void executeUntil(int64_t cycles) override;
+ int32_t getClockSpeed() const override { return CLOCK_SPEED; }
+ const char *getDeviceName() const override;
+ int getDigitiserWidth() const override;
+ int getDigitiserHeight() const override;
+ int getLCDOffsetX() const override;
+ int getLCDOffsetY() const override;
+ int getLCDWidth() const override;
+ int getLCDHeight() const override;
+ void readLCDIntoBuffer(uint8_t **lines, bool is32BitOutput) const override;
+ void setKeyboardKey(EpocKey key, bool value) override;
+ void updateTouchInput(int32_t x, int32_t y, bool down) override;
+};
+
+} // namespace SA1100
+
+#endif // SA1100_H