mirror of https://github.com/Treeki/WindEmu.git
make Osaris and 5mx emulator classes inherit from a shared base
This commit is contained in:
parent
bf71b18d27
commit
8f30d6fcef
32
README.md
32
README.md
|
@ -1,11 +1,11 @@
|
||||||
WindEmu is an attempt to emulate the Psion Series 5mx (or as internally called, "Windermere"). I don't think anyone's done this before... or if they have, I can't find evidence online!
|
WindEmu is an attempt to emulate various Psion PDAs.
|
||||||
|
|
||||||
- Platform-independent core emulation library written in C/C++
|
- Platform-independent core emulation library written in C/C++
|
||||||
- Qt5 front-end (currently quite barebones...)
|
- Qt5 front-end (currently quite barebones...)
|
||||||
- Very experimental
|
- Very experimental
|
||||||
- Boots from the Psion 5mx Pro's Sys$rom.bin
|
- Basic support for multiple devices
|
||||||
|
|
||||||
Hardware features:
|
Psion 5mx (EPOC R5) features:
|
||||||
|
|
||||||
- ✅ LCD: partially implemented
|
- ✅ LCD: partially implemented
|
||||||
- ✅ Keyboard: implemented
|
- ✅ Keyboard: implemented
|
||||||
|
@ -17,14 +17,22 @@ Hardware features:
|
||||||
- ❌ RTC alarm: not implemented
|
- ❌ RTC alarm: not implemented
|
||||||
- ❌ Standby mode: not implemented
|
- ❌ Standby mode: not implemented
|
||||||
|
|
||||||
|
Oregon Scientific Osaris (EPOC R4) features:
|
||||||
|
|
||||||
|
- ✅ LCD: implemented
|
||||||
|
- ✅ Keyboard: mostly implemented (key mappings wrong)
|
||||||
|
- ❌ Touch panel: not implemented
|
||||||
|
- ❌ Audio: not implemented
|
||||||
|
- ❌ Serial/UART support: stubbed out
|
||||||
|
- ❌ PCMCIA: mostly stubbed out
|
||||||
|
- ✅ RTC: implemented (needs testing)
|
||||||
|
- ❌ RTC alarm: not implemented
|
||||||
|
- ❌ Standby mode: not implemented
|
||||||
|
|
||||||
Known issues:
|
Known issues:
|
||||||
|
|
||||||
- ROM path is hardcoded into WindQt/main.cpp right now
|
|
||||||
- Memory protection is not enforced
|
|
||||||
- Memory errors do not result in an Abort exception but instead make the emulator freak out
|
|
||||||
- Some keys do not work properly
|
- Some keys do not work properly
|
||||||
- State is not saved (just like a real Psion :p)
|
- State is not saved (just like a real Psion :p)
|
||||||
- LCD controller is almost entirely unimplemented aside from the very basics to display the framebuffer
|
|
||||||
- EPOC misbehaves massively with memory banks larger than 0x800000 (may be an OS design flaw? need to confirm)
|
- EPOC misbehaves massively with memory banks larger than 0x800000 (may be an OS design flaw? need to confirm)
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
|
@ -32,16 +40,16 @@ Copyright
|
||||||
|
|
||||||
The Psion-specific code is copyright (c) 2019 Ash Wolf.
|
The Psion-specific code is copyright (c) 2019 Ash Wolf.
|
||||||
|
|
||||||
The ARM emulation core is a modified version of the one used in [mGBA](https://github.com/mgba-emu/mgba) by endrift.
|
The ARM disassembly code is a modified version of the one used in [mGBA](https://github.com/mgba-emu/mgba) by endrift.
|
||||||
|
|
||||||
WindEmu is available under the Mozilla Public License 2.0.
|
WindEmu is available under the Mozilla Public License 2.0.
|
||||||
|
|
||||||
Resources
|
Resources
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Special thanks to [PsiLinux/OpenPsion](http://linux-7110.sourceforge.net/index.shtml) for providing an avenue to learn about the hardware definitions (registers, etc).
|
Special thanks to [PsiLinux/OpenPsion](http://linux-7110.sourceforge.net/index.shtml) for providing an avenue to learn about the 5mx hardware definitions (registers, etc).
|
||||||
|
|
||||||
More information on the hardware is available in the NetBSD port: http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/arch/epoc32/?only_with_tag=MAIN
|
More information on the 5mx hardware is available in the NetBSD port: http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/arch/epoc32/?only_with_tag=MAIN
|
||||||
|
|
||||||
The EPOC C++ SDK is available here: https://web.archive.org/web/20071010101808/http://www.psionteklogix.com/teknet/pdk/netpad-pdk/epoc_downloads.htm
|
The EPOC C++ SDK is available here: https://web.archive.org/web/20071010101808/http://www.psionteklogix.com/teknet/pdk/netpad-pdk/epoc_downloads.htm
|
||||||
|
|
||||||
|
@ -49,6 +57,10 @@ The ARM variant used in the 5mx is documented here: http://infocenter.arm.com/he
|
||||||
|
|
||||||
The datasheet for the CL-PS7110 SoC used in the Series 5 (_not_ the 5mx) is available here: https://www.igorkov.org/revo/datasheets/CL-PS7110.pdf - while not identical to Windermere, some components operate in similar fashion.
|
The datasheet for the CL-PS7110 SoC used in the Series 5 (_not_ the 5mx) is available here: https://www.igorkov.org/revo/datasheets/CL-PS7110.pdf - while not identical to Windermere, some components operate in similar fashion.
|
||||||
|
|
||||||
|
The datasheet for the CL-PS7111 SoC used in the Osaris is available here: https://www.digchip.com/datasheets/parts/datasheet/096/CL-PS7111-pdf.php
|
||||||
|
|
||||||
|
The datasheet for the CL-PS6700 PCMCIA controller used in the Osaris is available here: https://pdf1.alldatasheet.com/datasheet-pdf/view/104907/CIRRUS/CL-PS6700.html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,12 +26,10 @@ SOURCES += \
|
||||||
arm710.cpp \
|
arm710.cpp \
|
||||||
clps7111.cpp \
|
clps7111.cpp \
|
||||||
clps7600.cpp \
|
clps7600.cpp \
|
||||||
|
emubase.cpp \
|
||||||
etna.cpp \
|
etna.cpp \
|
||||||
isa-arm.c \
|
|
||||||
decoder.c \
|
decoder.c \
|
||||||
decoder-arm.c \
|
decoder-arm.c \
|
||||||
arm.c \
|
|
||||||
wind_defs.cpp \
|
|
||||||
windermere.cpp
|
windermere.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
@ -39,6 +37,7 @@ HEADERS += \
|
||||||
clps7111.h \
|
clps7111.h \
|
||||||
clps7111_defs.h \
|
clps7111_defs.h \
|
||||||
clps7600.h \
|
clps7600.h \
|
||||||
|
emubase.h \
|
||||||
etna.h \
|
etna.h \
|
||||||
hardware.h \
|
hardware.h \
|
||||||
wind_defs.h \
|
wind_defs.h \
|
||||||
|
@ -50,7 +49,6 @@ HEADERS += \
|
||||||
decoder.h \
|
decoder.h \
|
||||||
decoder-inlines.h \
|
decoder-inlines.h \
|
||||||
common.h \
|
common.h \
|
||||||
arm.h \
|
|
||||||
windermere.h
|
windermere.h
|
||||||
unix {
|
unix {
|
||||||
target.path = /usr/lib
|
target.path = /usr/lib
|
||||||
|
|
236
WindCore/arm.c
236
WindCore/arm.c
|
@ -1,236 +0,0 @@
|
||||||
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
#include "arm.h"
|
|
||||||
#include "isa-arm.h"
|
|
||||||
#include "isa-inlines.h"
|
|
||||||
|
|
||||||
static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode);
|
|
||||||
|
|
||||||
void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) {
|
|
||||||
if (mode == cpu->privilegeMode) {
|
|
||||||
// Not switching modes after all
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum RegisterBank newBank = _ARMSelectBank(mode);
|
|
||||||
enum RegisterBank oldBank = _ARMSelectBank(cpu->privilegeMode);
|
|
||||||
if (newBank != oldBank) {
|
|
||||||
// Switch banked registers
|
|
||||||
if (mode == MODE_FIQ || cpu->privilegeMode == MODE_FIQ) {
|
|
||||||
int oldFIQBank = oldBank == BANK_FIQ;
|
|
||||||
int newFIQBank = newBank == BANK_FIQ;
|
|
||||||
cpu->bankedRegisters[oldFIQBank][2] = cpu->gprs[8];
|
|
||||||
cpu->bankedRegisters[oldFIQBank][3] = cpu->gprs[9];
|
|
||||||
cpu->bankedRegisters[oldFIQBank][4] = cpu->gprs[10];
|
|
||||||
cpu->bankedRegisters[oldFIQBank][5] = cpu->gprs[11];
|
|
||||||
cpu->bankedRegisters[oldFIQBank][6] = cpu->gprs[12];
|
|
||||||
cpu->gprs[8] = cpu->bankedRegisters[newFIQBank][2];
|
|
||||||
cpu->gprs[9] = cpu->bankedRegisters[newFIQBank][3];
|
|
||||||
cpu->gprs[10] = cpu->bankedRegisters[newFIQBank][4];
|
|
||||||
cpu->gprs[11] = cpu->bankedRegisters[newFIQBank][5];
|
|
||||||
cpu->gprs[12] = cpu->bankedRegisters[newFIQBank][6];
|
|
||||||
}
|
|
||||||
cpu->bankedRegisters[oldBank][0] = cpu->gprs[ARM_SP];
|
|
||||||
cpu->bankedRegisters[oldBank][1] = cpu->gprs[ARM_LR];
|
|
||||||
cpu->gprs[ARM_SP] = cpu->bankedRegisters[newBank][0];
|
|
||||||
cpu->gprs[ARM_LR] = cpu->bankedRegisters[newBank][1];
|
|
||||||
|
|
||||||
cpu->bankedSPSRs[oldBank] = cpu->spsr.packed;
|
|
||||||
cpu->spsr.packed = cpu->bankedSPSRs[newBank];
|
|
||||||
}
|
|
||||||
cpu->privilegeMode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode mode) {
|
|
||||||
switch (mode) {
|
|
||||||
case MODE_USER:
|
|
||||||
case MODE_SYSTEM:
|
|
||||||
// No banked registers
|
|
||||||
return BANK_NONE;
|
|
||||||
case MODE_FIQ:
|
|
||||||
return BANK_FIQ;
|
|
||||||
case MODE_IRQ:
|
|
||||||
return BANK_IRQ;
|
|
||||||
case MODE_SUPERVISOR:
|
|
||||||
return BANK_SUPERVISOR;
|
|
||||||
case MODE_ABORT:
|
|
||||||
return BANK_ABORT;
|
|
||||||
case MODE_UNDEFINED:
|
|
||||||
return BANK_UNDEFINED;
|
|
||||||
default:
|
|
||||||
// This should be unreached
|
|
||||||
return BANK_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMReset(struct ARMCore* cpu) {
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 16; ++i) {
|
|
||||||
cpu->gprs[i] = 0;
|
|
||||||
}
|
|
||||||
for (i = 0; i < 6; ++i) {
|
|
||||||
cpu->bankedRegisters[i][0] = 0;
|
|
||||||
cpu->bankedRegisters[i][1] = 0;
|
|
||||||
cpu->bankedRegisters[i][2] = 0;
|
|
||||||
cpu->bankedRegisters[i][3] = 0;
|
|
||||||
cpu->bankedRegisters[i][4] = 0;
|
|
||||||
cpu->bankedRegisters[i][5] = 0;
|
|
||||||
cpu->bankedRegisters[i][6] = 0;
|
|
||||||
cpu->bankedSPSRs[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu->privilegeMode = MODE_SYSTEM;
|
|
||||||
cpu->cpsr.packed = MODE_SYSTEM;
|
|
||||||
cpu->spsr.packed = 0;
|
|
||||||
|
|
||||||
cpu->shifterOperand = 0;
|
|
||||||
cpu->shifterCarryOut = 0;
|
|
||||||
|
|
||||||
ARMWritePC(cpu);
|
|
||||||
|
|
||||||
cpu->cycles = 0;
|
|
||||||
cpu->nextEvent = 0;
|
|
||||||
cpu->halted = 0;
|
|
||||||
|
|
||||||
cpu->irqh.reset(cpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMRaiseFIQ(struct ARMCore* cpu) {
|
|
||||||
if (cpu->cpsr.f) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
union PSR cpsr = cpu->cpsr;
|
|
||||||
ARMSetPrivilegeMode(cpu, MODE_FIQ);
|
|
||||||
cpu->cpsr.priv = MODE_FIQ;
|
|
||||||
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC];
|
|
||||||
cpu->gprs[ARM_PC] = BASE_FIQ;
|
|
||||||
cpu->cycles += ARMWritePC(cpu);
|
|
||||||
cpu->spsr = cpsr;
|
|
||||||
cpu->cpsr.f = 1;
|
|
||||||
cpu->cpsr.i = 1;
|
|
||||||
cpu->halted = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMRaiseIRQ(struct ARMCore* cpu) {
|
|
||||||
if (cpu->cpsr.i) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
union PSR cpsr = cpu->cpsr;
|
|
||||||
ARMSetPrivilegeMode(cpu, MODE_IRQ);
|
|
||||||
cpu->cpsr.priv = MODE_IRQ;
|
|
||||||
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC];
|
|
||||||
cpu->gprs[ARM_PC] = BASE_IRQ;
|
|
||||||
cpu->cycles += ARMWritePC(cpu);
|
|
||||||
cpu->spsr = cpsr;
|
|
||||||
cpu->cpsr.i = 1;
|
|
||||||
cpu->halted = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMRaiseSWI(struct ARMCore* cpu) {
|
|
||||||
union PSR cpsr = cpu->cpsr;
|
|
||||||
ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
|
|
||||||
cpu->cpsr.priv = MODE_SUPERVISOR;
|
|
||||||
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - 4;
|
|
||||||
cpu->gprs[ARM_PC] = BASE_SWI;
|
|
||||||
cpu->cycles += ARMWritePC(cpu);
|
|
||||||
cpu->spsr = cpsr;
|
|
||||||
cpu->cpsr.i = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMRaiseUndefined(struct ARMCore* cpu) {
|
|
||||||
union PSR cpsr = cpu->cpsr;
|
|
||||||
ARMSetPrivilegeMode(cpu, MODE_UNDEFINED);
|
|
||||||
cpu->cpsr.priv = MODE_UNDEFINED;
|
|
||||||
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - 4;
|
|
||||||
cpu->gprs[ARM_PC] = BASE_UNDEF;
|
|
||||||
cpu->cycles += ARMWritePC(cpu);
|
|
||||||
cpu->spsr = cpsr;
|
|
||||||
cpu->cpsr.i = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void ARMStep(struct ARMCore* cpu) {
|
|
||||||
uint32_t opcode = cpu->prefetch[0];
|
|
||||||
cpu->prefetch[0] = cpu->prefetch[1];
|
|
||||||
cpu->gprs[ARM_PC] += 4;
|
|
||||||
cpu->prefetch[1] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL);
|
|
||||||
|
|
||||||
unsigned condition = opcode >> 28;
|
|
||||||
if (condition != 0xE) {
|
|
||||||
bool conditionMet = false;
|
|
||||||
switch (condition) {
|
|
||||||
case 0x0:
|
|
||||||
conditionMet = ARM_COND_EQ;
|
|
||||||
break;
|
|
||||||
case 0x1:
|
|
||||||
conditionMet = ARM_COND_NE;
|
|
||||||
break;
|
|
||||||
case 0x2:
|
|
||||||
conditionMet = ARM_COND_CS;
|
|
||||||
break;
|
|
||||||
case 0x3:
|
|
||||||
conditionMet = ARM_COND_CC;
|
|
||||||
break;
|
|
||||||
case 0x4:
|
|
||||||
conditionMet = ARM_COND_MI;
|
|
||||||
break;
|
|
||||||
case 0x5:
|
|
||||||
conditionMet = ARM_COND_PL;
|
|
||||||
break;
|
|
||||||
case 0x6:
|
|
||||||
conditionMet = ARM_COND_VS;
|
|
||||||
break;
|
|
||||||
case 0x7:
|
|
||||||
conditionMet = ARM_COND_VC;
|
|
||||||
break;
|
|
||||||
case 0x8:
|
|
||||||
conditionMet = ARM_COND_HI;
|
|
||||||
break;
|
|
||||||
case 0x9:
|
|
||||||
conditionMet = ARM_COND_LS;
|
|
||||||
break;
|
|
||||||
case 0xA:
|
|
||||||
conditionMet = ARM_COND_GE;
|
|
||||||
break;
|
|
||||||
case 0xB:
|
|
||||||
conditionMet = ARM_COND_LT;
|
|
||||||
break;
|
|
||||||
case 0xC:
|
|
||||||
conditionMet = ARM_COND_GT;
|
|
||||||
break;
|
|
||||||
case 0xD:
|
|
||||||
conditionMet = ARM_COND_LE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!conditionMet) {
|
|
||||||
cpu->cycles += ARM_PREFETCH_CYCLES;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ARMInstruction instruction = _armTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)];
|
|
||||||
instruction(cpu, opcode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMRun(struct ARMCore* cpu) {
|
|
||||||
ARMStep(cpu);
|
|
||||||
if (cpu->cycles >= cpu->nextEvent) {
|
|
||||||
cpu->irqh.processEvents(cpu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMRunLoop(struct ARMCore* cpu) {
|
|
||||||
while (cpu->cycles < cpu->nextEvent) {
|
|
||||||
ARMStep(cpu);
|
|
||||||
}
|
|
||||||
cpu->irqh.processEvents(cpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMRunFake(struct ARMCore* cpu, uint32_t opcode) {
|
|
||||||
cpu->gprs[ARM_PC] -= 4;
|
|
||||||
cpu->prefetch[1] = cpu->prefetch[0];
|
|
||||||
cpu->prefetch[0] = opcode;
|
|
||||||
}
|
|
174
WindCore/arm.h
174
WindCore/arm.h
|
@ -1,174 +0,0 @@
|
||||||
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
#ifndef ARM_H
|
|
||||||
#define ARM_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
CXX_GUARD_START
|
|
||||||
|
|
||||||
// #include <mgba/core/cpu.h>
|
|
||||||
|
|
||||||
enum {
|
|
||||||
ARM_SP = 13,
|
|
||||||
ARM_LR = 14,
|
|
||||||
ARM_PC = 15
|
|
||||||
};
|
|
||||||
|
|
||||||
enum PrivilegeMode {
|
|
||||||
MODE_USER = 0x10,
|
|
||||||
MODE_FIQ = 0x11,
|
|
||||||
MODE_IRQ = 0x12,
|
|
||||||
MODE_SUPERVISOR = 0x13,
|
|
||||||
MODE_ABORT = 0x17,
|
|
||||||
MODE_UNDEFINED = 0x1B,
|
|
||||||
MODE_SYSTEM = 0x1F
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ExecutionVector {
|
|
||||||
BASE_RESET = 0x00000000,
|
|
||||||
BASE_UNDEF = 0x00000004,
|
|
||||||
BASE_SWI = 0x00000008,
|
|
||||||
BASE_PABT = 0x0000000C,
|
|
||||||
BASE_DABT = 0x00000010,
|
|
||||||
BASE_IRQ = 0x00000018,
|
|
||||||
BASE_FIQ = 0x0000001C
|
|
||||||
};
|
|
||||||
|
|
||||||
enum RegisterBank {
|
|
||||||
BANK_NONE = 0,
|
|
||||||
BANK_FIQ = 1,
|
|
||||||
BANK_IRQ = 2,
|
|
||||||
BANK_SUPERVISOR = 3,
|
|
||||||
BANK_ABORT = 4,
|
|
||||||
BANK_UNDEFINED = 5
|
|
||||||
};
|
|
||||||
|
|
||||||
enum LSMDirection {
|
|
||||||
LSM_B = 1,
|
|
||||||
LSM_D = 2,
|
|
||||||
LSM_IA = 0,
|
|
||||||
LSM_IB = 1,
|
|
||||||
LSM_DA = 2,
|
|
||||||
LSM_DB = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ARMCore;
|
|
||||||
|
|
||||||
union PSR {
|
|
||||||
struct {
|
|
||||||
#if defined(__POWERPC__) || defined(__PPC__)
|
|
||||||
unsigned n : 1;
|
|
||||||
unsigned z : 1;
|
|
||||||
unsigned c : 1;
|
|
||||||
unsigned v : 1;
|
|
||||||
unsigned unused : 20;
|
|
||||||
unsigned i : 1;
|
|
||||||
unsigned f : 1;
|
|
||||||
unsigned t : 1;
|
|
||||||
unsigned priv : 5;
|
|
||||||
#else
|
|
||||||
unsigned priv : 5;
|
|
||||||
unsigned t : 1;
|
|
||||||
unsigned f : 1;
|
|
||||||
unsigned i : 1;
|
|
||||||
unsigned unused : 20;
|
|
||||||
unsigned v : 1;
|
|
||||||
unsigned c : 1;
|
|
||||||
unsigned z : 1;
|
|
||||||
unsigned n : 1;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
|
||||||
#if defined(__BIG_ENDIAN__)
|
|
||||||
uint8_t flags;
|
|
||||||
uint8_t status;
|
|
||||||
uint8_t extension;
|
|
||||||
uint8_t control;
|
|
||||||
#else
|
|
||||||
uint8_t control;
|
|
||||||
uint8_t extension;
|
|
||||||
uint8_t status;
|
|
||||||
uint8_t flags;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
int32_t packed;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ARMMemory {
|
|
||||||
uint32_t (*load32)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
|
||||||
uint32_t (*load16)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
|
||||||
uint32_t (*load8)(struct ARMCore*, uint32_t address, int* cycleCounter);
|
|
||||||
|
|
||||||
void (*store32)(struct ARMCore*, uint32_t address, int32_t value, int* cycleCounter);
|
|
||||||
void (*store16)(struct ARMCore*, uint32_t address, int16_t value, int* cycleCounter);
|
|
||||||
void (*store8)(struct ARMCore*, uint32_t address, int8_t value, int* cycleCounter);
|
|
||||||
|
|
||||||
uint32_t (*loadMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction,
|
|
||||||
int* cycleCounter);
|
|
||||||
uint32_t (*storeMultiple)(struct ARMCore*, uint32_t baseAddress, int mask, enum LSMDirection direction,
|
|
||||||
int* cycleCounter);
|
|
||||||
|
|
||||||
uint32_t activeSeqCycles32;
|
|
||||||
// uint32_t activeSeqCycles16;
|
|
||||||
uint32_t activeNonseqCycles32;
|
|
||||||
// uint32_t activeNonseqCycles16;
|
|
||||||
int32_t (*stall)(struct ARMCore*, int32_t wait);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ARMInterruptHandler {
|
|
||||||
void (*reset)(struct ARMCore* cpu);
|
|
||||||
void (*processEvents)(struct ARMCore* cpu);
|
|
||||||
// void (*swi16)(struct ARMCore* cpu, int immediate);
|
|
||||||
void (*swi32)(struct ARMCore* cpu, int immediate);
|
|
||||||
void (*hitIllegal)(struct ARMCore* cpu, uint32_t opcode);
|
|
||||||
// void (*bkpt16)(struct ARMCore* cpu, int immediate);
|
|
||||||
void (*bkpt32)(struct ARMCore* cpu, int immediate);
|
|
||||||
void (*readCPSR)(struct ARMCore* cpu);
|
|
||||||
|
|
||||||
void (*hitStub)(struct ARMCore* cpu, uint32_t opcode);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ARMCore {
|
|
||||||
int32_t gprs[16];
|
|
||||||
union PSR cpsr;
|
|
||||||
union PSR spsr;
|
|
||||||
|
|
||||||
int64_t cycles;
|
|
||||||
int64_t nextEvent;
|
|
||||||
int halted;
|
|
||||||
|
|
||||||
int32_t bankedRegisters[6][7];
|
|
||||||
int32_t bankedSPSRs[6];
|
|
||||||
|
|
||||||
int32_t shifterOperand;
|
|
||||||
int32_t shifterCarryOut;
|
|
||||||
|
|
||||||
uint32_t prefetch[2];
|
|
||||||
enum PrivilegeMode privilegeMode;
|
|
||||||
|
|
||||||
struct ARMMemory memory;
|
|
||||||
struct ARMInterruptHandler irqh;
|
|
||||||
|
|
||||||
void *owner;
|
|
||||||
};
|
|
||||||
|
|
||||||
void ARMReset(struct ARMCore* cpu);
|
|
||||||
void ARMSetPrivilegeMode(struct ARMCore*, enum PrivilegeMode);
|
|
||||||
void ARMRaiseIRQ(struct ARMCore*);
|
|
||||||
void ARMRaiseFIQ(struct ARMCore*);
|
|
||||||
void ARMRaiseSWI(struct ARMCore*);
|
|
||||||
void ARMRaiseUndefined(struct ARMCore*);
|
|
||||||
|
|
||||||
void ARMRun(struct ARMCore* cpu);
|
|
||||||
void ARMRunLoop(struct ARMCore* cpu);
|
|
||||||
void ARMRunFake(struct ARMCore* cpu, uint32_t opcode);
|
|
||||||
|
|
||||||
CXX_GUARD_END
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -5,18 +5,19 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
|
||||||
CLPS7111::CLPS7111() : ARM710(false), pcCardController(this) {
|
namespace CLPS7111 {
|
||||||
|
Emulator::Emulator() : EmuBase(false), pcCardController(this) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t CLPS7111::getRTC() {
|
uint32_t Emulator::getRTC() {
|
||||||
return time(nullptr) - 946684800;
|
return time(nullptr) - 946684800;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t CLPS7111::readReg8(uint32_t reg) {
|
uint32_t Emulator::readReg8(uint32_t reg) {
|
||||||
if (reg == PADR) {
|
if (reg == PADR) {
|
||||||
return readKeyboard();
|
return readKeyboard(kScan);
|
||||||
} else if (reg == PBDR) {
|
} else if (reg == PBDR) {
|
||||||
return (portValues >> 16) & 0xFF;
|
return (portValues >> 16) & 0xFF;
|
||||||
} else if (reg == PDDR) {
|
} else if (reg == PDDR) {
|
||||||
|
@ -36,7 +37,7 @@ uint32_t CLPS7111::readReg8(uint32_t reg) {
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint32_t CLPS7111::readReg32(uint32_t reg) {
|
uint32_t Emulator::readReg32(uint32_t reg) {
|
||||||
if (reg == SYSCON1) {
|
if (reg == SYSCON1) {
|
||||||
uint32_t flg = 0;
|
uint32_t flg = 0;
|
||||||
if (tc1.config & Timer::PERIODIC) flg |= 0x10;
|
if (tc1.config & Timer::PERIODIC) flg |= 0x10;
|
||||||
|
@ -80,7 +81,7 @@ uint32_t CLPS7111::readReg32(uint32_t reg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLPS7111::writeReg8(uint32_t reg, uint8_t value) {
|
void Emulator::writeReg8(uint32_t reg, uint8_t value) {
|
||||||
if (reg == PADR) {
|
if (reg == PADR) {
|
||||||
uint32_t oldPorts = portValues;
|
uint32_t oldPorts = portValues;
|
||||||
portValues &= 0x00FFFFFF;
|
portValues &= 0x00FFFFFF;
|
||||||
|
@ -126,7 +127,7 @@ void CLPS7111::writeReg8(uint32_t reg, uint8_t value) {
|
||||||
log("RegWrite8 unknown:: pc=%08x reg=%03x value=%02x", getRealPC(), reg, value);
|
log("RegWrite8 unknown:: pc=%08x reg=%03x value=%02x", getRealPC(), reg, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void CLPS7111::writeReg32(uint32_t reg, uint32_t value) {
|
void Emulator::writeReg32(uint32_t reg, uint32_t value) {
|
||||||
if (reg == SYSCON1) {
|
if (reg == SYSCON1) {
|
||||||
kScan = value & 0xF;
|
kScan = value & 0xF;
|
||||||
tc1.config = Timer::ENABLED; // always on with PS-7111!
|
tc1.config = Timer::ENABLED; // always on with PS-7111!
|
||||||
|
@ -178,18 +179,7 @@ void CLPS7111::writeReg32(uint32_t reg, uint32_t value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLPS7111::isPhysAddressValid(uint32_t physAddress) const {
|
MaybeU32 Emulator::readPhysical(uint32_t physAddr, ValueSize valueSize) {
|
||||||
uint8_t region = (physAddress >> 24) & 0xF1;
|
|
||||||
switch (region) {
|
|
||||||
case 0: return true;
|
|
||||||
case 0x80: return (physAddress <= 0x80000FFF);
|
|
||||||
case 0xC0: return true;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MaybeU32 CLPS7111::readPhysical(uint32_t physAddr, ValueSize valueSize) {
|
|
||||||
uint8_t region = (physAddr >> 28);
|
uint8_t region = (physAddr >> 28);
|
||||||
if (valueSize == V8) {
|
if (valueSize == V8) {
|
||||||
if (region == 0)
|
if (region == 0)
|
||||||
|
@ -226,7 +216,7 @@ MaybeU32 CLPS7111::readPhysical(uint32_t physAddr, ValueSize valueSize) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLPS7111::writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) {
|
bool Emulator::writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) {
|
||||||
uint8_t region = (physAddr >> 28);
|
uint8_t region = (physAddr >> 28);
|
||||||
if (valueSize == V8) {
|
if (valueSize == V8) {
|
||||||
if (region == 0xC)
|
if (region == 0xC)
|
||||||
|
@ -256,7 +246,7 @@ bool CLPS7111::writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void CLPS7111::configure() {
|
void Emulator::configure() {
|
||||||
if (configured) return;
|
if (configured) return;
|
||||||
configured = true;
|
configured = true;
|
||||||
|
|
||||||
|
@ -273,13 +263,11 @@ void CLPS7111::configure() {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLPS7111::loadROM(const char *path) {
|
void Emulator::loadROM(uint8_t *buffer, size_t size) {
|
||||||
FILE *f = fopen(path, "rb");
|
memcpy(ROM, buffer, min(size, sizeof(ROM)));
|
||||||
fread(ROM, 1, sizeof(ROM), f);
|
|
||||||
fclose(f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLPS7111::executeUntil(int64_t cycles) {
|
void Emulator::executeUntil(int64_t cycles) {
|
||||||
if (!configured)
|
if (!configured)
|
||||||
configure();
|
configure();
|
||||||
|
|
||||||
|
@ -333,23 +321,8 @@ void CLPS7111::executeUntil(int64_t cycles) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLPS7111::dumpRAM(const char *path) {
|
|
||||||
FILE *f = fopen(path, "wb");
|
|
||||||
fwrite(MemoryBlockC0, 1, sizeof(MemoryBlockC0), f);
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const char *Emulator::identifyObjectCon(uint32_t ptr) {
|
||||||
|
|
||||||
void CLPS7111::printRegs() {
|
|
||||||
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 *CLPS7111::identifyObjectCon(uint32_t ptr) {
|
|
||||||
if (ptr == readVirtualDebug(0x80000880, V32).value()) return "process";
|
if (ptr == readVirtualDebug(0x80000880, V32).value()) return "process";
|
||||||
if (ptr == readVirtualDebug(0x80000884, V32).value()) return "thread";
|
if (ptr == readVirtualDebug(0x80000884, V32).value()) return "thread";
|
||||||
if (ptr == readVirtualDebug(0x80000888, V32).value()) return "chunk";
|
if (ptr == readVirtualDebug(0x80000888, V32).value()) return "chunk";
|
||||||
|
@ -366,7 +339,7 @@ const char *CLPS7111::identifyObjectCon(uint32_t ptr) {
|
||||||
return "???";
|
return "???";
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLPS7111::fetchStr(uint32_t str, char *buf) {
|
void Emulator::fetchStr(uint32_t str, char *buf) {
|
||||||
if (str == 0) {
|
if (str == 0) {
|
||||||
strcpy(buf, "<NULL>");
|
strcpy(buf, "<NULL>");
|
||||||
return;
|
return;
|
||||||
|
@ -378,15 +351,15 @@ void CLPS7111::fetchStr(uint32_t str, char *buf) {
|
||||||
buf[size] = 0;
|
buf[size] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLPS7111::fetchName(uint32_t obj, char *buf) {
|
void Emulator::fetchName(uint32_t obj, char *buf) {
|
||||||
fetchStr(readVirtualDebug(obj + 0x10, V32).value(), buf);
|
fetchStr(readVirtualDebug(obj + 0x10, V32).value(), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLPS7111::fetchProcessFilename(uint32_t obj, char *buf) {
|
void Emulator::fetchProcessFilename(uint32_t obj, char *buf) {
|
||||||
fetchStr(readVirtualDebug(obj + 0x3C, V32).value(), buf);
|
fetchStr(readVirtualDebug(obj + 0x3C, V32).value(), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLPS7111::debugPC(uint32_t pc) {
|
void Emulator::debugPC(uint32_t pc) {
|
||||||
char objName[1000];
|
char objName[1000];
|
||||||
if (pc == 0x32304) {
|
if (pc == 0x32304) {
|
||||||
// CObjectCon::AddL()
|
// CObjectCon::AddL()
|
||||||
|
@ -431,41 +404,48 @@ void CLPS7111::debugPC(uint32_t pc) {
|
||||||
log("DPlatChunkHw MAPPING: v:%08x p:%08x size:%08x arg:%08x",
|
log("DPlatChunkHw MAPPING: v:%08x p:%08x size:%08x arg:%08x",
|
||||||
virtAddr, physAddr, regionSize, a);
|
virtAddr, physAddr, regionSize, a);
|
||||||
}
|
}
|
||||||
// if (pc == 0x3B250) {
|
|
||||||
// log("DBG 5003B250: pc=%08x lr=%08x sp=%08x", getRealPC(), getGPR(14), getGPR(13));
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const uint8_t *CLPS7111::getLCDBuffer() const {
|
int Emulator::getLCDWidth() const {
|
||||||
if ((lcdAddress >> 24) == 0xC0)
|
return 320;
|
||||||
return &MemoryBlockC0[lcdAddress & MemoryBlockMask];
|
}
|
||||||
|
int Emulator::getLCDHeight() const {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
void Emulator::readLCDIntoBuffer(uint8_t **lines) const {
|
||||||
|
if (lcdAddress == 0xC0000000) {
|
||||||
|
int width = 320, height = 200;
|
||||||
|
int bpp = 1;
|
||||||
|
if (lcdControl & 0x40000000) bpp = 2;
|
||||||
|
if (lcdControl & 0x80000000) bpp = 4;
|
||||||
|
int ppb = 8 / bpp;
|
||||||
|
|
||||||
|
// build our image out
|
||||||
|
int lineWidth = (width * bpp) / 8;
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
int lineOffs = lineWidth * y;
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
uint8_t byte = MemoryBlockC0[lineOffs + (x / ppb)];
|
||||||
|
int shift = (x & (ppb - 1)) * bpp;
|
||||||
|
int mask = (1 << bpp) - 1;
|
||||||
|
int palIdx = (byte >> shift) & mask;
|
||||||
|
int palValue;
|
||||||
|
if (bpp == 1)
|
||||||
|
palValue = palIdx * 255;
|
||||||
else
|
else
|
||||||
return nullptr;
|
palValue = (lcdPalette >> (palIdx * 4)) & 0xF;
|
||||||
}
|
|
||||||
|
|
||||||
|
palValue |= (palValue << 4);
|
||||||
uint8_t CLPS7111::readKeyboard() {
|
lines[y][x] = palValue ^ 0xFF;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void CLPS7111::diffPorts(uint32_t oldval, uint32_t newval) {
|
void Emulator::diffPorts(uint32_t oldval, uint32_t newval) {
|
||||||
uint32_t changes = oldval ^ newval;
|
uint32_t changes = oldval ^ newval;
|
||||||
if (changes & 1) log("PRT E0: %d", newval&1);
|
if (changes & 1) log("PRT E0: %d", newval&1);
|
||||||
if (changes & 2) log("PRT E1: %d", newval&2);
|
if (changes & 2) log("PRT E1: %d", newval&2);
|
||||||
|
@ -486,12 +466,5 @@ void CLPS7111::diffPorts(uint32_t oldval, uint32_t newval) {
|
||||||
if (changes & 0x200000) log("PRT B5: %d", newval&0x200000);
|
if (changes & 0x200000) log("PRT B5: %d", newval&0x200000);
|
||||||
if (changes & 0x400000) log("PRT B6: %d", newval&0x400000);
|
if (changes & 0x400000) log("PRT B6: %d", newval&0x400000);
|
||||||
if (changes & 0x800000) log("PRT B7: %d", newval&0x800000);
|
if (changes & 0x800000) log("PRT B7: %d", newval&0x800000);
|
||||||
if (changes & 0x1000000) log("PRT A0: %d", newval&0x1000000);
|
}
|
||||||
if (changes & 0x2000000) log("PRT A1: %d", newval&0x2000000);
|
|
||||||
if (changes & 0x4000000) log("PRT A2: %d", newval&0x4000000);
|
|
||||||
if (changes & 0x8000000) log("PRT A3: %d", newval&0x8000000);
|
|
||||||
if (changes & 0x10000000) log("PRT A4: %d", newval&0x10000000);
|
|
||||||
if (changes & 0x20000000) log("PRT A5: %d", newval&0x20000000);
|
|
||||||
if (changes & 0x40000000) log("PRT A6: %d", newval&0x40000000);
|
|
||||||
if (changes & 0x80000000) log("PRT A7: %d", newval&0x80000000);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "arm710.h"
|
#include "emubase.h"
|
||||||
#include "clps7111_defs.h"
|
#include "clps7111_defs.h"
|
||||||
#include "clps7600.h"
|
#include "clps7600.h"
|
||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
#include "etna.h"
|
#include "etna.h"
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
class CLPS7111 : public ARM710 {
|
namespace CLPS7111 {
|
||||||
|
class Emulator : public EmuBase {
|
||||||
public:
|
public:
|
||||||
uint8_t ROM[0x800000];
|
uint8_t ROM[0x800000];
|
||||||
uint8_t ROM2[0x40000];
|
uint8_t ROM2[0x40000];
|
||||||
|
@ -26,13 +26,10 @@ private:
|
||||||
uint32_t rtcDiv = 0;
|
uint32_t rtcDiv = 0;
|
||||||
uint64_t lcdPalette = 0;
|
uint64_t lcdPalette = 0;
|
||||||
|
|
||||||
int64_t passedCycles = 0;
|
|
||||||
int64_t nextTickAt = 0;
|
|
||||||
Timer tc1, tc2;
|
Timer tc1, tc2;
|
||||||
CLPS7600 pcCardController;
|
CLPS7600 pcCardController;
|
||||||
bool halted = false, asleep = false;
|
bool halted = false, asleep = false;
|
||||||
|
|
||||||
std::unordered_set<uint32_t> _breakpoints;
|
|
||||||
|
|
||||||
uint32_t getRTC();
|
uint32_t getRTC();
|
||||||
|
|
||||||
|
@ -42,19 +39,13 @@ private:
|
||||||
void writeReg32(uint32_t reg, uint32_t value);
|
void writeReg32(uint32_t reg, uint32_t value);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool isPhysAddressValid(uint32_t addr) const;
|
|
||||||
MaybeU32 readPhysical(uint32_t physAddr, ValueSize valueSize) override;
|
MaybeU32 readPhysical(uint32_t physAddr, ValueSize valueSize) override;
|
||||||
bool writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) override;
|
bool writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) override;
|
||||||
|
|
||||||
const uint8_t *getLCDBuffer() const;
|
|
||||||
uint64_t getLCDPalette() const { return lcdPalette; }
|
|
||||||
uint32_t getLCDControl() const { return lcdControl; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool configured = false;
|
bool configured = false;
|
||||||
void configure();
|
void configure();
|
||||||
|
|
||||||
void printRegs();
|
|
||||||
const char *identifyObjectCon(uint32_t ptr);
|
const char *identifyObjectCon(uint32_t ptr);
|
||||||
void fetchStr(uint32_t str, char *buf);
|
void fetchStr(uint32_t str, char *buf);
|
||||||
void fetchName(uint32_t obj, char *buf);
|
void fetchName(uint32_t obj, char *buf);
|
||||||
|
@ -62,15 +53,13 @@ private:
|
||||||
void debugPC(uint32_t pc);
|
void debugPC(uint32_t pc);
|
||||||
void diffPorts(uint32_t oldval, uint32_t newval);
|
void diffPorts(uint32_t oldval, uint32_t newval);
|
||||||
|
|
||||||
uint8_t readKeyboard();
|
|
||||||
public:
|
public:
|
||||||
bool keyboardKeys[8*7] = {0};
|
Emulator();
|
||||||
|
void loadROM(uint8_t *buffer, size_t size) override;
|
||||||
public:
|
void executeUntil(int64_t cycles) override;
|
||||||
CLPS7111();
|
int32_t getClockSpeed() const override { return CLOCK_SPEED; }
|
||||||
void loadROM(const char *path);
|
int getLCDWidth() const override;
|
||||||
void dumpRAM(const char *path);
|
int getLCDHeight() const override;
|
||||||
void executeUntil(int64_t cycles);
|
void readLCDIntoBuffer(uint8_t **lines) const override;
|
||||||
std::unordered_set<uint32_t> &breakpoints() { return _breakpoints; }
|
|
||||||
uint64_t currentCycles() const { return passedCycles; }
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace CLPS7111 {
|
||||||
enum {
|
enum {
|
||||||
CLOCK_SPEED = 0x4800*1000,
|
CLOCK_SPEED = 0x4800*1000,
|
||||||
TICK_INTERVAL = CLOCK_SPEED / 64
|
TICK_INTERVAL = CLOCK_SPEED / 64
|
||||||
|
@ -30,7 +31,7 @@ enum Interrupt {
|
||||||
IRQ_INTERRUPTS = 0xFFF0
|
IRQ_INTERRUPTS = 0xFFF0
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Clps7111Reg {
|
enum Register {
|
||||||
PADR = 0,
|
PADR = 0,
|
||||||
PBDR = 1,
|
PBDR = 1,
|
||||||
PDDR = 3,
|
PDDR = 3,
|
||||||
|
@ -78,4 +79,5 @@ enum Clps7111Reg {
|
||||||
UBRLCR2 = 0x14C0,
|
UBRLCR2 = 0x14C0,
|
||||||
KBDEOI = 0x1700
|
KBDEOI = 0x1700
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
|
|
||||||
#include "arm.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
CXX_GUARD_START
|
CXX_GUARD_START
|
||||||
|
|
||||||
#include "arm.h"
|
|
||||||
|
|
||||||
// Bit 0: a register is involved with this operand
|
// Bit 0: a register is involved with this operand
|
||||||
// Bit 1: an immediate is invovled with this operand
|
// Bit 1: an immediate is invovled with this operand
|
||||||
// Bit 2: a memory access is invovled with this operand
|
// Bit 2: a memory access is invovled with this operand
|
||||||
|
@ -186,6 +184,9 @@ enum ARMMnemonic {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
ARM_SP = 13,
|
||||||
|
ARM_LR = 14,
|
||||||
|
ARM_PC = 15,
|
||||||
ARM_CPSR = 16,
|
ARM_CPSR = 16,
|
||||||
ARM_SPSR = 17
|
ARM_SPSR = 17
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include "emubase.h"
|
||||||
|
|
||||||
|
uint8_t EmuBase::readKeyboard(int kScan) {
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
#include "arm710.h"
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
class EmuBase : public ARM710
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
std::unordered_set<uint32_t> _breakpoints;
|
||||||
|
int64_t passedCycles = 0;
|
||||||
|
int64_t nextTickAt = 0;
|
||||||
|
uint8_t readKeyboard(int kScan);
|
||||||
|
|
||||||
|
public:
|
||||||
|
EmuBase(bool isTVersion) : ARM710(isTVersion) { }
|
||||||
|
|
||||||
|
virtual void loadROM(uint8_t *buffer, size_t size) = 0;
|
||||||
|
virtual void executeUntil(int64_t cycles) = 0;
|
||||||
|
virtual int32_t getClockSpeed() const = 0;
|
||||||
|
virtual int getLCDWidth() const = 0;
|
||||||
|
virtual int getLCDHeight() const = 0;
|
||||||
|
virtual void readLCDIntoBuffer(uint8_t **lines) const = 0;
|
||||||
|
|
||||||
|
std::unordered_set<uint32_t> &breakpoints() { return _breakpoints; }
|
||||||
|
uint64_t currentCycles() const { return passedCycles; }
|
||||||
|
|
||||||
|
bool keyboardKeys[8*7] = {0};
|
||||||
|
};
|
||||||
|
|
|
@ -1,741 +0,0 @@
|
||||||
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
#include "isa-arm.h"
|
|
||||||
|
|
||||||
#include "arm.h"
|
|
||||||
#include "emitter-arm.h"
|
|
||||||
#include "isa-inlines.h"
|
|
||||||
|
|
||||||
#define PSR_USER_MASK 0xF0000000
|
|
||||||
#define PSR_PRIV_MASK 0x000000CF
|
|
||||||
#define PSR_STATE_MASK 0x00000020
|
|
||||||
|
|
||||||
// Addressing mode 1
|
|
||||||
static inline void _shiftLSL(struct ARMCore* cpu, uint32_t opcode) {
|
|
||||||
int rm = opcode & 0x0000000F;
|
|
||||||
if (opcode & 0x00000010) {
|
|
||||||
int rs = (opcode >> 8) & 0x0000000F;
|
|
||||||
++cpu->cycles;
|
|
||||||
int shift = cpu->gprs[rs];
|
|
||||||
if (rs == ARM_PC) {
|
|
||||||
shift += 4;
|
|
||||||
}
|
|
||||||
shift &= 0xFF;
|
|
||||||
int32_t shiftVal = cpu->gprs[rm];
|
|
||||||
if (rm == ARM_PC) {
|
|
||||||
shiftVal += 4;
|
|
||||||
}
|
|
||||||
if (!shift) {
|
|
||||||
cpu->shifterOperand = shiftVal;
|
|
||||||
cpu->shifterCarryOut = cpu->cpsr.c;
|
|
||||||
} else if (shift < 32) {
|
|
||||||
cpu->shifterOperand = shiftVal << shift;
|
|
||||||
cpu->shifterCarryOut = (shiftVal >> (32 - shift)) & 1;
|
|
||||||
} else if (shift == 32) {
|
|
||||||
cpu->shifterOperand = 0;
|
|
||||||
cpu->shifterCarryOut = shiftVal & 1;
|
|
||||||
} else {
|
|
||||||
cpu->shifterOperand = 0;
|
|
||||||
cpu->shifterCarryOut = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int immediate = (opcode & 0x00000F80) >> 7;
|
|
||||||
if (!immediate) {
|
|
||||||
cpu->shifterOperand = cpu->gprs[rm];
|
|
||||||
cpu->shifterCarryOut = cpu->cpsr.c;
|
|
||||||
} else {
|
|
||||||
cpu->shifterOperand = cpu->gprs[rm] << immediate;
|
|
||||||
cpu->shifterCarryOut = (cpu->gprs[rm] >> (32 - immediate)) & 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _shiftLSR(struct ARMCore* cpu, uint32_t opcode) {
|
|
||||||
int rm = opcode & 0x0000000F;
|
|
||||||
if (opcode & 0x00000010) {
|
|
||||||
int rs = (opcode >> 8) & 0x0000000F;
|
|
||||||
++cpu->cycles;
|
|
||||||
int shift = cpu->gprs[rs];
|
|
||||||
if (rs == ARM_PC) {
|
|
||||||
shift += 4;
|
|
||||||
}
|
|
||||||
shift &= 0xFF;
|
|
||||||
uint32_t shiftVal = cpu->gprs[rm];
|
|
||||||
if (rm == ARM_PC) {
|
|
||||||
shiftVal += 4;
|
|
||||||
}
|
|
||||||
if (!shift) {
|
|
||||||
cpu->shifterOperand = shiftVal;
|
|
||||||
cpu->shifterCarryOut = cpu->cpsr.c;
|
|
||||||
} else if (shift < 32) {
|
|
||||||
cpu->shifterOperand = shiftVal >> shift;
|
|
||||||
cpu->shifterCarryOut = (shiftVal >> (shift - 1)) & 1;
|
|
||||||
} else if (shift == 32) {
|
|
||||||
cpu->shifterOperand = 0;
|
|
||||||
cpu->shifterCarryOut = shiftVal >> 31;
|
|
||||||
} else {
|
|
||||||
cpu->shifterOperand = 0;
|
|
||||||
cpu->shifterCarryOut = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int immediate = (opcode & 0x00000F80) >> 7;
|
|
||||||
if (immediate) {
|
|
||||||
cpu->shifterOperand = ((uint32_t) cpu->gprs[rm]) >> immediate;
|
|
||||||
cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1;
|
|
||||||
} else {
|
|
||||||
cpu->shifterOperand = 0;
|
|
||||||
cpu->shifterCarryOut = ARM_SIGN(cpu->gprs[rm]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _shiftASR(struct ARMCore* cpu, uint32_t opcode) {
|
|
||||||
int rm = opcode & 0x0000000F;
|
|
||||||
if (opcode & 0x00000010) {
|
|
||||||
int rs = (opcode >> 8) & 0x0000000F;
|
|
||||||
++cpu->cycles;
|
|
||||||
int shift = cpu->gprs[rs];
|
|
||||||
if (rs == ARM_PC) {
|
|
||||||
shift += 4;
|
|
||||||
}
|
|
||||||
shift &= 0xFF;
|
|
||||||
int shiftVal = cpu->gprs[rm];
|
|
||||||
if (rm == ARM_PC) {
|
|
||||||
shiftVal += 4;
|
|
||||||
}
|
|
||||||
if (!shift) {
|
|
||||||
cpu->shifterOperand = shiftVal;
|
|
||||||
cpu->shifterCarryOut = cpu->cpsr.c;
|
|
||||||
} else if (shift < 32) {
|
|
||||||
cpu->shifterOperand = shiftVal >> shift;
|
|
||||||
cpu->shifterCarryOut = (shiftVal >> (shift - 1)) & 1;
|
|
||||||
} else if (cpu->gprs[rm] >> 31) {
|
|
||||||
cpu->shifterOperand = 0xFFFFFFFF;
|
|
||||||
cpu->shifterCarryOut = 1;
|
|
||||||
} else {
|
|
||||||
cpu->shifterOperand = 0;
|
|
||||||
cpu->shifterCarryOut = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int immediate = (opcode & 0x00000F80) >> 7;
|
|
||||||
if (immediate) {
|
|
||||||
cpu->shifterOperand = cpu->gprs[rm] >> immediate;
|
|
||||||
cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1;
|
|
||||||
} else {
|
|
||||||
cpu->shifterCarryOut = ARM_SIGN(cpu->gprs[rm]);
|
|
||||||
cpu->shifterOperand = cpu->shifterCarryOut;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _shiftROR(struct ARMCore* cpu, uint32_t opcode) {
|
|
||||||
int rm = opcode & 0x0000000F;
|
|
||||||
if (opcode & 0x00000010) {
|
|
||||||
int rs = (opcode >> 8) & 0x0000000F;
|
|
||||||
++cpu->cycles;
|
|
||||||
int shift = cpu->gprs[rs];
|
|
||||||
if (rs == ARM_PC) {
|
|
||||||
shift += 4;
|
|
||||||
}
|
|
||||||
shift &= 0xFF;
|
|
||||||
int shiftVal = cpu->gprs[rm];
|
|
||||||
if (rm == ARM_PC) {
|
|
||||||
shiftVal += 4;
|
|
||||||
}
|
|
||||||
int rotate = shift & 0x1F;
|
|
||||||
if (!shift) {
|
|
||||||
cpu->shifterOperand = shiftVal;
|
|
||||||
cpu->shifterCarryOut = cpu->cpsr.c;
|
|
||||||
} else if (rotate) {
|
|
||||||
cpu->shifterOperand = ROR(shiftVal, rotate);
|
|
||||||
cpu->shifterCarryOut = (shiftVal >> (rotate - 1)) & 1;
|
|
||||||
} else {
|
|
||||||
cpu->shifterOperand = shiftVal;
|
|
||||||
cpu->shifterCarryOut = ARM_SIGN(shiftVal);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int immediate = (opcode & 0x00000F80) >> 7;
|
|
||||||
if (immediate) {
|
|
||||||
cpu->shifterOperand = ROR(cpu->gprs[rm], immediate);
|
|
||||||
cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1;
|
|
||||||
} else {
|
|
||||||
// RRX
|
|
||||||
cpu->shifterOperand = (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1);
|
|
||||||
cpu->shifterCarryOut = cpu->gprs[rm] & 0x00000001;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _immediate(struct ARMCore* cpu, uint32_t opcode) {
|
|
||||||
int rotate = (opcode & 0x00000F00) >> 7;
|
|
||||||
int immediate = opcode & 0x000000FF;
|
|
||||||
if (!rotate) {
|
|
||||||
cpu->shifterOperand = immediate;
|
|
||||||
cpu->shifterCarryOut = cpu->cpsr.c;
|
|
||||||
} else {
|
|
||||||
cpu->shifterOperand = ROR(immediate, rotate);
|
|
||||||
cpu->shifterCarryOut = ARM_SIGN(cpu->shifterOperand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instruction definitions
|
|
||||||
// Beware pre-processor antics
|
|
||||||
|
|
||||||
ATTRIBUTE_NOINLINE static void _additionS(struct ARMCore* cpu, int32_t m, int32_t n, int32_t d) {
|
|
||||||
cpu->cpsr.flags = 0;
|
|
||||||
cpu->cpsr.n = ARM_SIGN(d);
|
|
||||||
cpu->cpsr.z = !d;
|
|
||||||
cpu->cpsr.c = ARM_CARRY_FROM(m, n, d);
|
|
||||||
cpu->cpsr.v = ARM_V_ADDITION(m, n, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
ATTRIBUTE_NOINLINE static void _subtractionS(struct ARMCore* cpu, int32_t m, int32_t n, int32_t d) {
|
|
||||||
cpu->cpsr.flags = 0;
|
|
||||||
cpu->cpsr.n = ARM_SIGN(d);
|
|
||||||
cpu->cpsr.z = !d;
|
|
||||||
cpu->cpsr.c = ARM_BORROW_FROM(m, n, d);
|
|
||||||
cpu->cpsr.v = ARM_V_SUBTRACTION(m, n, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
|
||||||
cpu->cpsr.n = ARM_SIGN(d);
|
|
||||||
cpu->cpsr.z = !d; \
|
|
||||||
cpu->cpsr.c = cpu->shifterCarryOut; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ARM_ADDITION_S(M, N, D) \
|
|
||||||
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
|
|
||||||
cpu->cpsr = cpu->spsr; \
|
|
||||||
_ARMReadCPSR(cpu); \
|
|
||||||
} else { \
|
|
||||||
_additionS(cpu, M, N, D); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ARM_SUBTRACTION_S(M, N, D) \
|
|
||||||
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
|
|
||||||
cpu->cpsr = cpu->spsr; \
|
|
||||||
_ARMReadCPSR(cpu); \
|
|
||||||
} else { \
|
|
||||||
_subtractionS(cpu, M, N, D); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ARM_SUBTRACTION_CARRY_S(M, N, D, C) \
|
|
||||||
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
|
|
||||||
cpu->cpsr = cpu->spsr; \
|
|
||||||
_ARMReadCPSR(cpu); \
|
|
||||||
} else { \
|
|
||||||
cpu->cpsr.n = ARM_SIGN(D); \
|
|
||||||
cpu->cpsr.z = !(D); \
|
|
||||||
cpu->cpsr.c = ARM_BORROW_FROM_CARRY(M, N, D, C); \
|
|
||||||
cpu->cpsr.v = ARM_V_SUBTRACTION(M, N, D); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ARM_NEUTRAL_S(M, N, D) \
|
|
||||||
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
|
|
||||||
cpu->cpsr = cpu->spsr; \
|
|
||||||
_ARMReadCPSR(cpu); \
|
|
||||||
} else { \
|
|
||||||
_neutralS(cpu, D); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ARM_NEUTRAL_HI_S(DLO, DHI) \
|
|
||||||
cpu->cpsr.n = ARM_SIGN(DHI); \
|
|
||||||
cpu->cpsr.z = !((DHI) | (DLO));
|
|
||||||
|
|
||||||
#define ADDR_MODE_2_I_TEST (opcode & 0x00000F80)
|
|
||||||
#define ADDR_MODE_2_I ((opcode & 0x00000F80) >> 7)
|
|
||||||
#define ADDR_MODE_2_ADDRESS (address)
|
|
||||||
#define ADDR_MODE_2_RN (cpu->gprs[rn])
|
|
||||||
#define ADDR_MODE_2_RM (cpu->gprs[rm])
|
|
||||||
#define ADDR_MODE_2_IMMEDIATE (opcode & 0x00000FFF)
|
|
||||||
#define ADDR_MODE_2_INDEX(U_OP, M) (cpu->gprs[rn] U_OP M)
|
|
||||||
#define ADDR_MODE_2_WRITEBACK(ADDR) \
|
|
||||||
cpu->gprs[rn] = ADDR; \
|
|
||||||
if (UNLIKELY(rn == ARM_PC)) { \
|
|
||||||
currentCycles += ARMWritePC(cpu); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ADDR_MODE_2_WRITEBACK_PRE_STORE(WB)
|
|
||||||
#define ADDR_MODE_2_WRITEBACK_POST_STORE(WB) WB
|
|
||||||
#define ADDR_MODE_2_WRITEBACK_PRE_LOAD(WB) WB
|
|
||||||
#define ADDR_MODE_2_WRITEBACK_POST_LOAD(WB)
|
|
||||||
|
|
||||||
#define ADDR_MODE_2_LSL (cpu->gprs[rm] << ADDR_MODE_2_I)
|
|
||||||
#define ADDR_MODE_2_LSR (ADDR_MODE_2_I_TEST ? ((uint32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : 0)
|
|
||||||
#define ADDR_MODE_2_ASR (ADDR_MODE_2_I_TEST ? ((int32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : ((int32_t) cpu->gprs[rm]) >> 31)
|
|
||||||
#define ADDR_MODE_2_ROR (ADDR_MODE_2_I_TEST ? ROR(cpu->gprs[rm], ADDR_MODE_2_I) : (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1))
|
|
||||||
|
|
||||||
#define ADDR_MODE_3_ADDRESS ADDR_MODE_2_ADDRESS
|
|
||||||
#define ADDR_MODE_3_RN ADDR_MODE_2_RN
|
|
||||||
#define ADDR_MODE_3_RM ADDR_MODE_2_RM
|
|
||||||
#define ADDR_MODE_3_IMMEDIATE (((opcode & 0x00000F00) >> 4) | (opcode & 0x0000000F))
|
|
||||||
#define ADDR_MODE_3_INDEX(U_OP, M) ADDR_MODE_2_INDEX(U_OP, M)
|
|
||||||
#define ADDR_MODE_3_WRITEBACK(ADDR) ADDR_MODE_2_WRITEBACK(ADDR)
|
|
||||||
|
|
||||||
#define ADDR_MODE_4_WRITEBACK_LDM \
|
|
||||||
if (!((1 << rn) & rs)) { \
|
|
||||||
cpu->gprs[rn] = address; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ADDR_MODE_4_WRITEBACK_STM cpu->gprs[rn] = address;
|
|
||||||
|
|
||||||
#define ARM_LOAD_POST_BODY \
|
|
||||||
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; \
|
|
||||||
if (rd == ARM_PC) { \
|
|
||||||
currentCycles += ARMWritePC(cpu); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ARM_STORE_POST_BODY \
|
|
||||||
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32;
|
|
||||||
|
|
||||||
#define DEFINE_INSTRUCTION_ARM(NAME, BODY) \
|
|
||||||
static void _ARMInstruction ## NAME (struct ARMCore* cpu, uint32_t opcode) { \
|
|
||||||
int currentCycles = ARM_PREFETCH_CYCLES; \
|
|
||||||
BODY; \
|
|
||||||
cpu->cycles += currentCycles; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DEFINE_ALU_INSTRUCTION_EX_ARM(NAME, S_BODY, SHIFTER, BODY) \
|
|
||||||
DEFINE_INSTRUCTION_ARM(NAME, \
|
|
||||||
int rd = (opcode >> 12) & 0xF; \
|
|
||||||
int rn = (opcode >> 16) & 0xF; \
|
|
||||||
UNUSED(rn); \
|
|
||||||
SHIFTER(cpu, opcode); \
|
|
||||||
BODY; \
|
|
||||||
S_BODY; \
|
|
||||||
if (rd == ARM_PC) { \
|
|
||||||
currentCycles += ARMWritePC(cpu); \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define DEFINE_ALU_INSTRUCTION_ARM(NAME, S_BODY, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSL, , _shiftLSL, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_LSL, S_BODY, _shiftLSL, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSR, , _shiftLSR, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_LSR, S_BODY, _shiftLSR, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ASR, , _shiftASR, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_ASR, S_BODY, _shiftASR, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ROR, , _shiftROR, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## S_ROR, S_BODY, _shiftROR, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## I, , _immediate, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## SI, S_BODY, _immediate, BODY)
|
|
||||||
|
|
||||||
#define DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(NAME, S_BODY, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSL, S_BODY, _shiftLSL, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _LSR, S_BODY, _shiftLSR, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ASR, S_BODY, _shiftASR, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## _ROR, S_BODY, _shiftROR, BODY) \
|
|
||||||
DEFINE_ALU_INSTRUCTION_EX_ARM(NAME ## I, S_BODY, _immediate, BODY)
|
|
||||||
|
|
||||||
#define DEFINE_MULTIPLY_INSTRUCTION_EX_ARM(NAME, BODY, S_BODY) \
|
|
||||||
DEFINE_INSTRUCTION_ARM(NAME, \
|
|
||||||
int rd = (opcode >> 16) & 0xF; \
|
|
||||||
int rs = (opcode >> 8) & 0xF; \
|
|
||||||
int rm = opcode & 0xF; \
|
|
||||||
if (rd == ARM_PC) { \
|
|
||||||
return; \
|
|
||||||
} \
|
|
||||||
ARM_WAIT_MUL(cpu->gprs[rs]); \
|
|
||||||
BODY; \
|
|
||||||
S_BODY; \
|
|
||||||
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32)
|
|
||||||
|
|
||||||
#define DEFINE_MULTIPLY_INSTRUCTION_2_EX_ARM(NAME, BODY, S_BODY, WAIT) \
|
|
||||||
DEFINE_INSTRUCTION_ARM(NAME, \
|
|
||||||
int rd = (opcode >> 12) & 0xF; \
|
|
||||||
int rdHi = (opcode >> 16) & 0xF; \
|
|
||||||
int rs = (opcode >> 8) & 0xF; \
|
|
||||||
int rm = opcode & 0xF; \
|
|
||||||
if (rdHi == ARM_PC || rd == ARM_PC) { \
|
|
||||||
return; \
|
|
||||||
} \
|
|
||||||
currentCycles += cpu->memory.stall(cpu, WAIT); \
|
|
||||||
BODY; \
|
|
||||||
S_BODY; \
|
|
||||||
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32)
|
|
||||||
|
|
||||||
#define DEFINE_MULTIPLY_INSTRUCTION_ARM(NAME, BODY, S_BODY) \
|
|
||||||
DEFINE_MULTIPLY_INSTRUCTION_EX_ARM(NAME, BODY, ) \
|
|
||||||
DEFINE_MULTIPLY_INSTRUCTION_EX_ARM(NAME ## S, BODY, S_BODY)
|
|
||||||
|
|
||||||
#define DEFINE_MULTIPLY_INSTRUCTION_2_ARM(NAME, BODY, S_BODY, WAIT) \
|
|
||||||
DEFINE_MULTIPLY_INSTRUCTION_2_EX_ARM(NAME, BODY, , WAIT) \
|
|
||||||
DEFINE_MULTIPLY_INSTRUCTION_2_EX_ARM(NAME ## S, BODY, S_BODY, WAIT)
|
|
||||||
|
|
||||||
#define DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, ADDRESS, WRITEBACK, LS, BODY) \
|
|
||||||
DEFINE_INSTRUCTION_ARM(NAME, \
|
|
||||||
uint32_t address; \
|
|
||||||
int rn = (opcode >> 16) & 0xF; \
|
|
||||||
int rd = (opcode >> 12) & 0xF; \
|
|
||||||
int rm = opcode & 0xF; \
|
|
||||||
UNUSED(rm); \
|
|
||||||
address = ADDRESS; \
|
|
||||||
ADDR_MODE_2_WRITEBACK_PRE_ ## LS (WRITEBACK); \
|
|
||||||
BODY; \
|
|
||||||
ADDR_MODE_2_WRITEBACK_POST_ ## LS (WRITEBACK);)
|
|
||||||
|
|
||||||
#define DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME, SHIFTER, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, SHIFTER)), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## U, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, SHIFTER)), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## P, ADDR_MODE_2_INDEX(-, SHIFTER), , LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PW, ADDR_MODE_2_INDEX(-, SHIFTER), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PU, ADDR_MODE_2_INDEX(+, SHIFTER), , LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PUW, ADDR_MODE_2_INDEX(+, SHIFTER), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY)
|
|
||||||
|
|
||||||
#define DEFINE_LOAD_STORE_INSTRUCTION_ARM(NAME, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _LSL_, ADDR_MODE_2_LSL, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _LSR_, ADDR_MODE_2_LSR, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _ASR_, ADDR_MODE_2_ASR, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_SHIFTER_ARM(NAME ## _ROR_, ADDR_MODE_2_ROR, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## I, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IU, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IP, ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE), , LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPW, ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPU, ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE), , LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPUW, ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE), ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_ADDRESS), LS, BODY) \
|
|
||||||
|
|
||||||
#define DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(NAME, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(-, ADDR_MODE_3_RM)), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## U, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(+, ADDR_MODE_3_RM)), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## P, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_RM), , LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PW, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_RM), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PU, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_RM), , LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## PUW, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_RM), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## I, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(-, ADDR_MODE_3_IMMEDIATE)), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IU, ADDR_MODE_3_RN, ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_INDEX(+, ADDR_MODE_3_IMMEDIATE)), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IP, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_IMMEDIATE), , LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPW, ADDR_MODE_3_INDEX(-, ADDR_MODE_3_IMMEDIATE), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPU, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_IMMEDIATE), , LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IPUW, ADDR_MODE_3_INDEX(+, ADDR_MODE_3_IMMEDIATE), ADDR_MODE_3_WRITEBACK(ADDR_MODE_3_ADDRESS), LS, BODY) \
|
|
||||||
|
|
||||||
#define DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME, SHIFTER, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME, SHIFTER, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, ADDR_MODE_2_RM)), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## U, SHIFTER, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, ADDR_MODE_2_RM)), LS, BODY) \
|
|
||||||
|
|
||||||
#define DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(NAME, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _LSL_, ADDR_MODE_2_LSL, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _LSR_, ADDR_MODE_2_LSR, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _ASR_, ADDR_MODE_2_ASR, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _ROR_, ADDR_MODE_2_ROR, LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## I, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IU, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
|
|
||||||
|
|
||||||
#define ARM_MS_PRE(IS_LOAD) \
|
|
||||||
enum PrivilegeMode privilegeMode = cpu->privilegeMode; \
|
|
||||||
if (!(opcode & 0x8000) || !IS_LOAD) ARMSetPrivilegeMode(cpu, MODE_USER);
|
|
||||||
|
|
||||||
#define ARM_MS_POST(IS_LOAD) \
|
|
||||||
ARMSetPrivilegeMode(cpu, privilegeMode); \
|
|
||||||
if (IS_LOAD && (opcode & 0x8000)) { \
|
|
||||||
cpu->cpsr.packed = cpu->spsr.packed;\
|
|
||||||
ARMSetPrivilegeMode(cpu, cpu->cpsr.priv);\
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME, LS, WRITEBACK, S_PRE, S_POST, DIRECTION, POST_BODY) \
|
|
||||||
DEFINE_INSTRUCTION_ARM(NAME, \
|
|
||||||
int rn = (opcode >> 16) & 0xF; \
|
|
||||||
int rs = opcode & 0x0000FFFF; \
|
|
||||||
uint32_t address = cpu->gprs[rn]; \
|
|
||||||
S_PRE; \
|
|
||||||
address = cpu->memory. LS ## Multiple(cpu, address, rs, LSM_ ## DIRECTION, ¤tCycles); \
|
|
||||||
POST_BODY; \
|
|
||||||
WRITEBACK; \
|
|
||||||
S_POST; \
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(NAME, LS, IS_LOAD, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DA, LS, , , , DA, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , DA, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DB, LS, , , , DB, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , DB, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IA, LS, , , , IA, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , IA, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IB, LS, , , , IB, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , IB, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDA, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DA, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DA, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDB, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DB, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), DB, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIA, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IA, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IA, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIB, LS, , ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IB, POST_BODY) \
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE(IS_LOAD), ARM_MS_POST(IS_LOAD), IB, POST_BODY)
|
|
||||||
|
|
||||||
// Begin ALU definitions
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(ADD, ARM_ADDITION_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
|
||||||
int32_t n = cpu->gprs[rn];
|
|
||||||
cpu->gprs[rd] = n + cpu->shifterOperand;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(ADC, ARM_ADDITION_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
|
||||||
int32_t n = cpu->gprs[rn];
|
|
||||||
cpu->gprs[rd] = n + cpu->shifterOperand + cpu->cpsr.c;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(AND, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
|
||||||
cpu->gprs[rd] = cpu->gprs[rn] & cpu->shifterOperand;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(BIC, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
|
||||||
cpu->gprs[rd] = cpu->gprs[rn] & ~cpu->shifterOperand;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(CMN, ARM_ADDITION_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
|
||||||
int32_t aluOut = cpu->gprs[rn] + cpu->shifterOperand;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(CMP, ARM_SUBTRACTION_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
|
||||||
int32_t aluOut = cpu->gprs[rn] - cpu->shifterOperand;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(EOR, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
|
||||||
cpu->gprs[rd] = cpu->gprs[rn] ^ cpu->shifterOperand;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(MOV, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
|
||||||
cpu->gprs[rd] = cpu->shifterOperand;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(MVN, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
|
||||||
cpu->gprs[rd] = ~cpu->shifterOperand;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(ORR, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
|
|
||||||
cpu->gprs[rd] = cpu->gprs[rn] | cpu->shifterOperand;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(RSB, ARM_SUBTRACTION_S(cpu->shifterOperand, n, cpu->gprs[rd]),
|
|
||||||
int32_t n = cpu->gprs[rn];
|
|
||||||
cpu->gprs[rd] = cpu->shifterOperand - n;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(RSC, ARM_SUBTRACTION_CARRY_S(cpu->shifterOperand, n, cpu->gprs[rd], !cpu->cpsr.c),
|
|
||||||
int32_t n = cpu->gprs[rn];
|
|
||||||
cpu->gprs[rd] = cpu->shifterOperand - n - !cpu->cpsr.c;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(SBC, ARM_SUBTRACTION_CARRY_S(n, cpu->shifterOperand, cpu->gprs[rd], !cpu->cpsr.c),
|
|
||||||
int32_t n = cpu->gprs[rn];
|
|
||||||
cpu->gprs[rd] = n - cpu->shifterOperand - !cpu->cpsr.c;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_ARM(SUB, ARM_SUBTRACTION_S(n, cpu->shifterOperand, cpu->gprs[rd]),
|
|
||||||
int32_t n = cpu->gprs[rn];
|
|
||||||
cpu->gprs[rd] = n - cpu->shifterOperand;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(TEQ, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
|
||||||
int32_t aluOut = cpu->gprs[rn] ^ cpu->shifterOperand;)
|
|
||||||
|
|
||||||
DEFINE_ALU_INSTRUCTION_S_ONLY_ARM(TST, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, aluOut),
|
|
||||||
int32_t aluOut = cpu->gprs[rn] & cpu->shifterOperand;)
|
|
||||||
|
|
||||||
// End ALU definitions
|
|
||||||
|
|
||||||
// Begin multiply definitions
|
|
||||||
|
|
||||||
DEFINE_MULTIPLY_INSTRUCTION_2_ARM(MLA, cpu->gprs[rdHi] = cpu->gprs[rm] * cpu->gprs[rs] + cpu->gprs[rd], ARM_NEUTRAL_S(, , cpu->gprs[rdHi]), 2)
|
|
||||||
DEFINE_MULTIPLY_INSTRUCTION_ARM(MUL, cpu->gprs[rd] = cpu->gprs[rm] * cpu->gprs[rs], ARM_NEUTRAL_S(cpu->gprs[rm], cpu->gprs[rs], cpu->gprs[rd]))
|
|
||||||
|
|
||||||
DEFINE_MULTIPLY_INSTRUCTION_2_ARM(SMLAL,
|
|
||||||
int64_t d = ((int64_t) cpu->gprs[rm]) * ((int64_t) cpu->gprs[rs]);
|
|
||||||
int32_t dm = cpu->gprs[rd];
|
|
||||||
int32_t dn = d;
|
|
||||||
cpu->gprs[rd] = dm + dn;
|
|
||||||
cpu->gprs[rdHi] = cpu->gprs[rdHi] + (d >> 32) + ARM_CARRY_FROM(dm, dn, cpu->gprs[rd]);,
|
|
||||||
ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 3)
|
|
||||||
|
|
||||||
DEFINE_MULTIPLY_INSTRUCTION_2_ARM(SMULL,
|
|
||||||
int64_t d = ((int64_t) cpu->gprs[rm]) * ((int64_t) cpu->gprs[rs]);
|
|
||||||
cpu->gprs[rd] = d;
|
|
||||||
cpu->gprs[rdHi] = d >> 32;,
|
|
||||||
ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 2)
|
|
||||||
|
|
||||||
DEFINE_MULTIPLY_INSTRUCTION_2_ARM(UMLAL,
|
|
||||||
uint64_t d = ARM_UXT_64(cpu->gprs[rm]) * ARM_UXT_64(cpu->gprs[rs]);
|
|
||||||
int32_t dm = cpu->gprs[rd];
|
|
||||||
int32_t dn = d;
|
|
||||||
cpu->gprs[rd] = dm + dn;
|
|
||||||
cpu->gprs[rdHi] = cpu->gprs[rdHi] + (d >> 32) + ARM_CARRY_FROM(dm, dn, cpu->gprs[rd]);,
|
|
||||||
ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 3)
|
|
||||||
|
|
||||||
DEFINE_MULTIPLY_INSTRUCTION_2_ARM(UMULL,
|
|
||||||
uint64_t d = ARM_UXT_64(cpu->gprs[rm]) * ARM_UXT_64(cpu->gprs[rs]);
|
|
||||||
cpu->gprs[rd] = d;
|
|
||||||
cpu->gprs[rdHi] = d >> 32;,
|
|
||||||
ARM_NEUTRAL_HI_S(cpu->gprs[rd], cpu->gprs[rdHi]), 2)
|
|
||||||
|
|
||||||
// End multiply definitions
|
|
||||||
|
|
||||||
// Begin load/store definitions
|
|
||||||
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDR, LOAD, cpu->gprs[rd] = cpu->memory.load32(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(LDRB, LOAD, cpu->gprs[rd] = cpu->memory.load8(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
|
||||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRH, LOAD, cpu->gprs[rd] = cpu->memory.load16(cpu, address, ¤tCycles); ARM_LOAD_POST_BODY;)
|
|
||||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSB, LOAD, cpu->gprs[rd] = ARM_SXT_8(cpu->memory.load8(cpu, address, ¤tCycles)); ARM_LOAD_POST_BODY;)
|
|
||||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(LDRSH, LOAD, cpu->gprs[rd] = address & 1 ? ARM_SXT_8(cpu->memory.load16(cpu, address, ¤tCycles)) : ARM_SXT_16(cpu->memory.load16(cpu, address, ¤tCycles)); ARM_LOAD_POST_BODY;)
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STR, STORE, cpu->memory.store32(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
|
||||||
DEFINE_LOAD_STORE_INSTRUCTION_ARM(STRB, STORE, cpu->memory.store8(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
|
||||||
DEFINE_LOAD_STORE_MODE_3_INSTRUCTION_ARM(STRH, STORE, cpu->memory.store16(cpu, address, cpu->gprs[rd], ¤tCycles); ARM_STORE_POST_BODY;)
|
|
||||||
|
|
||||||
DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRBT, LOAD,
|
|
||||||
enum PrivilegeMode priv = cpu->privilegeMode;
|
|
||||||
ARMSetPrivilegeMode(cpu, MODE_USER);
|
|
||||||
int32_t r = cpu->memory.load8(cpu, address, ¤tCycles);
|
|
||||||
ARMSetPrivilegeMode(cpu, priv);
|
|
||||||
cpu->gprs[rd] = r;
|
|
||||||
ARM_LOAD_POST_BODY;)
|
|
||||||
|
|
||||||
DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(LDRT, LOAD,
|
|
||||||
enum PrivilegeMode priv = cpu->privilegeMode;
|
|
||||||
ARMSetPrivilegeMode(cpu, MODE_USER);
|
|
||||||
int32_t r = cpu->memory.load32(cpu, address, ¤tCycles);
|
|
||||||
ARMSetPrivilegeMode(cpu, priv);
|
|
||||||
cpu->gprs[rd] = r;
|
|
||||||
ARM_LOAD_POST_BODY;)
|
|
||||||
|
|
||||||
DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(STRBT, STORE,
|
|
||||||
enum PrivilegeMode priv = cpu->privilegeMode;
|
|
||||||
int32_t r = cpu->gprs[rd];
|
|
||||||
ARMSetPrivilegeMode(cpu, MODE_USER);
|
|
||||||
cpu->memory.store8(cpu, address, r, ¤tCycles);
|
|
||||||
ARMSetPrivilegeMode(cpu, priv);
|
|
||||||
ARM_STORE_POST_BODY;)
|
|
||||||
|
|
||||||
DEFINE_LOAD_STORE_T_INSTRUCTION_ARM(STRT, STORE,
|
|
||||||
enum PrivilegeMode priv = cpu->privilegeMode;
|
|
||||||
int32_t r = cpu->gprs[rd];
|
|
||||||
ARMSetPrivilegeMode(cpu, MODE_USER);
|
|
||||||
cpu->memory.store32(cpu, address, r, ¤tCycles);
|
|
||||||
ARMSetPrivilegeMode(cpu, priv);
|
|
||||||
ARM_STORE_POST_BODY;)
|
|
||||||
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(LDM,
|
|
||||||
load, true,
|
|
||||||
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32;
|
|
||||||
if ((rs & 0x8000) || !rs) {
|
|
||||||
currentCycles += ARMWritePC(cpu);
|
|
||||||
})
|
|
||||||
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(STM,
|
|
||||||
store, false,
|
|
||||||
ARM_STORE_POST_BODY;)
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(SWP,
|
|
||||||
int rm = opcode & 0xF;
|
|
||||||
int rd = (opcode >> 12) & 0xF;
|
|
||||||
int rn = (opcode >> 16) & 0xF;
|
|
||||||
int32_t d = cpu->memory.load32(cpu, cpu->gprs[rn], ¤tCycles);
|
|
||||||
cpu->memory.store32(cpu, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles);
|
|
||||||
cpu->gprs[rd] = d;)
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(SWPB,
|
|
||||||
int rm = opcode & 0xF;
|
|
||||||
int rd = (opcode >> 12) & 0xF;
|
|
||||||
int rn = (opcode >> 16) & 0xF;
|
|
||||||
int32_t d = cpu->memory.load8(cpu, cpu->gprs[rn], ¤tCycles);
|
|
||||||
cpu->memory.store8(cpu, cpu->gprs[rn], cpu->gprs[rm], ¤tCycles);
|
|
||||||
cpu->gprs[rd] = d;)
|
|
||||||
|
|
||||||
// End load/store definitions
|
|
||||||
|
|
||||||
// Begin branch definitions
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(B,
|
|
||||||
int32_t offset = opcode << 8;
|
|
||||||
offset >>= 6;
|
|
||||||
cpu->gprs[ARM_PC] += offset;
|
|
||||||
currentCycles += ARMWritePC(cpu);)
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(BL,
|
|
||||||
int32_t immediate = (opcode & 0x00FFFFFF) << 8;
|
|
||||||
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - 4;
|
|
||||||
cpu->gprs[ARM_PC] += immediate >> 6;
|
|
||||||
currentCycles += ARMWritePC(cpu);)
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(BX,
|
|
||||||
int rm = opcode & 0x0000000F;
|
|
||||||
cpu->gprs[ARM_PC] = cpu->gprs[rm] & 0xFFFFFFFE;
|
|
||||||
currentCycles += ARMWritePC(cpu);
|
|
||||||
)
|
|
||||||
|
|
||||||
// End branch definitions
|
|
||||||
|
|
||||||
// Begin coprocessor definitions
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(CDP, ARM_STUB)
|
|
||||||
DEFINE_INSTRUCTION_ARM(LDC, ARM_STUB)
|
|
||||||
DEFINE_INSTRUCTION_ARM(STC, ARM_STUB)
|
|
||||||
DEFINE_INSTRUCTION_ARM(MCR, ARM_STUB)
|
|
||||||
DEFINE_INSTRUCTION_ARM(MRC, ARM_STUB)
|
|
||||||
|
|
||||||
// Begin miscellaneous definitions
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(BKPT, cpu->irqh.bkpt32(cpu, ((opcode >> 4) & 0xFFF0) | (opcode & 0xF))); // Not strictly in ARMv4T, but here for convenience
|
|
||||||
DEFINE_INSTRUCTION_ARM(ILL, ARM_ILL) // Illegal opcode
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(MSR,
|
|
||||||
int c = opcode & 0x00010000;
|
|
||||||
int f = opcode & 0x00080000;
|
|
||||||
int32_t operand = cpu->gprs[opcode & 0x0000000F];
|
|
||||||
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
|
||||||
if (mask & PSR_USER_MASK) {
|
|
||||||
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_USER_MASK) | (operand & PSR_USER_MASK);
|
|
||||||
}
|
|
||||||
if (mask & PSR_STATE_MASK) {
|
|
||||||
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_STATE_MASK) | (operand & PSR_STATE_MASK);
|
|
||||||
}
|
|
||||||
if (cpu->privilegeMode != MODE_USER && (mask & PSR_PRIV_MASK)) {
|
|
||||||
ARMSetPrivilegeMode(cpu, (enum PrivilegeMode) ((operand & 0x0000000F) | 0x00000010));
|
|
||||||
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_PRIV_MASK) | (operand & PSR_PRIV_MASK);
|
|
||||||
}
|
|
||||||
_ARMReadCPSR(cpu);
|
|
||||||
// LOAD_32(cpu->prefetch[0], (cpu->gprs[ARM_PC] - 4) & cpu->memory.activeMask, cpu->memory.activeRegion);
|
|
||||||
// LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
|
||||||
cpu->prefetch[0] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC] - 4, NULL);
|
|
||||||
cpu->prefetch[1] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL);
|
|
||||||
)
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(MSRR,
|
|
||||||
int c = opcode & 0x00010000;
|
|
||||||
int f = opcode & 0x00080000;
|
|
||||||
int32_t operand = cpu->gprs[opcode & 0x0000000F];
|
|
||||||
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
|
||||||
mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK;
|
|
||||||
cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask) | 0x00000010;)
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(MRS, \
|
|
||||||
int rd = (opcode >> 12) & 0xF; \
|
|
||||||
cpu->gprs[rd] = cpu->cpsr.packed;)
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(MRSR, \
|
|
||||||
int rd = (opcode >> 12) & 0xF; \
|
|
||||||
cpu->gprs[rd] = cpu->spsr.packed;)
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(MSRI,
|
|
||||||
int c = opcode & 0x00010000;
|
|
||||||
int f = opcode & 0x00080000;
|
|
||||||
int rotate = (opcode & 0x00000F00) >> 7;
|
|
||||||
int32_t operand = ROR(opcode & 0x000000FF, rotate);
|
|
||||||
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
|
||||||
if (mask & PSR_USER_MASK) {
|
|
||||||
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_USER_MASK) | (operand & PSR_USER_MASK);
|
|
||||||
}
|
|
||||||
if (mask & PSR_STATE_MASK) {
|
|
||||||
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_STATE_MASK) | (operand & PSR_STATE_MASK);
|
|
||||||
}
|
|
||||||
if (cpu->privilegeMode != MODE_USER && (mask & PSR_PRIV_MASK)) {
|
|
||||||
ARMSetPrivilegeMode(cpu, (enum PrivilegeMode) ((operand & 0x0000000F) | 0x00000010));
|
|
||||||
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_PRIV_MASK) | (operand & PSR_PRIV_MASK);
|
|
||||||
}
|
|
||||||
_ARMReadCPSR(cpu);
|
|
||||||
// LOAD_32(cpu->prefetch[0], (cpu->gprs[ARM_PC] - 4) & cpu->memory.activeMask, cpu->memory.activeRegion);
|
|
||||||
// LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
|
||||||
cpu->prefetch[0] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC] - 4, NULL);
|
|
||||||
cpu->prefetch[1] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL);
|
|
||||||
)
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(MSRRI,
|
|
||||||
int c = opcode & 0x00010000;
|
|
||||||
int f = opcode & 0x00080000;
|
|
||||||
int rotate = (opcode & 0x00000F00) >> 7;
|
|
||||||
int32_t operand = ROR(opcode & 0x000000FF, rotate);
|
|
||||||
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
|
|
||||||
mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK;
|
|
||||||
cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask) | 0x00000010;)
|
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(SWI, cpu->irqh.swi32(cpu, opcode & 0xFFFFFF))
|
|
||||||
|
|
||||||
const ARMInstruction _armTable[0x1000] = {
|
|
||||||
DECLARE_ARM_EMITTER_BLOCK(_ARMInstruction)
|
|
||||||
};
|
|
|
@ -8,24 +8,6 @@
|
||||||
|
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
|
|
||||||
#include "arm.h"
|
|
||||||
|
|
||||||
#define ARM_COND_EQ (cpu->cpsr.z)
|
|
||||||
#define ARM_COND_NE (!cpu->cpsr.z)
|
|
||||||
#define ARM_COND_CS (cpu->cpsr.c)
|
|
||||||
#define ARM_COND_CC (!cpu->cpsr.c)
|
|
||||||
#define ARM_COND_MI (cpu->cpsr.n)
|
|
||||||
#define ARM_COND_PL (!cpu->cpsr.n)
|
|
||||||
#define ARM_COND_VS (cpu->cpsr.v)
|
|
||||||
#define ARM_COND_VC (!cpu->cpsr.v)
|
|
||||||
#define ARM_COND_HI (cpu->cpsr.c && !cpu->cpsr.z)
|
|
||||||
#define ARM_COND_LS (!cpu->cpsr.c || cpu->cpsr.z)
|
|
||||||
#define ARM_COND_GE (!cpu->cpsr.n == !cpu->cpsr.v)
|
|
||||||
#define ARM_COND_LT (!cpu->cpsr.n != !cpu->cpsr.v)
|
|
||||||
#define ARM_COND_GT (!cpu->cpsr.z && !cpu->cpsr.n == !cpu->cpsr.v)
|
|
||||||
#define ARM_COND_LE (cpu->cpsr.z || !cpu->cpsr.n != !cpu->cpsr.v)
|
|
||||||
#define ARM_COND_AL 1
|
|
||||||
|
|
||||||
#define ARM_SIGN(I) ((I) >> 31)
|
#define ARM_SIGN(I) ((I) >> 31)
|
||||||
#define ARM_SXT_8(I) (((int8_t) (I) << 24) >> 24)
|
#define ARM_SXT_8(I) (((int8_t) (I) << 24) >> 24)
|
||||||
#define ARM_SXT_16(I) (((int16_t) (I) << 16) >> 16)
|
#define ARM_SXT_16(I) (((int16_t) (I) << 16) >> 16)
|
||||||
|
@ -37,46 +19,4 @@
|
||||||
#define ARM_V_ADDITION(M, N, D) (!(ARM_SIGN((M) ^ (N))) && (ARM_SIGN((M) ^ (D))))
|
#define ARM_V_ADDITION(M, N, D) (!(ARM_SIGN((M) ^ (N))) && (ARM_SIGN((M) ^ (D))))
|
||||||
#define ARM_V_SUBTRACTION(M, N, D) ((ARM_SIGN((M) ^ (N))) && (ARM_SIGN((M) ^ (D))))
|
#define ARM_V_SUBTRACTION(M, N, D) ((ARM_SIGN((M) ^ (N))) && (ARM_SIGN((M) ^ (D))))
|
||||||
|
|
||||||
#define ARM_WAIT_MUL(R) \
|
|
||||||
{ \
|
|
||||||
int32_t wait; \
|
|
||||||
if ((R & 0xFFFFFF00) == 0xFFFFFF00 || !(R & 0xFFFFFF00)) { \
|
|
||||||
wait = 1; \
|
|
||||||
} else if ((R & 0xFFFF0000) == 0xFFFF0000 || !(R & 0xFFFF0000)) { \
|
|
||||||
wait = 2; \
|
|
||||||
} else if ((R & 0xFF000000) == 0xFF000000 || !(R & 0xFF000000)) { \
|
|
||||||
wait = 3; \
|
|
||||||
} else { \
|
|
||||||
wait = 4; \
|
|
||||||
} \
|
|
||||||
currentCycles += cpu->memory.stall(cpu, wait); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ARM_STUB cpu->irqh.hitStub(cpu, opcode)
|
|
||||||
#define ARM_ILL cpu->irqh.hitIllegal(cpu, opcode)
|
|
||||||
|
|
||||||
static inline int32_t ARMWritePC(struct ARMCore* cpu) {
|
|
||||||
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -4);
|
|
||||||
// cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]);
|
|
||||||
// LOAD_32(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
|
||||||
cpu->prefetch[0] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL);
|
|
||||||
cpu->gprs[ARM_PC] += 4;
|
|
||||||
// LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
|
||||||
cpu->prefetch[1] = cpu->memory.load32(cpu, cpu->gprs[ARM_PC], NULL);
|
|
||||||
return 2 + cpu->memory.activeNonseqCycles32 + cpu->memory.activeSeqCycles32;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int _ARMModeHasSPSR(enum PrivilegeMode mode) {
|
|
||||||
return mode != MODE_SYSTEM && mode != MODE_USER;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _ARMReadCPSR(struct ARMCore* cpu) {
|
|
||||||
ARMSetPrivilegeMode(cpu, cpu->cpsr.priv);
|
|
||||||
cpu->irqh.readCPSR(cpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t _ARMPCAddress(struct ARMCore* cpu) {
|
|
||||||
return cpu->gprs[ARM_PC] - 4 * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
#include "wind_defs.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
void windDiffPorts(uint32_t oldval, uint32_t newval) {
|
|
||||||
uint32_t changes = oldval ^ newval;
|
|
||||||
if (changes & 1) printf("PRT codec enable: %d\n", newval&1);
|
|
||||||
if (changes & 2) printf("PRT audio amp enable: %d\n", newval&2);
|
|
||||||
if (changes & 4) printf("PRT lcd power: %d\n", newval&4);
|
|
||||||
if (changes & 8) printf("PRT etna door: %d\n", newval&8);
|
|
||||||
if (changes & 0x10) printf("PRT sled: %d\n", newval&0x10);
|
|
||||||
if (changes & 0x20) printf("PRT pump pwr2: %d\n", newval&0x20);
|
|
||||||
if (changes & 0x40) printf("PRT pump pwr1: %d\n", newval&0x40);
|
|
||||||
if (changes & 0x80) printf("PRT etna err: %d\n", newval&0x80);
|
|
||||||
if (changes & 0x100) printf("PRT rs-232 rts: %d\n", newval&0x100);
|
|
||||||
if (changes & 0x200) printf("PRT rs-232 dtr toggle: %d\n", newval&0x200);
|
|
||||||
if (changes & 0x400) printf("PRT disable power led: %d\n", newval&0x400);
|
|
||||||
if (changes & 0x800) printf("PRT enable uart1: %d\n", newval&0x800);
|
|
||||||
if (changes & 0x1000) printf("PRT lcd backlight: %d\n", newval&0x1000);
|
|
||||||
if (changes & 0x2000) printf("PRT enable uart0: %d\n", newval&0x2000);
|
|
||||||
if (changes & 0x4000) printf("PRT dictaphone: %d\n", newval&0x4000);
|
|
||||||
// PROM read process makes this super spammy in stdout
|
|
||||||
// if (changes & 0x10000) printf("PRT EECS: %d\n", newval&0x10000);
|
|
||||||
// if (changes & 0x20000) printf("PRT EECLK: %d\n", newval&0x20000);
|
|
||||||
if (changes & 0x40000) printf("PRT contrast0: %d\n", newval&0x40000);
|
|
||||||
if (changes & 0x80000) printf("PRT contrast1: %d\n", newval&0x80000);
|
|
||||||
if (changes & 0x100000) printf("PRT contrast2: %d\n", newval&0x100000);
|
|
||||||
if (changes & 0x200000) printf("PRT contrast3: %d\n", newval&0x200000);
|
|
||||||
if (changes & 0x400000) printf("PRT case open: %d\n", newval&0x400000);
|
|
||||||
if (changes & 0x800000) printf("PRT etna cf power: %d\n", newval&0x800000);
|
|
||||||
if (changes & 0x1000000) printf("PRT kb0: %d\n", newval&0x1000000);
|
|
||||||
if (changes & 0x2000000) printf("PRT kb1: %d\n", newval&0x2000000);
|
|
||||||
if (changes & 0x4000000) printf("PRT kb2: %d\n", newval&0x4000000);
|
|
||||||
if (changes & 0x8000000) printf("PRT kb3: %d\n", newval&0x8000000);
|
|
||||||
if (changes & 0x10000000) printf("PRT kb4: %d\n", newval&0x10000000);
|
|
||||||
if (changes & 0x20000000) printf("PRT kb5: %d\n", newval&0x20000000);
|
|
||||||
if (changes & 0x40000000) printf("PRT kb6: %d\n", newval&0x40000000);
|
|
||||||
if (changes & 0x80000000) printf("PRT kb7: %d\n", newval&0x80000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void windDiffInterrupts(uint16_t oldval, uint16_t newval) {
|
|
||||||
uint16_t changes = oldval ^ newval;
|
|
||||||
if (changes & 1) printf("INTCHG external=%d\n", newval & 1);
|
|
||||||
if (changes & 2) printf("INTCHG lowbat=%d\n", newval & 2);
|
|
||||||
if (changes & 4) printf("INTCHG watchdog=%d\n", newval & 4);
|
|
||||||
if (changes & 8) printf("INTCHG mediachg=%d\n", newval & 8);
|
|
||||||
if (changes & 0x10) printf("INTCHG codec=%d\n", newval & 0x10);
|
|
||||||
if (changes & 0x20) printf("INTCHG ext1=%d\n", newval & 0x20);
|
|
||||||
if (changes & 0x40) printf("INTCHG ext2=%d\n", newval & 0x40);
|
|
||||||
if (changes & 0x80) printf("INTCHG ext3=%d\n", newval & 0x80);
|
|
||||||
if (changes & 0x100) printf("INTCHG timer1=%d\n", newval & 0x100);
|
|
||||||
if (changes & 0x200) printf("INTCHG timer2=%d\n", newval & 0x200);
|
|
||||||
if (changes & 0x400) printf("INTCHG rtcmatch=%d\n", newval & 0x400);
|
|
||||||
if (changes & 0x800) printf("INTCHG tick=%d\n", newval & 0x800);
|
|
||||||
if (changes & 0x1000) printf("INTCHG uart1=%d\n", newval & 0x1000);
|
|
||||||
if (changes & 0x2000) printf("INTCHG uart2=%d\n", newval & 0x2000);
|
|
||||||
if (changes & 0x4000) printf("INTCHG lcd=%d\n", newval & 0x4000);
|
|
||||||
if (changes & 0x8000) printf("INTCHG spi=%d\n", newval & 0x8000);
|
|
||||||
}
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace Windermere {
|
||||||
enum {
|
enum {
|
||||||
CLOCK_SPEED = 0x9000*1000,
|
CLOCK_SPEED = 0x9000*1000,
|
||||||
TICK_INTERVAL = CLOCK_SPEED / 64
|
TICK_INTERVAL = CLOCK_SPEED / 64
|
||||||
|
@ -27,7 +28,7 @@ enum Interrupt {
|
||||||
IRQ_INTERRUPTS = 0xFFF0
|
IRQ_INTERRUPTS = 0xFFF0
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WindermereReg {
|
enum Register {
|
||||||
MEMCFG1 = 0,
|
MEMCFG1 = 0,
|
||||||
MEMCFG2 = 4,
|
MEMCFG2 = 4,
|
||||||
DRAM_CFG = 0x100,
|
DRAM_CFG = 0x100,
|
||||||
|
@ -89,6 +90,4 @@ enum WindermereReg {
|
||||||
KSCAN = 0xE28,
|
KSCAN = 0xE28,
|
||||||
LCDMUX = 0xE2C
|
LCDMUX = 0xE2C
|
||||||
};
|
};
|
||||||
|
}
|
||||||
void windDiffPorts(uint32_t oldval, uint32_t newval);
|
|
||||||
void windDiffInterrupts(uint16_t oldval, uint16_t newval);
|
|
||||||
|
|
|
@ -8,16 +8,17 @@
|
||||||
//#define INCLUDE_D
|
//#define INCLUDE_D
|
||||||
//#define INCLUDE_BANK1
|
//#define INCLUDE_BANK1
|
||||||
|
|
||||||
Windermere::Windermere() : ARM710(true), etna(this) {
|
namespace Windermere {
|
||||||
|
Emulator::Emulator() : EmuBase(true), etna(this) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t Windermere::getRTC() {
|
uint32_t Emulator::getRTC() {
|
||||||
return time(nullptr) - 946684800;
|
return time(nullptr) - 946684800;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t Windermere::readReg8(uint32_t reg) {
|
uint32_t Emulator::readReg8(uint32_t reg) {
|
||||||
if ((reg & 0xF00) == 0x600) {
|
if ((reg & 0xF00) == 0x600) {
|
||||||
return uart1.readReg8(reg & 0xFF);
|
return uart1.readReg8(reg & 0xFF);
|
||||||
} else if ((reg & 0xF00) == 0x700) {
|
} else if ((reg & 0xF00) == 0x700) {
|
||||||
|
@ -27,7 +28,7 @@ uint32_t Windermere::readReg8(uint32_t reg) {
|
||||||
} else if (reg == TC2CTRL) {
|
} else if (reg == TC2CTRL) {
|
||||||
return tc2.config;
|
return tc2.config;
|
||||||
} else if (reg == PADR) {
|
} else if (reg == PADR) {
|
||||||
return readKeyboard();
|
return readKeyboard(kScan);
|
||||||
} else if (reg == PBDR) {
|
} else if (reg == PBDR) {
|
||||||
return (portValues >> 16) & 0xFF;
|
return (portValues >> 16) & 0xFF;
|
||||||
} else if (reg == PCDR) {
|
} else if (reg == PCDR) {
|
||||||
|
@ -47,7 +48,7 @@ uint32_t Windermere::readReg8(uint32_t reg) {
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint32_t Windermere::readReg32(uint32_t reg) {
|
uint32_t Emulator::readReg32(uint32_t reg) {
|
||||||
if (reg == LCDCTL) {
|
if (reg == LCDCTL) {
|
||||||
printf("LCD control read pc=%08x lr=%08x !!!\n", getGPR(15), getGPR(14));
|
printf("LCD control read pc=%08x lr=%08x !!!\n", getGPR(15), getGPR(14));
|
||||||
return lcdControl;
|
return lcdControl;
|
||||||
|
@ -92,7 +93,7 @@ uint32_t Windermere::readReg32(uint32_t reg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Windermere::writeReg8(uint32_t reg, uint8_t value) {
|
void Emulator::writeReg8(uint32_t reg, uint8_t value) {
|
||||||
if ((reg & 0xF00) == 0x600) {
|
if ((reg & 0xF00) == 0x600) {
|
||||||
uart1.writeReg8(reg & 0xFF, value);
|
uart1.writeReg8(reg & 0xFF, value);
|
||||||
} else if ((reg & 0xF00) == 0x700) {
|
} else if ((reg & 0xF00) == 0x700) {
|
||||||
|
@ -105,7 +106,7 @@ void Windermere::writeReg8(uint32_t reg, uint8_t value) {
|
||||||
uint32_t oldPorts = portValues;
|
uint32_t oldPorts = portValues;
|
||||||
portValues &= 0x00FFFFFF;
|
portValues &= 0x00FFFFFF;
|
||||||
portValues |= (uint32_t)value << 24;
|
portValues |= (uint32_t)value << 24;
|
||||||
windDiffPorts(oldPorts, portValues);
|
diffPorts(oldPorts, portValues);
|
||||||
} else if (reg == PBDR) {
|
} else if (reg == PBDR) {
|
||||||
uint32_t oldPorts = portValues;
|
uint32_t oldPorts = portValues;
|
||||||
portValues &= 0xFF00FFFF;
|
portValues &= 0xFF00FFFF;
|
||||||
|
@ -116,17 +117,17 @@ void Windermere::writeReg8(uint32_t reg, uint8_t value) {
|
||||||
etna.setPromBit0Low();
|
etna.setPromBit0Low();
|
||||||
if ((portValues & 0x20000) && !(oldPorts & 0x20000))
|
if ((portValues & 0x20000) && !(oldPorts & 0x20000))
|
||||||
etna.setPromBit1High();
|
etna.setPromBit1High();
|
||||||
windDiffPorts(oldPorts, portValues);
|
diffPorts(oldPorts, portValues);
|
||||||
} else if (reg == PCDR) {
|
} else if (reg == PCDR) {
|
||||||
uint32_t oldPorts = portValues;
|
uint32_t oldPorts = portValues;
|
||||||
portValues &= 0xFFFF00FF;
|
portValues &= 0xFFFF00FF;
|
||||||
portValues |= (uint32_t)value << 8;
|
portValues |= (uint32_t)value << 8;
|
||||||
windDiffPorts(oldPorts, portValues);
|
diffPorts(oldPorts, portValues);
|
||||||
} else if (reg == PDDR) {
|
} else if (reg == PDDR) {
|
||||||
uint32_t oldPorts = portValues;
|
uint32_t oldPorts = portValues;
|
||||||
portValues &= 0xFFFFFF00;
|
portValues &= 0xFFFFFF00;
|
||||||
portValues |= (uint32_t)value;
|
portValues |= (uint32_t)value;
|
||||||
windDiffPorts(oldPorts, portValues);
|
diffPorts(oldPorts, portValues);
|
||||||
} else if (reg == PADDR) {
|
} else if (reg == PADDR) {
|
||||||
portDirections &= 0x00FFFFFF;
|
portDirections &= 0x00FFFFFF;
|
||||||
portDirections |= (uint32_t)value << 24;
|
portDirections |= (uint32_t)value << 24;
|
||||||
|
@ -145,7 +146,7 @@ void Windermere::writeReg8(uint32_t reg, uint8_t value) {
|
||||||
// printf("RegWrite8 unknown:: pc=%08x reg=%03x value=%02x\n", getGPR(15)-4, reg, value);
|
// printf("RegWrite8 unknown:: pc=%08x reg=%03x value=%02x\n", getGPR(15)-4, reg, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Windermere::writeReg32(uint32_t reg, uint32_t value) {
|
void Emulator::writeReg32(uint32_t reg, uint32_t value) {
|
||||||
if (reg == LCDCTL) {
|
if (reg == LCDCTL) {
|
||||||
printf("LCD: ctl write %08x\n", value);
|
printf("LCD: ctl write %08x\n", value);
|
||||||
lcdControl = value;
|
lcdControl = value;
|
||||||
|
@ -159,10 +160,10 @@ void Windermere::writeReg32(uint32_t reg, uint32_t value) {
|
||||||
} else if (reg == LCDT2) {
|
} else if (reg == LCDT2) {
|
||||||
printf("LCD: clocks write %08x\n", value);
|
printf("LCD: clocks write %08x\n", value);
|
||||||
} else if (reg == INTENS) {
|
} else if (reg == INTENS) {
|
||||||
// windDiffInterrupts(interruptMask, interruptMask | value);
|
// diffInterrupts(interruptMask, interruptMask | value);
|
||||||
interruptMask |= value;
|
interruptMask |= value;
|
||||||
} else if (reg == INTENC) {
|
} else if (reg == INTENC) {
|
||||||
// windDiffInterrupts(interruptMask, interruptMask &~ value);
|
// diffInterrupts(interruptMask, interruptMask &~ value);
|
||||||
interruptMask &= ~value;
|
interruptMask &= ~value;
|
||||||
} else if (reg == HALT) {
|
} else if (reg == HALT) {
|
||||||
halted = true;
|
halted = true;
|
||||||
|
@ -190,21 +191,7 @@ void Windermere::writeReg32(uint32_t reg, uint32_t value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Windermere::isPhysAddressValid(uint32_t physAddress) const {
|
MaybeU32 Emulator::readPhysical(uint32_t physAddr, ValueSize valueSize) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MaybeU32 Windermere::readPhysical(uint32_t physAddr, ValueSize valueSize) {
|
|
||||||
uint8_t region = (physAddr >> 24) & 0xF1;
|
uint8_t region = (physAddr >> 24) & 0xF1;
|
||||||
if (valueSize == V8) {
|
if (valueSize == V8) {
|
||||||
if (region == 0)
|
if (region == 0)
|
||||||
|
@ -273,7 +260,7 @@ MaybeU32 Windermere::readPhysical(uint32_t physAddr, ValueSize valueSize) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Windermere::writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) {
|
bool Emulator::writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) {
|
||||||
uint8_t region = (physAddr >> 24) & 0xF1;
|
uint8_t region = (physAddr >> 24) & 0xF1;
|
||||||
if (valueSize == V8) {
|
if (valueSize == V8) {
|
||||||
#if defined(INCLUDE_BANK1)
|
#if defined(INCLUDE_BANK1)
|
||||||
|
@ -336,7 +323,7 @@ bool Windermere::writePhysical(uint32_t value, uint32_t physAddr, ValueSize valu
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Windermere::configure() {
|
void Emulator::configure() {
|
||||||
if (configured) return;
|
if (configured) return;
|
||||||
configured = true;
|
configured = true;
|
||||||
|
|
||||||
|
@ -355,13 +342,11 @@ void Windermere::configure() {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Windermere::loadROM(const char *path) {
|
void Emulator::loadROM(uint8_t *buffer, size_t size) {
|
||||||
FILE *f = fopen(path, "rb");
|
memcpy(ROM, buffer, min(size, sizeof(ROM)));
|
||||||
fread(ROM, 1, sizeof(ROM), f);
|
|
||||||
fclose(f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Windermere::executeUntil(int64_t cycles) {
|
void Emulator::executeUntil(int64_t cycles) {
|
||||||
if (!configured)
|
if (!configured)
|
||||||
configure();
|
configure();
|
||||||
|
|
||||||
|
@ -415,26 +400,8 @@ void Windermere::executeUntil(int64_t cycles) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Windermere::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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const char *Emulator::identifyObjectCon(uint32_t ptr) {
|
||||||
|
|
||||||
void Windermere::printRegs() {
|
|
||||||
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 *Windermere::identifyObjectCon(uint32_t ptr) {
|
|
||||||
if (ptr == readVirtualDebug(0x80000980, V32).value()) return "process";
|
if (ptr == readVirtualDebug(0x80000980, V32).value()) return "process";
|
||||||
if (ptr == readVirtualDebug(0x80000984, V32).value()) return "thread";
|
if (ptr == readVirtualDebug(0x80000984, V32).value()) return "thread";
|
||||||
if (ptr == readVirtualDebug(0x80000988, V32).value()) return "chunk";
|
if (ptr == readVirtualDebug(0x80000988, V32).value()) return "chunk";
|
||||||
|
@ -451,7 +418,7 @@ const char *Windermere::identifyObjectCon(uint32_t ptr) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Windermere::fetchStr(uint32_t str, char *buf) {
|
void Emulator::fetchStr(uint32_t str, char *buf) {
|
||||||
if (str == 0) {
|
if (str == 0) {
|
||||||
strcpy(buf, "<NULL>");
|
strcpy(buf, "<NULL>");
|
||||||
return;
|
return;
|
||||||
|
@ -463,15 +430,15 @@ void Windermere::fetchStr(uint32_t str, char *buf) {
|
||||||
buf[size] = 0;
|
buf[size] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Windermere::fetchName(uint32_t obj, char *buf) {
|
void Emulator::fetchName(uint32_t obj, char *buf) {
|
||||||
fetchStr(readVirtualDebug(obj + 0x10, V32).value(), buf);
|
fetchStr(readVirtualDebug(obj + 0x10, V32).value(), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Windermere::fetchProcessFilename(uint32_t obj, char *buf) {
|
void Emulator::fetchProcessFilename(uint32_t obj, char *buf) {
|
||||||
fetchStr(readVirtualDebug(obj + 0x3C, V32).value(), buf);
|
fetchStr(readVirtualDebug(obj + 0x3C, V32).value(), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Windermere::debugPC(uint32_t pc) {
|
void Emulator::debugPC(uint32_t pc) {
|
||||||
char objName[1000];
|
char objName[1000];
|
||||||
if (pc == 0x2CBC4) {
|
if (pc == 0x2CBC4) {
|
||||||
// CObjectCon::AddL()
|
// CObjectCon::AddL()
|
||||||
|
@ -511,28 +478,88 @@ void Windermere::debugPC(uint32_t pc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const uint8_t *Windermere::getLCDBuffer() const {
|
int Emulator::getLCDWidth() const {
|
||||||
if ((lcdAddress >> 24) == 0xC0)
|
return 640;
|
||||||
return &MemoryBlockC0[lcdAddress & MemoryBlockMask];
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
int Emulator::getLCDHeight() const {
|
||||||
|
return 240;
|
||||||
|
}
|
||||||
|
void Emulator::readLCDIntoBuffer(uint8_t **lines) const {
|
||||||
|
if ((lcdAddress >> 24) == 0xC0) {
|
||||||
|
const uint8_t *lcdBuf = &MemoryBlockC0[lcdAddress & MemoryBlockMask];
|
||||||
|
int width = 640, height = 240;
|
||||||
|
|
||||||
|
// fetch palette
|
||||||
|
int bpp = 1 << (lcdBuf[1] >> 4);
|
||||||
|
int ppb = 8 / bpp;
|
||||||
|
uint16_t palette[16];
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
palette[i] = lcdBuf[i*2] | ((lcdBuf[i*2+1] << 8) & 0xF00);
|
||||||
|
|
||||||
uint8_t Windermere::readKeyboard() {
|
// build our image out
|
||||||
uint8_t val = 0;
|
int lineWidth = (width * bpp) / 8;
|
||||||
if (kScan & 8) {
|
for (int y = 0; y < height; y++) {
|
||||||
// Select one keyboard
|
int lineOffs = 0x20 + (lineWidth * y);
|
||||||
int whichColumn = kScan & 7;
|
for (int x = 0; x < width; x++) {
|
||||||
for (int i = 0; i < 7; i++)
|
uint8_t byte = lcdBuf[lineOffs + (x / ppb)];
|
||||||
if (keyboardKeys[whichColumn * 7 + i])
|
int shift = (x & (ppb - 1)) * bpp;
|
||||||
val |= (1 << i);
|
int mask = (1 << bpp) - 1;
|
||||||
} else if (kScan == 0) {
|
int palIdx = (byte >> shift) & mask;
|
||||||
// Report all columns combined
|
int palValue = palette[palIdx];
|
||||||
// EPOC's keyboard driver relies on this...
|
|
||||||
for (int i = 0; i < 8*7; i++)
|
palValue |= (palValue << 4);
|
||||||
if (keyboardKeys[i])
|
lines[y][x] = palValue ^ 0xFF;
|
||||||
val |= (1 << (i % 7));
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return val;
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Emulator::diffPorts(uint32_t oldval, uint32_t newval) {
|
||||||
|
uint32_t changes = oldval ^ newval;
|
||||||
|
if (changes & 1) log("PRT codec enable: %d", newval&1);
|
||||||
|
if (changes & 2) log("PRT audio amp enable: %d", newval&2);
|
||||||
|
if (changes & 4) log("PRT lcd power: %d", newval&4);
|
||||||
|
if (changes & 8) log("PRT etna door: %d", newval&8);
|
||||||
|
if (changes & 0x10) log("PRT sled: %d", newval&0x10);
|
||||||
|
if (changes & 0x20) log("PRT pump pwr2: %d", newval&0x20);
|
||||||
|
if (changes & 0x40) log("PRT pump pwr1: %d", newval&0x40);
|
||||||
|
if (changes & 0x80) log("PRT etna err: %d", newval&0x80);
|
||||||
|
if (changes & 0x100) log("PRT rs-232 rts: %d", newval&0x100);
|
||||||
|
if (changes & 0x200) log("PRT rs-232 dtr toggle: %d", newval&0x200);
|
||||||
|
if (changes & 0x400) log("PRT disable power led: %d", newval&0x400);
|
||||||
|
if (changes & 0x800) log("PRT enable uart1: %d", newval&0x800);
|
||||||
|
if (changes & 0x1000) log("PRT lcd backlight: %d", newval&0x1000);
|
||||||
|
if (changes & 0x2000) log("PRT enable uart0: %d", newval&0x2000);
|
||||||
|
if (changes & 0x4000) log("PRT dictaphone: %d", newval&0x4000);
|
||||||
|
// PROM read process makes this super spammy in stdout
|
||||||
|
// if (changes & 0x10000) log("PRT EECS: %d", newval&0x10000);
|
||||||
|
// if (changes & 0x20000) log("PRT EECLK: %d", newval&0x20000);
|
||||||
|
if (changes & 0x40000) log("PRT contrast0: %d", newval&0x40000);
|
||||||
|
if (changes & 0x80000) log("PRT contrast1: %d", newval&0x80000);
|
||||||
|
if (changes & 0x100000) log("PRT contrast2: %d", newval&0x100000);
|
||||||
|
if (changes & 0x200000) log("PRT contrast3: %d", newval&0x200000);
|
||||||
|
if (changes & 0x400000) log("PRT case open: %d", newval&0x400000);
|
||||||
|
if (changes & 0x800000) log("PRT etna cf power: %d", newval&0x800000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emulator::diffInterrupts(uint16_t oldval, uint16_t newval) {
|
||||||
|
uint16_t changes = oldval ^ newval;
|
||||||
|
if (changes & 1) log("INTCHG external=%d", newval & 1);
|
||||||
|
if (changes & 2) log("INTCHG lowbat=%d", newval & 2);
|
||||||
|
if (changes & 4) log("INTCHG watchdog=%d", newval & 4);
|
||||||
|
if (changes & 8) log("INTCHG mediachg=%d", newval & 8);
|
||||||
|
if (changes & 0x10) log("INTCHG codec=%d", newval & 0x10);
|
||||||
|
if (changes & 0x20) log("INTCHG ext1=%d", newval & 0x20);
|
||||||
|
if (changes & 0x40) log("INTCHG ext2=%d", newval & 0x40);
|
||||||
|
if (changes & 0x80) log("INTCHG ext3=%d", newval & 0x80);
|
||||||
|
if (changes & 0x100) log("INTCHG timer1=%d", newval & 0x100);
|
||||||
|
if (changes & 0x200) log("INTCHG timer2=%d", newval & 0x200);
|
||||||
|
if (changes & 0x400) log("INTCHG rtcmatch=%d", newval & 0x400);
|
||||||
|
if (changes & 0x800) log("INTCHG tick=%d", newval & 0x800);
|
||||||
|
if (changes & 0x1000) log("INTCHG uart1=%d", newval & 0x1000);
|
||||||
|
if (changes & 0x2000) log("INTCHG uart2=%d", newval & 0x2000);
|
||||||
|
if (changes & 0x4000) log("INTCHG lcd=%d", newval & 0x4000);
|
||||||
|
if (changes & 0x8000) log("INTCHG spi=%d", newval & 0x8000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "arm710.h"
|
#include "emubase.h"
|
||||||
#include "wind_defs.h"
|
#include "wind_defs.h"
|
||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
#include "etna.h"
|
#include "etna.h"
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
class Windermere : public ARM710 {
|
namespace Windermere {
|
||||||
|
class Emulator : public EmuBase {
|
||||||
public:
|
public:
|
||||||
uint8_t ROM[0x1000000];
|
uint8_t ROM[0x1000000];
|
||||||
uint8_t ROM2[0x40000];
|
uint8_t ROM2[0x40000];
|
||||||
|
@ -26,15 +26,11 @@ private:
|
||||||
uint32_t kScan = 0;
|
uint32_t kScan = 0;
|
||||||
uint32_t rtc = 0;
|
uint32_t rtc = 0;
|
||||||
|
|
||||||
int64_t passedCycles = 0;
|
|
||||||
int64_t nextTickAt = 0;
|
|
||||||
Timer tc1, tc2;
|
Timer tc1, tc2;
|
||||||
UART uart1, uart2;
|
UART uart1, uart2;
|
||||||
Etna etna;
|
Etna etna;
|
||||||
bool halted = false, asleep = false;
|
bool halted = false, asleep = false;
|
||||||
|
|
||||||
std::unordered_set<uint32_t> _breakpoints;
|
|
||||||
|
|
||||||
uint32_t getRTC();
|
uint32_t getRTC();
|
||||||
|
|
||||||
uint32_t readReg8(uint32_t reg);
|
uint32_t readReg8(uint32_t reg);
|
||||||
|
@ -43,32 +39,28 @@ private:
|
||||||
void writeReg32(uint32_t reg, uint32_t value);
|
void writeReg32(uint32_t reg, uint32_t value);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool isPhysAddressValid(uint32_t addr) const;
|
|
||||||
MaybeU32 readPhysical(uint32_t physAddr, ValueSize valueSize) override;
|
MaybeU32 readPhysical(uint32_t physAddr, ValueSize valueSize) override;
|
||||||
bool writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) override;
|
bool writePhysical(uint32_t value, uint32_t physAddr, ValueSize valueSize) override;
|
||||||
|
|
||||||
const uint8_t *getLCDBuffer() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool configured = false;
|
bool configured = false;
|
||||||
void configure();
|
void configure();
|
||||||
|
|
||||||
void printRegs();
|
|
||||||
const char *identifyObjectCon(uint32_t ptr);
|
const char *identifyObjectCon(uint32_t ptr);
|
||||||
void fetchStr(uint32_t str, char *buf);
|
void fetchStr(uint32_t str, char *buf);
|
||||||
void fetchName(uint32_t obj, char *buf);
|
void fetchName(uint32_t obj, char *buf);
|
||||||
void fetchProcessFilename(uint32_t obj, char *buf);
|
void fetchProcessFilename(uint32_t obj, char *buf);
|
||||||
void debugPC(uint32_t pc);
|
void debugPC(uint32_t pc);
|
||||||
|
void diffPorts(uint32_t oldval, uint32_t newval);
|
||||||
uint8_t readKeyboard();
|
void diffInterrupts(uint16_t oldval, uint16_t newval);
|
||||||
public:
|
|
||||||
bool keyboardKeys[8*7] = {0};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Windermere();
|
Emulator();
|
||||||
void loadROM(const char *path);
|
void loadROM(uint8_t *buffer, size_t size) override;
|
||||||
void dumpRAM(const char *path);
|
void executeUntil(int64_t cycles) override;
|
||||||
void executeUntil(int64_t cycles);
|
int32_t getClockSpeed() const override { return CLOCK_SPEED; }
|
||||||
std::unordered_set<uint32_t> &breakpoints() { return _breakpoints; }
|
int getLCDWidth() const override;
|
||||||
uint64_t currentCycles() const { return passedCycles; }
|
int getLCDHeight() const override;
|
||||||
|
void readLCDIntoBuffer(uint8_t **lines) const override;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,58 @@
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include "../WindCore/clps7111.h"
|
||||||
|
#include "../WindCore/windermere.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
MainWindow w;
|
auto args = a.arguments();
|
||||||
|
|
||||||
|
QString romFile;
|
||||||
|
if (args.length() > 1)
|
||||||
|
romFile = args.first();
|
||||||
|
else
|
||||||
|
romFile = QFileDialog::getOpenFileName(nullptr, "Select a ROM");
|
||||||
|
if (romFile.isNull()) return 0;
|
||||||
|
|
||||||
|
// what do we have?
|
||||||
|
QFile f(romFile);
|
||||||
|
f.open(QFile::ReadOnly);
|
||||||
|
auto buffer = f.readAll();
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
if (buffer.size() < 0x400000) {
|
||||||
|
QMessageBox::critical(nullptr, "WindEmu", "Invalid ROM file!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmuBase *emu = nullptr;
|
||||||
|
uint8_t *romData = (uint8_t *)buffer.data();
|
||||||
|
|
||||||
|
// parse this ROM to learn what hardware it's for
|
||||||
|
int variantFile = *((uint32_t *)&romData[0x80 + 0x4C]) & 0xFFFFFFF;
|
||||||
|
if (variantFile < (buffer.size() - 8)) {
|
||||||
|
int variantImg = *((uint32_t *)&romData[variantFile + 4]) & 0xFFFFFFF;
|
||||||
|
if (variantImg < (buffer.size() - 0x70)) {
|
||||||
|
int variant = *((uint32_t *)&romData[variantImg + 0x60]);
|
||||||
|
|
||||||
|
if (variant == 0x7060001) {
|
||||||
|
// 5mx ROM
|
||||||
|
emu = new Windermere::Emulator;
|
||||||
|
} else if (variant == 0x5040001) {
|
||||||
|
// Osaris ROM
|
||||||
|
emu = new CLPS7111::Emulator;
|
||||||
|
} else {
|
||||||
|
QMessageBox::critical(nullptr, "WindEmu", "Unrecognised ROM file!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emu->loadROM(romData, buffer.size());
|
||||||
|
MainWindow w(emu);
|
||||||
w.show();
|
w.show();
|
||||||
|
|
||||||
return a.exec();
|
return a.exec();
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
#include "../WindCore/clps7111_defs.h"
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include "../WindCore/decoder.h"
|
#include "../WindCore/decoder.h"
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent) :
|
MainWindow::MainWindow(EmuBase *emu, QWidget *parent) :
|
||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
ui(new Ui::MainWindow)
|
ui(new Ui::MainWindow),
|
||||||
|
emu(emu)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->logView->setMaximumBlockCount(1000);
|
ui->logView->setMaximumBlockCount(1000);
|
||||||
|
|
||||||
emu = new CLPS7111;
|
|
||||||
emu->loadROM("/Users/ash/src/psion/Osaris.bin");
|
|
||||||
emu->setLogger([&](const char *str) {
|
emu->setLogger([&](const char *str) {
|
||||||
ui->logView->appendPlainText(str);
|
ui->logView->appendPlainText(str);
|
||||||
});
|
});
|
||||||
|
@ -102,68 +100,13 @@ void MainWindow::updateScreen()
|
||||||
ui->codeLabel->setText(codeLines.join('\n'));
|
ui->codeLabel->setText(codeLines.join('\n'));
|
||||||
|
|
||||||
// now, the actual screen
|
// now, the actual screen
|
||||||
const uint8_t *lcdBuf = emu->getLCDBuffer();
|
uint8_t *lines[1024];
|
||||||
if (lcdBuf) {
|
QImage img(emu->getLCDWidth(), emu->getLCDHeight(), QImage::Format_Grayscale8);
|
||||||
#if 0
|
for (int y = 0; y < img.height(); y++)
|
||||||
QImage img(640, 240, QImage::Format_Grayscale8);
|
lines[y] = img.scanLine(y);
|
||||||
|
emu->readLCDIntoBuffer(lines);
|
||||||
// fetch palette
|
|
||||||
int bpp = 1 << (lcdBuf[1] >> 4);
|
|
||||||
int ppb = 8 / bpp;
|
|
||||||
uint16_t palette[16];
|
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
palette[i] = lcdBuf[i*2] | ((lcdBuf[i*2+1] << 8) & 0xF00);
|
|
||||||
|
|
||||||
// build our image out
|
|
||||||
int lineWidth = (img.width() * bpp) / 8;
|
|
||||||
for (int y = 0; y < img.height(); y++) {
|
|
||||||
uint8_t *scanline = img.scanLine(y);
|
|
||||||
int lineOffs = 0x20 + (lineWidth * y);
|
|
||||||
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 palIdx = (byte >> shift) & mask;
|
|
||||||
int palValue = palette[palIdx];
|
|
||||||
|
|
||||||
palValue |= (palValue << 4);
|
|
||||||
scanline[x] = palValue ^ 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
QImage img(320, 200, QImage::Format_Grayscale8);
|
|
||||||
|
|
||||||
uint32_t lcdControl = emu->getLCDControl();
|
|
||||||
uint64_t lcdPalette = emu->getLCDPalette();
|
|
||||||
int bpp = 1;
|
|
||||||
if (lcdControl & 0x40000000) bpp = 2;
|
|
||||||
if (lcdControl & 0x80000000) bpp = 4;
|
|
||||||
int ppb = 8 / bpp;
|
|
||||||
|
|
||||||
// build our image out
|
|
||||||
int lineWidth = (img.width() * bpp) / 8;
|
|
||||||
for (int y = 0; y < img.height(); y++) {
|
|
||||||
uint8_t *scanline = img.scanLine(y);
|
|
||||||
int lineOffs = lineWidth * y;
|
|
||||||
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 palIdx = (byte >> shift) & mask;
|
|
||||||
int palValue;
|
|
||||||
if (bpp == 1)
|
|
||||||
palValue = palIdx * 255;
|
|
||||||
else
|
|
||||||
palValue = (lcdPalette >> (palIdx * 4)) & 0xF;
|
|
||||||
|
|
||||||
palValue |= (palValue << 4);
|
|
||||||
scanline[x] = palValue ^ 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ui->screen->setPixmap(QPixmap::fromImage(std::move(img)));
|
ui->screen->setPixmap(QPixmap::fromImage(std::move(img)));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -296,8 +239,10 @@ void MainWindow::on_stepInsnButton_clicked()
|
||||||
|
|
||||||
void MainWindow::execTimer()
|
void MainWindow::execTimer()
|
||||||
{
|
{
|
||||||
emu->executeUntil(emu->currentCycles() + (CLOCK_SPEED / 64));
|
if (emu) {
|
||||||
|
emu->executeUntil(emu->currentCycles() + (emu->getClockSpeed() / 64));
|
||||||
updateScreen();
|
updateScreen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_addBreakButton_clicked()
|
void MainWindow::on_addBreakButton_clicked()
|
||||||
|
@ -332,7 +277,7 @@ void MainWindow::updateMemory()
|
||||||
uint32_t virtBase = ui->memoryViewAddress->text().toUInt(nullptr, 16) & ~0xFF;
|
uint32_t virtBase = ui->memoryViewAddress->text().toUInt(nullptr, 16) & ~0xFF;
|
||||||
auto physBaseOpt = emu->virtToPhys(virtBase);
|
auto physBaseOpt = emu->virtToPhys(virtBase);
|
||||||
auto physBase = physBaseOpt.value_or(0xFFFFFFFF);
|
auto physBase = physBaseOpt.value_or(0xFFFFFFFF);
|
||||||
bool ok = physBaseOpt.has_value() && emu->isPhysAddressValid(physBase);
|
bool ok = physBaseOpt.has_value();
|
||||||
if (ok && (virtBase != physBase))
|
if (ok && (virtBase != physBase))
|
||||||
ui->physicalAddressLabel->setText(QStringLiteral("Physical: %1").arg(physBase, 8, 16, QLatin1Char('0')));
|
ui->physicalAddressLabel->setText(QStringLiteral("Physical: %1").arg(physBase, 8, 16, QLatin1Char('0')));
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define MAINWINDOW_H
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include "../WindCore/clps7111.h"
|
#include "../WindCore/emubase.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
@ -13,7 +13,7 @@ class MainWindow : public QMainWindow
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget *parent = nullptr);
|
explicit MainWindow(EmuBase *emu, QWidget *parent = nullptr);
|
||||||
~MainWindow() override;
|
~MainWindow() override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
@ -44,7 +44,7 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
CLPS7111 *emu;
|
EmuBase *emu;
|
||||||
QTimer *timer;
|
QTimer *timer;
|
||||||
void updateScreen();
|
void updateScreen();
|
||||||
void updateBreakpointsList();
|
void updateBreakpointsList();
|
||||||
|
|
Loading…
Reference in New Issue