├── hw ├── vga │ ├── text.c │ └── CMakeLists.txt ├── CMakeLists.txt ├── chipset │ ├── CMakeLists.txt │ ├── bios.c │ ├── ram.c │ ├── rom.c │ └── memory.c ├── cpu │ ├── CMakeLists.txt │ ├── cpu.c │ ├── memory.c │ ├── opcode_two.c │ ├── opcode_common.c │ ├── arithmetic.c │ └── opcode.c └── board.c ├── .gitignore ├── fw ├── bios.bin ├── hello.bin └── vgabios.bin ├── gui ├── CMakeLists.txt └── window.c ├── include ├── hw │ ├── chipset │ │ ├── bios.h │ │ ├── ram.h │ │ ├── rom.h │ │ └── memory.h │ ├── board.h │ └── cpu │ │ ├── memory.h │ │ ├── opcode.h │ │ ├── arithmetic.h │ │ └── cpu.h ├── gui │ └── window.h ├── tinyx86.h └── list.h ├── CMakeLists.txt ├── tinyx86.c ├── LICENSE ├── .clang-format ├── README.md ├── log.c └── main.c /hw/vga/text.c: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Directories 2 | build/ 3 | 4 | # File types 5 | *.o 6 | -------------------------------------------------------------------------------- /fw/bios.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoisonNinja/tinyx86/HEAD/fw/bios.bin -------------------------------------------------------------------------------- /fw/hello.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoisonNinja/tinyx86/HEAD/fw/hello.bin -------------------------------------------------------------------------------- /fw/vgabios.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoisonNinja/tinyx86/HEAD/fw/vgabios.bin -------------------------------------------------------------------------------- /gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(tinyx86 2 | PRIVATE 3 | ${CMAKE_CURRENT_SOURCE_DIR}/window.c 4 | ) 5 | -------------------------------------------------------------------------------- /hw/vga/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(tinyx86 2 | PRIVATE 3 | ${CMAKE_CURRENT_SOURCE_DIR}/text.c 4 | ) 5 | -------------------------------------------------------------------------------- /include/hw/chipset/bios.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct board; 6 | 7 | extern void bios_load(struct board* board); 8 | -------------------------------------------------------------------------------- /include/gui/window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern void window_init(void); 6 | extern void window_create(size_t x, size_t y); 7 | -------------------------------------------------------------------------------- /hw/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(tinyx86 2 | PRIVATE 3 | ${CMAKE_CURRENT_SOURCE_DIR}/board.c 4 | ) 5 | 6 | add_subdirectory(chipset) 7 | add_subdirectory(cpu) 8 | add_subdirectory(vga) 9 | -------------------------------------------------------------------------------- /hw/chipset/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(tinyx86 2 | PRIVATE 3 | ${CMAKE_CURRENT_SOURCE_DIR}/bios.c 4 | ${CMAKE_CURRENT_SOURCE_DIR}/memory.c 5 | ${CMAKE_CURRENT_SOURCE_DIR}/ram.c 6 | ${CMAKE_CURRENT_SOURCE_DIR}/rom.c 7 | ) 8 | -------------------------------------------------------------------------------- /hw/cpu/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(tinyx86 2 | PRIVATE 3 | ${CMAKE_CURRENT_SOURCE_DIR}/arithmetic.c 4 | ${CMAKE_CURRENT_SOURCE_DIR}/cpu.c 5 | ${CMAKE_CURRENT_SOURCE_DIR}/memory.c 6 | ${CMAKE_CURRENT_SOURCE_DIR}/opcode_common.c 7 | ${CMAKE_CURRENT_SOURCE_DIR}/opcode_two.c 8 | ${CMAKE_CURRENT_SOURCE_DIR}/opcode.c 9 | ) 10 | -------------------------------------------------------------------------------- /gui/window.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void window_init(void) 4 | { 5 | SDL_Init(SDL_INIT_VIDEO); 6 | } 7 | 8 | void window_create(size_t x, size_t y) 9 | { 10 | SDL_Window *window; 11 | SDL_Renderer *renderer; 12 | SDL_CreateWindowAndRenderer(x, y, 0, &window, &renderer); 13 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); 14 | SDL_RenderClear(renderer); 15 | SDL_RenderPresent(renderer); 16 | } 17 | -------------------------------------------------------------------------------- /include/hw/board.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct board { 8 | struct cpu* cpu; 9 | struct memory* memory; 10 | }; 11 | 12 | extern struct board* board_create(size_t memory); 13 | extern void board_destroy(struct board* board); 14 | extern void board_poweron(struct board* board); 15 | extern void board_poweroff(struct board* board); 16 | extern void board_run(struct board* board); 17 | extern void board_load(struct board* board, void* blob, size_t size); 18 | -------------------------------------------------------------------------------- /include/hw/chipset/ram.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern struct memory_region* memory_init_ram(struct board* board, addr_t base, 6 | size_t size); 7 | 8 | extern uint8_t ram_read_u8(struct memory_region* region, addr_t addr); 9 | extern void ram_write_u8(struct memory_region* region, addr_t addr, 10 | uint8_t value); 11 | 12 | extern uint16_t ram_read_u16(struct memory_region* region, addr_t addr); 13 | extern void ram_write_u16(struct memory_region* region, addr_t addr, 14 | uint16_t value); 15 | -------------------------------------------------------------------------------- /include/hw/chipset/rom.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern struct memory_region* memory_init_rom(struct board* board, addr_t base, 6 | size_t size); 7 | 8 | extern uint8_t rom_read_u8(struct memory_region* region, addr_t addr); 9 | extern void rom_write_u8(struct memory_region* region, addr_t addr, 10 | uint8_t value); 11 | 12 | extern uint16_t rom_read_u16(struct memory_region* region, addr_t addr); 13 | extern void rom_write_u16(struct memory_region* region, addr_t addr, 14 | uint16_t value); 15 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(tinyx86 C) 3 | 4 | # Check for in tree builds 5 | if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") 6 | message(FATAL_ERROR "In-source builds are not allowed.") 7 | endif("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") 8 | 9 | # Set CFLAGS 10 | set(CMAKE_C_STANDARD 11) 11 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") 12 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_CFLAGS_DEBUG} -g -O0") 13 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_CFLAGS_RELEASE} -O3") 14 | 15 | include_directories(include) 16 | 17 | add_executable(tinyx86 log.c main.c tinyx86.c) 18 | add_subdirectory(gui) 19 | add_subdirectory(hw) 20 | 21 | INCLUDE(FindPkgConfig) 22 | 23 | PKG_SEARCH_MODULE(SDL2 REQUIRED sdl2) 24 | 25 | include_directories(${SDL2_INCLUDE_DIRS}) 26 | target_link_libraries(tinyx86 ${SDL2_LIBRARIES}) 27 | 28 | install(TARGETS tinyx86 DESTINATION /usr/local/bin) 29 | -------------------------------------------------------------------------------- /include/hw/cpu/memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern uint8_t cpu_fetch_instruction_u8(struct cpu* cpu); 6 | extern uint16_t cpu_fetch_instruction_u16(struct cpu* cpu); 7 | extern uint32_t cpu_fetch_instruction_u32(struct cpu* cpu); 8 | 9 | extern uint8_t cpu_fetch_u8(struct cpu* cpu, struct cpu_segment* segment, 10 | addr_t offset); 11 | extern void cpu_store_u8(struct cpu* cpu, struct cpu_segment* segment, 12 | addr_t offset, uint8_t value); 13 | 14 | extern uint16_t cpu_fetch_u16(struct cpu* cpu, struct cpu_segment* segment, 15 | addr_t offset); 16 | extern void cpu_store_u16(struct cpu* cpu, struct cpu_segment* segment, 17 | addr_t offset, uint16_t value); 18 | 19 | extern uint32_t cpu_fetch_u32(struct cpu* cpu, struct cpu_segment* segment, 20 | addr_t offset); 21 | extern void cpu_store_u32(struct cpu* cpu, struct cpu_segment* segment, 22 | addr_t offset, uint32_t value); 23 | -------------------------------------------------------------------------------- /include/hw/cpu/opcode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct cpu; 4 | union cpu_register; 5 | 6 | struct modrm { 7 | uint8_t rm : 3; 8 | uint8_t reg : 3; 9 | uint8_t mod : 2; 10 | } __attribute__((packed)); 11 | 12 | struct sib { 13 | uint8_t base : 3; 14 | uint8_t index : 3; 15 | uint8_t scale : 2; 16 | } __attribute__((packed)); 17 | 18 | extern void opcode_execute(struct cpu* cpu); 19 | extern void opcode_two_execute(struct cpu* cpu); 20 | 21 | extern void raw_to_modrm(uint8_t raw, struct modrm* modrm); 22 | extern union cpu_register* modrm_to_register(struct cpu* cpu, uint8_t modrm); 23 | extern uint32_t modrm_to_address(struct cpu* cpu, uint8_t mod, uint8_t rm); 24 | 25 | typedef void (*opcode_fn_t)(struct cpu* cpu); 26 | 27 | #define OPCODE_DEFINE(number) static void opcode##number(struct cpu* cpu) 28 | 29 | extern uint8_t fetch_modrm_r8(struct cpu* cpu); 30 | extern uint8_t fetch_modrm_rm8(struct cpu* cpu); 31 | extern void store_modrm_r8(struct cpu* cpu, uint8_t val); 32 | extern void store_modrm_rm8(struct cpu* cpu, uint8_t val); 33 | 34 | extern void far_jump(struct cpu* cpu, uint32_t eip, uint16_t cs); -------------------------------------------------------------------------------- /hw/chipset/bios.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define BIOS_PATH "../fw/hello.bin" 6 | #define VGABIOS_PATH "../fw/vgabios.bin" 7 | 8 | void bios_load(struct board* board) 9 | { 10 | tinyx86_file_t bios = tinyx86_file_open(BIOS_PATH, "r"); 11 | if (!bios) { 12 | log_fatal("Failed to locate BIOS binary"); 13 | tinyx86_exit(1); 14 | } 15 | ssize_t bios_size = tinyx86_file_size(bios); 16 | uint8_t* bios_buffer = tinyx86_malloc(bios_size); 17 | if (tinyx86_file_read(bios, bios_buffer, bios_size) < bios_size) { 18 | log_fatal("Failed to read entire BIOS binary"); 19 | tinyx86_exit(1); 20 | } 21 | tinyx86_file_close(bios); 22 | struct memory_region* bios_low = memory_init_ram(board, 0, bios_size); 23 | memory_load_image(bios_low, bios_buffer, 0, bios_size); 24 | struct memory_region* bios_high = 25 | memory_init_ram(board, -(uint32_t)bios_size, bios_size); 26 | log_trace("BIOS loaded to %X and %X", bios_low->base, bios_high->base); 27 | memory_load_image(bios_high, bios_buffer, 0, bios_size); 28 | tinyx86_free(bios_buffer); 29 | } 30 | -------------------------------------------------------------------------------- /tinyx86.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | tinyx86_file_t tinyx86_file_open(const char* path, char* flags) 8 | { 9 | tinyx86_file_t file = fopen(path, flags); 10 | return file; 11 | } 12 | 13 | int tinyx86_file_close(tinyx86_file_t file) 14 | { 15 | int ret = fclose(file); 16 | if (ret) { 17 | return -errno; 18 | } 19 | return 0; 20 | } 21 | 22 | ssize_t tinyx86_file_read(tinyx86_file_t file, uint8_t* buffer, size_t size) 23 | { 24 | size_t ret = fread(buffer, 1, size, file); 25 | if (ret < size) { 26 | if (ferror(file)) { 27 | return -errno; 28 | } 29 | } 30 | return ret; 31 | } 32 | 33 | size_t tinyx86_file_size(tinyx86_file_t file) 34 | { 35 | size_t start = ftell(file); 36 | fseek(file, 0, SEEK_END); 37 | size_t size = ftell(file); 38 | fseek(file, start, SEEK_SET); 39 | return size; 40 | } 41 | 42 | void* tinyx86_malloc(size_t size) 43 | { 44 | return malloc(size); 45 | } 46 | 47 | void tinyx86_free(void* addr) 48 | { 49 | return free(addr); 50 | } 51 | 52 | void tinyx86_exit(int code) 53 | { 54 | exit(code); 55 | } 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Jason Lu 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /include/hw/cpu/arithmetic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | extern void cpu_arithmetic_add_u8(struct cpu* cpu, uint8_t* dest, uint8_t* src); 7 | extern void cpu_arithmetic_add_u16(struct cpu* cpu, uint16_t* dest, 8 | uint16_t* src); 9 | extern void cpu_arithmetic_add_u32(struct cpu* cpu, uint32_t* dest, 10 | uint32_t* src); 11 | 12 | extern void cpu_arithmetic_sub_u8(struct cpu* cpu, uint8_t* dest, uint8_t* src); 13 | extern void cpu_arithmetic_sub_u16(struct cpu* cpu, uint16_t* dest, 14 | uint16_t* src); 15 | extern void cpu_arithmetic_sub_u32(struct cpu* cpu, uint32_t* dest, 16 | uint32_t* src); 17 | 18 | #define cpu_arithmetic_cmp_u8 cpu_arithmetic_sub_u8 19 | #define cpu_arithmetic_cmp_u16 cpu_arithmetic_sub_u16 20 | #define cpu_arithmetic_cmp_u32 cpu_arithmetic_sub_u32 21 | 22 | extern void cpu_arithmetic_and_u8(struct cpu* cpu, uint8_t* dest, uint8_t* src); 23 | extern void cpu_arithmetic_and_u16(struct cpu* cpu, uint16_t* dest, 24 | uint16_t* src); 25 | 26 | extern void cpu_arithmetic_inc_u16(struct cpu* cpu, uint16_t* dest); 27 | extern void cpu_arithmetic_inc_u32(struct cpu* cpu, uint32_t* dest); 28 | 29 | extern bool cpu_get_cf(struct cpu* cpu); 30 | extern bool cpu_get_pf(struct cpu* cpu); 31 | extern bool cpu_get_af(struct cpu* cpu); 32 | extern bool cpu_get_zf(struct cpu* cpu); 33 | extern bool cpu_get_sf(struct cpu* cpu); 34 | extern bool cpu_get_of(struct cpu* cpu); 35 | 36 | extern void cpu_recompute_flags(struct cpu* cpu); 37 | -------------------------------------------------------------------------------- /include/tinyx86.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define TINYX86_VERSION_NAME "tinyx86" 10 | 11 | #define TINYX86_VERSION_MAJOR 0 12 | #define TINYX86_VERSION_MINOR 0 13 | #define TINYX86_VERSION_PATCH 1 14 | 15 | typedef size_t addr_t; 16 | 17 | #define TINYX86_MINIMUM_MEMORY 128 18 | #define TINYX86_MAXIMUM_MEMORY 1024 19 | 20 | typedef void (*log_LockFn)(void *udata, int lock); 21 | 22 | enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; 23 | 24 | #define log_trace(...) log_log(LOG_TRACE, __VA_ARGS__) 25 | #define log_debug(...) log_log(LOG_DEBUG, __VA_ARGS__) 26 | #define log_info(...) log_log(LOG_INFO, __VA_ARGS__) 27 | #define log_warn(...) log_log(LOG_WARN, __VA_ARGS__) 28 | #define log_error(...) log_log(LOG_ERROR, __VA_ARGS__) 29 | #define log_fatal(...) log_log(LOG_FATAL, __VA_ARGS__) 30 | 31 | void log_set_udata(void *udata); 32 | void log_set_lock(log_LockFn fn); 33 | void log_set_fp(FILE *fp); 34 | void log_set_level(int level); 35 | void log_set_quiet(int enable); 36 | 37 | void log_log(int level, const char *fmt, ...); 38 | 39 | // TODO: Move this to platform dependant 40 | typedef FILE *tinyx86_file_t; 41 | 42 | extern tinyx86_file_t tinyx86_file_open(const char *path, char *flags); 43 | extern int tinyx86_file_close(tinyx86_file_t file); 44 | extern ssize_t tinyx86_file_read(tinyx86_file_t file, uint8_t *buffer, 45 | size_t size); 46 | extern size_t tinyx86_file_size(tinyx86_file_t file); 47 | 48 | extern void *tinyx86_malloc(size_t size); 49 | extern void tinyx86_free(void *addr); 50 | 51 | extern __attribute__((noreturn)) void tinyx86_exit(int code); 52 | -------------------------------------------------------------------------------- /hw/board.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define MB_TO_BYTE(x) ((x)*1024 * 1024) 8 | 9 | static void dump_vga(struct board* board) 10 | { 11 | int x, y; 12 | for (y = 0; y < 25; y++) { 13 | for (x = 0; x < 80; x++) { 14 | fprintf(stdout, "%c", memory_read_u8(board, 0x8000 + (y * 80) + x)); 15 | } 16 | fprintf(stdout, "\n"); 17 | } 18 | } 19 | 20 | struct board* board_create(size_t memory) 21 | { 22 | struct board* board = malloc(sizeof(struct board)); 23 | if (!board) { 24 | log_fatal("Failed to allocate memory for board data structure."); 25 | return NULL; 26 | } 27 | board->cpu = cpu_create(); 28 | board->cpu->board = board; 29 | board->memory = memory_create(); 30 | memory_init_ram(board, 0, MB_TO_BYTE(memory)); 31 | bios_load(board); 32 | return board; 33 | } 34 | 35 | void board_destroy(struct board* board) 36 | { 37 | if (!board) { 38 | log_warn("Attempting to destroy a non-existent board?"); 39 | return; 40 | } 41 | cpu_destroy(board->cpu); 42 | free(board); 43 | } 44 | 45 | void board_poweron(struct board* board) 46 | { 47 | cpu_reset(board->cpu); 48 | } 49 | 50 | void board_poweroff(struct board* board) 51 | { 52 | cpu_reset(board->cpu); 53 | } 54 | 55 | void board_run(struct board* board) 56 | { 57 | board->cpu->state = CPU_RUNNING; 58 | while (1) { 59 | if (board->cpu->state == CPU_RUNNING) { 60 | cpu_cycle(board->cpu); 61 | } else { 62 | dump_vga(board); 63 | board_destroy(board); 64 | return; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /hw/cpu/cpu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void cpu_cycle(struct cpu* cpu) 9 | { 10 | if (cpu->state == CPU_RUNNING) { 11 | opcode_execute(cpu); 12 | cpu_dump(cpu); 13 | } 14 | } 15 | 16 | void cpu_reset(struct cpu* cpu) 17 | { 18 | struct board* board = cpu->board; 19 | memset(cpu, 0, sizeof(struct cpu)); 20 | cpu->board = board; 21 | cpu->cs.selector = 0; 22 | cpu->cs.base = 0; 23 | cpu->cs.limit = 0; 24 | cpu->ip.regs_16 = 0; 25 | cpu->state = CPU_STOPPED; 26 | } 27 | 28 | struct cpu* cpu_create(void) 29 | { 30 | struct cpu* cpu = malloc(sizeof(struct cpu)); 31 | if (!cpu) { 32 | log_fatal("Failed to allocate memory for CPU structure"); 33 | return NULL; 34 | } 35 | return cpu; 36 | } 37 | 38 | void cpu_destroy(struct cpu* cpu) 39 | { 40 | if (!cpu) 41 | return; 42 | free(cpu); 43 | } 44 | 45 | void cpu_dump(struct cpu* cpu) 46 | { 47 | log_trace("========================= CPU Dump ========================="); 48 | log_trace("AX: 0x%04X BX: 0x%04X CX: 0x%04X DX: 0x%04X", cpu->ax.regs_16, 49 | cpu->bx.regs_16, cpu->cx.regs_16, cpu->dx.regs_16); 50 | log_trace("SP: 0x%04X BP: 0x%04X SI: 0x%04X DI: 0x%04X IP: 0x%04X", 51 | cpu->sp.regs_16, cpu->bp.regs_16, cpu->si.regs_16, 52 | cpu->di.regs_16, cpu->ip.regs_16); 53 | log_trace( 54 | "CS: 0x%04X DS: 0x%04X SS: 0x%04X ES: 0x%04X FS: 0x%04X GS: 0x%04X", 55 | cpu->cs.selector, cpu->ds.selector, cpu->ss.selector, cpu->es.selector, 56 | cpu->fs.selector, cpu->gs.selector); 57 | log_trace("============================================================"); 58 | } 59 | -------------------------------------------------------------------------------- /include/hw/chipset/memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct memory_region; 7 | 8 | struct memory_region_operations { 9 | uint8_t (*read_u8)(struct memory_region* region, addr_t addr); 10 | void (*write_u8)(struct memory_region* region, addr_t addr, uint8_t value); 11 | uint16_t (*read_u16)(struct memory_region* region, addr_t addr); 12 | void (*write_u16)(struct memory_region* region, addr_t addr, 13 | uint16_t value); 14 | uint32_t (*read_u32)(struct memory_region* region, addr_t addr); 15 | void (*write_u32)(struct memory_region* region, addr_t addr, 16 | uint32_t value); 17 | }; 18 | 19 | struct memory_region { 20 | addr_t base; 21 | void* host_base; 22 | addr_t size; 23 | struct memory_region_operations* memory_region_ops; 24 | struct list_element subregions; 25 | struct list_element list; 26 | }; 27 | 28 | struct memory { 29 | struct list_element regions; 30 | }; 31 | 32 | struct board; 33 | 34 | extern struct memory* memory_create(void); 35 | 36 | extern uint8_t memory_read_u8(struct board* board, addr_t addr); 37 | extern void memory_write_u8(struct board* board, addr_t addr, uint8_t value); 38 | 39 | extern uint16_t memory_read_u16(struct board* board, addr_t addr); 40 | extern void memory_write_u16(struct board* board, addr_t addr, uint16_t value); 41 | 42 | extern uint32_t memory_read_u32(struct board* board, addr_t addr); 43 | extern void memory_write_u32(struct board* board, addr_t addr, uint32_t value); 44 | 45 | extern void memory_register_region(struct board* board, 46 | struct memory_region* region); 47 | 48 | extern int memory_load_image(struct memory_region* region, void* blob, 49 | addr_t offset, size_t size); 50 | -------------------------------------------------------------------------------- /include/hw/cpu/cpu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct board; 6 | 7 | union cpu_register { 8 | uint32_t regs_32; 9 | uint16_t regs_16; 10 | uint8_t regs_8; 11 | }; 12 | 13 | struct cpu_segment { 14 | uint32_t base, limit; 15 | uint16_t selector; 16 | }; 17 | 18 | #define CPU_EFLAGS_CF (1 << 0) 19 | #define CPU_EFLAGS_PF (1 << 2) 20 | #define CPU_EFLAGS_AF (1 << 4) 21 | #define CPU_EFLAGS_ZF (1 << 6) 22 | #define CPU_EFLAGS_SF (1 << 7) 23 | #define CPU_EFLAGS_OF (1 << 11) 24 | #define CPU_EFLAGS_ALL \ 25 | (CPU_EFLAGS_CF | CPU_EFLAGS_CF | CPU_EFLAGS_AF | CPU_EFLAGS_ZF | \ 26 | CPU_EFLAGS_SF | CPU_EFLAGS_OF) 27 | 28 | struct cpu_prefix_state { 29 | struct cpu_segment* segment; 30 | #define CPU_PREFIX_STATE_OPERAND32(cpu) ((cpu)->prefix_state.operand32 ? 1 : 0) 31 | uint8_t operand32; 32 | uint8_t repne; 33 | uint8_t repe; 34 | }; 35 | 36 | struct cpu { 37 | struct board* board; 38 | // CPU registers 39 | union cpu_register ax, bx, cx, dx; 40 | union cpu_register sp, bp, si, di, ip; 41 | struct cpu_segment cs, ds, ss, es, fs, gs; 42 | // CPU EFLAGS + required support structures for lazy computation 43 | uint32_t eflags; 44 | uint32_t eflags_dirty; 45 | uint32_t last_op1; 46 | uint32_t last_op2; 47 | uint32_t last_result; 48 | // ModRM byte 49 | uint8_t modrm; 50 | #define CPU_OP_SIZE_8 7 51 | #define CPU_OP_SIZE_16 15 52 | #define CPU_OP_SIZE_32 31 53 | uint8_t last_size; 54 | struct cpu_prefix_state prefix_state; 55 | #define CPU_STOPPED 0x0 56 | #define CPU_RUNNING 0x1 57 | #define CPU_HALTED 0x2 58 | uint8_t state; 59 | }; 60 | 61 | extern void cpu_cycle(struct cpu* cpu); 62 | extern void cpu_reset(struct cpu* cpu); 63 | extern struct cpu* cpu_create(void); 64 | extern void cpu_destroy(struct cpu* cpu); 65 | extern void cpu_dump(struct cpu* cpu); 66 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | AccessModifierOffset: -1 3 | AlignAfterOpenBracket: true 4 | AlignEscapedNewlinesLeft: true 5 | AlignOperands: true 6 | AlignTrailingComments: true 7 | AllowAllParametersOfDeclarationOnNextLine: true 8 | AllowShortBlocksOnASingleLine: false 9 | AllowShortCaseLabelsOnASingleLine: false 10 | AllowShortFunctionsOnASingleLine: false 11 | AllowShortIfStatementsOnASingleLine: false 12 | AllowShortLoopsOnASingleLine: false 13 | AlwaysBreakAfterDefinitionReturnType: false 14 | AlwaysBreakBeforeMultilineStrings: false 15 | AlwaysBreakTemplateDeclarations: true 16 | BinPackArguments: true 17 | BinPackParameters: true 18 | BreakBeforeBinaryOperators: None 19 | BreakBeforeBraces: Linux 20 | BreakBeforeTernaryOperators: false 21 | BreakConstructorInitializersBeforeComma: false 22 | ColumnLimit: 80 23 | CommentPragmas: '^ IWYU pragma:' 24 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 25 | ConstructorInitializerIndentWidth: 4 26 | ContinuationIndentWidth: 4 27 | Cpp11BracedListStyle: true 28 | DerivePointerAlignment: true 29 | DisableFormat: false 30 | ExperimentalAutoDetectBinPacking: false 31 | IndentCaseLabels: true 32 | IndentWidth: 4 33 | IndentWrappedFunctionNames: false 34 | KeepEmptyLinesAtTheStartOfBlocks: false 35 | Language: Cpp 36 | MaxEmptyLinesToKeep: 1 37 | NamespaceIndentation: None 38 | ObjCBlockIndentWidth: 2 39 | ObjCSpaceAfterProperty: false 40 | ObjCSpaceBeforeProtocolList: false 41 | PenaltyBreakBeforeFirstCallParameter: 1 42 | PenaltyBreakComment: 300 43 | PenaltyBreakFirstLessLess: 120 44 | PenaltyBreakString: 1000 45 | PenaltyExcessCharacter: 1000000 46 | PenaltyReturnTypeOnItsOwnLine: 200 47 | PointerAlignment: Left 48 | SpaceAfterCStyleCast: false 49 | SpaceBeforeAssignmentOperators: true 50 | SpaceBeforeParens: ControlStatements 51 | SpaceInEmptyParentheses: false 52 | SpacesBeforeTrailingComments: 2 53 | SpacesInAngles: false 54 | SpacesInCStyleCastParentheses: false 55 | SpacesInContainerLiterals: true 56 | SpacesInParentheses: false 57 | SpacesInSquareBrackets: false 58 | Standard: Auto 59 | TabWidth: 4 60 | UseTab: Never 61 | -------------------------------------------------------------------------------- /hw/chipset/ram.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | uint8_t ram_read_u8(struct memory_region* region, addr_t addr) 5 | { 6 | uint8_t* memory = (uint8_t*)region->host_base; 7 | return memory[addr - region->base]; 8 | } 9 | 10 | void ram_write_u8(struct memory_region* region, addr_t addr, uint8_t value) 11 | { 12 | uint8_t* memory = (uint8_t*)region->host_base; 13 | memory[addr - region->base] = value; 14 | } 15 | 16 | uint16_t ram_read_u16(struct memory_region* region, addr_t addr) 17 | { 18 | uint8_t* memory = (uint8_t*)region->host_base; 19 | return *(uint16_t*)&memory[addr - region->base]; 20 | } 21 | 22 | void ram_write_u16(struct memory_region* region, addr_t addr, uint16_t value) 23 | { 24 | uint8_t* memory = (uint8_t*)region->host_base; 25 | *(uint16_t*)&memory[addr - region->base] = value; 26 | } 27 | 28 | uint32_t ram_read_u32(struct memory_region* region, addr_t addr) 29 | { 30 | uint8_t* memory = (uint8_t*)region->host_base; 31 | return *(uint32_t*)&memory[addr - region->base]; 32 | } 33 | 34 | void ram_write_u32(struct memory_region* region, addr_t addr, uint32_t value) 35 | { 36 | uint8_t* memory = (uint8_t*)region->host_base; 37 | *(uint32_t*)&memory[addr - region->base] = value; 38 | } 39 | 40 | struct memory_region_operations ram_memory_region_operations = { 41 | .read_u8 = ram_read_u8, 42 | .write_u8 = ram_write_u8, 43 | .read_u16 = ram_read_u16, 44 | .write_u16 = ram_write_u16, 45 | .read_u32 = ram_read_u32, 46 | .write_u32 = ram_write_u32, 47 | }; 48 | 49 | struct memory_region* memory_init_ram(struct board* board, addr_t base, 50 | size_t size) 51 | { 52 | struct memory_region* region = tinyx86_malloc(sizeof(struct memory_region)); 53 | region->base = base; 54 | region->size = size; 55 | region->host_base = tinyx86_malloc(size); 56 | region->memory_region_ops = &ram_memory_region_operations; 57 | list_runtime_init(®ion->subregions); 58 | memory_register_region(board, region); 59 | return region; 60 | } 61 | -------------------------------------------------------------------------------- /include/list.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define containerof(ptr, type, member) \ 6 | ({ \ 7 | const typeof(((type *)0)->member) *__mptr = (ptr); \ 8 | (type *)((char *)__mptr - offsetof(type, member)); \ 9 | }) 10 | 11 | struct list_element { 12 | struct list_element *next, *prev; 13 | }; 14 | 15 | #define LIST_COMPILE_INIT(list) \ 16 | { \ 17 | .next = &list, .prev = &list, \ 18 | } 19 | 20 | static inline void list_runtime_init(struct list_element *list) 21 | { 22 | list->next = list; 23 | list->prev = list; 24 | } 25 | 26 | static inline void list_add(struct list_element *list, 27 | struct list_element *element) 28 | { 29 | element->next = list->next; 30 | element->prev = list; 31 | list->next->prev = element; 32 | list->next = element; 33 | } 34 | 35 | static inline void list_add_tail(struct list_element *list, 36 | struct list_element *element) 37 | { 38 | element->next = list; 39 | element->prev = list->prev; 40 | list->prev->next = element; 41 | list->prev = element; 42 | } 43 | 44 | static inline void list_delete(struct list_element *element) 45 | { 46 | element->next->prev = element->prev; 47 | element->prev->next = element->next; 48 | element->next = NULL; 49 | element->prev = NULL; 50 | } 51 | 52 | static inline int list_empty(struct list_element *list) 53 | { 54 | return list->next == list; 55 | } 56 | 57 | #define list_first_entry(list, type, member) \ 58 | containerof((list)->next, type, member) 59 | 60 | #define list_next_entry(pos, member) \ 61 | containerof((pos)->member.next, typeof(*(pos)), member) 62 | 63 | #define list_for_each(list, member, pos) \ 64 | for ((pos) = containerof((list)->next, typeof(*pos), member); \ 65 | &(pos)->member != (list); \ 66 | pos = containerof((pos)->member.next, typeof(*pos), member)) 67 | -------------------------------------------------------------------------------- /hw/chipset/rom.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | uint8_t rom_read_u8(struct memory_region* region, addr_t addr) 5 | { 6 | uint8_t* memory = (uint8_t*)region->host_base; 7 | return memory[addr - region->base]; 8 | } 9 | 10 | void rom_write_u8(__attribute__((unused)) struct memory_region* region, 11 | __attribute__((unused)) addr_t addr, 12 | __attribute__((unused)) uint8_t value) 13 | { 14 | return; 15 | } 16 | 17 | uint16_t rom_read_u16(struct memory_region* region, addr_t addr) 18 | { 19 | uint8_t* memory = (uint8_t*)region->host_base; 20 | return *(uint16_t*)&memory[addr - region->base]; 21 | } 22 | 23 | void rom_write_u16(__attribute__((unused)) struct memory_region* region, 24 | __attribute__((unused)) addr_t addr, 25 | __attribute__((unused)) uint16_t value) 26 | { 27 | return; 28 | } 29 | 30 | uint32_t rom_read_u32(struct memory_region* region, addr_t addr) 31 | { 32 | uint8_t* memory = (uint8_t*)region->host_base; 33 | return *(uint32_t*)&memory[addr - region->base]; 34 | } 35 | 36 | void rom_write_u32(__attribute__((unused)) struct memory_region* region, 37 | __attribute__((unused)) addr_t addr, 38 | __attribute__((unused)) uint32_t value) 39 | { 40 | return; 41 | } 42 | 43 | struct memory_region_operations rom_memory_region_operations = { 44 | .read_u8 = rom_read_u8, 45 | .write_u8 = rom_write_u8, 46 | .read_u16 = rom_read_u16, 47 | .write_u16 = rom_write_u16, 48 | .read_u32 = rom_read_u32, 49 | .write_u32 = rom_write_u32, 50 | }; 51 | 52 | struct memory_region* memory_init_rom(struct board* board, addr_t base, 53 | size_t size) 54 | { 55 | struct memory_region* region = tinyx86_malloc(sizeof(struct memory_region)); 56 | list_runtime_init(®ion->subregions); 57 | region->base = base; 58 | region->size = size; 59 | region->host_base = tinyx86_malloc(size); 60 | region->memory_region_ops = &rom_memory_region_operations; 61 | memory_register_region(board, region); 62 | return region; 63 | } 64 | -------------------------------------------------------------------------------- /hw/cpu/memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | uint8_t cpu_fetch_instruction_u8(struct cpu* cpu) 8 | { 9 | addr_t final = cpu->cs.base + cpu->ip.regs_16; 10 | uint8_t ret = memory_read_u8(cpu->board, final); 11 | cpu->ip.regs_16++; 12 | return ret; 13 | } 14 | 15 | uint16_t cpu_fetch_instruction_u16(struct cpu* cpu) 16 | { 17 | addr_t final = cpu->cs.base + cpu->ip.regs_16; 18 | uint16_t ret = memory_read_u16(cpu->board, final); 19 | cpu->ip.regs_16 += 2; 20 | return ret; 21 | } 22 | 23 | uint32_t cpu_fetch_instruction_u32(struct cpu* cpu) 24 | { 25 | addr_t final = cpu->cs.base + cpu->ip.regs_32; 26 | uint16_t ret = memory_read_u32(cpu->board, final); 27 | cpu->ip.regs_32 += 4; 28 | return ret; 29 | } 30 | 31 | uint8_t cpu_fetch_u8(struct cpu* cpu, struct cpu_segment* segment, 32 | addr_t offset) 33 | { 34 | addr_t final = segment->base + offset; 35 | return memory_read_u8(cpu->board, final); 36 | } 37 | 38 | void cpu_store_u8(struct cpu* cpu, struct cpu_segment* segment, addr_t offset, 39 | uint8_t value) 40 | { 41 | addr_t final = segment->base + offset; 42 | return memory_write_u8(cpu->board, final, value); 43 | } 44 | 45 | uint16_t cpu_fetch_u16(struct cpu* cpu, struct cpu_segment* segment, 46 | addr_t offset) 47 | { 48 | addr_t final = segment->base + offset; 49 | return memory_read_u16(cpu->board, final); 50 | } 51 | 52 | void cpu_store_u16(struct cpu* cpu, struct cpu_segment* segment, addr_t offset, 53 | uint16_t value) 54 | { 55 | addr_t final = segment->base + offset; 56 | return memory_write_u16(cpu->board, final, value); 57 | } 58 | 59 | uint32_t cpu_fetch_u32(struct cpu* cpu, struct cpu_segment* segment, 60 | addr_t offset) 61 | { 62 | addr_t final = segment->base + offset; 63 | return memory_read_u32(cpu->board, final); 64 | } 65 | 66 | void cpu_store_u32(struct cpu* cpu, struct cpu_segment* segment, addr_t offset, 67 | uint32_t value) 68 | { 69 | addr_t final = segment->base + offset; 70 | return memory_write_u32(cpu->board, final, value); 71 | } 72 | -------------------------------------------------------------------------------- /hw/cpu/opcode_two.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* 7 | * 0x85: jnz rel16/32 8 | */ 9 | OPCODE_DEFINE(85) 10 | { 11 | log_trace("jnz rel16/32"); 12 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 13 | int32_t offset = cpu_fetch_instruction_u32(cpu); 14 | if (!cpu_get_zf(cpu)) { 15 | cpu->ip.regs_32 += offset; 16 | } 17 | } else { 18 | int16_t offset = cpu_fetch_instruction_u16(cpu); 19 | if (!cpu_get_zf(cpu)) { 20 | cpu->ip.regs_16 += offset; 21 | } 22 | } 23 | } 24 | 25 | static opcode_fn_t opcode_table[256] = { 26 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 27 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 28 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 29 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 30 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 31 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 32 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 33 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 34 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 35 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 36 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 37 | NULL, opcode85, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 38 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 39 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 40 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 41 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 42 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 43 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 44 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 45 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 46 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 47 | NULL, NULL, NULL, NULL, 48 | }; 49 | 50 | void opcode_two_execute(struct cpu* cpu) 51 | { 52 | uint8_t opcode = cpu_fetch_instruction_u8(cpu); 53 | log_trace("Two-byte opcode: %X", opcode); 54 | if (!opcode_table[opcode]) { 55 | log_fatal("Unknown opcode, should probably throw an Invalid Opcode " 56 | "exception, got opcode 0x%X at IP 0x%X", 57 | opcode, cpu->cs.base + cpu->ip.regs_16 - 1); 58 | cpu->state = CPU_HALTED; 59 | return; 60 | } 61 | opcode_table[opcode](cpu); 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tinyx86 2 | tinyx86 is a simple emulator with the goal of emulating the Intel 80836 and the components necessary to boot up an operating system such as Linux or Windows 95/98. 3 | 4 | tinyx86 is released under the BSD 3-Clause license. For the full license details, see the LICENSE file in the root directory 5 | 6 | # Details 7 | tinyx86 is designed to emulate the Intel 80836, the first 32-bit protected mode processor from Intel. Currently, tinyx86 implements approximately 20 - 25 opcodes, but a lot of work is going into completing the instruction set. tinyx86 accurately emulates the operations effect on the different registers, including the different quirks inherent in the x86 architecture. 8 | 9 | In addition to the processor, tinyx86 aims to implement all the hardware in an actual PC. Future support is planned for: 10 | * VGA framebuffer 11 | * 8254 Programmble Interrupt Timer 12 | * 8259 Programmble Interrupt Controller 13 | * IDE Controller 14 | 15 | The current goal of tinyx86 is to be able to boot SeaBIOS, which will eventually allow us to boot actual operating systems. 16 | 17 | # Building 18 | ## Setting up the environment 19 | Building tinyx86 has only been tested on MacOS (10.12) and Ubuntu (17.04), other operating systems are not officially supported. 20 | 21 | You will need a reasonably modern compiler with C11 support. Both Clang (Linux and Mac) and GCC (5 and 6) are known to build tinyx86 successfully, so try to use those if you are having issues with your compiler. 22 | 23 | You will also need SDL2 to draw the VGA framebuffer. For Debian derivatives, you can simply run `sudo apt install libsdl2-dev`. For other systems, you will need to manually install SDL2. 24 | 25 | ## Obtaining the source 26 | It's preferable to obtain the source using git, since that reflects the latest development progress and bugfixes: 27 | ``` 28 | git clone https://github.com/PoisonNinja/tinyx86.git 29 | ``` 30 | 31 | ## Setting up build files 32 | In the tinyx86 directory, create a build directory. In tree builds are not supported to avoid cluttering up the source code tree with object files. Attempting to do so will cause a fatal error. 33 | 34 | `cd` to the build directory, and execute `cmake -G `. Replace `` with your preferred build system, such as `make` or `ninja`. Replace `` with the path to the root directory of the tinyx86 source. 35 | 36 | For example, if my tree looked like: `~/tinyx86` and the build directory was at `~/tinyx86/build`, and I wanted to use Ninja, then I would run `cmake -G Ninja ..`. 37 | 38 | CMake should complete without any errors or warnings. 39 | 40 | ## Building 41 | Simply run your build system. The default target builds a binary called `tinyx86` located in the build directory. At this time, an install target is not provided, so you will have to run it from the build folder. 42 | 43 | # Basics 44 | There isn't really much to see currently, but tinyx86 does have some basic functionality implemented. 45 | 46 | Upon start, tinyx86 loads SeaBIOS into memory and starts executing it. In its current state, not all opcodes and required hardware support is implemented, so there isn't actualyl anything to see. 47 | 48 | Some other useful flags include: 49 | * `--logging-level`: Set the minimum logging level. Higher is more serious 50 | * `-m | --memory`: Set the amount of memory in MiB 51 | 52 | By default, tinyx86 prints out only LOG_FATAL messages, but this can be configured using the `--log-level` option as described above. 53 | 54 | For more detailed help, run tinyx86 with the `--help` or `-h` argument 55 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 rxi 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | static struct { 32 | void *udata; 33 | log_LockFn lock; 34 | FILE *fp; 35 | int level; 36 | int quiet; 37 | } L; 38 | 39 | static const char *level_names[] = {"TRACE", "DEBUG", "INFO", 40 | "WARN", "ERROR", "FATAL"}; 41 | 42 | static const char *level_colors[] = {"\x1b[94m", "\x1b[36m", "\x1b[32m", 43 | "\x1b[33m", "\x1b[31m", "\x1b[35m"}; 44 | 45 | static void lock(void) 46 | { 47 | if (L.lock) { 48 | L.lock(L.udata, 1); 49 | } 50 | } 51 | 52 | static void unlock(void) 53 | { 54 | if (L.lock) { 55 | L.lock(L.udata, 0); 56 | } 57 | } 58 | 59 | void log_set_udata(void *udata) 60 | { 61 | L.udata = udata; 62 | } 63 | 64 | void log_set_lock(log_LockFn fn) 65 | { 66 | L.lock = fn; 67 | } 68 | 69 | void log_set_fp(FILE *fp) 70 | { 71 | L.fp = fp; 72 | } 73 | 74 | void log_set_level(int level) 75 | { 76 | L.level = level; 77 | } 78 | 79 | void log_set_quiet(int enable) 80 | { 81 | L.quiet = enable ? 1 : 0; 82 | } 83 | 84 | void log_log(int level, const char *fmt, ...) 85 | { 86 | if (level < L.level) { 87 | return; 88 | } 89 | 90 | /* Acquire lock */ 91 | lock(); 92 | 93 | /* Get current time */ 94 | time_t t = time(NULL); 95 | struct tm *lt = localtime(&t); 96 | 97 | /* Log to stderr */ 98 | if (!L.quiet) { 99 | va_list args; 100 | char buf[16]; 101 | buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; 102 | fprintf(stderr, "%s %s%-5s\x1b[0m:\x1b[0m ", buf, level_colors[level], 103 | level_names[level]); 104 | va_start(args, fmt); 105 | vfprintf(stderr, fmt, args); 106 | va_end(args); 107 | fprintf(stderr, "\n"); 108 | } 109 | 110 | /* Log to file */ 111 | if (L.fp) { 112 | va_list args; 113 | char buf[32]; 114 | buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; 115 | fprintf(L.fp, "%s %-5s: ", buf, level_names[level]); 116 | va_start(args, fmt); 117 | vfprintf(L.fp, fmt, args); 118 | va_end(args); 119 | fprintf(L.fp, "\n"); 120 | } 121 | 122 | /* Release lock */ 123 | unlock(); 124 | } 125 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static struct board* board = NULL; 14 | 15 | #define GETOPT_LOG_LEVEL 0x1001 16 | 17 | static struct option long_options[] = { 18 | {"log-level", required_argument, 0, GETOPT_LOG_LEVEL}, 19 | {"help", no_argument, 0, 'h'}, 20 | {"memory", required_argument, 0, 'm'}, 21 | {"version", no_argument, 0, 'v'}, 22 | {NULL, 0, NULL, 0}, 23 | }; 24 | 25 | static void print_usage(char* argv[]) 26 | { 27 | printf("usage: %s [options]\n", argv[0]); 28 | printf( 29 | "\nOptions:\n" 30 | "-h | --help Print this help out\n" 31 | "--log-level Logging level. Higher is more serious\n" 32 | "-m | --memory Set the memory available to the machine in MiB\n" 33 | "-v | --version Display the version number\n"); 34 | } 35 | 36 | static void print_version() 37 | { 38 | printf("%s %d.%d.%d\n", TINYX86_VERSION_NAME, TINYX86_VERSION_MAJOR, 39 | TINYX86_VERSION_MINOR, TINYX86_VERSION_PATCH); 40 | } 41 | 42 | static void validate_memory(size_t* memory) 43 | { 44 | if (*memory < TINYX86_MINIMUM_MEMORY) { 45 | log_warn("Requested memory size is too small, minimum is %zuMB", 46 | TINYX86_MINIMUM_MEMORY); 47 | *memory = TINYX86_MINIMUM_MEMORY; 48 | } else if (*memory > TINYX86_MAXIMUM_MEMORY) { 49 | log_warn("Requested memory size is too large, maximum is %zuMB", 50 | TINYX86_MAXIMUM_MEMORY); 51 | *memory = TINYX86_MAXIMUM_MEMORY; 52 | } 53 | } 54 | 55 | static void validate_log_level(int* level) 56 | { 57 | if (*level < LOG_TRACE) { 58 | log_warn("Requested logging level is too low, minimum is %d", 59 | LOG_TRACE); 60 | *level = LOG_TRACE; 61 | } else if (*level > LOG_FATAL) { 62 | log_warn("Requested logging level is too high, maximum is %d", 63 | LOG_FATAL); 64 | *level = LOG_FATAL; 65 | } 66 | } 67 | 68 | int main(int argc, char** argv) 69 | { 70 | int c, long_index; 71 | char* end = NULL; 72 | int log_level = LOG_FATAL; 73 | size_t memory = TINYX86_MINIMUM_MEMORY; 74 | while ((c = getopt_long(argc, argv, "hm:v", long_options, &long_index)) != 75 | -1) { 76 | switch (c) { 77 | case GETOPT_LOG_LEVEL: 78 | log_level = atoi(optarg); 79 | validate_log_level(&log_level); 80 | break; 81 | case 'h': 82 | print_usage(argv); 83 | exit(0); 84 | case 'm': 85 | memory = strtoll(optarg, &end, 0); 86 | if (*end) { 87 | fprintf(stderr, 88 | "%s: Option -m requires a numerical argument.\n", 89 | argv[0]); 90 | exit(1); 91 | } 92 | validate_memory(&memory); 93 | break; 94 | case 'v': 95 | print_version(); 96 | exit(0); 97 | default: 98 | exit(1); 99 | } 100 | } 101 | log_set_level(log_level); 102 | log_info("Memory: %zuMB", memory); 103 | board = board_create(memory); 104 | if (!board) { 105 | exit(1); 106 | } 107 | log_debug("Created board"); 108 | log_debug("Launching window..."); 109 | window_create(720, 400); 110 | board_poweron(board); 111 | log_debug("Powered on board"); 112 | board_run(board); 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /hw/cpu/opcode_common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void raw_to_modrm(uint8_t raw, struct modrm* modrm) 6 | { 7 | uint8_t* tmp = (uint8_t*)modrm; 8 | *tmp = raw; 9 | log_trace("mod: %u reg: %u rm: %u", modrm->mod, modrm->reg, modrm->rm); 10 | } 11 | 12 | union cpu_register* modrm_to_register(struct cpu* cpu, uint8_t modrm) 13 | { 14 | switch (modrm) { 15 | case 0: 16 | return &cpu->ax; 17 | break; 18 | case 1: 19 | return &cpu->cx; 20 | break; 21 | case 2: 22 | return &cpu->dx; 23 | break; 24 | case 3: 25 | return &cpu->bx; 26 | break; 27 | case 4: 28 | return &cpu->sp; 29 | break; 30 | case 5: 31 | return &cpu->bp; 32 | break; 33 | case 6: 34 | return &cpu->si; 35 | break; 36 | case 7: 37 | return &cpu->di; 38 | break; 39 | default: 40 | return NULL; 41 | } 42 | } 43 | 44 | static uint32_t modrm_to_address_no_dis(struct cpu* cpu, uint8_t rm) 45 | { 46 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 47 | switch (rm) { 48 | case 0: 49 | return cpu->ax.regs_32; 50 | break; 51 | case 1: 52 | return cpu->cx.regs_32; 53 | break; 54 | case 2: 55 | return cpu->dx.regs_32; 56 | break; 57 | case 3: 58 | return cpu->bx.regs_32; 59 | break; 60 | case 4: 61 | // TODO: SIB decoding 62 | return 0; 63 | break; 64 | case 5: 65 | return cpu_fetch_instruction_u32(cpu); 66 | break; 67 | case 6: 68 | return cpu->si.regs_32; 69 | break; 70 | case 7: 71 | return cpu->di.regs_32; 72 | break; 73 | default: 74 | return 0; 75 | } 76 | } else { 77 | switch (rm) { 78 | case 0: 79 | return (cpu->bx.regs_16 + cpu->si.regs_16); 80 | break; 81 | case 1: 82 | return (cpu->bx.regs_16 + cpu->di.regs_16); 83 | break; 84 | case 2: 85 | return (cpu->bp.regs_16 + cpu->si.regs_16); 86 | break; 87 | case 3: 88 | return (cpu->bp.regs_16 + cpu->di.regs_16); 89 | break; 90 | case 4: 91 | return (cpu->si.regs_16); 92 | break; 93 | case 5: 94 | return (cpu->di.regs_16); 95 | break; 96 | case 6: 97 | return cpu_fetch_instruction_u16(cpu); 98 | break; 99 | case 7: 100 | return (cpu->bx.regs_16); 101 | break; 102 | default: 103 | return 0; 104 | } 105 | } 106 | } 107 | 108 | uint32_t modrm_to_address(struct cpu* cpu, uint8_t mod, uint8_t rm) 109 | { 110 | switch (mod) { 111 | case 0: 112 | return modrm_to_address_no_dis(cpu, rm); 113 | break; 114 | case 1: 115 | log_fatal("modrm_to_address_one_dis is not implemented yet!"); 116 | break; 117 | case 2: 118 | log_fatal("modrm_to_address_four_dis is not implemented yet!"); 119 | break; 120 | default: 121 | log_fatal("modrm_to_address received invalid mod!"); 122 | } 123 | return 0; 124 | } 125 | 126 | uint8_t fetch_modrm_r8(struct cpu* cpu) 127 | { 128 | struct modrm* modrm = &cpu->modrm; 129 | return modrm_to_register(cpu, modrm->reg)->regs_8; 130 | } 131 | 132 | uint8_t fetch_modrm_rm8(struct cpu* cpu) 133 | { 134 | struct modrm* modrm = &cpu->modrm; 135 | if (modrm->mod == 3) { 136 | return modrm_to_register(cpu, modrm->reg)->regs_8; 137 | } else { 138 | return cpu_fetch_u8( 139 | cpu, 140 | (cpu->prefix_state.segment) ? cpu->prefix_state.segment : &cpu->ds, 141 | modrm_to_address(cpu, modrm->mod, modrm->rm)); 142 | } 143 | } 144 | 145 | void store_modrm_r8(struct cpu* cpu, uint8_t val) 146 | { 147 | struct modrm* modrm = &cpu->modrm; 148 | modrm_to_register(cpu, modrm->reg)->regs_8 = val; 149 | } 150 | 151 | void store_modrm_rm8(struct cpu* cpu, uint8_t val) 152 | { 153 | struct modrm* modrm = &cpu->modrm; 154 | if (modrm->mod == 3) { 155 | modrm_to_register(cpu, modrm->reg)->regs_8 = val; 156 | } else { 157 | cpu_store_u8( 158 | cpu, 159 | (cpu->prefix_state.segment) ? cpu->prefix_state.segment : &cpu->ds, 160 | modrm_to_address(cpu, modrm->mod, modrm->rm), val); 161 | } 162 | } -------------------------------------------------------------------------------- /hw/chipset/memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | struct memory* memory_create() 8 | { 9 | struct memory* memory = tinyx86_malloc(sizeof(struct memory)); 10 | list_runtime_init(&memory->regions); 11 | return memory; 12 | } 13 | 14 | static struct memory_region* __memory_resolve_region( 15 | struct memory_region* parent, addr_t addr) 16 | { 17 | struct memory_region* subregion = NULL; 18 | list_for_each(&parent->subregions, list, subregion) 19 | { 20 | if (subregion->base <= addr && 21 | subregion->base + subregion->size > addr) { 22 | if (!list_empty(&subregion->subregions)) { 23 | return __memory_resolve_region(subregion, addr); 24 | } 25 | return subregion; 26 | } 27 | } 28 | return parent; 29 | } 30 | 31 | struct memory_region* memory_resolve_region(struct board* board, addr_t addr) 32 | { 33 | struct memory_region* top = NULL; 34 | list_for_each(&board->memory->regions, list, top) 35 | { 36 | if (top->base <= addr && top->base + top->size > addr) { 37 | if (!list_empty(&top->subregions)) { 38 | return __memory_resolve_region(top, addr); 39 | } 40 | return top; 41 | } 42 | } 43 | return NULL; 44 | } 45 | 46 | static void __memory_register_region(struct memory_region* parent, 47 | struct memory_region* region) 48 | { 49 | if (list_empty(&parent->subregions)) { 50 | list_add(&parent->subregions, ®ion->list); 51 | } else { 52 | struct memory_region* subregion = NULL; 53 | list_for_each(&parent->subregions, list, subregion) 54 | { 55 | if (subregion->base <= region->base && 56 | subregion->base + subregion->size >= 57 | region->base + region->size) { 58 | return __memory_register_region(subregion, region); 59 | } 60 | } 61 | list_add(&parent->subregions, ®ion->list); 62 | } 63 | } 64 | 65 | void memory_register_region(struct board* board, struct memory_region* region) 66 | { 67 | if (list_empty(&board->memory->regions)) { 68 | list_add(&board->memory->regions, ®ion->list); 69 | } else { 70 | struct memory_region* top = NULL; 71 | list_for_each(&board->memory->regions, list, top) 72 | { 73 | if (top->base <= region->base && 74 | top->base + top->size >= region->base + region->size) { 75 | return __memory_register_region(top, region); 76 | } 77 | } 78 | list_add(&board->memory->regions, ®ion->list); 79 | } 80 | } 81 | 82 | uint8_t memory_read_u8(struct board* board, addr_t addr) 83 | { 84 | struct memory_region* region = NULL; 85 | if ((region = memory_resolve_region(board, addr))) { 86 | return region->memory_region_ops->read_u8(region, addr); 87 | } 88 | log_fatal("Attempting to read outside memory: %p", addr); 89 | tinyx86_exit(1); 90 | } 91 | 92 | void memory_write_u8(struct board* board, addr_t addr, uint8_t value) 93 | { 94 | struct memory_region* region = NULL; 95 | if ((region = memory_resolve_region(board, addr))) { 96 | return region->memory_region_ops->write_u8(region, addr, value); 97 | } 98 | log_fatal("Attempting to write outside memory: %p", addr); 99 | tinyx86_exit(1); 100 | } 101 | 102 | uint16_t memory_read_u16(struct board* board, addr_t addr) 103 | { 104 | struct memory_region* region = NULL; 105 | if ((region = memory_resolve_region(board, addr))) { 106 | return region->memory_region_ops->read_u16(region, addr); 107 | } 108 | log_fatal("Attempting to read outside memory: %p", addr); 109 | tinyx86_exit(1); 110 | } 111 | 112 | void memory_write_u16(struct board* board, addr_t addr, uint16_t value) 113 | { 114 | struct memory_region* region = NULL; 115 | if ((region = memory_resolve_region(board, addr))) { 116 | return region->memory_region_ops->write_u16(region, addr, value); 117 | } 118 | log_fatal("Attempting to write outside memory: %p", addr); 119 | tinyx86_exit(1); 120 | } 121 | 122 | uint32_t memory_read_u32(struct board* board, addr_t addr) 123 | { 124 | struct memory_region* region = NULL; 125 | if ((region = memory_resolve_region(board, addr))) { 126 | return region->memory_region_ops->read_u32(region, addr); 127 | } 128 | log_fatal("Attempting to read outside memory: %p", addr); 129 | tinyx86_exit(1); 130 | } 131 | 132 | void memory_write_u32(struct board* board, addr_t addr, uint32_t value) 133 | { 134 | struct memory_region* region = NULL; 135 | if ((region = memory_resolve_region(board, addr))) { 136 | return region->memory_region_ops->write_u32(region, addr, value); 137 | } 138 | log_fatal("Attempting to write outside memory: %p", addr); 139 | tinyx86_exit(1); 140 | } 141 | 142 | int memory_load_image(struct memory_region* region, void* blob, addr_t offset, 143 | size_t size) 144 | { 145 | if (offset + size > region->size) { 146 | return 1; 147 | } 148 | uint8_t* target = ((uint8_t*)region->host_base) + offset; 149 | memcpy(target, blob, size); 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /hw/cpu/arithmetic.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void cpu_arithmetic_add_u8(struct cpu* cpu, uint8_t* dest, uint8_t* src) 4 | { 5 | cpu->last_op1 = *dest; 6 | cpu->last_op2 = *src; 7 | cpu->last_result = *dest = *dest + *src; 8 | cpu->last_size = CPU_OP_SIZE_8; 9 | cpu->eflags_dirty = CPU_EFLAGS_ALL; 10 | } 11 | 12 | void cpu_arithmetic_add_u16(struct cpu* cpu, uint16_t* dest, uint16_t* src) 13 | { 14 | cpu->last_op1 = *dest; 15 | cpu->last_op2 = *src; 16 | cpu->last_result = *dest = *dest + *src; 17 | cpu->last_size = CPU_OP_SIZE_16; 18 | cpu->eflags_dirty = CPU_EFLAGS_ALL; 19 | } 20 | 21 | void cpu_arithmetic_add_u32(struct cpu* cpu, uint32_t* dest, uint32_t* src) 22 | { 23 | cpu->last_op1 = *dest; 24 | cpu->last_op2 = *src; 25 | cpu->last_result = *dest = *dest + *src; 26 | cpu->last_size = CPU_OP_SIZE_32; 27 | cpu->eflags_dirty = CPU_EFLAGS_ALL; 28 | } 29 | 30 | void cpu_arithmetic_sub_u8(struct cpu* cpu, uint8_t* dest, uint8_t* src) 31 | { 32 | cpu->last_result = *dest; 33 | cpu->last_op2 = *src; 34 | cpu->last_op1 = *dest = *dest - *src; 35 | cpu->last_size = CPU_OP_SIZE_8; 36 | cpu->eflags_dirty = CPU_EFLAGS_ALL; 37 | } 38 | 39 | void cpu_arithmetic_sub_u16(struct cpu* cpu, uint16_t* dest, uint16_t* src) 40 | { 41 | cpu->last_result = *dest; 42 | cpu->last_op2 = *src; 43 | cpu->last_op1 = *dest = *dest - *src; 44 | cpu->last_size = CPU_OP_SIZE_16; 45 | cpu->eflags_dirty = CPU_EFLAGS_ALL; 46 | } 47 | 48 | void cpu_arithmetic_sub_u32(struct cpu* cpu, uint32_t* dest, uint32_t* src) 49 | { 50 | cpu->last_result = *dest; 51 | cpu->last_op2 = *src; 52 | cpu->last_op1 = *dest = *dest - *src; 53 | cpu->last_size = CPU_OP_SIZE_32; 54 | cpu->eflags_dirty = CPU_EFLAGS_ALL; 55 | } 56 | 57 | void cpu_arithmetic_and_u8(struct cpu* cpu, uint8_t* dest, uint8_t* src) 58 | { 59 | cpu->last_op1 = *dest; 60 | cpu->last_op2 = *src; 61 | cpu->last_result = *dest = *dest & *src; 62 | cpu->last_size = CPU_OP_SIZE_8; 63 | cpu->eflags &= ~CPU_EFLAGS_CF & ~CPU_EFLAGS_OF; 64 | cpu->eflags_dirty = CPU_EFLAGS_PF | CPU_EFLAGS_ZF | CPU_EFLAGS_SF; 65 | } 66 | 67 | void cpu_arithmetic_and_u16(struct cpu* cpu, uint16_t* dest, uint16_t* src) 68 | { 69 | cpu->last_op1 = *dest; 70 | cpu->last_op2 = *src; 71 | cpu->last_result = *dest = *dest & *src; 72 | cpu->last_size = CPU_OP_SIZE_16; 73 | cpu->eflags &= ~CPU_EFLAGS_CF & ~CPU_EFLAGS_OF; 74 | cpu->eflags_dirty = CPU_EFLAGS_PF | CPU_EFLAGS_ZF | CPU_EFLAGS_SF; 75 | } 76 | 77 | void cpu_arithmetic_and_u32(struct cpu* cpu, uint16_t* dest, uint16_t* src) 78 | { 79 | cpu->last_op1 = *dest; 80 | cpu->last_op2 = *src; 81 | cpu->last_result = *dest = *dest & *src; 82 | cpu->last_size = CPU_OP_SIZE_32; 83 | cpu->eflags &= ~CPU_EFLAGS_CF & ~CPU_EFLAGS_OF; 84 | cpu->eflags_dirty = CPU_EFLAGS_PF | CPU_EFLAGS_ZF | CPU_EFLAGS_SF; 85 | } 86 | 87 | void cpu_arithmetic_inc_u16(struct cpu* cpu, uint16_t* dest) 88 | { 89 | cpu->last_op1 = *dest; 90 | cpu->last_op2 = 1; 91 | cpu->last_result = ++*dest; 92 | cpu->last_size = CPU_OP_SIZE_16; 93 | cpu->eflags_dirty = CPU_EFLAGS_PF | CPU_EFLAGS_AF | CPU_EFLAGS_ZF | 94 | CPU_EFLAGS_SF | CPU_EFLAGS_OF; 95 | } 96 | 97 | void cpu_arithmetic_inc_u32(struct cpu* cpu, uint32_t* dest) 98 | { 99 | cpu->last_op1 = *dest; 100 | cpu->last_op2 = 1; 101 | cpu->last_result = ++*dest; 102 | cpu->last_size = CPU_OP_SIZE_32; 103 | cpu->eflags_dirty = CPU_EFLAGS_PF | CPU_EFLAGS_AF | CPU_EFLAGS_ZF | 104 | CPU_EFLAGS_SF | CPU_EFLAGS_OF; 105 | } 106 | 107 | /* 108 | * The following flag calculations are largely borrowed from v86 by 109 | * Fabian Hemmer. The code is licensed under the Simplified BSD license. 110 | * It has been reproduced here: 111 | * 112 | * Copyright (c) 2012-2014, Fabian Hemmer 113 | * All rights reserved. 114 | * 115 | * Redistribution and use in source and binary forms, with or without 116 | * modification, are permitted provided that the following conditions are met: 117 | * 118 | * 1. Redistributions of source code must retain the above copyright notice, 119 | * this list of conditions and the following disclaimer. 120 | * 2. Redistributions in binary form must reproduce the above copyright notice, 121 | * this list of conditions and the following disclaimer in the documentation 122 | * and/or other materials provided with the distribution. 123 | * 124 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 125 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 126 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 127 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 128 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 129 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 130 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 131 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 132 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 133 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 134 | * POSSIBILITY OF SUCH DAMAGE. 135 | * 136 | * The views and conclusions contained in the software and documentation are 137 | * those of the authors and should not be interpreted as representing official 138 | * policies, either expressed or implied, of the FreeBSD Project. 139 | */ 140 | bool cpu_get_cf(struct cpu* cpu) 141 | { 142 | if (cpu->eflags_dirty & CPU_EFLAGS_CF) { 143 | cpu->eflags |= 144 | ((cpu->last_op1 ^ ((cpu->last_op1 ^ cpu->last_op2) & 145 | (cpu->last_op2 ^ cpu->last_result))) >> 146 | cpu->last_size & 147 | 1) ? 148 | CPU_EFLAGS_CF : 149 | 0; 150 | cpu->eflags_dirty &= ~CPU_EFLAGS_CF; 151 | } 152 | return cpu->eflags & CPU_EFLAGS_CF; 153 | } 154 | 155 | bool cpu_get_pf(struct cpu* cpu) 156 | { 157 | if (cpu->eflags_dirty & CPU_EFLAGS_PF) { 158 | uint8_t v = cpu->last_result & 0xFF; // Low 8 bits 159 | v ^= v >> 1; 160 | v ^= v >> 2; 161 | v = (v & 0x11111111U) * 0x11111111U; 162 | cpu->eflags |= (!((v >> 28) & 1)) ? CPU_EFLAGS_PF : 0; 163 | cpu->eflags_dirty &= ~CPU_EFLAGS_PF; 164 | } 165 | return cpu->eflags & CPU_EFLAGS_PF; 166 | } 167 | 168 | bool cpu_get_af(struct cpu* cpu) 169 | { 170 | if (cpu->eflags_dirty & CPU_EFLAGS_AF) { 171 | cpu->eflags |= 172 | ((cpu->last_op1 ^ cpu->last_op2 ^ cpu->last_result) & 16) ? 173 | CPU_EFLAGS_AF : 174 | 0; 175 | cpu->eflags_dirty &= ~CPU_EFLAGS_AF; 176 | } 177 | return cpu->eflags & CPU_EFLAGS_AF; 178 | } 179 | 180 | bool cpu_get_zf(struct cpu* cpu) 181 | { 182 | if (cpu->eflags_dirty & CPU_EFLAGS_ZF) { 183 | cpu->eflags |= (cpu->last_result) ? 0 : CPU_EFLAGS_ZF; 184 | cpu->eflags_dirty &= ~CPU_EFLAGS_ZF; 185 | } 186 | return cpu->eflags & CPU_EFLAGS_ZF; 187 | } 188 | 189 | bool cpu_get_sf(struct cpu* cpu) 190 | { 191 | if (cpu->eflags_dirty & CPU_EFLAGS_SF) { 192 | cpu->eflags |= 193 | (cpu->last_result >> cpu->last_size & 1) ? CPU_EFLAGS_SF : 0; 194 | cpu->eflags_dirty &= ~CPU_EFLAGS_ZF; 195 | } 196 | return cpu->eflags & CPU_EFLAGS_SF; 197 | } 198 | 199 | bool cpu_get_of(struct cpu* cpu) 200 | { 201 | if (cpu->eflags_dirty & CPU_EFLAGS_OF) { 202 | cpu->eflags |= (((cpu->last_op1 ^ cpu->last_result) & 203 | (cpu->last_op2 ^ cpu->last_result)) >> 204 | cpu->last_size & 205 | 1) ? 206 | CPU_EFLAGS_OF : 207 | 0; 208 | cpu->eflags_dirty &= ~CPU_EFLAGS_OF; 209 | } 210 | return cpu->eflags & CPU_EFLAGS_OF; 211 | } 212 | 213 | void cpu_recompute_flags(struct cpu* cpu) 214 | { 215 | cpu_get_cf(cpu); 216 | cpu_get_pf(cpu); 217 | cpu_get_af(cpu); 218 | cpu_get_zf(cpu); 219 | cpu_get_sf(cpu); 220 | cpu_get_of(cpu); 221 | } 222 | -------------------------------------------------------------------------------- /hw/cpu/opcode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | /* 6 | * Stack operations (push, pop) 7 | */ 8 | void push_u16(struct cpu* cpu, uint16_t value) 9 | { 10 | cpu->sp.regs_16 -= 2; 11 | cpu_store_u16(cpu, &cpu->ss, cpu->sp.regs_16, value); 12 | } 13 | 14 | uint16_t pop_u16(struct cpu* cpu) 15 | { 16 | uint16_t ret = cpu_fetch_u16(cpu, &cpu->ss, cpu->sp.regs_16); 17 | cpu->sp.regs_16 += 2; 18 | return ret; 19 | } 20 | 21 | void push_u32(struct cpu* cpu, uint32_t value) 22 | { 23 | cpu->sp.regs_16 -= 4; 24 | cpu_store_u32(cpu, &cpu->ss, cpu->sp.regs_32, value); 25 | } 26 | 27 | uint16_t pop_u32(struct cpu* cpu) 28 | { 29 | uint16_t ret = cpu_fetch_u32(cpu, &cpu->ss, cpu->sp.regs_32); 30 | cpu->sp.regs_16 += 4; 31 | return ret; 32 | } 33 | 34 | /* 35 | * 0x20: AND r/m8, r8 36 | */ 37 | OPCODE_DEFINE(20) 38 | { 39 | log_trace("AND r/m8, r8"); 40 | struct modrm modrm; 41 | raw_to_modrm(cpu_fetch_instruction_u8(cpu), &modrm); 42 | if (modrm.mod == 3) { 43 | // Direct addressing mode, reg is source, rm is dest because d == 0 44 | union cpu_register* source = modrm_to_register(cpu, modrm.reg); 45 | union cpu_register* dest = modrm_to_register(cpu, modrm.rm); 46 | cpu_arithmetic_and_u8(cpu, &dest->regs_8, &source->regs_8); 47 | } else { 48 | // TODO: Implement if mod != 3 49 | log_fatal("Only MOD 3 for opcode 0x20 is supported"); 50 | } 51 | } 52 | 53 | /* 54 | * 0x43: INC dx 55 | */ 56 | OPCODE_DEFINE(43) 57 | { 58 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 59 | log_trace("inc ebx"); 60 | cpu_arithmetic_inc_u32(cpu, &cpu->bx.regs_32); 61 | } else { 62 | log_trace("inc bx"); 63 | cpu_arithmetic_inc_u16(cpu, &cpu->bx.regs_16); 64 | } 65 | } 66 | 67 | /* 68 | * 0x47: INC di 69 | */ 70 | OPCODE_DEFINE(47) 71 | { 72 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 73 | log_trace("inc edi"); 74 | cpu_arithmetic_inc_u32(cpu, &cpu->di.regs_32); 75 | } else { 76 | log_trace("inc di"); 77 | cpu_arithmetic_inc_u16(cpu, &cpu->di.regs_16); 78 | } 79 | } 80 | 81 | /* 82 | * 0x52: PUSH dx 83 | */ 84 | OPCODE_DEFINE(52) 85 | { 86 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 87 | log_trace("push edx"); 88 | push_u32(cpu, cpu->dx.regs_32); 89 | } else { 90 | log_trace("push dx"); 91 | push_u16(cpu, cpu->dx.regs_16); 92 | } 93 | } 94 | 95 | /* 96 | * 0x53: PUSH bx 97 | */ 98 | OPCODE_DEFINE(53) 99 | { 100 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 101 | log_trace("push ebx"); 102 | push_u32(cpu, cpu->bx.regs_32); 103 | } else { 104 | log_trace("push bx"); 105 | push_u16(cpu, cpu->bx.regs_16); 106 | } 107 | } 108 | 109 | /* 110 | * 0x57: PUSH di 111 | */ 112 | OPCODE_DEFINE(57) 113 | { 114 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 115 | log_trace("push edi"); 116 | push_u32(cpu, cpu->di.regs_32); 117 | } else { 118 | log_trace("push di"); 119 | push_u16(cpu, cpu->di.regs_16); 120 | } 121 | } 122 | 123 | /* 124 | * 0x5A: POP dx 125 | */ 126 | OPCODE_DEFINE(5A) 127 | { 128 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 129 | log_trace("pop edx"); 130 | cpu->dx.regs_32 = pop_u32(cpu); 131 | } else { 132 | log_trace("pop dx"); 133 | cpu->dx.regs_16 = pop_u16(cpu); 134 | } 135 | } 136 | 137 | /* 138 | * 0x5B: POP bx 139 | */ 140 | OPCODE_DEFINE(5B) 141 | { 142 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 143 | log_trace("pop ebx"); 144 | cpu->bx.regs_32 = pop_u32(cpu); 145 | } else { 146 | log_trace("pop bx"); 147 | cpu->bx.regs_16 = pop_u16(cpu); 148 | } 149 | } 150 | 151 | /* 152 | * 0x5F: POP di 153 | */ 154 | OPCODE_DEFINE(5F) 155 | { 156 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 157 | log_trace("pop edi"); 158 | cpu->di.regs_32 = pop_u32(cpu); 159 | } else { 160 | log_trace("pop di"); 161 | cpu->di.regs_16 = pop_u16(cpu); 162 | } 163 | } 164 | 165 | /* 166 | * 0x75: JNZ/JNE rel8 167 | */ 168 | OPCODE_DEFINE(75) 169 | { 170 | log_trace("jnz rel8"); 171 | int8_t offset = cpu_fetch_instruction_u8(cpu); 172 | uint32_t ip = ((CPU_PREFIX_STATE_OPERAND32(cpu)) ? cpu->ip.regs_32 : 173 | cpu->ip.regs_16) + 174 | offset; 175 | if (!cpu_get_zf(cpu)) 176 | cpu->ip.regs_32 = ip; 177 | } 178 | 179 | OPCODE_DEFINE(83) 180 | { 181 | log_trace("add r/m16/32, imm8"); 182 | struct modrm modrm; 183 | raw_to_modrm(cpu_fetch_instruction_u8(cpu), &modrm); 184 | if (modrm.mod == 3) { 185 | // TODO: Implement if mod != 0 186 | log_fatal("Only MOD 0 for opcode 0x83 is supported"); 187 | } else { 188 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 189 | uint16_t value = cpu_fetch_instruction_u16(cpu); 190 | uint16_t imm8 = cpu_fetch_instruction_u8(cpu); 191 | log_trace("Value: %X, imm8: %X\n", value, imm8); 192 | switch (modrm.reg) { 193 | case 7: 194 | cpu_arithmetic_cmp_u16(cpu, &value, &imm8); 195 | break; 196 | default: 197 | log_fatal("Unsupported modrm.reg!"); 198 | } 199 | } else { 200 | log_fatal("Only 32-bit for opcode 0x83 is supported!"); 201 | } 202 | } 203 | } 204 | 205 | /* 206 | * 0x86: XCHG r8, r/m8 207 | */ 208 | OPCODE_DEFINE(86) 209 | { 210 | log_trace("xchg r8, r/m8"); 211 | struct modrm modrm; 212 | raw_to_modrm(cpu_fetch_instruction_u8(cpu), &modrm); 213 | if (modrm.mod == 3) { 214 | // Direct addressing mode, rm is source, reg is dest because d == 1 215 | union cpu_register* a = modrm_to_register(cpu, modrm.reg); 216 | union cpu_register* b = modrm_to_register(cpu, modrm.rm); 217 | uint8_t temp = a->regs_8; 218 | a->regs_8 = b->regs_8; 219 | b->regs_8 = temp; 220 | } else { 221 | // TODO: Implement if mod != 3 222 | log_fatal("Only MOD 3 for opcode 0x86 is supported"); 223 | } 224 | } 225 | 226 | /* 227 | * 0x88: r/m8, r8 228 | */ 229 | OPCODE_DEFINE(88) 230 | { 231 | log_trace("mov r/m8, r8"); 232 | cpu->modrm = cpu_fetch_instruction_u8(cpu); 233 | store_modrm_rm8(cpu, fetch_modrm_r8(cpu)); 234 | } 235 | 236 | /* 237 | * 0x89: MOV r/m16/32, r16/32 238 | */ 239 | 240 | OPCODE_DEFINE(89) 241 | { 242 | log_trace("mov r/m16/32, r16/32"); 243 | struct modrm modrm; 244 | raw_to_modrm(cpu_fetch_instruction_u8(cpu), &modrm); 245 | if (modrm.mod == 3) { 246 | // Direct addressing mode, reg is source, rm is dest because d == 0 247 | union cpu_register* source = modrm_to_register(cpu, modrm.reg); 248 | union cpu_register* dest = modrm_to_register(cpu, modrm.rm); 249 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 250 | dest->regs_32 = source->regs_32; 251 | } else { 252 | dest->regs_16 = source->regs_16; 253 | } 254 | } else { 255 | union cpu_register* source = modrm_to_register(cpu, modrm.reg); 256 | addr_t dest = modrm_to_address(cpu, modrm.mod, modrm.rm); 257 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 258 | cpu_store_u32(cpu, &cpu->ds, dest, source->regs_32); 259 | } else { 260 | cpu_store_u16(cpu, &cpu->ds, dest, source->regs_16); 261 | } 262 | } 263 | } 264 | 265 | /* 266 | * 0x8A: MOV r8, r/m8 267 | */ 268 | OPCODE_DEFINE(8A) 269 | { 270 | log_trace("mov r8, r/m8"); 271 | struct modrm modrm; 272 | raw_to_modrm(cpu_fetch_instruction_u8(cpu), &modrm); 273 | if (modrm.mod == 3) { 274 | // TODO; Implement if mod == 3 275 | log_fatal("MOD 3 for opcode 0x8A is unsupported"); 276 | } else { 277 | // Direct addressing mode, rm is source, reg is dest because d == 1 278 | union cpu_register* dest = modrm_to_register(cpu, modrm.reg); 279 | dest->regs_16 = cpu_fetch_u8( 280 | cpu, &cpu->ds, modrm_to_address(cpu, modrm.mod, modrm.rm)); 281 | } 282 | } 283 | 284 | OPCODE_DEFINE(8B) 285 | { 286 | log_trace("mov r8, r/m8"); 287 | struct modrm modrm; 288 | raw_to_modrm(cpu_fetch_instruction_u8(cpu), &modrm); 289 | if (modrm.mod == 3) { 290 | // TODO; Implement if mod == 3 291 | log_fatal("MOD 3 for opcode 0x8A is unsupported"); 292 | } else { 293 | // Direct addressing mode, rm is source, reg is dest because d == 1 294 | union cpu_register* dest = modrm_to_register(cpu, modrm.reg); 295 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 296 | dest->regs_32 = cpu_fetch_u32( 297 | cpu, &cpu->ds, modrm_to_address(cpu, modrm.mod, modrm.rm)); 298 | } else { 299 | dest->regs_16 = cpu_fetch_u16( 300 | cpu, &cpu->ds, modrm_to_address(cpu, modrm.mod, modrm.rm)); 301 | } 302 | } 303 | } 304 | 305 | /* 306 | * 0xB8: MOV r16/32, imm16/32 307 | */ 308 | OPCODE_DEFINE(B8) 309 | { 310 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 311 | log_trace("mov eax, imm16"); 312 | cpu->ax.regs_32 = cpu_fetch_instruction_u32(cpu); 313 | } else { 314 | log_trace("mov ax, imm16"); 315 | cpu->ax.regs_16 = cpu_fetch_instruction_u16(cpu); 316 | } 317 | } 318 | 319 | /* 320 | * 0xBB: MOV bx, imm16/32 321 | */ 322 | OPCODE_DEFINE(BB) 323 | { 324 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 325 | log_trace("mov ebx, imm16/32"); 326 | cpu->bx.regs_32 = cpu_fetch_instruction_u32(cpu); 327 | } else { 328 | log_trace("mov bx, imm16/32"); 329 | cpu->bx.regs_16 = cpu_fetch_instruction_u16(cpu); 330 | } 331 | } 332 | 333 | /* 334 | * 0xBC: MOV sp, imm16/32 335 | */ 336 | OPCODE_DEFINE(BC) 337 | { 338 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 339 | log_trace("mov esp, imm16/32"); 340 | cpu->sp.regs_32 = cpu_fetch_instruction_u32(cpu); 341 | } else { 342 | log_trace("mov sp, imm16/32"); 343 | cpu->sp.regs_16 = cpu_fetch_instruction_u16(cpu); 344 | } 345 | } 346 | /* 347 | * 0xC3: RETN 348 | */ 349 | OPCODE_DEFINE(C3) 350 | { 351 | log_trace("retn"); 352 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 353 | cpu->ip.regs_32 = pop_u32(cpu); 354 | } else { 355 | cpu->ip.regs_16 = pop_u16(cpu); 356 | } 357 | } 358 | 359 | /* 360 | * 0xE8: CALL rel16/32 361 | */ 362 | OPCODE_DEFINE(E8) 363 | { 364 | log_trace("call rel16/32"); 365 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 366 | int32_t offset = cpu_fetch_instruction_u32(cpu); 367 | uint32_t eip = cpu->ip.regs_32 + offset; 368 | push_u32(cpu, cpu->ip.regs_32); 369 | cpu->ip.regs_32 = eip; 370 | } else { 371 | int16_t offset = cpu_fetch_instruction_u16(cpu); 372 | uint16_t eip = cpu->ip.regs_16 + offset; 373 | push_u16(cpu, cpu->ip.regs_16); 374 | cpu->ip.regs_16 = eip; 375 | } 376 | } 377 | 378 | /* 379 | * 0xEA: JMPF ptr16:16/32 380 | */ 381 | OPCODE_DEFINE(EA) 382 | { 383 | log_trace("jmpf ptr16:16/32"); 384 | uint32_t eip = 0; 385 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 386 | eip = cpu_fetch_instruction_u32(cpu); 387 | } else { 388 | eip = cpu_fetch_instruction_u16(cpu); 389 | } 390 | uint16_t cs = cpu_fetch_instruction_u16(cpu); 391 | cpu->cs.base = cs << 4; 392 | cpu->cs.selector = cs; 393 | if (CPU_PREFIX_STATE_OPERAND32(cpu)) { 394 | cpu->ip.regs_32 = eip; 395 | } else { 396 | cpu->ip.regs_16 = eip; 397 | } 398 | } 399 | 400 | /* 401 | * 0xF4: HLT 402 | */ 403 | OPCODE_DEFINE(F4) 404 | { 405 | log_trace("hlt"); 406 | cpu->state = CPU_HALTED; 407 | } 408 | 409 | static opcode_fn_t opcode_table[256] = { 410 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 411 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 412 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 413 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 414 | NULL, NULL, NULL, NULL, opcode20, NULL, NULL, 415 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 416 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 417 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 418 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 419 | NULL, NULL, NULL, NULL, opcode43, NULL, NULL, 420 | NULL, opcode47, NULL, NULL, NULL, NULL, NULL, 421 | NULL, NULL, NULL, NULL, NULL, opcode52, opcode53, 422 | NULL, NULL, NULL, opcode57, NULL, NULL, opcode5A, 423 | opcode5B, NULL, NULL, NULL, opcode5F, NULL, NULL, 424 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 425 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 426 | NULL, NULL, NULL, NULL, NULL, opcode75, NULL, 427 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 428 | NULL, NULL, NULL, NULL, NULL, opcode83, NULL, 429 | NULL, opcode86, NULL, opcode88, opcode89, opcode8A, opcode8B, 430 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 431 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 432 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 433 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 434 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 435 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 436 | NULL, NULL, opcodeB8, NULL, NULL, opcodeBB, opcodeBC, 437 | NULL, NULL, NULL, NULL, NULL, NULL, opcodeC3, 438 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 439 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 440 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 441 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 442 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 443 | NULL, opcodeE8, NULL, opcodeEA, NULL, NULL, NULL, 444 | NULL, NULL, NULL, NULL, NULL, NULL, opcodeF4, 445 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, 446 | NULL, NULL, NULL, NULL, 447 | }; 448 | 449 | void opcode_execute(struct cpu* cpu) 450 | { 451 | uint8_t opcode = 0; 452 | bool isPrefix = 1; 453 | while (isPrefix) { 454 | switch ((opcode = cpu_fetch_instruction_u8(cpu))) { 455 | case 0x2E: 456 | log_trace("Prefix: %X", opcode); 457 | cpu->prefix_state.segment = &cpu->cs; 458 | break; 459 | case 0x66: 460 | log_trace("Prefix: %X", opcode); 461 | cpu->prefix_state.operand32 = 1; 462 | break; 463 | default: 464 | isPrefix = 0; 465 | break; 466 | } 467 | } 468 | if (opcode == 0x0F) { 469 | opcode_two_execute(cpu); 470 | } else { 471 | log_trace("Opcode: %X", opcode); 472 | if (!opcode_table[opcode]) { 473 | log_fatal("Unknown opcode, should probably throw an Invalid Opcode " 474 | "exception, got opcode 0x%X at IP 0x%X", 475 | opcode, cpu->cs.base + cpu->ip.regs_16 - 1); 476 | cpu->state = CPU_HALTED; 477 | return; 478 | } 479 | opcode_table[opcode](cpu); 480 | } 481 | 482 | // Reset CPU prefix state 483 | cpu->prefix_state.segment = NULL; 484 | cpu->prefix_state.operand32 = 0; 485 | cpu->prefix_state.repne = 0; 486 | cpu->prefix_state.repe = 0; 487 | } 488 | --------------------------------------------------------------------------------