#-------------------------------------------------
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
#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 \
emu.cpp
HEADERS += \
+ arm710a.h \
etna.h \
wind_hw.h \
wind.h \
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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);
+};
#include "wind.h"
#include "wind_hw.h"
#include <time.h>
+#include "common.h"
#define INCLUDE_BANK1
} 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;
} 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;
}
}
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;
} 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) {
// diffInterrupts(interruptMask, interruptMask &~ value);
interruptMask &= ~value;
} else if (reg == HALT) {
- cpu.halted = true;
+ halted = true;
// BLEOI = 0x410,
// MCEOI = 0x414,
} else if (reg == TEOI) {
} 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;
}
#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];
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;
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();
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);
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; }
};
#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,
};
struct UART {
- struct ARMCore *cpu;
+ ARM710a *cpu;
enum {
IntRx = 1,
// 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;
}
}
// 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;
}
}
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) {
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));
}
}
};
# 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 \
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()
{
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)
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();
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;
}
}
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
int k = resolveKey(event->key());
- if (k >= 0)
+ if (k >= 0)
emu->keyboardKeys[k] = false;
}
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); }
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();
}