]> localhost Git - WindEmu.git/commitdiff
broken draft of a fully custom ARM emulator core
authorAsh Wolf <ninji@wuffs.org>
Sun, 22 Dec 2019 05:02:55 +0000 (05:02 +0000)
committerAsh Wolf <ninji@wuffs.org>
Sun, 22 Dec 2019 05:02:55 +0000 (05:02 +0000)
WindCore/WindCore.pro
WindCore/arm710a.cpp [new file with mode: 0644]
WindCore/arm710a.h [new file with mode: 0644]
WindCore/emu.cpp
WindCore/emu.h
WindCore/wind_hw.h
WindQt/WindQt.pro
WindQt/mainwindow.cpp

index 74c4a850fc02e4862eeb49c01f882b0b88e7b65f..71ef9ce8b3f290143a64b1a4b758da7471d20da9 100644 (file)
@@ -5,10 +5,11 @@
 #-------------------------------------------------
 
 QT       -= core gui
+QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.14
 
 TARGET = WindCore
 TEMPLATE = lib
-CONFIG += staticlib
+CONFIG += staticlib c++17
 
 # The following define makes your compiler emit warnings if you use
 # any feature of Qt which has been marked as deprecated (the exact warnings
@@ -22,6 +23,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
 
 SOURCES += \
+    arm710a.cpp \
     etna.cpp \
     wind.cpp \
     isa-arm.c \
@@ -31,6 +33,7 @@ SOURCES += \
     emu.cpp
 
 HEADERS += \
+    arm710a.h \
     etna.h \
     wind_hw.h \
     wind.h \
diff --git a/WindCore/arm710a.cpp b/WindCore/arm710a.cpp
new file mode 100644 (file)
index 0000000..41434f3
--- /dev/null
@@ -0,0 +1,976 @@
+#include "arm710a.h"
+#include "common.h"
+
+// this will need changing if this code ever compiles on big-endian procs
+inline uint32_t read32LE(uint8_t *p) {
+       return *((uint32_t *)p);
+}
+inline void write32LE(uint8_t *p, uint32_t v) {
+       *((uint32_t *)p) = v;
+}
+
+
+void ARM710a::switchBank(BankIndex newBank) {
+       if (newBank != bank) {
+               // R13 and R14 need saving/loading for all banks
+               allModesBankedRegisters[bank][0] = GPRs[13];
+               allModesBankedRegisters[bank][1] = GPRs[14];
+               GPRs[13] = allModesBankedRegisters[newBank][0];
+               GPRs[14] = allModesBankedRegisters[newBank][1];
+
+               // R8 to R12 are only banked in FIQ mode
+               auto oldBankR8to12 = (bank == FiqBank) ? FiqBank : MainBank;
+               auto newBankR8to12 = (newBank == FiqBank) ? FiqBank : MainBank;
+               if (oldBankR8to12 != newBankR8to12) {
+                       // swap these sets around
+                       for (int i = 0; i < 5; i++)
+                               fiqBankedRegisters[oldBankR8to12][i] = GPRs[8 + i];
+                       for (int i = 0; i < 5; i++)
+                               GPRs[8 + i] = fiqBankedRegisters[newBankR8to12][i];
+               }
+
+               bank = newBank;
+       }
+}
+
+
+void ARM710a::switchMode(Mode newMode) {
+       auto oldMode = currentMode();
+       if (newMode != oldMode) {
+               switchBank(modeToBank[newMode & 0xF]);
+
+               CPSR &= ~CPSR_ModeMask;
+               CPSR |= newMode;
+       }
+}
+
+void ARM710a::raiseException(Mode mode, uint32_t savedPC, uint32_t newPC) {
+       auto bankIndex = modeToBank[mode & 0xF];
+       SPSRs[bankIndex] = CPSR;
+
+       switchMode(mode);
+
+       prefetchCount = 0;
+       GPRs[14] = savedPC;
+       GPRs[15] = newPC;
+}
+
+void ARM710a::requestFIQ() {
+       raiseException(FIQ32, GPRs[15], 0x1C);
+       CPSR |= CPSR_FIQDisable;
+       CPSR |= CPSR_IRQDisable;
+}
+
+void ARM710a::requestIRQ() {
+       raiseException(FIQ32, GPRs[15], 0x18);
+       CPSR |= CPSR_IRQDisable;
+}
+
+void ARM710a::reset() {
+       clearCache();
+       raiseException(Supervisor32, 0, 0);
+}
+
+
+
+uint32_t ARM710a::tick() {
+       // pop an instruction off the end of the pipeline
+       bool haveInsn = false;
+       uint32_t insn;
+       MMUFault insnFault;
+       if (prefetchCount == 2) {
+               haveInsn = true;
+               insn = prefetch[1];
+               insnFault = prefetchFaults[1];
+       }
+
+       // move the instruction we fetched last tick once along
+       if (prefetchCount >= 1) {
+               prefetch[1] = prefetch[0];
+               prefetchFaults[1] = prefetchFaults[0];
+       }
+
+       // fetch a new instruction
+       auto newInsn = readVirtual(GPRs[15], V32);
+       GPRs[15] += 4;
+       prefetch[0] = newInsn.first.value_or(0);
+       prefetchFaults[0] = newInsn.second;
+       if (prefetchCount < 2)
+               prefetchCount++;
+
+       // now deal with the one we popped
+       uint32_t clocks = 1;
+       if (haveInsn) {
+               if (insnFault != NoFault) {
+                       // Raise a prefetch error
+                       // These do not set FSR or FAR
+                       raiseException(Abort32, GPRs[15] - 8, 0xC);
+               } else {
+                       clocks += executeInstruction(insn);
+               }
+       }
+
+       if (faultTriggeredThisCycle) {
+               // data abort time!
+               faultTriggeredThisCycle = false;
+               raiseException(Abort32, GPRs[15] - 4, 0x10);
+       }
+
+       return clocks;
+}
+
+
+static inline uint32_t extract(uint32_t value, uint32_t hiBit, uint32_t loBit) {
+       return (value >> loBit) & ((1 << (hiBit - loBit + 1)) - 1);
+//     return (value >> (32 - offset - length)) & ((1 << length) - 1);
+}
+static inline bool extract1(uint32_t value, uint32_t bit) {
+       return (value >> bit) & 1;
+}
+
+
+uint32_t ARM710a::executeInstruction(uint32_t i) {
+       uint32_t cycles = 1;
+
+       // a big old dispatch thing here
+       // but first, conditions!
+       if (!checkCondition(extract(i, 31, 28)))
+               return cycles;
+
+       if ((i & 0x0F000000) == 0x0F000000)
+               raiseException(Supervisor32, GPRs[15] - 8, 0x08);
+       else if ((i & 0x0F000F10) == 0x0E000F10)
+               cycles += execCP15RegisterTransfer(extract(i,23,21), extract1(i,20), extract(i,19,16), extract(i,15,12), extract(i,7,5), extract(i,3,0));
+       else if ((i & 0x0E000000) == 0x0A000000)
+               cycles += execBranch(extract1(i,24), extract(i,23,0));
+       else if ((i & 0x0E000000) == 0x08000000)
+               cycles += execBlockDataTransfer(extract(i,24,20), extract(i,19,16), extract(i,15,0));
+       else if ((i & 0x0C000000) == 0x04000000)
+               cycles += execSingleDataTransfer(extract(i,25,20), extract(i,19,16), extract(i,15,12), extract(i,11,0));
+       else if ((i & 0x0FB00FF0) == 0x01000090)
+               cycles += execSingleDataSwap(extract1(i,22), extract(i,19,16), extract(i,15,12), extract(i,3,0));
+       else if ((i & 0x0F8000F0) == 0x00000090)
+               cycles += execMultiply(extract(i,21,20), extract(i,19,16), extract(i,15,12), extract(i,11,8), extract(i,3,0));
+       else if ((i & 0x0C000000) == 0x00000000)
+               cycles += execDataProcessing(extract1(i,25), extract(i,24,21), extract1(i,20), extract(i,19,16), extract(i,15,12), extract(i,11,0));
+       else
+               raiseException(Undefined32, GPRs[15] - 8, 0x04);
+
+       return cycles;
+}
+
+uint32_t ARM710a::execDataProcessing(bool I, uint32_t Opcode, bool S, uint32_t Rn, uint32_t Rd, uint32_t Operand2)
+{
+       uint32_t cycles = 0; // TODO increment me semi-accurately
+       bool shifterCarryOutput;
+
+       // compute our Op1 (may be unnecessary but that's ok)
+       uint32_t op1 = GPRs[Rn];
+
+       // compute our Op2
+       uint32_t op2;
+       if (!I) {
+               // REGISTER
+               uint32_t Rm = extract(Operand2, 3, 0);
+               op2 = GPRs[Rm];
+
+               uint8_t shiftBy;
+
+               // this is the real painful one, honestly
+               if (extract(Operand2, 4, 4)) {
+                       // Shift by Register
+                       uint32_t Rs = extract(Operand2, 11, 8);
+                       shiftBy = GPRs[Rs] & 0xFF;
+               } else {
+                       // Shift by Immediate
+                       shiftBy = extract(Operand2, 11, 7);
+
+                       if (Rn == 15) // if PC is fetched...
+                               op1 -= 4; // compensate for prefetching
+                       if (Rm == 15)
+                               op2 -= 4;
+               }
+
+               switch (extract(Operand2, 6, 5)) {
+               case 0: // Logical Left (LSL)
+                       if (shiftBy == 0) {
+                               shifterCarryOutput = flagC();
+                               // no change to op2!
+                       } else if (shiftBy <= 31) {
+                               shifterCarryOutput = extract1(op2, 31 - shiftBy);
+                               op2 <<= shiftBy;
+                       } else if (shiftBy == 32) {
+                               shifterCarryOutput = extract1(op2, 0);
+                               op2 = 0;
+                       } else /*if (shiftBy >= 33)*/ {
+                               shifterCarryOutput = false;
+                               op2 = 0;
+                       }
+                       break;
+               case 1: // Logical Right (LSR)
+                       if (shiftBy == 0 || shiftBy == 32) {
+                               shifterCarryOutput = extract1(op2, 31);
+                               op2 = 0;
+                       } else if (shiftBy <= 31) {
+                               shifterCarryOutput = extract1(op2, shiftBy - 1);
+                               op2 >>= shiftBy;
+                       } else /*if (shiftBy >= 33)*/ {
+                               shifterCarryOutput = false;
+                               op2 = 0;
+                       }
+                       break;
+               case 2: // Arithmetic Right (ASR)
+                       if (shiftBy == 0 || shiftBy >= 32) {
+                               shifterCarryOutput = extract1(op2, 31);
+                               op2 = (int32_t)op2 >> 31;
+                       } else /*if (shiftBy <= 31)*/ {
+                               shifterCarryOutput = extract1(op2, shiftBy - 1);
+                               op2 = (int32_t)op2 >> shiftBy;
+                       }
+                       break;
+               case 3: // Rotate Right (ROR)
+                       if (shiftBy == 0) { // treated as RRX
+                               shifterCarryOutput = op2 & 1;
+                               op2 >>= 1;
+                               op2 |= flagC() ? 0x80000000 : 0;
+                       } else {
+                               shiftBy %= 32;
+                               if (shiftBy == 0) { // like 32
+                                       shifterCarryOutput = extract1(op2, 31);
+                                       // no change to op2
+                               } else {
+                                       shifterCarryOutput = extract1(op2, shiftBy - 1);
+                                       op2 = ROR(op2, shiftBy);
+                               }
+                       }
+                       break;
+               }
+       } else {
+               // IMMEDIATE
+               if (Rn == 15) // if PC is fetched...
+                       op1 -= 4; // compensate for prefetching
+
+               uint32_t Rotate = extract(Operand2, 11, 8);
+               uint32_t Imm = extract(Operand2, 7, 0);
+               Imm = (uint32_t)(int8_t)Imm;
+               op2 = ROR(Imm, Rotate * 2);
+               shifterCarryOutput = flagC(); // correct? unsure...
+       }
+
+       // we have our operands, what next
+       uint64_t result = 0;
+       uint32_t flags = 0;
+
+#define LOGICAL_OP(v) \
+       result = v; \
+       flags |= (result & 0xFFFFFFFF) ? 0 : CPSR_Z; \
+       flags |= (result & 0x80000000) ? CPSR_N : 0; \
+       flags |= shifterCarryOutput ? CPSR_C : 0; \
+       flags |= (CPSR & CPSR_V);
+
+#define ADD_OP(a, b, c) \
+       result = a + b + (uint32_t)(c); \
+       flags |= (result & 0xFFFFFFFF) ? 0 : CPSR_Z; \
+       flags |= (result & 0x80000000) ? CPSR_N : 0; \
+       flags |= (result & 0x100000000) ? CPSR_C : 0; \
+       flags |= (result >= 0x100000000) ? CPSR_V : 0;
+
+#define SUB_OP(a, b, c) ADD_OP(a, ~b, c)
+
+
+       switch (Opcode) {
+       case 0:   LOGICAL_OP(op1 & op2);     break; // AND
+       case 1:   LOGICAL_OP(op1 ^ op2);     break; // EOR
+       case 2:   SUB_OP(op1, op2, 1);       break; // SUB
+       case 3:   SUB_OP(op2, op1, 1);       break; // RSB
+       case 4:   ADD_OP(op1, op2, 0);       break; // ADD
+       case 5:   ADD_OP(op1, op2, flagC()); break; // ADC
+       case 6:   SUB_OP(op1, op2, flagC()); break; // SBC
+       case 7:   SUB_OP(op2, op1, flagC()); break; // RSC
+       case 8:   LOGICAL_OP(op1 & op2);     break; // TST
+       case 9:   LOGICAL_OP(op1 ^ op2);     break; // TEQ
+       case 0xA: SUB_OP(op1, op2, 1);       break; // CMP
+       case 0xB: ADD_OP(op1, op2, 0);       break; // CMN
+       case 0xC: LOGICAL_OP(op1 | op2);     break; // ORR
+       case 0xD: LOGICAL_OP(op2);           break; // MOV
+       case 0xE: LOGICAL_OP(op1 & ~op2);    break; // BIC
+       case 0xF: LOGICAL_OP(~op2);          break; // MVN
+       }
+
+       if (Opcode >= 8 && Opcode <= 0xB) {
+               // Output-less opcodes: special behaviour
+               if (S) {
+                       CPSR = (CPSR & ~CPSR_FlagMask) | flags;
+               } else if (Opcode == 8) {
+                       // MRS, CPSR -> Reg
+                       GPRs[Rd] = CPSR;
+               } else if (Opcode == 9) {
+                       // MSR, Reg -> CPSR
+                       bool canChangeMode = extract1(Rn, 0);
+                       if (canChangeMode && isPrivileged()) {
+                               auto newCPSR = GPRs[extract(Operand2, 3, 0)];
+                               switchMode(modeFromCPSR(newCPSR));
+                               CPSR = newCPSR;
+                       } 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);
+                       }
+               } else if (Opcode == 0xA) {
+                       // MRS, SPSR -> Reg
+                       if (isPrivileged())
+                               GPRs[Rd] = SPSRs[currentBank()];
+               } else /*if (Opcode == 0xB)*/ {
+                       bool canChangeMode = extract1(Rn, 0);
+                       if (isPrivileged()) {
+                               if (canChangeMode) {
+                                       SPSRs[currentBank()] = GPRs[extract(Operand2, 3, 0)];
+                               } else {
+                                       // same hat
+                                       auto newFlag = I ? op2 : GPRs[extract(Operand2, 3, 0)];
+                                       SPSRs[currentBank()] &= ~CPSR_FlagMask;
+                                       SPSRs[currentBank()] |= (newFlag & CPSR_FlagMask);
+                               }
+                       }
+               }
+       } else {
+               GPRs[Rd] = result & 0xFFFFFFFF;
+
+               if (Rd == 15) {
+                       // Writing to PC
+                       // Special things occur here!
+                       prefetchCount = 0;
+                       if (S && isPrivileged()) {
+                               // We SHOULD be privileged
+                               // (Raise an error otherwise...?)
+                               auto saved = SPSRs[currentBank()];
+                               switchMode(modeFromCPSR(saved));
+                               CPSR = saved;
+                       }
+               } else if (S) {
+                       CPSR = (CPSR & ~CPSR_FlagMask) | flags;
+               }
+       }
+
+       return cycles;
+}
+
+uint32_t ARM710a::execMultiply(uint32_t AS, uint32_t Rd, uint32_t Rn, uint32_t Rs, uint32_t Rm)
+{
+       // no need for R15 fuckery
+       // datasheet says it's not allowed here
+       if (AS & 2)
+               GPRs[Rd] = GPRs[Rm] * GPRs[Rs] + GPRs[Rn];
+       else
+               GPRs[Rd] = GPRs[Rm] * GPRs[Rs];
+
+       if (AS & 1) {
+               CPSR &= ~(CPSR_N | CPSR_Z);
+               CPSR |= GPRs[Rd] ? 0 : CPSR_Z;
+               CPSR |= (GPRs[Rd] & 0x80000000) ? CPSR_N : 0;
+       }
+
+       return 0;
+}
+
+uint32_t ARM710a::execSingleDataSwap(bool B, uint32_t Rn, uint32_t Rd, uint32_t Rm)
+{
+       auto valueSize = B ? V8 : V32;
+       auto readResult = readVirtual(GPRs[Rn], valueSize);
+       auto fault = readResult.second;
+
+       if (fault == NoFault) {
+               fault = writeVirtual(GPRs[Rm], GPRs[Rn], valueSize);
+               if (fault == NoFault)
+                       GPRs[Rd] = readResult.first.value();
+       }
+
+       if (fault != NoFault)
+               reportFault(fault);
+
+       return 1;
+}
+
+uint32_t ARM710a::execSingleDataTransfer(uint32_t IPUBWL, uint32_t Rn, uint32_t Rd, uint32_t offset)
+{
+       bool load = extract1(IPUBWL, 0);
+       bool writeback = extract1(IPUBWL, 1);
+       auto valueSize = extract1(IPUBWL, 2) ? V8 : V32;
+       bool up = extract1(IPUBWL, 3);
+       bool preIndex = extract1(IPUBWL, 4);
+       bool immediate = extract1(IPUBWL, 5);
+
+       // calculate the offset
+       uint32_t calcOffset;
+       if (!immediate) {
+               // REGISTER
+               uint32_t Rm = extract(offset, 3, 0);
+               calcOffset = GPRs[Rm];
+
+               uint8_t shiftBy = extract(offset, 11, 7);
+
+               switch (extract(offset, 6, 5)) {
+               case 0: // Logical Left (LSL)
+                       if (shiftBy > 0)
+                               calcOffset <<= shiftBy;
+                       break;
+               case 1: // Logical Right (LSR)
+                       if (shiftBy == 0)
+                               calcOffset = 0;
+                       else
+                               calcOffset >>= shiftBy;
+                       break;
+               case 2: // Arithmetic Right (ASR)
+                       if (shiftBy == 0)
+                               calcOffset = (int32_t)calcOffset >> 31;
+                       else
+                               calcOffset = (int32_t)calcOffset >> shiftBy;
+                       break;
+               case 3: // Rotate Right (ROR)
+                       if (shiftBy == 0) { // treated as RRX
+                               calcOffset >>= 1;
+                               calcOffset |= flagC() ? 0x80000000 : 0;
+                       } else
+                               calcOffset = ROR(calcOffset, shiftBy);
+                       break;
+               }
+       } else {
+               // IMMEDIATE
+               uint32_t Rotate = extract(offset, 11, 8);
+               uint32_t Imm = extract(offset, 7, 0);
+               Imm = (uint32_t)(int8_t)Imm;
+               calcOffset = ROR(Imm, Rotate * 2);
+       }
+
+       uint32_t base = GPRs[Rn];
+       if (Rn == 15) base -= 4; // prefetch adjustment
+       uint32_t modifiedBase = up ? (base + calcOffset) : (base - calcOffset);
+       uint32_t transferAddr = preIndex ? modifiedBase : base;
+
+       bool changeModes = !preIndex && writeback && isPrivileged();
+       auto saveMode = currentMode();
+
+       MMUFault fault;
+
+       if (load) {
+               if (changeModes) switchMode(User32);
+               auto readResult = readVirtual(transferAddr, valueSize);
+               if (changeModes) switchMode(saveMode);
+               if (readResult.first.has_value())
+                       GPRs[Rd] = readResult.first.value();
+               fault = readResult.second;
+       } else {
+               uint32_t value = GPRs[Rd];
+               if (changeModes) switchMode(User32);
+               fault = writeVirtual(value, transferAddr, valueSize);
+               if (changeModes) switchMode(saveMode);
+       }
+
+       if (preIndex && writeback)
+               GPRs[Rn] = modifiedBase;
+
+       if (fault != NoFault)
+               reportFault(fault);
+
+       return 2;
+}
+
+uint32_t ARM710a::execBlockDataTransfer(uint32_t PUSWL, uint32_t Rn, uint32_t registerList)
+{
+       bool load = extract1(PUSWL, 0);
+       bool store = !load;
+       bool writeback = extract1(PUSWL, 1);
+       bool psrForceUser = extract1(PUSWL, 2);
+       bool up = extract1(PUSWL, 3);
+       bool preIndex = extract1(PUSWL, 4);
+
+       MMUFault fault = NoFault;
+       uint32_t base = GPRs[Rn] & ~3;
+       uint32_t blockSize = popcount32(registerList) * 4;
+
+       uint32_t lowAddr, updatedBase;
+       if (up) {
+               updatedBase = base + blockSize;
+               lowAddr = base + (preIndex ? 4 : 0);
+       } else {
+               updatedBase = base - blockSize;
+               lowAddr = updatedBase + (preIndex ? 0 : 4);
+       }
+
+       auto saveBank = bank;
+       if (psrForceUser && (store || !(registerList & 0x8000)))
+               switchBank(MainBank);
+
+       bool doneWriteback = false;
+       if (load && writeback) {
+               doneWriteback = true;
+               GPRs[Rn] = updatedBase;
+       }
+
+       uint32_t addr = lowAddr;
+       for (int i = 0; i < 16; i++) {
+               if (registerList & (1 << i)) {
+                       // work on this one
+                       if (load) {
+                               // handling for LDM faults may be kinda iffy...
+                               // wording on datasheet is a bit unclear
+                               auto readResult = readVirtual(addr, V32);
+                               if (readResult.first.has_value())
+                                       GPRs[i] = readResult.first.value();
+                               if (readResult.second != NoFault) {
+                                       fault = readResult.second;
+                                       break;
+                               }
+                       } else {
+                               auto newFault = writeVirtual(GPRs[i], addr, V32);
+                               if (newFault != NoFault)
+                                       fault = newFault;
+                       }
+
+                       addr += 4;
+
+                       if (writeback && !doneWriteback) {
+                               doneWriteback = true;
+                               GPRs[Rn] = updatedBase;
+                       }
+               }
+       }
+
+       // datasheet specifies that base register must be
+       // restored if an error occurs during LDM
+       if (load && fault != NoFault)
+               GPRs[Rn] = writeback ? updatedBase : base;
+
+       if (psrForceUser && (!load || !(registerList & 0x8000)))
+               switchBank(saveBank);
+
+       if (fault != NoFault)
+               reportFault(fault);
+
+       return 0; // fixme
+}
+
+uint32_t ARM710a::execBranch(bool L, uint32_t offset)
+{
+       if (L)
+               GPRs[14] = GPRs[15] - 8;
+
+       // start with 24 bits, shift left 2, sign extend to 32
+       int32_t sextOffset = (int32_t)(offset << 8) >> 6;
+
+       prefetchCount = 0;
+       GPRs[15] -= 4; // account for our prefetch being +4 too much
+       GPRs[15] += sextOffset;
+       return 0;
+}
+
+uint32_t ARM710a::execCP15RegisterTransfer(uint32_t CPOpc, bool L, uint32_t CRn, uint32_t Rd, uint32_t CP, uint32_t CRm)
+{
+       (void)CPOpc; // not used by ARM CP15
+       (void)CP;
+       (void)CRm;
+
+       if (!isPrivileged())
+               return 0;
+
+       if (L) {
+               // read a value
+               uint32_t what = 0;
+
+               switch (CRn) {
+               case 0: what = cp15_id; break;
+               case 5: what = cp15_faultStatus; break;
+               case 6: what = cp15_faultAddress; break;
+               }
+
+               if (Rd == 15)
+                       CPSR = (CPSR & ~CPSR_FlagMask) | (what & CPSR_FlagMask);
+               else
+                       GPRs[Rd] = what;
+       } else {
+               // store a value
+               uint32_t what = GPRs[Rd];
+
+               switch (CRn) {
+               case 1: cp15_control = what; break;
+               case 2: cp15_translationTableBase = what; break;
+               case 3: cp15_domainAccessControl = what; break;
+               case 5: flushTlb(); break;
+               case 6: flushTlb(what); break;
+               case 7: clearCache(); break;
+               }
+       }
+
+       return 0;
+}
+
+
+
+void ARM710a::clearCache() {
+       for (uint32_t i = 0; i < CacheSets; i++) {
+               for (uint32_t j = 0; j < CacheBlocksPerSet; j++) {
+                       cacheBlockTags[i][j] = 0;
+               }
+       }
+}
+
+uint8_t *ARM710a::findCacheLine(uint32_t virtAddr) {
+       uint32_t set = virtAddr & CacheAddressSetMask;
+       uint32_t tag = virtAddr & CacheAddressTagMask;
+       set >>= CacheAddressSetShift;
+
+       for (uint32_t i = 0; i < CacheBlocksPerSet; i++) {
+               if (cacheBlockTags[set][i] & CacheBlockEnabled) {
+                       if ((cacheBlockTags[set][i] & ~CacheBlockEnabled) == tag)
+                               return &cacheBlocks[set][i][0];
+               }
+       }
+
+       return nullptr;
+}
+
+pair<MaybeU32, ARM710a::MMUFault> ARM710a::addCacheLineAndRead(uint32_t physAddr, uint32_t virtAddr, ValueSize valueSize, int domain, bool isPage) {
+       uint32_t set = virtAddr & CacheAddressSetMask;
+       uint32_t tag = virtAddr & CacheAddressTagMask;
+       set >>= CacheAddressSetShift;
+
+       // "it will be randomly placed in a cache bank"
+       //    - the ARM710a data sheet, 6-2 (p90)
+       uint32_t i = rand() % CacheBlocksPerSet;
+       uint8_t *block = &cacheBlocks[set][i][0];
+       MaybeU32 result;
+       MMUFault fault = NoFault;
+
+       for (uint32_t j = 0; j < CacheBlockSize; j += 4) {
+               auto word = readPhysical(physAddr + j, V32);
+               if (word.has_value()) {
+                       write32LE(&block[j], word.value());
+                       if (valueSize == V8 && j == (virtAddr & CacheAddressLineMask & ~3))
+                               result = (word.value() >> ((virtAddr & 3) * 8)) & 0xFF;
+                       else if (valueSize == V32 && j == (virtAddr & CacheAddressLineMask))
+                               result = word.value();
+               } else {
+                       // read error, great
+                       // TODO: should probably prioritise specific kinds of faults over others
+                       fault = encodeFaultSorP(SorPLinefetchError, isPage, domain, virtAddr & ~CacheAddressLineMask);
+                       break;
+               }
+       }
+
+       // the cache block is only stored if it's complete
+       if (fault == NoFault)
+               cacheBlockTags[set][i] = tag | CacheBlockEnabled;
+
+       return make_pair(result, fault);
+}
+
+MaybeU32 ARM710a::readCached(uint32_t virtAddr, ValueSize valueSize) {
+       uint8_t *line = findCacheLine(virtAddr);
+       if (line) {
+               if (valueSize == V8)
+                       return line[virtAddr & CacheAddressLineMask];
+               else /*if (valueSize == V32)*/
+                       return read32LE(&line[virtAddr & CacheAddressLineMask]);
+       }
+       return {};
+}
+
+
+bool ARM710a::writeCached(uint32_t value, uint32_t virtAddr, ValueSize valueSize) {
+       uint8_t *line = findCacheLine(virtAddr);
+       if (line) {
+               if (valueSize == V8)
+                       line[virtAddr & CacheAddressLineMask] = value & 0xFF;
+               else /*if (valueSize == V32)*/
+                       write32LE(&line[virtAddr & CacheAddressLineMask], value);
+               return true;
+       }
+       return false;
+}
+
+
+uint32_t ARM710a::physAddrFromTlbEntry(TlbEntry *tlbEntry, uint32_t virtAddr) {
+       if ((tlbEntry->lv2Entry & 3) == 2) {
+               // Smøl page
+               return (tlbEntry->lv2Entry & 0xFFFFF000) | (virtAddr & 0xFFF);
+       } else if ((tlbEntry->lv2Entry & 3) == 1) {
+               // Lørge page
+               return (tlbEntry->lv2Entry & 0xFFFF0000) | (virtAddr & 0xFFFF);
+       } else {
+               // Section
+               return (tlbEntry->lv1Entry & 0xFFF00000) | (virtAddr & 0xFFFFF);
+       }
+}
+
+
+MaybeU32 ARM710a::virtToPhys(uint32_t virtAddr) {
+       if (!isMMUEnabled())
+               return virtAddr;
+
+       TlbEntry tempEntry;
+       auto translated = translateAddressUsingTlb(virtAddr, &tempEntry);
+       if (holds_alternative<TlbEntry *>(translated)) {
+               auto tlbEntry = get<TlbEntry *>(translated);
+               return physAddrFromTlbEntry(tlbEntry, virtAddr);
+       } else {
+               return MaybeU32();
+       }
+}
+
+
+MaybeU32 ARM710a::readVirtualDebug(uint32_t virtAddr, ValueSize valueSize) {
+       if (auto v = virtToPhys(virtAddr); v.has_value())
+               return readPhysical(v.value(), valueSize);
+       else
+               return {};
+}
+
+
+pair<MaybeU32, ARM710a::MMUFault> ARM710a::readVirtual(uint32_t virtAddr, ValueSize valueSize) {
+       if (isAlignmentFaultEnabled() && valueSize == V32 && virtAddr & 3)
+               return make_pair(MaybeU32(), encodeFault(AlignmentFault, 0, virtAddr));
+
+       // fast path: cache
+       if (auto v = readCached(virtAddr, valueSize); v.has_value())
+               return make_pair(v.value(), NoFault);
+
+       if (!isMMUEnabled()) {
+               // things are very simple without a MMU
+               if (auto v = readPhysical(virtAddr, valueSize); v.has_value())
+                       return make_pair(v.value(), NoFault);
+               else
+                       return make_pair(MaybeU32(), NonMMUError);
+       }
+
+       auto translated = translateAddressUsingTlb(virtAddr);
+       if (holds_alternative<MMUFault>(translated))
+               return make_pair(MaybeU32(), get<MMUFault>(translated));
+
+       // resolve this boy
+       auto tlbEntry = get<TlbEntry *>(translated);
+
+       if (auto f = checkAccessPermissions(tlbEntry, virtAddr, false); f != NoFault)
+               return make_pair(MaybeU32(), f);
+
+       int domain = (tlbEntry->lv1Entry >> 5) & 0xF;
+       bool isPage = (tlbEntry->lv2Entry != 0);
+
+       uint32_t physAddr = physAddrFromTlbEntry(tlbEntry, virtAddr);
+       bool cacheable = tlbEntry->lv2Entry ? (tlbEntry->lv2Entry & 8) : (tlbEntry->lv1Entry & 8);
+
+       if (cacheable && isCacheEnabled())
+               return addCacheLineAndRead(physAddr, virtAddr, valueSize, domain, isPage);
+       else if (auto result = readPhysical(physAddr, valueSize); result.has_value())
+               return make_pair(result, NoFault);
+       else
+               return make_pair(result, encodeFaultSorP(SorPOtherBusError, isPage, domain, virtAddr));
+}
+
+ARM710a::MMUFault ARM710a::writeVirtual(uint32_t value, uint32_t virtAddr, ValueSize valueSize) {
+       if (isAlignmentFaultEnabled() && valueSize == V32 && virtAddr & 3)
+               return encodeFault(AlignmentFault, 0, virtAddr);
+
+       if (!isMMUEnabled()) {
+               // direct virtual -> physical mapping, sans MMU
+               if (!writePhysical(value, virtAddr, valueSize))
+                       return NonMMUError;
+       } else {
+               auto translated = translateAddressUsingTlb(virtAddr);
+               if (holds_alternative<MMUFault>(translated))
+                       return get<MMUFault>(translated);
+
+               // resolve this boy
+               auto tlbEntry = get<TlbEntry *>(translated);
+
+               if (auto f = checkAccessPermissions(tlbEntry, virtAddr, true); f != NoFault)
+                       return f;
+
+               uint32_t physAddr = physAddrFromTlbEntry(tlbEntry, virtAddr);
+               int domain = (tlbEntry->lv1Entry >> 5) & 0xF;
+               bool isPage = (tlbEntry->lv2Entry != 0);
+
+               if (!writePhysical(value, physAddr, valueSize))
+                       return encodeFaultSorP(SorPOtherBusError, isPage, domain, virtAddr);
+       }
+
+       // commit to cache if all was good
+       writeCached(value, virtAddr, valueSize);
+       return NoFault;
+}
+
+
+
+// TLB
+void ARM710a::flushTlb() {
+       for (TlbEntry &e : tlb)
+               e = {0, 0, 0, 0};
+}
+void ARM710a::flushTlb(uint32_t virtAddr) {
+       for (TlbEntry &e : tlb) {
+               if (e.addrMask && (virtAddr & e.addrMask) == e.addr) {
+                       e = {0, 0, 0, 0};
+                       break;
+               }
+       }
+}
+
+ARM710a::TlbEntry *ARM710a::_allocateTlbEntry(uint32_t addrMask, uint32_t addr) {
+       TlbEntry *entry = &tlb[nextTlbIndex];
+       entry->addrMask = addrMask;
+       entry->addr = addr & addrMask;
+       nextTlbIndex = (nextTlbIndex + 1) % TlbSize;
+       return entry;
+}
+
+variant<ARM710a::TlbEntry *, ARM710a::MMUFault> ARM710a::translateAddressUsingTlb(uint32_t virtAddr, TlbEntry *useMe) {
+       // first things first, do we have a matching entry in the TLB?
+       for (TlbEntry &e : tlb) {
+               if (e.addrMask && (virtAddr & e.addrMask) == e.addr)
+                       return &e;
+       }
+
+       // no, so do a page table walk
+       TlbEntry *entry;
+       uint32_t tableIndex = virtAddr >> 20;
+
+       // fetch the Level 1 entry
+       auto lv1EntryOpt = readPhysical(cp15_translationTableBase | (tableIndex << 2), V32);
+       if (!lv1EntryOpt.has_value())
+               return Lv1TranslationError;
+       auto lv1Entry = lv1EntryOpt.value();
+       int domain = (lv1Entry >> 5) & 0xF;
+
+       switch (lv1Entry & 3) {
+       case 0:
+       case 3:
+               // invalid!
+               return encodeFault(SectionTranslationFault, domain, virtAddr);
+       case 2:
+               // a Section entry is straightforward
+               // we just throw that immediately into the TLB
+               entry = useMe ? useMe : _allocateTlbEntry(0xFFF00000, virtAddr);
+               entry->lv1Entry = lv1Entry;
+               entry->lv2Entry = 0;
+               return entry;
+       case 1:
+               // a Page requires a Level 2 read
+               uint32_t pageTableAddr = lv1Entry & 0xFFFFFC00;
+               uint32_t lv2TableIndex = (virtAddr >> 12) & 0xFF;
+
+               auto lv2EntryOpt = readPhysical(pageTableAddr | (lv2TableIndex << 2), V32);
+               if (!lv2EntryOpt.has_value())
+                       return encodeFault(Lv2TranslationError, domain, virtAddr);
+               auto lv2Entry = lv2EntryOpt.value();
+
+               switch (lv2Entry & 3) {
+               case 0:
+               case 3:
+                       // invalid!
+                       return encodeFault(PageTranslationFault, domain, virtAddr);
+               case 1:
+                       // Large 64kb page
+                       entry = useMe ? useMe : _allocateTlbEntry(0xFFFF0000, virtAddr);
+                       entry->lv1Entry = lv1Entry;
+                       entry->lv2Entry = lv2Entry;
+                       return entry;
+               case 2:
+                       // Small 4kb page
+                       entry = useMe ? useMe : _allocateTlbEntry(0xFFFFF000, virtAddr);
+                       entry->lv1Entry = lv1Entry;
+                       entry->lv2Entry = lv2Entry;
+                       return entry;
+               }
+       }
+
+       // we should never get here as the switch covers 0, 1, 2, 3
+       // but this satisfies a compiler warning
+       return SectionTranslationFault;
+}
+
+
+
+ARM710a::MMUFault ARM710a::checkAccessPermissions(ARM710a::TlbEntry *entry, uint32_t virtAddr, bool isWrite) const {
+       int domain;
+       int accessPerms;
+       bool isPage;
+
+       // extract info from the entries
+       domain = (entry->lv1Entry >> 5) & 0xF;
+       if (entry->lv2Entry) {
+               // Page
+               accessPerms = (entry->lv2Entry >> 4) & 0xFF;
+
+               int permIndex;
+               if ((entry->lv2Entry & 3) == 1) // Large 64kb
+                       permIndex = (virtAddr >> 14) & 3;
+               else                            // Small 4kb
+                       permIndex = (virtAddr >> 10) & 3;
+
+               accessPerms >>= (permIndex * 2);
+               accessPerms &= 3;
+               isPage = true;
+       } else {
+               // Section
+               accessPerms = (entry->lv1Entry >> 10) & 3;
+               isPage = false;
+       }
+
+       // now, do our checks
+       int primaryAccessControls = (cp15_domainAccessControl >> (domain * 2)) & 3;
+
+       // Manager: always allowed
+       if (primaryAccessControls == 3)
+               return NoFault;
+
+       // Client: enforce checks!
+       if (primaryAccessControls == 1) {
+#define OK_IF_TRUE(b) return ((b) ? NoFault : encodeFaultSorP(SorPPermissionFault, isPage, domain, virtAddr))
+               bool System = cp15_control & 0x100;
+               bool ROM = cp15_control & 0x200;
+
+               if (accessPerms == 0) {
+                       if (!System && !ROM) {
+                               // 00/0/0: Any access generates a permission fault
+                               OK_IF_TRUE(false);
+                       } else if (System && !ROM) {
+                               // 00/1/0: Supervisor read only permitted
+                               OK_IF_TRUE(!isWrite && isPrivileged());
+                       } else if (!System && ROM) {
+                               // 00/0/1: Any write generates a permission fault
+                               OK_IF_TRUE(!isWrite);
+                       } else /*if (System && ROM)*/ {
+                               // Reserved
+                               OK_IF_TRUE(false);
+                       }
+               } else if (accessPerms == 1) {
+                       // 01/x/x: Access allowed only in Supervisor mode
+                       OK_IF_TRUE(isPrivileged());
+               } else if (accessPerms == 2) {
+                       // 10/x/x: Writes in User mode cause permission fault
+                       OK_IF_TRUE(!isWrite || isPrivileged());
+               } else /*if (accessPerms == 3)*/ {
+                       // 11/x/x: All access types permitted in both modes
+                       OK_IF_TRUE(true);
+               }
+#undef OK_IF_TRUE
+       }
+
+       // No Access or Reserved: never allowed (Domain Fault)
+       return encodeFaultSorP(SorPDomainFault, isPage, domain, virtAddr);
+}
+
+
+void ARM710a::reportFault(MMUFault fault) {
+       if (fault != NoFault) {
+               if ((fault & 0xF) != NonMMUError) {
+                       cp15_faultStatus = fault & 0xFFFF;
+                       cp15_faultAddress = fault >> 32;
+               }
+
+               // this signals a branch to DataAbort after the
+               // instruction is done executing
+               faultTriggeredThisCycle = true;
+       }
+}
diff --git a/WindCore/arm710a.h b/WindCore/arm710a.h
new file mode 100644 (file)
index 0000000..f4116d6
--- /dev/null
@@ -0,0 +1,261 @@
+#pragma once
+#include <stdint.h>
+#include <optional>
+#include <variant>
+
+using namespace std;
+
+// ASSUMPTIONS:
+// - Little-endian will be used
+// - 26-bit address spaces will not be used
+// - Alignment faults will always be on
+
+// Write buffer is 4 address FIFO, 8 data FIFO
+// TLB is 64 entries
+
+typedef optional<uint32_t> MaybeU32;
+
+class ARM710a
+{
+public:
+       enum ValueSize { V8 = 0, V32 = 1 };
+
+       enum MMUFault : uint64_t {
+               // ref: datasheet 9-13 (p111)
+               NoFault                 = 0,
+               AlignmentFault          = 1,
+               // the ARM gods say there is to be no fault 2 or 3
+               SectionLinefetchError   = 4,
+               SectionTranslationFault = 5,
+               PageLinefetchError      = 6,
+               PageTranslationFault    = 7,
+               SectionOtherBusError    = 8,
+               SectionDomainFault      = 9,
+               PageOtherBusError       = 0xA,
+               PageDomainFault         = 0xB,
+               Lv1TranslationError     = 0xC,
+               SectionPermissionFault  = 0xD,
+               Lv2TranslationError     = 0xE,
+               PagePermissionFault     = 0xF,
+
+               // not actually in the ARM datasheet
+               // so we are reusing it for nefarious purposes
+               NonMMUError             = 3,
+
+               MMUFaultDomainMask      = 0xF0,
+               MMUFaultAddressMask     = 0xFFFFFFFF00000000
+       };
+
+
+
+       ARM710a() {
+               cp15_id = 0x41047100;
+               clearAllValues();
+       }
+       virtual ~ARM710a() { }
+
+       void clearAllValues() {
+               bank = MainBank;
+               CPSR = 0;
+               for (int i = 0; i < 16; i++) GPRs[i] = 0;
+               for (int i = 0; i < 5; i++) {
+                       fiqBankedRegisters[0][i] = 0;
+                       fiqBankedRegisters[1][i] = 0;
+                       SPSRs[i] = 0;
+               }
+               for (int i = 0; i < 6; i++) {
+                       allModesBankedRegisters[i][0] = 0;
+                       allModesBankedRegisters[i][1] = 0;
+               }
+
+               cp15_control = 0;
+               cp15_translationTableBase = 0;
+               cp15_domainAccessControl = 0;
+               cp15_faultStatus = 0;
+               cp15_faultAddress = 0;
+               prefetchCount = 0;
+               clearCache();
+               flushTlb();
+       }
+
+       void setProcessorID(uint32_t v) { cp15_id = v; }
+       void requestFIQ(); // pull nFIQ low
+       void requestIRQ(); // pull nIRQ low
+       void reset();      // pull nRESET low
+
+       uint32_t tick();   // run the chip for at least 1 clock cycle
+
+       MaybeU32 readVirtualDebug(uint32_t virtAddr, ValueSize valueSize);
+       MaybeU32 virtToPhys(uint32_t virtAddr);
+
+       pair<MaybeU32, MMUFault> readVirtual(uint32_t virtAddr, ValueSize valueSize);
+       virtual MaybeU32 readPhysical(uint32_t physAddr, ValueSize valueSize) = 0;
+       MMUFault writeVirtual(uint32_t value, uint32_t virtAddr, ARM710a::ValueSize valueSize);
+       virtual bool writePhysical(uint32_t value, uint32_t physAddr, ARM710a::ValueSize valueSize) = 0;
+
+       uint32_t getGPR(int index) const { return GPRs[index]; }
+
+private:
+       enum { Nop = 0xE1A00000 };
+
+       enum Mode : uint8_t {
+               User32       = 0x10,
+               FIQ32        = 0x11,
+               IRQ32        = 0x12,
+               Supervisor32 = 0x13,
+               Abort32      = 0x17,
+               Undefined32  = 0x1B
+       };
+
+       enum BankIndex : uint8_t {
+               FiqBank,
+               IrqBank,
+               SvcBank,
+               AbtBank,
+               UndBank,
+               MainBank
+       };
+
+       constexpr static const BankIndex modeToBank[16] = {
+               MainBank, FiqBank,  IrqBank,  SvcBank,
+               MainBank, MainBank, MainBank, AbtBank,
+               MainBank, MainBank, MainBank, UndBank,
+               MainBank, MainBank, MainBank, MainBank
+       };
+
+       enum : uint32_t {
+               CPSR_ModeMask   = 0x0000001F,
+               CPSR_FIQDisable = 0x00000040,
+               CPSR_IRQDisable = 0x00000080,
+               CPSR_V          = 0x10000000,
+               CPSR_C          = 0x20000000,
+               CPSR_Z          = 0x40000000,
+               CPSR_N          = 0x80000000,
+               CPSR_FlagMask   = 0xF0000000
+       };
+
+       // active state
+       BankIndex bank;
+       uint32_t CPSR;
+       uint32_t GPRs[16];
+
+       // saved state
+       uint32_t fiqBankedRegisters[2][5];      // R8..R12 inclusive
+       uint32_t allModesBankedRegisters[6][2]; // R13, R14
+       uint32_t SPSRs[5];
+
+       // coprocessor 15
+       uint32_t cp15_id;                   // 0: read-only
+       uint32_t cp15_control;              // 1: write-only
+       uint32_t cp15_translationTableBase; // 2: write-only
+       uint32_t cp15_domainAccessControl;  // 3: write-only
+       uint8_t  cp15_faultStatus;          // 5: read-only (writing has unrelated effects)
+       uint32_t cp15_faultAddress;         // 6: read-only (writing has unrelated effects)
+
+       bool flagV() const { return CPSR & CPSR_V; }
+       bool flagC() const { return CPSR & CPSR_C; }
+       bool flagZ() const { return CPSR & CPSR_Z; }
+       bool flagN() const { return CPSR & CPSR_N; }
+       bool checkCondition(int cond) const {
+               switch (cond) {
+               case 0:   return flagZ();
+               case 1:   return !flagZ();
+               case 2:   return flagC();
+               case 3:   return !flagC();
+               case 4:   return flagN();
+               case 5:   return !flagN();
+               case 6:   return flagV();
+               case 7:   return !flagV();
+               case 8:   return flagC() && !flagZ();
+               case 9:   return !flagC() || flagZ();
+               case 0xA: return flagN() == flagV();
+               case 0xB: return flagN() != flagV();
+               case 0xC: return !flagZ() && (flagN() == flagV());
+               case 0xD: return flagZ() || (flagN() != flagV());
+               case 0xE: return true;
+               /*case 0xF:*/
+               default:  return false;
+               }
+       }
+
+       static Mode modeFromCPSR(uint32_t v) { return (Mode)(v & CPSR_ModeMask); }
+       Mode currentMode()             const { return modeFromCPSR(CPSR); }
+       BankIndex currentBank()        const { return modeToBank[(Mode)(CPSR & 0xF)]; }
+       bool isPrivileged()            const { return (CPSR & 0x1F) > User32; }
+       bool isMMUEnabled()            const { return (cp15_control & 1); }
+       bool isAlignmentFaultEnabled() const { return (cp15_control & 2); }
+       bool isCacheEnabled()          const { return (cp15_control & 4); }
+       bool isWriteBufferEnabled()    const { return (cp15_control & 8); }
+
+       void switchMode(Mode mode);
+       void switchBank(BankIndex bank);
+       void raiseException(Mode mode, uint32_t savedPC, uint32_t newPC);
+
+       // MMU/TLB
+       enum MMUFaultSorP : uint64_t {
+               SorPLinefetchError      = 4,
+               SorPTranslationFault    = 5,
+               SorPOtherBusError       = 8,
+               SorPDomainFault         = 9,
+               SorPPermissionFault     = 0xD,
+       };
+
+       MMUFault encodeFault(MMUFault fault, int domain, uint32_t virtAddr) const {
+               return (MMUFault)(fault | (domain << 4) | ((uint64_t)virtAddr << 32));
+       }
+       MMUFault encodeFaultSorP(MMUFaultSorP baseFault, bool isPage, int domain, uint32_t virtAddr) const {
+               return (MMUFault)(baseFault | (isPage ? 2 : 0) | (domain << 4) | ((uint64_t)virtAddr << 32));
+       }
+
+       enum { TlbSize = 64 };
+       struct TlbEntry { uint32_t addrMask, addr, lv1Entry, lv2Entry; };
+       TlbEntry tlb[TlbSize];
+       int nextTlbIndex = 0;
+
+       void flushTlb();
+       void flushTlb(uint32_t virtAddr);
+       variant<TlbEntry *, MMUFault> translateAddressUsingTlb(uint32_t virtAddr, TlbEntry *useMe=nullptr);
+       TlbEntry *_allocateTlbEntry(uint32_t addrMask, uint32_t addr);
+
+       static uint32_t physAddrFromTlbEntry(TlbEntry *tlbEntry, uint32_t virtAddr);
+       MMUFault checkAccessPermissions(TlbEntry *entry, uint32_t virtAddr, bool isWrite) const;
+
+       bool faultTriggeredThisCycle = false;
+       void reportFault(MMUFault fault);
+
+       // Instruction/Data Cache
+       enum {
+               CacheSets = 4,
+               CacheBlocksPerSet = 128,
+               CacheBlockSize = 0x10,
+
+               CacheAddressLineMask = 0x0000000F,
+               CacheAddressSetMask  = 0x00000030, CacheAddressSetShift = 4,
+               CacheAddressTagMask  = 0xFFFFFFC0,
+
+               CacheBlockEnabled = 1
+       };
+       uint32_t cacheBlockTags[CacheSets][CacheBlocksPerSet];
+       uint8_t cacheBlocks[CacheSets][CacheBlocksPerSet][CacheBlockSize];
+
+       void clearCache();
+       uint8_t *findCacheLine(uint32_t virtAddr);
+       pair<MaybeU32, MMUFault> addCacheLineAndRead(uint32_t physAddr, uint32_t virtAddr, ValueSize valueSize, int domain, bool isPage);
+       MaybeU32 readCached(uint32_t virtAddr, ValueSize valueSize);
+       bool writeCached(uint32_t value, uint32_t virtAddr, ValueSize valueSize);
+
+       // Instruction Loop
+       int prefetchCount;
+       uint32_t prefetch[2];
+       MMUFault prefetchFaults[2];
+
+       uint32_t executeInstruction(uint32_t insn);
+
+       uint32_t execDataProcessing(bool I, uint32_t Opcode, bool S, uint32_t Rn, uint32_t Rd, uint32_t Operand2);
+       uint32_t execMultiply(uint32_t AS, uint32_t Rd, uint32_t Rn, uint32_t Rs, uint32_t Rm);
+       uint32_t execSingleDataSwap(bool B, uint32_t Rn, uint32_t Rd, uint32_t Rm);
+       uint32_t execSingleDataTransfer(uint32_t IPUBWL, uint32_t Rn, uint32_t Rd, uint32_t offset);
+       uint32_t execBlockDataTransfer(uint32_t PUSWL, uint32_t Rn, uint32_t registerList);
+       uint32_t execBranch(bool L, uint32_t offset);
+       uint32_t execCP15RegisterTransfer(uint32_t CPOpc, bool L, uint32_t CRn, uint32_t Rd, uint32_t CP, uint32_t CRm);
+};
index d8152242536f23bcc6790ab86265da18a684cd76..4234af9b8d2f8b5e6e851a0b8c4994ddeceb2707 100644 (file)
@@ -2,6 +2,7 @@
 #include "wind.h"
 #include "wind_hw.h"
 #include <time.h>
+#include "common.h"
 
 
 #define INCLUDE_BANK1
@@ -41,19 +42,19 @@ uint32_t Emu::readReg8(uint32_t reg) {
        } else if (reg == PDDDR) {
                return portDirections & 0xFF;
        } else {
-//             printf("RegRead8 unknown:: pc=%08x lr=%08x reg=%03x\n", cpu.gprs[ARM_PC]-4, cpu.gprs[ARM_LR], reg);
+//             printf("RegRead8 unknown:: pc=%08x lr=%08x reg=%03x\n", getGPR(15)-4, getGPR(14), reg);
                return 0xFF;
        }
 }
 uint32_t Emu::readReg32(uint32_t reg) {
        if (reg == LCDCTL) {
-        printf("LCD control read pc=%08x lr=%08x !!!\n", cpu.gprs[ARM_PC], cpu.gprs[ARM_LR]);
+               printf("LCD control read pc=%08x lr=%08x !!!\n", getGPR(15), getGPR(14));
                return lcdControl;
        } else if (reg == LCDST) {
-        printf("LCD state read pc=%08x lr=%08x !!!\n", cpu.gprs[ARM_PC], cpu.gprs[ARM_LR]);
+               printf("LCD state read pc=%08x lr=%08x !!!\n", getGPR(15), getGPR(14));
                return 0xFFFFFFFF;
        } else if (reg == PWRSR) {
-//             printf("!!! PWRSR read pc=%08x lr=%08x !!!\n", cpu.gprs[ARM_PC], cpu.gprs[ARM_LR]);
+//             printf("!!! PWRSR read pc=%08x lr=%08x !!!\n", getGPR(15), getGPR(14));
                return pwrsr;
        } else if (reg == INTSR) {
                return pendingInterrupts & interruptMask;
@@ -85,7 +86,7 @@ uint32_t Emu::readReg32(uint32_t reg) {
     } else if (reg == KSCAN) {
         return kScan;
     } else {
-//             printf("RegRead32 unknown:: pc=%08x lr=%08x reg=%03x\n", cpu.gprs[ARM_PC]-4, cpu.gprs[ARM_LR], reg);
+//             printf("RegRead32 unknown:: pc=%08x lr=%08x reg=%03x\n", getGPR(15)-4, getGPR(14), reg);
                return 0xFFFFFFFF;
        }
 }
@@ -108,13 +109,13 @@ void Emu::writeReg8(uint32_t reg, uint8_t value) {
                uint32_t oldPorts = portValues;
                portValues &= 0xFF00FFFF;
                portValues |= (uint32_t)value << 16;
-        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);
+               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;
@@ -140,7 +141,7 @@ void Emu::writeReg8(uint32_t reg, uint8_t value) {
     } else if (reg == KSCAN) {
         kScan = value;
     } else {
-//             printf("RegWrite8 unknown:: pc=%08x reg=%03x value=%02x\n", cpu.gprs[ARM_PC]-4, reg, value);
+//             printf("RegWrite8 unknown:: pc=%08x reg=%03x value=%02x\n", getGPR(15)-4, reg, value);
        }
 }
 void Emu::writeReg32(uint32_t reg, uint32_t value) {
@@ -163,7 +164,7 @@ void Emu::writeReg32(uint32_t reg, uint32_t value) {
 //             diffInterrupts(interruptMask, interruptMask &~ value);
                interruptMask &= ~value;
        } else if (reg == HALT) {
-               cpu.halted = true;
+               halted = true;
        // BLEOI = 0x410,
        // MCEOI = 0x414,
        } else if (reg == TEOI) {
@@ -184,511 +185,280 @@ void Emu::writeReg32(uint32_t reg, uint32_t value) {
        } else if (reg == TC2EOI) {
                pendingInterrupts &= ~(1 << TC2OI);
        } else {
-//             printf("RegWrite32 unknown:: pc=%08x reg=%03x value=%08x\n", cpu.gprs[ARM_PC]-4, reg, value);
+//             printf("RegWrite32 unknown:: pc=%08x reg=%03x value=%08x\n", getGPR(15)-4, reg, value);
        }
 }
 
 bool Emu::isPhysAddressValid(uint32_t physAddress) const {
-    uint8_t region = (physAddress >> 24) & 0xF1;
-    switch (region) {
-    case 0: return true;
-    case 0x80: return (physAddress <= 0x80000FFF);
-    case 0xC0: return true;
-    case 0xC1: return true;
-    case 0xD0: return true;
-    case 0xD1: return true;
-    default: return false;
-    }
+       uint8_t region = (physAddress >> 24) & 0xF1;
+       switch (region) {
+       case 0: return true;
+       case 0x80: return (physAddress <= 0x80000FFF);
+       case 0xC0: return true;
+       case 0xC1: return true;
+       case 0xD0: return true;
+       case 0xD1: return true;
+       default: return false;
+       }
 }
 
-uint32_t Emu::readPhys8(uint32_t physAddress) {
-    uint32_t result = 0xFF;
-    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)
-        result = MemoryBlockC0[physAddress & MemoryBlockMask];
-#ifdef INCLUDE_BANK1
-    else if (region == 0xC1)
-        result = MemoryBlockC1[physAddress & MemoryBlockMask];
-#endif
-    else if (region == 0xD0)
-        result = MemoryBlockD0[physAddress & MemoryBlockMask];
-#ifdef INCLUDE_BANK1
-    else if (region == 0xD1)
-        result = MemoryBlockD1[physAddress & MemoryBlockMask];
-#endif
-//     else
-//             printf("<%08x> unmapped read8 addr p:%08x\n", cpu.gprs[ARM_PC] - 4, physAddress);
-       return result;
-}
-uint32_t Emu::readPhys16(uint32_t physAddress) {
-       uint32_t result = 0xFFFFFFFF;
-    uint8_t region = (physAddress >> 24) & 0xF1;
-       if (region == 0)
-               LOAD_16LE(result, physAddress & 0xFFFFFF, ROM);
-    else if (region == 0xC0)
-        LOAD_16LE(result, physAddress & MemoryBlockMask, MemoryBlockC0);
+
+MaybeU32 Emu::readPhysical(uint32_t physAddr, ValueSize valueSize) {
+       uint8_t region = (physAddr >> 24) & 0xF1;
+       if (valueSize == V8) {
+               if (region == 0)
+                       return ROM[physAddr & 0xFFFFFF];
+               else if (region == 0x20 && physAddr <= 0x20000FFF)
+                       return etna.readReg8(physAddr & 0xFFF);
+               else if (region == 0x80 && physAddr <= 0x80000FFF)
+                       return readReg8(physAddr & 0xFFF);
+               else if (region == 0xC0)
+                       return MemoryBlockC0[physAddr & MemoryBlockMask];
 #ifdef INCLUDE_BANK1
-    else if (region == 0xC1)
-        LOAD_16LE(result, physAddress & MemoryBlockMask, MemoryBlockC1);
+               else if (region == 0xC1)
+                       return MemoryBlockC1[physAddr & MemoryBlockMask];
 #endif
-    else if (region == 0xD0)
-        LOAD_16LE(result, physAddress & MemoryBlockMask, MemoryBlockD0);
+               else if (region == 0xD0)
+                       return MemoryBlockD0[physAddr & MemoryBlockMask];
 #ifdef INCLUDE_BANK1
-    else if (region == 0xD1)
-        LOAD_16LE(result, physAddress & MemoryBlockMask, MemoryBlockD1);
+               else if (region == 0xD1)
+                       return MemoryBlockD1[physAddr & MemoryBlockMask];
 #endif
-//     else
-//             printf("<%08x> unmapped read16 addr p:%08x\n", cpu.gprs[ARM_PC] - 4, physAddress);
-       return result;
-}
-uint32_t Emu::readPhys32(uint32_t physAddress) {
-       uint32_t result = 0xFFFFFFFF;
-    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)
-        LOAD_32LE(result, physAddress & MemoryBlockMask, MemoryBlockC0);
+       } else {
+               uint32_t result;
+               if (region == 0)
+                       LOAD_32LE(result, physAddr & 0xFFFFFF, ROM);
+               else if (region == 0x20 && physAddr <= 0x20000FFF)
+                       result = etna.readReg32(physAddr & 0xFFF);
+               else if (region == 0x80 && physAddr <= 0x80000FFF)
+                       result = readReg32(physAddr & 0xFFF);
+               else if (region == 0xC0)
+                       LOAD_32LE(result, physAddr & MemoryBlockMask, MemoryBlockC0);
 #ifdef INCLUDE_BANK1
-    else if (region == 0xC1)
-        LOAD_32LE(result, physAddress & MemoryBlockMask, MemoryBlockC1);
+               else if (region == 0xC1)
+                       LOAD_32LE(result, physAddr & MemoryBlockMask, MemoryBlockC1);
 #endif
-    else if (region == 0xD0)
-        LOAD_32LE(result, physAddress & MemoryBlockMask, MemoryBlockD0);
+               else if (region == 0xD0)
+                       LOAD_32LE(result, physAddr & MemoryBlockMask, MemoryBlockD0);
 #ifdef INCLUDE_BANK1
-    else if (region == 0xD1)
-        LOAD_32LE(result, physAddress & MemoryBlockMask, MemoryBlockD1);
+               else if (region == 0xD1)
+                       LOAD_32LE(result, physAddr & MemoryBlockMask, MemoryBlockD1);
 #endif
-//     else
-//             printf("<%08x> unmapped read32 addr p:%08x\n", cpu.gprs[ARM_PC] - 4, physAddress);
-       return result;
-}
+               else
+                       return {};
+               return result;
+       }
 
-void Emu::writePhys8(uint32_t physAddress, uint8_t value) {
-    uint8_t region = (physAddress >> 24) & 0xF1;
-    if (region == 0xC0)
-        MemoryBlockC0[physAddress & MemoryBlockMask] = (uint8_t)value;
-#ifdef INCLUDE_BANK1
-    else if (region == 0xC1)
-        MemoryBlockC1[physAddress & MemoryBlockMask] = (uint8_t)value;
-#endif
-    else if (region == 0xD0)
-        MemoryBlockD0[physAddress & MemoryBlockMask] = (uint8_t)value;
-#ifdef INCLUDE_BANK1
-    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);
-//     else
-//             printf("<%08x> unmapped write8 addr p:%08x :: %02x\n", cpu.gprs[ARM_PC] - 4, physAddress, value);
+       return {};
 }
-void Emu::writePhys16(uint32_t physAddress, uint16_t value) {
-    uint8_t region = (physAddress >> 24) & 0xF1;
-    if (region == 0xC0)
-        STORE_16LE(value, physAddress & MemoryBlockMask, MemoryBlockC0);
+
+bool Emu::writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) {
+       uint8_t region = (physAddr >> 24) & 0xF1;
+       if (valueSize == V8) {
+               if (region == 0xC0)
+                       MemoryBlockC0[physAddr & MemoryBlockMask] = (uint8_t)value;
 #ifdef INCLUDE_BANK1
-    else if (region == 0xC1)
-        STORE_16LE(value, physAddress & MemoryBlockMask, MemoryBlockC1);
+               else if (region == 0xC1)
+                       MemoryBlockC1[physAddr & MemoryBlockMask] = (uint8_t)value;
 #endif
-    else if (region == 0xD0)
-        STORE_16LE(value, physAddress & MemoryBlockMask, MemoryBlockD0);
+               else if (region == 0xD0)
+                       MemoryBlockD0[physAddr & MemoryBlockMask] = (uint8_t)value;
 #ifdef INCLUDE_BANK1
-    else if (region == 0xD1)
-        STORE_16LE(value, physAddress & MemoryBlockMask, MemoryBlockD1);
+               else if (region == 0xD1)
+                       MemoryBlockD1[physAddr & MemoryBlockMask] = (uint8_t)value;
 #endif
-//     else
-//             printf("<%08x> unmapped write16 addr p:%08x :: %04x\n", cpu.gprs[ARM_PC] - 4, physAddress, value);
-}
-void Emu::writePhys32(uint32_t physAddress, uint32_t value) {
-    uint8_t region = (physAddress >> 24) & 0xF1;
-    if (region == 0xC0)
-        STORE_32LE(value, physAddress & MemoryBlockMask, MemoryBlockC0);
+               else if (region == 0x20 && physAddr <= 0x20000FFF)
+                       etna.writeReg8(physAddr & 0xFFF, value);
+               else if (region == 0x80 && physAddr <= 0x80000FFF)
+                       writeReg8(physAddr & 0xFFF, value);
+               else
+                       return false;
+       } else {
+               uint8_t region = (physAddr >> 24) & 0xF1;
+               if (region == 0xC0)
+                       STORE_32LE(value, physAddr & MemoryBlockMask, MemoryBlockC0);
 #ifdef INCLUDE_BANK1
-    else if (region == 0xC1)
-        STORE_32LE(value, physAddress & MemoryBlockMask, MemoryBlockC1);
+               else if (region == 0xC1)
+                       STORE_32LE(value, physAddr & MemoryBlockMask, MemoryBlockC1);
 #endif
-    else if (region == 0xD0)
-        STORE_32LE(value, physAddress & MemoryBlockMask, MemoryBlockD0);
+               else if (region == 0xD0)
+                       STORE_32LE(value, physAddr & MemoryBlockMask, MemoryBlockD0);
 #ifdef INCLUDE_BANK1
-    else if (region == 0xD1)
-        STORE_32LE(value, physAddress & MemoryBlockMask, MemoryBlockD1);
+               else if (region == 0xD1)
+                       STORE_32LE(value, physAddr & 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
-//             printf("<%08x> unmapped write32 addr p:%08x :: %08x\n", cpu.gprs[ARM_PC] - 4, physAddress, value);
+               else if (region == 0x20 && physAddr <= 0x20000FFF)
+                       etna.writeReg32(physAddr & 0xFFF, value);
+               else if (region == 0x80 && physAddr <= 0x80000FFF)
+                       writeReg32(physAddr & 0xFFF, value);
+               else
+                       return false;
+       }
+       return true;
 }
 
-uint32_t Emu::virtToPhys(uint32_t virtAddress) {
-       if (!isMMU())
-               return virtAddress;
-
-       // find the TTB
-       uint32_t ttbEntryAddr = translationTableBase & 0xFFFFC000;
-       ttbEntryAddr |= ((virtAddress & 0xFFF00000) >> 18);
-       uint32_t ttbEntry = readPhys32(ttbEntryAddr);
-
-       if ((ttbEntry & 3) == 1) {
-               // Page
-               uint32_t pageTableAddr = ttbEntry & 0xFFFFFC00;
-               pageTableAddr |= ((virtAddress & 0x000FF000) >> 10);
-               uint32_t pageTableEntry = readPhys32(pageTableAddr);
-               if ((pageTableEntry & 3) == 1) {
-                       // Large Page
-                       uint32_t lpBaseAddr = pageTableEntry & 0xFFFF0000;
-                       return lpBaseAddr | (virtAddress & 0x0000FFFF);
-               } else if ((pageTableEntry & 3) == 2) {
-                       // Small Page
-                       uint32_t lpBaseAddr = pageTableEntry & 0xFFFFF000;
-                       return lpBaseAddr | (virtAddress & 0x00000FFF);
-               } else {
-                       // Fault/Reserved
-            // TODO: should raise Abort here?
-                       printf("!!! lv2 bad entry=%d vaddr=%08x !!!\n", pageTableEntry & 3, virtAddress);
-                       return 0xFFFFFFFF;
-               }
-       } else if ((ttbEntry & 3) == 2) {
-               // Section
-               uint32_t sectBaseAddr = ttbEntry & 0xFFF00000;
-        return sectBaseAddr | (virtAddress & 0x000FFFFF);
-    } else {
-        // Fault/Reserved
-        // TODO: should raise Abort here?
-        printf("!!! lv1 bad entry=%d vaddr=%08x !!!\n", ttbEntry & 3, virtAddress);
-        return 0xFFFFFFFF;
-    }
-}
 
 
 void Emu::configure() {
-    if (configured) return;
-    configured = true;
-
-    uart1.cpu = &cpu;
-    uart2.cpu = &cpu;
-    memset(&tc1, 0, sizeof(tc1));
-    memset(&tc2, 0, sizeof(tc1));
-    cpu.owner = this;
+       if (configured) return;
+       configured = true;
 
-    nextTickAt = TICK_INTERVAL;
-    rtc = getRTC();
+       uart1.cpu = this;
+       uart2.cpu = this;
+       memset(&tc1, 0, sizeof(tc1));
+       memset(&tc2, 0, sizeof(tc1));
 
-    configureMemoryBindings();
-    configureCpuHandlers();
+       nextTickAt = TICK_INTERVAL;
+       rtc = getRTC();
 
-    ARMReset(&cpu);
-}
-
-void Emu::configureMemoryBindings() {
-    cpu.memory.load8 = [](struct ARMCore *cpu, uint32_t address, int *) {
-        return ((Emu *)cpu->owner)->readVirt8(address);
-    };
-    cpu.memory.load16 = [](struct ARMCore *cpu, uint32_t address, int *) {
-        return ((Emu *)cpu->owner)->readVirt16(address);
-    };
-    cpu.memory.load32 = [](struct ARMCore *cpu, uint32_t address, int *) {
-        return ((Emu *)cpu->owner)->readVirt32(address);
-    };
-    cpu.memory.loadMultiple = [](struct ARMCore *cpu, uint32_t address, int mask, enum LSMDirection direction, int *cycleCounter) {
-        uint32_t value;
-        int i, offset = 4, popcount = 0;
-
-        if (direction & LSM_D) {
-            offset = -4;
-            popcount = popcount32(mask);
-            address -= (popcount << 2) - 4;
-        }
-        if (direction & LSM_B)
-            address += offset;
-
-        if (!mask) {
-            value = cpu->memory.load32(cpu, address, cycleCounter);
-            cpu->gprs[ARM_PC] = value;
-            address += 64;
-        }
-        for (i = 0; i < 16; i++) {
-            if (mask & (1 << i)) {
-                value = cpu->memory.load32(cpu, address, cycleCounter);
-                cpu->gprs[i] = value;
-                address += 4;
-            }
-        }
-
-        if (direction & LSM_B)
-            address -= offset;
-        if (direction & LSM_D)
-            address -= (popcount << 2) + 4;
-
-        return address;
-    };
-
-    cpu.memory.store8 = [](struct ARMCore *cpu, uint32_t address, int8_t value, int *) {
-        ((Emu *)cpu->owner)->writeVirt8(address, value);
-    };
-    cpu.memory.store16 = [](struct ARMCore *cpu, uint32_t address, int16_t value, int *) {
-        ((Emu *)cpu->owner)->writeVirt16(address, value);
-    };
-    cpu.memory.store32 = [](struct ARMCore *cpu, uint32_t address, int32_t value, int *) {
-        ((Emu *)cpu->owner)->writeVirt32(address, value);
-    };
-    cpu.memory.storeMultiple = [](struct ARMCore *cpu, uint32_t address, int mask, enum LSMDirection direction, int *cycleCounter) {
-        uint32_t value;
-        int i, offset = 4, popcount = 0;
-
-        if (direction & LSM_D) {
-            offset = -4;
-            popcount = popcount32(mask);
-            address -= (popcount << 2) - 4;
-        }
-        if (direction & LSM_B)
-            address += offset;
-
-        if (!mask) {
-            value = cpu->gprs[ARM_PC] + 4;
-            cpu->memory.store32(cpu, address, value, cycleCounter);
-            address += 64;
-        }
-        for (i = 0; i < 16; i++) {
-            if (mask & (1 << i)) {
-                value = cpu->gprs[i];
-                if (i == ARM_PC) value += 4;
-                cpu->memory.store32(cpu, address, value, cycleCounter);
-                address += 4;
-            }
-        }
-
-        if (direction & LSM_B)
-            address -= offset;
-        if (direction & LSM_D)
-            address -= (popcount << 2) + 4;
-
-        return address;
-    };
-
-    cpu.memory.activeSeqCycles32 = 0;
-    cpu.memory.activeNonseqCycles32 = 0;
-    cpu.memory.stall = [](struct ARMCore *cpu, int32_t wait) {
-        return 0;
-    };
-}
-
-void Emu::configureCpuHandlers() {
-    cpu.irqh.reset = [](struct ARMCore *cpu) {
-        printf("reset...\n");
-    };
-    cpu.irqh.processEvents = [](struct ARMCore *cpu) {
-        // printf("processEvents...\n");
-    };
-    cpu.irqh.swi32 = [](struct ARMCore *cpu, int immediate) {
-        ARMRaiseSWI(cpu);
-    };
-    cpu.irqh.hitIllegal = [](struct ARMCore *cpu, uint32_t opcode) {
-        printf("hitIllegal... %08x\n", opcode);
-    };
-    cpu.irqh.bkpt32 = [](struct ARMCore *cpu, int immediate) {
-        printf("bkpt32... %08x\n", immediate);
-    };
-    cpu.irqh.readCPSR = [](struct ARMCore *cpu) {
-        // printf("readCPSR...\n");
-        // printf("at %08x our priv became %s\n", cpu->gprs[ARM_PC]-4, privname(cpu));
-    };
-    cpu.irqh.hitStub = [](struct ARMCore *cpu, uint32_t opcode) {
-        Emu *emu = (Emu *)cpu->owner;
-        if ((opcode & 0x0F100F10) == 0x0E100F10) {
-            // coprocessor read
-            int cpReg = (opcode & 0x000F0000) >> 16;
-            int armReg = (opcode & 0x0000F000) >> 12;
-            if (cpReg == 0)
-                cpu->gprs[armReg] = 0x41807100; //5mx device id
-        } else if ((opcode & 0x0F100F10) == 0x0E000F10) {
-            // coprocessor write
-            int cpReg = (opcode & 0x000F0000) >> 16;
-            int armReg = (opcode & 0x0000F000) >> 12;
-            if (cpReg == 1) {
-                emu->controlReg = cpu->gprs[armReg];
-                printf("mmu is now %s\n", emu->isMMU() ? "on" : "off");
-            } else if (cpReg == 2) {
-                emu->translationTableBase = cpu->gprs[armReg];
-            } else if (cpReg == 3) {
-                emu->domainAccessControl = cpu->gprs[armReg];
-            }
-        } else {
-            printf("hitStub... %08x\n", opcode);
-        }
-    };
+       reset();
 }
 
 void Emu::loadROM(const char *path) {
-    FILE *f = fopen(path, "rb");
-    fread(ROM, 1, sizeof(ROM), f);
-    fclose(f);
+       FILE *f = fopen(path, "rb");
+       fread(ROM, 1, sizeof(ROM), f);
+       fclose(f);
 }
 
 void Emu::executeUntil(int64_t cycles) {
-    if (!configured)
-        configure();
-
-    while (!asleep && cpu.cycles < cycles) {
-        if (cpu.cycles >= nextTickAt) {
-            // increment RTCDIV
-            if ((pwrsr & 0x3F) == 0x3F) {
-                rtc++;
-                pwrsr &= ~0x3F;
-            } else {
-                pwrsr++;
-            }
-
-            nextTickAt += TICK_INTERVAL;
-            pendingInterrupts |= (1<<TINT);
-        }
-        if (tc1.tick(cpu.cycles))
-            pendingInterrupts |= (1<<TC1OI);
-        if (tc2.tick(cpu.cycles))
-            pendingInterrupts |= (1<<TC2OI);
-
-        if ((pendingInterrupts & interruptMask & FIQ_INTERRUPTS) != 0)
-            ARMRaiseFIQ(&cpu);
-        if ((pendingInterrupts & interruptMask & IRQ_INTERRUPTS) != 0)
-            ARMRaiseIRQ(&cpu);
-
-//        if (cpu.cycles >= 30000000) {
-//            static bool lcdtest = false;
-//            if (!lcdtest) {
-//                printf("lcdtest\n");
-//                pendingInterrupts |= (1<<LCDINT);
-//                lcdtest = true;
-//            }
-//        }
-
-        // what's running?
-        if (cpu.halted) {
-            // keep the clock moving
-            cpu.cycles++;
-        } else {
-            uint32_t pc = cpu.gprs[ARM_PC] - 4;
-            uint32_t phys_pc = virtToPhys(pc);
-            debugPC(phys_pc);
-            ARMRun(&cpu);
-
-            uint32_t new_pc = cpu.gprs[ARM_PC] - 4;
-            if (_breakpoints.find(new_pc) != _breakpoints.end())
-                return;
-        }
-    }
+       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)
+                       requestFIQ();
+               if ((pendingInterrupts & interruptMask & IRQ_INTERRUPTS) != 0)
+                       requestIRQ();
+
+               // what's running?
+               if (halted) {
+                       // keep the clock moving
+                       passedCycles++;
+               } else {
+                       passedCycles += tick();
+
+                       uint32_t new_pc = getGPR(15) - 0xC;
+                       if (_breakpoints.find(new_pc) != _breakpoints.end())
+                               return;
+               }
+       }
 }
 
 void Emu::dumpRAM(const char *path) {
-    FILE *f = fopen(path, "wb");
-    fwrite(MemoryBlockC0, 1, sizeof(MemoryBlockC0), f);
-    fwrite(MemoryBlockC1, 1, sizeof(MemoryBlockC1), f);
-    fwrite(MemoryBlockD0, 1, sizeof(MemoryBlockD0), f);
-    fwrite(MemoryBlockD1, 1, sizeof(MemoryBlockD1), f);
-    fclose(f);
+       FILE *f = fopen(path, "wb");
+       fwrite(MemoryBlockC0, 1, sizeof(MemoryBlockC0), f);
+       fwrite(MemoryBlockC1, 1, sizeof(MemoryBlockC1), f);
+       fwrite(MemoryBlockD0, 1, sizeof(MemoryBlockD0), f);
+       fwrite(MemoryBlockD1, 1, sizeof(MemoryBlockD1), f);
+       fclose(f);
 }
 
 
 
 void Emu::printRegs() {
-    printf("R00:%08x R01:%08x R02:%08x R03:%08x\n", cpu.gprs[0], cpu.gprs[1], cpu.gprs[2], cpu.gprs[3]);
-    printf("R04:%08x R05:%08x R06:%08x R07:%08x\n", cpu.gprs[4], cpu.gprs[5], cpu.gprs[6], cpu.gprs[7]);
-    printf("R08:%08x R09:%08x R10:%08x R11:%08x\n", cpu.gprs[8], cpu.gprs[9], cpu.gprs[10], cpu.gprs[11]);
-    printf("R12:%08x R13:%08x R14:%08x R15:%08x\n", cpu.gprs[12], cpu.gprs[13], cpu.gprs[14], cpu.gprs[15]);
-    printf("cpsr=%08x spsr=%08x\n", cpu.cpsr.packed, cpu.spsr.packed);
+       printf("R00:%08x R01:%08x R02:%08x R03:%08x\n", getGPR(0), getGPR(1), getGPR(2), getGPR(3));
+       printf("R04:%08x R05:%08x R06:%08x R07:%08x\n", getGPR(4), getGPR(5), getGPR(6), getGPR(7));
+       printf("R08:%08x R09:%08x R10:%08x R11:%08x\n", getGPR(8), getGPR(9), getGPR(10), getGPR(11));
+       printf("R12:%08x R13:%08x R14:%08x R15:%08x\n", getGPR(12), getGPR(13), getGPR(14), getGPR(15));
+//    printf("cpsr=%08x spsr=%08x\n", cpu.cpsr.packed, cpu.spsr.packed);
 }
 
 const char *Emu::identifyObjectCon(uint32_t ptr) {
-    if (ptr == readVirt32(0x80000980)) return "process";
-    if (ptr == readVirt32(0x80000984)) return "thread";
-    if (ptr == readVirt32(0x80000988)) return "chunk";
-//     if (ptr == readVirt32(0x8000098C)) return "semaphore";
-//     if (ptr == readVirt32(0x80000990)) return "mutex";
-    if (ptr == readVirt32(0x80000994)) return "logicaldevice";
-    if (ptr == readVirt32(0x80000998)) return "physicaldevice";
-    if (ptr == readVirt32(0x8000099C)) return "channel";
-    if (ptr == readVirt32(0x800009A0)) return "server";
-//     if (ptr == readVirt32(0x800009A4)) return "unk9A4"; // name always null
-    if (ptr == readVirt32(0x800009AC)) return "library";
-//     if (ptr == readVirt32(0x800009B0)) return "unk9B0"; // name always null
-//     if (ptr == readVirt32(0x800009B4)) return "unk9B4"; // name always null
-    return NULL;
+       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 Emu::fetchStr(uint32_t str, char *buf) {
-    if (str == 0) {
-        strcpy(buf, "<NULL>");
-        return;
-    }
-    int size = readVirt32(str);
-    for (int i = 0; i < size; i++) {
-        buf[i] = readVirt8(str + 4 + i);
-    }
-    buf[size] = 0;
+       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 Emu::fetchName(uint32_t obj, char *buf) {
-    fetchStr(readVirt32(obj + 0x10), buf);
+       fetchStr(readVirtualDebug(obj + 0x10, V32).value(), buf);
 }
 
 void Emu::fetchProcessFilename(uint32_t obj, char *buf) {
-    fetchStr(readVirt32(obj + 0x3C), buf);
+       fetchStr(readVirtualDebug(obj + 0x3C, V32).value(), buf);
 }
 
 void Emu::debugPC(uint32_t pc) {
-    char objName[1000];
-    if (pc == 0x2CBC4) {
-        // CObjectCon::AddL()
-        uint32_t container = cpu.gprs[0];
-        uint32_t obj = cpu.gprs[1];
-        const char *wut = identifyObjectCon(container);
-        if (wut) {
-            fetchName(obj, objName);
-            printf("OBJS: added %s at %08x <%s>", wut, obj, objName);
-            if (strcmp(wut, "process") == 0) {
-                fetchProcessFilename(obj, objName);
-                printf(" <%s>", objName);
-            }
-            printf("\n");
-        }
-    }
+       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);
+                       printf("OBJS: added %s at %08x <%s>", wut, obj, objName);
+                       if (strcmp(wut, "process") == 0) {
+                               fetchProcessFilename(obj, objName);
+                               printf(" <%s>", objName);
+                       }
+                       printf("\n");
+               }
+       }
 }
 
 
 const uint8_t *Emu::getLCDBuffer() const {
-    if ((lcdAddress >> 24) == 0xC0)
-        return &MemoryBlockC0[lcdAddress & MemoryBlockMask];
-    else
-        return nullptr;
+       if ((lcdAddress >> 24) == 0xC0)
+               return &MemoryBlockC0[lcdAddress & MemoryBlockMask];
+       else
+               return nullptr;
 }
 
 
 uint8_t Emu::readKeyboard() {
-    uint8_t val = 0;
-    if (kScan & 8) {
-        // Select one keyboard
-        int whichColumn = kScan & 7;
-        for (int i = 0; i < 7; i++)
-            if (keyboardKeys[whichColumn * 7 + i])
-                val |= (1 << i);
-    } else if (kScan == 0) {
-        // Report all columns combined
-        // EPOC's keyboard driver relies on this...
-        for (int i = 0; i < 8*7; i++)
-            if (keyboardKeys[i])
-                val |= (1 << (i % 7));
-    }
-    return val;
+       uint8_t val = 0;
+       if (kScan & 8) {
+               // Select one keyboard
+               int whichColumn = kScan & 7;
+               for (int i = 0; i < 7; i++)
+                       if (keyboardKeys[whichColumn * 7 + i])
+                               val |= (1 << i);
+       } else if (kScan == 0) {
+               // Report all columns combined
+               // EPOC's keyboard driver relies on this...
+               for (int i = 0; i < 8*7; i++)
+                       if (keyboardKeys[i])
+                               val |= (1 << (i % 7));
+       }
+       return val;
 }
index 3a97c4c4d8bb2e73637a6b5964255a58b597e879..05bb5e289be2840e3116ad25049a6d3f039fdecc 100644 (file)
@@ -1,10 +1,10 @@
 #pragma once
-#include "arm.h"
+#include "arm710a.h"
 #include "wind_hw.h"
 #include "etna.h"
 #include <unordered_set>
 
-class Emu {
+class Emu : public ARM710a {
 public:
     uint8_t ROM[0x1000000];
     uint8_t MemoryBlockC0[0x800000];
@@ -14,9 +14,6 @@ public:
     enum { MemoryBlockMask = 0x7FFFFF };
 
 private:
-    uint32_t controlReg;
-    uint32_t translationTableBase;
-    uint32_t domainAccessControl;
     uint16_t pendingInterrupts = 0;
     uint16_t interruptMask = 0;
     uint32_t portValues = 0;
@@ -27,19 +24,14 @@ private:
     uint32_t kScan = 0;
     uint32_t rtc = 0;
 
+       int64_t passedCycles = 0;
     int64_t nextTickAt = 0;
     Timer tc1, tc2;
     UART uart1, uart2;
-    Etna etna;
-    bool asleep = false;
+       Etna etna;
+       bool halted = false, asleep = false;
 
-    std::unordered_set<uint32_t> _breakpoints;
-
-    struct ARMCore cpu;
-
-    inline bool isMMU() {
-        return (controlReg & 1);
-    }
+       std::unordered_set<uint32_t> _breakpoints;
 
     uint32_t getRTC();
 
@@ -49,31 +41,15 @@ private:
     void writeReg32(uint32_t reg, uint32_t value);
 
 public:
-    bool isPhysAddressValid(uint32_t physAddress) const;
-
-    uint32_t readPhys8(uint32_t physAddress);
-    uint32_t readPhys16(uint32_t physAddress);
-    uint32_t readPhys32(uint32_t physAddress);
-    void writePhys8(uint32_t physAddress, uint8_t value);
-    void writePhys16(uint32_t physAddress, uint16_t value);
-    void writePhys32(uint32_t physAddress, uint32_t value);
-
-    uint32_t virtToPhys(uint32_t virtAddress);
-
-    uint32_t readVirt8(uint32_t virtAddress) { return readPhys8(virtToPhys(virtAddress)); }
-    uint32_t readVirt16(uint32_t virtAddress) { return readPhys16(virtToPhys(virtAddress)); }
-    uint32_t readVirt32(uint32_t virtAddress) { return readPhys32(virtToPhys(virtAddress)); }
-    void writeVirt8(uint32_t virtAddress, uint8_t value) { writePhys8(virtToPhys(virtAddress), value); }
-    void writeVirt16(uint32_t virtAddress, uint16_t value) { writePhys16(virtToPhys(virtAddress), value); }
-    void writeVirt32(uint32_t virtAddress, uint32_t value) { writePhys32(virtToPhys(virtAddress), value); }
+       bool isPhysAddressValid(uint32_t addr) const;
+       MaybeU32 readPhysical(uint32_t physAddr, ValueSize valueSize) override;
+       bool writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) override;
 
     const uint8_t *getLCDBuffer() const;
 
 private:
     bool configured = false;
     void configure();
-    void configureMemoryBindings();
-    void configureCpuHandlers();
 
     void printRegs();
     const char *identifyObjectCon(uint32_t ptr);
@@ -90,8 +66,7 @@ public:
     Emu();
     void loadROM(const char *path);
     void dumpRAM(const char *path);
-    void executeUntil(int64_t cycles);
-    int64_t currentCycles() const { return cpu.cycles; }
-    uint32_t getGPR(int index) const { return cpu.gprs[index]; }
-    std::unordered_set<uint32_t> &breakpoints() { return _breakpoints; }
+       void executeUntil(int64_t cycles);
+       std::unordered_set<uint32_t> &breakpoints() { return _breakpoints; }
+       uint64_t currentCycles() const { return passedCycles; }
 };
index 6ff4624f8b539c6de02b2201f2024183a5b4fc3f..cf0c95cf1b44b8599f18679b298a32e51072016c 100644 (file)
@@ -1,10 +1,10 @@
 #pragma once
 #include "wind.h"
-#include "arm.h"
+#include "arm710a.h"
 #include <stdio.h>
 
 struct Timer {
-       struct ARMCore *cpu;
+       ARM710a *cpu;
 
        enum {
                TICK_INTERVAL_SLOW = CLOCK_SPEED / 2000,
@@ -50,7 +50,7 @@ struct Timer {
 };
 
 struct UART {
-       struct ARMCore *cpu;
+       ARM710a *cpu;
 
        enum {
                IntRx = 1,
@@ -105,7 +105,7 @@ struct UART {
                // UART0INTM?
                // UART0INTR?
                } else {
-                       printf("unhandled 8bit uart read %x at pc=%08x lr=%08x\n", reg, cpu->gprs[ARM_PC], cpu->gprs[ARM_LR]);
+                       printf("unhandled 8bit uart read %x at pc=%08x lr=%08x\n", reg, cpu->getGPR(15), cpu->getGPR(14));
                        return 0xFF;
                }
        }
@@ -118,7 +118,7 @@ struct UART {
                        // we pretend we are never busy, never have full fifo
                        return FlagReceiveFifoEmpty;
                } else {
-                       printf("unhandled 32bit uart read %x at pc=%08x lr=%08x\n", reg, cpu->gprs[ARM_PC], cpu->gprs[ARM_LR]);
+                       printf("unhandled 32bit uart read %x at pc=%08x lr=%08x\n", reg, cpu->getGPR(15), cpu->getGPR(14));
                        return 0xFFFFFFFF;
                }
        }
@@ -132,7 +132,7 @@ struct UART {
                        printf("uart interruptmask updated: %d\n", value);
                // UART0INTR?
                } else {
-                       printf("unhandled 8bit uart write %x value %02x at pc=%08x lr=%08x\n", reg, value, cpu->gprs[ARM_PC], cpu->gprs[ARM_LR]);
+                       printf("unhandled 8bit uart write %x value %02x at pc=%08x lr=%08x\n", reg, value, cpu->getGPR(15), cpu->getGPR(14));
                }
        }
        void writeReg32(uint32_t reg, uint32_t value) {
@@ -151,7 +151,7 @@ struct UART {
                        printf("uart interrupts %x -> %x\n", interrupts, value);
                        interrupts = value;
                } else {
-                       printf("unhandled 32bit uart write %x value %08x at pc=%08x lr=%08x\n", reg, value, cpu->gprs[ARM_PC], cpu->gprs[ARM_LR]);
+                       printf("unhandled 32bit uart write %x value %08x at pc=%08x lr=%08x\n", reg, value, cpu->getGPR(15), cpu->getGPR(14));
                }
        }
 };
index 5612e6aefa5b71e632f42afc97b3096761948421..aae565c92b41a8bd572f4d513c84a501616bf1aa 100644 (file)
@@ -20,7 +20,8 @@ DEFINES += QT_DEPRECATED_WARNINGS
 # You can also select to disable deprecated APIs only up to a certain version of Qt.
 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
 
-CONFIG += c++11
+CONFIG += c++17
+QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.14
 
 SOURCES += \
         main.cpp \
index b273ab9b165009fcdc26048d9ffade5a680f3544..36a385e9e31c49b007ff45a24512c26b58752e24 100644 (file)
@@ -12,13 +12,13 @@ MainWindow::MainWindow(QWidget *parent) :
     ui->setupUi(this);
 
     emu = new Emu;
-    emu->loadROM("/Users/ash/src/psion/Sys$rom.bin");
+       emu->loadROM("/Users/ash/src/psion/Sys$rom.bin");
 
     timer = new QTimer(this);
     timer->setInterval(1000/64);
     connect(timer, SIGNAL(timeout()), SLOT(execTimer()));
 
-    updateScreen();
+       updateScreen();
 }
 
 MainWindow::~MainWindow()
@@ -30,10 +30,10 @@ void MainWindow::updateScreen()
 {
     ui->cycleCounter->setText(QString("Cycles: %1").arg(emu->currentCycles()));
 
-    updateMemory();
+       updateMemory();
 
     ui->regsLabel->setText(
-                QString("R0: %1 / R1: %2 / R2: %3 / R3: %4 / R4: %5 / R5: %6 / R6: %7 / R7: %8\nR8: %9 / R9: %10 / R10:%11 / R11:%12 / R12:%13 / SP: %14 / LR: %15 / PC: %16")
+                               QString("R0: %1 / R1: %2 / R2: %3 / R3: %4 / R4: %5 / R5: %6 / R6: %7 / R7: %8\nR8: %9 / R9: %10 / R10:%11 / R11:%12 / R12:%13 / SP: %14 / LR: %15 / PC: %16")
                 .arg(emu->getGPR(0), 8, 16)
                 .arg(emu->getGPR(1), 8, 16)
                 .arg(emu->getGPR(2), 8, 16)
@@ -56,24 +56,27 @@ void MainWindow::updateScreen()
     const int context = 8 * 4;
     uint32_t pc = emu->getGPR(15) - 4;
     uint32_t minCode = pc - context;
-    if (minCode >= (UINT32_MAX - context))
+       if (minCode >= (UINT32_MAX - context))
         minCode = 0;
     uint32_t maxCode = pc + context;
-    if (maxCode < context)
+       if (maxCode < context)
         maxCode = UINT32_MAX;
 
-    QStringList codeLines;
-    for (uint32_t addr = minCode; addr >= minCode && addr <= maxCode; addr += 4) {
-        const char *prefix = (addr == pc) ? "==>" : "   ";
+       QStringList codeLines;
+       for (uint32_t addr = minCode; addr >= minCode && addr <= maxCode; addr += 4) {
+               const char *prefix = (addr == pc) ? "==>" : "   ";
         struct ARMInstructionInfo info;
         char buffer[512];
 
-        uint32_t opcode = emu->readVirt32(addr);
-        ARMDecodeARM(opcode, &info);
-        ARMDisassemble(&info, addr, buffer, sizeof(buffer));
-        codeLines.append(QString("%1 %2 | %3 | %4").arg(prefix).arg(addr, 8, 16).arg(opcode, 8, 16).arg(buffer));
+               auto result = emu->readVirtual(addr, ARM710a::V32);
+               if (result.first.has_value()) {
+                       uint32_t opcode = result.first.value();
+                       ARMDecodeARM(opcode, &info);
+                       ARMDisassemble(&info, addr, buffer, sizeof(buffer));
+                       codeLines.append(QString("%1 %2 | %3 | %4").arg(prefix).arg(addr, 8, 16).arg(opcode, 8, 16).arg(buffer));
+               }
     }
-    ui->codeLabel->setText(codeLines.join('\n'));
+       ui->codeLabel->setText(codeLines.join('\n'));
 
     // now, the actual screen
     const uint8_t *lcdBuf = emu->getLCDBuffer();
@@ -95,12 +98,12 @@ void MainWindow::updateScreen()
             for (int x = 0; x < img.width(); x++) {
                 uint8_t byte = lcdBuf[lineOffs + (x / ppb)];
                 int shift = (x & (ppb - 1)) * bpp;
-                int mask = (1 << bpp) - 1;
+                               int mask = (1 << bpp) - 1;
                 int palIdx = (byte >> shift) & mask;
                 int palValue = palette[palIdx];
 
-                palValue |= (palValue << 4);
-                scanline[x] = palValue ^ 0xFF;
+                               palValue |= (palValue << 4);
+                               scanline[x] = palValue ^ 0xFF;
             }
         }
 
@@ -198,7 +201,7 @@ void MainWindow::keyPressEvent(QKeyEvent *event)
 void MainWindow::keyReleaseEvent(QKeyEvent *event)
 {
     int k = resolveKey(event->key());
-    if (k >= 0)
+       if (k >= 0)
         emu->keyboardKeys[k] = false;
 }
 
@@ -243,71 +246,72 @@ void MainWindow::execTimer()
 
 void MainWindow::on_addBreakButton_clicked()
 {
-    uint32_t addr = ui->breakpointAddress->text().toUInt(nullptr, 16);
-    emu->breakpoints().insert(addr);
-    updateBreakpointsList();
+       uint32_t addr = ui->breakpointAddress->text().toUInt(nullptr, 16);
+       emu->breakpoints().insert(addr);
+       updateBreakpointsList();
 }
 
 void MainWindow::on_removeBreakButton_clicked()
 {
-    uint32_t addr = ui->breakpointAddress->text().toUInt(nullptr, 16);
-    emu->breakpoints().erase(addr);
-    updateBreakpointsList();
+       uint32_t addr = ui->breakpointAddress->text().toUInt(nullptr, 16);
+       emu->breakpoints().erase(addr);
+       updateBreakpointsList();
 }
 
 void MainWindow::updateBreakpointsList()
 {
-    ui->breakpointsList->clear();
-    for (uint32_t addr : emu->breakpoints()) {
-        ui->breakpointsList->addItem(QString::number(addr, 16));
-    }
+       ui->breakpointsList->clear();
+       for (uint32_t addr : emu->breakpoints()) {
+               ui->breakpointsList->addItem(QString::number(addr, 16));
+       }
 }
 
 void MainWindow::on_memoryViewAddress_textEdited(const QString &)
 {
-    updateMemory();
+       updateMemory();
 }
 
 void MainWindow::updateMemory()
 {
-    uint32_t virtBase = ui->memoryViewAddress->text().toUInt(nullptr, 16) & ~0xFF;
-    uint32_t physBase = emu->virtToPhys(virtBase);
-    bool ok = (physBase != 0xFFFFFFFF) && emu->isPhysAddressValid(physBase);
-    if (ok && (virtBase != physBase))
-        ui->physicalAddressLabel->setText(QStringLiteral("Physical: %1").arg(physBase, 8, 16, QLatin1Char('0')));
-
-    uint8_t block[0x100];
-    if (ok) {
-        for (int i = 0; i < 0x100; i++) {
-            block[i] = emu->readPhys8(physBase + i);
-        }
-    }
-
-    QStringList output;
-    for (int row = 0; row < 16; row++) {
-        QString outLine;
-        outLine.reserve(8 + 2 + (2 * 16) + 3 + 16);
-        outLine.append(QStringLiteral("%1 |").arg(virtBase + (row * 16), 8, 16));
-        for (int col = 0; col < 16; col++) {
-            if (ok)
-                outLine.append(QStringLiteral(" %1").arg(block[row*16+col], 2, 16, QLatin1Char('0')));
-            else
-                outLine.append(QStringLiteral(" ??"));
-        }
-        outLine.append(QStringLiteral(" | "));
-        for (int col = 0; col < 16; col++) {
-            uint8_t byte = block[row*16+col];
-            if (!ok)
-                outLine.append('?');
-            else if (byte >= 0x20 && byte <= 0x7E)
-                outLine.append(byte);
-            else
-                outLine.append('.');
-        }
-        output.append(outLine);
-    }
-
-    ui->memoryViewLabel->setText(output.join('\n'));
+       uint32_t virtBase = ui->memoryViewAddress->text().toUInt(nullptr, 16) & ~0xFF;
+       auto physBaseOpt = emu->virtToPhys(virtBase);
+       auto physBase = physBaseOpt.value_or(0xFFFFFFFF);
+       bool ok = physBaseOpt.has_value() && emu->isPhysAddressValid(physBase);
+       if (ok && (virtBase != physBase))
+               ui->physicalAddressLabel->setText(QStringLiteral("Physical: %1").arg(physBase, 8, 16, QLatin1Char('0')));
+
+       uint8_t block[0x100];
+       if (ok) {
+               for (int i = 0; i < 0x100; i++) {
+                       block[i] = emu->readPhysical(physBase + i, ARM710a::V8).value();
+               }
+       }
+
+       QStringList output;
+       for (int row = 0; row < 16; row++) {
+               QString outLine;
+               outLine.reserve(8 + 2 + (2 * 16) + 3 + 16);
+               outLine.append(QStringLiteral("%1 |").arg(virtBase + (row * 16), 8, 16));
+               for (int col = 0; col < 16; col++) {
+                       if (ok)
+                               outLine.append(QStringLiteral(" %1").arg(block[row*16+col], 2, 16, QLatin1Char('0')));
+                       else
+                               outLine.append(QStringLiteral(" ??"));
+               }
+               outLine.append(QStringLiteral(" | "));
+               for (int col = 0; col < 16; col++) {
+                       uint8_t byte = block[row*16+col];
+                       if (!ok)
+                               outLine.append('?');
+                       else if (byte >= 0x20 && byte <= 0x7E)
+                               outLine.append(byte);
+                       else
+                               outLine.append('.');
+               }
+               output.append(outLine);
+       }
+
+       ui->memoryViewLabel->setText(output.join('\n'));
 }
 
 void MainWindow::on_memoryAdd1_clicked() { adjustMemoryAddress(1); }
@@ -320,24 +324,24 @@ void MainWindow::on_memorySub10_clicked() { adjustMemoryAddress(-0x10); }
 void MainWindow::on_memorySub100_clicked() { adjustMemoryAddress(-0x100); }
 
 void MainWindow::adjustMemoryAddress(int offset) {
-    uint32_t address = ui->memoryViewAddress->text().toUInt(nullptr, 16);
-    address += offset;
-    ui->memoryViewAddress->setText(QString("%1").arg(address, 8, 16, QLatin1Char('0')));
-    updateMemory();
+       uint32_t address = ui->memoryViewAddress->text().toUInt(nullptr, 16);
+       address += offset;
+       ui->memoryViewAddress->setText(QString("%1").arg(address, 8, 16, QLatin1Char('0')));
+       updateMemory();
 }
 
 void MainWindow::on_writeByteButton_clicked()
 {
-    uint32_t address = ui->memoryViewAddress->text().toUInt(nullptr, 16);
-    uint8_t value = (uint8_t)ui->memoryWriteValue->text().toUInt(nullptr, 16);
-    emu->writeVirt8(address, value);
-    updateMemory();
+       uint32_t address = ui->memoryViewAddress->text().toUInt(nullptr, 16);
+       uint8_t value = (uint8_t)ui->memoryWriteValue->text().toUInt(nullptr, 16);
+       emu->writeVirtual(value, address, ARM710a::V8);
+       updateMemory();
 }
 
 void MainWindow::on_writeDwordButton_clicked()
 {
-    uint32_t address = ui->memoryViewAddress->text().toUInt(nullptr, 16);
-    uint32_t value = ui->memoryWriteValue->text().toUInt(nullptr, 16);
-    emu->writeVirt32(address, value);
-    updateMemory();
+       uint32_t address = ui->memoryViewAddress->text().toUInt(nullptr, 16);
+       uint32_t value = ui->memoryWriteValue->text().toUInt(nullptr, 16);
+       emu->writeVirtual(value, address, ARM710a::V32);
+       updateMemory();
 }