mirror of
https://github.com/liuk7071/ChonkyStation.git
synced 2025-04-02 10:52:38 -04:00
1301 lines
No EOL
35 KiB
C++
1301 lines
No EOL
35 KiB
C++
#include "cpu.h"
|
|
#ifdef TEST_GTE
|
|
#include "tests.h"
|
|
#endif TEST_GTE
|
|
#include <iostream>
|
|
|
|
#ifdef log_cpu
|
|
#define debug_log(fmt, ...) if (debug) { \
|
|
printf((fmt), __VA_ARGS__); \
|
|
}
|
|
#else
|
|
#define debug_log
|
|
#endif
|
|
|
|
cpu::cpu(std::string rom_directory, std::string bios_directory, bool running_in_ci) : rom_directory(rom_directory) {
|
|
if (!running_in_ci) // Do not load a BIOS if we're in CI
|
|
bus.mem.loadBios(bios_directory);
|
|
|
|
pc = 0xbfc00000;
|
|
next_instr = pc + 4;
|
|
for (int i = 0x0; i < 32; i++) {
|
|
regs[i] = 0;
|
|
}
|
|
|
|
exe = !rom_directory.empty(); // Don't sideload an exe if no ROM path is given
|
|
log_kernel = false;
|
|
tty = true;
|
|
|
|
delay = false;
|
|
|
|
bus.mem.logwnd = &log;
|
|
bus.mem.regs = regs;
|
|
bus.mem.pc = &pc;
|
|
bus.mem.frame_cycles = &frame_cycles;
|
|
bus.mem.shouldCheckDMA = &shouldCheckDMA;
|
|
|
|
// tests
|
|
#ifdef TEST_GTE
|
|
testRTPS test1;
|
|
test1.doTest();
|
|
testRTPT test2;
|
|
test2.doTest();
|
|
testNCLIP test3;
|
|
test3.doTest();
|
|
testNCDS test4;
|
|
test4.doTest();
|
|
testMVMVA test5;
|
|
test5.doTest();
|
|
testDPCS test6;
|
|
test6.doTest();
|
|
testNCT test7;
|
|
test7.doTest();
|
|
#endif
|
|
}
|
|
|
|
cpu::~cpu() {
|
|
|
|
}
|
|
|
|
void cpu::reset() {
|
|
for (auto& i : regs) {
|
|
i = 0;
|
|
}
|
|
pc = 0xbfc00000;
|
|
delay = false;
|
|
shouldCheckDMA = false;
|
|
}
|
|
|
|
void cpu::sideloadExecutable(std::string directory) {
|
|
debug = false;
|
|
bus.mem.debug = false;
|
|
std::cout << "Kernel setup done. Sideloading executable at " << directory << "\n";
|
|
pc = bus.mem.loadExec(rom_directory);
|
|
exe = false;
|
|
memcpy(®s[28], &bus.mem.file[0x14], sizeof(uint32_t));
|
|
uint32_t r29_30_base;
|
|
uint32_t r29_30_offset;
|
|
memcpy(&r29_30_base, &bus.mem.file[0x30], sizeof(uint32_t));
|
|
memcpy(&r29_30_offset, &bus.mem.file[0x34], sizeof(uint32_t));
|
|
regs[29] = r29_30_base + r29_30_offset;
|
|
regs[30] = r29_30_base + r29_30_offset;
|
|
if (regs[29] == 0) regs[29] = 0x801fff00;
|
|
}
|
|
|
|
inline void cpu::debug_warn(const char* fmt, ...) {
|
|
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN);
|
|
std::va_list args;
|
|
va_start(args, fmt);
|
|
std::vprintf(fmt, args);
|
|
va_end(args);
|
|
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
|
}
|
|
inline void cpu::debug_err(const char* fmt, ...) {
|
|
SetConsoleTextAttribute(hConsole, FOREGROUND_RED);
|
|
std::va_list args;
|
|
va_start(args, fmt);
|
|
std::vprintf(fmt, args);
|
|
va_end(args);
|
|
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
|
}
|
|
|
|
|
|
void cpu::exception(exceptions exc) {
|
|
uint32_t handler = 0;
|
|
//if (delay) return;
|
|
if (delay) COP0.regs[13] |= (1 << 31); else COP0.regs[13] &= ~(1 << 31);
|
|
|
|
if (exc == 0x4 || exc == 0x5) { // BadVAddr
|
|
COP0.regs[8] = pc;
|
|
}
|
|
|
|
if (COP0.regs[12] & 0x400000) {
|
|
handler = 0xbfc00180;
|
|
}
|
|
else {
|
|
handler = 0x80000080;
|
|
}
|
|
|
|
uint32_t temp = COP0.regs[12] & 0x3f;
|
|
COP0.regs[12] &= ~0x3f;
|
|
COP0.regs[12] |= ((temp << 2) & 0x3f);
|
|
COP0.regs[13] &= ~0xff;
|
|
COP0.regs[13] |= (uint32_t(exc) << 2);
|
|
COP0.regs[14] = pc;
|
|
if (delay) COP0.regs[14] -= 4;
|
|
pc = handler;
|
|
delay = false;
|
|
}
|
|
|
|
template<int channel>
|
|
void cpu::do_dma() {
|
|
//debug = true;
|
|
switch (channel) { // switch on the channels
|
|
case 0: { // MDECin
|
|
auto sync_mode = (bus.mem.Ch0.CHCR >> 9) & 0b11;
|
|
bool incrementing = ((bus.mem.Ch0.CHCR >> 1) & 1) == 0;
|
|
auto direction = (bus.mem.Ch0.CHCR) & 1;
|
|
uint16_t words = (bus.mem.Ch0.BCR) & 0xffff;
|
|
uint32_t addr = bus.mem.Ch0.MADR & 0x1ffffc;
|
|
switch (sync_mode) {
|
|
case 1: { // Block Copy
|
|
words *= (bus.mem.Ch0.BCR >> 16);
|
|
debug_log("[DMA] Start MDECin Block Copy\n");
|
|
switch (direction) {
|
|
case 1:
|
|
debug_log("[DMA] Transfer direction: ram to device\n");
|
|
debug_log("[DMA] Transfer size: %d words\n", words);
|
|
while (words > 0) {
|
|
uint32_t current_addr = addr & 0x1ffffc;
|
|
uint32_t data = bus.mem.read32(current_addr);
|
|
bus.MDEC.command(data);
|
|
if (incrementing) addr += 4; else addr -= 4;
|
|
words--;
|
|
}
|
|
bus.mem.Ch0.CHCR &= ~(1 << 24);
|
|
bus.mem.Ch0.CHCR &= ~(1 << 28);
|
|
if (((bus.mem.DICR >> 16) & 1) && ((bus.mem.DICR >> 23) & 1)) {
|
|
bus.mem.DICR |= (1 << 24);
|
|
bus.mem.I_STAT |= 0b1000;
|
|
}
|
|
return;
|
|
default:
|
|
printf("[DMA] Unhandled Direction (GPU Block Copy)");
|
|
exit(0);
|
|
}
|
|
}
|
|
default:
|
|
printf("[DMA] Unhandled sync mode %d (MDEC)", sync_mode);
|
|
exit(0);
|
|
}
|
|
break;
|
|
}
|
|
case 1: { // MDECout
|
|
auto sync_mode = (bus.mem.Ch1.CHCR >> 9) & 0b11;
|
|
bool incrementing = ((bus.mem.Ch1.CHCR >> 1) & 1) == 0;
|
|
auto direction = (bus.mem.Ch1.CHCR) & 1;
|
|
uint16_t words = (bus.mem.Ch1.BCR) & 0xffff;
|
|
uint32_t addr = bus.mem.Ch1.MADR & 0x1ffffc;
|
|
switch (sync_mode) {
|
|
case 1: { // Block Copy
|
|
words *= (bus.mem.Ch1.BCR >> 16);
|
|
debug_log("[DMA] Start MDECout Block Copy\n");
|
|
switch (direction) {
|
|
case 0: {
|
|
debug_log("[DMA] Transfer direction: device to ram\n");
|
|
debug_log("[DMA] Transfer size: %d words\n", words);
|
|
while (words > 0) {
|
|
uint32_t current_addr = addr & 0x1ffffc;
|
|
|
|
uint8_t r = bus.MDEC.output[(bus.MDEC.dma_out_index + 0) & 0x4fffff] >> 3;
|
|
uint8_t g = bus.MDEC.output[(bus.MDEC.dma_out_index + 1) & 0x4fffff] >> 3;
|
|
uint8_t b = bus.MDEC.output[(bus.MDEC.dma_out_index + 2) & 0x4fffff] >> 3;
|
|
uint16_t rgb = ((bus.MDEC.status & (1 << 23)) >> 8) | (b << 10) | (g << 5) | r;
|
|
bus.mem.write16(current_addr, rgb);
|
|
bus.MDEC.dma_out_index += 3;
|
|
r = bus.MDEC.output[(bus.MDEC.dma_out_index + 0) & 0x4fffff] >> 3;
|
|
g = bus.MDEC.output[(bus.MDEC.dma_out_index + 1) & 0x4fffff] >> 3;
|
|
b = bus.MDEC.output[(bus.MDEC.dma_out_index + 2) & 0x4fffff] >> 3;
|
|
rgb = ((bus.MDEC.status & (1 << 23)) >> 8) | (b << 10) | (g << 5) | r;
|
|
bus.mem.write16(current_addr + 2, rgb);
|
|
if (incrementing) addr += 4; else addr -= 4;
|
|
bus.MDEC.dma_out_index += 3;
|
|
words--;
|
|
}
|
|
bus.mem.Ch1.CHCR &= ~(1 << 24);
|
|
bus.mem.Ch1.CHCR &= ~(1 << 28);
|
|
if (((bus.mem.DICR >> 17) & 1) && ((bus.mem.DICR >> 23) & 1)) {
|
|
bus.mem.DICR |= (1 << 25);
|
|
bus.mem.I_STAT |= 0b1000;
|
|
}
|
|
return;
|
|
}
|
|
default:
|
|
printf("[DMA] Unhandled Direction (DMAout Block Copy)");
|
|
exit(0);
|
|
}
|
|
}
|
|
default:
|
|
printf("[DMA] Unhandled sync mode %d (MDEC)", sync_mode);
|
|
exit(0);
|
|
}
|
|
break;
|
|
}
|
|
case 2: { // GPU
|
|
auto sync_mode = (bus.mem.Ch2.CHCR >> 9) & 0b11;
|
|
bool incrementing = ((bus.mem.Ch2.CHCR >> 1) & 1) == 0;
|
|
auto direction = (bus.mem.Ch2.CHCR) & 1;
|
|
uint16_t words = (bus.mem.Ch2.BCR) & 0xffff;
|
|
if (words == 0) words = 0x10000;
|
|
uint32_t addr = bus.mem.Ch2.MADR & 0x1ffffc;
|
|
uint32_t header = bus.mem.read32(addr);
|
|
|
|
switch (sync_mode) {
|
|
case 1: { // Block Copy
|
|
words *= ((bus.mem.Ch2.BCR >> 16) != 0) ? (bus.mem.Ch2.BCR >> 16) : 0x10000;
|
|
debug_log("[DMA] Start GPU Block Copy\n");
|
|
switch (direction) {
|
|
case 1:
|
|
debug_log("[DMA] Transfer direction: ram to device\n");
|
|
debug_log("[DMA] Transfer size: %d words\n", words);
|
|
while (words > 0) {
|
|
uint32_t current_addr = addr & 0x1ffffc;
|
|
uint32_t data = bus.mem.read32(current_addr);
|
|
bus.Gpu.execute_gp0(data);
|
|
if (incrementing) addr += 4; else addr -= 4;
|
|
words--;
|
|
}
|
|
bus.mem.Ch2.BCR &= 0xffff; // SyncMode=1 decrements BA to zero
|
|
bus.mem.Ch2.CHCR &= ~(1 << 24);
|
|
bus.mem.Ch2.CHCR &= ~(1 << 28);
|
|
//debug = false;
|
|
if (((bus.mem.DICR >> 18) & 1) && ((bus.mem.DICR >> 23) & 1)) {
|
|
bus.mem.DICR |= (1 << 26);
|
|
bus.mem.I_STAT |= 0b1000;
|
|
//should_service_dma_irq = true;
|
|
}
|
|
return;
|
|
case 0:
|
|
debug_log("[DMA] Transfer direction: device to ram\n");
|
|
debug_log("[DMA] Transfer size: %d words\n", words);
|
|
while (words > 0) {
|
|
uint32_t current_addr = addr & 0x1ffffc;
|
|
bus.mem.write32(current_addr, bus.Gpu.ReadBuffer[bus.Gpu.ReadBufferCnt++]);
|
|
if (incrementing) addr += 4; else addr -= 4;
|
|
words--;
|
|
}
|
|
bus.mem.Ch2.BCR &= 0xffff; // SyncMode=1 decrements BA to zero
|
|
bus.mem.Ch2.CHCR &= ~(1 << 24);
|
|
bus.mem.Ch2.CHCR &= ~(1 << 28);
|
|
//debug = false;
|
|
if (((bus.mem.DICR >> 18) & 1) && ((bus.mem.DICR >> 23) & 1)) {
|
|
bus.mem.DICR |= (1 << 26);
|
|
bus.mem.I_STAT |= 0b1000;
|
|
//should_service_dma_irq = true;
|
|
}
|
|
return;
|
|
default:
|
|
printf("[DMA] Unhandled Direction (GPU Block Copy)");
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
case 2: // Linked List
|
|
debug_log("[DMA] Start GPU Linked List\n");
|
|
switch (direction) {
|
|
case 1:
|
|
debug_log("[DMA] Transfer direction: ram to device\n");
|
|
while (1) {
|
|
uint32_t _header = bus.mem.read32(addr);
|
|
auto _words = _header >> 24;
|
|
while (_words > 0) {
|
|
addr += 4;
|
|
uint32_t command = bus.mem.read32(addr);
|
|
addr &= 0x1ffffc;
|
|
bus.Gpu.execute_gp0(command);
|
|
_words--;
|
|
}
|
|
if ((_header & 0x800000) != 0)
|
|
break;
|
|
addr = _header & 0x1ffffc;
|
|
}
|
|
if (((bus.mem.DICR >> 18) & 1) && ((bus.mem.DICR >> 23) & 1)) {
|
|
bus.mem.DICR |= (1 << 26);
|
|
bus.mem.I_STAT |= 0b1000;
|
|
//should_service_dma_irq = true;
|
|
}
|
|
bus.mem.Ch2.CHCR &= ~(1 << 24);
|
|
debug_log("[DMA] GPU Linked List transfer complete\n");
|
|
//debug = false;
|
|
return;
|
|
default:
|
|
printf("[DMA] Unhandled Direction (GPU Linked List)\n");
|
|
exit(0);
|
|
}
|
|
default:
|
|
printf("[DMA] Unhandled sync mode (GPU)");
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
case 3: { // CDROM
|
|
auto sync_mode = (bus.mem.Ch3.CHCR >> 9) & 0b11;
|
|
bool incrementing = ((bus.mem.Ch3.CHCR >> 1) & 1) == 0;
|
|
uint16_t words = (bus.mem.Ch3.BCR) & 0xffff;
|
|
auto direction = (bus.mem.Ch3.CHCR) & 1;
|
|
uint32_t addr = bus.mem.Ch3.MADR;
|
|
|
|
switch (sync_mode) { // switch on the sync mode
|
|
case 0: { // block dma
|
|
debug_log("[DMA] Start CDROM Block Copy\n");
|
|
uint32_t current_addr = addr & 0x1ffffc;
|
|
switch (direction) {
|
|
case 0:
|
|
debug_log("[DMA] Transfer direction: device to ram\n");
|
|
debug_log("[DMA] MADR: 0x%x\n", addr);
|
|
debug_log("[DMA] Transfer size: %d words\n", words);
|
|
while (words > 0) {
|
|
current_addr = addr & 0x1ffffc;
|
|
|
|
uint8_t b1 = bus.mem.CDROM.cd.ReadDataByte();
|
|
uint8_t b2 = bus.mem.CDROM.cd.ReadDataByte();
|
|
uint8_t b3 = bus.mem.CDROM.cd.ReadDataByte();
|
|
uint8_t b4 = bus.mem.CDROM.cd.ReadDataByte();
|
|
uint32_t word = (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
|
|
bus.mem.write32(current_addr, word);
|
|
if (incrementing) addr += 4; else addr -= 4;
|
|
words--;
|
|
}
|
|
if (bus.mem.Ch3.CHCR & (1 << 8)) bus.mem.Ch3.BCR &= ~0xffff; // SyncMode=0 with chopping enabled decrements BC to zero
|
|
printf("[DMA] CDROM Block Copy completed (pc = 0x%08x)\n", pc);
|
|
bus.mem.Ch3.CHCR &= ~(1 << 24);
|
|
bus.mem.Ch3.CHCR &= ~(1 << 28);
|
|
//debug = false;
|
|
if (((bus.mem.DICR >> 19) & 1) && ((bus.mem.DICR >> 23) & 1)) {
|
|
bus.mem.DICR |= (1 << 27);
|
|
bus.mem.I_STAT |= 0b1000;
|
|
//should_service_dma_irq = true;
|
|
}
|
|
return;
|
|
default:
|
|
printf("[DMA] Unhandled Direction (CDROM Block Copy)\n");
|
|
exit(0);
|
|
}
|
|
printf("\n");
|
|
exit(0);
|
|
}
|
|
default:
|
|
printf("[DMA] Unhandled sync mode (CDROM: %d)", sync_mode);
|
|
exit(0);
|
|
}
|
|
printf("\n");
|
|
exit(0);
|
|
break;
|
|
}
|
|
|
|
case 4: { // SPU
|
|
auto sync_mode = (bus.mem.Ch4.CHCR >> 9) & 0b11;
|
|
bool incrementing = ((bus.mem.Ch4.CHCR >> 1) & 1) == 0;
|
|
uint16_t words = (bus.mem.Ch4.BCR) & 0xffff;
|
|
words *= (bus.mem.Ch2.BCR >> 16);
|
|
auto direction = (bus.mem.Ch4.CHCR) & 1;
|
|
uint32_t addr = bus.mem.Ch4.MADR;
|
|
|
|
switch (sync_mode) { // switch on the sync mode
|
|
case 1: { // block dma
|
|
debug_log("[DMA] Start SPU Block Copy (stubbed)\n");
|
|
uint32_t current_addr = addr & 0x1ffffc;
|
|
uint32_t spu_addr = bus.mem.spu_ram_transfer_address * 8;
|
|
switch (direction) {
|
|
case 1:
|
|
debug_log("[DMA] Transfer direction: ram to device\n");
|
|
debug_log("[DMA] MADR: 0x%x\n", addr);
|
|
debug_log("[DMA] Transfer size: %d words\n", words);
|
|
while (words > 0) {
|
|
current_addr = addr & 0x1ffffc;
|
|
spu_addr &= 0x7ffff;
|
|
|
|
uint32_t word = bus.mem.read32(current_addr);
|
|
uint16_t halfword1 = (word >> 16);
|
|
uint16_t halfword2 = (word & 0xffff);
|
|
bus.mem.spu_ram[spu_addr + 0] = (halfword1 >> 8);
|
|
bus.mem.spu_ram[spu_addr + 1] = (halfword1 & 0xff);
|
|
bus.mem.spu_ram[spu_addr + 2] = (halfword2 >> 8);
|
|
bus.mem.spu_ram[spu_addr + 3] = (halfword2 & 0xff);
|
|
|
|
spu_addr += 4;
|
|
if (incrementing) addr += 4; else addr -= 4;
|
|
words--;
|
|
}
|
|
|
|
printf("[DMA] SPU Block Copy completed (pc = 0x%08x)\n", pc);
|
|
bus.mem.Ch4.CHCR &= ~(1 << 24);
|
|
bus.mem.Ch4.CHCR &= ~(1 << 28);
|
|
if (((bus.mem.DICR >> 20) & 1) && ((bus.mem.DICR >> 23) & 1)) {
|
|
bus.mem.DICR |= (1 << 28);
|
|
bus.mem.I_STAT |= 0b1000;
|
|
//bus.mem.I_STAT |= (1 << 9);
|
|
}
|
|
return;
|
|
case 0:
|
|
debug_log("[DMA] Transfer direction: device to ram\n");
|
|
debug_log("[DMA] MADR: 0x%x\n", addr);
|
|
debug_log("[DMA] Transfer size: %d words\n", words);
|
|
while (words > 0) {
|
|
current_addr = addr & 0x1ffffc;
|
|
spu_addr &= 0x7ffff;
|
|
|
|
uint8_t b1 = bus.mem.spu_ram[spu_addr + 0];
|
|
uint8_t b2 = bus.mem.spu_ram[spu_addr + 1];
|
|
uint8_t b3 = bus.mem.spu_ram[spu_addr + 2];
|
|
uint8_t b4 = bus.mem.spu_ram[spu_addr + 3];
|
|
uint32_t word = (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
|
|
bus.mem.write32(current_addr, word);
|
|
|
|
spu_addr += 4;
|
|
if (incrementing) addr += 4; else addr -= 4;
|
|
words--;
|
|
}
|
|
printf("[DMA] SPU Block Copy completed (pc = 0x%08x)\n", pc);
|
|
bus.mem.Ch4.CHCR &= ~(1 << 24);
|
|
bus.mem.Ch4.CHCR &= ~(1 << 28);
|
|
if (((bus.mem.DICR >> 20) & 1) && ((bus.mem.DICR >> 23) & 1)) {
|
|
bus.mem.DICR |= (1 << 28);
|
|
bus.mem.I_STAT |= 0b1000;
|
|
//bus.mem.I_STAT |= (1 << 9);
|
|
}
|
|
return;
|
|
default:
|
|
printf("[DMA] Unhandled Direction (SPU Block Copy)\n");
|
|
exit(0);
|
|
}
|
|
printf("\n");
|
|
exit(0);
|
|
}
|
|
default:
|
|
printf("[DMA] Unhandled sync mode (SPU: %d)", sync_mode);
|
|
exit(0);
|
|
}
|
|
printf("\n");
|
|
exit(0);
|
|
break;
|
|
}
|
|
|
|
case 6: { // OTC
|
|
auto sync_mode = (bus.mem.Ch6.CHCR >> 9) & 0b11;
|
|
bool incrementing = ((bus.mem.Ch6.CHCR >> 1) & 1) == 0;
|
|
uint16_t words = (bus.mem.Ch6.BCR) & 0xffff;
|
|
auto direction = (bus.mem.Ch6.CHCR) & 1;
|
|
uint32_t addr = bus.mem.Ch6.MADR;
|
|
|
|
switch (sync_mode) { // switch on the sync mode
|
|
case 0: { // block dma
|
|
debug_log("[DMA] Start OTC Block Copy\n");
|
|
uint32_t current_addr = addr & 0x1ffffc;
|
|
switch (direction) {
|
|
case 0:
|
|
debug_log("[DMA] Transfer direction: device to ram\n");
|
|
debug_log("[DMA] Transfer size: %d words\n", words);
|
|
while (words >= 0) {
|
|
current_addr = addr & 0x1ffffc;
|
|
if (words == 1) {
|
|
bus.mem.write32(current_addr, 0xffffff);
|
|
debug_log("[DMA] OTC Block Copy completed\n");
|
|
bus.mem.Ch6.CHCR &= ~(1 << 24);
|
|
bus.mem.Ch6.CHCR &= ~(1 << 28);
|
|
//debug = false;
|
|
if (((bus.mem.DICR >> 22) & 1) && ((bus.mem.DICR >> 23) & 1)) {
|
|
bus.mem.DICR |= (1 << 30);
|
|
bus.mem.I_STAT |= 0b1000;
|
|
//should_service_dma_irq = true;
|
|
}
|
|
return;
|
|
}
|
|
bus.mem.write32(current_addr, (addr - 4) & 0x1fffff);
|
|
if (incrementing) addr += 4; else addr -= 4;
|
|
words--;
|
|
}
|
|
|
|
default:
|
|
printf("[DMA] Unhandled Direction (OTC Block Copy)\n");
|
|
exit(0);
|
|
}
|
|
printf("\na");
|
|
exit(0);
|
|
}
|
|
default:
|
|
printf("[DMA] Unhandled sync mode (OTC)");
|
|
exit(0);
|
|
}
|
|
printf("\nb");
|
|
exit(0);
|
|
break;
|
|
}
|
|
default:
|
|
printf("[DMA] Unhandled DMA channel");
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
|
|
void cpu::check_dma() {
|
|
bool enabled = ((bus.mem.Ch0.CHCR >> 24) & 1) == 1;
|
|
bool trigger = ((bus.mem.Ch0.CHCR >> 28) & 1) == 1;
|
|
auto sync_mode = (bus.mem.Ch0.CHCR >> 9) & 0b11;
|
|
|
|
auto triggered = !(sync_mode == 0 && !trigger);
|
|
if (enabled && triggered) {
|
|
do_dma<0>();
|
|
}
|
|
|
|
enabled = ((bus.mem.Ch1.CHCR >> 24) & 1) == 1;
|
|
trigger = ((bus.mem.Ch1.CHCR >> 28) & 1) == 1;
|
|
sync_mode = (bus.mem.Ch1.CHCR >> 9) & 0b11;
|
|
|
|
triggered = !(sync_mode == 0 && !trigger);
|
|
if (enabled && triggered) {
|
|
do_dma<1>();
|
|
}
|
|
|
|
enabled = ((bus.mem.Ch2.CHCR >> 24) & 1) == 1;
|
|
trigger = ((bus.mem.Ch2.CHCR >> 28) & 1) == 1;
|
|
sync_mode = (bus.mem.Ch2.CHCR >> 9) & 0b11;
|
|
|
|
triggered = !(sync_mode == 0 && !trigger);
|
|
if (enabled && triggered) { do_dma<2>(); return; }
|
|
|
|
enabled = ((bus.mem.Ch3.CHCR >> 24) & 1) == 1;
|
|
trigger = ((bus.mem.Ch3.CHCR >> 28) & 1) == 1;
|
|
sync_mode = (bus.mem.Ch3.CHCR >> 9) & 0b11;
|
|
|
|
triggered = !(sync_mode == 0 && !trigger);
|
|
if (enabled && triggered) { do_dma<3>(); return; }
|
|
|
|
enabled = ((bus.mem.Ch4.CHCR >> 24) & 1) == 1;
|
|
trigger = ((bus.mem.Ch4.CHCR >> 28) & 1) == 1;
|
|
sync_mode = (bus.mem.Ch4.CHCR >> 9) & 0b11;
|
|
|
|
triggered = !(sync_mode == 0 && !trigger);
|
|
if (enabled && triggered) { do_dma<4>(); return; }
|
|
|
|
enabled = ((bus.mem.Ch6.CHCR >> 24) & 1) == 1;
|
|
trigger = ((bus.mem.Ch6.CHCR >> 28) & 1) == 1;
|
|
sync_mode = (bus.mem.Ch6.CHCR >> 9) & 0b11;
|
|
|
|
triggered = !(sync_mode == 0 && !trigger);
|
|
if (enabled && triggered) { do_dma<6>(); return; }
|
|
}
|
|
|
|
void cpu::execute(uint32_t instr) {
|
|
regs[0] = 0; // $zero
|
|
|
|
#ifdef log_kernel_tty
|
|
if (pc == 0xA0 || pc == 0x800000A0 || pc == 0xA00000A0) {
|
|
if (log_kernel) printf("\nkernel call A(0x%x)", regs[9]);
|
|
}
|
|
if (pc == 0xB0 || pc == 0x800000B0 || pc == 0xA00000B0) {
|
|
if (log_kernel) printf("\nkernel call B(0x%x)", regs[9]);
|
|
if (regs[9] == 0x3d)
|
|
if (tty) { printf("%c", regs[4]); log.AddLog("%c", regs[4]); }
|
|
}
|
|
if (pc == 0xC0 || pc == 0x800000C0 || pc == 0xA00000C0) {
|
|
if (log_kernel) printf("\nkernel call C(0x%x)", regs[9]);
|
|
}
|
|
#endif
|
|
if (pc == 0x80030000 && exe) {
|
|
sideloadExecutable(rom_directory);
|
|
return;
|
|
}
|
|
|
|
uint8_t primary = instr >> 26;
|
|
|
|
if (delay) { // branch delay slot
|
|
pc = jump - 4;
|
|
delay = false;
|
|
}
|
|
|
|
// Quick and dirty hack. TODO: Replace this with a bitfield
|
|
uint8_t rs = (instr >> 21) & 0x1f;
|
|
uint8_t rd = (instr >> 11) & 0x1f;
|
|
uint8_t rt = (instr >> 16) & 0x1f;
|
|
int32_t signed_rs = int32_t(regs[rs]);
|
|
uint16_t imm = instr & 0xffff;
|
|
uint32_t sign_extended_imm = uint32_t(int16_t(imm));
|
|
int32_t signed_sign_extended_imm = int32_t(uint32_t(int32_t(int16_t(imm)))); // ??????????? is this even needed
|
|
uint8_t shift_imm = (instr >> 6) & 0x1f;
|
|
uint32_t jump_imm = instr & 0x3ffffff;
|
|
|
|
switch (primary) {
|
|
case 0x00: {
|
|
uint8_t secondary = instr & 0x3f;
|
|
switch (secondary) {
|
|
case 0x00: {
|
|
uint32_t result = regs[rt] << shift_imm;
|
|
regs[rd] = result;
|
|
debug_log("sll %s, %s, 0x%.8x\n", reg[rd].c_str(), reg[rt].c_str(), shift_imm);
|
|
break;
|
|
}
|
|
case 0x02: {
|
|
regs[rd] = regs[rt] >> shift_imm;
|
|
debug_log("srl 0x%.2X, %s, 0x%.8x\n", reg[rd].c_str(), reg[rt].c_str(), shift_imm);
|
|
break;
|
|
}
|
|
case 0x03: {
|
|
regs[rd] = int32_t(regs[rt]) >> shift_imm;
|
|
debug_log("sra %s, %s, 0x%.8x\n", reg[rd].c_str(), reg[rt].c_str(), shift_imm);
|
|
break;
|
|
}
|
|
case 0x04: {
|
|
regs[rd] = regs[rt] << (regs[rs] & 0x1f);
|
|
debug_log("sllv %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x06: {
|
|
regs[rd] = regs[rt] >> (regs[rs] & 0x1f);
|
|
debug_log("srlv %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x07: {
|
|
regs[rd] = uint32_t(int32_t(regs[rt]) >> (regs[rs] & 0x1f));
|
|
debug_log("srav %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x08: {
|
|
uint32_t addr = regs[rs];
|
|
if (addr & 3) {
|
|
exception(exceptions::BadFetchAddr);
|
|
return;
|
|
}
|
|
jump = addr;
|
|
debug_log("jr %s\n", reg[rs].c_str());
|
|
delay = true;
|
|
break;
|
|
}
|
|
case 0x09: {
|
|
uint32_t addr = regs[rs];
|
|
regs[rd] = pc + 8;
|
|
if (addr & 3) {
|
|
exception(exceptions::BadFetchAddr);
|
|
return;
|
|
}
|
|
jump = addr;
|
|
debug_log("jalr %s\n", reg[rs].c_str());
|
|
delay = true;
|
|
break;
|
|
}
|
|
case 0x0C: {
|
|
debug_log("syscall\n");
|
|
exception(exceptions::SysCall);
|
|
return;
|
|
}
|
|
case 0x0D: {
|
|
debug_log("break\n");
|
|
exception(exceptions::Break);
|
|
return;
|
|
}
|
|
case 0x10: {
|
|
regs[rd] = hi;
|
|
debug_log("mfhi %s\n", reg[rd].c_str());
|
|
break;
|
|
}
|
|
case 0x11: {
|
|
hi = regs[rs];
|
|
debug_log("mthi %s\n", reg[rs].c_str());
|
|
break;
|
|
}
|
|
case 0x12: {
|
|
regs[rd] = lo;
|
|
debug_log("mflo %s\n", reg[rd].c_str());
|
|
break;
|
|
}
|
|
case 0x13: {
|
|
lo = regs[rs];
|
|
debug_log("mtlo %s\n", reg[rs].c_str());
|
|
break;
|
|
}
|
|
case 0x18: {
|
|
int64_t x = int64_t(int32_t(regs[rs]));
|
|
int64_t y = int64_t(int32_t(regs[rt]));
|
|
uint64_t result = uint64_t(x * y);
|
|
|
|
hi = uint32_t((result >> 32) & 0xffffffff);
|
|
lo = uint32_t(result & 0xffffffff);
|
|
debug_log("mult %s, %s", reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x19: {
|
|
uint64_t x = uint64_t(regs[rs]);
|
|
uint64_t y = uint64_t(regs[rt]);
|
|
uint64_t result = x * y;
|
|
|
|
hi = uint32_t(result >> 32);
|
|
lo = uint32_t(result);
|
|
debug_log("multu %s, %s", reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x1A: {
|
|
int32_t n = int32_t(regs[rs]);
|
|
int32_t d = int32_t(regs[rt]);
|
|
|
|
if (d == 0) {
|
|
hi = uint32_t(n);
|
|
if (n >= 0) {
|
|
lo = 0xffffffff;
|
|
}
|
|
else {
|
|
lo = 1;
|
|
}
|
|
break;
|
|
}
|
|
if (uint32_t(n) == 0x80000000 && d == -1) {
|
|
hi = 0;
|
|
lo = 0x80000000;
|
|
break;
|
|
}
|
|
hi = uint32_t(n % d);
|
|
lo = uint32_t(n / d);
|
|
debug_log("div %s, %s\n", reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x1B: {
|
|
if (regs[rt] == 0) {
|
|
hi = regs[rs];
|
|
lo = 0xffffffff;
|
|
break;
|
|
}
|
|
hi = regs[rs] % regs[rt];
|
|
lo = regs[rs] / regs[rt];
|
|
debug_log("divu %s, %s\n", reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x20: {
|
|
debug_log("add %s, %s, %s", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
uint32_t result = regs[rs] + regs[rt];
|
|
|
|
if (((regs[rs] >> 31) == (regs[rt] >> 31)) && ((regs[rs] >> 31) != (result >> 31))) {
|
|
debug_log(" add exception");
|
|
exception(exceptions::Overflow);
|
|
return;
|
|
}
|
|
debug_log("\n");
|
|
regs[rd] = result;
|
|
break;
|
|
}
|
|
case 0x21: {
|
|
regs[rd] = regs[rs] + regs[rt];
|
|
debug_log("addu %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x22: {
|
|
uint32_t result = regs[rs] - regs[rt];
|
|
debug_log("sub %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
if (((regs[rs] ^ result) & (~regs[rt] ^ result)) >> 31) { // overflow
|
|
exception(exceptions::Overflow);
|
|
return;
|
|
}
|
|
regs[rd] = result;
|
|
break;
|
|
}
|
|
case 0x23: {
|
|
regs[rd] = regs[rs] - regs[rt];
|
|
debug_log("subu %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x24: {
|
|
regs[rd] = regs[rs] & regs[rt];
|
|
debug_log("and %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x25: {
|
|
regs[rd] = regs[rs] | regs[rt];
|
|
debug_log("or %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x26: {
|
|
regs[rd] = regs[rs] ^ regs[rt];
|
|
debug_log("xor %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x27: {
|
|
regs[rd] = ~(regs[rs] | regs[rt]);
|
|
debug_log("nor %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case 0x2A: {
|
|
debug_log("slt %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
regs[rd] = int32_t(regs[rs]) < int32_t(regs[rt]);
|
|
break;
|
|
}
|
|
case 0x2B: {
|
|
debug_log("sltu %s, %s, %s\n", reg[rd].c_str(), reg[rs].c_str(), reg[rt].c_str());
|
|
regs[rd] = regs[rs] < regs[rt];
|
|
break;
|
|
}
|
|
|
|
default:
|
|
debug_err("\nUnimplemented subinstruction: 0x%x", secondary);
|
|
exit(0);
|
|
|
|
}
|
|
break;
|
|
}
|
|
case 0x01: {
|
|
auto bit16 = (instr >> 16) & 1;
|
|
bool link = ((instr >> 17) & 0xF) == 8;
|
|
if (bit16 == 0) { // bltz
|
|
if (link) regs[0x1f] = pc + 8; // check if link (bltzal)
|
|
debug_log("BxxZ %s, 0x%.4x", reg[rs].c_str(), sign_extended_imm);
|
|
if (signed_rs < 0) {
|
|
jump = (pc + 4) + (sign_extended_imm << 2);
|
|
delay = true;
|
|
debug_log(" branched\n");
|
|
}
|
|
else { debug_log("\n"); }
|
|
break;
|
|
}
|
|
else { // bgez
|
|
debug_log("BxxZ %s, 0x%.4x", reg[rs].c_str(), sign_extended_imm);
|
|
if (link) regs[0x1f] = pc + 8; // check if link (bgezal)
|
|
if (signed_rs >= 0) {
|
|
jump = (pc + 4) + (sign_extended_imm << 2);
|
|
delay = true;
|
|
debug_log(" branched\n");
|
|
}
|
|
else { debug_log("\n"); }
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 0x02: {
|
|
jump = (pc & 0xf0000000) | (jump_imm << 2);
|
|
debug_log("j 0x%.8x\n", jump_imm);
|
|
delay = true;
|
|
break;
|
|
}
|
|
case 0x03: {
|
|
jump = (pc & 0xf0000000) | (jump_imm << 2);
|
|
debug_log("jal 0x%.8x\n", jump_imm);
|
|
regs[0x1f] = pc + 8;
|
|
delay = true;
|
|
break;
|
|
}
|
|
case 0x04: {
|
|
debug_log("beq %s, %s, 0x%.4x", reg[rs].c_str(), reg[rt].c_str(), sign_extended_imm);
|
|
if (regs[rs] == regs[rt]) {
|
|
jump = (pc + 4) + (sign_extended_imm << 2);
|
|
delay = true;
|
|
debug_log(" branched\n");
|
|
}
|
|
else { debug_log("\n"); }
|
|
break;
|
|
}
|
|
case 0x05: {
|
|
debug_log("bne %s, %s, 0x%.4x", reg[rs].c_str(), reg[rt].c_str(), sign_extended_imm);
|
|
if (regs[rs] != regs[rt]) {
|
|
jump = (pc + 4) + (sign_extended_imm << 2);
|
|
delay = true;
|
|
debug_log(" branched\n");
|
|
}
|
|
else { debug_log("\n"); }
|
|
break;
|
|
}
|
|
case 0x06: {
|
|
debug_log("blez %s, 0x%.4x", reg[rs].c_str(), sign_extended_imm);
|
|
if (signed_rs <= 0) {
|
|
jump = (pc + 4) + (sign_extended_imm << 2);
|
|
delay = true;
|
|
debug_log(" branched\n");
|
|
}
|
|
else { debug_log("\n"); }
|
|
break;
|
|
}
|
|
case 0x07: {
|
|
debug_log("bgtz %s, 0x%.4x", reg[rs].c_str(), sign_extended_imm);
|
|
if (signed_rs > 0) {
|
|
jump = (pc + 4) + (sign_extended_imm << 2);
|
|
delay = true;
|
|
debug_log(" branched\n");
|
|
}
|
|
else { debug_log("\n"); }
|
|
break;
|
|
}
|
|
case 0x08: {
|
|
debug_log("addi %s, %s, 0x%.4x", reg[rs].c_str(), reg[rt].c_str(), imm);
|
|
uint32_t result = regs[rs] + sign_extended_imm;
|
|
if (((regs[rs] >> 31) == (sign_extended_imm >> 31)) && ((regs[rs] >> 31) != (result >> 31))) {
|
|
debug_log(" addi exception");
|
|
exception(exceptions::Overflow);
|
|
return;
|
|
}
|
|
debug_log("\n");
|
|
regs[rt] = result;
|
|
break;
|
|
}
|
|
case 0x09: {
|
|
regs[rt] = regs[rs] + sign_extended_imm;
|
|
debug_log("addiu %s, %s, 0x%.4x\n", reg[rs].c_str(), reg[rt].c_str(), imm);
|
|
break;
|
|
}
|
|
case 0x0A: {
|
|
regs[rt] = 0;
|
|
if (signed_rs < (int32_t)sign_extended_imm)
|
|
regs[rt] = 1;
|
|
debug_log("slti %s, %s, 0x%.4X\n", reg[rs].c_str(), reg[rt].c_str(), imm);
|
|
break;
|
|
}
|
|
case 0x0B: {
|
|
regs[rt] = regs[rs] < sign_extended_imm;
|
|
debug_log("sltiu %s, %s, 0x%.4X\n", reg[rs].c_str(), reg[rt].c_str(), imm);
|
|
break;
|
|
}
|
|
case 0x0C: {
|
|
debug_log("andi %s, %s, 0x%.4x\n", reg[rs].c_str(), reg[rt].c_str(), imm);
|
|
regs[rt] = (regs[rs] & imm);
|
|
break;
|
|
}
|
|
case 0x0D: {
|
|
debug_log("ori %s, %s, 0x%.4x\n", reg[rs].c_str(), reg[rt].c_str(), imm);
|
|
regs[rt] = (regs[rs] | imm);
|
|
break;
|
|
}
|
|
case 0x0E: {
|
|
debug_log("xori %s, %s, 0x%.4x\n", reg[rs].c_str(), reg[rt].c_str(), imm);
|
|
regs[rt] = (regs[rs] ^ imm);
|
|
break;
|
|
}
|
|
case 0x0F: {
|
|
debug_log("lui %s, 0x%.4x\n", reg[rt].c_str(), imm);
|
|
regs[rt] = imm << 16;
|
|
break;
|
|
}
|
|
case 0x10: {
|
|
uint8_t cop_instr = instr >> 21;
|
|
switch (cop_instr) {
|
|
case(0b00000): { // mfc0
|
|
regs[rt] = COP0.regs[rd];
|
|
debug_log("mfc0 %s, %s\n", reg[rd].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case(0b00100): { // mtc0
|
|
COP0.regs[rd] = regs[rt];
|
|
debug_log("mtc0 %s, %s\n", reg[rd].c_str(), reg[rt].c_str());
|
|
break;
|
|
}
|
|
case(0b10000): { // rfe
|
|
if ((instr & 0x3f) != 0b010000) {
|
|
printf("Invalid RFE");
|
|
exit(0);
|
|
}
|
|
debug_log("rfe");
|
|
//auto mode = COP0.regs[12] & 0x3f;
|
|
//COP0.regs[12] &= ~0x3f;
|
|
//COP0.regs[12] |= mode >> 2;
|
|
COP0.regs[12] = (COP0.regs[12] & 0xfffffff0) | ((COP0.regs[12] & 0x3c) >> 2);
|
|
break;
|
|
}
|
|
default:
|
|
printf("Unknown coprocessor instruction: 0x%.2x", cop_instr);
|
|
exit(0);
|
|
}
|
|
break;
|
|
}
|
|
case 0x12: {
|
|
GTE.execute(instr, regs);
|
|
break;
|
|
}
|
|
case 0x20: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
if ((COP0.regs[12] & 0x10000) == 1) {
|
|
debug_log(" cache isolated, ignoring load\n");
|
|
break;
|
|
}
|
|
|
|
uint8_t byte = bus.mem.read(addr);
|
|
regs[rt] = int32_t(int16_t(int8_t(byte)));
|
|
debug_log("lb %s, 0x%.4x(%s)\n", reg[rt].c_str(), imm, reg[rs].c_str());
|
|
break;
|
|
}
|
|
case 0x21: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
debug_log("lh %s, 0x%.4x(%s)\n", reg[rt].c_str(), imm, reg[rs].c_str());
|
|
if ((COP0.regs[12] & 0x10000) == 0) {
|
|
int16_t data = int16_t(bus.mem.read16(addr));
|
|
regs[rt] = uint32_t(int32_t(data));
|
|
debug_log("\n");
|
|
}
|
|
else {
|
|
debug_log(" cache isolated, ignoring load\n");
|
|
}
|
|
break;
|
|
}
|
|
case 0x22: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
uint32_t aligned_addr = addr & ~3;
|
|
uint32_t aligned_word = bus.mem.read32(aligned_addr);
|
|
debug_log("lwl %s, 0x%.4x(%s)", reg[rt].c_str(), imm, reg[rs].c_str());
|
|
switch (addr & 3) {
|
|
case 0:
|
|
regs[rt] = (regs[rt] & 0x00ffffff) | (aligned_word << 24);
|
|
break;
|
|
case 1:
|
|
regs[rt] = (regs[rt] & 0x0000ffff) | (aligned_word << 16);
|
|
break;
|
|
case 2:
|
|
regs[rt] = (regs[rt] & 0x000000ff) | (aligned_word << 8);
|
|
break;
|
|
case 3:
|
|
regs[rt] = (regs[rt] & 0x00000000) | (aligned_word << 0);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 0x23: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
uint32_t value = bus.mem.read32(addr);
|
|
debug_log("lw %s, 0x%.4x(%s) ; addr = 0x%08x, val = 0x%08x", reg[rt].c_str(), imm, reg[rs].c_str(), addr, value);
|
|
if ((COP0.regs[12] & 0x10000) == 0) {
|
|
regs[rt] = value;
|
|
debug_log("\n");
|
|
}
|
|
else {
|
|
debug_log(" cache isolated, ignoring load\n");
|
|
}
|
|
break;
|
|
}
|
|
case 0x24: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
debug_log("lbu %s, 0x%.4x(%s)", reg[rt].c_str(), imm, reg[rs].c_str());
|
|
if ((COP0.regs[12] & 0x10000) == 0) {
|
|
uint8_t byte = bus.mem.read(addr);
|
|
regs[rt] = uint32_t(byte);
|
|
debug_log("\n");
|
|
}
|
|
else {
|
|
debug_log(" cache isolated, ignoring load\n");
|
|
}
|
|
break;
|
|
}
|
|
case 0x25: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
debug_log("lhu %s, 0x%.4x(%s)", reg[rt].c_str(), imm, reg[rs].c_str());
|
|
if ((COP0.regs[12] & 0x10000) == 0) {
|
|
uint16_t data = bus.mem.read16(addr);
|
|
regs[rt] = uint32_t(data);
|
|
debug_log("\n");
|
|
}
|
|
else {
|
|
debug_log(" cache isolated, ignoring load\n");
|
|
}
|
|
break;
|
|
}
|
|
case 0x26: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
uint32_t aligned_addr = addr & ~3;
|
|
uint32_t aligned_word = bus.mem.read32(aligned_addr);
|
|
debug_log("lwr %s, 0x%.4x(%s)", reg[rt].c_str(), imm, reg[rs].c_str());
|
|
switch (addr & 3) {
|
|
case 0:
|
|
regs[rt] = (regs[rt] & 0x00000000) | (aligned_word >> 0);
|
|
break;
|
|
case 1:
|
|
regs[rt] = (regs[rt] & 0xff000000) | (aligned_word >> 8);
|
|
break;
|
|
case 2:
|
|
regs[rt] = (regs[rt] & 0xffff0000) | (aligned_word >> 16);
|
|
break;
|
|
case 3:
|
|
regs[rt] = (regs[rt] & 0xffffff00) | (aligned_word >> 24);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 0x28: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
debug_log("sb %s, 0x%.4x(%s)", reg[rt].c_str(), imm, reg[rs].c_str());
|
|
if ((COP0.regs[12] & 0x10000) == 0) {
|
|
bus.mem.write(addr, uint8_t(regs[rt]), true);
|
|
debug_log("\n");
|
|
}
|
|
else {
|
|
debug_log(" cache isolated, ignoring write\n");
|
|
}
|
|
break;
|
|
}
|
|
case 0x29: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
if (addr & 1) {
|
|
exception(exceptions::BadStoreAddr);
|
|
return;
|
|
}
|
|
debug_log("sh %s, 0x%.4x(%s)", reg[rt].c_str(), imm, reg[rs].c_str());
|
|
if ((COP0.regs[12] & 0x10000) == 0) {
|
|
bus.mem.write16(addr, uint16_t(regs[rt]));
|
|
debug_log("\n");
|
|
}
|
|
else {
|
|
debug_log(" cache isolated, ignoring write\n");
|
|
}
|
|
break;
|
|
}
|
|
case 0x2A: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
uint32_t aligned_addr = addr & ~3;
|
|
|
|
uint32_t mem = bus.mem.read32(aligned_addr);
|
|
uint32_t val = 0;
|
|
|
|
switch (addr & 3) {
|
|
case 0:
|
|
val = (mem & 0xffffff00) | (regs[rt] >> 24);
|
|
break;
|
|
case 1:
|
|
val = (mem & 0xffff0000) | (regs[rt] >> 16);
|
|
break;
|
|
case 2:
|
|
val = (mem & 0xff000000) | (regs[rt] >> 8);
|
|
break;
|
|
case 3:
|
|
val = (mem & 0x00000000) | (regs[rt] >> 0);
|
|
break;
|
|
}
|
|
|
|
bus.mem.write32(aligned_addr, val);
|
|
debug_log("swl %s, 0x%.4x(%s)", reg[rt].c_str(), imm, reg[rs].c_str());
|
|
break;
|
|
}
|
|
case 0x2B: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
debug_log("sw %s, 0x%.4x(%s) ; %s = 0x%.8x\n", reg[rt].c_str(), imm, reg[rs].c_str(), reg[rs].c_str(), regs[rs]);
|
|
if (addr & 3) {
|
|
exception(exceptions::BadStoreAddr);
|
|
return;
|
|
}
|
|
|
|
uint32_t masked_addr = bus.mem.mask_address(addr);
|
|
if (masked_addr == 0x1f801810) { // handle gp0 command
|
|
bus.Gpu.execute_gp0(regs[rt]);
|
|
break;
|
|
}
|
|
|
|
if (masked_addr == 0x1f801814) { // handle gp1 command
|
|
bus.Gpu.execute_gp1(regs[rt]);
|
|
break;
|
|
}
|
|
|
|
if ((COP0.regs[12] & 0x10000) == 0) {
|
|
bus.mem.write32(addr, regs[rt]);
|
|
debug_log("\n");
|
|
}
|
|
break;
|
|
}
|
|
case 0x2E: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
uint32_t aligned_addr = addr & ~3;
|
|
|
|
uint32_t mem = bus.mem.read32(aligned_addr);
|
|
uint32_t val = 0;
|
|
|
|
switch (addr & 3) {
|
|
case 0:
|
|
val = (mem & 0x00000000) | (regs[rt] << 0);
|
|
break;
|
|
case 1:
|
|
val = (mem & 0x000000ff) | (regs[rt] << 8);
|
|
break;
|
|
case 2:
|
|
val = (mem & 0x0000ffff) | (regs[rt] << 16);
|
|
break;
|
|
case 3:
|
|
val = (mem & 0x00ffffff) | (regs[rt] << 24);
|
|
break;
|
|
}
|
|
|
|
bus.mem.write32(aligned_addr, val);
|
|
debug_log("swr %s, 0x%.4x(%s)", reg[rt].c_str(), imm, reg[rs].c_str());
|
|
break;
|
|
}
|
|
case 0x32: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
GTE.writeCop2d(rt, bus.mem.read32(addr));
|
|
break;
|
|
}
|
|
case 0x3A: {
|
|
uint32_t addr = regs[rs] + sign_extended_imm;
|
|
uint32_t val = GTE.readCop2d(rt);
|
|
bus.mem.write32(addr, GTE.readCop2d(rt));
|
|
break;
|
|
}
|
|
default:
|
|
debug_err("\nUnimplemented instruction: 0x%x @ 0x%08x", primary, pc);
|
|
exit(0);
|
|
}
|
|
pc += 4;
|
|
}
|
|
|
|
void cpu::check_CDROM_IRQ() {
|
|
//bus.mem.CDROM.delayedINT();
|
|
//if (bus.mem.CDROM.queued_read) {
|
|
// bus.mem.CDROM.queuedRead();
|
|
//}
|
|
if (bus.mem.CDROM.interrupt_enable & bus.mem.CDROM.interrupt_flag) {
|
|
//printf("[IRQ] CDROM INT%d, setting I_STAT bit (I_MASK = 0x%x)\n", bus.mem.CDROM.interrupt_flag & 0b111, bus.mem.I_MASK);
|
|
bus.mem.I_STAT |= (1 << 2);
|
|
}
|
|
}
|
|
void IRQ7(void* dataptr) {
|
|
cpu* cpuptr = (cpu*)dataptr;
|
|
if (!cpuptr->bus.mem.pads.abort_irq) {
|
|
cpuptr->bus.mem.I_STAT |= (1 << 7);
|
|
cpuptr->bus.mem.pads.joy_stat |= (1 << 9);
|
|
cpuptr->bus.mem.pads.joy_stat &= ~(1 << 7);
|
|
//printf("[IRQ] IRQ7\n");
|
|
}
|
|
else cpuptr->bus.mem.pads.abort_irq = false;
|
|
}
|
|
void cpu::step() {
|
|
if (shouldCheckDMA) {
|
|
check_dma();
|
|
shouldCheckDMA = false;
|
|
//check_dma(); // TODO: Only check DMA when control registers are written to
|
|
}
|
|
|
|
if (bus.mem.I_STAT & bus.mem.I_MASK) {
|
|
COP0.regs[13] |= (1 << 10);
|
|
if ((COP0.regs[12] & 1) && (COP0.regs[12] & (1 << 10))) {
|
|
//printf("[IRQ] Interrupt fired\n");
|
|
exception(exceptions::INT);
|
|
}
|
|
}
|
|
const auto instr = bus.mem.read32(pc);
|
|
#ifdef log_cpu
|
|
debug_log("0x%.8X | 0x%.8X: ", pc, instr);
|
|
#endif
|
|
execute(instr);
|
|
frame_cycles += 2;
|
|
|
|
auto clock_source = (bus.mem.tmr1.counter_mode >> 8) & 3;
|
|
auto reset_counter = (bus.mem.tmr1.counter_mode >> 3) & 1;
|
|
auto irq_when_target = (bus.mem.tmr1.counter_mode >> 4) & 1;
|
|
auto sync_mode = (bus.mem.tmr1.counter_mode >> 1) & 3;
|
|
bus.mem.tmr0.current_value++;
|
|
if (!(bus.mem.tmr1.counter_mode & 1) || (sync_mode != 3)) {
|
|
if ((clock_source == 1) || (clock_source == 3)) {
|
|
bus.mem.tmr1_stub += 2;
|
|
if (bus.mem.tmr1_stub > 2160) {
|
|
bus.mem.tmr1.current_value++;
|
|
bus.mem.tmr1_stub = 0;
|
|
}
|
|
}
|
|
else bus.mem.tmr1.current_value++;
|
|
}
|
|
if (irq_when_target && (bus.mem.tmr1.current_value >= bus.mem.tmr1.target_value)) {
|
|
bus.mem.I_STAT |= 0b100000;
|
|
}
|
|
if (reset_counter && (bus.mem.tmr1.current_value >= bus.mem.tmr1.target_value)) bus.mem.tmr1.current_value = 0;
|
|
|
|
clock_source = (bus.mem.tmr2.counter_mode >> 8) & 3;
|
|
reset_counter = (bus.mem.tmr2.counter_mode >> 3) & 1;
|
|
irq_when_target = (bus.mem.tmr2.counter_mode >> 4) & 1;
|
|
if (irq_when_target && (bus.mem.tmr2.current_value >= bus.mem.tmr2.target_value)) {
|
|
bus.mem.I_STAT |= 0b1000000;
|
|
}
|
|
if (reset_counter && (bus.mem.tmr2.current_value >= bus.mem.tmr2.target_value)) bus.mem.tmr2.current_value = 0;
|
|
if ((clock_source == 2) || (clock_source == 3)) {
|
|
bus.mem.tmr2_stub += 2;
|
|
if (bus.mem.tmr2_stub > 8) {
|
|
bus.mem.tmr2.current_value++;
|
|
bus.mem.tmr2_stub = 0;
|
|
}
|
|
}
|
|
else bus.mem.tmr2.current_value++;
|
|
bus.mem.CDROM.Scheduler.tick(2);
|
|
if (bus.mem.CDROM.interrupt_enable & bus.mem.CDROM.interrupt_flag)
|
|
bus.mem.I_STAT |= (1 << 2);
|
|
if (bus.mem.pads.irq) {
|
|
//printf("[PAD] ACK\n");
|
|
bus.mem.CDROM.Scheduler.push(&IRQ7, bus.mem.CDROM.Scheduler.time + 1500, this, "IRQ7");
|
|
bus.mem.pads.irq = false;
|
|
//bus.mem.pads.joy_stat |= (1 << 7);
|
|
//bus.mem.pads.joy_stat |= (1 << 9);
|
|
}
|
|
} |