ultimecia

A ps1 emulator in c
Log | Files | Refs

commit 2b441df3713557728536ea2133a5960ab6684456
parent 57897fe670ecbe13e70032167987b8ba2bd65c3d
Author: Edea Kramer <edea@lunarcry.my.domain>
Date:   Thu, 21 Mar 2024 22:24:43 +0200

half of the opcodes (roughly)

Diffstat:
A.gitignore | 3+++
Abuild.sh | 1+
Dcompile | 1-
Dinclude/bios.h | 12------------
Dinclude/cpu.h | 64----------------------------------------------------------------
Dinclude/interconnect.h | 16----------------
Dinclude/mem.h | 14--------------
Dinclude/util.h | 21---------------------
Msrc/bios.c | 6++++--
Asrc/bios.h | 13+++++++++++++
Msrc/cpu.c | 369+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Asrc/cpu.h | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rinclude/defs.h -> src/defs.h | 0
Msrc/interconnect.c | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Asrc/interconnect.h | 19+++++++++++++++++++
Msrc/main.c | 12++++++------
Msrc/mem.c | 18+++++++++++++++---
Asrc/mem.h | 16++++++++++++++++
Rinclude/types.h -> src/types.h | 0
Msrc/util.c | 28++++++++++++++++++++++++++--
Asrc/util.h | 43+++++++++++++++++++++++++++++++++++++++++++
Mtest.c | 3+++
22 files changed, 749 insertions(+), 188 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +bin/ +a.out +test.c diff --git a/build.sh b/build.sh @@ -0,0 +1 @@ +tcc -static -std=c99 -O3 -Wall -pedantic -g src/*.c -o bin/ultimecia diff --git a/compile b/compile @@ -1 +0,0 @@ -cc -std=c89 -Wall -pedantic -g src/*.c diff --git a/include/bios.h b/include/bios.h @@ -1,12 +0,0 @@ -#pragma once - -#include "types.h" - -struct BIOS { - u8* data; -}; - -typedef struct BIOS BIOS; - -BIOS* BIOS_new(const char*); -u32 BIOS_load32(BIOS*, u32); diff --git a/include/cpu.h b/include/cpu.h @@ -1,64 +0,0 @@ -#pragma once - -#include "../include/interconnect.h" -#include "../include/types.h" - -#define IMM_JUMP(i) (i & 0x3ffffff) -#define IMM_SE(i) (((u32)((i16)(i & 0xffff)))); - -struct instruction { - u32 _0; - u32 fn; - u32 t; - u32 s; - u32 d; - u32 imm; - u32 imm_se; - u32 imm_jump; - u32 sub; - u32 shift; - u32 cop_opcode; -}; - -typedef struct instruction instruction; - -struct load { - u32 _0; - u32 _1; -}; - -typedef struct load load; - -struct CPU { - - /* Program Counter register */ - u32 pc; - - /* Cop0 register 12: Status Register*/ - u32 sr; - - instruction next_instruction; - - Interconnect* inter; - - /* General Registers */ - u32 regs[32]; - - /* Second set of regs to emulate the LOAD DELAY SLOT. - They contain the output of the current instruction */ - u32 out_regs[32]; - - /* LOAD -> on load delay slots */ - load load; - -}; - -typedef struct CPU CPU; - -CPU* new_cpu(Interconnect*); -u32 CPU_load32(CPU*, u32); -void CPU_decode_and_execute(CPU*, instruction*); -void CPU_run_next_instruction(CPU*); - -instruction new_instr(u32); -void print_instr(instruction*); diff --git a/include/interconnect.h b/include/interconnect.h @@ -1,16 +0,0 @@ -#pragma once - -#include "../include/bios.h" -#include "../include/mem.h" -#include "../include/types.h" - -struct Interconnect { - BIOS* BIOS; - RAM* RAM; -}; - -typedef struct Interconnect Interconnect; - -Interconnect* new_interconnect(void); -u32 INTER_load32(Interconnect*, u32); -void INTER_store32(Interconnect*, u32, u32); diff --git a/include/mem.h b/include/mem.h @@ -1,14 +0,0 @@ -#pragma once - -#include "util.h" -#include "types.h" - -struct RAM { - u8* data; -}; - -typedef struct RAM RAM; - -RAM* RAM_new(void); -u32 RAM_load32(RAM*, u32); -void RAM_store32(RAM* r, u32, u32); diff --git a/include/util.h b/include/util.h @@ -1,21 +0,0 @@ -#pragma once - -#include "types.h" - -#define BIOS_SIZE 512 * 1024 -#define BIOS_START 0xBFC00000 - -#define SYSCONTROL_SIZE 36 -#define SYSCONTROL_START 0x1F801000 - -#define RAM_SIZE_SIZE 4 -#define RAM_SIZE_START 0x1F801060 - -#define CACHECONTROL_SIZE 4 -#define CACHECONTROL_START 0xFFFE0130 - -#define RAM_SIZE 2 * 1024 * 1024 -#define RAM_START 0xA0000000 - - -u32 UTIL_contains(u32, u32, u32); diff --git a/src/bios.c b/src/bios.c @@ -1,7 +1,7 @@ #include <stdio.h> #include <stdlib.h> -#include "../include/bios.h" -#include "../include/types.h" +#include "bios.h" +#include "types.h" BIOS* BIOS_new(const char* path) @@ -45,3 +45,5 @@ BIOS_load32(BIOS* b, u32 offset) (b->data[offset + 1] << 8) | (b->data[offset + 0]); } + +u8 BIOS_load8(BIOS* b, u32 offset) { return b->data[offset]; } diff --git a/src/bios.h b/src/bios.h @@ -0,0 +1,13 @@ +#pragma once + +#include "types.h" + +struct BIOS { + u8* data; +}; + +typedef struct BIOS BIOS; + +BIOS* BIOS_new(const char*); +u32 BIOS_load32(BIOS*, u32); +u8 BIOS_load8(BIOS*, u32); diff --git a/src/cpu.c b/src/cpu.c @@ -1,10 +1,11 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "../include/interconnect.h" -#include "../include/cpu.h" -#include "../include/types.h" -#include "../include/defs.h" +#include "interconnect.h" +#include "cpu.h" +#include "types.h" +#include "defs.h" +#include "util.h" CPU* new_cpu(Interconnect* inter) { @@ -15,29 +16,29 @@ new_cpu(Interconnect* inter) { cpu->load._0 = 0; cpu->load._1 = 0; cpu->next_instruction = new_instr(0x0); /* NOP */ memset(cpu->regs, 0, sizeof(cpu->regs)); - //memset(cpu->out_regs, 0, sizeof(cpu->out_regs)); + memset(cpu->out_regs, 0, sizeof(cpu->out_regs)); return cpu; } u32 CPU_load32(CPU* cpu, u32 addr) { return INTER_load32(cpu->inter, addr); } -//u16 CPU_load16(CPU* cpu, u16 addr) { return INTER_load16(cpu->INTER, addr); } -//u8 CPU_load8(CPU* cpu, u8 addr) { return INTER_load8(cpu->INTER, addr); } -// +//u16 CPU_load16(CPU* cpu, u16 addr) { return INTER_load16(cpu->inter, addr); } +u8 CPU_load8(CPU* cpu, u32 addr) { return INTER_load8(cpu->inter, addr); } + void CPU_store32(CPU* cpu, u32 addr, u32 val) { INTER_store32(cpu->inter, addr, val); } -//u16 CPU_store16(CPU* cpu, u16 addr) { return INTER_store16(cpu->INTER, addr); } -//u8 CPU_store8(CPU* cpu, u8 addr) { return INTER_store8(cpu->INTER, addr); } +void CPU_store16(CPU* cpu, u32 addr, u16 val) { INTER_store16(cpu->inter, addr, val); } +void CPU_store8(CPU* cpu, u32 addr, u8 val) { INTER_store8(cpu->inter, addr, val); } void set_reg(CPU* cpu, u32 reg, u32 val) { - cpu->regs[reg] = val; - cpu->regs[0] = 0; + cpu->out_regs[reg] = val; + cpu->out_regs[0] = 0; } u32 reg(CPU* cpu, u32 reg) { - return cpu->regs[reg]; + return cpu->out_regs[reg]; } void @@ -53,7 +54,14 @@ op_or(CPU* cpu, instruction* i) u32 v; v = reg(cpu, i->s) | reg(cpu, i->t); set_reg(cpu, i->d, v); - //printf("d: %d = %d\n", i->d, v); +} + +void +op_and(CPU* cpu, instruction* i) +{ + u32 v; + v = reg(cpu, i->s) & reg(cpu, i->t); + set_reg(cpu, i->d, v); } void @@ -69,6 +77,12 @@ op_sw(CPU* cpu, instruction* i) { u32 imm_se, t, s, addr, v; + if ((cpu->sr & 0x10000) != 0) + { + printf("ignoring store while cache is isolated!\n"); + return; + } + imm_se = i->imm_se; t = i->t; s = i->s; @@ -79,6 +93,46 @@ op_sw(CPU* cpu, instruction* i) } void +op_sh(CPU* cpu, instruction* i) +{ + u32 imm_se, t, s, addr, v; + + if ((cpu->sr & 0x10000) != 0) + { + printf("ignoring store while cache is isolated!\n"); + return; + } + + imm_se = i->imm_se; + t = i->t; + s = i->s; + + addr = reg(cpu, s) + imm_se; + v = reg(cpu, t); + CPU_store16(cpu, addr, (u16)v); +} + +void +op_sb(CPU* cpu, instruction* i) +{ + u32 imm_se, t, s, addr, v; + + if ((cpu->sr & 0x10000) != 0) + { + printf("ignoring store while cache is isolated!\n"); + return; + } + + imm_se = i->imm_se; + t = i->t; + s = i->s; + + addr = reg(cpu, s) + imm_se; + v = reg(cpu, t); + CPU_store8(cpu, addr, (u8)v); +} + +void op_sll(CPU* cpu, instruction* i) { u32 shift, t, d, v; @@ -101,48 +155,309 @@ op_addiu(CPU* cpu, instruction* i) } void +op_addi(CPU* cpu, instruction* i) +{ + u32 v; + u32 imm_se; + i32 res; + + imm_se = (i32)i->imm_se; + v = (i32)reg(cpu, i->s); + + if(checked_addi32(v, imm_se, &res)) + set_reg(cpu, i->t, res); + else + fprintf(stderr, "ADDI OVERFLOW!"), exit(EXIT_FAILURE); +} + +void op_j(CPU* cpu, instruction* i) { cpu->pc = (cpu->pc & 0xf0000000) | (i->imm_jump << 2); } +void op_jal(CPU* cpu, instruction* i) { set_reg(cpu, 31, cpu->pc); op_j(cpu, i); } +void op_jr(CPU* cpu, instruction* i) { cpu->pc = reg(cpu, i->s); } + +void op_jalr(CPU* cpu, instruction* i) +{ + set_reg(cpu, i->d, cpu->pc); + cpu->pc = reg(cpu, i->s); +} + void op_mtc0(CPU* cpu, instruction* i) { - return; + u32 cop_r, v; + + cop_r = i->d; + v = reg(cpu, i->t); + + switch(cop_r) { + case 3: case 5: case 6: case 7: case 9: case 11: + if (v != 0) + { + fprintf(stderr, "Unhandled cop0r%d", cop_r); + exit(EXIT_FAILURE); + } + break; + case 12: + cpu->sr = v; + break; + case 13: + if (v != 0) + { + fprintf(stderr, "Unhandled write to CAUSE register\n"); + exit(EXIT_FAILURE); + } + break; + default: + fprintf(stderr, "Unhandled cop0 register %d\n", cop_r); + exit(EXIT_FAILURE); + } } +void +op_mfc0(CPU* cpu, instruction* i) +{ + u32 cop_r, v; + + cop_r = i->d; + v = reg(cpu, i->t); + + switch(cop_r) { + case 12: + cpu->load._0 = i->t; + cpu->load._1 = cpu->sr; + break; + case 13: + fprintf(stderr, "Unhandled read from CAUSE register\n"); + exit(EXIT_FAILURE); + break; + default: + fprintf(stderr, "Unhandled read from cop0r%d\n", cop_r); + exit(EXIT_FAILURE); + } +} void op_cop0(CPU* cpu, instruction* i) { switch (i->cop_opcode) { + case 0x00: op_mfc0(cpu, i); break; case 0x04: op_mtc0(cpu, i); break; - default: PANIC("Unhandled cop0 instruction %08X", i->_0); + default: printf("Unhandled cop0 instruction %08X", i->_0); exit(EXIT_FAILURE); + } +} + +void +branch(CPU* cpu, u32 offset) +{ + cpu->pc += (offset << 2) - 4; +} + +void +op_bne(CPU* cpu, instruction* i) +{ + if (reg(cpu, i->s) != reg(cpu, i->t)) branch(cpu, i->imm_se); +} + +void +op_beq(CPU* cpu, instruction* i) +{ + if (reg(cpu, i->s) == reg(cpu, i->t)) branch(cpu, i->imm_se); +} + +void +op_bgtz(CPU* cpu, instruction* i) +{ + u32 v; + + v = (i32)reg(cpu, i->s); + + if (v > 0) + branch(cpu, i->imm_se); +} + +void +op_blez(CPU* cpu, instruction* i) +{ + u32 v; + + v = (i32)reg(cpu, i->s); + + if (v <= 0) + branch(cpu, i->imm_se); +} + +void +op_bxx(CPU* cpu, instruction* i) +{ + u8 is_bgez, is_link, test; + u32 v; + + is_bgez = (i->_0 >> 16) & 1; + is_link = (i->_0 >> 20) & 1; + + v = (i32)reg(cpu, i->s); + + test = v < 0; + + test = test ^ is_bgez; + + if (test != 0) + { + if (is_link) + set_reg(cpu, 31, cpu->pc); + + branch(cpu, i->imm_se); } } void +op_lw(CPU* cpu, instruction* i) +{ + u32 v, addr; + + addr = reg(cpu, i->s) + i->imm_se; + v = CPU_load32(cpu, addr); + + cpu->load._0 = i->t; + cpu->load._1 = v; +} + +void +op_lb(CPU* cpu, instruction* i) +{ + u32 v, addr; + + addr = reg(cpu, i->s) + i->imm_se; + v = (i8)CPU_load8(cpu, addr); + + cpu->load._0 = i->t; + cpu->load._1 = (u32)v; +} + +void +op_lbu(CPU* cpu, instruction* i) +{ + u32 v, addr; + + addr = reg(cpu, i->s) + i->imm_se; + v = CPU_load8(cpu, addr); + + cpu->load._0 = i->t; + cpu->load._1 = (u32)v; +} + +void +op_sltu(CPU* cpu, instruction* i) +{ + u32 v; + + v = reg(cpu, i->s) < reg(cpu, i->t); + set_reg(cpu, i->d, (u32)v); +} + +void +op_slti(CPU* cpu, instruction* i) +{ + u32 v; + v = (i32)reg(cpu, i->s) < (i32)i->imm_se; + set_reg(cpu, i->t, (u32)v); +} + +void +op_add(CPU* cpu, instruction* i) +{ + u32 v, s, t, res; + + s = (i32)reg(cpu, i->s); + t = (i32)reg(cpu, i->t); + + if(checked_addi32(s, t, &res)) + set_reg(cpu, i->d, (u32)res); + else + fprintf(stderr, "ADD OVERFLOW!"), exit(EXIT_FAILURE); +} + +void +op_addu(CPU* cpu, instruction* i) +{ + u32 v; + + v = reg(cpu, i->t) + reg(cpu, i->s); + set_reg(cpu, i->d, v); + +} + +void +op_subu(CPU* cpu, instruction* i) +{ + u32 v; + + v = reg(cpu, i->t) - reg(cpu, i->s); + set_reg(cpu, i->d, v); + +} + +void +op_andi(CPU* cpu, instruction *i) +{ + u32 v; + v = reg(cpu, i->s) & i->imm; + set_reg(cpu, i->t, v); +} + +void +op_sra(CPU* cpu, instruction* i) +{ + i32 v; + v = (i32)reg(cpu, i->t) >> i->shift; + set_reg(cpu, i->d, (u32)v); +} + +void CPU_decode_and_execute(CPU* cpu, instruction* i) { switch(i->fn) { case 0x0: - { switch(i->sub) { case 0x0: op_sll(cpu, i); break; + case 0x03: op_sra(cpu, i); break; + case 0x08: op_jr(cpu, i); break; + case 0x09: op_jalr(cpu, i); break; + case 0x24: op_and(cpu, i); break; case 0x25: op_or(cpu, i); break; - default: PANIC("Unhandled instruction %08X\n", i->_0); break; + case 0x2B: op_sltu(cpu, i); break; + case 0x20: op_add(cpu, i); break; + case 0x21: op_addu(cpu, i); break; + case 0x23: op_subu(cpu, i); break; + default: printf("Unhandled instruction %08X\n", i->_0); exit(EXIT_FAILURE); } break; - } - case 0x0f: op_lui(cpu, i); break; - case 0x0d: op_ori(cpu, i); break; - case 0x2b: op_sw(cpu, i); break; - case 0x09: op_addiu(cpu, i); break; + case 0x01: op_bxx(cpu, i); break; case 0x02: op_j(cpu, i); break; + case 0x03: op_jal(cpu, i); break; + case 0x04: op_beq(cpu, i); break; + case 0x05: op_bne(cpu, i); break; + case 0x06: op_blez(cpu, i); break; + case 0x07: op_bgtz(cpu, i); break; + case 0x08: op_addi(cpu, i); break; + case 0x09: op_addiu(cpu, i); break; + case 0x0a: op_slti(cpu, i); break; + case 0x0c: op_andi(cpu, i); break; + case 0x0d: op_ori(cpu, i); break; + case 0x0f: op_lui(cpu, i); break; case 0x10: op_cop0(cpu, i); break; - default: PANIC("Unhandled instruction %08X\n", i->_0);break; + case 0x20: op_lb(cpu, i); break; + case 0x23: op_lw(cpu, i); break; + case 0x24: op_lbu(cpu, i); break; + case 0x2b: op_sw(cpu, i); break; + case 0x28: op_sb(cpu, i); break; + case 0x29: op_sh(cpu, i); break; + default: printf("Unhandled instruction %08X\n", i->_0); exit(EXIT_FAILURE); } } @@ -159,9 +474,13 @@ CPU_run_next_instruction(CPU* cpu) cpu->pc += 4; + set_reg(cpu, cpu->load._0, cpu->load._1); + + cpu->load._0 = 0; cpu->load._1 = 0; + CPU_decode_and_execute(cpu, &i); - //memcpy(cpu->regs, cpu->out_regs, sizeof(u32) * 32); + memcpy(cpu->regs, cpu->out_regs, sizeof(u32) * 32); } instruction diff --git a/src/cpu.h b/src/cpu.h @@ -0,0 +1,61 @@ +#pragma once + +#include "interconnect.h" +#include "types.h" + +struct instruction { + u32 _0; + u32 fn; + u32 t; + u32 s; + u32 d; + u32 imm; + u32 imm_se; + u32 imm_jump; + u32 sub; + u32 shift; + u32 cop_opcode; +}; + +typedef struct instruction instruction; + +struct load { + u32 _0; + u32 _1; +}; + +typedef struct load load; + +struct CPU { + + /* Program Counter register */ + u32 pc; + + /* Cop0 register 12: Status Register*/ + u32 sr; + + instruction next_instruction; + + Interconnect* inter; + + /* General Registers */ + u32 regs[32]; + + /* Second set of regs to emulate the LOAD DELAY SLOT. + They contain the output of the current instruction */ + u32 out_regs[32]; + + /* LOAD -> on load delay slots */ + load load; + +}; + +typedef struct CPU CPU; + +CPU* new_cpu(Interconnect*); +u32 CPU_load32(CPU*, u32); +void CPU_decode_and_execute(CPU*, instruction*); +void CPU_run_next_instruction(CPU*); + +instruction new_instr(u32); +void print_instr(instruction*); diff --git a/include/defs.h b/src/defs.h diff --git a/src/interconnect.c b/src/interconnect.c @@ -1,12 +1,12 @@ #include <stdio.h> #include <stdlib.h> -#include "../include/interconnect.h" -#include "../include/bios.h" -#include "../include/mem.h" -#include "../include/util.h" -#include "../include/types.h" -#include "../include/defs.h" +#include "interconnect.h" +#include "bios.h" +#include "mem.h" +#include "util.h" +#include "types.h" +#include "defs.h" Interconnect* new_interconnect(void) { @@ -16,67 +16,240 @@ new_interconnect(void) { return inter; } +u8 +INTER_load8(Interconnect* inter, u32 addr) +{ + u32 offset; + u32 abs_addr; + + offset = 0; + abs_addr = mask_region(addr); + + /* Assert abs_address Mappings */ + offset = UTIL_contains(BIOS_START, BIOS_SIZE, abs_addr); + if (offset != -1) + return BIOS_load8(inter->BIOS, offset); + + offset = UTIL_contains(EXPANSION1_START, EXPANSION1_SIZE, abs_addr); + if (offset != -1) + return 0xff; + + offset = UTIL_contains(RAM_START, RAM_SIZE, abs_addr); + if (offset != -1) + return RAM_load8(inter->RAM, offset); + + PANIC("Unhandled Load8 At Address %08X\n", abs_addr); +} + u32 INTER_load32(Interconnect* inter, u32 addr) { - u32 offset = 0; + u32 offset; + u32 abs_addr; - if (addr % 4 != 0) fprintf(stderr, "Unaligned_load32_address: %08X", addr); + offset = 0; + abs_addr = mask_region(addr); + + if (addr % 4 != 0) + { + fprintf(stderr, "Unaligned_load32_abs_address: %08X\n", abs_addr); + exit(EXIT_FAILURE); + } - /* Assert address Mappings */ - offset = UTIL_contains(BIOS_START, BIOS_SIZE, addr); + /* Assert abs_address Mappings */ + offset = UTIL_contains(BIOS_START, BIOS_SIZE, abs_addr); if (offset != -1) return BIOS_load32(inter->BIOS, offset); - offset = UTIL_contains(RAM_START, RAM_SIZE, addr); + offset = UTIL_contains(RAM_START, RAM_SIZE, abs_addr); if (offset != -1) return RAM_load32(inter->RAM, offset); - PANIC("Unhandled Load32 At Address %08X\n", addr); + PANIC("Unhandled Load32 At Address %08X\n", abs_addr); +} + +void +INTER_store8(Interconnect* inter, u32 addr, u8 val) +{ + u32 offset = 0; + u32 abs_addr; + + abs_addr = mask_region(addr); + + offset = UTIL_contains(EXPANSION2_START, EXPANSION2_SIZE, abs_addr); + if (offset != -1) + { + fprintf(stdout, "Ignoring write to EXPANSION2 register at: %08X\n", abs_addr); + return; + } + + offset = UTIL_contains(RAM_START, RAM_SIZE, abs_addr); + if (offset != -1) + { + return RAM_store8(inter->RAM, offset, val); + } + + //offset = UTIL_contains(RAM_SIZE_START, RAM_SIZE_SIZE, abs_addr); + //if (offset != -1) + //{ + // fprintf(stdout, "Ignoring RAM_SIZE register write %X\n", abs_addr); + // return; + //} + + //offset = UTIL_contains(CACHECONTROL_START, CACHECONTROL_SIZE, abs_addr); + //if (offset != -1) + //{ + // fprintf(stdout, "Ignoring CACHECONTROL abs_address write: %X\n", abs_addr); + // return; + //} + + //offset = UTIL_contains(SYSCONTROL_START, SYSCONTROL_SIZE, abs_addr); + //if (offset != -1) + //{ + // switch(offset) { + // case 0: + // if (val != 0x1F000000) { + // fprintf(stderr, "Bad Expansion 1 base abs_address: %08X", val); + // exit(EXIT_FAILURE); + // } + // break; + // case 4: + // if (val != 0x1F802000) { + // fprintf(stderr, "Bad expansion 2 base abs_address: %08X", val); + // exit(EXIT_FAILURE); + // } + // break; + // default: + // fprintf(stderr, "Unhandled write to SYSCONTROL register\n"); + // return; + // } + // return; + //} + + PANIC("Unhandled store8 into abs_address: %08X\n", abs_addr); +} + +void +INTER_store16(Interconnect* inter, u32 addr, u16 val) +{ + u32 offset = 0; + u32 abs_addr; + + abs_addr = mask_region(addr); + + if (addr % 2 != 0) + { + fprintf(stderr, "Unaligned_store16_address: %08X", addr); + exit(EXIT_FAILURE); + + } + + offset = UTIL_contains(SPU_START, SPU_SIZE, abs_addr); + if (offset != -1) + { + fprintf(stdout, "Ignoring SPU register write; %X\n", abs_addr); + return; + } + + //offset = UTIL_contains(RAM_START, RAM_SIZE, abs_addr); + //if (offset != -1) + //{ + // fprintf(stdout, "Ignoring RAM write; %X\n", abs_addr); + // return; + //} + + //offset = UTIL_contains(RAM_SIZE_START, RAM_SIZE_SIZE, abs_addr); + //if (offset != -1) + //{ + // fprintf(stdout, "Ignoring RAM_SIZE register write %X\n", abs_addr); + // return; + //} + + //offset = UTIL_contains(CACHECONTROL_START, CACHECONTROL_SIZE, abs_addr); + //if (offset != -1) + //{ + // fprintf(stdout, "Ignoring CACHECONTROL abs_address write: %X\n", abs_addr); + // return; + //} + + //offset = UTIL_contains(SYSCONTROL_START, SYSCONTROL_SIZE, abs_addr); + //if (offset != -1) + //{ + // switch(offset) { + // case 0: + // if (val != 0x1F000000) { + // fprintf(stderr, "Bad Expansion 1 base abs_address: %08X", val); + // exit(EXIT_FAILURE); + // } + // break; + // case 4: + // if (val != 0x1F802000) { + // fprintf(stderr, "Bad expansion 2 base abs_address: %08X", val); + // exit(EXIT_FAILURE); + // } + // break; + // default: + // fprintf(stderr, "Unhandled write to SYSCONTROL register\n"); + // return; + // } + // return; + //} + + PANIC("Unhandled store16 into abs_address: %08X\n", abs_addr); } void INTER_store32(Interconnect* inter, u32 addr, u32 val) { u32 offset = 0; + u32 abs_addr; + + abs_addr = mask_region(addr); if (addr % 4 != 0) fprintf(stderr, "Unaligned_store32_address: %08X", addr); - offset = UTIL_contains(RAM_START, RAM_SIZE, addr); + offset = UTIL_contains(RAM_START, RAM_SIZE, abs_addr); if (offset != -1) { - fprintf(stdout, "Ignoring RAM write; %X\n", addr); + RAM_store32(inter->RAM, offset, val); return; } - offset = UTIL_contains(RAM_SIZE_START, RAM_SIZE_SIZE, addr); + offset = UTIL_contains(RAM_SIZE_START, RAM_SIZE_SIZE, abs_addr); + if (offset != -1) + { + fprintf(stdout, "Ignoring RAM_SIZE register write %X\n", abs_addr); + return; + } + + offset = UTIL_contains(CACHECONTROL_START, CACHECONTROL_SIZE, abs_addr); if (offset != -1) { - fprintf(stdout, "Ignoring RAM_SIZE register write %X\n", addr); + fprintf(stdout, "Ignoring CACHECONTROL abs_address write: %X\n", abs_addr); return; } - offset = UTIL_contains(CACHECONTROL_START, CACHECONTROL_SIZE, addr); + offset = UTIL_contains(IRQ_CONTROL_START, IRQ_CONTROL_SIZE, abs_addr); if (offset != -1) { - fprintf(stdout, "Ignoring CACHECONTROL address write: %X\n", addr); + fprintf(stdout, "Ignoring IRQ CONTROL write %08X to address %08X\n", val, offset); return; } - offset = UTIL_contains(SYSCONTROL_START, SYSCONTROL_SIZE, addr); + offset = UTIL_contains(SYSCONTROL_START, SYSCONTROL_SIZE, abs_addr); if (offset != -1) { switch(offset) { case 0: if (val != 0x1F000000) { - fprintf(stderr, "Bad Expansion 1 base address: %08X", val); + fprintf(stderr, "Bad Expansion 1 base abs_address: %08X", val); exit(EXIT_FAILURE); } break; case 4: if (val != 0x1F802000) { - fprintf(stderr, "Bad expansion 2 base address: %08X", val); + fprintf(stderr, "Bad expansion 2 base abs_address: %08X", val); exit(EXIT_FAILURE); } break; @@ -87,5 +260,5 @@ INTER_store32(Interconnect* inter, u32 addr, u32 val) return; } - PANIC("Unhandled store 32 into address: %08X\n", addr); + PANIC("Unhandled store 32 into abs_address: %08X\n", abs_addr); } diff --git a/src/interconnect.h b/src/interconnect.h @@ -0,0 +1,19 @@ +#pragma once + +#include "bios.h" +#include "mem.h" +#include "types.h" + +struct Interconnect { + BIOS* BIOS; + RAM* RAM; +}; + +typedef struct Interconnect Interconnect; + +Interconnect* new_interconnect(void); +u32 INTER_load32(Interconnect*, u32); +u8 INTER_load8(Interconnect*, u32); +void INTER_store32(Interconnect*, u32, u32); +void INTER_store16(Interconnect*, u32, u16); +void INTER_store8(Interconnect*, u32, u8); diff --git a/src/main.c b/src/main.c @@ -2,12 +2,12 @@ #include <string.h> #include <stdlib.h> -#include "../include/types.h" -#include "../include/cpu.h" -#include "../include/interconnect.h" -#include "../include/bios.h" -#include "../include/mem.h" -#include "../include/defs.h" +#include "types.h" +#include "cpu.h" +#include "interconnect.h" +#include "bios.h" +#include "mem.h" +#include "defs.h" int main(int argc, char **argv) diff --git a/src/mem.c b/src/mem.c @@ -1,8 +1,8 @@ #include <stdlib.h> #include <string.h> -#include "../include/mem.h" -#include "../include/types.h" -#include "../include/util.h" +#include "mem.h" +#include "types.h" +#include "util.h" RAM* RAM_new(void) @@ -23,6 +23,18 @@ RAM_load32(RAM* r, u32 offset) (r->data[offset + 0]); } +u8 +RAM_load8(RAM* r, u32 offset) +{ + return r->data[offset]; +} + +void +RAM_store8(RAM* r, u32 offset, u8 val) +{ + r->data[offset]=val; +} + void RAM_store32(RAM* r, u32 offset, u32 val) { diff --git a/src/mem.h b/src/mem.h @@ -0,0 +1,16 @@ +#pragma once + +#include "util.h" +#include "types.h" + +struct RAM { + u8* data; +}; + +typedef struct RAM RAM; + +RAM* RAM_new(void); +u32 RAM_load32(RAM*, u32); +u8 RAM_load8(RAM*, u32); +void RAM_store32(RAM* r, u32, u32); +void RAM_store8(RAM* r, u32, u8); diff --git a/include/types.h b/src/types.h diff --git a/src/util.c b/src/util.c @@ -1,8 +1,32 @@ -#include "../include/util.h" -#include "../include/types.h" +#include "util.h" +#include "types.h" +#include <limits.h> + +const u32 REGION_MASK[8] = { + // KUSEG: 2048MB + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + // KSEG0: 512MB // + 0x7fffffff, + // KSEG1: 512MB // + 0x1fffffff, + // KSEG2: 1024MB // + 0xffffffff, 0xffffffff +}; u32 UTIL_contains(u32 start, u32 length, u32 addr) { return (addr >= start && addr < start + length) ? addr-start : -1; } + +u8 +checked_addi32(i32 a, i32 b, i32* res) +{ + if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)) { + return 0; + } + *res = a + b; + return 1; +} + +u32 mask_region(u32 addr) { return addr & REGION_MASK[addr >> 29]; } diff --git a/src/util.h b/src/util.h @@ -0,0 +1,43 @@ +#pragma once + +#include "types.h" + +#define BIOS_SIZE 512 * 1024 +#define BIOS_START 0x1FC00000 + +#define SYSCONTROL_SIZE 36 +#define SYSCONTROL_START 0x1F801000 + +#define RAM_SIZE_SIZE 4 +#define RAM_SIZE_START 0x1F801060 + +#define CACHECONTROL_SIZE 4 +#define CACHECONTROL_START 0xFFFE0130 + +#define RAM_SIZE 2 * 1024 * 1024 +#define RAM_START 0x00000000 + +#define SPU_SIZE 640 +#define SPU_START 0x1F801C00 + +#define EXPANSION2_SIZE 66 +#define EXPANSION2_START 0x1F802000 + +#define EXPANSION1_START 0x1F000000 +#define EXPANSION1_SIZE 512*1024 + +#define IRQ_CONTROL_START 0x1F801070 +#define IRQ_CONTROL_SIZE 8 + +#define TIMERS_START 0x1F801100 +#define TIMERS_SIZE 0x30 + +#define DMA_START 0x1F801080 +#define DMA_SIZE 0x80 + +#define GPU_START 0x1F801810 +#define GPU_SIZE 8 + +u32 UTIL_contains(u32, u32, u32); +u8 checked_addi32(i32, i32, i32*); +u32 mask_region(u32); diff --git a/test.c b/test.c @@ -20,5 +20,8 @@ main() printf("%d", foo1.pc); bar(&foo1); printf("%d", foo1.pc); + + (1) ? printf("hello there"), printf("what") : 0; + return 0; }