Implement a somewhat hacky MMU
This commit is contained in:
parent
8f0c4e6c51
commit
b06f6de79c
1
Makefile
1
Makefile
|
@ -8,6 +8,7 @@ CFILES = src/main.c \
|
|||
src/disk.c \
|
||||
src/framebuffer.c \
|
||||
src/keyboard.c \
|
||||
src/mmu.c \
|
||||
src/mouse.c \
|
||||
src/screen.c
|
||||
|
||||
|
|
2064
fox32rom.h
2064
fox32rom.h
File diff suppressed because it is too large
Load Diff
136
src/cpu.c
136
src/cpu.c
|
@ -4,6 +4,7 @@
|
|||
#include <setjmp.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mmu.h"
|
||||
|
||||
typedef fox32_err_t err_t;
|
||||
|
||||
|
@ -62,6 +63,7 @@ enum {
|
|||
OP_PUSH = 0x0A,
|
||||
OP_IN = 0x0B,
|
||||
OP_ISE = 0x0C,
|
||||
OP_MSE = 0x0D,
|
||||
OP_HALT = 0x10,
|
||||
OP_INC = 0x11,
|
||||
OP_OR = 0x13,
|
||||
|
@ -74,6 +76,7 @@ enum {
|
|||
OP_POP = 0x1A,
|
||||
OP_OUT = 0x1B,
|
||||
OP_ICL = 0x1C,
|
||||
OP_MCL = 0x1D,
|
||||
OP_BRK = 0x20,
|
||||
OP_SUB = 0x21,
|
||||
OP_DIV = 0x22,
|
||||
|
@ -85,13 +88,15 @@ enum {
|
|||
OP_LOOP = 0x28,
|
||||
OP_RLOOP = 0x29,
|
||||
OP_RET = 0x2A,
|
||||
OP_TLB = 0x2D,
|
||||
OP_DEC = 0x31,
|
||||
OP_REM = 0x32,
|
||||
OP_NOT = 0x33,
|
||||
OP_IDIV = 0x34,
|
||||
OP_IREM = 0x35,
|
||||
OP_RTA = 0x39,
|
||||
OP_RETI = 0x3A
|
||||
OP_RETI = 0x3A,
|
||||
OP_FLP = 0x3D
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -205,6 +210,7 @@ static const asm_iinfo_t asm_iinfos[256] = {
|
|||
[OP_PUSH ] = { "PUSH ", 1 },
|
||||
[OP_IN ] = { "IN ", 2 },
|
||||
[OP_ISE ] = { "ISE ", 0 },
|
||||
[OP_MSE ] = { "MSE ", 0 },
|
||||
[OP_HALT ] = { "HALT ", 0 },
|
||||
[OP_INC ] = { "INC ", 1 },
|
||||
[OP_OR ] = { "OR ", 2 },
|
||||
|
@ -217,6 +223,7 @@ static const asm_iinfo_t asm_iinfos[256] = {
|
|||
[OP_POP ] = { "POP ", 1 },
|
||||
[OP_OUT ] = { "OUT ", 2 },
|
||||
[OP_ICL ] = { "ICL ", 0 },
|
||||
[OP_MCL ] = { "MCL ", 0 },
|
||||
[OP_BRK ] = { "BRK ", 0 },
|
||||
[OP_SUB ] = { "SUB ", 2 },
|
||||
[OP_DIV ] = { "DIV ", 2 },
|
||||
|
@ -228,13 +235,15 @@ static const asm_iinfo_t asm_iinfos[256] = {
|
|||
[OP_LOOP ] = { "LOOP ", 1 },
|
||||
[OP_RLOOP] = { "RLOOP", 1 },
|
||||
[OP_RET ] = { "RET ", 0 },
|
||||
[OP_TLB ] = { "TLB ", 1 },
|
||||
[OP_DEC ] = { "DEC ", 1 },
|
||||
[OP_REM ] = { "REM ", 2 },
|
||||
[OP_NOT ] = { "NOT ", 1 },
|
||||
[OP_IDIV ] = { "IDIV ", 2 },
|
||||
[OP_IREM ] = { "IREM ", 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) {
|
||||
|
@ -351,6 +360,7 @@ static void vm_init(vm_t *vm) {
|
|||
vm->pointer_instr = FOX32_POINTER_DEFAULT_INSTR;
|
||||
vm->pointer_stack = FOX32_POINTER_DEFAULT_STACK;
|
||||
vm->halted = true;
|
||||
vm->mmu_enabled = false;
|
||||
vm->io_user = NULL;
|
||||
vm->io_read = io_read_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) {
|
||||
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) {
|
||||
vm->flag_zero = (flags & 1) != 0;
|
||||
vm->flag_carry = (flags & 2) != 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) {
|
||||
|
@ -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) {
|
||||
uint32_t address_end = address + size;
|
||||
if (address_end > address) {
|
||||
if (address_end <= FOX32_MEMORY_RAM) {
|
||||
return &vm->memory_ram[address];
|
||||
if (!vm->mmu_enabled) {
|
||||
if (address_end > 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 (
|
||||
!write &&
|
||||
(address >= FOX32_MEMORY_ROM_START) &&
|
||||
(address -= FOX32_MEMORY_ROM_START) + size <= FOX32_MEMORY_ROM
|
||||
) {
|
||||
return &vm->memory_rom[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);
|
||||
}
|
||||
} 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) \
|
||||
|
@ -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) { \
|
||||
VM_PRELUDE_0(); \
|
||||
_type a = _vm_source(vm, instr.source); \
|
||||
_type b = _vm_source(vm, instr.target); \
|
||||
_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) { \
|
||||
VM_PRELUDE_2(_size); \
|
||||
_type a = _vm_source(vm, instr.source); \
|
||||
_type b = _vm_source(vm, instr.target); \
|
||||
_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] = {};
|
||||
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) {
|
||||
|
@ -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_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:
|
||||
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) {
|
||||
if (!vm->flag_interrupt) {
|
||||
if (!vm->flag_interrupt && vector < 256) {
|
||||
return FOX32_ERR_NOINTERRUPTS;
|
||||
}
|
||||
if (setjmp(vm->panic_jmp) != 0) {
|
||||
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);
|
||||
vm_push8(vm, vm_flags_get(vm));
|
||||
if (vm->flag_swap_sp) {
|
||||
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->halted = true;
|
||||
|
|
|
@ -47,19 +47,25 @@ typedef struct {
|
|||
uint32_t pointer_stack;
|
||||
uint32_t pointer_exception_stack;
|
||||
uint32_t pointer_frame;
|
||||
uint32_t pointer_page_directory;
|
||||
uint32_t registers[FOX32_REGISTER_COUNT];
|
||||
|
||||
bool flag_zero;
|
||||
bool flag_carry;
|
||||
bool flag_interrupt;
|
||||
bool flag_swap_sp;
|
||||
|
||||
bool halted;
|
||||
|
||||
bool debug;
|
||||
|
||||
bool mmu_enabled;
|
||||
|
||||
jmp_buf panic_jmp;
|
||||
fox32_err_t panic_err;
|
||||
|
||||
uint32_t exception_operand;
|
||||
|
||||
void *io_user;
|
||||
fox32_io_read_t *io_read;
|
||||
fox32_io_write_t *io_write;
|
||||
|
|
|
@ -102,8 +102,10 @@ void main_loop(void) {
|
|||
|
||||
error = fox32_resume(&vm, cycles_left);
|
||||
if (error != FOX32_ERR_OK) {
|
||||
puts(fox32_strerr(error));
|
||||
fox32_recover(&vm, error);
|
||||
//puts(fox32_strerr(error));
|
||||
error = fox32_recover(&vm, error);
|
||||
//if (error != FOX32_ERR_OK)
|
||||
//puts(fox32_strerr(error));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
123
src/mmu.c
Normal file
123
src/mmu.c
Normal 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
13
src/mmu.h
Normal 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);
|
Loading…
Reference in New Issue
Block a user