Lots of changes

This commit is contained in:
Your Name 2022-09-05 10:47:29 -04:00
parent 32c7787c86
commit 72ba66a000
12 changed files with 489 additions and 32 deletions

3
.gitignore vendored
View file

@ -1,4 +1,5 @@
build
compile_commands.json
.clangd
.vscode
.vscode
boost

View file

@ -5,7 +5,8 @@ set(CMAKE_MINIMUM_REQUIRED_VERSION 3.16.3)
set(SOURCES src/main.cpp
src/app/Application.cpp
src/emu/Bus.cpp
src/emu/cpu/EmotionEngine.cpp)
src/emu/cpu/EmotionEngine.cpp
src/emu/cpu/instructions.cpp)
set(CMAKE_BUILD_TYPE DEBUG)

View file

@ -8,6 +8,7 @@ class Bus
private:
uint8_t ram[0x2000000];
uint8_t bios[0x400000];
uint8_t scratchpad[0x4000];
uint32_t Translate(uint32_t addr)
{
@ -40,9 +41,40 @@ public:
}
template<typename T>
void write(uint32_t addr, T)
void write(uint32_t addr, T data)
{
addr = Translate(addr);
if (addr >= 0x70000000 && addr < 0x70004000)
{
*(T*)&scratchpad[addr - 0x70000000] = data;
return;
}
switch (addr)
{
case 0x1000f500:
return;
}
printf("[emu/Bus]: %s: Write to unknown addr 0x%08x\n", __FUNCTION__, addr);
exit(1);
}
// Returns whether a region of memory is cacheable by the bus
bool IsCacheable(uint32_t addr)
{
if (addr <= 0x7FFFFFFF)
return true;
else if (addr >= 0x80000000 && addr <= 0x9FFFFFFF)
return true;
else if (addr >= 0xA0000000 && addr <= 0xBFFFFFFF)
return false;
else if (addr >= 0xC0000000 && addr <= 0xDFFFFFFF)
return true;
else if (addr >= 0xE0000000 && addr <= 0xFFFFFFF)
return true;
else
return false;
}
};

View file

@ -11,6 +11,8 @@ EmotionEngine::EmotionEngine(Bus* bus)
memset(regs, 0, sizeof(regs));
pc = 0xBFC00000;
next_pc = pc + 4;
cop0_regs[15] = 0x2E20;
}
void EmotionEngine::Clock()
@ -18,16 +20,99 @@ void EmotionEngine::Clock()
Opcode instr;
instr.full = Read32Instr(pc);
AdvancePC();
if (instr.full == 0)
{
printf("nop\n");
return;
}
switch (instr.r_type.opcode)
{
case 0x00:
{
switch (instr.r_type.func)
{
case 0x00:
sll(instr);
break;
case 0x08:
jr(instr);
break;
case 0x09:
jalr(instr);
break;
case 0x0F:
printf("sync\n");
break;
case 0x2d:
daddu(instr);
break;
default:
printf("[emu/CPU]: %s: Unknown special instruction 0x%08x (0x%02x)\n", __FUNCTION__, instr.full, instr.r_type.func);
Application::Exit(1);
break;
}
}
break;
case 0x03:
j(instr);
break;
case 0x05:
bne(instr);
break;
case 0x09:
addiu(instr);
break;
case 0x0A:
slti(instr);
break;
case 0x0C:
andi(instr);
break;
case 0x0D:
ori(instr);
break;
case 0x0F:
lui(instr);
break;
case 0x10:
{
switch (instr.r_type.rs)
{
case 0:
mfc0(instr);
break;
case 4:
mtc0(instr);
break;
case 0x10:
printf("TODO: tlbwi\n");
break;
default:
printf("[emu/CPU]: %s: Unknown cop0 instruction 0x%08x (0x%02x)\n", __FUNCTION__, instr.full, instr.r_type.rs);
Application::Exit(1);
break;
}
}
break;
case 0x2B:
sw(instr);
break;
case 0x3f:
sd(instr);
break;
default:
printf("[emu/CPU]: %s: Unknown instruction 0x%08x (0x%02x)\n", __FUNCTION__, instr.full, instr.r_type.opcode);
Application::Exit(1);
}
regs[0].u64[0] = regs[0].u64[1] = 0;
}
void EmotionEngine::Dump()
{
for (int i = 0; i < 32; i++)
printf("[emu/CPU]: %s: r%d\t->\t%s\n", __FUNCTION__, i, print_128(regs[i]));
printf("[emu/CPU]: %s: %s\t->\t%s\n", __FUNCTION__, Reg(i), print_128(regs[i]));
}

View file

@ -1,6 +1,7 @@
#pragma once
#include "emu/Bus.h"
#include "emu/cpu/opcode.h"
#include "util/uint128.h"
#include <bits/stdint-uintn.h>
@ -10,6 +11,7 @@ class EmotionEngine
private:
Bus* bus;
uint128_t regs[32];
uint32_t cop0_regs[32];
uint32_t pc, next_pc;
uint64_t hi, lo;
@ -17,53 +19,242 @@ private:
{
bool valid = false;
bool dirty = false;
bool lrf = false;
uint32_t page = 0;
};
struct Cache
{
CacheTag tag;
uint8_t data[64] = {0};
CacheTag tag[2];
uint8_t data[2][64] = {0};
} icache[128], dcache[64];
bool isCacheEnabled = false;
uint32_t Read32Instr(uint32_t addr)
{
if (!bus->IsCacheable(addr) || !isCacheEnabled)
return bus->read<uint32_t>(addr);
uint32_t page = (addr >> 14);
uint32_t index = (addr >> 5) & 8;
uint32_t offset = addr & 0x3F;
Cache& line = icache[index];
if (line.tag.page != page || !line.tag.valid)
for (int way = 0; way < 2; way++)
{
printf("[emu/CPU]: Cache miss at 0x%08x\n", addr);
if (line.tag.dirty)
if (line.tag[way].page == page && line.tag[way].valid)
{
uint32_t p = (line.tag.page << 14);
for (int i = 0; i < 64; i++)
bus->write(p+i, line.data[i]);
return *((uint32_t*)&line.data[way][offset]);
}
line.tag.page = page;
line.tag.valid = true;
line.tag.dirty = false;
for (int i = 0; i < 64; i++)
{
line.data[i] = bus->read<uint8_t>((page << 14) + i);
}
return *(uint32_t*)&line.data[offset];
}
else
{
printf("Cache hit at 0x%08x\n", addr);
return *(uint32_t*)&line.data[offset];
}
// Welp, cache miss
printf("[emu/CPU]: Cache miss for address 0x%08x\n", addr);
int replace = 0;
if (!line.tag[0].valid && line.tag[1].valid)
replace = 0;
else if (line.tag[0].valid && !line.tag[1].valid)
replace = 1;
else
replace = line.tag[0].lrf ^ line.tag[1].lrf;
for (int i = 0; i < 64; i++)
line.data[replace][i] = bus->read<uint8_t>((page << 14) + i);
return *(uint32_t*)&line.data[replace][offset];
}
void Write32(uint32_t addr, uint32_t data)
{
if (!bus->IsCacheable(addr) || !isCacheEnabled)
{
bus->write(addr, data);
return;
}
uint32_t page = (addr >> 13);
uint32_t index = (addr >> 5) & 0b1111111;
uint32_t offset = addr & 0x3F;
Cache& line = dcache[index];
for (int way = 0; way < 2; way++)
{
if (line.tag[way].page == page && line.tag[way].valid)
{
*((uint32_t*)&line.data[way][offset]) = data;
line.tag[way].dirty = true; // We mark as dirty to flush back to main memory on eviction
}
}
printf("[emu/CPU]: Cache miss for address 0x%08x\n", addr);
int replace = 0;
if (!line.tag[0].valid && line.tag[1].valid)
replace = 0;
else if (line.tag[0].valid && !line.tag[1].valid)
replace = 1;
else
replace = line.tag[0].lrf ^ line.tag[1].lrf;
if (line.tag[replace].dirty)
{
for (int i = 0; i < 64; i++)
bus->write((page << 13) + i, line.data[replace][i]);
}
for (int i = 0; i < 64; i++)
{
line.data[replace][i] = bus->read<uint8_t>((page << 13) + i);
}
*(uint32_t*)&line.data[replace][offset] = data;
line.tag[replace].dirty = true;
}
void Write64(uint32_t addr, uint64_t data)
{
if (!bus->IsCacheable(addr) || !isCacheEnabled)
{
bus->write(addr, data);
return;
}
uint32_t page = (addr >> 13);
uint32_t index = (addr >> 5) & 0b1111111;
uint32_t offset = addr & 0x3F;
Cache& line = dcache[index];
for (int way = 0; way < 2; way++)
{
if (line.tag[way].page == page && line.tag[way].valid)
{
*((uint64_t*)&line.data[way][offset]) = data;
line.tag[way].dirty = true; // We mark as dirty to flush back to main memory on eviction
}
}
printf("[emu/CPU]: Cache miss for address 0x%08x\n", addr);
int replace = 0;
if (!line.tag[0].valid && line.tag[1].valid)
replace = 0;
else if (line.tag[0].valid && !line.tag[1].valid)
replace = 1;
else
replace = line.tag[0].lrf ^ line.tag[1].lrf;
if (line.tag[replace].dirty)
{
for (int i = 0; i < 64; i++)
bus->write((page << 13) + i, line.data[replace][i]);
}
for (int i = 0; i < 64; i++)
{
line.data[replace][i] = bus->read<uint8_t>((page << 13) + i);
}
*(uint64_t*)&line.data[replace][offset] = data;
line.tag[replace].dirty = true;
}
void j(Opcode i); // 0x03
void bne(Opcode i); // 0x05
void addiu(Opcode i); // 0x09
void slti(Opcode i); // 0x0A
void andi(Opcode i); // 0x0C
void ori(Opcode i); // 0x0D
void lui(Opcode i); // 0x0F
void mfc0(Opcode i); // 0x10 0x00
void mtc0(Opcode i); // 0x10 0x04
void sw(Opcode i); // 0x2B
void sd(Opcode i); // 0x3f
void sll(Opcode i); // 0x00
void jr(Opcode i); // 0x08
void jalr(Opcode i); // 0x09
void daddu(Opcode i); // 0x2d
void AdvancePC()
{
pc = next_pc;
next_pc += 4;
}
const char* Reg(int index)
{
switch (index)
{
case 0:
return "$zero";
case 1:
return "$at";
case 2:
return "$v0";
case 3:
return "$v1";
case 4:
return "$a0";
case 5:
return "$a1";
case 6:
return "$a2";
case 7:
return "$a3";
case 8:
return "$t0";
case 9:
return "$t1";
case 10:
return "$t2";
case 11:
return "$t3";
case 12:
return "$t4";
case 13:
return "$t5";
case 14:
return "$t6";
case 15:
return "$t7";
case 16:
return "$s0";
case 17:
return "$s1";
case 18:
return "$s2";
case 19:
return "$s3";
case 20:
return "$s4";
case 21:
return "$s5";
case 22:
return "$s6";
case 23:
return "$s7";
case 24:
return "$t8";
case 25:
return "$t9";
case 26:
return "$k0";
case 27:
return "$k1";
case 28:
return "$gp";
case 29:
return "$sp";
case 30:
return "$fp";
case 31:
return "$ra";
default:
return "";
}
}
public:
EmotionEngine(Bus* bus);

View file

@ -0,0 +1,141 @@
#include "emu/cpu/opcode.h"
#include <bits/stdint-intn.h>
#include <bits/stdint-uintn.h>
#include <cstdio>
#include <emu/cpu/EmotionEngine.h>
void EmotionEngine::j(Opcode i)
{
uint32_t target = (i.j_type.target << 2);
next_pc = (pc & 0xF0000000) | target;
printf("j 0x%08x\n", next_pc);
}
void EmotionEngine::bne(Opcode i)
{
int32_t imm = (int32_t)((uint32_t)i.i_type.imm << 2);
printf("bne %s, %s, 0x%08x\n", Reg(i.i_type.rs), Reg(i.i_type.rt), next_pc + imm);
if ((int32_t)regs[i.i_type.rs].u32[0] != (int32_t)regs[i.i_type.rt].u32[0])
{
next_pc = pc + imm;
}
}
void EmotionEngine::addiu(Opcode i)
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
int32_t imm = (int16_t)(uint32_t)i.i_type.imm;
regs[rt].u32[0] = (uint32_t)((int32_t)regs[rt].u32[0] + imm);
printf("addiu %s, %s, %d\n", Reg(rt), Reg(rs), imm);
}
void EmotionEngine::slti(Opcode i)
{
regs[i.i_type.rt].u32[0] = (int32_t)regs[i.i_type.rs].u32[0] < (int32_t)(uint32_t)i.i_type.imm;
printf("slti %s, %s, 0x%04x\n", Reg(i.i_type.rt), Reg(i.i_type.rs), i.i_type.imm);
}
void EmotionEngine::andi(Opcode i)
{
regs[i.i_type.rt].u32[0] = regs[i.i_type.rs].u32[0] & (uint32_t)(int32_t)i.i_type.imm;
printf("andi %s, %s, 0x%08x\n", Reg(i.i_type.rt), Reg(i.i_type.rs), i.i_type.imm);
}
void EmotionEngine::ori(Opcode i)
{
regs[i.i_type.rt].u32[0] = regs[i.i_type.rs].u32[0] | (uint32_t)(int32_t)i.i_type.imm;
printf("ori %s, %s, 0x%08x\n", Reg(i.i_type.rt), Reg(i.i_type.rs), i.i_type.imm);
}
void EmotionEngine::lui(Opcode i)
{
regs[i.i_type.rt].u32[0] = (int32_t)((uint32_t)i.i_type.imm << 16);
printf("lui %s, 0x%08x\n", Reg(i.i_type.rt), regs[i.i_type.rt].u32[0]);
}
void EmotionEngine::mfc0(Opcode i)
{
regs[i.r_type.rt].u32[0] = cop0_regs[i.r_type.rd];
printf("mfc0 %s, r%d\n", Reg(i.r_type.rt), i.r_type.rd);
}
void EmotionEngine::mtc0(Opcode i)
{
cop0_regs[i.r_type.rd] = regs[i.r_type.rt].u32[0];
printf("mtc0 %s, r%d\n", Reg(i.r_type.rt), i.r_type.rd);
}
void EmotionEngine::sw(Opcode i)
{
int base = i.i_type.rs;
int rt = i.i_type.rt;
int16_t off = (int16_t)i.i_type.imm;
uint32_t addr = regs[base].u32[0] + off;
Write32(addr, regs[rt].u32[0]);
printf("sw %s, %d(%s)\n", Reg(rt), off, Reg(base));
}
void EmotionEngine::sd(Opcode i)
{
int base = i.i_type.rs;
int rt = i.i_type.rt;
int16_t off = (int16_t)i.i_type.imm;
uint32_t addr = regs[base].u32[0] + off;
Write64(addr, regs[rt].u32[0]);
printf("sd %s, %d(%s)\n", Reg(rt), off, Reg(base));
}
void EmotionEngine::jr(Opcode i)
{
next_pc = regs[i.r_type.rs].u32[0];
printf("jr %s\n", Reg(i.r_type.rs));
}
void EmotionEngine::sll(Opcode i)
{
int rd = i.r_type.rd;
int rt = i.r_type.rt;
int sa = i.r_type.sa;
regs[rd].u32[0] = regs[rt].u32[0] << sa;
printf("sll %s, %s, %d\n", Reg(rd), Reg(rt), sa);
}
void EmotionEngine::jalr(Opcode i)
{
int rs = i.r_type.rs;
int rd = i.r_type.rd;
regs[rd].u32[0] = next_pc;
next_pc = regs[rs].u32[0];
printf("jalr %s, %s\n", Reg(rs), Reg(rd));
}
void EmotionEngine::daddu(Opcode i)
{
int rd = i.r_type.rd;
int rs = i.r_type.rs;
int rt = i.r_type.rt;
int64_t reg1 = regs[rs].u64[0];
int64_t reg2 = regs[rt].u64[0];
regs[rd].u64[0] = reg1 + reg2;
printf("daddu %s, %s, %s\n", Reg(rd), Reg(rs), Reg(rt));
}

View file

@ -6,12 +6,6 @@ typedef union
uint32_t full;
struct
{
// uint32_t opcode : 6;
// uint32_t rs : 5;
// uint32_t rt : 5;
// uint32_t rd : 5;
// uint32_t sa : 5;
// uint32_t func : 6;
uint32_t func : 6;
uint32_t sa : 5;
uint32_t rd : 5;
@ -19,4 +13,16 @@ typedef union
uint32_t rs : 5;
uint32_t opcode : 6;
} r_type;
struct
{
uint32_t imm : 16;
uint32_t rt : 5;
uint32_t rs : 5;
uint32_t opcode : 6;
} i_type;
struct
{
uint32_t target : 26;
uint32_t opcode : 6;
} j_type;
} Opcode;

View file

@ -21,7 +21,7 @@ inline char* print_128(uint128_t s)
memset(ret, 0, 33);
sprintf(ret, "%lx%016lx", s.u64[0], s.u64[1]);
sprintf(ret, "%lx%016lx", s.u64[1], s.u64[0]);
return ret;
}