mirror of
https://github.com/kinnay/Wii-U-Firmware-Emulator.git
synced 2024-05-16 11:20:16 -04:00
Implement low level DSP emulation to fix crash in AXInit
This commit is contained in:
parent
05d7afa58f
commit
490295e247
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -3,6 +3,6 @@ files/*
|
|||
logs/*
|
||||
main
|
||||
main.i64
|
||||
oprofile_data/
|
||||
!files/.gitignore
|
||||
!logs/.gitignore
|
||||
!files/dsp_irom.bin
|
||||
!files/dsp_drom.bin
|
||||
!logs/.gitignore
|
||||
|
|
|
@ -19,6 +19,7 @@ This emulator used to be written in both Python and C++. You can still find the
|
|||
| `WATCHPOINTS` | Enables memory watchpoints in the debugger. |
|
||||
| `BREAKPOINTS` | Enables instruction breakpoints in the debugger. |
|
||||
| `SYSLOG` | Writes the system log into `logs/syslog.txt`. |
|
||||
| `DSPDMA` | Logs DSP DMA transfers to `logs/dspdma.txt`. |
|
||||
|
||||
Additionally, you can adjust the log level in `src/main.cpp`. To disable warnings about unimplemented hardware features set the log level to `ERROR` or `NONE`.
|
||||
|
||||
|
@ -30,7 +31,7 @@ Using this emulator you can actually see what boot1, IOSU and Cafe OS look like
|
|||
| `help` | Print list of commands. |
|
||||
| `exit` | Stop the emulator. |
|
||||
| `quit` | Same as `exit`. |
|
||||
| `select arm/ppc0/ppc1/ppc2` | Select a processor to debug. |
|
||||
| `select arm/ppc0/ppc1/ppc2/dsp` | Select a processor to debug. |
|
||||
| `step (<steps>)` | Execute a fixed number of instructions on the current processor. |
|
||||
| `run` | Continue emulation normally. |
|
||||
| `reset` | Reset the emulator to its initial state. |
|
||||
|
@ -48,6 +49,7 @@ Using this emulator you can actually see what boot1, IOSU and Cafe OS look like
|
|||
| `print <expr>` | Evaluate the given expression and print the result. |
|
||||
| `trace` | Print stack trace on the current processor. |
|
||||
| `read virt/phys <address> <length>` | Read `length` bytes at the given virtual or physical `address` and print them both in hex and ascii characters. If a virtual address is given, the MMU of the current processor is used to translate the address. |
|
||||
| `read code/data <address> <length>` | Read `length` bytes at the given DSP memory address and print them in hex. |
|
||||
| `translate <address>` | Translate the given virtual address and print the physical address, using the MMU of the current processor. |
|
||||
| `memmap` | Print the virtual memory map of the current processor. |
|
||||
| `modules` | Print the list of loaded RPL files and the starting address of their .text segment. |
|
||||
|
|
BIN
files/dsp_drom.bin
Executable file
BIN
files/dsp_drom.bin
Executable file
Binary file not shown.
BIN
files/dsp_irom.bin
Executable file
BIN
files/dsp_irom.bin
Executable file
Binary file not shown.
|
@ -5,38 +5,39 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
template<class T>
|
||||
class Bits {
|
||||
public:
|
||||
Bits() {
|
||||
value = 0;
|
||||
}
|
||||
|
||||
inline uint32_t getfield(uint32_t mask) const {
|
||||
inline T getfield(T mask) const {
|
||||
return value & mask;
|
||||
}
|
||||
|
||||
inline void setfield(uint32_t mask, uint32_t value) {
|
||||
inline void setfield(T mask, T value) {
|
||||
this->value = (this->value & ~mask) | value;
|
||||
}
|
||||
|
||||
inline bool get(uint32_t mask) const {
|
||||
inline bool get(T mask) const {
|
||||
return value & mask;
|
||||
}
|
||||
|
||||
inline void set(uint32_t mask, bool enable) {
|
||||
inline void set(T mask, bool enable) {
|
||||
if (enable) value |= mask;
|
||||
else value &= ~mask;
|
||||
}
|
||||
|
||||
inline uint32_t operator =(uint32_t value) {
|
||||
inline T operator =(T value) {
|
||||
this->value = value;
|
||||
return value;
|
||||
}
|
||||
|
||||
inline operator uint32_t() const {
|
||||
inline operator T() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t value;
|
||||
T value;
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
class OutputTextStream : public RefCountedObj {
|
||||
public:
|
||||
template <class... Args>
|
||||
void write(const char *str, Args... args) {
|
||||
void write(std::string str, Args... args) {
|
||||
std::string s = StringUtils::format(str, args...);
|
||||
write(s);
|
||||
}
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
#define WATCHPOINTS 1
|
||||
#define BREAKPOINTS 1
|
||||
#define SYSLOG 1
|
||||
#define DSPDMA 1
|
||||
|
|
|
@ -59,8 +59,8 @@ public:
|
|||
|
||||
void triggerException(ExceptionType type);
|
||||
|
||||
Bits cpsr;
|
||||
Bits spsr;
|
||||
Bits<uint32_t> cpsr;
|
||||
Bits<uint32_t> spsr;
|
||||
|
||||
uint32_t regs[16];
|
||||
uint32_t regsUser[16];
|
||||
|
@ -70,11 +70,11 @@ public:
|
|||
uint32_t regsAbt[2];
|
||||
uint32_t regsUnd[2];
|
||||
|
||||
Bits spsrFiq;
|
||||
Bits spsrIrq;
|
||||
Bits spsrSvc;
|
||||
Bits spsrAbt;
|
||||
Bits spsrUnd;
|
||||
Bits<uint32_t> spsrFiq;
|
||||
Bits<uint32_t> spsrIrq;
|
||||
Bits<uint32_t> spsrSvc;
|
||||
Bits<uint32_t> spsrAbt;
|
||||
Bits<uint32_t> spsrUnd;
|
||||
|
||||
uint32_t control;
|
||||
uint32_t domain;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
|
||||
ARMProcessor::ARMProcessor(Emulator *emulator) :
|
||||
Processor(emulator, 0),
|
||||
Processor(emulator, 0, true),
|
||||
mmu(&emulator->physmem, &core),
|
||||
jit(&emulator->physmem, this),
|
||||
thumb(&emulator->physmem, this)
|
||||
|
|
808
src/cpu/dsp.cpp
Normal file
808
src/cpu/dsp.cpp
Normal file
|
@ -0,0 +1,808 @@
|
|||
|
||||
#include "cpu/dsp.h"
|
||||
|
||||
#include "common/exceptions.h"
|
||||
#include "common/fileutils.h"
|
||||
#include "common/logger.h"
|
||||
#include "common/buffer.h"
|
||||
#include "common/endian.h"
|
||||
|
||||
#include "physicalmemory.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
||||
static int64_t sign8(uint8_t value) {
|
||||
if (value & 0x80) {
|
||||
return (int64_t)value - 0x100;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
DSPStack::DSPStack(int capacity) {
|
||||
this->capacity = capacity;
|
||||
}
|
||||
|
||||
void DSPStack::clear() {
|
||||
stack.clear();
|
||||
}
|
||||
|
||||
void DSPStack::push(uint16_t value) {
|
||||
if (stack.size() == capacity) {
|
||||
runtime_error("DSP stack overflow");
|
||||
}
|
||||
stack.push_back(value);
|
||||
}
|
||||
|
||||
uint16_t DSPStack::peek() {
|
||||
if (stack.empty()) {
|
||||
runtime_error("DSP stack underflow");
|
||||
}
|
||||
return stack[stack.size() - 1];
|
||||
}
|
||||
|
||||
uint16_t DSPStack::pop() {
|
||||
uint16_t value = peek();
|
||||
stack.pop_back();
|
||||
return value;
|
||||
}
|
||||
|
||||
uint16_t DSPStack::get(int index) {
|
||||
if (index >= stack.size()) {
|
||||
runtime_error("DSP stack invalid index");
|
||||
}
|
||||
return stack[stack.size() - index - 1];
|
||||
}
|
||||
|
||||
bool DSPStack::empty() {
|
||||
return stack.empty();
|
||||
}
|
||||
|
||||
int DSPStack::size() {
|
||||
return stack.size();
|
||||
}
|
||||
|
||||
|
||||
DSPInterpreter::DSPInterpreter(Emulator *emulator) :
|
||||
Processor(emulator, 4, false),
|
||||
st {8, 8, 4, 4}
|
||||
{
|
||||
Buffer irom_data = FileUtils::load("files/dsp_irom.bin");
|
||||
if (irom_data.size() > sizeof(irom)) {
|
||||
runtime_error("DSP IROM is too large!");
|
||||
}
|
||||
|
||||
Buffer drom_data = FileUtils::load("files/dsp_drom.bin");
|
||||
if (drom_data.size() > sizeof(drom)) {
|
||||
runtime_error("DSP DROM is too large!");
|
||||
}
|
||||
|
||||
memcpy(irom, irom_data.get(), irom_data.size());
|
||||
memcpy(drom, drom_data.get(), drom_data.size());
|
||||
|
||||
dma_logger.init("logs/dspdma.txt");
|
||||
}
|
||||
|
||||
void DSPInterpreter::reset() {
|
||||
memset(iram, 0, sizeof(iram));
|
||||
memset(dram, 0, sizeof(dram));
|
||||
memset(ar, 0, sizeof(ix));
|
||||
memset(ix, 0, sizeof(ix));
|
||||
memset(wr, 0, sizeof(ix));
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
st[i].clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ac[i].value = 0;
|
||||
ax[i].value = 0;
|
||||
}
|
||||
|
||||
config = 0;
|
||||
status = 0;
|
||||
|
||||
pc = 0x8000;
|
||||
|
||||
irq = false;
|
||||
|
||||
timer = 10000;
|
||||
|
||||
mailbox_in_h = 0;
|
||||
mailbox_in_l = 0;
|
||||
mailbox_out_h = 0;
|
||||
mailbox_out_l = 0;
|
||||
|
||||
dma_addr_main = 0;
|
||||
dma_addr_dsp = 0;
|
||||
dma_control = 0;
|
||||
|
||||
ffd2 = 0;
|
||||
|
||||
#if STATS
|
||||
instrs_executed = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
dma_transfers[i] = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t DSPInterpreter::fetch() {
|
||||
return read_code(pc++);
|
||||
}
|
||||
|
||||
uint16_t DSPInterpreter::read_code(uint16_t addr) {
|
||||
if (addr < 0x2000) {
|
||||
return Endian::swap16(iram[addr]);
|
||||
}
|
||||
|
||||
if (0x8000 <= addr && addr < 0x9000) {
|
||||
return Endian::swap16(irom[addr - 0x8000]);
|
||||
}
|
||||
|
||||
runtime_error("DSP invalid instruction address: 0x%X", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t DSPInterpreter::read(uint16_t addr) {
|
||||
#if WATCHPOINTS
|
||||
checkWatchpoints(false, false, addr, 1);
|
||||
#endif
|
||||
|
||||
if (addr < 0x3000) return Endian::swap16(dram[addr]);
|
||||
if (addr < 0x3800) return Endian::swap16(drom[addr - 0x3000]);
|
||||
|
||||
if (addr == DSCR) return dma_control;
|
||||
if (addr == DSBL) return 0;
|
||||
|
||||
if (addr == FFD2) return ffd2;
|
||||
|
||||
if (addr == DMBH) return mailbox_out_h;
|
||||
if (addr == CMBH) return mailbox_in_h;
|
||||
if (addr == CMBL) {
|
||||
mailbox_in_h &= 0x7FFF;
|
||||
return mailbox_in_l;
|
||||
}
|
||||
|
||||
Logger::warning("Unknown DSP read at 0x%X: 0x%X", pc, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSPInterpreter::write(uint16_t addr, uint16_t value) {
|
||||
#if WATCHPOINTS
|
||||
checkWatchpoints(true, false, addr, 1);
|
||||
#endif
|
||||
|
||||
if (addr < 0x3000) dram[addr] = Endian::swap16(value);
|
||||
|
||||
else if (addr == DMA_ADDR_MAIN_H) {}
|
||||
else if (addr == DRC_AI_BUFF_L) {}
|
||||
else if (addr == DRC_AI_BUFF_M) {}
|
||||
else if (addr == DRC_AI_BUFF_H) {}
|
||||
else if (addr == TV_AI_BUFF_L) {}
|
||||
else if (addr == TV_AI_BUFF_M) {}
|
||||
else if (addr == TV_AI_BUFF_H) {}
|
||||
|
||||
else if (addr == DSCR) dma_control = value;
|
||||
else if (addr == DSBL) {
|
||||
if (value & 1) {
|
||||
runtime_error("DSP DMA length is not aligned");
|
||||
}
|
||||
|
||||
void *ptr = dma_ptr(dma_addr_dsp, value / 2, dma_control & 2);
|
||||
if (!ptr) {
|
||||
runtime_error("DSP DMA address out of range");
|
||||
}
|
||||
|
||||
if (dma_control & 1) {
|
||||
physmem->write(dma_addr_main, ptr, value);
|
||||
}
|
||||
else {
|
||||
physmem->read(dma_addr_main, ptr, value);
|
||||
}
|
||||
|
||||
#if DSPDMA
|
||||
std::string name1 = "CPU";
|
||||
std::string name2 = dma_control & 2 ? "IRAM" : "DRAM";
|
||||
|
||||
std::string addr1 = StringUtils::format("%08X", dma_addr_main);
|
||||
std::string addr2 = StringUtils::format("%04X", dma_addr_dsp);
|
||||
|
||||
if (dma_control & 1) {
|
||||
std::swap(name1, name2);
|
||||
std::swap(addr1, addr2);
|
||||
}
|
||||
|
||||
std::string message = StringUtils::format(
|
||||
"%04X: %s -> %s, %s -> %s (%i bytes)\n",
|
||||
pc, name1, name2, addr1, addr2, value
|
||||
);
|
||||
dma_logger.write(message);
|
||||
#endif
|
||||
|
||||
#if WATCHPOINTS
|
||||
if (!(dma_control & 2)) {
|
||||
checkWatchpoints(!(dma_control & 1), false, dma_addr_dsp, value / 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STATS
|
||||
dma_transfers[dma_control & 3]++;
|
||||
#endif
|
||||
}
|
||||
else if (addr == DSPA) dma_addr_dsp = value;
|
||||
else if (addr == DSMAH) {
|
||||
value &= 0x7FFF;
|
||||
dma_addr_main &= 0xFFFF;
|
||||
dma_addr_main |= value << 16;
|
||||
}
|
||||
else if (addr == DSMAL) {
|
||||
dma_addr_main &= ~0xFFFF;
|
||||
dma_addr_main |= value;
|
||||
}
|
||||
|
||||
else if (addr == FFD2) ffd2 = value;
|
||||
|
||||
else if (addr == DIRQ) irq = value & 1;
|
||||
|
||||
else if (addr == DMBH) mailbox_out_h = value;
|
||||
else if (addr == DMBL) {
|
||||
mailbox_out_l = value;
|
||||
mailbox_out_h |= 0x8000;
|
||||
}
|
||||
|
||||
else {
|
||||
Logger::warning("Unknown DSP write at 0x%X: 0x%X (0x%04X)", pc, addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
void *DSPInterpreter::dma_ptr(uint16_t addr, uint16_t length, bool code) {
|
||||
if (code) {
|
||||
if (addr < 0x2000 && length <= 0x2000 - addr) {
|
||||
return iram + addr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (addr < 0x3000 && length <= 0x3000 - addr) {
|
||||
return dram + addr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16_t DSPInterpreter::readreg(int reg) {
|
||||
if (AR0 <= reg && reg <= AR3) return ar[reg - AR0];
|
||||
if (IX0 <= reg && reg <= IX3) return ix[reg - IX0];
|
||||
if (WR0 <= reg && reg <= WR3) return wr[reg - WR0];
|
||||
if (ST0 <= reg && reg <= ST3) return st[reg - ST0].pop();
|
||||
|
||||
if (reg == AX0L) return ax[0].l;
|
||||
if (reg == AX1L) return ax[1].l;
|
||||
if (reg == AX0H) return ax[0].h;
|
||||
if (reg == AX1H) return ax[1].h;
|
||||
|
||||
if (reg == AC0L) return ac[0].l;
|
||||
if (reg == AC1L) return ac[1].l;
|
||||
if (reg == AC0M) return ac[0].m;
|
||||
if (reg == AC1M) return ac[1].m;
|
||||
if (reg == AC0H) return ac[0].h;
|
||||
if (reg == AC1H) return ac[1].h;
|
||||
|
||||
if (reg == CONFIG) return config;
|
||||
if (reg == SR) return status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSPInterpreter::writereg(int reg, uint16_t value) {
|
||||
if (AR0 <= reg && reg <= AR3) ar[reg - AR0] = value;
|
||||
else if (IX0 <= reg && reg <= IX3) ix[reg - IX0] = value;
|
||||
else if (ST0 <= reg && reg <= ST3) st[reg - ST0].push(value);
|
||||
|
||||
else if (reg == CONFIG) config = value;
|
||||
else if (reg == SR) status = value;
|
||||
|
||||
else if (reg == AX0L || reg == AX1L) ax[reg - AX0L].l = value;
|
||||
else if (reg == AX0H || reg == AX1H) ax[reg - AX0H].h = value;
|
||||
|
||||
else if (reg == AC0L || reg == AC1L) ac[reg - AC0L].l = value;
|
||||
else if (reg == AC0H || reg == AC1H) ac[reg - AC0H].h = value;
|
||||
else if (reg == AC0M || reg == AC1M) {
|
||||
ac[reg - AC0M].m = value;
|
||||
if (status.get(SXM)) {
|
||||
ac[reg - AC0M].h = value & 0x8000 ? 0xFF : 0;
|
||||
ac[reg - AC0M].l = 0;
|
||||
}
|
||||
}
|
||||
else if (reg == AC1L) ac[1].l = value;
|
||||
else if (reg == AC0M) ac[0].m = value;
|
||||
else if (reg == AC1M) ac[1].m = value;
|
||||
else if (reg == AC0H) ac[0].h = value;
|
||||
else if (reg == AC1H) ac[1].h = value;
|
||||
else {
|
||||
runtime_error("Unsupported DSP register: %i", reg);
|
||||
}
|
||||
}
|
||||
|
||||
// When the dsp_os_switch task receives the 'stop' message
|
||||
// it waits until bit 1 << 2 is set in @dspState. This bit
|
||||
// is only set by the exception handler at address 0xC, but
|
||||
// this exception isn't documented or implemented by dolphin-emu.
|
||||
//
|
||||
// The PPC doesn't seem to do anything that might trigger
|
||||
// the exception. For now I'm assuming that the exception
|
||||
// is triggered periodically by the DSP itself. This at
|
||||
// least prevents the DSP from getting stuck while waiting
|
||||
// for the bit in @dspState.
|
||||
void DSPInterpreter::update_timer() {
|
||||
if (!(status.get(1 << 10))) { // No idea if this is correct
|
||||
if (timer-- == 0) {
|
||||
trigger_exception(6);
|
||||
timer = 10000; // This is arbitrary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DSPInterpreter::trigger_exception(int type) {
|
||||
writereg(ST0, pc);
|
||||
writereg(ST1, status);
|
||||
status.set(1 << 10, true);
|
||||
pc = type * 2;
|
||||
}
|
||||
|
||||
void DSPInterpreter::step() {
|
||||
update_timer();
|
||||
|
||||
uint16_t prev = this->pc;
|
||||
|
||||
uint16_t instr = fetch();
|
||||
if (instr == 0) nop(instr);
|
||||
else if ((instr & 0xFFFC) == 0x0008) iar(instr);
|
||||
else if ((instr & 0xFFE0) == 0x0040) loop(instr);
|
||||
else if ((instr & 0xFFE0) == 0x0060) bloop(instr);
|
||||
else if ((instr & 0xFFE0) == 0x0080) lri(instr);
|
||||
else if ((instr & 0xFFE0) == 0x00C0) lr(instr);
|
||||
else if ((instr & 0xFFE0) == 0x00E0) sr(instr);
|
||||
else if ((instr & 0xFEFC) == 0x0210) ilrr(instr);
|
||||
else if ((instr & 0xFEFC) == 0x0218) ilrri(instr);
|
||||
else if ((instr & 0xFEFF) == 0x0240) andi(instr);
|
||||
else if ((instr & 0xFEFF) == 0x0260) ori(instr);
|
||||
else if ((instr & 0xFFF0) == 0x0290) jcc(instr);
|
||||
else if ((instr & 0xFEFF) == 0x02A0) andf(instr);
|
||||
else if ((instr & 0xFFFF) == 0x02BF) call(instr);
|
||||
else if ((instr & 0xFEFF) == 0x02C0) andcf(instr);
|
||||
else if ((instr & 0xFFFF) == 0x02DF) ret(instr);
|
||||
else if ((instr & 0xFFFF) == 0x02FF) rti(instr);
|
||||
else if ((instr & 0xFE00) == 0x0400) addis(instr);
|
||||
else if ((instr & 0xF800) == 0x0800) lris(instr);
|
||||
else if ((instr & 0xFF00) == 0x1100) bloopi(instr);
|
||||
else if ((instr & 0xFFF8) == 0x1200) sbset(instr);
|
||||
else if ((instr & 0xFFF8) == 0x1300) sbclr(instr);
|
||||
else if ((instr & 0xFEC0) == 0x1400) lsl(instr);
|
||||
else if ((instr & 0xFEC0) == 0x1440) lsr(instr);
|
||||
else if ((instr & 0xFF00) == 0x1600) si(instr);
|
||||
else if ((instr & 0xFF1F) == 0x170F) jmpr(instr);
|
||||
else if ((instr & 0xFF1F) == 0x171F) callr(instr);
|
||||
else if ((instr & 0xFF80) == 0x1800) lrr(instr);
|
||||
else if ((instr & 0xFF80) == 0x1900) lrri(instr);
|
||||
else if ((instr & 0xFF80) == 0x1980) lrrn(instr);
|
||||
else if ((instr & 0xFF80) == 0x1A00) srr(instr);
|
||||
else if ((instr & 0xFF80) == 0x1B00) srri(instr);
|
||||
else if ((instr & 0xFF80) == 0x1B80) srrn(instr);
|
||||
else if ((instr & 0xFC00) == 0x1C00) mrr(instr);
|
||||
else if ((instr & 0xF800) == 0x2000) lrs(instr);
|
||||
else if ((instr & 0xF800) == 0x2800) srs(instr);
|
||||
else if ((instr & 0xFC80) == 0x3000) xorr(instr);
|
||||
else if ((instr & 0xFC80) == 0x3080) xorc(instr);
|
||||
else if ((instr & 0xFC80) == 0x3400) andr(instr);
|
||||
else if ((instr & 0xFC80) == 0x3800) orr(instr);
|
||||
else if ((instr & 0xFE80) == 0x3E00) orc(instr);
|
||||
else if ((instr & 0xF800) == 0x4000) addr(instr);
|
||||
else if ((instr & 0xFC00) == 0x4800) addax(instr);
|
||||
else if ((instr & 0xFE00) == 0x4C00) add(instr);
|
||||
else if ((instr & 0xF800) == 0x5000) subr(instr);
|
||||
else if ((instr & 0xFE00) == 0x5C00) sub(instr);
|
||||
else if ((instr & 0xFE00) == 0x7400) incm(instr);
|
||||
else if ((instr & 0xFE00) == 0x7600) inc(instr);
|
||||
else if ((instr & 0xF700) == 0x8100) clr(instr);
|
||||
else if ((instr & 0xFF00) == 0x8200) cmp(instr);
|
||||
else if ((instr & 0xFE00) == 0x8A00) setam(instr);
|
||||
else if ((instr & 0xFE00) == 0x8C00) setsu(instr);
|
||||
else if ((instr & 0xFE00) == 0x8E00) sxm(instr);
|
||||
else if ((instr & 0xF700) == 0xB100) tst(instr);
|
||||
else {
|
||||
runtime_error("Unknown DSP instruction at 0x%X: 0x%04X", pc - 1, instr);
|
||||
}
|
||||
|
||||
if (!st[2].empty() && st[2].peek() == prev) {
|
||||
uint16_t counter = st[3].pop() - 1;
|
||||
if (counter == 0) {
|
||||
st[0].pop();
|
||||
st[2].pop();
|
||||
}
|
||||
else {
|
||||
st[3].push(counter);
|
||||
pc = st[0].peek();
|
||||
}
|
||||
}
|
||||
|
||||
#if STATS
|
||||
instrs_executed++;
|
||||
#endif
|
||||
|
||||
#if BREAKPOINTS
|
||||
checkBreakpoints(pc);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DSPInterpreter::skip() {
|
||||
uint16_t instr = fetch();
|
||||
if ((instr & 0xFFE0) == 0x0060) pc++; // bloop
|
||||
else if ((instr & 0xFFE0) == 0x0080) pc++; // lri
|
||||
else if ((instr & 0xFFE0) == 0x00C0) pc++; // lr
|
||||
else if ((instr & 0xFFE0) == 0x00E0) pc++; // sr
|
||||
else if ((instr & 0xFEFF) == 0x0240) pc++; // andi
|
||||
else if ((instr & 0xFEFF) == 0x0260) pc++; // ori
|
||||
else if ((instr & 0xFFF0) == 0x0290) pc++; // jcc
|
||||
else if ((instr & 0xFEFF) == 0x02A0) pc++; // andf
|
||||
else if ((instr & 0xFFFF) == 0x02BF) pc++; // call
|
||||
else if ((instr & 0xFEFF) == 0x02C0) pc++; // andcf
|
||||
else if ((instr & 0xFF00) == 0x1100) pc++; // bloopi
|
||||
else if ((instr & 0xFF00) == 0x1600) pc++; // si
|
||||
}
|
||||
|
||||
bool DSPInterpreter::checkcond(int cond) {
|
||||
if (cond == 0) return status.get(O) == status.get(S);
|
||||
if (cond == 1) return status.get(O) != status.get(S);
|
||||
if (cond == 2) return status.get(O) == status.get(S) && !status.get(Z);
|
||||
if (cond == 4) return !status.get(Z);
|
||||
if (cond == 5) return status.get(Z);
|
||||
if (cond == 12) return !status.get(LZ);
|
||||
if (cond == 13) return status.get(LZ);
|
||||
if (cond == 15) return true;
|
||||
|
||||
runtime_error("Unsupported DSP condition: %i", cond);
|
||||
return false;
|
||||
}
|
||||
|
||||
void DSPInterpreter::updateflags(uint64_t value) {
|
||||
status.set(Z, value == 0);
|
||||
status.set(S, value >> 39);
|
||||
}
|
||||
|
||||
uint64_t DSPInterpreter::add_with_flags(uint64_t v1, uint64_t v2) {
|
||||
v1 &= 0xFFFFFFFFFF;
|
||||
v2 &= 0xFFFFFFFFFF;
|
||||
|
||||
uint64_t result = v1 + v2;
|
||||
bool s1 = v1 >> 39;
|
||||
bool s2 = v2 >> 39;
|
||||
bool s3 = (result >> 39) & 1;
|
||||
status.set(C, result >> 40);
|
||||
status.set(O, s1 == s2 && s1 != s3);
|
||||
|
||||
result &= 0xFFFFFFFFFF;
|
||||
|
||||
updateflags(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void DSPInterpreter::doext(uint16_t instr) {
|
||||
uint8_t ext = instr & 0xFF;
|
||||
if (ext) {
|
||||
runtime_error("Unknown extended opcode at 0x%X: 0x%02X", pc, ext);
|
||||
}
|
||||
}
|
||||
|
||||
void DSPInterpreter::add(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
ac[d].value = add_with_flags(ac[d].value, ac[1 - d].value);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::addax(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
int s = (instr >> 9) & 1;
|
||||
ac[d].value = add_with_flags(ac[d].value, ax[s].value);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::addis(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
ac[d].value = add_with_flags(ac[d].value, sign8(instr & 0xFF) << 16);
|
||||
}
|
||||
|
||||
void DSPInterpreter::addr(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
int s = (instr >> 9) & 3;
|
||||
ac[d].value = add_with_flags(ac[d].value, readreg(24 + s) << 16);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::andcf(uint16_t instr) {
|
||||
uint16_t mask = fetch();
|
||||
uint16_t result = ac[(instr >> 8) & 1].m & mask;
|
||||
status.set(LZ, result == mask);
|
||||
}
|
||||
|
||||
void DSPInterpreter::andf(uint16_t instr) {
|
||||
uint16_t mask = fetch();
|
||||
uint16_t result = ac[(instr >> 8) & 1].m & mask;
|
||||
status.set(LZ, result == 0);
|
||||
}
|
||||
|
||||
void DSPInterpreter::andi(uint16_t instr) {
|
||||
int r = (instr >> 8) & 1;
|
||||
ac[r].m &= fetch();
|
||||
updateflags(ac[r].value);
|
||||
}
|
||||
|
||||
void DSPInterpreter::andr(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
int s = (instr >> 9) & 1;
|
||||
ac[d].m &= ax[s].h;
|
||||
updateflags(ac[d].value);
|
||||
doext(instr & 0x7F);
|
||||
}
|
||||
|
||||
void DSPInterpreter::bloop(uint16_t instr) {
|
||||
uint16_t count = readreg(instr & 0x1F);
|
||||
uint16_t end = fetch();
|
||||
if (count) {
|
||||
writereg(ST0, pc);
|
||||
writereg(ST2, end);
|
||||
writereg(ST3, count);
|
||||
}
|
||||
else {
|
||||
pc = end;
|
||||
skip();
|
||||
}
|
||||
}
|
||||
|
||||
void DSPInterpreter::bloopi(uint16_t instr) {
|
||||
uint16_t count = instr & 0xFF;
|
||||
uint16_t end = fetch();
|
||||
if (count) {
|
||||
writereg(ST0, pc);
|
||||
writereg(ST2, end);
|
||||
writereg(ST3, count);
|
||||
}
|
||||
else {
|
||||
pc = end;
|
||||
skip();
|
||||
}
|
||||
}
|
||||
|
||||
void DSPInterpreter::call(uint16_t instr) {
|
||||
writereg(ST0, pc + 1);
|
||||
pc = fetch();
|
||||
}
|
||||
|
||||
void DSPInterpreter::callr(uint16_t instr) {
|
||||
writereg(ST0, pc);
|
||||
pc = readreg((instr >> 5) & 7);
|
||||
}
|
||||
|
||||
void DSPInterpreter::clr(uint16_t instr) {
|
||||
ac[(instr >> 11) & 1].value = 0;
|
||||
updateflags(0);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::cmp(uint16_t instr) {
|
||||
add_with_flags(ac[0].value, -ac[1].value);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::iar(uint16_t instr) {
|
||||
ar[instr & 3]++;
|
||||
}
|
||||
|
||||
void DSPInterpreter::ilrr(uint16_t instr) {
|
||||
ac[(instr >> 8) & 1].m = read_code(ar[instr & 3]);
|
||||
}
|
||||
|
||||
void DSPInterpreter::ilrri(uint16_t instr) {
|
||||
ac[(instr >> 8) & 1].m = read_code(ar[instr & 3]++);
|
||||
}
|
||||
|
||||
void DSPInterpreter::inc(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
ac[d].value = add_with_flags(ac[d].value, 1);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::incm(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
ac[d].value = add_with_flags(ac[d].value, 1 << 16);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::jcc(uint16_t instr) {
|
||||
uint16_t target = fetch();
|
||||
if (checkcond(instr & 0xF)) {
|
||||
pc = target;
|
||||
}
|
||||
}
|
||||
|
||||
void DSPInterpreter::jmpr(uint16_t instr) {
|
||||
pc = readreg((instr >> 5) & 7);
|
||||
}
|
||||
|
||||
void DSPInterpreter::loop(uint16_t instr) {
|
||||
uint16_t count = readreg(instr & 0x1F);
|
||||
if (count) {
|
||||
writereg(ST0, pc);
|
||||
writereg(ST2, pc);
|
||||
writereg(ST3, count);
|
||||
}
|
||||
else {
|
||||
skip();
|
||||
}
|
||||
}
|
||||
|
||||
void DSPInterpreter::lr(uint16_t instr) {
|
||||
writereg(instr & 0x1F, read(fetch()));
|
||||
}
|
||||
|
||||
void DSPInterpreter::lri(uint16_t instr) {
|
||||
writereg(instr & 0x1F, fetch());
|
||||
}
|
||||
|
||||
void DSPInterpreter::lris(uint16_t instr) {
|
||||
writereg(24 + ((instr >> 8) & 7), sign8(instr & 0xFF));
|
||||
}
|
||||
|
||||
void DSPInterpreter::lrr(uint16_t instr) {
|
||||
writereg(instr & 0x1F, read(ar[(instr >> 5) & 3]));
|
||||
}
|
||||
|
||||
void DSPInterpreter::lrri(uint16_t instr) {
|
||||
writereg(instr & 0x1F, read(ar[(instr >> 5) & 3]++));
|
||||
}
|
||||
|
||||
void DSPInterpreter::lrrn(uint16_t instr) {
|
||||
int s = (instr >> 5) & 3;
|
||||
writereg(instr & 0x1F, read(ar[s]));
|
||||
ar[s] += ix[s];
|
||||
}
|
||||
|
||||
void DSPInterpreter::lrs(uint16_t instr) {
|
||||
writereg(24 + ((instr >> 8) & 7), read(sign8(instr & 0xFF)));
|
||||
}
|
||||
|
||||
void DSPInterpreter::lsl(uint16_t instr) {
|
||||
int r = (instr >> 8) & 1;
|
||||
ac[r].value <<= instr & 0x3F;
|
||||
ac[r].value &= 0xFFFFFFFFFF;
|
||||
updateflags(ac[r].value);
|
||||
}
|
||||
|
||||
void DSPInterpreter::lsr(uint16_t instr) {
|
||||
int r = (instr >> 8) & 1;
|
||||
ac[r].value >>= 64 - (instr & 0x3F);
|
||||
updateflags(ac[r].value);
|
||||
}
|
||||
|
||||
void DSPInterpreter::mrr(uint16_t instr) {
|
||||
writereg((instr >> 5) & 0x1F, readreg(instr & 0x1F));
|
||||
}
|
||||
|
||||
void DSPInterpreter::nop(uint16_t instr) {}
|
||||
|
||||
void DSPInterpreter::orc(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
ac[d].m |= ac[1 - d].m;
|
||||
updateflags(ac[d].value);
|
||||
doext(instr & 0x7F);
|
||||
}
|
||||
|
||||
void DSPInterpreter::ori(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
ac[d].m |= fetch();
|
||||
updateflags(ac[d].value);
|
||||
}
|
||||
|
||||
void DSPInterpreter::orr(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
int s = (instr >> 9) & 1;
|
||||
ac[d].m |= ax[s].h;
|
||||
updateflags(ac[d].value);
|
||||
doext(instr & 0x7F);
|
||||
}
|
||||
|
||||
void DSPInterpreter::ret(uint16_t instr) {
|
||||
pc = readreg(ST0);
|
||||
}
|
||||
|
||||
void DSPInterpreter::rti(uint16_t instr) {
|
||||
status = readreg(ST1);
|
||||
pc = readreg(ST0);
|
||||
}
|
||||
|
||||
void DSPInterpreter::sbclr(uint16_t instr) {
|
||||
status.set(1 << ((instr & 7) + 6), false);
|
||||
}
|
||||
|
||||
void DSPInterpreter::sbset(uint16_t instr) {
|
||||
status.set(1 << ((instr & 7) + 6), true);
|
||||
}
|
||||
|
||||
void DSPInterpreter::setam(uint16_t instr) {
|
||||
status.set(AM, (instr >> 8) & 1);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::setsu(uint16_t instr) {
|
||||
status.set(SU, (instr >> 8) & 1);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::si(uint16_t instr) {
|
||||
write(sign8(instr & 0xFF), fetch());
|
||||
}
|
||||
|
||||
void DSPInterpreter::sr(uint16_t instr) {
|
||||
write(fetch(), readreg(instr & 0x1F));
|
||||
}
|
||||
|
||||
void DSPInterpreter::srr(uint16_t instr) {
|
||||
int d = (instr >> 5) & 3;
|
||||
write(ar[d], readreg(instr & 0x1F));
|
||||
}
|
||||
|
||||
void DSPInterpreter::srri(uint16_t instr) {
|
||||
int d = (instr >> 5) & 3;
|
||||
write(ar[d]++, readreg(instr & 0x1F));
|
||||
}
|
||||
|
||||
void DSPInterpreter::srrn(uint16_t instr) {
|
||||
int d = (instr >> 5) & 3;
|
||||
write(ar[d], readreg(instr & 0x1F));
|
||||
ar[d] += ix[d];
|
||||
}
|
||||
|
||||
void DSPInterpreter::srs(uint16_t instr) {
|
||||
write(sign8(instr & 0xFF), readreg(24 + ((instr >> 8) & 7)));
|
||||
}
|
||||
|
||||
void DSPInterpreter::sub(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
ac[d].value = add_with_flags(ac[d].value, -ac[1 - d].value);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::subr(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
int s = (instr >> 9) & 3;
|
||||
ac[d].value = add_with_flags(ac[d].value, -(readreg(24 + s) << 16));
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::sxm(uint16_t instr) {
|
||||
status.set(SXM, (instr >> 8) & 1);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::tst(uint16_t instr) {
|
||||
updateflags(ac[(instr >> 11) & 1].value);
|
||||
doext(instr & 0xFF);
|
||||
}
|
||||
|
||||
void DSPInterpreter::xorc(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
ac[d].m ^= ac[1 - d].m;
|
||||
updateflags(ac[d].value);
|
||||
doext(instr & 0x7F);
|
||||
}
|
||||
|
||||
void DSPInterpreter::xorr(uint16_t instr) {
|
||||
int d = (instr >> 8) & 1;
|
||||
int s = (instr >> 9) & 1;
|
||||
ac[d].m ^= ax[s].h;
|
||||
updateflags(ac[d].value);
|
||||
doext(instr & 0x7F);
|
||||
}
|
264
src/cpu/dsp.h
Normal file
264
src/cpu/dsp.h
Normal file
|
@ -0,0 +1,264 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "cpu/processor.h"
|
||||
#include "common/bits.h"
|
||||
#include "logger.h"
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
class DSPStack {
|
||||
public:
|
||||
DSPStack(int capacity);
|
||||
|
||||
void clear();
|
||||
void push(uint16_t value);
|
||||
uint16_t peek();
|
||||
uint16_t pop();
|
||||
|
||||
uint16_t get(int index);
|
||||
bool empty();
|
||||
int size();
|
||||
|
||||
private:
|
||||
int capacity;
|
||||
std::vector<uint16_t> stack;
|
||||
};
|
||||
|
||||
|
||||
class DSPInterpreter : public Processor {
|
||||
public:
|
||||
union Acc40 {
|
||||
struct {
|
||||
uint16_t l;
|
||||
uint16_t m;
|
||||
uint8_t h;
|
||||
};
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
union Acc32 {
|
||||
struct {
|
||||
uint16_t l;
|
||||
uint16_t h;
|
||||
};
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
enum Status {
|
||||
C = 1 << 0,
|
||||
O = 1 << 1,
|
||||
Z = 1 << 2,
|
||||
S = 1 << 3,
|
||||
LZ = 1 << 6,
|
||||
AM = 1 << 13,
|
||||
SXM = 1 << 14,
|
||||
SU = 1 << 15
|
||||
};
|
||||
|
||||
// The Wii U model has many new registers.
|
||||
enum Memory {
|
||||
DMA_ADDR_MAIN_H = 0xFFBA,
|
||||
|
||||
DRC_AI_BUFF_M = 0xFFBE,
|
||||
DRC_AI_BUFF_L = 0xFFBF,
|
||||
|
||||
DSCR = 0xFFC9,
|
||||
|
||||
DRC_AI_BUFF_H = 0xFFCA,
|
||||
|
||||
DSBL = 0xFFCB,
|
||||
DSPA = 0xFFCD,
|
||||
DSMAH = 0xFFCE,
|
||||
DSMAL = 0xFFCF,
|
||||
|
||||
TV_AI_BUFF_L = 0xFFD0,
|
||||
|
||||
FFD2 = 0xFFD2,
|
||||
|
||||
TV_AI_BUFF_H = 0xFFEE,
|
||||
TV_AI_BUFF_M = 0xFFFA,
|
||||
|
||||
DIRQ = 0xFFFB,
|
||||
DMBH = 0xFFFC,
|
||||
DMBL = 0xFFFD,
|
||||
CMBH = 0xFFFE,
|
||||
CMBL = 0xFFFF
|
||||
};
|
||||
|
||||
enum Register {
|
||||
AR0 = 0,
|
||||
AR1 = 1,
|
||||
AR2 = 2,
|
||||
AR3 = 3,
|
||||
|
||||
IX0 = 4,
|
||||
IX1 = 5,
|
||||
IX2 = 6,
|
||||
IX3 = 7,
|
||||
|
||||
WR0 = 8,
|
||||
WR1 = 9,
|
||||
WR2 = 10,
|
||||
WR3 = 11,
|
||||
|
||||
ST0 = 12,
|
||||
ST1 = 13,
|
||||
ST2 = 14,
|
||||
ST3 = 15,
|
||||
|
||||
AC0H = 16,
|
||||
AC1H = 17,
|
||||
|
||||
CONFIG = 18,
|
||||
|
||||
SR = 19,
|
||||
|
||||
PRODL = 20,
|
||||
PRODM1 = 21,
|
||||
PRODH = 22,
|
||||
PRODM2 = 23,
|
||||
|
||||
AX0L = 24,
|
||||
AX1L = 25,
|
||||
AX0H = 26,
|
||||
AX1H = 27,
|
||||
|
||||
AC0L = 28,
|
||||
AC1L = 29,
|
||||
AC0M = 30,
|
||||
AC1M = 31
|
||||
};
|
||||
|
||||
DSPInterpreter(Emulator *emulator);
|
||||
|
||||
void reset();
|
||||
void step();
|
||||
|
||||
uint16_t readreg(int reg);
|
||||
|
||||
bool irq;
|
||||
|
||||
int timer;
|
||||
|
||||
uint16_t mailbox_in_h;
|
||||
uint16_t mailbox_in_l;
|
||||
uint16_t mailbox_out_h;
|
||||
uint16_t mailbox_out_l;
|
||||
|
||||
uint32_t dma_addr_main;
|
||||
uint16_t dma_addr_dsp;
|
||||
uint16_t dma_control;
|
||||
|
||||
uint16_t ffd2;
|
||||
|
||||
uint16_t pc;
|
||||
|
||||
uint16_t ar[4];
|
||||
uint16_t ix[4];
|
||||
uint16_t wr[4];
|
||||
DSPStack st[4];
|
||||
|
||||
Acc40 ac[2];
|
||||
Acc32 ax[2];
|
||||
|
||||
uint16_t config;
|
||||
|
||||
Bits<uint16_t> status;
|
||||
|
||||
uint16_t iram[0x2000];
|
||||
uint16_t irom[0x1000];
|
||||
|
||||
// The Wii U model increased the amount of DRAM
|
||||
// compared to the Wii. DRAM now has 0x3000 words
|
||||
// and DROM starts at address 0x3000 instead of
|
||||
// 0x1000.
|
||||
uint16_t dram[0x3000];
|
||||
uint16_t drom[0x800];
|
||||
|
||||
#if STATS
|
||||
uint64_t instrs_executed;
|
||||
uint64_t dma_transfers[4];
|
||||
#endif
|
||||
|
||||
FileLogger dma_logger;
|
||||
|
||||
private:
|
||||
void *dma_ptr(uint16_t addr, uint16_t length, bool code);
|
||||
|
||||
void skip();
|
||||
|
||||
uint16_t fetch();
|
||||
|
||||
uint16_t read(uint16_t addr);
|
||||
uint16_t read_code(uint16_t addr);
|
||||
void write(uint16_t addr, uint16_t value);
|
||||
|
||||
void writereg(int reg, uint16_t value);
|
||||
|
||||
bool checkcond(int cond);
|
||||
void updateflags(uint64_t value);
|
||||
|
||||
uint64_t add_with_flags(uint64_t a, uint64_t b);
|
||||
|
||||
void trigger_exception(int type);
|
||||
void update_timer();
|
||||
|
||||
void doext(uint16_t instr);
|
||||
|
||||
void add(uint16_t instr);
|
||||
void addax(uint16_t instr);
|
||||
void addis(uint16_t instr);
|
||||
void addr(uint16_t instr);
|
||||
void andcf(uint16_t instr);
|
||||
void andf(uint16_t instr);
|
||||
void andi(uint16_t instr);
|
||||
void andr(uint16_t instr);
|
||||
void bloop(uint16_t instr);
|
||||
void bloopi(uint16_t instr);
|
||||
void call(uint16_t instr);
|
||||
void callr(uint16_t instr);
|
||||
void clr(uint16_t instr);
|
||||
void cmp(uint16_t instr);
|
||||
void iar(uint16_t instr);
|
||||
void ilrr(uint16_t instr);
|
||||
void ilrri(uint16_t instr);
|
||||
void inc(uint16_t instr);
|
||||
void incm(uint16_t instr);
|
||||
void jcc(uint16_t instr);
|
||||
void jmpr(uint16_t instr);
|
||||
void loop(uint16_t instr);
|
||||
void lr(uint16_t instr);
|
||||
void lri(uint16_t instr);
|
||||
void lris(uint16_t instr);
|
||||
void lrr(uint16_t instr);
|
||||
void lrri(uint16_t instr);
|
||||
void lrrn(uint16_t instr);
|
||||
void lrs(uint16_t instr);
|
||||
void lsl(uint16_t instr);
|
||||
void lsr(uint16_t instr);
|
||||
void mrr(uint16_t instr);
|
||||
void nop(uint16_t instr);
|
||||
void orc(uint16_t instr);
|
||||
void ori(uint16_t instr);
|
||||
void orr(uint16_t instr);
|
||||
void ret(uint16_t instr);
|
||||
void rti(uint16_t instr);
|
||||
void sbclr(uint16_t instr);
|
||||
void sbset(uint16_t instr);
|
||||
void setam(uint16_t instr);
|
||||
void setsu(uint16_t instr);
|
||||
void si(uint16_t instr);
|
||||
void sr(uint16_t instr);
|
||||
void srr(uint16_t instr);
|
||||
void srri(uint16_t instr);
|
||||
void srrn(uint16_t instr);
|
||||
void srs(uint16_t instr);
|
||||
void sub(uint16_t instr);
|
||||
void subr(uint16_t instr);
|
||||
void sxm(uint16_t instr);
|
||||
void tst(uint16_t instr);
|
||||
void xorc(uint16_t instr);
|
||||
void xorr(uint16_t instr);
|
||||
};
|
|
@ -110,7 +110,7 @@ public:
|
|||
bool getCarry();
|
||||
void setCarry(bool carry);
|
||||
|
||||
Bits cr;
|
||||
Bits<uint32_t> cr;
|
||||
uint32_t pc;
|
||||
uint32_t regs[32];
|
||||
uint32_t sprs[0x200];
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
|
||||
PPCProcessor::PPCProcessor(Emulator *emulator, PPCReservation *reservation, int index) :
|
||||
Processor(emulator, index + 1),
|
||||
Processor(emulator, index + 1, true),
|
||||
mmu(&emulator->physmem, &core),
|
||||
jit(&emulator->physmem, this)
|
||||
{
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
#include <algorithm>
|
||||
|
||||
|
||||
Processor::Processor(Emulator *emulator, int index) {
|
||||
Processor::Processor(Emulator *emulator, int index, bool threaded) {
|
||||
this->emulator = emulator;
|
||||
this->physmem = &emulator->physmem;
|
||||
this->hardware = &emulator->hardware;
|
||||
this->threaded = threaded;
|
||||
this->index = index;
|
||||
enabled = false;
|
||||
paused = true;
|
||||
|
@ -17,33 +18,46 @@ Processor::Processor(Emulator *emulator, int index) {
|
|||
|
||||
void Processor::start() {
|
||||
paused = false;
|
||||
if (enabled) {
|
||||
if (threaded && enabled) {
|
||||
thread = std::thread(threadFunc, this);
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::pause() {
|
||||
paused = true;
|
||||
if (thread.joinable()) {
|
||||
if (threaded && thread.joinable()) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::enable() {
|
||||
enabled = true;
|
||||
if (!paused) {
|
||||
if (threaded && !paused) {
|
||||
thread = std::thread(threadFunc, this);
|
||||
}
|
||||
}
|
||||
|
||||
void Processor::disable() {
|
||||
enabled = false;
|
||||
if (thread.joinable()) {
|
||||
if (threaded && thread.joinable()) {
|
||||
thread.join();
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
bool Processor::isPaused() {
|
||||
return paused;
|
||||
}
|
||||
|
||||
bool Processor::isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void Processor::setEnabled(bool enabled) {
|
||||
if (enabled) enable();
|
||||
else disable();
|
||||
}
|
||||
|
||||
void Processor::threadFunc(Processor *cpu) {
|
||||
cpu->mainLoop();
|
||||
}
|
||||
|
@ -86,7 +100,7 @@ void Processor::removeBreakpoint(uint32_t addr) {
|
|||
void Processor::checkWatchpoints(bool write, bool virt, uint32_t addr, int length) {
|
||||
if (isWatchpoint(write, virt, addr, length)) {
|
||||
Sys::out->write(
|
||||
"Watchpoint (%s) hit at %s address 0x%08X\n",
|
||||
"Watchpoint (%s) hit at %s address 0x%X\n",
|
||||
write ? "write" : "read",
|
||||
virt ? "virtual" : "physical",
|
||||
addr
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "physicalmemory.h"
|
||||
#include "hardware.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <vector>
|
||||
|
@ -11,10 +9,13 @@
|
|||
|
||||
|
||||
class Emulator;
|
||||
class PhysicalMemory;
|
||||
class Hardware;
|
||||
|
||||
|
||||
class Processor {
|
||||
public:
|
||||
Processor(Emulator *emulator, int index);
|
||||
Processor(Emulator *emulator, int index, bool threaded);
|
||||
|
||||
void start();
|
||||
void pause();
|
||||
|
@ -22,6 +23,11 @@ public:
|
|||
void enable();
|
||||
void disable();
|
||||
|
||||
bool isPaused();
|
||||
bool isEnabled();
|
||||
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
virtual void reset() = 0;
|
||||
virtual void step() = 0;
|
||||
|
||||
|
@ -61,6 +67,7 @@ private:
|
|||
void mainLoop();
|
||||
|
||||
std::thread thread;
|
||||
bool threaded;
|
||||
bool enabled;
|
||||
bool paused;
|
||||
};
|
||||
|
|
|
@ -57,6 +57,10 @@ std::string ARMDebugger::name() {
|
|||
return "ARM";
|
||||
}
|
||||
|
||||
std::string ARMDebugger::format() {
|
||||
return "%08X";
|
||||
}
|
||||
|
||||
Ref<EvalContext> ARMDebugger::getContext() {
|
||||
Ref<EvalContext> context = new EvalContext();
|
||||
|
||||
|
@ -82,10 +86,6 @@ uint32_t ARMDebugger::pc() {
|
|||
return cpu->core.regs[ARMCore::PC];
|
||||
}
|
||||
|
||||
void ARMDebugger::step() {
|
||||
cpu->step();
|
||||
}
|
||||
|
||||
bool ARMDebugger::translate(uint32_t *address) {
|
||||
return cpu->mmu.translate(address, MemoryAccess::DataRead, true);
|
||||
}
|
||||
|
|
|
@ -17,8 +17,7 @@ public:
|
|||
Ref<EvalContext> getContext();
|
||||
Processor *getProcessor();
|
||||
bool translate(uint32_t *address);
|
||||
|
||||
void step();
|
||||
std::string format();
|
||||
|
||||
void printState();
|
||||
void printStateDetailed();
|
||||
|
|
|
@ -15,7 +15,7 @@ const char *HELP_TEXT =
|
|||
" help\n"
|
||||
" exit\n"
|
||||
" quit\n"
|
||||
" select arm/ppc0/ppc1/ppc2\n"
|
||||
" select arm/ppc0/ppc1/ppc2/dsp\n"
|
||||
"\n"
|
||||
"Emulation:\n"
|
||||
" step (<steps>)\n"
|
||||
|
@ -53,7 +53,8 @@ const char *HELP_TEXT =
|
|||
" trace\n"
|
||||
"\n"
|
||||
"Memory state:\n"
|
||||
" read virt/phys <address> <length>\n"
|
||||
" read virt/phys <address> <length> // ARM + PPC\n"
|
||||
" read code/data <address> <length> // DSP\n"
|
||||
" translate <address>\n"
|
||||
" memmap\n"
|
||||
"\n"
|
||||
|
@ -129,6 +130,7 @@ Debugger::Debugger(Emulator *emulator) {
|
|||
ppc[0] = new PPCDebugger(physmem, &emulator->ppc[0], 0);
|
||||
ppc[1] = new PPCDebugger(physmem, &emulator->ppc[1], 1);
|
||||
ppc[2] = new PPCDebugger(physmem, &emulator->ppc[2], 2);
|
||||
dsp = new DSPDebugger(&emulator->dsp);
|
||||
|
||||
core = 0;
|
||||
}
|
||||
|
@ -138,9 +140,13 @@ Ref<DebugInterface> Debugger::getInterface() {
|
|||
if (core == 1) return ppc[0];
|
||||
if (core == 2) return ppc[1];
|
||||
if (core == 3) return ppc[2];
|
||||
if (core == 4) return dsp;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Debugger::isPPC() { return core == 1 || core == 2 || core == 3; }
|
||||
bool Debugger::isDSP() { return core == 4; }
|
||||
|
||||
void Debugger::show(int core) {
|
||||
emulator->pause();
|
||||
|
||||
|
@ -152,7 +158,8 @@ void Debugger::show(int core) {
|
|||
while (debugging) {
|
||||
DebugInterface *debugger = getInterface();
|
||||
|
||||
Sys::out->write("%s:%08X> ", debugger->name(), debugger->pc());
|
||||
std::string format = StringUtils::format("%%s:%s> ", debugger->format());
|
||||
Sys::out->write(format, debugger->name(), debugger->pc());
|
||||
|
||||
std::string line = Sys::in->readline();
|
||||
|
||||
|
@ -253,6 +260,7 @@ void Debugger::select(ArgParser *args) {
|
|||
else if (name == "ppc0") core = 1;
|
||||
else if (name == "ppc1") core = 2;
|
||||
else if (name == "ppc2") core = 3;
|
||||
else if (name == "dsp") core = 4;
|
||||
else {
|
||||
Sys::out->write("Please provide a valid processor name.\n");
|
||||
}
|
||||
|
@ -266,6 +274,11 @@ void Debugger::step(ArgParser *args) {
|
|||
}
|
||||
|
||||
Processor *proc = getInterface()->getProcessor();
|
||||
if (!proc->isEnabled()) {
|
||||
Sys::out->write("Processor is disabled.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
proc->step();
|
||||
}
|
||||
|
@ -295,6 +308,7 @@ void Debugger::stats(ArgParser *args) {
|
|||
for (int i = 0; i < 3; i++) {
|
||||
ppc[i]->printStats();
|
||||
}
|
||||
dsp->printStats();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -367,7 +381,8 @@ void Debugger::breakp(ArgParser *args) {
|
|||
std::string command;
|
||||
if (!args->string(&command)) return;
|
||||
|
||||
Processor *cpu = getInterface()->getProcessor();
|
||||
DebugInterface *interface = getInterface();
|
||||
Processor *cpu = interface->getProcessor();
|
||||
if (command == "list") {
|
||||
if (!args->finish()) return;
|
||||
|
||||
|
@ -385,19 +400,22 @@ void Debugger::breakp(ArgParser *args) {
|
|||
Sys::out->write("%i breakpoints:\n", breakpoints.size());
|
||||
}
|
||||
for (uint32_t bp : breakpoints) {
|
||||
Sys::out->write(" 0x%08X\n", bp);
|
||||
std::string format = StringUtils::format(" 0x%s\n", interface->format());
|
||||
Sys::out->write(format, bp);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (command == "clear") {
|
||||
if (!args->finish()) return;
|
||||
|
||||
if (core == 0) arm->getProcessor()->breakpoints.clear();
|
||||
else {
|
||||
if (isPPC()) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ppc[i]->getProcessor()->breakpoints.clear();
|
||||
}
|
||||
}
|
||||
else {
|
||||
cpu->breakpoints.clear();
|
||||
}
|
||||
}
|
||||
else if (command == "add" || command == "del") {
|
||||
uint32_t address;
|
||||
|
@ -411,12 +429,14 @@ void Debugger::breakp(ArgParser *args) {
|
|||
}
|
||||
else {
|
||||
Sys::out->write("Added breakpoint at 0x%X.\n", address);
|
||||
if (core == 0) arm->getProcessor()->addBreakpoint(address);
|
||||
else {
|
||||
if (isPPC()) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ppc[i]->getProcessor()->addBreakpoint(address);
|
||||
}
|
||||
}
|
||||
else {
|
||||
cpu->addBreakpoint(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -425,12 +445,14 @@ void Debugger::breakp(ArgParser *args) {
|
|||
}
|
||||
else {
|
||||
Sys::out->write("Removed breakpoint at 0x%X.\n", address);
|
||||
if (core == 0) arm->getProcessor()->removeBreakpoint(address);
|
||||
else {
|
||||
if (isPPC()) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ppc[i]->getProcessor()->removeBreakpoint(address);
|
||||
}
|
||||
}
|
||||
else {
|
||||
cpu->removeBreakpoint(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -445,7 +467,8 @@ void Debugger::watch(ArgParser *args) {
|
|||
std::string command;
|
||||
if (!args->string(&command)) return;
|
||||
|
||||
Processor *cpu = getInterface()->getProcessor();
|
||||
DebugInterface *interface = getInterface();
|
||||
Processor *cpu = interface->getProcessor();
|
||||
if (command == "list") {
|
||||
if (!args->finish()) return;
|
||||
|
||||
|
@ -468,8 +491,9 @@ void Debugger::watch(ArgParser *args) {
|
|||
for (int virt = 0; virt < 2; virt++) {
|
||||
for (int write = 0; write < 2; write++) {
|
||||
for (uint32_t wp : cpu->watchpoints[write][virt]) {
|
||||
std::string format = StringUtils::format(" 0x%s (%%s, %%s)\n", interface->format());
|
||||
Sys::out->write(
|
||||
" 0x%08X (%s, %s)\n", wp,
|
||||
format, wp,
|
||||
virt ? "virtual" : "physical",
|
||||
write ? "write" : "read"
|
||||
);
|
||||
|
@ -507,6 +531,11 @@ void Debugger::watch(ArgParser *args) {
|
|||
bool write = type == "write";
|
||||
bool virt = mode == "virt";
|
||||
|
||||
if (virt && isDSP()) {
|
||||
Sys::out->write("DSP does not have virtual memory.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mode = virt ? "virtual" : "physical";
|
||||
|
||||
bool exists = cpu->isWatchpoint(write, virt, address, 1);
|
||||
|
@ -574,27 +603,42 @@ void Debugger::read(ArgParser *args) {
|
|||
if (!args->integer(&length)) return;
|
||||
if (!args->finish()) return;
|
||||
|
||||
if (mode == "virt") {
|
||||
if (!getInterface()->translate(&address)) {
|
||||
Sys::out->write("Address translation failed.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (mode != "phys") {
|
||||
Sys::out->write("Please specify either 'phys' or 'virt'.\n");
|
||||
if (mode != "phys" && mode != "virt" && mode != "code" && mode != "data") {
|
||||
Sys::out->write("Please specify either 'phys', 'virt', 'code' or 'data'.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Buffer buffer = physmem->read(address, length);
|
||||
|
||||
std::string text = buffer.tostring();
|
||||
for (int i = 0; i < text.size(); i++) {
|
||||
if (!StringUtils::is_printable(text[i])) {
|
||||
text[i] = ' ';
|
||||
Buffer data;
|
||||
if (mode == "code" || mode == "data") {
|
||||
data = dsp->read(address, length, mode == "code");
|
||||
}
|
||||
else {
|
||||
DebugInterface *interface = getInterface();
|
||||
if (mode == "virt") {
|
||||
if (!interface->translate(&address)) {
|
||||
Sys::out->write("Address translation failed.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (address + length < address) {
|
||||
length = ~address + 1;
|
||||
}
|
||||
|
||||
data = physmem->read(address, length);
|
||||
}
|
||||
|
||||
Sys::out->write("%s\n\n%s\n", buffer.hexstring(), text);
|
||||
Sys::out->write("%s\n", data.hexstring());
|
||||
|
||||
if (mode == "phys" || mode == "virt") {
|
||||
std::string text = data.tostring();
|
||||
for (int i = 0; i < text.size(); i++) {
|
||||
if (!StringUtils::is_printable(text[i])) {
|
||||
text[i] = ' ';
|
||||
}
|
||||
}
|
||||
Sys::out->write("\n%s\n", text);
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::translate(ArgParser *args) {
|
||||
|
@ -603,7 +647,8 @@ void Debugger::translate(ArgParser *args) {
|
|||
if (!args->finish()) return;
|
||||
|
||||
if (getInterface()->translate(&addr)) {
|
||||
Sys::out->write("0x%08X\n", addr);
|
||||
std::string format = StringUtils::format("0x%s\n", getInterface()->format());
|
||||
Sys::out->write(format, addr);
|
||||
}
|
||||
else {
|
||||
Sys::out->write("Address translation failed.\n");
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "debugger/expression.h"
|
||||
#include "debugger/ppc.h"
|
||||
#include "debugger/arm.h"
|
||||
#include "debugger/dsp.h"
|
||||
|
||||
#include "physicalmemory.h"
|
||||
#include "config.h"
|
||||
|
@ -50,6 +51,9 @@ private:
|
|||
|
||||
Ref<DebugInterface> getInterface();
|
||||
|
||||
bool isPPC();
|
||||
bool isDSP();
|
||||
|
||||
void help(ArgParser *parser);
|
||||
void quit(ArgParser *parser);
|
||||
void select(ArgParser *parser);
|
||||
|
@ -99,6 +103,7 @@ private:
|
|||
Emulator *emulator;
|
||||
PhysicalMemory *physmem;
|
||||
|
||||
Ref<DSPDebugger> dsp;
|
||||
Ref<ARMDebugger> arm;
|
||||
Ref<PPCDebugger> ppc[3];
|
||||
|
||||
|
|
175
src/debugger/dsp.cpp
Normal file
175
src/debugger/dsp.cpp
Normal file
|
@ -0,0 +1,175 @@
|
|||
|
||||
#include "debugger/dsp.h"
|
||||
|
||||
#include "common/sys.h"
|
||||
|
||||
|
||||
static const char *RegisterNames[] = {
|
||||
"ar0", "ar1", "ar2", "ar3",
|
||||
"ix0", "ix1", "ix2", "ix3",
|
||||
"wr0", "wr1", "wr2", "wr3",
|
||||
"st0", "st1", "st2", "st3",
|
||||
"ac0h", "ac1h", "config", "sr",
|
||||
"prodl", "prodm1", "prodh", "prodm2",
|
||||
"ax0l", "ax1l", "ax0h", "ax1h",
|
||||
"ac0l", "ac1l", "ac0m", "ac1m"
|
||||
};
|
||||
|
||||
|
||||
DSPDebugger::DSPDebugger(DSPInterpreter *cpu) {
|
||||
this->cpu = cpu;
|
||||
}
|
||||
|
||||
Processor *DSPDebugger::getProcessor() {
|
||||
return cpu;
|
||||
}
|
||||
|
||||
std::string DSPDebugger::name() {
|
||||
return "DSP";
|
||||
}
|
||||
|
||||
std::string DSPDebugger::format() {
|
||||
return "%04X";
|
||||
}
|
||||
|
||||
Ref<EvalContext> DSPDebugger::getContext() {
|
||||
Ref<EvalContext> context = new EvalContext();
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (DSPInterpreter::ST0 <= i && i <= DSPInterpreter::ST3) continue;
|
||||
|
||||
uint16_t value = cpu->readreg(i);
|
||||
|
||||
std::string reg = StringUtils::format("r%i", i);
|
||||
|
||||
context->add(RegisterNames[i], value);
|
||||
context->add(reg, value);
|
||||
}
|
||||
context->add("pc", cpu->pc);
|
||||
return context;
|
||||
}
|
||||
|
||||
uint32_t DSPDebugger::pc() {
|
||||
return cpu->pc;
|
||||
}
|
||||
|
||||
bool DSPDebugger::translate(uint32_t *address) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Buffer DSPDebugger::read(uint32_t address, uint32_t length, bool code) {
|
||||
std::pair<void *, uint32_t> pair = getPtr(address, code);
|
||||
if (!pair.first) {
|
||||
return Buffer();
|
||||
}
|
||||
|
||||
if (length > pair.second * 2) {
|
||||
length = pair.second * 2;
|
||||
}
|
||||
|
||||
return Buffer(pair.first, length, Buffer::CreateCopy);
|
||||
}
|
||||
|
||||
std::pair<void *, uint32_t> DSPDebugger::getPtr(uint32_t address, bool code) {
|
||||
if (code) {
|
||||
if (address < 0x2000) {
|
||||
return std::make_pair(cpu->iram + address, 0x2000 - address);
|
||||
}
|
||||
if (0x8000 <= address && address < 0x9000) {
|
||||
address -= 0x8000;
|
||||
return std::make_pair(cpu->irom + address, 0x1000 - address);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (address < 0x3000) {
|
||||
return std::make_pair(cpu->dram + address, 0x3000 - address);
|
||||
}
|
||||
if (address < 0x3800) {
|
||||
address -= 0x3000;
|
||||
return std::make_pair(cpu->drom + address, 0x800 - address);
|
||||
}
|
||||
}
|
||||
return std::make_pair(nullptr, 0);
|
||||
}
|
||||
|
||||
void DSPDebugger::printState() {
|
||||
Sys::out->write(
|
||||
"ar0 = %04X ar1 = %04X ar2 = %04X ar3 = %04X\n"
|
||||
"ix0 = %04X ix1 = %04X ix2 = %04X ix3 = %04X\n"
|
||||
"wr0 = %04X wr1 = %04X wr2 = %04X wr3 = %04X\n\n"
|
||||
"ac0 = %02X%04X%04X ac1 = %02X%04X%04X\n"
|
||||
"ax0 = %04X%04X ax1 = %04X%04X\n\n"
|
||||
"config = %04X sr = %04X\n\n",
|
||||
cpu->ar[0], cpu->ar[1], cpu->ar[2], cpu->ar[3],
|
||||
cpu->ix[0], cpu->ix[1], cpu->ix[2], cpu->ix[3],
|
||||
cpu->wr[0], cpu->wr[1], cpu->wr[2], cpu->wr[3],
|
||||
cpu->ac[0].h, cpu->ac[0].m, cpu->ac[0].l,
|
||||
cpu->ac[1].h, cpu->ac[1].m, cpu->ac[1].l,
|
||||
cpu->ax[0].h, cpu->ax[0].l, cpu->ax[1].h, cpu->ax[1].l,
|
||||
cpu->config, cpu->status
|
||||
);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
printStack(i);
|
||||
}
|
||||
}
|
||||
|
||||
void DSPDebugger::printStateDetailed() {
|
||||
printState();
|
||||
}
|
||||
|
||||
void DSPDebugger::printStack(int index) {
|
||||
Sys::out->write("st%i = {", index);
|
||||
for (int i = 0; i < cpu->st[index].size(); i++) {
|
||||
if (i != 0) {
|
||||
Sys::out->write(", ");
|
||||
}
|
||||
Sys::out->write("%04X", cpu->st[index].get(i));
|
||||
}
|
||||
Sys::out->write("}\n");
|
||||
}
|
||||
|
||||
void DSPDebugger::printStackTrace() {
|
||||
Sys::out->write("pc = %04X\n", cpu->pc);
|
||||
|
||||
Sys::out->write("st0 = {");
|
||||
for (int i = 0; i < cpu->st[0].size(); i++) {
|
||||
Sys::out->write("\n");
|
||||
if (i != 0) {
|
||||
Sys::out->write(",\n");
|
||||
}
|
||||
Sys::out->write(" %04X", cpu->st[0].get(i));
|
||||
}
|
||||
Sys::out->write("\n}\n");
|
||||
}
|
||||
|
||||
void DSPDebugger::printMemoryMap() {
|
||||
Sys::out->write("DSP does not have virtual memory.");
|
||||
}
|
||||
|
||||
void DSPDebugger::printThreads() {
|
||||
Sys::out->write("DSP does not have threads.");
|
||||
}
|
||||
|
||||
void DSPDebugger::printThreadDetails(uint32_t id) {
|
||||
Sys::out->write("DSP does not have threads.");
|
||||
}
|
||||
|
||||
#if STATS
|
||||
void DSPDebugger::printStats() {
|
||||
uint64_t totalTransfers = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
totalTransfers += cpu->dma_transfers[i];
|
||||
}
|
||||
|
||||
Sys::out->write("\n");
|
||||
Sys::out->write("DSP:\n");
|
||||
Sys::out->write(" Instructions executed: %i\n", cpu->instrs_executed);
|
||||
Sys::out->write(" \n");
|
||||
Sys::out->write(" DMA transfers: %i\n", totalTransfers);
|
||||
Sys::out->write(" CPU to IRAM: %i\n", cpu->dma_transfers[2]);
|
||||
Sys::out->write(" CPU to DRAM: %i\n", cpu->dma_transfers[0]);
|
||||
Sys::out->write(" IRAM to CPU: %i\n", cpu->dma_transfers[3]);
|
||||
Sys::out->write(" DRAM to CPU: %i\n", cpu->dma_transfers[1]);
|
||||
}
|
||||
#endif
|
44
src/debugger/dsp.h
Normal file
44
src/debugger/dsp.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "cpu/dsp.h"
|
||||
|
||||
#include "debugger/interface.h"
|
||||
#include "debugger/expression.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
|
||||
class DSPDebugger : public DebugInterface {
|
||||
public:
|
||||
DSPDebugger(DSPInterpreter *cpu);
|
||||
|
||||
uint32_t pc();
|
||||
std::string name();
|
||||
Ref<EvalContext> getContext();
|
||||
Processor *getProcessor();
|
||||
bool translate(uint32_t *address);
|
||||
std::string format();
|
||||
|
||||
void printState();
|
||||
void printStateDetailed();
|
||||
void printStackTrace();
|
||||
void printMemoryMap();
|
||||
void printThreads();
|
||||
void printThreadDetails(uint32_t id);
|
||||
|
||||
Buffer read(uint32_t address, uint32_t length, bool code);
|
||||
|
||||
#if STATS
|
||||
void printStats();
|
||||
#endif
|
||||
|
||||
private:
|
||||
void printStack(int index);
|
||||
|
||||
std::pair<void *, uint32_t> getPtr(uint32_t address, bool code);
|
||||
|
||||
DSPInterpreter *cpu;
|
||||
};
|
|
@ -14,8 +14,7 @@ public:
|
|||
virtual Ref<EvalContext> getContext() = 0;
|
||||
virtual Processor *getProcessor() = 0;
|
||||
virtual bool translate(uint32_t *address) = 0;
|
||||
|
||||
virtual void step() = 0;
|
||||
virtual std::string format() = 0;
|
||||
|
||||
virtual void printState() = 0;
|
||||
virtual void printStateDetailed() = 0;
|
||||
|
|
|
@ -33,6 +33,10 @@ std::string PPCDebugger::name() {
|
|||
return StringUtils::format("PPC%i", cpu->core.sprs[PPCCore::PIR]);
|
||||
}
|
||||
|
||||
std::string PPCDebugger::format() {
|
||||
return "%08X";
|
||||
}
|
||||
|
||||
Ref<EvalContext> PPCDebugger::getContext() {
|
||||
Ref<EvalContext> context = new EvalContext();
|
||||
|
||||
|
@ -56,10 +60,6 @@ uint32_t PPCDebugger::pc() {
|
|||
return cpu->core.pc;
|
||||
}
|
||||
|
||||
void PPCDebugger::step() {
|
||||
cpu->step();
|
||||
}
|
||||
|
||||
bool PPCDebugger::translate(uint32_t *address) {
|
||||
return cpu->mmu.translate(address, MemoryAccess::DataRead, true);
|
||||
}
|
||||
|
@ -393,7 +393,7 @@ void PPCDebugger::printThreads() {
|
|||
|
||||
std::string state = StringUtils::format("(%s)", stateName);
|
||||
|
||||
Sys::out->write(" %08X: %-12s %-10s p=%-2i %-9s %s\n", addr, owner, core, priority, state, name);
|
||||
Sys::out->write(" %08X: %-12s %-10s p=%-2i %-9s %s\n", addr, owner, core, priority, state, name);
|
||||
|
||||
thread = physmem->read<uint32_t>(thread + 0x38C);
|
||||
}
|
||||
|
|
|
@ -30,8 +30,7 @@ public:
|
|||
Ref<EvalContext> getContext();
|
||||
Processor *getProcessor();
|
||||
bool translate(uint32_t *address);
|
||||
|
||||
void step();
|
||||
std::string format();
|
||||
|
||||
void printState();
|
||||
void printStateDetailed();
|
||||
|
|
|
@ -28,7 +28,8 @@ Emulator::Emulator() :
|
|||
{this, &reservation, 0},
|
||||
{this, &reservation, 1},
|
||||
{this, &reservation, 2}
|
||||
}
|
||||
},
|
||||
dsp(this)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
@ -65,6 +66,7 @@ void Emulator::run() {
|
|||
for (int i = 0; i < 3; i++) {
|
||||
ppc[i].start();
|
||||
}
|
||||
dsp.start();
|
||||
|
||||
while (core == -1 && !keyboard_interrupt) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
|
@ -86,6 +88,7 @@ void Emulator::pause() {
|
|||
for (int i = 0; i < 3; i++) {
|
||||
ppc[i].pause();
|
||||
}
|
||||
dsp.pause();
|
||||
}
|
||||
|
||||
void Emulator::signal(int core) {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "cpu/ppc/ppcreservation.h"
|
||||
#include "cpu/ppc/ppcprocessor.h"
|
||||
#include "cpu/arm/armprocessor.h"
|
||||
#include "cpu/dsp.h"
|
||||
|
||||
#include "debugger/debugger.h"
|
||||
|
||||
|
@ -26,6 +27,7 @@ public:
|
|||
PPCReservation reservation;
|
||||
PPCProcessor ppc[3];
|
||||
ARMProcessor arm;
|
||||
DSPInterpreter dsp;
|
||||
|
||||
Hardware hardware;
|
||||
Debugger debugger;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
Hardware::Hardware(Emulator *emulator) :
|
||||
latte(emulator),
|
||||
dsp(emulator),
|
||||
|
||||
pi(&emulator->physmem),
|
||||
aes(&emulator->physmem),
|
||||
|
@ -147,6 +148,7 @@ void Hardware::write(uint32_t addr, uint16_t value) {
|
|||
|
||||
void Hardware::update() {
|
||||
ai.update();
|
||||
dsp.update();
|
||||
gpu.update();
|
||||
latte.update();
|
||||
|
||||
|
|
|
@ -1,76 +1,65 @@
|
|||
|
||||
#include "hardware/dsp.h"
|
||||
|
||||
#include "common/logger.h"
|
||||
|
||||
#include "emulator.h"
|
||||
|
||||
void DSPController::reset() {
|
||||
mailbox_in = 0;
|
||||
mailbox_out = 0x80000000;
|
||||
|
||||
halted = true;
|
||||
int_status = false;
|
||||
int_mask = false;
|
||||
|
||||
state = STATE_NEXT;
|
||||
|
||||
DSPController::DSPController(Emulator *emulator) {
|
||||
interpreter = &emulator->dsp;
|
||||
}
|
||||
|
||||
uint32_t DSPController::read(uint32_t addr) {
|
||||
void DSPController::reset() {
|
||||
int_enabled = false;
|
||||
|
||||
interpreter->reset();
|
||||
}
|
||||
|
||||
void DSPController::update() {
|
||||
if (interpreter->isEnabled() && !interpreter->isPaused()) {
|
||||
interpreter->step();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t DSPController::read(uint32_t addr) {
|
||||
switch (addr) {
|
||||
case DSP_MAILBOX_IN_H: return 0;
|
||||
case DSP_MAILBOX_OUT_H: return mailbox_out >> 16;
|
||||
case DSP_MAILBOX_OUT_L: return mailbox_out & 0xFFFF;
|
||||
case DSP_MAILBOX_IN_H: return interpreter->mailbox_in_h;
|
||||
case DSP_MAILBOX_OUT_H: return interpreter->mailbox_out_h;
|
||||
case DSP_MAILBOX_OUT_L:
|
||||
interpreter->mailbox_out_h &= 0x7FFF;
|
||||
return interpreter->mailbox_out_l;
|
||||
case DSP_CONTROL_STATUS:
|
||||
return (halted << 2) | (int_status << 7) | (int_mask << 8);
|
||||
bool halted = !interpreter->isEnabled();
|
||||
return (halted << 2) | (interpreter->irq << 7) | (int_enabled << 8);
|
||||
}
|
||||
Logger::warning("Unknown dsp read: 0x%X", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSPController::write(uint32_t addr, uint32_t value) {
|
||||
if (addr == DSP_MAILBOX_IN_H) mailbox_in = (mailbox_in & 0xFFFF) | (value << 16);
|
||||
void DSPController::write(uint32_t addr, uint16_t value) {
|
||||
if (addr == DSP_MAILBOX_IN_H) interpreter->mailbox_in_h = value;
|
||||
else if (addr == DSP_MAILBOX_IN_L) {
|
||||
mailbox_in = (mailbox_in & ~0xFFFF) | value;
|
||||
accept(mailbox_in);
|
||||
interpreter->mailbox_in_l = value;
|
||||
interpreter->mailbox_in_h |= 0x8000;
|
||||
}
|
||||
else if (addr == DSP_CONTROL_STATUS) {
|
||||
halted = value & 4;
|
||||
int_mask = value & 0x100;
|
||||
interpreter->setEnabled(!(value & 4));
|
||||
|
||||
int_enabled = value & 0x100;
|
||||
if (value & 0x80) {
|
||||
int_status = false;
|
||||
interpreter->irq = false;
|
||||
}
|
||||
|
||||
if (value & 1) {
|
||||
interpreter->reset();
|
||||
}
|
||||
if (value & 0x801) reset();
|
||||
}
|
||||
else {
|
||||
Logger::warning("Unknown dsp write: 0x%X (0x%08X)", addr, value);
|
||||
Logger::warning("Unknown dsp write: 0x%X (0x%04X)", addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool DSPController::check_interrupts() {
|
||||
return int_status && int_mask;
|
||||
}
|
||||
|
||||
void DSPController::send_mail(MessageOut message) {
|
||||
mailbox_out = message;
|
||||
int_status = true;
|
||||
}
|
||||
|
||||
void DSPController::accept(uint32_t mail) {
|
||||
if (state == STATE_NEXT) {
|
||||
message = mail;
|
||||
state = STATE_ARGUMENT;
|
||||
}
|
||||
else {
|
||||
process(message, mail);
|
||||
state = STATE_NEXT;
|
||||
}
|
||||
}
|
||||
|
||||
void DSPController::process(uint32_t message, uint32_t arg) {
|
||||
if (message == 0x80F3D001) {
|
||||
if (arg & 0x10) {
|
||||
send_mail(DSP_INIT);
|
||||
}
|
||||
}
|
||||
Logger::warning("Unknown dsp message: 0x%08X 0x%08X", message, arg);
|
||||
return interpreter->irq && int_enabled;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "cpu/dsp.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
class Emulator;
|
||||
|
||||
|
||||
class DSPController {
|
||||
public:
|
||||
enum Register {
|
||||
|
@ -13,35 +19,19 @@ public:
|
|||
DSP_CONTROL_STATUS = 0xC28000A
|
||||
};
|
||||
|
||||
void reset();
|
||||
DSPController(Emulator *physmem);
|
||||
|
||||
uint32_t read(uint32_t addr);
|
||||
void write(uint32_t addr, uint32_t value);
|
||||
void reset();
|
||||
void update();
|
||||
|
||||
uint16_t read(uint32_t addr);
|
||||
void write(uint32_t addr, uint16_t value);
|
||||
|
||||
bool check_interrupts();
|
||||
|
||||
|
||||
private:
|
||||
enum MessageOut {
|
||||
DSP_INIT = 0xDCD10000
|
||||
};
|
||||
DSPInterpreter *interpreter;
|
||||
|
||||
enum State {
|
||||
STATE_NEXT,
|
||||
STATE_ARGUMENT
|
||||
};
|
||||
|
||||
void accept(uint32_t mail);
|
||||
void process(uint32_t message, uint32_t arg);
|
||||
|
||||
void send_mail(MessageOut message);
|
||||
|
||||
uint32_t mailbox_in;
|
||||
uint32_t mailbox_out;
|
||||
|
||||
bool halted;
|
||||
bool int_status;
|
||||
bool int_mask;
|
||||
|
||||
uint32_t message;
|
||||
State state;
|
||||
};
|
||||
bool int_enabled;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue