Implement low level DSP emulation to fix crash in AXInit

This commit is contained in:
Yannik Marchand 2021-04-01 11:36:59 +02:00
parent 05d7afa58f
commit 490295e247
29 changed files with 1500 additions and 151 deletions

6
.gitignore vendored
View file

@ -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

View file

@ -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

Binary file not shown.

BIN
files/dsp_irom.bin Executable file

Binary file not shown.

View file

@ -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;
};

View file

@ -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);
}

View file

@ -6,3 +6,4 @@
#define WATCHPOINTS 1
#define BREAKPOINTS 1
#define SYSLOG 1
#define DSPDMA 1

View file

@ -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;

View file

@ -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
View 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
View 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);
};

View file

@ -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];

View file

@ -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)
{

View file

@ -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

View file

@ -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;
};

View file

@ -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);
}

View file

@ -17,8 +17,7 @@ public:
Ref<EvalContext> getContext();
Processor *getProcessor();
bool translate(uint32_t *address);
void step();
std::string format();
void printState();
void printStateDetailed();

View file

@ -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");

View file

@ -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
View 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
View 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;
};

View file

@ -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;

View file

@ -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);
}

View file

@ -30,8 +30,7 @@ public:
Ref<EvalContext> getContext();
Processor *getProcessor();
bool translate(uint32_t *address);
void step();
std::string format();
void printState();
void printStateDetailed();

View file

@ -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) {

View file

@ -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;

View file

@ -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();

View file

@ -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;
}

View file

@ -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;
};