Last active
September 3, 2023 03:10
-
-
Save louisswarren/9c171da1309b6d7c2e578d22ed4ee744 to your computer and use it in GitHub Desktop.
PSX emulation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Usage: | |
X(opnum, type, opname, opdesc, opfmt, code...) | |
*/ | |
#define FMT_ad " %1$06x" | |
#define FMT_rs " $%1$02d" | |
#define FMT_rt " $%2$02d" | |
#define FMT_rd " $%3$02d" | |
#define FMT_im " %3$04x" | |
#define FMT_ims " %4$4d" | |
#define FMT_sh " %4$2d" | |
#define FMT_cd " CP0$%3$02d" | |
#define FMT_im_rs " %4$4d($%1$02d)" | |
#define RESET_BUS(r) do {\ | |
slot->reg = (uint8_t)(slot->reg * (slot->reg != rt - cpu->gr));\ | |
} while (0) | |
#define INSTRUCTIONS_X \ | |
X( 2, JMP, "J", "Jump", FMT_ad, \ | |
pre_branch_pause("Jumping ..."); \ | |
next = (pc & 0xf0000000) | (ad << 2); \ | |
) \ | |
X( 3, JMP, "JAL", "Jump And Link", FMT_ad, \ | |
pre_branch_pause("Jumping ..."); \ | |
cpu->gr[31] = next; \ | |
next = (pc & 0xf0000000) | (ad << 2); \ | |
) \ | |
X( 4, IMM, "BEQ", "Branch On Equal", FMT_rs FMT_rt FMT_ims, \ | |
pre_branch_pause("Jumping if %u == %u ...", *rs, *rt); \ | |
if (*rs == *rt) next = pc + 4 * (int16_t)im; \ | |
) \ | |
X( 5, IMM, "BNE", "Branch On Not Equal", FMT_rs FMT_rt FMT_ims, \ | |
pre_branch_pause("Jumping if %u != %u ...", *rs, *rt); \ | |
if (*rs != *rt) next = pc + 4 * (int16_t)im; \ | |
) \ | |
X( 6, IMM, "BLEZ", "Branch On <= 0", FMT_rs FMT_ims, \ | |
pre_branch_pause("Jumping if %u <= 0...", *rs); \ | |
if ((int32_t)*rs <= 0) next = pc + 4 * (int16_t)im; \ | |
) \ | |
X( 7, IMM, "BGTZ", "Branch On > 0", FMT_rs FMT_ims, \ | |
pre_branch_pause("Jumping if %u > 0...", *rs); \ | |
if ((int32_t)*rs > 0) next = pc + 4 * (int16_t)im; \ | |
) \ | |
X( 8, IMM, "ADDI", "Add Immediate", FMT_rt FMT_rs FMT_ims, \ | |
if (((int16_t)im > 0 && *rs + (int16_t)im < *rs) || \ | |
((int16_t)im < 0 && *rs + (int16_t)im > *rs)) { \ | |
die("Trapped overflow"); \ | |
} \ | |
*rt = *rs + (int16_t)im; \ | |
RESET_BUS(rt); \ | |
) \ | |
X( 9, IMM, "ADDIU", "Add Immediate Unsigned", FMT_rt FMT_rs FMT_ims, \ | |
*rt = *rs + (int16_t)im; \ | |
RESET_BUS(rt); \ | |
) \ | |
X( 12, IMM, "ANDI", "And Immediate", FMT_rt FMT_rs FMT_im, \ | |
*rt = *rs & im; \ | |
RESET_BUS(rt); \ | |
) \ | |
X( 13, IMM, "ORI", "Or Immediate", FMT_rt FMT_rs FMT_im, \ | |
*rt = *rs | im; \ | |
RESET_BUS(rt); \ | |
) \ | |
X( 15, IMM, "LUI", "Load Upper Immediate", FMT_rt FMT_im, \ | |
*rt = (uint32_t)im << 16; \ | |
RESET_BUS(rt); \ | |
) \ | |
X( 32, IMM, "LB", "Load Byte", FMT_rt FMT_im_rs, \ | |
cpu->load_slot[!cpu->flipflop].reg = (uint8_t)(rt - cpu->gr); \ | |
cpu->load_slot[!cpu->flipflop].val = \ | |
(int8_t)load8(cpu, *rs + (int16_t)im); \ | |
) \ | |
X( 35, IMM, "LW", "Load Word", FMT_rt FMT_im_rs, \ | |
cpu->load_slot[!cpu->flipflop].reg = (uint8_t)(rt - cpu->gr); \ | |
cpu->load_slot[!cpu->flipflop].val = load32(cpu, *rs + (int16_t)im); \ | |
) \ | |
X( 36, IMM, "LBU", "Load Byte Unsigned", FMT_rt FMT_im_rs, \ | |
cpu->load_slot[!cpu->flipflop].reg = (uint8_t)(rt - cpu->gr); \ | |
cpu->load_slot[!cpu->flipflop].val = load8(cpu, *rs + (int16_t)im); \ | |
) \ | |
X( 40, IMM, "SB", "Store Byte", FMT_rt FMT_im_rs, \ | |
if (!FLAG_ISOLATE_CACHE(cpu)) \ | |
store8(cpu, *rs + (int16_t)im, (uint8_t)*rt); \ | |
else debug(" (cache)"); \ | |
) \ | |
X( 41, IMM, "SH", "Store Halfword", FMT_rt FMT_im_rs, \ | |
if (!FLAG_ISOLATE_CACHE(cpu)) \ | |
store16(cpu, *rs + (int16_t)im, (uint16_t)*rt); \ | |
else debug(" (cache)"); \ | |
) \ | |
X( 43, IMM, "SW", "Store Word", FMT_rt FMT_im_rs, \ | |
if (!FLAG_ISOLATE_CACHE(cpu)) \ | |
store32(cpu, *rs + (int16_t)im, *rt); \ | |
else debug(" (cache)"); \ | |
) \ | |
/* Register instructions */ \ | |
X( 64| 0, REG, "SLL", "Shift Left Logical", FMT_rd FMT_rt FMT_sh, \ | |
*rd = *rt << sh; \ | |
RESET_BUS(rd); \ | |
) \ | |
X( 64| 8, REG, "JR", "Jump Register", FMT_rs, \ | |
next = *rs; \ | |
) \ | |
X( 64| 9, REG, "JALR", "Jump And Link Register", FMT_rd FMT_rs, \ | |
*rd = next; \ | |
next = *rs; \ | |
) \ | |
X( 64|32, REG, "ADD", "Add", FMT_rd FMT_rs FMT_rt, \ | |
if (*rs + *rt < *rs) { \ | |
die("Trapped overflow"); \ | |
} \ | |
*rd = *rs + *rt; \ | |
RESET_BUS(rd); \ | |
) \ | |
X( 64|33, REG, "ADDU", "Add unsigned", FMT_rd FMT_rs FMT_rt, \ | |
*rd = *rs + *rt; \ | |
RESET_BUS(rd); \ | |
) \ | |
X( 64|36, REG, "AND", "And", FMT_rd FMT_rs FMT_rt, \ | |
*rd = *rs & *rt; \ | |
RESET_BUS(rd); \ | |
) \ | |
X( 64|37, REG, "OR", "Or", FMT_rd FMT_rs FMT_rt, \ | |
*rd = *rs | *rt; \ | |
RESET_BUS(rd); \ | |
) \ | |
X( 64|43, REG, "SLTU", "Set Less Than Unsigned", FMT_rd FMT_rs FMT_rt, \ | |
*rd = *rs < *rt; \ | |
RESET_BUS(rd); \ | |
) \ | |
/* Co-processor 0 instructions */ \ | |
X(128| 0, CP0, "MFC0", "Move from COP0", FMT_rt FMT_cd, \ | |
cpu->load_slot[!cpu->flipflop].reg = (uint8_t)(rt - cpu->gr); \ | |
cpu->load_slot[!cpu->flipflop].val = *cd; \ | |
) \ | |
X(128| 4, CP0, "MTC0", "Move to COP0", FMT_rt FMT_cd, \ | |
*cd = *rt; \ | |
) \ | |
//#undef Fad | |
//#undef Frs | |
//#undef Frt | |
//#undef Frd | |
//#undef Fim | |
//#undef Fsh | |
// | |
//#undef Fim_Frs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CFLAGS = -Wall -Warray-bounds=2 -Wcast-align=strict -Wcast-qual -Wconversion -Wno-sign-conversion -Wdangling-else -Wdate-time -Wdouble-promotion -Wextra -Wfloat-conversion -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation=2 -Wformat=2 -Winit-self -Wjump-misses-init -Wlogical-op -Wmissing-include-dirs -Wnested-externs -Wnull-dereference -Wpacked -Wpedantic -Wredundant-decls -Wshadow -Wshift-negative-value -Wshift-overflow=2 -Wstrict-aliasing -Wstrict-overflow=2 -Wstrict-prototypes -Wstringop-overflow=4 -Wstringop-truncation -Wswitch-default -Wswitch-enum -Wuninitialized -Wunsafe-loop-optimizations -Wunused -Wuse-after-free=3 -Wwrite-strings -fanalyzer -fmax-errors=2 -pedantic-errors | |
.PHONY: default | |
default: test | |
.PHONY: test | |
test: xods | |
./$< < /dev/null | diff expected.log - | head -n40 | |
xods.o: xods.c instructions.h | |
expected.log: xods | |
./$< > $@ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <inttypes.h> | |
#include <assert.h> | |
#include "instructions.h" | |
#define debug(...) do { \ | |
printf(__VA_ARGS__); \ | |
fflush(stdout); \ | |
} while (0) | |
#define error(...) do { \ | |
fprintf(stderr, __VA_ARGS__); \ | |
fflush(stderr); \ | |
} while (0) | |
#define die(...) do { \ | |
fprintf(stderr, __VA_ARGS__); \ | |
fprintf(stderr, "\n"); \ | |
exit(1); \ | |
} while (0) | |
// Break glass in case of infinite loops | |
#if 0 | |
#define pre_branch_pause(...) do { \ | |
printf("\n" __VA_ARGS__); \ | |
getchar(); \ | |
} while (0) | |
#else | |
#define pre_branch_pause(...) (void)0 | |
#endif | |
static uint32_t breakpoints[] = { | |
0, | |
0xbfc06ecc, | |
}; | |
#define MEMMAP_RES 12 | |
uint32_t ADDR_BIOS = 0xbfc00000; | |
struct load_slot { | |
uint8_t reg; | |
uint32_t val; | |
}; | |
struct cpu { | |
uint8_t flipflop; | |
struct load_slot load_slot[2]; | |
uint32_t pc; | |
uint32_t next_instr; | |
uint32_t next_instr_addr; // Debug | |
uint32_t hi; | |
uint32_t lo; | |
uint32_t gr[32]; | |
uint32_t c0[32]; | |
uint8_t ram[2048 << 10]; | |
uint8_t exp[8192 << 10]; | |
uint8_t scr[ 1 << 10]; | |
uint8_t reg[ 8 << 10]; | |
uint8_t bio[ 512 << 10]; | |
uint8_t pio[ 512]; | |
uint8_t *memmap[1 << (32 - MEMMAP_RES)]; | |
}; | |
#define FLAG_ISOLATE_CACHE(c) ((c)->c0[12] & 0x00010000) | |
void | |
dump_reg(const struct cpu *cpu) | |
{ | |
for (int i = 0; i < 8; ++i) { | |
fprintf(stdout, | |
"$%02d=%08x\t" | |
"$%02d=%08x\t" | |
"$%02d=%08x\t" | |
"$%02d=%08x\n", | |
0 + i, cpu->gr[ 0 + i], | |
8 + i, cpu->gr[ 8 + i], | |
16 + i, cpu->gr[16 + i], | |
24 + i, cpu->gr[24 + i]); | |
} | |
} | |
enum memory_segment { | |
KUSEG = 0, | |
KSEG0 = 4, | |
KSEG1 = 5, // Not cached | |
KSEG2 = 7, | |
}; | |
uint8_t * | |
addr(const struct cpu *cpu, uint32_t n) | |
{ | |
// enum memory_segment seg = n >> 29; | |
n &= 0x1fffffff; | |
uint8_t *page = cpu->memmap[n >> MEMMAP_RES]; | |
/* Can't access unmapped memory */ | |
assert(page); | |
return &page[n & ((1 << MEMMAP_RES) - 1)]; | |
} | |
uint8_t | |
load8(const struct cpu *cpu, uint32_t ad) | |
{ | |
const uint8_t *p = addr(cpu, ad); | |
return p[0]; | |
} | |
uint16_t | |
load16(const struct cpu *cpu, uint32_t ad) | |
{ | |
/* Can't access unaligned memory */ | |
assert(!(ad & 1)); | |
const uint8_t *p = addr(cpu, ad); | |
return p[0] | p[1] << 8; | |
} | |
uint32_t | |
load32(const struct cpu *cpu, uint32_t ad) | |
{ | |
/* Can't access unaligned memory */ | |
assert(!(ad & 3)); | |
const uint8_t *p = addr(cpu, ad); | |
return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; | |
} | |
void | |
store8(struct cpu *cpu, uint32_t ad, uint8_t x) | |
{ | |
/* Expansion 1 & 2 addresses. | |
* It's never correct to set these to anything else. | |
*/ | |
assert(ad != 0x1f801000 || x == 0x00); | |
assert(ad != 0x1f801004 || x == 0x00); | |
uint8_t *p = addr(cpu, ad); | |
p[0] = (uint8_t)(x >> 0) & 0xff; | |
} | |
void | |
store16(struct cpu *cpu, uint32_t ad, uint16_t x) | |
{ | |
/* Expansion 1 & 2 addresses. | |
* It's never correct to set these to anything else. | |
*/ | |
assert(ad != 0x1f801000 || x == 0x0000); | |
assert(ad != 0x1f801004 || x == 0x2000); | |
/* Can't access unaligned memory */ | |
assert(!(ad & 1)); | |
uint8_t *p = addr(cpu, ad); | |
p[0] = (uint8_t)(x >> 0) & 0xff; | |
p[1] = (uint8_t)(x >> 8) & 0xff; | |
} | |
void | |
store32(struct cpu *cpu, uint32_t ad, uint32_t x) | |
{ | |
/* Expansion 1 & 2 addresses. | |
* It's never correct to set these to anything else. | |
*/ | |
assert(ad != 0x1f801000 || x == 0x1f000000); | |
assert(ad != 0x1f801004 || x == 0x1f802000); | |
/* Can't access unaligned memory */ | |
assert(!(ad & 3)); | |
uint8_t *p = addr(cpu, ad); | |
p[0] = (uint8_t)(x >> 0) & 0xff; | |
p[1] = (uint8_t)(x >> 8) & 0xff; | |
p[2] = (uint8_t)(x >> 16) & 0xff; | |
p[3] = (uint8_t)(x >> 24) & 0xff; | |
} | |
void | |
reset(struct cpu *cpu) | |
{ | |
cpu->flipflop = 0; | |
cpu->load_slot[0].reg = 0; | |
cpu->load_slot[0].val = 0; | |
cpu->load_slot[1].reg = 0; | |
cpu->load_slot[1].val = 0; | |
cpu->pc = ADDR_BIOS; | |
cpu->gr[0] = 0; | |
// Not sure if this is needed | |
cpu->gr[31] = cpu->pc; | |
// Set some canary values | |
for (size_t i = 1; i < 31; ++i) | |
cpu->gr[i] = 0x89ABCDEF; | |
for (size_t i = 0; i < 32; ++i) | |
cpu->c0[i] = 0x89ABCDEF; | |
// Set the status register to 0 | |
cpu->c0[12] = 0; | |
cpu->hi = 0x89ABCDEF; | |
cpu->lo = 0x89ABCDEF; | |
memset(cpu->ram, 0x7f, sizeof(cpu->ram)); | |
memset(cpu->exp, 0xff, sizeof(cpu->exp)); | |
memset(cpu->scr, 0x7f, sizeof(cpu->scr)); | |
memset(cpu->reg, 0x7f, sizeof(cpu->reg)); | |
memset(cpu->bio, 0x7f, sizeof(cpu->bio)); | |
memset(cpu->pio, 0x7f, sizeof(cpu->pio)); | |
FILE *f = fopen("scph1001.bin", "r"); | |
if (!f) | |
die("Failed to open BIOS"); | |
if (!fread(cpu->bio, sizeof(cpu->bio), 1, f)) | |
die("Failed to read BIOS"); | |
if (fclose(f)) | |
die("Failed to close BIOS"); | |
/* Preload first instruction */ | |
cpu->next_instr_addr = cpu->pc; | |
cpu->next_instr = load32(cpu, cpu->pc); | |
cpu->pc += 4; | |
} | |
void | |
setup_memmap(struct cpu *cpu) | |
{ | |
memset(cpu->memmap, 0, sizeof(cpu->memmap)); | |
#define create_memmap(start, mem) \ | |
for (size_t i = 0; i < sizeof(mem); i += 1 << MEMMAP_RES) \ | |
cpu->memmap[((start) + i) >> MEMMAP_RES] = mem + i | |
create_memmap(0x00000000, cpu->ram); | |
create_memmap(0x1f000000, cpu->exp); | |
create_memmap(0x1f800000, cpu->scr); | |
create_memmap(0x1f801000, cpu->reg); | |
create_memmap(0x1fc00000, cpu->bio); | |
create_memmap(0xfffe0000 & 0x1fffffff, cpu->pio); | |
#undef create_memmap | |
assert(&cpu->ram[0] == addr(cpu, 0x00000000)); | |
assert(&cpu->ram[0] == addr(cpu, 0x80000000)); | |
assert(&cpu->ram[0] == addr(cpu, 0xa0000000)); | |
assert(&cpu->ram[4] == addr(cpu, 0x00000004)); | |
assert(&cpu->ram[4] == addr(cpu, 0x80000004)); | |
assert(&cpu->ram[4] == addr(cpu, 0xa0000004)); | |
assert(&cpu->bio[4] == addr(cpu, 0x1fc00004)); | |
assert(&cpu->bio[4] == addr(cpu, 0x9fc00004)); | |
assert(&cpu->bio[4] == addr(cpu, 0xbfc00004)); | |
} | |
#pragma GCC diagnostic push | |
#pragma GCC diagnostic ignored "-Wunused-variable" | |
#pragma GCC diagnostic ignored "-Wunused-but-set-variable" | |
#pragma GCC diagnostic ignored "-Wformat" | |
#pragma GCC diagnostic ignored "-Wformat-extra-args" | |
void | |
do_cycle(struct cpu *cpu) | |
{ | |
uint32_t instr = cpu->next_instr; | |
uint32_t instr_addr = cpu->next_instr_addr; | |
cpu->next_instr_addr = cpu->pc; | |
cpu->next_instr = load32(cpu, cpu->pc); | |
uint32_t next = cpu->pc + 4; | |
cpu->flipflop ^= 1; | |
struct load_slot *slot = &cpu->load_slot[cpu->flipflop]; | |
unsigned int opcode = instr >> 26; | |
// Register instructions have opcode 0, and use the 6 LSB instead | |
// Translate these so we just need one switch statement | |
unsigned int fn = instr & 0x3f; | |
opcode += (opcode == 0) * (64 + fn); | |
// Coprocessor instructions have opcode 16 (cop0) or 18 (cop2) and use | |
// the five bits after the opcode for their true opcode | |
unsigned int copcode = (instr >> 21) & 0x1f; | |
opcode += (opcode == 16) * (128 - 16 + copcode); | |
opcode += (opcode == 18) * (160 - 18 + copcode); | |
// Translated opcode ranges are: | |
// 0 - 63: Standard (= opcode) | |
// 64 - 127: Register instructions (= 64 + fn) | |
// 128 - 159: cop0 instructions (= 128 + copcode) | |
// 160 - 191: cop2 instructions (= 160 + copcode) | |
uint32_t pc = cpu->pc; | |
uint32_t *rs; | |
uint32_t *rt; | |
uint32_t *rd; | |
uint8_t sh; | |
#define INIT_REG(fmt) do { \ | |
rs = &cpu->gr[(instr >> 21) & 0x1f]; \ | |
rt = &cpu->gr[(instr >> 16) & 0x1f]; \ | |
rd = &cpu->gr[(instr >> 11) & 0x1f]; \ | |
sh = (instr >> 6) & 0x1f ; \ | |
debug(fmt, (int)(rs - cpu->gr), (int)(rt - cpu->gr), (int)(rd - cpu->gr), sh); \ | |
} while (0) | |
uint16_t im; | |
#define INIT_IMM(fmt) do {\ | |
rs = &cpu->gr[(instr >> 21) & 0x1f]; \ | |
rt = &cpu->gr[(instr >> 16) & 0x1f]; \ | |
im = (instr >> 0) & 0xffff ; \ | |
debug(fmt, (int)(rs - cpu->gr), (int)(rt - cpu->gr), im, (int16_t)im); \ | |
} while (0) | |
uint32_t ad; | |
#define INIT_JMP(fmt) do { \ | |
ad = (instr >> 0) & 0x3ffffff; \ | |
debug(fmt, ad); \ | |
} while (0) | |
uint32_t *cd; | |
#define INIT_CP0(fmt) do { \ | |
rt = &cpu->gr[(instr >> 16) & 0x1f]; \ | |
cd = &cpu->c0[(instr >> 11) & 0x1f]; \ | |
debug(fmt, copcode, (int)(rt - cpu->gr), (int)(cd - cpu->c0)); \ | |
} while (0) | |
switch (opcode) { | |
#define X(opnum, type, opname, opdesc, opfmt, ...) \ | |
case opnum: \ | |
debug("%08x: %08x(%02x) | ", instr_addr, instr, opcode); \ | |
debug("%-22s | %-5s", opdesc, opname); \ | |
INIT_##type(opfmt); \ | |
__VA_ARGS__ \ | |
break; | |
INSTRUCTIONS_X | |
#undef X | |
default: | |
error("Bad instruction "); | |
if (opcode < 64) | |
error("%u (%08x)\n", opcode, instr); | |
else if (opcode < 128) | |
error("(reg) 0(%u) (%08x)\n", fn, instr); | |
else if (opcode < 160) | |
error("(cop0) 16(%u) (%08x)\n", copcode, instr); | |
else if (opcode < 192) | |
error("(cop2) 18(%u) (%08x)\n", copcode, instr); | |
else | |
error("(?) (%u) (%08x)\n", opcode, instr); | |
// Still developing so don't complain too much | |
exit(0); | |
} | |
cpu->pc = next; | |
// Load delay | |
// Idea: we can reset the bus by setting slot->reg to 0 | |
// In which case the bus overwrites $00, which is about to get zeroed | |
// if (slot->reg) debug(" | $%02x <- %08x\n", slot->reg, slot->val); | |
cpu->gr[slot->reg] = slot->val; | |
slot->reg = 0; | |
cpu->gr[0] = 0; | |
debug("\n"); | |
} | |
#pragma GCC diagnostic pop | |
uint64_t | |
mem_fill(uint64_t seed, uint8_t *p, size_t n) | |
{ | |
assert(!(n & 3)); | |
if (!seed) | |
seed = 0xb113476d4b33afd7; | |
for (size_t i = 0; i < n; i += 4) { | |
/* https://nullprogram.com/blog/2017/09/21/ */ | |
seed *= 0x9b60933458e17d7d; | |
seed += 0xd737232eeccdf7ed; | |
uint32_t x = (uint32_t)(seed >> (29 - (seed >> 61))); | |
p[i + 0] = (uint8_t)(x >> 0) & 0xff; | |
p[i + 1] = (uint8_t)(x >> 8) & 0xff; | |
p[i + 2] = (uint8_t)(x >> 16) & 0xff; | |
p[i + 3] = (uint8_t)(x >> 24) & 0xff; | |
} | |
return seed; | |
} | |
uint64_t | |
mem_assert(uint64_t seed, uint8_t *p, size_t n) | |
{ | |
assert(!(n & 3)); | |
if (!seed) | |
seed = 0xb113476d4b33afd7; | |
for (size_t i = 0; i < n; i += 4) { | |
/* https://nullprogram.com/blog/2017/09/21/ */ | |
seed *= 0x9b60933458e17d7d; | |
seed += 0xd737232eeccdf7ed; | |
uint32_t x = (uint32_t)(seed >> (29 - (seed >> 61))); | |
uint32_t y = p[i + 0] << 0 | | |
p[i + 1] << 8 | | |
p[i + 2] << 16 | | |
p[i + 3] << 24; | |
assert(x == y); | |
} | |
return seed; | |
} | |
int | |
main(void) | |
{ | |
struct cpu *cpu = calloc(1, sizeof(*cpu)); | |
if (!cpu) | |
die("Failed to allocate mem_premap"); | |
//fprintf(stderr, "CPU size: %luKB\n", sizeof(*cpu) / 1024); | |
//fprintf(stderr, "Memmap size: %luKB\n", sizeof(cpu->memmap) / 1024); | |
setup_memmap(cpu); | |
/* Test the memory map */ | |
struct { | |
uint8_t *p; | |
size_t n; | |
uint32_t offset; | |
const char *name; | |
} memtest[] = { | |
{cpu->ram, sizeof(cpu->ram), 0x00000000, "RAM"}, | |
{cpu->exp, sizeof(cpu->exp), 0x1f000000, "Expansion"}, | |
{cpu->scr, sizeof(cpu->scr), 0x1f800000, "Scratchpad"}, | |
{cpu->reg, sizeof(cpu->reg), 0x1f801000, "Hardware registers"}, | |
{cpu->bio, sizeof(cpu->bio), 0x1fc00000, "BIOS"}, | |
{cpu->pio, sizeof(cpu->pio), 0xfffe0000, "IO Ports"}, | |
}; | |
uint64_t seed = 0; | |
for (size_t i = 0; i < sizeof(memtest) / sizeof(*memtest); ++i) { | |
seed = mem_fill(seed, memtest[i].p, memtest[i].n); | |
} | |
seed = 0; | |
for (size_t i = 0; i < sizeof(memtest) / sizeof(*memtest); ++i) { | |
uint8_t *p = addr(cpu, memtest[i].offset); | |
seed = mem_assert(seed, p, memtest[i].n); | |
fprintf(stderr, "%s OK\n", memtest[i].name); | |
} | |
reset(cpu); | |
const int n_breakpoints = sizeof(breakpoints) / sizeof(breakpoints[0]); | |
while (1) { | |
int i = -1; | |
for (i = 0; i < n_breakpoints; ++i) { | |
uint32_t a = cpu->next_instr_addr & 0x1fffffff; | |
if (a == (breakpoints[i] & 0x1fffffff)) { | |
do_cycle(cpu); | |
dump_reg(cpu); | |
(void)getchar(); | |
break; | |
} | |
} | |
if (i == n_breakpoints) | |
do_cycle(cpu); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment