├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── bochs.conf ├── docs └── screenshots │ ├── screenshot1.jpg │ └── screenshot2.jpg └── src ├── bus.c ├── bus.h ├── cpu.c ├── cpu.h ├── disk.c ├── disk.h ├── framebuffer.c ├── framebuffer.h ├── keyboard.c ├── keyboard.h ├── main.c ├── mmu.c ├── mmu.h ├── mouse.c ├── mouse.h ├── serial.c └── serial.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.img 2 | *.elf 3 | *.rom 4 | fox32rom.h 5 | **/*.o 6 | .vscode/ 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "fennecos"] 2 | path = fennecos 3 | url = https://github.com/ry755/fennecos 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 ryfox/ry755 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | AS = ~/opt/cross/bin/i686-elf-as 2 | CC = ~/opt/cross/bin/i686-elf-gcc 3 | 4 | CFILES = src/main.c \ 5 | src/bus.c \ 6 | src/cpu.c \ 7 | src/disk.c \ 8 | src/framebuffer.c \ 9 | src/keyboard.c \ 10 | src/mmu.c \ 11 | src/mouse.c 12 | 13 | OBJS = $(addsuffix .o, $(basename $(CFILES))) 14 | OBJS += \ 15 | fennecos/build/user/crt0.o \ 16 | fennecos/build/user/framebuffer.o \ 17 | fennecos/build/user/keyboard.o \ 18 | fennecos/build/user/user.o \ 19 | fennecos/build/libc/fox/alloc.o \ 20 | fennecos/build/libc/fox/string.o \ 21 | fennecos/build/libc/stdio/getchar.o \ 22 | fennecos/build/libc/stdio/gets.o \ 23 | fennecos/build/libc/stdio/printf.o \ 24 | fennecos/build/libc/stdio/putchar.o \ 25 | fennecos/build/libc/stdio/puts.o \ 26 | fennecos/build/libc/stdio/vprintf.o \ 27 | fennecos/build/libc/stdlib/itoa.o \ 28 | fennecos/build/libc/stdlib/utoa.o \ 29 | fennecos/build/libc/string/memcmp.o \ 30 | fennecos/build/libc/string/memcpy.o \ 31 | fennecos/build/libc/string/memmove.o \ 32 | fennecos/build/libc/string/memset.o \ 33 | fennecos/build/libc/string/strcat.o \ 34 | fennecos/build/libc/string/strchr.o \ 35 | fennecos/build/libc/string/strcmp.o \ 36 | fennecos/build/libc/string/strcpy.o \ 37 | fennecos/build/libc/string/strlen.o 38 | 39 | .PHONY: all 40 | all: fennecos_build fox32.elf fennecos_image 41 | 42 | fennecos_build: 43 | cd fennecos && ./build.sh 44 | 45 | fennecos_image: 46 | cp fox32.elf fennecos/base_image/bin/ 47 | cp fox32.rom fennecos/base_image/bin/ 48 | cd fennecos && sudo ./image.sh 49 | mv fennecos/boot.img hdd.img 50 | cd fennecos && sudo ./image-floppy.sh 51 | mv fennecos/floppy.img fdd.img 52 | 53 | fox32.elf: $(OBJS) 54 | $(CC) -o $@ $(OBJS) -I fennecos/kernel/include -I fennecos/libc/include -g -std=gnu99 -ffreestanding -O2 -Wall -Wextra -nostdlib -lgcc 55 | 56 | %.o: %.c $(FOX32ROM_OUT) 57 | $(CC) -o $@ -c $< -I fennecos/kernel/include -I fennecos/libc/include -g -std=gnu99 -ffreestanding -O2 -Wall -Wextra 58 | 59 | clean: 60 | rm -rf fox32.elf fox32rom.h $(OBJS) 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fox86 2 | 3 | **fox86** is a port of the [**fox32** virtual machine](https://github.com/fox32-arch/fox32) to [FennecOS](https://github.com/ry755/fennecos). This allows it to run on a very tiny x86 kernel, essentially creating a bootable fox32 virtual machine! 4 | 5 | # Screenshots 6 | 7 | ![Photo of fox86 running on a desktop computer](docs/screenshots/screenshot1.jpg) 8 | 9 | ![Another photo of fox86 running on a desktop computer, with a few windows open](docs/screenshots/screenshot2.jpg) 10 | -------------------------------------------------------------------------------- /bochs.conf: -------------------------------------------------------------------------------- 1 | # configuration file generated by Bochs 2 | plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, iodebug=true 3 | config_interface: textconfig 4 | display_library: sdl2 5 | memory: guest=256, host=256, block_size=128 6 | romimage: file="/usr/local/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none, flash_data=none 7 | vgaromimage: file="/usr/local/share/bochs/VGABIOS-lgpl-latest" 8 | boot: disk, floppy 9 | floppy_bootsig_check: disabled=0 10 | floppya: type=1_44, 1_44="fdd.img", status=inserted, write_protected=0 11 | # no floppyb 12 | ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 13 | ata0-master: type=disk, path="hdd.img", mode=flat, cylinders=0, heads=16, spt=63, sect_size=512, model="Generic 1234", biosdetect=auto, translation=auto 14 | ata0-slave: type=none 15 | ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15 16 | ata1-master: type=none 17 | ata1-slave: type=none 18 | ata2: enabled=false 19 | ata3: enabled=false 20 | optromimage1: file=none 21 | optromimage2: file=none 22 | optromimage3: file=none 23 | optromimage4: file=none 24 | optramimage1: file=none 25 | optramimage2: file=none 26 | optramimage3: file=none 27 | optramimage4: file=none 28 | pci: enabled=1, chipset=i440fx, slot1=none, slot2=none, slot3=none, slot4=none, slot5=none 29 | vga: extension=vbe, update_freq=10, realtime=1, ddc=builtin 30 | cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 31 | cpuid: level=6, stepping=3, model=3, family=6, vendor_string="GenuineIntel", brand_string=" Intel(R) Pentium(R) 4 CPU " 32 | cpuid: mmx=true, apic=xapic, simd=sse2, sse4a=false, misaligned_sse=false, sep=true 33 | cpuid: movbe=false, adx=false, aes=false, sha=false, xsave=false, xsaveopt=false, smep=false 34 | cpuid: smap=false, mwait=true 35 | print_timestamps: enabled=0 36 | debugger_log: - 37 | magic_break: enabled=1 0x8 38 | port_e9_hack: enabled=false, all_rings=false 39 | iodebug: all_rings=0 40 | private_colormap: enabled=0 41 | clock: sync=none, time0=local, rtc_sync=0 42 | # no cmosimage 43 | log: - 44 | logprefix: %t%e%d 45 | debug: action=ignore 46 | info: action=report 47 | error: action=report 48 | panic: action=ask 49 | keyboard: type=mf, serial_delay=150, paste_delay=100000, user_shortcut=none 50 | mouse: type=ps2, enabled=false, toggle=ctrl+mbutton 51 | speaker: enabled=true, mode=system 52 | parport1: enabled=true, file=none 53 | parport2: enabled=false 54 | com1: enabled=true, mode=null 55 | com2: enabled=false 56 | com3: enabled=false 57 | com4: enabled=false 58 | -------------------------------------------------------------------------------- /docs/screenshots/screenshot1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fox32-arch/fox86/6a85366cf17cc0588c94638d85346d8314a29c34/docs/screenshots/screenshot1.jpg -------------------------------------------------------------------------------- /docs/screenshots/screenshot2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fox32-arch/fox86/6a85366cf17cc0588c94638d85346d8314a29c34/docs/screenshots/screenshot2.jpg -------------------------------------------------------------------------------- /src/bus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "bus.h" 9 | #include "cpu.h" 10 | #include "disk.h" 11 | #include "framebuffer.h" 12 | #include "keyboard.h" 13 | #include "mouse.h" 14 | #include "serial.h" 15 | 16 | bool bus_requests_exit = false; 17 | 18 | extern uint32_t rtc_uptime; 19 | 20 | extern fox32_vm_t vm; 21 | extern disk_controller_t disk_controller; 22 | extern mouse_t mouse; 23 | 24 | int bus_io_read(void *user, uint32_t *value, uint32_t port) { 25 | (void) user; 26 | switch (port) { 27 | #ifndef WINDOWS 28 | case 0x00000000: { // serial port 29 | //*value = serial_get(); 30 | break; 31 | }; 32 | #endif 33 | 34 | case 0x80000000 ... 0x8000031F: { // overlay port 35 | uint8_t overlay_number = port & 0x000000FF; 36 | uint8_t setting = (port & 0x0000FF00) >> 8; 37 | switch (setting) { 38 | case 0x00: { 39 | // overlay position 40 | uint32_t x = overlay_get(overlay_number)->x; 41 | uint32_t y = overlay_get(overlay_number)->y; 42 | *value = (y << 16) | x; 43 | break; 44 | }; 45 | case 0x01: { 46 | // overlay size 47 | uint32_t width = overlay_get(overlay_number)->width; 48 | uint32_t height = overlay_get(overlay_number)->height; 49 | *value = (height << 16) | width; 50 | break; 51 | }; 52 | case 0x02: { 53 | // overlay framebuffer pointer 54 | *value = overlay_get(overlay_number)->pointer; 55 | break; 56 | }; 57 | case 0x03: { 58 | // overlay enable status 59 | *value = overlay_get(overlay_number)->enabled ? 1 : 0; 60 | break; 61 | }; 62 | } 63 | 64 | break; 65 | }; 66 | 67 | case 0x80000400 ... 0x80000401: { // mouse port 68 | uint8_t setting = port & 0x000000FF; 69 | switch (setting) { 70 | case 0x00: { 71 | // button states 72 | if (mouse.clicked) *value |= 0b001; 73 | if (mouse.released) *value |= 0b010; 74 | if (mouse.held) *value |= 0b100; else *value &= ~(0b100); 75 | break; 76 | }; 77 | case 0x01: { 78 | // position 79 | *value = (mouse.y << 16) | mouse.x; 80 | break; 81 | }; 82 | } 83 | 84 | break; 85 | }; 86 | 87 | case 0x80000500: { 88 | *value = (uint32_t) key_take(); 89 | 90 | break; 91 | } 92 | 93 | case 0x80000700 ... 0x80000707: { // RTC port 94 | uint8_t setting = port & 0x000000FF; 95 | //struct tm *now = localtime(&rtc_time); 96 | switch (setting) { 97 | case 0x00: { // year 98 | //*value = now->tm_year + 1900; 99 | break; 100 | } 101 | case 0x01: { // month 102 | //*value = now->tm_mon + 1; 103 | break; 104 | } 105 | case 0x02: { // day 106 | //*value = now->tm_mday; 107 | break; 108 | } 109 | case 0x03: { // hour 110 | //*value = now->tm_hour; 111 | break; 112 | } 113 | case 0x04: { // minute 114 | //*value = now->tm_min; 115 | break; 116 | } 117 | case 0x05: { // second 118 | //*value = now->tm_sec; 119 | break; 120 | } 121 | case 0x06: { // ms since startup 122 | *value = rtc_uptime; 123 | break; 124 | } 125 | case 0x07: { // daylight savings time active 126 | //*value = now->tm_isdst; 127 | break; 128 | } 129 | } 130 | 131 | break; 132 | } 133 | 134 | case 0x80001000 ... 0x80002003: { // disk controller port 135 | size_t id = port & 0xFF; 136 | uint8_t operation = (port & 0x0000F000) >> 8; 137 | switch (operation) { 138 | case 0x10: { 139 | // current insert state of specified disk id 140 | // size will be zero if disk isn't inserted 141 | *value = get_disk_size(id); 142 | break; 143 | }; 144 | case 0x20: { 145 | // current buffer pointer 146 | *value = disk_controller.buffer_pointer; 147 | break; 148 | }; 149 | } 150 | 151 | break; 152 | }; 153 | } 154 | 155 | return 0; 156 | } 157 | 158 | int bus_io_write(void *user, uint32_t value, uint32_t port) { 159 | (void) user; 160 | switch (port) { 161 | case 0x00000000: { // serial port 162 | //serial_put(value); 163 | break; 164 | }; 165 | 166 | case 0x80000000 ... 0x8000031F: { // overlay port 167 | uint8_t overlay_number = (port & 0x000000FF); 168 | uint8_t setting = (port & 0x0000FF00) >> 8; 169 | switch (setting) { 170 | case 0x00: { 171 | // overlay position 172 | uint32_t x = value & 0x0000FFFF; 173 | uint32_t y = (value & 0xFFFF0000) >> 16; 174 | overlay_get(overlay_number)->x = x; 175 | overlay_get(overlay_number)->y = y; 176 | break; 177 | }; 178 | case 0x01: { 179 | // overlay size 180 | uint32_t width = value & 0x0000FFFF; 181 | uint32_t height = (value & 0xFFFF0000) >> 16; 182 | overlay_get(overlay_number)->width = width; 183 | overlay_get(overlay_number)->height = height; 184 | break; 185 | }; 186 | case 0x02: { 187 | // overlay framebuffer pointer 188 | overlay_get(overlay_number)->pointer = value; 189 | break; 190 | }; 191 | case 0x03: { 192 | // overlay enable status 193 | overlay_get(overlay_number)->enabled = value != 0; 194 | break; 195 | }; 196 | } 197 | 198 | break; 199 | }; 200 | 201 | case 0x80000400 ... 0x80000401: { // mouse port 202 | uint8_t setting = port & 0x000000FF; 203 | switch (setting) { 204 | case 0x00: { 205 | // button states 206 | mouse.clicked = value & 0b001; 207 | mouse.released = value & 0b010; 208 | mouse.held = value & 0b100; 209 | break; 210 | }; 211 | case 0x01: { 212 | // position 213 | mouse.x = value & 0x0000FFFF; 214 | mouse.y = (value & 0xFFFF0000) >> 16; 215 | break; 216 | }; 217 | } 218 | 219 | break; 220 | }; 221 | 222 | case 0x80001000 ... 0x80005003: { // disk controller port 223 | size_t id = port & 0xFF; 224 | uint8_t operation = (port & 0x0000F000) >> 8; 225 | switch (operation) { 226 | case 0x10: { 227 | // no-op 228 | break; 229 | }; 230 | case 0x20: { 231 | // set the buffer pointer 232 | disk_controller.buffer_pointer = value; 233 | break; 234 | }; 235 | case 0x30: { 236 | // read specified disk sector into memory 237 | set_disk_sector(id, value); 238 | read_disk_into_memory(id); 239 | break; 240 | }; 241 | case 0x40: { 242 | // write specified disk sector from memory 243 | set_disk_sector(id, value); 244 | write_disk_from_memory(id); 245 | break; 246 | }; 247 | case 0x50: { 248 | // remove specified disk 249 | remove_disk(id); 250 | break; 251 | }; 252 | } 253 | 254 | break; 255 | }; 256 | 257 | case 0x80010000: { // power control port 258 | if (value == 0) { 259 | bus_requests_exit = true; 260 | } 261 | }; 262 | } 263 | 264 | return 0; 265 | } 266 | 267 | void drop_file(char *filename) { 268 | int last_id = 0; 269 | for (int i = 0; i < 4; i++) { 270 | if (disk_controller.disks[i].size != 0) { 271 | last_id++; 272 | } 273 | } 274 | new_disk(filename, last_id); 275 | } 276 | -------------------------------------------------------------------------------- /src/bus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int bus_io_read(void *user, uint32_t *value, uint32_t port); 4 | int bus_io_write(void *user, uint32_t value, uint32_t port); 5 | void drop_file(char *filename); 6 | -------------------------------------------------------------------------------- /src/cpu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "cpu.h" 7 | #include "mmu.h" 8 | 9 | typedef fox32_err_t err_t; 10 | 11 | static const char *const err_messages[] = { 12 | "", 13 | "internal error", 14 | "breakpoint reached", 15 | "fault while reading memory", 16 | "fault while writing memory", 17 | "invalid opcode", 18 | "invalid condition", 19 | "invalid register", 20 | "write to immediate", 21 | "division by zero", 22 | "io read failed", 23 | "io write failed", 24 | "interrupts disabled", 25 | "error is not recoverable" 26 | }; 27 | 28 | static const char *err_tostring(err_t err) { 29 | if (err > 0 && err <= FOX32_ERR_CANTRECOVER) { 30 | return err_messages[err]; 31 | } 32 | return err_messages[FOX32_ERR_OK]; 33 | } 34 | 35 | typedef fox32_io_read_t io_read_t; 36 | typedef fox32_io_write_t io_write_t; 37 | 38 | static int io_read_default_impl(void *user, uint32_t *value, uint32_t port) { 39 | return (void) user, (void) value, (int) port; 40 | } 41 | static int io_write_default_impl(void *user, uint32_t value, uint32_t port) { 42 | if (port == 0) { 43 | putchar((int) value); 44 | //fflush(stdout); 45 | } 46 | return (void) user, (int) port; 47 | } 48 | 49 | static io_read_t *const io_read_default = io_read_default_impl; 50 | static io_write_t *const io_write_default = io_write_default_impl; 51 | 52 | enum { 53 | OP_NOP = 0x00, 54 | OP_ADD = 0x01, 55 | OP_MUL = 0x02, 56 | OP_AND = 0x03, 57 | OP_SLA = 0x04, 58 | OP_SRA = 0x05, 59 | OP_BSE = 0x06, 60 | OP_CMP = 0x07, 61 | OP_JMP = 0x08, 62 | OP_RJMP = 0x09, 63 | OP_PUSH = 0x0A, 64 | OP_IN = 0x0B, 65 | OP_ISE = 0x0C, 66 | OP_MSE = 0x0D, 67 | OP_HALT = 0x10, 68 | OP_INC = 0x11, 69 | OP_OR = 0x13, 70 | OP_IMUL = 0x14, 71 | OP_SRL = 0x15, 72 | OP_BCL = 0x16, 73 | OP_MOV = 0x17, 74 | OP_CALL = 0x18, 75 | OP_RCALL = 0x19, 76 | OP_POP = 0x1A, 77 | OP_OUT = 0x1B, 78 | OP_ICL = 0x1C, 79 | OP_MCL = 0x1D, 80 | OP_BRK = 0x20, 81 | OP_SUB = 0x21, 82 | OP_DIV = 0x22, 83 | OP_XOR = 0x23, 84 | OP_ROL = 0x24, 85 | OP_ROR = 0x25, 86 | OP_BTS = 0x26, 87 | OP_MOVZ = 0x27, 88 | OP_LOOP = 0x28, 89 | OP_RLOOP = 0x29, 90 | OP_RET = 0x2A, 91 | OP_INT = 0x2C, 92 | OP_TLB = 0x2D, 93 | OP_DEC = 0x31, 94 | OP_REM = 0x32, 95 | OP_NOT = 0x33, 96 | OP_IDIV = 0x34, 97 | OP_IREM = 0x35, 98 | OP_ICMP = 0x37, 99 | OP_RTA = 0x39, 100 | OP_RETI = 0x3A, 101 | OP_FLP = 0x3D, 102 | }; 103 | 104 | enum { 105 | SZ_BYTE, 106 | SZ_HALF, 107 | SZ_WORD 108 | }; 109 | 110 | #define OP(_size, _optype) (((uint8_t) (_optype)) | (((uint8_t) (_size)) << 6)) 111 | 112 | enum { 113 | CD_ALWAYS, 114 | CD_IFZ, 115 | CD_IFNZ, 116 | CD_IFC, 117 | CD_IFNC, 118 | CD_IFGT, 119 | CD_IFLTEQ 120 | }; 121 | 122 | enum { 123 | TY_REG, 124 | TY_REGPTR, 125 | TY_IMM, 126 | TY_IMMPTR, 127 | TY_NONE 128 | }; 129 | 130 | enum { 131 | EX_DIVZERO = 256 + 0x00, 132 | EX_ILLEGAL = 256 + 0x01, 133 | EX_FAULT_RD = 256 + 0x02, 134 | EX_FAULT_WR = 256 + 0x03, 135 | EX_DEBUGGER = 256 + 0x04, 136 | EX_BUS = 256 + 0x05 137 | }; 138 | 139 | #define SIZE8 1 140 | #define SIZE16 2 141 | #define SIZE32 4 142 | 143 | static uint8_t ptr_get8(const void *ptr) { 144 | return *((const uint8_t *) ptr); 145 | } 146 | static uint16_t ptr_get16(const void *ptr) { 147 | const uint8_t *bytes = ptr; 148 | return 149 | (((uint16_t) bytes[0])) | 150 | (((uint16_t) bytes[1]) << 8); 151 | } 152 | static uint32_t ptr_get32(const void *ptr) { 153 | const uint8_t *bytes = ptr; 154 | return 155 | (((uint32_t) bytes[0])) | 156 | (((uint32_t) bytes[1]) << 8) | 157 | (((uint32_t) bytes[2]) << 16) | 158 | (((uint32_t) bytes[3]) << 24); 159 | } 160 | static void ptr_set8(void *ptr, uint8_t value) { 161 | *((uint8_t *) ptr) = value; 162 | } 163 | static void ptr_set16(void *ptr, uint16_t value) { 164 | uint8_t *bytes = ptr; 165 | bytes[0] = (uint8_t) (value); 166 | bytes[1] = (uint8_t) (value >> 8); 167 | } 168 | static void ptr_set32(void *ptr, uint32_t value) { 169 | uint8_t *bytes = ptr; 170 | bytes[0] = (uint8_t) (value); 171 | bytes[1] = (uint8_t) (value >> 8); 172 | bytes[2] = (uint8_t) (value >> 16); 173 | bytes[3] = (uint8_t) (value >> 24); 174 | } 175 | 176 | typedef struct { 177 | uint8_t opcode; 178 | uint8_t condition; 179 | uint8_t offset; 180 | uint8_t target; 181 | uint8_t source; 182 | uint8_t size; 183 | } asm_instr_t; 184 | 185 | static asm_instr_t asm_instr_from(uint16_t half) { 186 | asm_instr_t instr = { 187 | (half >> 8), 188 | (half >> 4) & 7, 189 | (half >> 7) & 1, 190 | (half >> 2) & 3, 191 | (half ) & 3, 192 | (half >> 14) 193 | }; 194 | return instr; 195 | } 196 | 197 | typedef fox32_vm_t vm_t; 198 | 199 | static void vm_init(vm_t *vm) { 200 | memset(vm, 0, sizeof(vm_t)); 201 | vm->pointer_instr = FOX32_POINTER_DEFAULT_INSTR; 202 | vm->pointer_stack = FOX32_POINTER_DEFAULT_STACK; 203 | vm->halted = true; 204 | vm->soft_halted = false; 205 | vm->mmu_enabled = false; 206 | vm->io_user = NULL; 207 | vm->io_read = io_read_default; 208 | vm->io_write = io_write_default; 209 | } 210 | 211 | static noreturn void vm_panic(vm_t *vm, err_t err) { 212 | __builtin_longjmp(vm->panic_jmp, (vm->panic_err = err, 1)); 213 | } 214 | static noreturn void vm_unreachable(vm_t *vm) { 215 | vm_panic(vm, FOX32_ERR_INTERNAL); 216 | } 217 | 218 | static uint32_t vm_io_read(vm_t *vm, uint32_t port) { 219 | uint32_t value = 0; 220 | int status = vm->io_read(vm->io_user, &value, port); 221 | if (status != 0) { 222 | vm_panic(vm, FOX32_ERR_IOREAD); 223 | } 224 | return value; 225 | } 226 | static void vm_io_write(vm_t *vm, uint32_t port, uint32_t value) { 227 | int status = vm->io_write(vm->io_user, value, port); 228 | if (status != 0) { 229 | vm_panic(vm, FOX32_ERR_IOWRITE); 230 | } 231 | } 232 | 233 | static uint8_t vm_flags_get(vm_t *vm) { 234 | return (((uint8_t) vm->flag_swap_sp) << 3) | 235 | (((uint8_t) vm->flag_interrupt) << 2) | 236 | (((uint8_t) vm->flag_carry) << 1) | 237 | ((uint8_t) vm->flag_zero); 238 | } 239 | static void vm_flags_set(vm_t *vm, uint8_t flags) { 240 | vm->flag_zero = (flags & 1) != 0; 241 | vm->flag_carry = (flags & 2) != 0; 242 | vm->flag_interrupt = (flags & 4) != 0; 243 | vm->flag_swap_sp = (flags & 8) != 0; 244 | } 245 | 246 | static uint32_t *vm_findlocal(vm_t *vm, uint8_t local) { 247 | if (local < FOX32_REGISTER_COUNT) { 248 | return &vm->registers[local]; 249 | } 250 | if (local == FOX32_REGISTER_COUNT) { 251 | return &vm->pointer_stack; 252 | } 253 | if (local == FOX32_REGISTER_COUNT + 1) { 254 | return &vm->pointer_exception_stack; 255 | } 256 | if (local == FOX32_REGISTER_COUNT + 2) { 257 | return &vm->pointer_frame; 258 | } 259 | vm_panic(vm, FOX32_ERR_BADREGISTER); 260 | } 261 | 262 | static uint8_t *vm_findmemory_phys(vm_t *vm, uint32_t address, uint32_t size, bool write) { 263 | uint32_t address_end = address + size; 264 | 265 | if (address_end > address) { 266 | if (address_end <= FOX32_MEMORY_RAM) { 267 | return &vm->memory_ram[address]; 268 | } 269 | if ( 270 | !write && 271 | (address >= FOX32_MEMORY_ROM_START) && 272 | (address -= FOX32_MEMORY_ROM_START) + size <= FOX32_MEMORY_ROM 273 | ) { 274 | return &vm->memory_rom[address]; 275 | } 276 | } 277 | if (!write) { 278 | vm->exception_operand = address; 279 | vm_panic(vm, FOX32_ERR_FAULT_RD); 280 | } else { 281 | vm->exception_operand = address; 282 | vm_panic(vm, FOX32_ERR_FAULT_WR); 283 | } 284 | } 285 | 286 | static uint8_t *vm_findmemory(vm_t *vm, uint32_t address, uint32_t size, bool write) { 287 | if (!vm->mmu_enabled) { 288 | return vm_findmemory_phys(vm, address, size, write); 289 | } else { 290 | mmu_page_t *virtual_page = get_present_page(address); 291 | if (virtual_page == NULL) { 292 | if (!write) { 293 | vm->exception_operand = address; 294 | vm_panic(vm, FOX32_ERR_FAULT_RD); 295 | } else { 296 | vm->exception_operand = address; 297 | vm_panic(vm, FOX32_ERR_FAULT_WR); 298 | } 299 | } 300 | if (!virtual_page->rw && write) { 301 | vm->exception_operand = address; 302 | vm_panic(vm, FOX32_ERR_FAULT_WR); 303 | } 304 | uint32_t offset = address & 0x00000FFF; 305 | uint32_t physical_address = virtual_page->physical_address | offset; 306 | 307 | return vm_findmemory_phys(vm, physical_address, size, write); 308 | } 309 | } 310 | 311 | static uint32_t vm_read_across(vm_t *vm, uint32_t address, int size) { 312 | uint32_t result = 0; 313 | 314 | int shift = 0; 315 | 316 | // read the first page 317 | 318 | int bytes = 0x1000 - (address&0xFFF); 319 | uint8_t *ptr = vm_findmemory(vm, address, bytes, false); 320 | 321 | while (bytes) { 322 | result |= (*ptr<>= 8; 365 | 366 | ptr_first++; 367 | bytes_first--; 368 | } 369 | 370 | // write the second page 371 | 372 | while (bytes_second) { 373 | *ptr_second = value & 0xFF; 374 | value >>= 8; 375 | 376 | ptr_second++; 377 | bytes_second--; 378 | } 379 | } 380 | 381 | #define VM_READ_BODY(_ptr_get, _size) \ 382 | if ((address & 0xFFFFF000) == ((address + _size - 1) & 0xFFFFF000)) { \ 383 | return _ptr_get(vm_findmemory(vm, address, _size, false)); \ 384 | } else { \ 385 | return vm_read_across(vm, address, _size); \ 386 | } 387 | 388 | static uint8_t vm_read8(vm_t *vm, uint32_t address) { 389 | VM_READ_BODY(ptr_get8, SIZE8) 390 | } 391 | static uint16_t vm_read16(vm_t *vm, uint32_t address) { 392 | VM_READ_BODY(ptr_get16, SIZE16) 393 | } 394 | static uint32_t vm_read32(vm_t *vm, uint32_t address) { 395 | VM_READ_BODY(ptr_get32, SIZE32) 396 | } 397 | 398 | #define VM_WRITE_BODY(_ptr_set, _size) \ 399 | if ((address & 0xFFFFF000) == ((address + _size - 1) & 0xFFFFF000)) { \ 400 | return _ptr_set(vm_findmemory(vm, address, _size, true), value); \ 401 | } else { \ 402 | return vm_write_across(vm, address, _size, value); \ 403 | } 404 | 405 | 406 | static void vm_write8(vm_t *vm, uint32_t address, uint8_t value) { 407 | VM_WRITE_BODY(ptr_set8, SIZE8) 408 | } 409 | static void vm_write16(vm_t *vm, uint32_t address, uint16_t value) { 410 | VM_WRITE_BODY(ptr_set16, SIZE16) 411 | } 412 | static void vm_write32(vm_t *vm, uint32_t address, uint32_t value) { 413 | VM_WRITE_BODY(ptr_set32, SIZE32) 414 | } 415 | 416 | #define VM_PUSH_BODY(_vm_write, _size) \ 417 | _vm_write(vm, vm->pointer_stack - _size, value); \ 418 | vm->pointer_stack -= _size; 419 | 420 | static void vm_push8(vm_t *vm, uint8_t value) { 421 | VM_PUSH_BODY(vm_write8, SIZE8) 422 | } 423 | static void vm_push16(vm_t *vm, uint16_t value) { 424 | VM_PUSH_BODY(vm_write16, SIZE16) 425 | } 426 | static void vm_push32(vm_t *vm, uint32_t value) { 427 | VM_PUSH_BODY(vm_write32, SIZE32) 428 | } 429 | 430 | #define VM_POP_BODY(_vm_read, _size) \ 431 | uint32_t result = _vm_read(vm, vm->pointer_stack); \ 432 | vm->pointer_stack += _size; \ 433 | return result; 434 | 435 | static uint8_t vm_pop8(vm_t *vm) { 436 | VM_POP_BODY(vm_read8, SIZE8) 437 | } 438 | static uint16_t vm_pop16(vm_t *vm) { 439 | VM_POP_BODY(vm_read16, SIZE16) 440 | } 441 | static uint32_t vm_pop32(vm_t *vm) { 442 | VM_POP_BODY(vm_read32, SIZE32) 443 | } 444 | 445 | #define VM_SOURCE_BODY(_vm_read, _size, _type, _move, _offset) \ 446 | uint32_t pointer_base = vm->pointer_instr_mut; \ 447 | switch (prtype) { \ 448 | case TY_REG: { \ 449 | if (_move) vm->pointer_instr_mut += SIZE8; \ 450 | return (_type) *vm_findlocal(vm, vm_read8(vm, pointer_base)); \ 451 | }; \ 452 | case TY_REGPTR: { \ 453 | if (_move) vm->pointer_instr_mut += SIZE8+_offset; \ 454 | return _vm_read(vm, *vm_findlocal(vm, vm_read8(vm, pointer_base)) \ 455 | +(_offset ? vm_read8(vm, pointer_base + 1) : 0)); \ 456 | }; \ 457 | case TY_IMM: { \ 458 | if (_move) vm->pointer_instr_mut += _size; \ 459 | return _vm_read(vm, pointer_base); \ 460 | }; \ 461 | case TY_IMMPTR: { \ 462 | if (_move) vm->pointer_instr_mut += SIZE32; \ 463 | return _vm_read(vm, vm_read32(vm, pointer_base)); \ 464 | }; \ 465 | } \ 466 | vm_unreachable(vm); 467 | 468 | static uint8_t vm_source8(vm_t *vm, uint8_t prtype, uint8_t offset) { 469 | VM_SOURCE_BODY(vm_read8, SIZE8, uint8_t, true, offset) 470 | } 471 | static uint8_t vm_source8_stay(vm_t *vm, uint8_t prtype, uint8_t offset) { 472 | VM_SOURCE_BODY(vm_read8, SIZE8, uint8_t, false, offset) 473 | } 474 | static uint16_t vm_source16(vm_t *vm, uint8_t prtype, uint8_t offset) { 475 | VM_SOURCE_BODY(vm_read16, SIZE16, uint16_t, true, offset) 476 | } 477 | static uint16_t vm_source16_stay(vm_t *vm, uint8_t prtype, uint8_t offset) { 478 | VM_SOURCE_BODY(vm_read16, SIZE16, uint16_t, false, offset) 479 | } 480 | static uint32_t vm_source32(vm_t *vm, uint8_t prtype, uint8_t offset) { 481 | VM_SOURCE_BODY(vm_read32, SIZE32, uint32_t, true, offset) 482 | } 483 | static uint32_t vm_source32_stay(vm_t *vm, uint8_t prtype, uint8_t offset) { 484 | VM_SOURCE_BODY(vm_read32, SIZE32, uint32_t, false, offset) 485 | } 486 | 487 | #define VM_TARGET_BODY(_vm_write, _localvalue, _offset) \ 488 | uint32_t pointer_base = vm->pointer_instr_mut; \ 489 | switch (prtype) { \ 490 | case TY_REG: { \ 491 | vm->pointer_instr_mut += SIZE8; \ 492 | uint8_t local = vm_read8(vm, pointer_base); \ 493 | *vm_findlocal(vm, local) = _localvalue; \ 494 | return; \ 495 | }; \ 496 | case TY_REGPTR: { \ 497 | vm->pointer_instr_mut += SIZE8+_offset; \ 498 | _vm_write(vm, ( _offset ? vm_read8(vm, pointer_base + 1) : 0) + \ 499 | *vm_findlocal(vm, vm_read8(vm, pointer_base)), value); \ 500 | return; \ 501 | }; \ 502 | case TY_IMM: { \ 503 | vm_panic(vm, FOX32_ERR_BADIMMEDIATE); \ 504 | return; \ 505 | }; \ 506 | case TY_IMMPTR: { \ 507 | vm->pointer_instr_mut += SIZE32; \ 508 | _vm_write(vm, vm_read32(vm, pointer_base), value); \ 509 | return; \ 510 | }; \ 511 | }; \ 512 | vm_unreachable(vm); 513 | 514 | static void vm_target8(vm_t *vm, uint8_t prtype, uint8_t value, uint8_t offset) { 515 | VM_TARGET_BODY(vm_write8, (*vm_findlocal(vm, local) & 0xFFFFFF00) | (uint32_t) value, offset) 516 | } 517 | static void vm_target8_zero(vm_t *vm, uint8_t prtype, uint8_t value, uint8_t offset) { 518 | VM_TARGET_BODY(vm_write32, (uint32_t) value, offset) 519 | } 520 | static void vm_target16(vm_t *vm, uint8_t prtype, uint16_t value, uint8_t offset) { 521 | VM_TARGET_BODY(vm_write16, (*vm_findlocal(vm, local) & 0xFFFF0000) | (uint32_t) value, offset) 522 | } 523 | static void vm_target16_zero(vm_t *vm, uint8_t prtype, uint16_t value, uint8_t offset) { 524 | VM_TARGET_BODY(vm_write32, (uint32_t) value, offset) 525 | } 526 | static void vm_target32(vm_t *vm, uint8_t prtype, uint32_t value, uint8_t offset) { 527 | VM_TARGET_BODY(vm_write32, value, offset) 528 | } 529 | 530 | static bool vm_shouldskip(vm_t *vm, uint8_t condition) { 531 | switch (condition) { 532 | case CD_ALWAYS: { 533 | return false; 534 | }; 535 | case CD_IFZ: { 536 | return vm->flag_zero == false; 537 | }; 538 | case CD_IFNZ: { 539 | return vm->flag_zero == true; 540 | }; 541 | case CD_IFC: { 542 | return vm->flag_carry == false; 543 | }; 544 | case CD_IFNC: { 545 | return vm->flag_carry == true; 546 | }; 547 | case CD_IFGT: { 548 | return (vm->flag_zero == true) || (vm->flag_carry == true); 549 | }; 550 | case CD_IFLTEQ: { 551 | return (vm->flag_zero == false) && (vm->flag_carry == false); 552 | }; 553 | } 554 | vm_panic(vm, FOX32_ERR_BADCONDITION); 555 | } 556 | 557 | static void vm_skipparam(vm_t *vm, uint32_t size, uint8_t prtype, uint8_t offset) { 558 | if (prtype < TY_IMM) { 559 | vm->pointer_instr_mut += SIZE8; 560 | if (offset && prtype==TY_REGPTR) 561 | vm->pointer_instr_mut += SIZE8; 562 | } else if (prtype == TY_IMMPTR) { 563 | vm->pointer_instr_mut += SIZE32; 564 | } else { 565 | vm->pointer_instr_mut += size; 566 | } 567 | } 568 | 569 | #define CHECKED_ADD(_a, _b, _out) __builtin_add_overflow(_a, _b, _out) 570 | #define CHECKED_SUB(_a, _b, _out) __builtin_sub_overflow(_a, _b, _out) 571 | #define CHECKED_MUL(_a, _b, _out) __builtin_mul_overflow(_a, _b, _out) 572 | 573 | #define OPER_DIV(_a, _b) ((_a) / (_b)) 574 | #define OPER_REM(_a, _b) ((_a) % (_b)) 575 | #define OPER_AND(_a, _b) ((_a) & (_b)) 576 | #define OPER_XOR(_a, _b) ((_a) ^ (_b)) 577 | #define OPER_OR(_a, _b) ((_a) | (_b)) 578 | #define OPER_SHIFT_LEFT(_a, _b) ((_a) << (_b)) 579 | #define OPER_SHIFT_RIGHT(_a, _b) ((_a) >> (_b)) 580 | #define OPER_BIT_SET(_a, _b) ((_a) | (1 << (_b))) 581 | #define OPER_BIT_CLEAR(_a, _b) ((_a) & ~(1 << (_b))) 582 | 583 | #define ROTATE_LEFT(_size, _a, _b) (((_a) << (_b)) | ((_a) >> (((_size) * 8) - (_b)))) 584 | #define ROTATE_LEFT8(_a, _b) ROTATE_LEFT(SIZE8, _a, _b) 585 | #define ROTATE_LEFT16(_a, _b) ROTATE_LEFT(SIZE16, _a, _b) 586 | #define ROTATE_LEFT32(_a, _b) ROTATE_LEFT(SIZE32, _a, _b) 587 | #define ROTATE_RIGHT(_size, _a, _b) (((_a) >> (_b)) | ((_a) << (((_size) * 8) - (_b)))) 588 | #define ROTATE_RIGHT8(_a, _b) ROTATE_RIGHT(SIZE8, _a, _b) 589 | #define ROTATE_RIGHT16(_a, _b) ROTATE_RIGHT(SIZE16, _a, _b) 590 | #define ROTATE_RIGHT32(_a, _b) ROTATE_RIGHT(SIZE32, _a, _b) 591 | 592 | #define SOURCEMAP_IDENTITY(x) (x) 593 | #define SOURCEMAP_RELATIVE(x) (instr_base + (x)) 594 | 595 | #define VM_PRELUDE_0() { \ 596 | if (vm_shouldskip(vm, instr.condition)) { \ 597 | break; \ 598 | } \ 599 | } 600 | #define VM_PRELUDE_1(_size) { \ 601 | if (vm_shouldskip(vm, instr.condition)) { \ 602 | vm_skipparam(vm, _size, instr.source, instr.offset); \ 603 | break; \ 604 | } \ 605 | } 606 | #define VM_PRELUDE_2(_size) { \ 607 | if (vm_shouldskip(vm, instr.condition)) { \ 608 | vm_skipparam(vm, _size, instr.target, instr.offset); \ 609 | vm_skipparam(vm, _size, instr.source, instr.offset); \ 610 | break; \ 611 | } \ 612 | } 613 | #define VM_PRELUDE_BIT(_size) { \ 614 | if (vm_shouldskip(vm, instr.condition)) { \ 615 | vm_skipparam(vm, _size, instr.target, instr.offset); \ 616 | vm_skipparam(vm, SIZE8, instr.source, instr.offset); \ 617 | break; \ 618 | } \ 619 | } 620 | 621 | #define VM_IMPL_JMP(_size, _sourcemap) { \ 622 | VM_PRELUDE_1(_size); \ 623 | switch (_size) { \ 624 | case SIZE8: vm->pointer_instr_mut = _sourcemap((int8_t)vm_source8(vm, instr.source, instr.offset)); break; \ 625 | case SIZE16: vm->pointer_instr_mut = _sourcemap((int16_t)vm_source16(vm, instr.source, instr.offset)); break; \ 626 | default: vm->pointer_instr_mut = _sourcemap(vm_source32(vm, instr.source, instr.offset)); break; \ 627 | } \ 628 | break; \ 629 | } 630 | 631 | #define VM_IMPL_LOOP(_size, _sourcemap) { \ 632 | if ( \ 633 | !vm_shouldskip(vm, instr.condition) && \ 634 | (vm->registers[FOX32_REGISTER_LOOP] -= 1) != 0 \ 635 | ) { \ 636 | switch (_size) { \ 637 | case SIZE8: vm->pointer_instr_mut = _sourcemap((int8_t)vm_source8(vm, instr.source, instr.offset)); break; \ 638 | case SIZE16: vm->pointer_instr_mut = _sourcemap((int16_t)vm_source16(vm, instr.source, instr.offset)); break; \ 639 | default: vm->pointer_instr_mut = _sourcemap(vm_source32(vm, instr.source, instr.offset)); break; \ 640 | } \ 641 | } else { \ 642 | vm_skipparam(vm, _size, instr.source, instr.offset); \ 643 | } \ 644 | break; \ 645 | } 646 | 647 | #define VM_IMPL_CALL(_size, _sourcemap) { \ 648 | VM_PRELUDE_1(_size); \ 649 | uint32_t pointer_call; \ 650 | switch (_size) { \ 651 | case SIZE8: pointer_call = (int8_t)vm_source8(vm, instr.source, instr.offset); break; \ 652 | case SIZE16: pointer_call = (int16_t)vm_source16(vm, instr.source, instr.offset); break; \ 653 | default: pointer_call = vm_source32(vm, instr.source, instr.offset); break; \ 654 | } \ 655 | vm_push32(vm, vm->pointer_instr_mut); \ 656 | switch (_size) { \ 657 | case SIZE8: vm->pointer_instr_mut = _sourcemap((int8_t)pointer_call); break; \ 658 | case SIZE16: vm->pointer_instr_mut = _sourcemap((int16_t)pointer_call); break; \ 659 | default: vm->pointer_instr_mut = _sourcemap(pointer_call); break; \ 660 | } \ 661 | break; \ 662 | } 663 | 664 | // make sure NOT to update the stack pointer until the full instruction has 665 | // been read, and the target has been written. otherwise a pagefault halfway 666 | // through could wreak havoc. 667 | 668 | #define VM_IMPL_POP(_size, _vm_target, _vm_pop) { \ 669 | VM_PRELUDE_1(_size); \ 670 | uint32_t oldsp = vm->pointer_stack; \ 671 | uint32_t val = _vm_pop(vm); \ 672 | uint32_t newsp = vm->pointer_stack; \ 673 | vm->pointer_stack = oldsp; \ 674 | _vm_target(vm, instr.source, val, instr.offset); \ 675 | vm->pointer_stack = newsp; \ 676 | break; \ 677 | } 678 | 679 | #define VM_IMPL_PUSH(_size, _vm_source, _vm_push) { \ 680 | VM_PRELUDE_1(_size); \ 681 | _vm_push(vm, _vm_source(vm, instr.source, instr.offset)); \ 682 | break; \ 683 | } 684 | 685 | #define VM_IMPL_MOV(_size, _vm_source, _vm_target) { \ 686 | VM_PRELUDE_2(_size); \ 687 | _vm_target(vm, instr.target, _vm_source(vm, instr.source, instr.offset), instr.offset); \ 688 | break; \ 689 | } 690 | 691 | #define VM_IMPL_NOT(_size, _type, _vm_source_stay, _vm_target) { \ 692 | VM_PRELUDE_1(_size); \ 693 | _type v = _vm_source_stay(vm, instr.source, instr.offset); \ 694 | _type x = ~v; \ 695 | _vm_target(vm, instr.source, x, instr.offset); \ 696 | vm->flag_zero = x == 0; \ 697 | break; \ 698 | } 699 | 700 | #define VM_IMPL_INC(_size, _type, _vm_source_stay, _vm_target, _oper) { \ 701 | VM_PRELUDE_1(_size); \ 702 | _type v = _vm_source_stay(vm, instr.source, instr.offset); \ 703 | _type x; \ 704 | bool carry = _oper(v, 1 << instr.target, &x); \ 705 | _vm_target(vm, instr.source, x, instr.offset); \ 706 | vm->flag_carry = carry; \ 707 | vm->flag_zero = x == 0; \ 708 | break; \ 709 | } 710 | 711 | #define VM_IMPL_ADD(_size, _type, _type_target, _vm_source, _vm_source_stay, _vm_target, _oper) { \ 712 | VM_PRELUDE_2(_size); \ 713 | _type a = (_type) _vm_source(vm, instr.source, instr.offset); \ 714 | _type b = (_type) _vm_source_stay(vm, instr.target, instr.offset); \ 715 | _type x; \ 716 | bool carry = _oper(b, a, &x); \ 717 | _vm_target(vm, instr.target, (_type_target) x, instr.offset); \ 718 | vm->flag_carry = carry; \ 719 | vm->flag_zero = x == 0; \ 720 | break; \ 721 | } 722 | 723 | #define VM_IMPL_AND(_size, _type, _type_target, _vm_source, _vm_source_stay, _vm_target, _oper) { \ 724 | VM_PRELUDE_2(_size); \ 725 | _type a = (_type) _vm_source(vm, instr.source, instr.offset); \ 726 | _type b = (_type) _vm_source_stay(vm, instr.target, instr.offset); \ 727 | _type x = _oper(b, a); \ 728 | _vm_target(vm, instr.target, (_type_target) x, instr.offset); \ 729 | vm->flag_zero = x == 0; \ 730 | break; \ 731 | } 732 | 733 | #define VM_IMPL_SHIFT(_size, _type, _type_target, _vm_source, _vm_source_stay, _vm_target, _oper){\ 734 | VM_PRELUDE_BIT(_size); \ 735 | _type a = (_type) vm_source8(vm, instr.source, instr.offset); \ 736 | _type b = (_type) _vm_source_stay(vm, instr.target, instr.offset); \ 737 | _type x = _oper(b, a); \ 738 | _vm_target(vm, instr.target, (_type_target) x, instr.offset); \ 739 | vm->flag_zero = x == 0; \ 740 | break; \ 741 | } 742 | 743 | #define VM_IMPL_DIV(_size, _type, _type_target, _vm_source, _vm_source_stay, _vm_target, _oper) { \ 744 | VM_PRELUDE_2(_size); \ 745 | _type a = (_type) _vm_source(vm, instr.source, instr.offset); \ 746 | _type b = (_type) _vm_source_stay(vm, instr.target, instr.offset); \ 747 | if (a == 0) { \ 748 | vm_panic(vm, FOX32_ERR_DIVZERO); \ 749 | break; \ 750 | } \ 751 | _type x = _oper(b, a); \ 752 | _vm_target(vm, instr.target, (_type_target) x, instr.offset); \ 753 | vm->flag_zero = x == 0; \ 754 | break; \ 755 | } 756 | 757 | #define VM_IMPL_CMP(_size, _type, _vm_source) { \ 758 | VM_PRELUDE_2(_size); \ 759 | _type a = _vm_source(vm, instr.source, instr.offset); \ 760 | _type b = _vm_source(vm, instr.target, instr.offset); \ 761 | vm->flag_carry = a > b; \ 762 | vm->flag_zero = a == b; \ 763 | break; \ 764 | } 765 | 766 | #define VM_IMPL_BTS(_size, _type, _vm_source) { \ 767 | VM_PRELUDE_BIT(_size); \ 768 | _type a = vm_source8(vm, instr.source, instr.offset); \ 769 | _type b = _vm_source(vm, instr.target, instr.offset); \ 770 | _type x = b & (1 << a); \ 771 | vm->flag_zero = x == 0; \ 772 | break; \ 773 | } 774 | 775 | static void vm_execute(vm_t *vm) { 776 | uint32_t instr_base = vm->pointer_instr; 777 | uint16_t instr_raw = vm_read16(vm, instr_base); 778 | 779 | asm_instr_t instr = asm_instr_from(instr_raw); 780 | 781 | vm->pointer_instr_mut = instr_base + SIZE16; 782 | 783 | //if (vm->debug) vm_debug(vm, instr, instr_base, vm->pointer_stack); 784 | 785 | switch (instr.opcode) { 786 | case OP(SZ_BYTE, OP_NOP): 787 | case OP(SZ_HALF, OP_NOP): 788 | case OP(SZ_WORD, OP_NOP): { 789 | break; 790 | }; 791 | 792 | case OP(SZ_BYTE, OP_HALT): 793 | case OP(SZ_HALF, OP_HALT): 794 | case OP(SZ_WORD, OP_HALT): { 795 | VM_PRELUDE_0(); 796 | vm->soft_halted = true; 797 | break; 798 | }; 799 | 800 | case OP(SZ_BYTE, OP_BRK): 801 | case OP(SZ_HALF, OP_BRK): 802 | case OP(SZ_WORD, OP_BRK): { 803 | VM_PRELUDE_0(); 804 | vm->pointer_instr = vm->pointer_instr_mut; 805 | vm_panic(vm, FOX32_ERR_DEBUGGER); 806 | break; 807 | }; 808 | 809 | case OP(SZ_WORD, OP_IN): { 810 | VM_PRELUDE_2(SIZE32); 811 | vm_target32(vm, instr.target, vm_io_read(vm, vm_source32(vm, instr.source, 0)), instr.offset); 812 | break; 813 | }; 814 | case OP(SZ_WORD, OP_OUT): { 815 | VM_PRELUDE_2(SIZE32); 816 | uint32_t value = vm_source32(vm, instr.source, instr.offset); 817 | uint32_t port = vm_source32(vm, instr.target, instr.offset); 818 | vm_io_write(vm, port, value); 819 | break; 820 | }; 821 | 822 | case OP(SZ_BYTE, OP_RTA): { 823 | VM_PRELUDE_2(SIZE8); 824 | vm_target32(vm, instr.target, instr_base + (int8_t)vm_source8(vm, instr.source, instr.offset), instr.offset); 825 | break; 826 | }; 827 | case OP(SZ_HALF, OP_RTA): { 828 | VM_PRELUDE_2(SIZE16); 829 | vm_target32(vm, instr.target, instr_base + (int16_t)vm_source16(vm, instr.source, instr.offset), instr.offset); 830 | break; 831 | }; 832 | case OP(SZ_WORD, OP_RTA): { 833 | VM_PRELUDE_2(SIZE32); 834 | vm_target32(vm, instr.target, instr_base + vm_source32(vm, instr.source, instr.offset), instr.offset); 835 | break; 836 | }; 837 | 838 | case OP(SZ_WORD, OP_RET): { 839 | VM_PRELUDE_0(); 840 | vm->pointer_instr_mut = vm_pop32(vm); 841 | break; 842 | }; 843 | case OP(SZ_WORD, OP_RETI): { 844 | VM_PRELUDE_0(); 845 | vm_flags_set(vm, vm_pop8(vm)); 846 | vm->pointer_instr_mut = vm_pop32(vm); 847 | if (vm->flag_swap_sp) { 848 | vm->pointer_stack = vm_pop32(vm); 849 | } 850 | break; 851 | }; 852 | 853 | case OP(SZ_WORD, OP_ISE): { 854 | VM_PRELUDE_0(); 855 | vm->flag_interrupt = true; 856 | break; 857 | }; 858 | case OP(SZ_WORD, OP_ICL): { 859 | VM_PRELUDE_0(); 860 | vm->flag_interrupt = false; 861 | break; 862 | }; 863 | 864 | case OP(SZ_WORD, OP_JMP): VM_IMPL_JMP(SIZE32, SOURCEMAP_IDENTITY); 865 | case OP(SZ_WORD, OP_CALL): VM_IMPL_CALL(SIZE32, SOURCEMAP_IDENTITY); 866 | case OP(SZ_WORD, OP_LOOP): VM_IMPL_LOOP(SIZE32, SOURCEMAP_IDENTITY); 867 | 868 | case OP(SZ_BYTE, OP_RJMP): VM_IMPL_JMP(SIZE8, SOURCEMAP_RELATIVE); 869 | case OP(SZ_BYTE, OP_RCALL): VM_IMPL_CALL(SIZE8, SOURCEMAP_RELATIVE); 870 | case OP(SZ_BYTE, OP_RLOOP): VM_IMPL_LOOP(SIZE8, SOURCEMAP_RELATIVE); 871 | case OP(SZ_HALF, OP_RJMP): VM_IMPL_JMP(SIZE16, SOURCEMAP_RELATIVE); 872 | case OP(SZ_HALF, OP_RCALL): VM_IMPL_CALL(SIZE16, SOURCEMAP_RELATIVE); 873 | case OP(SZ_HALF, OP_RLOOP): VM_IMPL_LOOP(SIZE16, SOURCEMAP_RELATIVE); 874 | case OP(SZ_WORD, OP_RJMP): VM_IMPL_JMP(SIZE32, SOURCEMAP_RELATIVE); 875 | case OP(SZ_WORD, OP_RCALL): VM_IMPL_CALL(SIZE32, SOURCEMAP_RELATIVE); 876 | case OP(SZ_WORD, OP_RLOOP): VM_IMPL_LOOP(SIZE32, SOURCEMAP_RELATIVE); 877 | 878 | case OP(SZ_BYTE, OP_POP): VM_IMPL_POP(SIZE8, vm_target8, vm_pop8); 879 | case OP(SZ_HALF, OP_POP): VM_IMPL_POP(SIZE16, vm_target16, vm_pop16); 880 | case OP(SZ_WORD, OP_POP): VM_IMPL_POP(SIZE32, vm_target32, vm_pop32); 881 | 882 | case OP(SZ_BYTE, OP_PUSH): VM_IMPL_PUSH(SIZE8, vm_source8, vm_push8); 883 | case OP(SZ_HALF, OP_PUSH): VM_IMPL_PUSH(SIZE16, vm_source16, vm_push16); 884 | case OP(SZ_WORD, OP_PUSH): VM_IMPL_PUSH(SIZE32, vm_source32, vm_push32); 885 | 886 | case OP(SZ_BYTE, OP_MOV): VM_IMPL_MOV(SIZE8, vm_source8, vm_target8); 887 | case OP(SZ_BYTE, OP_MOVZ): VM_IMPL_MOV(SIZE8, vm_source8, vm_target8_zero); 888 | case OP(SZ_HALF, OP_MOV): VM_IMPL_MOV(SIZE16, vm_source16, vm_target16); 889 | case OP(SZ_HALF, OP_MOVZ): VM_IMPL_MOV(SIZE16, vm_source16, vm_target16_zero); 890 | case OP(SZ_WORD, OP_MOV): 891 | case OP(SZ_WORD, OP_MOVZ): VM_IMPL_MOV(SIZE32, vm_source32, vm_target32); 892 | 893 | case OP(SZ_BYTE, OP_NOT): VM_IMPL_NOT(SIZE8, uint8_t, vm_source8_stay, vm_target8); 894 | case OP(SZ_HALF, OP_NOT): VM_IMPL_NOT(SIZE16, uint16_t, vm_source16_stay, vm_target16); 895 | case OP(SZ_WORD, OP_NOT): VM_IMPL_NOT(SIZE32, uint32_t, vm_source32_stay, vm_target32); 896 | 897 | case OP(SZ_BYTE, OP_INC): VM_IMPL_INC(SIZE8, uint8_t, vm_source8_stay, vm_target8, CHECKED_ADD); 898 | case OP(SZ_HALF, OP_INC): VM_IMPL_INC(SIZE16, uint16_t, vm_source16_stay, vm_target16, CHECKED_ADD); 899 | case OP(SZ_WORD, OP_INC): VM_IMPL_INC(SIZE32, uint32_t, vm_source32_stay, vm_target32, CHECKED_ADD); 900 | case OP(SZ_BYTE, OP_DEC): VM_IMPL_INC(SIZE8, uint8_t, vm_source8_stay, vm_target8, CHECKED_SUB); 901 | case OP(SZ_HALF, OP_DEC): VM_IMPL_INC(SIZE16, uint16_t, vm_source16_stay, vm_target16, CHECKED_SUB); 902 | case OP(SZ_WORD, OP_DEC): VM_IMPL_INC(SIZE32, uint32_t, vm_source32_stay, vm_target32, CHECKED_SUB); 903 | 904 | case OP(SZ_BYTE, OP_ADD): VM_IMPL_ADD(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, CHECKED_ADD); 905 | case OP(SZ_HALF, OP_ADD): VM_IMPL_ADD(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, CHECKED_ADD); 906 | case OP(SZ_WORD, OP_ADD): VM_IMPL_ADD(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, CHECKED_ADD); 907 | case OP(SZ_BYTE, OP_SUB): VM_IMPL_ADD(SIZE8, uint8_t, uint8_t ,vm_source8, vm_source8_stay, vm_target8, CHECKED_SUB); 908 | case OP(SZ_HALF, OP_SUB): VM_IMPL_ADD(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, CHECKED_SUB); 909 | case OP(SZ_WORD, OP_SUB): VM_IMPL_ADD(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, CHECKED_SUB); 910 | case OP(SZ_BYTE, OP_MUL): VM_IMPL_ADD(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, CHECKED_MUL); 911 | case OP(SZ_HALF, OP_MUL): VM_IMPL_ADD(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, CHECKED_MUL); 912 | case OP(SZ_WORD, OP_MUL): VM_IMPL_ADD(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, CHECKED_MUL); 913 | case OP(SZ_BYTE, OP_IMUL): VM_IMPL_ADD(SIZE8, int8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, CHECKED_MUL); 914 | case OP(SZ_HALF, OP_IMUL): VM_IMPL_ADD(SIZE16, int16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, CHECKED_MUL); 915 | case OP(SZ_WORD, OP_IMUL): VM_IMPL_ADD(SIZE32, int32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, CHECKED_MUL); 916 | 917 | case OP(SZ_BYTE, OP_DIV): VM_IMPL_DIV(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_DIV); 918 | case OP(SZ_HALF, OP_DIV): VM_IMPL_DIV(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_DIV); 919 | case OP(SZ_WORD, OP_DIV): VM_IMPL_DIV(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_DIV); 920 | case OP(SZ_BYTE, OP_REM): VM_IMPL_DIV(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_REM); 921 | case OP(SZ_HALF, OP_REM): VM_IMPL_DIV(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_REM); 922 | case OP(SZ_WORD, OP_REM): VM_IMPL_DIV(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_REM); 923 | case OP(SZ_BYTE, OP_IDIV): VM_IMPL_DIV(SIZE8, int8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_DIV); 924 | case OP(SZ_HALF, OP_IDIV): VM_IMPL_DIV(SIZE16, int16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_DIV); 925 | case OP(SZ_WORD, OP_IDIV): VM_IMPL_DIV(SIZE32, int32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_DIV); 926 | case OP(SZ_BYTE, OP_IREM): VM_IMPL_DIV(SIZE8, int8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_REM); 927 | case OP(SZ_HALF, OP_IREM): VM_IMPL_DIV(SIZE16, int16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_REM); 928 | case OP(SZ_WORD, OP_IREM): VM_IMPL_DIV(SIZE32, int32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_REM); 929 | 930 | case OP(SZ_BYTE, OP_AND): VM_IMPL_AND(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_AND); 931 | case OP(SZ_HALF, OP_AND): VM_IMPL_AND(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_AND); 932 | case OP(SZ_WORD, OP_AND): VM_IMPL_AND(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_AND); 933 | case OP(SZ_BYTE, OP_XOR): VM_IMPL_AND(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_XOR); 934 | case OP(SZ_HALF, OP_XOR): VM_IMPL_AND(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_XOR); 935 | case OP(SZ_WORD, OP_XOR): VM_IMPL_AND(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_XOR); 936 | case OP(SZ_BYTE, OP_OR): VM_IMPL_AND(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_OR); 937 | case OP(SZ_HALF, OP_OR): VM_IMPL_AND(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_OR); 938 | case OP(SZ_WORD, OP_OR): VM_IMPL_AND(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_OR); 939 | 940 | case OP(SZ_BYTE, OP_SLA): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_SHIFT_LEFT); 941 | case OP(SZ_HALF, OP_SLA): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_SHIFT_LEFT); 942 | case OP(SZ_WORD, OP_SLA): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_SHIFT_LEFT); 943 | case OP(SZ_BYTE, OP_SRL): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_SHIFT_RIGHT); 944 | case OP(SZ_HALF, OP_SRL): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_SHIFT_RIGHT); 945 | case OP(SZ_WORD, OP_SRL): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_SHIFT_RIGHT); 946 | case OP(SZ_BYTE, OP_SRA): VM_IMPL_SHIFT(SIZE8, int8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_SHIFT_RIGHT); 947 | case OP(SZ_HALF, OP_SRA): VM_IMPL_SHIFT(SIZE16, int16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_SHIFT_RIGHT); 948 | case OP(SZ_WORD, OP_SRA): VM_IMPL_SHIFT(SIZE32, int32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_SHIFT_RIGHT); 949 | 950 | case OP(SZ_BYTE, OP_ROL): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, ROTATE_LEFT8); 951 | case OP(SZ_HALF, OP_ROL): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, ROTATE_LEFT16); 952 | case OP(SZ_WORD, OP_ROL): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, ROTATE_LEFT32); 953 | case OP(SZ_BYTE, OP_ROR): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, ROTATE_RIGHT8); 954 | case OP(SZ_HALF, OP_ROR): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, ROTATE_RIGHT16); 955 | case OP(SZ_WORD, OP_ROR): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, ROTATE_RIGHT32); 956 | 957 | case OP(SZ_BYTE, OP_BSE): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_BIT_SET); 958 | case OP(SZ_HALF, OP_BSE): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_BIT_SET); 959 | case OP(SZ_WORD, OP_BSE): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_BIT_SET); 960 | case OP(SZ_BYTE, OP_BCL): VM_IMPL_SHIFT(SIZE8, uint8_t, uint8_t, vm_source8, vm_source8_stay, vm_target8, OPER_BIT_CLEAR); 961 | case OP(SZ_HALF, OP_BCL): VM_IMPL_SHIFT(SIZE16, uint16_t, uint16_t, vm_source16, vm_source16_stay, vm_target16, OPER_BIT_CLEAR); 962 | case OP(SZ_WORD, OP_BCL): VM_IMPL_SHIFT(SIZE32, uint32_t, uint32_t, vm_source32, vm_source32_stay, vm_target32, OPER_BIT_CLEAR); 963 | 964 | case OP(SZ_BYTE, OP_CMP): VM_IMPL_CMP(SIZE8, uint8_t, vm_source8); 965 | case OP(SZ_HALF, OP_CMP): VM_IMPL_CMP(SIZE16, uint16_t, vm_source16); 966 | case OP(SZ_WORD, OP_CMP): VM_IMPL_CMP(SIZE32, uint32_t, vm_source32); 967 | case OP(SZ_BYTE, OP_ICMP): VM_IMPL_CMP(SIZE8, int8_t, vm_source8); 968 | case OP(SZ_HALF, OP_ICMP): VM_IMPL_CMP(SIZE16, int16_t, vm_source16); 969 | case OP(SZ_WORD, OP_ICMP): VM_IMPL_CMP(SIZE32, int32_t, vm_source32); 970 | 971 | case OP(SZ_BYTE, OP_BTS): VM_IMPL_BTS(SIZE8, uint8_t, vm_source8); 972 | case OP(SZ_HALF, OP_BTS): VM_IMPL_BTS(SIZE16, uint16_t, vm_source16); 973 | case OP(SZ_WORD, OP_BTS): VM_IMPL_BTS(SIZE32, uint32_t, vm_source32); 974 | 975 | case OP(SZ_WORD, OP_MSE): { 976 | VM_PRELUDE_0(); 977 | vm->mmu_enabled = true; 978 | break; 979 | }; 980 | case OP(SZ_WORD, OP_MCL): { 981 | VM_PRELUDE_0(); 982 | vm->mmu_enabled = false; 983 | break; 984 | }; 985 | case OP(SZ_WORD, OP_INT): { 986 | VM_PRELUDE_1(SIZE32); 987 | uint32_t intr = vm_source32(vm, instr.source, instr.offset); 988 | vm->pointer_instr = vm->pointer_instr_mut; 989 | fox32_raise(vm, intr); 990 | vm->pointer_instr_mut = vm->pointer_instr; 991 | break; 992 | }; 993 | case OP(SZ_WORD, OP_TLB): { 994 | VM_PRELUDE_1(SIZE32); 995 | set_and_flush_tlb(vm_source32(vm, instr.source, instr.offset)); 996 | break; 997 | }; 998 | case OP(SZ_WORD, OP_FLP): { 999 | VM_PRELUDE_1(SIZE32); 1000 | flush_single_page(vm_source32(vm, instr.source, instr.offset)); 1001 | break; 1002 | }; 1003 | 1004 | default: 1005 | vm_panic(vm, FOX32_ERR_BADOPCODE); 1006 | } 1007 | 1008 | vm->pointer_instr = vm->pointer_instr_mut; 1009 | } 1010 | 1011 | static err_t vm_step(vm_t *vm) { 1012 | if (__builtin_setjmp(vm->panic_jmp) != 0) { 1013 | return vm->halted = true, vm->panic_err; 1014 | } 1015 | vm_execute(vm); 1016 | return FOX32_ERR_OK; 1017 | } 1018 | static err_t vm_resume(vm_t *vm, uint32_t count, uint32_t *executed) { 1019 | if (__builtin_setjmp(vm->panic_jmp) != 0) { 1020 | return vm->halted = true, vm->panic_err; 1021 | } 1022 | 1023 | vm->halted = false; 1024 | 1025 | uint32_t remaining = count; 1026 | while (!vm->halted && !vm->soft_halted && remaining > 0) { 1027 | vm_execute(vm); 1028 | remaining -= 1; 1029 | *executed += 1; 1030 | } 1031 | 1032 | if (vm->soft_halted) { 1033 | *executed = count; 1034 | } 1035 | 1036 | return FOX32_ERR_OK; 1037 | } 1038 | 1039 | static fox32_err_t vm_raise(vm_t *vm, uint16_t vector) { 1040 | if (!vm->flag_interrupt && vector < 256) { 1041 | return FOX32_ERR_NOINTERRUPTS; 1042 | } 1043 | if (__builtin_setjmp(vm->panic_jmp) != 0) { 1044 | return vm->panic_err; 1045 | } 1046 | 1047 | uint32_t pointer_handler = 1048 | vm->memory_ram[SIZE32 * (uint32_t) vector] | 1049 | vm->memory_ram[SIZE32 * (uint32_t) vector + 1] << 8 | 1050 | vm->memory_ram[SIZE32 * (uint32_t) vector + 2] << 16 | 1051 | vm->memory_ram[SIZE32 * (uint32_t) vector + 3] << 24; 1052 | 1053 | if (vm->flag_swap_sp) { 1054 | uint32_t old_stack_pointer = vm->pointer_stack; 1055 | vm->pointer_stack = vm->pointer_exception_stack; 1056 | vm_push32(vm, old_stack_pointer); 1057 | vm_push32(vm, vm->pointer_instr); 1058 | vm_push8(vm, vm_flags_get(vm)); 1059 | vm->flag_swap_sp = false; 1060 | } else { 1061 | vm_push32(vm, vm->pointer_instr); 1062 | vm_push8(vm, vm_flags_get(vm)); 1063 | } 1064 | 1065 | if (vector >= 256) { 1066 | // if this is an exception, push the operand 1067 | vm_push32(vm, vm->exception_operand); 1068 | vm->exception_operand = 0; 1069 | } else { 1070 | // if this is an interrupt, push the vector 1071 | vm_push32(vm, (uint32_t) vector); 1072 | } 1073 | 1074 | vm->pointer_instr = pointer_handler; 1075 | vm->halted = true; 1076 | vm->soft_halted = false; 1077 | vm->flag_interrupt = false; 1078 | 1079 | return FOX32_ERR_OK; 1080 | } 1081 | 1082 | static fox32_err_t vm_recover(vm_t *vm, err_t err) { 1083 | switch (err) { 1084 | case FOX32_ERR_DEBUGGER: 1085 | return vm_raise(vm, EX_DEBUGGER); 1086 | case FOX32_ERR_FAULT_RD: 1087 | return vm_raise(vm, EX_FAULT_RD); 1088 | case FOX32_ERR_FAULT_WR: 1089 | return vm_raise(vm, EX_FAULT_WR); 1090 | case FOX32_ERR_BADOPCODE: 1091 | case FOX32_ERR_BADCONDITION: 1092 | case FOX32_ERR_BADREGISTER: 1093 | case FOX32_ERR_BADIMMEDIATE: 1094 | return vm_raise(vm, EX_ILLEGAL); 1095 | case FOX32_ERR_DIVZERO: 1096 | return vm_raise(vm, EX_DIVZERO); 1097 | case FOX32_ERR_IOREAD: 1098 | case FOX32_ERR_IOWRITE: 1099 | return vm_raise(vm, EX_BUS); 1100 | default: 1101 | return FOX32_ERR_CANTRECOVER; 1102 | } 1103 | } 1104 | 1105 | #define VM_SAFEPUSH_BODY(_vm_push) \ 1106 | if (__builtin_setjmp(vm->panic_jmp) != 0) { \ 1107 | return vm->panic_err; \ 1108 | } \ 1109 | _vm_push(vm, value); \ 1110 | return FOX32_ERR_OK; 1111 | 1112 | static fox32_err_t vm_safepush_byte(vm_t *vm, uint8_t value) { 1113 | VM_SAFEPUSH_BODY(vm_push8) 1114 | } 1115 | static fox32_err_t vm_safepush_half(vm_t *vm, uint16_t value) { 1116 | VM_SAFEPUSH_BODY(vm_push16) 1117 | } 1118 | static fox32_err_t vm_safepush_word(vm_t *vm, uint32_t value) { 1119 | VM_SAFEPUSH_BODY(vm_push32) 1120 | } 1121 | 1122 | #define VM_SAFEPOP_BODY(_vm_pop) \ 1123 | *value = 0; \ 1124 | if (__builtin_setjmp(vm->panic_jmp) != 0) { \ 1125 | return vm->panic_err; \ 1126 | } \ 1127 | *value = _vm_pop(vm); \ 1128 | return FOX32_ERR_OK; 1129 | 1130 | static fox32_err_t vm_safepop_byte(vm_t *vm, uint8_t *value) { 1131 | VM_SAFEPOP_BODY(vm_pop8) 1132 | } 1133 | static fox32_err_t vm_safepop_half(vm_t *vm, uint16_t *value) { 1134 | VM_SAFEPOP_BODY(vm_pop16) 1135 | } 1136 | static fox32_err_t vm_safepop_word(vm_t *vm, uint32_t *value) { 1137 | VM_SAFEPOP_BODY(vm_pop32) 1138 | } 1139 | 1140 | const char *fox32_strerr(fox32_err_t err) { 1141 | return err_tostring(err); 1142 | } 1143 | void fox32_init(fox32_vm_t *vm) { 1144 | vm_init(vm); 1145 | } 1146 | fox32_err_t fox32_step(fox32_vm_t *vm) { 1147 | return vm_step(vm); 1148 | } 1149 | fox32_err_t fox32_resume(fox32_vm_t *vm, uint32_t count, uint32_t *executed) { 1150 | return vm_resume(vm, count, executed); 1151 | } 1152 | fox32_err_t fox32_raise(fox32_vm_t *vm, uint16_t vector) { 1153 | return vm_raise(vm, vector); 1154 | } 1155 | fox32_err_t fox32_recover(fox32_vm_t *vm, fox32_err_t err) { 1156 | return vm_recover(vm, err); 1157 | } 1158 | fox32_err_t fox32_push_byte(fox32_vm_t *vm, uint8_t value) { 1159 | return vm_safepush_byte(vm, value); 1160 | } 1161 | fox32_err_t fox32_push_half(fox32_vm_t *vm, uint16_t value) { 1162 | return vm_safepush_half(vm, value); 1163 | } 1164 | fox32_err_t fox32_push_word(fox32_vm_t *vm, uint32_t value) { 1165 | return vm_safepush_word(vm, value); 1166 | } 1167 | fox32_err_t fox32_pop_byte(fox32_vm_t *vm, uint8_t *value) { 1168 | return vm_safepop_byte(vm, value); 1169 | } 1170 | fox32_err_t fox32_pop_half(fox32_vm_t *vm, uint16_t *value) { 1171 | return vm_safepop_half(vm, value); 1172 | } 1173 | fox32_err_t fox32_pop_word(fox32_vm_t *vm, uint32_t *value) { 1174 | return vm_safepop_word(vm, value); 1175 | } 1176 | -------------------------------------------------------------------------------- /src/cpu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define FOX32_CPU_HZ 33000000 8 | 9 | #define FOX32_MEMORY_RAM 0x04000000 // 64 MiB 10 | #define FOX32_MEMORY_ROM 0x00080000 // 512 KiB 11 | #define FOX32_MEMORY_ROM_START 0xF0000000 12 | 13 | #define FOX32_POINTER_DEFAULT_INSTR FOX32_MEMORY_ROM_START 14 | #define FOX32_POINTER_DEFAULT_STACK 0x00000000 15 | 16 | #define FOX32_POINTER_INTERRUPTVECS 0x00000000 17 | 18 | #define FOX32_REGISTER_LOOP 31 19 | #define FOX32_REGISTER_COUNT 32 20 | 21 | typedef enum { 22 | FOX32_ERR_OK, 23 | FOX32_ERR_INTERNAL, 24 | FOX32_ERR_DEBUGGER, 25 | FOX32_ERR_FAULT_RD, 26 | FOX32_ERR_FAULT_WR, 27 | FOX32_ERR_BADOPCODE, 28 | FOX32_ERR_BADCONDITION, 29 | FOX32_ERR_BADREGISTER, 30 | FOX32_ERR_BADIMMEDIATE, 31 | FOX32_ERR_DIVZERO, 32 | FOX32_ERR_IOREAD, 33 | FOX32_ERR_IOWRITE, 34 | FOX32_ERR_NOINTERRUPTS, 35 | FOX32_ERR_CANTRECOVER 36 | } fox32_err_t; 37 | 38 | const char *fox32_strerr(fox32_err_t err); 39 | 40 | typedef int fox32_io_read_t(void *user, uint32_t *value, uint32_t port); 41 | typedef int fox32_io_write_t(void *user, uint32_t value, uint32_t port); 42 | 43 | typedef struct { 44 | uint32_t pointer_instr_mut; 45 | uint32_t pointer_instr; 46 | uint32_t pointer_stack; 47 | uint32_t pointer_exception_stack; 48 | uint32_t pointer_frame; 49 | uint32_t pointer_page_directory; 50 | uint32_t registers[FOX32_REGISTER_COUNT]; 51 | 52 | bool flag_zero; 53 | bool flag_carry; 54 | bool flag_interrupt; 55 | bool flag_swap_sp; 56 | 57 | bool halted; 58 | bool soft_halted; 59 | 60 | bool debug; 61 | bool headless; 62 | 63 | bool mmu_enabled; 64 | 65 | intptr_t panic_jmp[5]; 66 | fox32_err_t panic_err; 67 | 68 | uint32_t exception_operand; 69 | 70 | void *io_user; 71 | fox32_io_read_t *io_read; 72 | fox32_io_write_t *io_write; 73 | 74 | uint8_t memory_ram[FOX32_MEMORY_RAM]; 75 | uint8_t memory_rom[FOX32_MEMORY_ROM]; 76 | } fox32_vm_t; 77 | 78 | void fox32_init(fox32_vm_t *vm); 79 | 80 | fox32_err_t fox32_step(fox32_vm_t *vm); 81 | fox32_err_t fox32_resume(fox32_vm_t *vm, uint32_t count, uint32_t *executed); 82 | 83 | fox32_err_t fox32_raise(fox32_vm_t *vm, uint16_t vector); 84 | fox32_err_t fox32_recover(fox32_vm_t *vm, fox32_err_t err); 85 | 86 | fox32_err_t fox32_push_byte(fox32_vm_t *vm, uint8_t value); 87 | fox32_err_t fox32_push_half(fox32_vm_t *vm, uint16_t value); 88 | fox32_err_t fox32_push_word(fox32_vm_t *vm, uint32_t value); 89 | fox32_err_t fox32_pop_byte(fox32_vm_t *vm, uint8_t *value); 90 | fox32_err_t fox32_pop_half(fox32_vm_t *vm, uint16_t *value); 91 | fox32_err_t fox32_pop_word(fox32_vm_t *vm, uint32_t *value); 92 | -------------------------------------------------------------------------------- /src/disk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "cpu.h" 9 | #include "disk.h" 10 | 11 | disk_controller_t disk_controller; 12 | 13 | extern fox32_vm_t vm; 14 | 15 | void new_disk(const char *filename, size_t id) { 16 | if (id > 3) { puts("attempting to access disk with ID > 3"); return; } 17 | printf("mounting %s as disk ID %d\n", filename, (int) id); 18 | disk_controller.disks[id].file = open(filename, MODE_READ | MODE_WRITE); 19 | if (disk_controller.disks[id].file == (uint32_t) -1) { 20 | printf("couldn't open disk file\n"); 21 | yield(); 22 | return; 23 | } 24 | //fseek(disk_controller.disks[id].file, 0, SEEK_END); 25 | //disk_controller.disks[id].size = ftell(disk_controller.disks[id].file); 26 | disk_controller.disks[id].size = 512 * 1024; // TODO: fix this!!!!!!! 27 | //rewind(disk_controller.disks[id].file); 28 | yield(); 29 | } 30 | 31 | void insert_disk(disk_t disk, size_t id) { 32 | if (id > 3) { puts("attempting to access disk with ID > 3"); return; } 33 | if (disk_controller.disks[id].size > 0) remove_disk(id); 34 | printf("mounting disk ID %d\n", (int) id); 35 | disk_controller.disks[id] = disk; 36 | } 37 | 38 | void remove_disk(size_t id) { 39 | if (id > 3) { puts("attempting to access disk with ID > 3"); return; } 40 | printf("unmounting disk ID %d\n", (int) id); 41 | close(disk_controller.disks[id].file); 42 | disk_controller.disks[id].size = 0; 43 | } 44 | 45 | uint64_t get_disk_size(size_t id) { 46 | if (id > 3) { puts("attempting to access disk with ID > 3"); return 0; } 47 | return disk_controller.disks[id].size; 48 | } 49 | 50 | void set_disk_sector(size_t id, uint64_t sector) { 51 | if (id > 3) { puts("attempting to access disk with ID > 3"); return; } 52 | seek(disk_controller.disks[id].file, sector * 512); 53 | } 54 | 55 | size_t read_disk_into_memory(size_t id) { 56 | if (id > 3) { puts("attempting to access disk with ID > 3"); return 0; } 57 | return read(disk_controller.disks[id].file, &vm.memory_ram[disk_controller.buffer_pointer], 512); 58 | } 59 | 60 | size_t write_disk_from_memory(size_t id) { 61 | if (id > 3) { puts("attempting to access disk with ID > 3"); return 0; } 62 | return write(disk_controller.disks[id].file, &vm.memory_ram[disk_controller.buffer_pointer], 512); 63 | } 64 | -------------------------------------------------------------------------------- /src/disk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | uint32_t file; 7 | uint64_t size; 8 | } disk_t; 9 | 10 | typedef struct { 11 | disk_t disks[4]; 12 | size_t buffer_pointer; 13 | } disk_controller_t; 14 | 15 | void new_disk(const char *filename, size_t id); 16 | void insert_disk(disk_t disk, size_t id); 17 | void remove_disk(size_t id); 18 | uint64_t get_disk_size(size_t id); 19 | void set_disk_sector(size_t id, uint64_t sector); 20 | size_t read_disk_into_memory(size_t id); 21 | size_t write_disk_from_memory(size_t id); 22 | -------------------------------------------------------------------------------- /src/framebuffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "bus.h" 9 | #include "cpu.h" 10 | #include "framebuffer.h" 11 | 12 | extern fox32_vm_t vm; 13 | 14 | static uint8_t framebuffer[FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * 4]; 15 | static overlay_t overlays[32]; 16 | 17 | uint8_t *hardware_framebuffer = (void *) 0xF0000000; 18 | 19 | overlay_t *overlay_get(uint32_t index) { 20 | //if (index >= 32) abort(); 21 | return &overlays[index]; 22 | } 23 | 24 | void draw_framebuffer() { 25 | memcpy(framebuffer, &vm.memory_ram[0x02000000], FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * 4); 26 | 27 | for (size_t i = 0; i < 32; i++) { 28 | overlay_t *overlay = &overlays[i]; 29 | if (!overlay->enabled) continue; 30 | 31 | size_t pointer = overlay->pointer; 32 | 33 | size_t height = overlay->height; 34 | size_t width = overlay->width; 35 | 36 | size_t ymin = overlay->y, ymax = ymin + height; 37 | size_t xmin = overlay->x, xmax = xmin + width; 38 | ymax = ymax < FRAMEBUFFER_HEIGHT ? ymax : FRAMEBUFFER_HEIGHT; 39 | xmax = xmax < FRAMEBUFFER_WIDTH ? xmax : FRAMEBUFFER_WIDTH; 40 | if (ymin >= ymax) continue; 41 | if (xmin >= xmax) continue; 42 | 43 | for (size_t y = ymin; y < ymax; y++) { 44 | for (size_t x = xmin; x < xmax; x++) { 45 | size_t index_dst = (x + y * FRAMEBUFFER_WIDTH) * 4; 46 | size_t index_src = ((x - xmin) + (y - ymin) * width) * 4 + pointer; 47 | if (vm.memory_ram[index_src + 3] > 0) { 48 | memcpy(&framebuffer[index_dst], &vm.memory_ram[index_src], 4); 49 | } 50 | } 51 | } 52 | } 53 | 54 | //memcpy((void *) 0xF0000000, framebuffer, FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * 4); 55 | for (uint32_t i = 0; i < FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * 4; i += 4) { 56 | hardware_framebuffer[i] = framebuffer[i+2]; 57 | hardware_framebuffer[i+1] = framebuffer[i+1]; 58 | hardware_framebuffer[i+2] = framebuffer[i]; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/framebuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define FRAMEBUFFER_WIDTH 640 4 | #define FRAMEBUFFER_HEIGHT 480 5 | 6 | #define VSYNC_INTERRUPT_VECTOR 0xFF 7 | 8 | void draw_framebuffer(); 9 | 10 | typedef struct { 11 | uint32_t pointer; 12 | uint32_t x, y; 13 | uint32_t width, height; 14 | bool enabled; 15 | } overlay_t; 16 | 17 | overlay_t *overlay_get(uint32_t index); 18 | -------------------------------------------------------------------------------- /src/keyboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "cpu.h" 10 | #include "keyboard.h" 11 | 12 | extern fox32_vm_t vm; 13 | 14 | typedef struct node_s { 15 | struct node_s *prev; 16 | struct node_s *next; 17 | keycode_t code; 18 | } node_t; 19 | 20 | static node_t *head = NULL; 21 | static node_t *tail = NULL; 22 | 23 | keycode_t key_take(void) { 24 | node_t *node = head; 25 | 26 | if (node == NULL) { 27 | return 0; 28 | } 29 | 30 | if (node == tail) { 31 | head = NULL; 32 | tail = NULL; 33 | } else { 34 | head = node->next; 35 | head->prev = NULL; 36 | } 37 | 38 | keycode_t code = node->code; 39 | return free(node), code; 40 | } 41 | 42 | void key_put(keycode_t code) { 43 | if (code == 0) return; 44 | 45 | node_t *node = malloc(sizeof(node_t)); 46 | 47 | node->prev = tail; 48 | node->next = NULL; 49 | node->code = code; 50 | 51 | if (head == NULL) { 52 | head = node; 53 | } else { 54 | tail->next = node; 55 | } 56 | 57 | tail = node; 58 | } 59 | 60 | keycode_t key_convert(int sdlcode) { 61 | //if (sdlcode < 0 || sdlcode > SDL_NUM_SCANCODES) return 0; 62 | //return key_map[sdlcode]; 63 | return sdlcode; 64 | } 65 | 66 | void key_pressed(int sdlcode) { 67 | //if (sdlcode == SDL_SCANCODE_F11) vm.debug = !vm.debug; 68 | keycode_t code = key_convert(sdlcode); 69 | if (code) key_put(code); 70 | } 71 | 72 | void key_released(int sdlcode) { 73 | keycode_t code = key_convert(sdlcode) | 0x80; 74 | if (code) key_put(code); 75 | } 76 | -------------------------------------------------------------------------------- /src/keyboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef unsigned char keycode_t; 4 | 5 | keycode_t key_take(void); 6 | void key_put(keycode_t code); 7 | 8 | keycode_t key_convert(int sdlcode); 9 | void key_pressed(int sdlcode); 10 | void key_released(int sdlcode); 11 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "bus.h" 11 | #include "cpu.h" 12 | #include "disk.h" 13 | #include "framebuffer.h" 14 | #include "keyboard.h" 15 | #include "mouse.h" 16 | #include "serial.h" 17 | 18 | #define FPS 60 19 | #define TPF 1 20 | #define TPS (FPS * TPF) 21 | 22 | fox32_vm_t vm; 23 | 24 | extern bool bus_requests_exit; 25 | extern disk_controller_t disk_controller; 26 | 27 | uint32_t tick_start; 28 | uint32_t tick_end; 29 | int ticks = 0; 30 | bool done = false; 31 | 32 | //time_t rtc_time; 33 | uint32_t rtc_uptime; 34 | 35 | void main_loop(void); 36 | 37 | int main() { 38 | fox32_init(&vm); 39 | vm.io_read = bus_io_read; 40 | vm.io_write = bus_io_write; 41 | vm.halted = false; 42 | vm.debug = false; 43 | vm.headless = false; 44 | 45 | uint32_t fox32rom_file = open("fox32.rom", MODE_READ); 46 | if (fox32rom_file == (uint32_t) -1) { 47 | printf("failed to open fox32.rom!\n"); 48 | exit(1); 49 | } 50 | printf("reading fox32.rom...\n"); 51 | yield(); 52 | read(fox32rom_file, vm.memory_rom, 524288); 53 | close(fox32rom_file); 54 | 55 | //tick_start = SDL_GetTicks(); 56 | //tick_end = SDL_GetTicks(); 57 | 58 | new_disk("disk0.img", 0); 59 | new_disk("disk1.img", 1); 60 | new_disk("2:/bin/disk2.img", 2); 61 | new_disk("2:/bin/disk3.img", 3); 62 | 63 | while (!done && !bus_requests_exit) { 64 | main_loop(); 65 | 66 | //tick_end = SDL_GetTicks(); 67 | int delay = 1000/TPS - (tick_end - tick_start); 68 | if (delay > 0) { 69 | //sleep(delay); 70 | } else { 71 | //printf("time overrun %d\n", delay); 72 | } 73 | } 74 | 75 | return 0; 76 | } 77 | 78 | void main_loop(void) { 79 | //int dt = SDL_GetTicks() - tick_start; 80 | //tick_start = SDL_GetTicks(); 81 | //if (!dt) 82 | // dt = 1; 83 | 84 | int dt = 1; 85 | 86 | int cycles_per_tick = FOX32_CPU_HZ / TPS / dt; 87 | int extra_cycles = FOX32_CPU_HZ / TPS - (cycles_per_tick * dt); 88 | 89 | fox32_err_t error = FOX32_ERR_OK; 90 | 91 | for (int i = 0; i < 1; i++) { 92 | rtc_uptime += 1; 93 | //rtc_time = time(NULL); 94 | 95 | int cycles_left = cycles_per_tick; 96 | 97 | if (i == dt - 1) 98 | cycles_left += extra_cycles; 99 | 100 | while (cycles_left > 0) { 101 | uint32_t executed = 0; 102 | 103 | //printf("starting, vm.pointer_instr: 0x%X\n", vm.pointer_instr); 104 | //printf("rom[0] = %X, rom[1] = %X\n", vm.memory_rom[0], vm.memory_rom[1]); 105 | error = fox32_resume(&vm, cycles_left, &executed); 106 | //printf("executed %d instructions, vm.pointer_instr: 0x%X\n", executed, vm.pointer_instr); 107 | //printf("rom[0] = %X, rom[1] = %X\n", vm.memory_rom[0], vm.memory_rom[1]); 108 | if (error != FOX32_ERR_OK) { 109 | //if (vm.debug) puts(fox32_strerr(error)); 110 | error = fox32_recover(&vm, error); 111 | if (error != FOX32_ERR_OK) { 112 | printf("error: %d\n", error); 113 | yield(); 114 | exit(1); 115 | } 116 | } 117 | 118 | cycles_left -= executed; 119 | } 120 | } 121 | 122 | uint32_t mouse = get_mouse(); 123 | mouse_moved(mouse & 0xFFFF, mouse >> 16); 124 | 125 | event_t event; 126 | if (get_next_event(&event)) { 127 | if (event.type == KEY_DOWN) { 128 | if (event.arg0 == 87) // F11 129 | done = true; 130 | else 131 | key_pressed(event.arg0); 132 | } else if (event.type == KEY_UP) { 133 | key_released(event.arg0); 134 | } else if (event.type == MOUSE_DOWN) { 135 | mouse_pressed(0); 136 | } else if (event.type == MOUSE_UP) { 137 | mouse_released(0); 138 | } 139 | } 140 | 141 | if ((ticks % TPF) == 0) { 142 | if (!vm.headless) 143 | draw_framebuffer(); 144 | fox32_raise(&vm, VSYNC_INTERRUPT_VECTOR); 145 | vm.halted = false; 146 | } 147 | 148 | //done = ScreenProcessEvents(); 149 | 150 | ticks++; 151 | } 152 | -------------------------------------------------------------------------------- /src/mmu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "cpu.h" 9 | #include "mmu.h" 10 | 11 | mmu_page_t mmu_tlb[64]; 12 | 13 | extern fox32_vm_t vm; 14 | 15 | uint32_t replacement_index = 0; 16 | 17 | static size_t find_free_tlb_entry_index() { 18 | for (size_t i = 0; i < 64; i++) { 19 | if (!mmu_tlb[i].present) { 20 | return i; 21 | } 22 | } 23 | 24 | return (replacement_index++) & 63; 25 | } 26 | 27 | void set_and_flush_tlb(uint32_t virtual_address) { 28 | vm.pointer_page_directory = virtual_address; 29 | for (size_t i = 0; i < 64; i++) { 30 | mmu_tlb[i] = (mmu_page_t) { 31 | .physical_address = 0, 32 | .virtual_page = 0, 33 | .present = false, 34 | .rw = false 35 | }; 36 | } 37 | //printf("flushed TLB and set page directory pointer to %X\n", virtual_address); 38 | } 39 | 40 | void flush_single_page(uint32_t virtual_address) { 41 | uint32_t virtual_page = virtual_address & 0xFFFFF000; 42 | //printf("flushing single page %X\n", virtual_page); 43 | for (size_t i = 0; i < 64; i++) { 44 | if (mmu_tlb[i].virtual_page == virtual_page) { 45 | mmu_tlb[i].physical_address = 0; 46 | mmu_tlb[i].virtual_page = 0; 47 | mmu_tlb[i].present = false; 48 | mmu_tlb[i].rw = false; 49 | //printf("flushed\n"); 50 | break; 51 | } 52 | } 53 | } 54 | 55 | mmu_page_t *get_present_page(uint32_t virtual_address) { 56 | uint32_t virtual_page = virtual_address & 0xFFFFF000; 57 | //printf("attempting to fetch physical address for virtual address %X (page %X)\n", virtual_address, virtual_page); 58 | mmu_page_t *physical_page = NULL; 59 | for (size_t i = 0; i < 64; i++) { 60 | if (mmu_tlb[i].virtual_page == virtual_page) { 61 | physical_page = &mmu_tlb[i]; 62 | break; 63 | } 64 | } 65 | if (physical_page == NULL) { 66 | // we didn't find an entry for this page in the TLB, try to insert it from the tables in memory 67 | //printf("couldn't find an entry for this virtual page in the TLB, attempting to cache from tables\n"); 68 | uint32_t page_directory_index = virtual_address >> 22; 69 | uint32_t page_table_index = (virtual_address >> 12) & 0x03FF; 70 | bool directory_present = insert_tlb_entry_from_tables(page_directory_index, page_table_index); 71 | if (!directory_present) return NULL; 72 | // try again after possibly inserting the TLB entry 73 | for (size_t i = 0; i < 64; i++) { 74 | if (mmu_tlb[i].virtual_page == virtual_page) { 75 | physical_page = &mmu_tlb[i]; 76 | break; 77 | } 78 | } 79 | // if we still can't find the page, return NULL 80 | if (physical_page == NULL) return NULL; 81 | } 82 | // if the page is present, return it, otherwise return NULL 83 | if (physical_page->present) { 84 | //printf("found physical address: %X\n", physical_page->physical_address); 85 | return physical_page; 86 | } else { 87 | return NULL; 88 | } 89 | } 90 | 91 | bool insert_tlb_entry_from_tables(uint32_t page_directory_index, uint32_t page_table_index) { 92 | uint32_t directory = 93 | vm.memory_ram[vm.pointer_page_directory + (page_directory_index * 4)] | 94 | vm.memory_ram[vm.pointer_page_directory + (page_directory_index * 4) + 1] << 8 | 95 | vm.memory_ram[vm.pointer_page_directory + (page_directory_index * 4) + 2] << 16 | 96 | vm.memory_ram[vm.pointer_page_directory + (page_directory_index * 4) + 3] << 24; 97 | //printf("operating on directory at %X\n", vm.pointer_page_directory + (page_directory_index * 4)); 98 | bool directory_present = (directory & 0b1) != 0; 99 | uint32_t directory_address = directory & 0xFFFFF000; 100 | //printf("directory_present: %X, directory_address: %X\n", directory_present, directory_address); 101 | if (directory_present) { 102 | uint32_t table = 103 | vm.memory_ram[directory_address + (page_table_index * 4)] | 104 | vm.memory_ram[directory_address + (page_table_index * 4) + 1] << 8 | 105 | vm.memory_ram[directory_address + (page_table_index * 4) + 2] << 16 | 106 | vm.memory_ram[directory_address + (page_table_index * 4) + 3] << 24; 107 | bool table_present = (table & 0b01) != 0; 108 | bool table_rw = (table & 0b10) != 0; 109 | uint32_t table_address = table & 0xFFFFF000; 110 | if (table_present) { 111 | mmu_page_t entry = { 112 | .physical_address = table_address, 113 | .virtual_page = (page_directory_index << 22) | (page_table_index << 12), 114 | .present = table_present, 115 | .rw = table_rw 116 | }; 117 | size_t entry_index = find_free_tlb_entry_index(); 118 | mmu_tlb[entry_index] = entry; 119 | //printf("inserting virtual page %X into the TLB\n", (page_directory_index << 22) | (page_table_index << 12)); 120 | } 121 | } 122 | return directory_present; 123 | } 124 | -------------------------------------------------------------------------------- /src/mmu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct { 4 | uint32_t virtual_page; 5 | uint32_t physical_address; 6 | bool present; 7 | bool rw; 8 | } mmu_page_t; 9 | 10 | void set_and_flush_tlb(uint32_t virtual_address); 11 | void flush_single_page(uint32_t virtual_address); 12 | mmu_page_t *get_present_page(uint32_t virtual_address); 13 | bool insert_tlb_entry_from_tables(uint32_t page_directory_index, uint32_t page_table_index); 14 | -------------------------------------------------------------------------------- /src/mouse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "mouse.h" 9 | 10 | mouse_t mouse; 11 | 12 | void mouse_moved(int x, int y) { 13 | mouse.x = x; 14 | mouse.y = y; 15 | 16 | if (mouse.x > 0x8000) mouse.x = 0; 17 | if (mouse.x > 640) mouse.x = 640; 18 | if (mouse.y > 0x8000) mouse.y = 0; 19 | if (mouse.y > 480) mouse.y = 480; 20 | } 21 | 22 | void mouse_pressed(int button) { 23 | (void) button; // TODO: check which button was clicked 24 | mouse.clicked = true; 25 | mouse.held = true; 26 | } 27 | 28 | void mouse_released(int button) { 29 | (void) button; // TODO: check which button was released 30 | mouse.released = true; 31 | mouse.held = false; 32 | } 33 | -------------------------------------------------------------------------------- /src/mouse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct { 4 | uint16_t x, y; 5 | bool clicked; 6 | bool released; 7 | bool held; 8 | } mouse_t; 9 | 10 | void mouse_moved(int x, int y); 11 | void mouse_pressed(int button); 12 | void mouse_released(int button); 13 | -------------------------------------------------------------------------------- /src/serial.c: -------------------------------------------------------------------------------- 1 | #include 2 | #ifndef WINDOWS 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static struct termios saved_tios; 10 | static bool is_terminal = false; 11 | 12 | static void exit_handler(void) { 13 | if (is_terminal) 14 | tcsetattr(0, TCSANOW, &saved_tios); 15 | } 16 | 17 | void serial_init(void) { 18 | struct termios tios; 19 | 20 | if (tcgetattr(0, &tios) != -1) { 21 | is_terminal = 1; 22 | saved_tios = tios; 23 | 24 | atexit(exit_handler); 25 | } 26 | 27 | if (is_terminal) { 28 | tios.c_lflag &= ~(ICANON|ECHO); 29 | tcsetattr(0, TCSANOW, &tios); 30 | } 31 | } 32 | 33 | int serial_get(void) { 34 | fd_set readfds; 35 | int fd = STDIN_FILENO; 36 | struct timeval tm = { 0, 0 }; 37 | 38 | FD_ZERO(&readfds); 39 | FD_SET(fd, &readfds); 40 | 41 | int ready = select(fd + 1, &readfds, NULL, NULL, &tm); 42 | 43 | if (ready == 1 && FD_ISSET(fd, &readfds)) { 44 | char c; 45 | int ret = read(fd, &c, 1); 46 | 47 | if (ret == 1) 48 | return c; 49 | } 50 | 51 | return 0; 52 | } 53 | 54 | #endif 55 | 56 | void serial_put(int value) { 57 | putchar((int) value); 58 | fflush(stdout); 59 | } 60 | -------------------------------------------------------------------------------- /src/serial.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void serial_init(void); 4 | int serial_get(void); 5 | void serial_put(int value); 6 | --------------------------------------------------------------------------------