Implement a somewhat hacky MMU

This commit is contained in:
Ry 2022-10-25 22:44:18 -07:00
parent 8f0c4e6c51
commit b06f6de79c
7 changed files with 1297 additions and 1052 deletions

View File

@ -8,6 +8,7 @@ CFILES = src/main.c \
src/disk.c \ src/disk.c \
src/framebuffer.c \ src/framebuffer.c \
src/keyboard.c \ src/keyboard.c \
src/mmu.c \
src/mouse.c \ src/mouse.c \
src/screen.c src/screen.c

2064
fox32rom.h

File diff suppressed because it is too large Load Diff

136
src/cpu.c
View File

@ -4,6 +4,7 @@
#include <setjmp.h> #include <setjmp.h>
#include "cpu.h" #include "cpu.h"
#include "mmu.h"
typedef fox32_err_t err_t; typedef fox32_err_t err_t;
@ -62,6 +63,7 @@ enum {
OP_PUSH = 0x0A, OP_PUSH = 0x0A,
OP_IN = 0x0B, OP_IN = 0x0B,
OP_ISE = 0x0C, OP_ISE = 0x0C,
OP_MSE = 0x0D,
OP_HALT = 0x10, OP_HALT = 0x10,
OP_INC = 0x11, OP_INC = 0x11,
OP_OR = 0x13, OP_OR = 0x13,
@ -74,6 +76,7 @@ enum {
OP_POP = 0x1A, OP_POP = 0x1A,
OP_OUT = 0x1B, OP_OUT = 0x1B,
OP_ICL = 0x1C, OP_ICL = 0x1C,
OP_MCL = 0x1D,
OP_BRK = 0x20, OP_BRK = 0x20,
OP_SUB = 0x21, OP_SUB = 0x21,
OP_DIV = 0x22, OP_DIV = 0x22,
@ -85,13 +88,15 @@ enum {
OP_LOOP = 0x28, OP_LOOP = 0x28,
OP_RLOOP = 0x29, OP_RLOOP = 0x29,
OP_RET = 0x2A, OP_RET = 0x2A,
OP_TLB = 0x2D,
OP_DEC = 0x31, OP_DEC = 0x31,
OP_REM = 0x32, OP_REM = 0x32,
OP_NOT = 0x33, OP_NOT = 0x33,
OP_IDIV = 0x34, OP_IDIV = 0x34,
OP_IREM = 0x35, OP_IREM = 0x35,
OP_RTA = 0x39, OP_RTA = 0x39,
OP_RETI = 0x3A OP_RETI = 0x3A,
OP_FLP = 0x3D
}; };
enum { enum {
@ -205,6 +210,7 @@ static const asm_iinfo_t asm_iinfos[256] = {
[OP_PUSH ] = { "PUSH ", 1 }, [OP_PUSH ] = { "PUSH ", 1 },
[OP_IN ] = { "IN ", 2 }, [OP_IN ] = { "IN ", 2 },
[OP_ISE ] = { "ISE ", 0 }, [OP_ISE ] = { "ISE ", 0 },
[OP_MSE ] = { "MSE ", 0 },
[OP_HALT ] = { "HALT ", 0 }, [OP_HALT ] = { "HALT ", 0 },
[OP_INC ] = { "INC ", 1 }, [OP_INC ] = { "INC ", 1 },
[OP_OR ] = { "OR ", 2 }, [OP_OR ] = { "OR ", 2 },
@ -217,6 +223,7 @@ static const asm_iinfo_t asm_iinfos[256] = {
[OP_POP ] = { "POP ", 1 }, [OP_POP ] = { "POP ", 1 },
[OP_OUT ] = { "OUT ", 2 }, [OP_OUT ] = { "OUT ", 2 },
[OP_ICL ] = { "ICL ", 0 }, [OP_ICL ] = { "ICL ", 0 },
[OP_MCL ] = { "MCL ", 0 },
[OP_BRK ] = { "BRK ", 0 }, [OP_BRK ] = { "BRK ", 0 },
[OP_SUB ] = { "SUB ", 2 }, [OP_SUB ] = { "SUB ", 2 },
[OP_DIV ] = { "DIV ", 2 }, [OP_DIV ] = { "DIV ", 2 },
@ -228,13 +235,15 @@ static const asm_iinfo_t asm_iinfos[256] = {
[OP_LOOP ] = { "LOOP ", 1 }, [OP_LOOP ] = { "LOOP ", 1 },
[OP_RLOOP] = { "RLOOP", 1 }, [OP_RLOOP] = { "RLOOP", 1 },
[OP_RET ] = { "RET ", 0 }, [OP_RET ] = { "RET ", 0 },
[OP_TLB ] = { "TLB ", 1 },
[OP_DEC ] = { "DEC ", 1 }, [OP_DEC ] = { "DEC ", 1 },
[OP_REM ] = { "REM ", 2 }, [OP_REM ] = { "REM ", 2 },
[OP_NOT ] = { "NOT ", 1 }, [OP_NOT ] = { "NOT ", 1 },
[OP_IDIV ] = { "IDIV ", 2 }, [OP_IDIV ] = { "IDIV ", 2 },
[OP_IREM ] = { "IREM ", 2 }, [OP_IREM ] = { "IREM ", 2 },
[OP_RTA ] = { "RTA ", 2 }, [OP_RTA ] = { "RTA ", 2 },
[OP_RETI ] = { "RETI ", 0 } [OP_RETI ] = { "RETI ", 0 },
[OP_FLP ] = { "FLP ", 0 }
}; };
static const asm_iinfo_t *asm_iinfo_get(uint8_t opcode) { static const asm_iinfo_t *asm_iinfo_get(uint8_t opcode) {
@ -351,6 +360,7 @@ static void vm_init(vm_t *vm) {
vm->pointer_instr = FOX32_POINTER_DEFAULT_INSTR; vm->pointer_instr = FOX32_POINTER_DEFAULT_INSTR;
vm->pointer_stack = FOX32_POINTER_DEFAULT_STACK; vm->pointer_stack = FOX32_POINTER_DEFAULT_STACK;
vm->halted = true; vm->halted = true;
vm->mmu_enabled = false;
vm->io_user = NULL; vm->io_user = NULL;
vm->io_read = io_read_default; vm->io_read = io_read_default;
vm->io_write = io_write_default; vm->io_write = io_write_default;
@ -379,12 +389,16 @@ static void vm_io_write(vm_t *vm, uint32_t port, uint32_t value) {
} }
static uint8_t vm_flags_get(vm_t *vm) { static uint8_t vm_flags_get(vm_t *vm) {
return (((uint8_t) vm->flag_interrupt) << 2) | (((uint8_t) vm->flag_carry) << 1) | ((uint8_t) vm->flag_zero); return (((uint8_t) vm->flag_swap_sp) << 3) |
(((uint8_t) vm->flag_interrupt) << 2) |
(((uint8_t) vm->flag_carry) << 1) |
((uint8_t) vm->flag_zero);
} }
static void vm_flags_set(vm_t *vm, uint8_t flags) { static void vm_flags_set(vm_t *vm, uint8_t flags) {
vm->flag_zero = (flags & 1) != 0; vm->flag_zero = (flags & 1) != 0;
vm->flag_carry = (flags & 2) != 0; vm->flag_carry = (flags & 2) != 0;
vm->flag_interrupt = (flags & 4) != 0; vm->flag_interrupt = (flags & 4) != 0;
vm->flag_swap_sp = (flags & 8) != 0;
} }
static uint32_t *vm_findlocal(vm_t *vm, uint8_t local) { static uint32_t *vm_findlocal(vm_t *vm, uint8_t local) {
@ -405,19 +419,60 @@ static uint32_t *vm_findlocal(vm_t *vm, uint8_t local) {
static uint8_t *vm_findmemory(vm_t *vm, uint32_t address, uint32_t size, bool write) { static uint8_t *vm_findmemory(vm_t *vm, uint32_t address, uint32_t size, bool write) {
uint32_t address_end = address + size; uint32_t address_end = address + size;
if (address_end > address) { if (!vm->mmu_enabled) {
if (address_end <= FOX32_MEMORY_RAM) { if (address_end > address) {
return &vm->memory_ram[address]; if (address_end <= FOX32_MEMORY_RAM) {
return &vm->memory_ram[address];
}
if (
!write &&
(address >= FOX32_MEMORY_ROM_START) &&
(address -= FOX32_MEMORY_ROM_START) + size <= FOX32_MEMORY_ROM
) {
return &vm->memory_rom[address];
}
} }
if ( if (!write) {
!write && vm->exception_operand = address;
(address >= FOX32_MEMORY_ROM_START) && vm_panic(vm, FOX32_ERR_FAULT_RD);
(address -= FOX32_MEMORY_ROM_START) + size <= FOX32_MEMORY_ROM } else {
) { vm->exception_operand = address;
return &vm->memory_rom[address]; vm_panic(vm, FOX32_ERR_FAULT_WR);
}
} else {
mmu_page_t *virtual_page = get_present_page(address);
if (virtual_page == NULL) {
if (!write) {
vm->exception_operand = address;
vm_panic(vm, FOX32_ERR_FAULT_RD);
} else {
vm->exception_operand = address;
vm_panic(vm, FOX32_ERR_FAULT_WR);
}
}
uint32_t offset = address & 0x00000FFF;
uint32_t physical_address = virtual_page->physical_address | offset;
address_end = physical_address + size;
if (address_end > physical_address) {
if (address_end <= FOX32_MEMORY_RAM) {
return &vm->memory_ram[physical_address];
}
if (
!write &&
(physical_address >= FOX32_MEMORY_ROM_START) &&
(physical_address -= FOX32_MEMORY_ROM_START) + size <= FOX32_MEMORY_ROM
) {
return &vm->memory_rom[physical_address];
}
}
if (!write) {
vm->exception_operand = address;
vm_panic(vm, FOX32_ERR_FAULT_RD);
} else {
vm->exception_operand = address;
vm_panic(vm, FOX32_ERR_FAULT_WR);
} }
} }
if (!write) vm_panic(vm, FOX32_ERR_FAULT_RD); else vm_panic(vm, FOX32_ERR_FAULT_WR);
} }
#define VM_READ_BODY(_ptr_get, _size) \ #define VM_READ_BODY(_ptr_get, _size) \
@ -723,6 +778,7 @@ static void vm_skipparam(vm_t *vm, uint32_t size, uint8_t prtype) {
} }
#define VM_IMPL_CMP(_size, _type, _vm_source) { \ #define VM_IMPL_CMP(_size, _type, _vm_source) { \
VM_PRELUDE_0(); \
_type a = _vm_source(vm, instr.source); \ _type a = _vm_source(vm, instr.source); \
_type b = _vm_source(vm, instr.target); \ _type b = _vm_source(vm, instr.target); \
_type x; \ _type x; \
@ -732,6 +788,7 @@ static void vm_skipparam(vm_t *vm, uint32_t size, uint8_t prtype) {
} }
#define VM_IMPL_BTS(_size, _type, _vm_source) { \ #define VM_IMPL_BTS(_size, _type, _vm_source) { \
VM_PRELUDE_2(_size); \
_type a = _vm_source(vm, instr.source); \ _type a = _vm_source(vm, instr.source); \
_type b = _vm_source(vm, instr.target); \ _type b = _vm_source(vm, instr.target); \
_type x = b & (1 << a); \ _type x = b & (1 << a); \
@ -751,7 +808,7 @@ static void vm_debug(vm_t *vm, asm_instr_t instr, uint32_t address) {
char buffer[128] = {}; char buffer[128] = {};
asm_disas_print(instr, iinfo, params_data, buffer); asm_disas_print(instr, iinfo, params_data, buffer);
printf("%08x %s\n", address, buffer); printf("%08X %s\n", address, buffer);
} }
static void vm_execute(vm_t *vm) { static void vm_execute(vm_t *vm) {
@ -930,6 +987,27 @@ static void vm_execute(vm_t *vm) {
case OP(SZ_HALF, OP_BTS): VM_IMPL_BTS(SIZE16, uint16_t, vm_source16); case OP(SZ_HALF, OP_BTS): VM_IMPL_BTS(SIZE16, uint16_t, vm_source16);
case OP(SZ_WORD, OP_BTS): VM_IMPL_BTS(SIZE32, uint32_t, vm_source32); case OP(SZ_WORD, OP_BTS): VM_IMPL_BTS(SIZE32, uint32_t, vm_source32);
case OP(SZ_WORD, OP_MSE): {
VM_PRELUDE_0();
vm->mmu_enabled = true;
break;
};
case OP(SZ_WORD, OP_MCL): {
VM_PRELUDE_0();
vm->mmu_enabled = false;
break;
};
case OP(SZ_WORD, OP_TLB): {
VM_PRELUDE_1(SIZE32);
set_and_flush_tlb(vm_source32(vm, instr.source));
break;
};
case OP(SZ_WORD, OP_FLP): {
VM_PRELUDE_1(SIZE32);
flush_single_page(vm_source32(vm, instr.source));
break;
};
default: default:
vm_panic(vm, FOX32_ERR_BADOPCODE); vm_panic(vm, FOX32_ERR_BADOPCODE);
} }
@ -957,17 +1035,39 @@ static err_t vm_resume(vm_t *vm, uint32_t count) {
} }
static fox32_err_t vm_raise(vm_t *vm, uint16_t vector) { static fox32_err_t vm_raise(vm_t *vm, uint16_t vector) {
if (!vm->flag_interrupt) { if (!vm->flag_interrupt && vector < 256) {
return FOX32_ERR_NOINTERRUPTS; return FOX32_ERR_NOINTERRUPTS;
} }
if (setjmp(vm->panic_jmp) != 0) { if (setjmp(vm->panic_jmp) != 0) {
return vm->panic_err; return vm->panic_err;
} }
uint32_t pointer_handler = vm_read32(vm, SIZE32 * (uint32_t) vector); uint32_t pointer_handler =
vm->memory_ram[SIZE32 * (uint32_t) vector] |
vm->memory_ram[SIZE32 * (uint32_t) vector + 1] << 8 |
vm->memory_ram[SIZE32 * (uint32_t) vector + 2] << 16 |
vm->memory_ram[SIZE32 * (uint32_t) vector + 3] << 24;
vm_push32(vm, vm->pointer_instr); if (vm->flag_swap_sp) {
vm_push8(vm, vm_flags_get(vm)); uint32_t old_stack_pointer = vm->pointer_stack;
vm->pointer_stack = vm->pointer_exception_stack;
vm_push32(vm, old_stack_pointer);
vm_push32(vm, vm->pointer_instr);
vm_push8(vm, vm_flags_get(vm));
vm->flag_swap_sp = false;
} else {
vm_push32(vm, vm->pointer_instr);
vm_push8(vm, vm_flags_get(vm));
}
if (vector >= 256) {
// if this is an exception, push the operand
vm_push32(vm, vm->exception_operand);
vm->exception_operand = 0;
} else {
// if this is an interrupt, push the vector
vm_push32(vm, (uint32_t) vector);
}
vm->pointer_instr = pointer_handler; vm->pointer_instr = pointer_handler;
vm->halted = true; vm->halted = true;

View File

@ -47,19 +47,25 @@ typedef struct {
uint32_t pointer_stack; uint32_t pointer_stack;
uint32_t pointer_exception_stack; uint32_t pointer_exception_stack;
uint32_t pointer_frame; uint32_t pointer_frame;
uint32_t pointer_page_directory;
uint32_t registers[FOX32_REGISTER_COUNT]; uint32_t registers[FOX32_REGISTER_COUNT];
bool flag_zero; bool flag_zero;
bool flag_carry; bool flag_carry;
bool flag_interrupt; bool flag_interrupt;
bool flag_swap_sp;
bool halted; bool halted;
bool debug; bool debug;
bool mmu_enabled;
jmp_buf panic_jmp; jmp_buf panic_jmp;
fox32_err_t panic_err; fox32_err_t panic_err;
uint32_t exception_operand;
void *io_user; void *io_user;
fox32_io_read_t *io_read; fox32_io_read_t *io_read;
fox32_io_write_t *io_write; fox32_io_write_t *io_write;

View File

@ -102,8 +102,10 @@ void main_loop(void) {
error = fox32_resume(&vm, cycles_left); error = fox32_resume(&vm, cycles_left);
if (error != FOX32_ERR_OK) { if (error != FOX32_ERR_OK) {
puts(fox32_strerr(error)); //puts(fox32_strerr(error));
fox32_recover(&vm, error); error = fox32_recover(&vm, error);
//if (error != FOX32_ERR_OK)
//puts(fox32_strerr(error));
} }
} }

123
src/mmu.c Normal file
View File

@ -0,0 +1,123 @@
#include <SDL2/SDL.h>
#include <getopt.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cpu.h"
#include "mmu.h"
mmu_page_t mmu_tlb[64];
extern fox32_vm_t vm;
static size_t find_free_tlb_entry_index() {
for (size_t i = 0; i < 64; i++) {
if (!mmu_tlb[i].present) {
return i;
}
}
return 0;
}
void set_and_flush_tlb(uint32_t virtual_address) {
vm.pointer_page_directory = virtual_address;
for (size_t i = 0; i < 64; i++) {
mmu_tlb[i] = (mmu_page_t) {
.physical_address = 0,
.virtual_page = 0,
.present = false,
.rw = false
};
}
//printf("flushed TLB and set page directory pointer to %X\n", virtual_address);
}
void flush_single_page(uint32_t virtual_address) {
uint32_t virtual_page = virtual_address & 0xFFFFF000;
//printf("flushing single page %X\n", virtual_page);
for (size_t i = 0; i < 64; i++) {
if (mmu_tlb[i].virtual_page == virtual_page) {
mmu_tlb[i].physical_address = 0;
mmu_tlb[i].virtual_page = 0;
mmu_tlb[i].present = false;
mmu_tlb[i].rw = false;
//printf("flushed\n");
break;
}
}
}
mmu_page_t *get_present_page(uint32_t virtual_address) {
uint32_t virtual_page = virtual_address & 0xFFFFF000;
//printf("attempting to fetch physical address for virtual address %X (page %X)\n", virtual_address, virtual_page);
mmu_page_t *physical_page = NULL;
for (size_t i = 0; i < 64; i++) {
if (mmu_tlb[i].virtual_page == virtual_page) {
physical_page = &mmu_tlb[i];
break;
}
}
if (physical_page == NULL) {
// we didn't find an entry for this page in the TLB, try to insert it from the tables in memory
//printf("couldn't find an entry for this virtual page in the TLB, attempting to cache from tables\n");
uint32_t page_directory_index = virtual_address >> 22;
uint32_t page_table_index = (virtual_address >> 12) & 0x03FF;
bool directory_present = insert_tlb_entry_from_tables(page_directory_index, page_table_index);
if (!directory_present) return NULL;
// try again after possibly inserting the TLB entry
for (size_t i = 0; i < 64; i++) {
if (mmu_tlb[i].virtual_page == virtual_page) {
physical_page = &mmu_tlb[i];
break;
}
}
// if we still can't find the page, return NULL
if (physical_page == NULL) return NULL;
}
// if the page is present, return it, otherwise return NULL
if (physical_page->present) {
//printf("found physical address: %X\n", physical_page->physical_address);
return physical_page;
} else {
return NULL;
}
}
bool insert_tlb_entry_from_tables(uint32_t page_directory_index, uint32_t page_table_index) {
uint32_t directory =
vm.memory_ram[vm.pointer_page_directory + (page_directory_index * 4)] |
vm.memory_ram[vm.pointer_page_directory + (page_directory_index * 4) + 1] << 8 |
vm.memory_ram[vm.pointer_page_directory + (page_directory_index * 4) + 2] << 16 |
vm.memory_ram[vm.pointer_page_directory + (page_directory_index * 4) + 3] << 24;
//printf("operating on directory at %X\n", vm.pointer_page_directory + (page_directory_index * 4));
bool directory_present = (directory & 0b1) != 0;
uint32_t directory_address = directory & 0xFFFFF000;
//printf("directory_present: %X, directory_address: %X\n", directory_present, directory_address);
if (directory_present) {
uint32_t table =
vm.memory_ram[directory_address + (page_table_index * 4)] |
vm.memory_ram[directory_address + (page_table_index * 4) + 1] << 8 |
vm.memory_ram[directory_address + (page_table_index * 4) + 2] << 16 |
vm.memory_ram[directory_address + (page_table_index * 4) + 3] << 24;
bool table_present = (table & 0b01) != 0;
bool table_rw = (table & 0b10) != 0;
uint32_t table_address = table & 0xFFFFF000;
if (table_present) {
mmu_page_t entry = {
.physical_address = table_address,
.virtual_page = (page_directory_index << 22) | (page_table_index << 12),
.present = table_present,
.rw = table_rw
};
size_t entry_index = find_free_tlb_entry_index();
mmu_tlb[entry_index] = entry;
//printf("inserting virtual page %X into the TLB\n", (page_directory_index << 22) | (page_table_index << 12));
}
}
return directory_present;
}

13
src/mmu.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
typedef struct {
uint32_t virtual_page;
uint32_t physical_address;
bool present;
bool rw;
} mmu_page_t;
void set_and_flush_tlb(uint32_t virtual_address);
void flush_single_page(uint32_t virtual_address);
mmu_page_t *get_present_page(uint32_t virtual_address);
bool insert_tlb_entry_from_tables(uint32_t page_directory_index, uint32_t page_table_index);