mirror of https://github.com/Treeki/WindEmu.git
lots of arm710a fixes
This commit is contained in:
parent
c93b268061
commit
a746bd9e5f
|
@ -104,6 +104,7 @@ uint32_t ARM710a::tick() {
|
||||||
if (insnFault != NoFault) {
|
if (insnFault != NoFault) {
|
||||||
// Raise a prefetch error
|
// Raise a prefetch error
|
||||||
// These do not set FSR or FAR
|
// These do not set FSR or FAR
|
||||||
|
log("prefetch error!");
|
||||||
raiseException(Abort32, GPRs[15] - 8, 0xC);
|
raiseException(Abort32, GPRs[15] - 8, 0xC);
|
||||||
} else {
|
} else {
|
||||||
clocks += executeInstruction(insn);
|
clocks += executeInstruction(insn);
|
||||||
|
@ -131,6 +132,7 @@ static inline bool extract1(uint32_t value, uint32_t bit) {
|
||||||
|
|
||||||
uint32_t ARM710a::executeInstruction(uint32_t i) {
|
uint32_t ARM710a::executeInstruction(uint32_t i) {
|
||||||
uint32_t cycles = 1;
|
uint32_t cycles = 1;
|
||||||
|
// log("executing insn %08x @ %08x", i, GPRs[15] - 0xC);
|
||||||
|
|
||||||
// a big old dispatch thing here
|
// a big old dispatch thing here
|
||||||
// but first, conditions!
|
// but first, conditions!
|
||||||
|
@ -191,59 +193,64 @@ uint32_t ARM710a::execDataProcessing(bool I, uint32_t Opcode, bool S, uint32_t R
|
||||||
op2 -= 4;
|
op2 -= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (extract(Operand2, 6, 5)) {
|
if (extract(Operand2, 4, 4) && (shiftBy == 0)) {
|
||||||
case 0: // Logical Left (LSL)
|
// register shift by 0 never does anything
|
||||||
if (shiftBy == 0) {
|
shifterCarryOutput = flagC();
|
||||||
shifterCarryOutput = flagC();
|
} else {
|
||||||
// no change to op2!
|
switch (extract(Operand2, 6, 5)) {
|
||||||
} else if (shiftBy <= 31) {
|
case 0: // Logical Left (LSL)
|
||||||
shifterCarryOutput = extract1(op2, 31 - shiftBy);
|
if (shiftBy == 0) {
|
||||||
op2 <<= shiftBy;
|
shifterCarryOutput = flagC();
|
||||||
} else if (shiftBy == 32) {
|
// no change to op2!
|
||||||
shifterCarryOutput = extract1(op2, 0);
|
} else if (shiftBy <= 31) {
|
||||||
op2 = 0;
|
shifterCarryOutput = extract1(op2, 31 - shiftBy);
|
||||||
} else /*if (shiftBy >= 33)*/ {
|
op2 <<= shiftBy;
|
||||||
shifterCarryOutput = false;
|
} else if (shiftBy == 32) {
|
||||||
op2 = 0;
|
shifterCarryOutput = extract1(op2, 0);
|
||||||
}
|
op2 = 0;
|
||||||
break;
|
} else /*if (shiftBy >= 33)*/ {
|
||||||
case 1: // Logical Right (LSR)
|
shifterCarryOutput = false;
|
||||||
if (shiftBy == 0 || shiftBy == 32) {
|
op2 = 0;
|
||||||
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;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// IMMEDIATE
|
// IMMEDIATE
|
||||||
|
@ -252,7 +259,6 @@ uint32_t ARM710a::execDataProcessing(bool I, uint32_t Opcode, bool S, uint32_t R
|
||||||
|
|
||||||
uint32_t Rotate = extract(Operand2, 11, 8);
|
uint32_t Rotate = extract(Operand2, 11, 8);
|
||||||
uint32_t Imm = extract(Operand2, 7, 0);
|
uint32_t Imm = extract(Operand2, 7, 0);
|
||||||
Imm = (uint32_t)(int8_t)Imm;
|
|
||||||
op2 = ROR(Imm, Rotate * 2);
|
op2 = ROR(Imm, Rotate * 2);
|
||||||
shifterCarryOutput = flagC(); // correct? unsure...
|
shifterCarryOutput = flagC(); // correct? unsure...
|
||||||
}
|
}
|
||||||
|
@ -269,7 +275,7 @@ uint32_t ARM710a::execDataProcessing(bool I, uint32_t Opcode, bool S, uint32_t R
|
||||||
flags |= (CPSR & CPSR_V);
|
flags |= (CPSR & CPSR_V);
|
||||||
|
|
||||||
#define ADD_OP(a, b, c) \
|
#define ADD_OP(a, b, c) \
|
||||||
result = a + b + (uint32_t)(c); \
|
result = (uint64_t)(a) + (uint64_t)(b) + (uint64_t)(c); \
|
||||||
flags |= (result & 0xFFFFFFFF) ? 0 : CPSR_Z; \
|
flags |= (result & 0xFFFFFFFF) ? 0 : CPSR_Z; \
|
||||||
flags |= (result & 0x80000000) ? CPSR_N : 0; \
|
flags |= (result & 0x80000000) ? CPSR_N : 0; \
|
||||||
flags |= (result & 0x100000000) ? CPSR_C : 0; \
|
flags |= (result & 0x100000000) ? CPSR_C : 0; \
|
||||||
|
@ -301,9 +307,11 @@ uint32_t ARM710a::execDataProcessing(bool I, uint32_t Opcode, bool S, uint32_t R
|
||||||
// Output-less opcodes: special behaviour
|
// Output-less opcodes: special behaviour
|
||||||
if (S) {
|
if (S) {
|
||||||
CPSR = (CPSR & ~CPSR_FlagMask) | flags;
|
CPSR = (CPSR & ~CPSR_FlagMask) | flags;
|
||||||
|
// log("CPSR setflags=%08x results in CPSR=%08x", flags, CPSR);
|
||||||
} else if (Opcode == 8) {
|
} else if (Opcode == 8) {
|
||||||
// MRS, CPSR -> Reg
|
// MRS, CPSR -> Reg
|
||||||
GPRs[Rd] = CPSR;
|
GPRs[Rd] = CPSR;
|
||||||
|
log("r%d <- CPSR(%08x)", Rd, GPRs[Rd]);
|
||||||
} else if (Opcode == 9) {
|
} else if (Opcode == 9) {
|
||||||
// MSR, Reg -> CPSR
|
// MSR, Reg -> CPSR
|
||||||
bool canChangeMode = extract1(Rn, 0);
|
bool canChangeMode = extract1(Rn, 0);
|
||||||
|
@ -311,27 +319,33 @@ uint32_t ARM710a::execDataProcessing(bool I, uint32_t Opcode, bool S, uint32_t R
|
||||||
auto newCPSR = GPRs[extract(Operand2, 3, 0)];
|
auto newCPSR = GPRs[extract(Operand2, 3, 0)];
|
||||||
switchMode(modeFromCPSR(newCPSR));
|
switchMode(modeFromCPSR(newCPSR));
|
||||||
CPSR = newCPSR;
|
CPSR = newCPSR;
|
||||||
|
log("CPSR change privileged: %08x", CPSR);
|
||||||
} else {
|
} else {
|
||||||
// for the flag-only version, immediates are allowed
|
// for the flag-only version, immediates are allowed
|
||||||
// so we just re-use what was calculated earlier...
|
// so we just re-use what was calculated earlier...
|
||||||
auto newFlag = I ? op2 : GPRs[extract(Operand2, 3, 0)];
|
auto newFlag = I ? op2 : GPRs[extract(Operand2, 3, 0)];
|
||||||
CPSR &= ~CPSR_FlagMask;
|
CPSR &= ~CPSR_FlagMask;
|
||||||
CPSR |= (newFlag & CPSR_FlagMask);
|
CPSR |= (newFlag & CPSR_FlagMask);
|
||||||
|
log("CPSR change unprivileged: new=%08x result=%08x", newFlag, CPSR);
|
||||||
}
|
}
|
||||||
} else if (Opcode == 0xA) {
|
} else if (Opcode == 0xA) {
|
||||||
// MRS, SPSR -> Reg
|
// MRS, SPSR -> Reg
|
||||||
if (isPrivileged())
|
if (isPrivileged()) {
|
||||||
GPRs[Rd] = SPSRs[currentBank()];
|
GPRs[Rd] = SPSRs[currentBank()];
|
||||||
|
log("r%d <- SPSR(%08x)", Rd, GPRs[Rd]);
|
||||||
|
}
|
||||||
} else /*if (Opcode == 0xB)*/ {
|
} else /*if (Opcode == 0xB)*/ {
|
||||||
bool canChangeMode = extract1(Rn, 0);
|
bool canChangeMode = extract1(Rn, 0);
|
||||||
if (isPrivileged()) {
|
if (isPrivileged()) {
|
||||||
if (canChangeMode) {
|
if (canChangeMode) {
|
||||||
SPSRs[currentBank()] = GPRs[extract(Operand2, 3, 0)];
|
SPSRs[currentBank()] = GPRs[extract(Operand2, 3, 0)];
|
||||||
|
log("SPSR change privileged: %08x", SPSRs[currentBank()]);
|
||||||
} else {
|
} else {
|
||||||
// same hat
|
// same hat
|
||||||
auto newFlag = I ? op2 : GPRs[extract(Operand2, 3, 0)];
|
auto newFlag = I ? op2 : GPRs[extract(Operand2, 3, 0)];
|
||||||
SPSRs[currentBank()] &= ~CPSR_FlagMask;
|
SPSRs[currentBank()] &= ~CPSR_FlagMask;
|
||||||
SPSRs[currentBank()] |= (newFlag & CPSR_FlagMask);
|
SPSRs[currentBank()] |= (newFlag & CPSR_FlagMask);
|
||||||
|
log("SPSR change unprivileged: new=%08x result=%08x", newFlag, SPSRs[currentBank()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,9 +362,11 @@ uint32_t ARM710a::execDataProcessing(bool I, uint32_t Opcode, bool S, uint32_t R
|
||||||
auto saved = SPSRs[currentBank()];
|
auto saved = SPSRs[currentBank()];
|
||||||
switchMode(modeFromCPSR(saved));
|
switchMode(modeFromCPSR(saved));
|
||||||
CPSR = saved;
|
CPSR = saved;
|
||||||
|
log("dataproc restore CPSR: %08x", CPSR);
|
||||||
}
|
}
|
||||||
} else if (S) {
|
} else if (S) {
|
||||||
CPSR = (CPSR & ~CPSR_FlagMask) | flags;
|
CPSR = (CPSR & ~CPSR_FlagMask) | flags;
|
||||||
|
// log("dataproc flag change: flags=%08x CPSR=%08x", flags, CPSR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,7 +416,7 @@ uint32_t ARM710a::execSingleDataTransfer(uint32_t IPUBWL, uint32_t Rn, uint32_t
|
||||||
auto valueSize = extract1(IPUBWL, 2) ? V8 : V32;
|
auto valueSize = extract1(IPUBWL, 2) ? V8 : V32;
|
||||||
bool up = extract1(IPUBWL, 3);
|
bool up = extract1(IPUBWL, 3);
|
||||||
bool preIndex = extract1(IPUBWL, 4);
|
bool preIndex = extract1(IPUBWL, 4);
|
||||||
bool immediate = extract1(IPUBWL, 5);
|
bool immediate = !extract1(IPUBWL, 5);
|
||||||
|
|
||||||
// calculate the offset
|
// calculate the offset
|
||||||
uint32_t calcOffset;
|
uint32_t calcOffset;
|
||||||
|
@ -438,10 +454,8 @@ uint32_t ARM710a::execSingleDataTransfer(uint32_t IPUBWL, uint32_t Rn, uint32_t
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// IMMEDIATE
|
// IMMEDIATE
|
||||||
uint32_t Rotate = extract(offset, 11, 8);
|
// No rotation or anything here
|
||||||
uint32_t Imm = extract(offset, 7, 0);
|
calcOffset = offset;
|
||||||
Imm = (uint32_t)(int8_t)Imm;
|
|
||||||
calcOffset = ROR(Imm, Rotate * 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t base = GPRs[Rn];
|
uint32_t base = GPRs[Rn];
|
||||||
|
@ -458,8 +472,10 @@ uint32_t ARM710a::execSingleDataTransfer(uint32_t IPUBWL, uint32_t Rn, uint32_t
|
||||||
if (changeModes) switchMode(User32);
|
if (changeModes) switchMode(User32);
|
||||||
auto readResult = readVirtual(transferAddr, valueSize);
|
auto readResult = readVirtual(transferAddr, valueSize);
|
||||||
if (changeModes) switchMode(saveMode);
|
if (changeModes) switchMode(saveMode);
|
||||||
if (readResult.first.has_value())
|
if (readResult.first.has_value()) {
|
||||||
GPRs[Rd] = readResult.first.value();
|
GPRs[Rd] = readResult.first.value();
|
||||||
|
if (Rd == 15) prefetchCount = 0;
|
||||||
|
}
|
||||||
fault = readResult.second;
|
fault = readResult.second;
|
||||||
} else {
|
} else {
|
||||||
uint32_t value = GPRs[Rd];
|
uint32_t value = GPRs[Rd];
|
||||||
|
@ -538,6 +554,9 @@ uint32_t ARM710a::execBlockDataTransfer(uint32_t PUSWL, uint32_t Rn, uint32_t re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (registerList & 0x8000)
|
||||||
|
prefetchCount = 0;
|
||||||
|
|
||||||
// datasheet specifies that base register must be
|
// datasheet specifies that base register must be
|
||||||
// restored if an error occurs during LDM
|
// restored if an error occurs during LDM
|
||||||
if (load && fault != NoFault)
|
if (load && fault != NoFault)
|
||||||
|
@ -741,7 +760,7 @@ pair<MaybeU32, ARM710a::MMUFault> ARM710a::readVirtual(uint32_t virtAddr, ValueS
|
||||||
if (auto v = readPhysical(virtAddr, valueSize); v.has_value())
|
if (auto v = readPhysical(virtAddr, valueSize); v.has_value())
|
||||||
return make_pair(v.value(), NoFault);
|
return make_pair(v.value(), NoFault);
|
||||||
else
|
else
|
||||||
return make_pair(MaybeU32(), NonMMUError);
|
return make_pair(MaybeU32(), encodeFault(NonMMUError, 0, virtAddr));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto translated = translateAddressUsingTlb(virtAddr);
|
auto translated = translateAddressUsingTlb(virtAddr);
|
||||||
|
@ -775,7 +794,7 @@ ARM710a::MMUFault ARM710a::writeVirtual(uint32_t value, uint32_t virtAddr, Value
|
||||||
if (!isMMUEnabled()) {
|
if (!isMMUEnabled()) {
|
||||||
// direct virtual -> physical mapping, sans MMU
|
// direct virtual -> physical mapping, sans MMU
|
||||||
if (!writePhysical(value, virtAddr, valueSize))
|
if (!writePhysical(value, virtAddr, valueSize))
|
||||||
return NonMMUError;
|
return encodeFault(NonMMUError, 0, virtAddr);
|
||||||
} else {
|
} else {
|
||||||
auto translated = translateAddressUsingTlb(virtAddr);
|
auto translated = translateAddressUsingTlb(virtAddr);
|
||||||
if (holds_alternative<MMUFault>(translated))
|
if (holds_alternative<MMUFault>(translated))
|
||||||
|
@ -965,12 +984,61 @@ ARM710a::MMUFault ARM710a::checkAccessPermissions(ARM710a::TlbEntry *entry, uint
|
||||||
void ARM710a::reportFault(MMUFault fault) {
|
void ARM710a::reportFault(MMUFault fault) {
|
||||||
if (fault != NoFault) {
|
if (fault != NoFault) {
|
||||||
if ((fault & 0xF) != NonMMUError) {
|
if ((fault & 0xF) != NonMMUError) {
|
||||||
cp15_faultStatus = fault & 0xFFFF;
|
cp15_faultStatus = fault & (MMUFaultTypeMask | MMUFaultDomainMask);
|
||||||
cp15_faultAddress = fault >> 32;
|
cp15_faultAddress = fault >> MMUFaultAddressShift;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *faultTypes[] = {
|
||||||
|
"NoFault",
|
||||||
|
"AlignmentFault",
|
||||||
|
"???",
|
||||||
|
"NonMMUError",
|
||||||
|
"SectionLinefetchError",
|
||||||
|
"SectionTranslationFault",
|
||||||
|
"PageLinefetchError",
|
||||||
|
"PageTranslationFault",
|
||||||
|
"SectionOtherBusError",
|
||||||
|
"SectionDomainFault",
|
||||||
|
"PageOtherBusError",
|
||||||
|
"PageDomainFault",
|
||||||
|
"Lv1TranslationError",
|
||||||
|
"SectionPermissionFault",
|
||||||
|
"Lv2TranslationError",
|
||||||
|
"PagePermissionFault"
|
||||||
|
};
|
||||||
|
log("⚠️ Fault type=%s domain=%d address=%08x pc=%08x lr=%08x",
|
||||||
|
faultTypes[fault & MMUFaultTypeMask],
|
||||||
|
(fault & MMUFaultDomainMask) >> MMUFaultDomainShift,
|
||||||
|
fault >> MMUFaultAddressShift,
|
||||||
|
GPRs[15], GPRs[14]);
|
||||||
|
|
||||||
// this signals a branch to DataAbort after the
|
// this signals a branch to DataAbort after the
|
||||||
// instruction is done executing
|
// instruction is done executing
|
||||||
faultTriggeredThisCycle = true;
|
faultTriggeredThisCycle = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ARM710a::log(const char *format, ...) {
|
||||||
|
if (logger) {
|
||||||
|
char buffer[1024];
|
||||||
|
|
||||||
|
va_list vaList;
|
||||||
|
va_start(vaList, format);
|
||||||
|
vsnprintf(buffer, sizeof(buffer), format, vaList);
|
||||||
|
va_end(vaList);
|
||||||
|
|
||||||
|
logger(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ARM710a::test() {
|
||||||
|
uint64_t result;
|
||||||
|
uint32_t flags = 0;
|
||||||
|
uint32_t v = 0x10000000;
|
||||||
|
|
||||||
|
SUB_OP(v, v, 1);
|
||||||
|
|
||||||
|
log("RESULT:%llx FLAGS:%08x", result, flags);
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ typedef optional<uint32_t> MaybeU32;
|
||||||
class ARM710a
|
class ARM710a
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
void test();
|
||||||
|
|
||||||
enum ValueSize { V8 = 0, V32 = 1 };
|
enum ValueSize { V8 = 0, V32 = 1 };
|
||||||
|
|
||||||
enum MMUFault : uint64_t {
|
enum MMUFault : uint64_t {
|
||||||
|
@ -42,8 +44,11 @@ public:
|
||||||
// so we are reusing it for nefarious purposes
|
// so we are reusing it for nefarious purposes
|
||||||
NonMMUError = 3,
|
NonMMUError = 3,
|
||||||
|
|
||||||
|
MMUFaultTypeMask = 0xF,
|
||||||
MMUFaultDomainMask = 0xF0,
|
MMUFaultDomainMask = 0xF0,
|
||||||
MMUFaultAddressMask = 0xFFFFFFFF00000000
|
MMUFaultDomainShift = 4,
|
||||||
|
MMUFaultAddressMask = 0xFFFFFFFF00000000,
|
||||||
|
MMUFaultAddressShift = 32
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,6 +88,7 @@ public:
|
||||||
void requestIRQ(); // pull nIRQ low
|
void requestIRQ(); // pull nIRQ low
|
||||||
void reset(); // pull nRESET low
|
void reset(); // pull nRESET low
|
||||||
|
|
||||||
|
bool instructionReady() const { return (prefetchCount == 2); }
|
||||||
uint32_t tick(); // run the chip for at least 1 clock cycle
|
uint32_t tick(); // run the chip for at least 1 clock cycle
|
||||||
|
|
||||||
MaybeU32 readVirtualDebug(uint32_t virtAddr, ValueSize valueSize);
|
MaybeU32 readVirtualDebug(uint32_t virtAddr, ValueSize valueSize);
|
||||||
|
@ -94,8 +100,14 @@ public:
|
||||||
virtual bool writePhysical(uint32_t value, uint32_t physAddr, ARM710a::ValueSize valueSize) = 0;
|
virtual bool writePhysical(uint32_t value, uint32_t physAddr, ARM710a::ValueSize valueSize) = 0;
|
||||||
|
|
||||||
uint32_t getGPR(int index) const { return GPRs[index]; }
|
uint32_t getGPR(int index) const { return GPRs[index]; }
|
||||||
|
uint32_t getCPSR() const { return CPSR; }
|
||||||
|
|
||||||
|
void setLogger(std::function<void(const char *)> newLogger) { logger = newLogger; }
|
||||||
|
protected:
|
||||||
|
void log(const char *format, ...);
|
||||||
private:
|
private:
|
||||||
|
std::function<void(const char *)> logger;
|
||||||
|
|
||||||
enum { Nop = 0xE1A00000 };
|
enum { Nop = 0xE1A00000 };
|
||||||
|
|
||||||
enum Mode : uint8_t {
|
enum Mode : uint8_t {
|
||||||
|
@ -158,22 +170,22 @@ private:
|
||||||
bool flagN() const { return CPSR & CPSR_N; }
|
bool flagN() const { return CPSR & CPSR_N; }
|
||||||
bool checkCondition(int cond) const {
|
bool checkCondition(int cond) const {
|
||||||
switch (cond) {
|
switch (cond) {
|
||||||
case 0: return flagZ();
|
/*EQ*/ case 0: return flagZ();
|
||||||
case 1: return !flagZ();
|
/*NE*/ case 1: return !flagZ();
|
||||||
case 2: return flagC();
|
/*CS*/ case 2: return flagC();
|
||||||
case 3: return !flagC();
|
/*CC*/ case 3: return !flagC();
|
||||||
case 4: return flagN();
|
/*MI*/ case 4: return flagN();
|
||||||
case 5: return !flagN();
|
/*PL*/ case 5: return !flagN();
|
||||||
case 6: return flagV();
|
/*VS*/ case 6: return flagV();
|
||||||
case 7: return !flagV();
|
/*VC*/ case 7: return !flagV();
|
||||||
case 8: return flagC() && !flagZ();
|
/*HI*/ case 8: return flagC() && !flagZ();
|
||||||
case 9: return !flagC() || flagZ();
|
/*LS*/ case 9: return !flagC() || flagZ();
|
||||||
case 0xA: return flagN() == flagV();
|
/*GE*/ case 0xA: return flagN() == flagV();
|
||||||
case 0xB: return flagN() != flagV();
|
/*LT*/ case 0xB: return flagN() != flagV();
|
||||||
case 0xC: return !flagZ() && (flagN() == flagV());
|
/*GT*/ case 0xC: return !flagZ() && (flagN() == flagV());
|
||||||
case 0xD: return flagZ() || (flagN() != flagV());
|
/*LE*/ case 0xD: return flagZ() || (flagN() != flagV());
|
||||||
case 0xE: return true;
|
/*AL*/ case 0xE: return true;
|
||||||
/*case 0xF:*/
|
/*NV*/ /*case 0xF:*/
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,6 +224,8 @@ MaybeU32 Emu::readPhysical(uint32_t physAddr, ValueSize valueSize) {
|
||||||
else if (region == 0xD1)
|
else if (region == 0xD1)
|
||||||
return MemoryBlockD1[physAddr & MemoryBlockMask];
|
return MemoryBlockD1[physAddr & MemoryBlockMask];
|
||||||
#endif
|
#endif
|
||||||
|
else if (region >= 0xC0)
|
||||||
|
return 0xFF; // just throw accesses to unmapped RAM away
|
||||||
} else {
|
} else {
|
||||||
uint32_t result;
|
uint32_t result;
|
||||||
if (region == 0)
|
if (region == 0)
|
||||||
|
@ -244,6 +246,8 @@ MaybeU32 Emu::readPhysical(uint32_t physAddr, ValueSize valueSize) {
|
||||||
else if (region == 0xD1)
|
else if (region == 0xD1)
|
||||||
LOAD_32LE(result, physAddr & MemoryBlockMask, MemoryBlockD1);
|
LOAD_32LE(result, physAddr & MemoryBlockMask, MemoryBlockD1);
|
||||||
#endif
|
#endif
|
||||||
|
else if (region >= 0xC0)
|
||||||
|
return 0xFFFFFFFF; // just throw accesses to unmapped RAM away
|
||||||
else
|
else
|
||||||
return {};
|
return {};
|
||||||
return result;
|
return result;
|
||||||
|
@ -267,6 +271,8 @@ bool Emu::writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize)
|
||||||
else if (region == 0xD1)
|
else if (region == 0xD1)
|
||||||
MemoryBlockD1[physAddr & MemoryBlockMask] = (uint8_t)value;
|
MemoryBlockD1[physAddr & MemoryBlockMask] = (uint8_t)value;
|
||||||
#endif
|
#endif
|
||||||
|
else if (region >= 0xC0)
|
||||||
|
return true; // just throw accesses to unmapped RAM away
|
||||||
else if (region == 0x20 && physAddr <= 0x20000FFF)
|
else if (region == 0x20 && physAddr <= 0x20000FFF)
|
||||||
etna.writeReg8(physAddr & 0xFFF, value);
|
etna.writeReg8(physAddr & 0xFFF, value);
|
||||||
else if (region == 0x80 && physAddr <= 0x80000FFF)
|
else if (region == 0x80 && physAddr <= 0x80000FFF)
|
||||||
|
@ -287,6 +293,8 @@ bool Emu::writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize)
|
||||||
else if (region == 0xD1)
|
else if (region == 0xD1)
|
||||||
STORE_32LE(value, physAddr & MemoryBlockMask, MemoryBlockD1);
|
STORE_32LE(value, physAddr & MemoryBlockMask, MemoryBlockD1);
|
||||||
#endif
|
#endif
|
||||||
|
else if (region >= 0xC0)
|
||||||
|
return true; // just throw accesses to unmapped RAM away
|
||||||
else if (region == 0x20 && physAddr <= 0x20000FFF)
|
else if (region == 0x20 && physAddr <= 0x20000FFF)
|
||||||
etna.writeReg32(physAddr & 0xFFF, value);
|
etna.writeReg32(physAddr & 0xFFF, value);
|
||||||
else if (region == 0x80 && physAddr <= 0x80000FFF)
|
else if (region == 0x80 && physAddr <= 0x80000FFF)
|
||||||
|
@ -311,6 +319,7 @@ void Emu::configure() {
|
||||||
nextTickAt = TICK_INTERVAL;
|
nextTickAt = TICK_INTERVAL;
|
||||||
rtc = getRTC();
|
rtc = getRTC();
|
||||||
|
|
||||||
|
setProcessorID(0x41807100);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,11 +361,15 @@ void Emu::executeUntil(int64_t cycles) {
|
||||||
// keep the clock moving
|
// keep the clock moving
|
||||||
passedCycles++;
|
passedCycles++;
|
||||||
} else {
|
} else {
|
||||||
|
if (auto v = virtToPhys(getGPR(15) - 0xC); v.has_value() && instructionReady())
|
||||||
|
debugPC(v.value());
|
||||||
passedCycles += tick();
|
passedCycles += tick();
|
||||||
|
|
||||||
uint32_t new_pc = getGPR(15) - 0xC;
|
uint32_t new_pc = getGPR(15) - 0xC;
|
||||||
if (_breakpoints.find(new_pc) != _breakpoints.end())
|
if (_breakpoints.find(new_pc) != _breakpoints.end()) {
|
||||||
|
log("⚠️ Breakpoint triggered at %08x!\n", new_pc);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,14 +439,34 @@ void Emu::debugPC(uint32_t pc) {
|
||||||
const char *wut = identifyObjectCon(container);
|
const char *wut = identifyObjectCon(container);
|
||||||
if (wut) {
|
if (wut) {
|
||||||
fetchName(obj, objName);
|
fetchName(obj, objName);
|
||||||
printf("OBJS: added %s at %08x <%s>", wut, obj, objName);
|
|
||||||
if (strcmp(wut, "process") == 0) {
|
if (strcmp(wut, "process") == 0) {
|
||||||
fetchProcessFilename(obj, objName);
|
char procName[1000];
|
||||||
printf(" <%s>", objName);
|
fetchProcessFilename(obj, procName);
|
||||||
|
log("OBJS: added %s at %08x <%s> <%s>", wut, obj, objName, procName);
|
||||||
|
} else {
|
||||||
|
log("OBJS: added %s at %08x <%s>", wut, obj, objName);
|
||||||
}
|
}
|
||||||
printf("\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pc == 0x6D8) {
|
||||||
|
uint32_t virtAddr = getGPR(0);
|
||||||
|
uint32_t physAddr = getGPR(1);
|
||||||
|
uint32_t btIndex = getGPR(2);
|
||||||
|
uint32_t regionSize = getGPR(3);
|
||||||
|
log("KERNEL MMU SECTION: v:%08x p:%08x size:%08x idx:%02x",
|
||||||
|
virtAddr, physAddr, regionSize, btIndex);
|
||||||
|
}
|
||||||
|
if (pc == 0x710) {
|
||||||
|
uint32_t virtAddr = getGPR(0);
|
||||||
|
uint32_t physAddr = getGPR(1);
|
||||||
|
uint32_t btIndex = getGPR(2);
|
||||||
|
uint32_t regionSize = getGPR(3);
|
||||||
|
uint32_t pageTableA = getGPR(4);
|
||||||
|
uint32_t pageTableB = getGPR(5);
|
||||||
|
log("KERNEL MMU PAGES: v:%08x p:%08x size:%08x idx:%02x tableA:%08x tableB:%08x",
|
||||||
|
virtAddr, physAddr, regionSize, btIndex, pageTableA, pageTableB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,14 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||||
ui(new Ui::MainWindow)
|
ui(new Ui::MainWindow)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
ui->logView->setMaximumBlockCount(1000);
|
||||||
|
|
||||||
emu = new Emu;
|
emu = new Emu;
|
||||||
emu->loadROM("/Users/ash/src/psion/Sys$rom.bin");
|
emu->loadROM("/Users/ash/src/psion/Sys$rom.bin");
|
||||||
|
emu->setLogger([&](const char *str) {
|
||||||
|
ui->logView->appendPlainText(str);
|
||||||
|
});
|
||||||
|
emu->test();
|
||||||
|
|
||||||
timer = new QTimer(this);
|
timer = new QTimer(this);
|
||||||
timer->setInterval(1000/64);
|
timer->setInterval(1000/64);
|
||||||
|
@ -32,8 +37,25 @@ void MainWindow::updateScreen()
|
||||||
|
|
||||||
updateMemory();
|
updateMemory();
|
||||||
|
|
||||||
|
char flagDisplay[] = {
|
||||||
|
(emu->getCPSR() & 0x80000000) ? 'N' : '-',
|
||||||
|
(emu->getCPSR() & 0x40000000) ? 'Z' : '-',
|
||||||
|
(emu->getCPSR() & 0x20000000) ? 'C' : '-',
|
||||||
|
(emu->getCPSR() & 0x10000000) ? 'V' : '-',
|
||||||
|
0
|
||||||
|
};
|
||||||
|
const char *modeName = "???";
|
||||||
|
switch (emu->getCPSR() & 0x1F) {
|
||||||
|
case 0x10: modeName = "User"; break;
|
||||||
|
case 0x11: modeName = "FIQ"; break;
|
||||||
|
case 0x12: modeName = "IRQ"; break;
|
||||||
|
case 0x13: modeName = "Supervisor"; break;
|
||||||
|
case 0x17: modeName = "Abort"; break;
|
||||||
|
case 0x1B: modeName = "Undefined"; break;
|
||||||
|
}
|
||||||
|
|
||||||
ui->regsLabel->setText(
|
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\n%17 / Mode: %18")
|
||||||
.arg(emu->getGPR(0), 8, 16)
|
.arg(emu->getGPR(0), 8, 16)
|
||||||
.arg(emu->getGPR(1), 8, 16)
|
.arg(emu->getGPR(1), 8, 16)
|
||||||
.arg(emu->getGPR(2), 8, 16)
|
.arg(emu->getGPR(2), 8, 16)
|
||||||
|
@ -50,11 +72,13 @@ void MainWindow::updateScreen()
|
||||||
.arg(emu->getGPR(13), 8, 16)
|
.arg(emu->getGPR(13), 8, 16)
|
||||||
.arg(emu->getGPR(14), 8, 16)
|
.arg(emu->getGPR(14), 8, 16)
|
||||||
.arg(emu->getGPR(15), 8, 16)
|
.arg(emu->getGPR(15), 8, 16)
|
||||||
|
.arg(flagDisplay)
|
||||||
|
.arg(modeName)
|
||||||
);
|
);
|
||||||
|
|
||||||
// show a crude disassembly
|
// show a crude disassembly
|
||||||
const int context = 8 * 4;
|
const int context = 8 * 4;
|
||||||
uint32_t pc = emu->getGPR(15) - 4;
|
uint32_t pc = emu->getGPR(15) - 8;
|
||||||
uint32_t minCode = pc - context;
|
uint32_t minCode = pc - context;
|
||||||
if (minCode >= (UINT32_MAX - context))
|
if (minCode >= (UINT32_MAX - context))
|
||||||
minCode = 0;
|
minCode = 0;
|
||||||
|
@ -64,7 +88,7 @@ void MainWindow::updateScreen()
|
||||||
|
|
||||||
QStringList codeLines;
|
QStringList codeLines;
|
||||||
for (uint32_t addr = minCode; addr >= minCode && addr <= maxCode; addr += 4) {
|
for (uint32_t addr = minCode; addr >= minCode && addr <= maxCode; addr += 4) {
|
||||||
const char *prefix = (addr == pc) ? "==>" : " ";
|
const char *prefix = (addr == pc) ? (emu->instructionReady() ? "==>" : "...") : " ";
|
||||||
struct ARMInstructionInfo info;
|
struct ARMInstructionInfo info;
|
||||||
char buffer[512];
|
char buffer[512];
|
||||||
|
|
||||||
|
@ -228,8 +252,9 @@ void MainWindow::on_stopButton_clicked()
|
||||||
|
|
||||||
void MainWindow::on_stepTickButton_clicked()
|
void MainWindow::on_stepTickButton_clicked()
|
||||||
{
|
{
|
||||||
emu->executeUntil(emu->currentCycles() + (CLOCK_SPEED * 2));
|
// emu->executeUntil(emu->currentCycles() + (CLOCK_SPEED * 2));
|
||||||
updateScreen();
|
emu->executeUntil(emu->currentCycles() + 25000);
|
||||||
|
updateScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_stepInsnButton_clicked()
|
void MainWindow::on_stepInsnButton_clicked()
|
||||||
|
|
|
@ -22,72 +22,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="5">
|
|
||||||
<widget class="QLabel" name="regsLabel">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<family>Courier New</family>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="3">
|
|
||||||
<widget class="QPushButton" name="startButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Start</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="4">
|
|
||||||
<widget class="QPushButton" name="stepTickButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Step (Tick)</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0" colspan="6">
|
|
||||||
<widget class="QLabel" name="screen">
|
|
||||||
<property name="focusPolicy">
|
|
||||||
<enum>Qt::ClickFocus</enum>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="3">
|
|
||||||
<widget class="QPushButton" name="stepInsnButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>Step (Insn)</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="2">
|
|
||||||
<spacer name="horizontalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="4">
|
|
||||||
<widget class="QPushButton" name="stopButton">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Stop</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0" colspan="5">
|
<item row="4" column="0" colspan="5">
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
|
@ -315,6 +249,75 @@
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0" colspan="5">
|
||||||
|
<widget class="QLabel" name="regsLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Courier New</family>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="3">
|
||||||
|
<widget class="QPushButton" name="startButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Start</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="4">
|
||||||
|
<widget class="QPushButton" name="stepTickButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Step (Tick)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="6">
|
||||||
|
<widget class="QLabel" name="screen">
|
||||||
|
<property name="focusPolicy">
|
||||||
|
<enum>Qt::ClickFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="3">
|
||||||
|
<widget class="QPushButton" name="stepInsnButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Step (Insn)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="4">
|
||||||
|
<widget class="QPushButton" name="stopButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Stop</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0" colspan="5">
|
||||||
|
<widget class="QPlainTextEdit" name="logView"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
|
Loading…
Reference in New Issue