From 9c89bbcfc2f4ee6e4139e0aea1cf803778790a04 Mon Sep 17 00:00:00 2001 From: Ole Morud Date: Wed, 15 Oct 2025 23:20:23 +0200 Subject: [PATCH] more wip --- Makefile | 8 +- linker.ld | 13 +- src/kernel/file_system.c | 291 +++++++++++++++++++++ src/kernel/file_system.h | 60 +++++ src/kernel/interrupts.c | 6 +- src/kernel/kernel.c | 86 +++--- src/kernel/malloc.h | 9 + src/kernel/serial.h | 192 ++++++++++++++ src/kernel/syscall.c | 60 +++++ src/kernel/syscall.h | 20 ++ src/kernel/syscall_handler.S | 30 +++ src/lib/dump_asm.c | 493 +++++++++++++++++++++++++++++++++++ src/lib/dump_asm.h | 0 src/lib/printf.c | 26 ++ 14 files changed, 1255 insertions(+), 39 deletions(-) create mode 100644 src/kernel/file_system.c create mode 100644 src/kernel/file_system.h create mode 100644 src/kernel/malloc.h create mode 100644 src/kernel/serial.h create mode 100644 src/kernel/syscall.c create mode 100644 src/kernel/syscall.h create mode 100644 src/kernel/syscall_handler.S create mode 100644 src/lib/dump_asm.c create mode 100644 src/lib/dump_asm.h diff --git a/Makefile b/Makefile index 7ca0817..14671c1 100644 --- a/Makefile +++ b/Makefile @@ -24,8 +24,9 @@ LD := $(CONTAINER_CMD) i686-elf-ld AS := $(CONTAINER_CMD) i686-elf-as AR := $(CONTAINER_CMD) i686-elf-ar -C_SOURCES := $(shell find $(SOURCE_DIR) ! -name '*_test*' -name '*.c') -ASM_SOURCES := $(shell find $(SOURCE_DIR) -name '*.S') +C_SOURCES := $(shell set -x; find $(SOURCE_DIR) -path $(SOURCE_DIR)/userspace -prune -o -type f -name '*.c' ! -name '*_test*' -print) +$(info C_SOURCES=$(C_SOURCES)) +ASM_SOURCES := $(shell find $(SOURCE_DIR) -name '*.S' ! -name '*userspace*') OBJECTS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.o) $(ASM_SOURCES:.S=.o)) DEPENDS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.d)) @@ -59,7 +60,7 @@ myos.iso: $(BUILD_DIR)/myos.bin $(BUILD_DIR)/myos.bin: $(OBJECTS) @mkdir -p $(@D) - $(CC) -T linker.ld -o $@ $(CFLAGS) -nostdlib $^ -lgcc + $(CC) -T linker.ld -o $@ $(CFLAGS) -nostdlib $(OBJECTS) -lgcc -include $(DEPENDS) @@ -75,7 +76,6 @@ $(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.S Makefile @mkdir -p $(@D) $(AS) $(ASFLAGS) $< -o $@ - ################### # TESTS # ################### diff --git a/linker.ld b/linker.ld index b33280d..f283719 100644 --- a/linker.ld +++ b/linker.ld @@ -58,11 +58,20 @@ SECTIONS { *(COMMON) *(.bss) - - kernel_memory_end = .; } + kernel_memory_end = 0x400000; + . = 0x400000; + /* The compiler may produce other sections, by default it will put them in a segment with the same name. Simply add stuff here as needed. */ + + .userland-text ALIGN(16) : { + *(.userland-text) + } + + .userland-rodata ALIGN(16) : { + *(.userland-rodata) + } } diff --git a/src/kernel/file_system.c b/src/kernel/file_system.c new file mode 100644 index 0000000..57ffee8 --- /dev/null +++ b/src/kernel/file_system.c @@ -0,0 +1,291 @@ + +#include "file_system.h" + +#include +#include +#include + +#include "malloc.h" +#include "libc.h" +#include "str.h" + +#define MIN(a, b) ((a) > (b) ? (b) : (a)) + +/* + * File node implementation + * ======================== + * */ +static void node_init(struct file_node* n, uint32_t block_offset) +{ + n->block_offset = block_offset; + n->next = NULL; +} + +/** + * [path -> file] mapping + * ======== + */ +static uint32_t djb2_hash(struct str key) +{ + /* credits: Daniel J. Bernstein */ + uint32_t hash = 5381; + for (size_t i = 0; i < key.len; i++) { + hash <<= 5; + hash += (uint32_t)key.data[i]; + } + return hash; +} + +static struct file_path* file_map_at(struct file_path_map* file_map, struct str path) +{ + const uint32_t hash = djb2_hash(path) % FILE_MAP_SIZE; + return &(file_map->entries[hash]); +} + +static inline struct file_path* collision_alternative(struct file_path_map* file_map, struct file_path* f) +{ + /* i = (i + 1) % FILE_MAP_SIZE */ + return ((f - file_map->entries + 1) % FILE_MAP_SIZE) + file_map->entries; +} + +struct file_path* file_map_get(struct file_path_map* file_map, struct str path) +{ + struct file_path* f = file_map_at(file_map, path); + + for (int i = 0; i < FILE_MAP_SIZE; i++) { + if ((f->attr & FILE_PRESENT) == 0) { + return NULL; + } + uint32_t min = MIN(path.len, f->name_len); + if (memcmp(path.data, f->name, min) == 0) { + return f; + } + f = collision_alternative(file_map, f); + } + + return NULL; +} + +struct file_path* file_map_insert(struct file_path_map* map, struct str path) +{ + if (map->count == FILE_MAP_SIZE) { + return NULL; + } + struct file_path* f = file_map_at(map, path); + + for (int i = 0; i < FILE_MAP_SIZE; i++) { + uint32_t min = MIN(path.len, f->name_len); + if (memcmp(path.data, f->name, min) == 0) { + return NULL /* file with name already exists */; + } + f = collision_alternative(map, f); + if ((f->attr & FILE_PRESENT) == 0) { + return f; + } + } + return NULL; +} + + +/** + * Ramdisk implementation + * =========== + */ +constexpr int RAMDISK_BLOCK_SIZE = 4096; +constexpr int RAMDISK_BLOCK_COUNT = 128; + +uint8_t ramdisk_data[RAMDISK_BLOCK_SIZE * RAMDISK_BLOCK_COUNT]; +uint32_t ramdisk_free_bitmap[RAMDISK_BLOCK_COUNT / (sizeof(uint32_t) * CHAR_BIT)]; + +/* returns block offset */ +int ramdisk_allocate_block(struct file_system* fs) +{ + int free_pos = bitmap_find(&fs->free_blocks_bitmap, 0); + if (free_pos == -1) { + return -1; + } + if (bitmap_set(&fs->free_blocks_bitmap, free_pos)) { + /* this means there's an error in the bitmap implementation */ + panic(str_attach("failed to set bitmap when allocating block")); + } + return free_pos * RAMDISK_BLOCK_SIZE; +} + +bool ramdisk_write_block(struct file_system*, uint32_t block_offset, const uint8_t* buf) +{ + /* for now just use the memory as a file system */ + for (size_t i = 0; i < RAMDISK_BLOCK_SIZE; i++) { + ramdisk_data[block_offset + i] = buf[i]; + } + return true; +} + +bool ramdisk_read_block(struct file_system*, uint32_t block_offset, uint8_t* output) +{ + for (size_t i = 0; i < RAMDISK_BLOCK_SIZE; i++) { + output[i] = ramdisk_data[block_offset + i]; + } + return true; +} + +struct file_system g_ramdisk = { + .free_blocks_bitmap = BITMAP_ATTACH(ramdisk_free_bitmap, + sizeof(ramdisk_free_bitmap)), + + .block_size = RAMDISK_BLOCK_SIZE, + .block_count = RAMDISK_BLOCK_COUNT, + + .file_write_block = ramdisk_write_block, + .file_read_block = ramdisk_read_block, + .file_allocate_block = ramdisk_allocate_block, +}; + + +/** + * Higher level interfaces + * ======================= + */ +struct file_path* file_create(struct file_system* fs, struct str path) +{ + struct file_path* f = file_map_insert(&fs->file_map, path); + + memset(f, 0, sizeof *f); + f->attr |= FILE_PRESENT; + memcpy(f->name, path.data, MIN(path.len, FILE_NAME_MAX)); + + return f; +} + +struct file_path* file_open(struct file_system* fs, struct str path) +{ + return file_map_get(&fs->file_map, path); +} + +bool file_write(struct file_system* fs, struct file_path* f, const uint8_t* data, size_t n) +{ + struct file_node** node = &f->node; + size_t written = 0; + + while (written < n) { + if (*node == NULL) { + struct file_node* new_node = kalloc(sizeof *new_node); + if (new_node == NULL) { + panic(str_attach("failed to allocate new node for file")); + } + int offset = fs->file_allocate_block(fs); + if (offset == -1) { + panic(str_attach("failed to allocate new block in disk")); + } + node_init(new_node, offset); + *node = new_node; + } + + bool ok = fs->file_write_block(fs, (*node)->block_offset, &data[written]); + if (!ok) { + panic(str_attach("file_write_byte failed")); + } + written += fs->block_size; + + node = &((*node)->next); + } + + return true; +} + +bool file_read(struct file_system* fs, struct file_path* f, uint8_t* out, int n) +{ + uint8_t buf[MAX_FILE_BLOCK_SIZE]; + static int32_t last_read_block = -1; + + struct file_node dummy = {.next = f->node}; + struct file_node* node = &dummy; + int read = 0; + + while (read < n) { + if (!node->next) { + panic(str_attach("no next block found when reading")); + } + node = node->next; + + bool ok; + int remaining = n - read; + if (remaining > fs->block_size) { + ok = fs->file_read_block(fs, node->block_offset, &out[read]); + if (!ok) { + panic(str_attach("file_read_block failed")); + } + read += fs->block_size; + } else { + ok = fs->file_read_block(fs, node->block_offset, buf); + if (!ok) { + panic(str_attach("file_read_block failed")); + } + memcpy(&out[read], buf, remaining); + read += n - read; + } + } + return true; +} + +/* TODO: remove this hacky test technique! */ + +int test_file_system() +{ + struct file_path* f = file_create(&g_ramdisk, str_attach("test_file")); + + const uint8_t data[] = "hello world!!!!\n"; + printf(str_attach("trying to write to file {cstr}"), f->name); + bool ok = file_write(&g_ramdisk, f, data, sizeof data); + if (!ok) { + printf(str_attach("\nfailed to write to file {cstr}\n"), f->name); + return -1; + } + printf(str_attach(" - OK!\n")); + + printf(str_attach("trying to read from file {cstr}"), f->name); + uint8_t read_buf[sizeof data + 1]; + read_buf[sizeof data] = '\0'; + ok = file_read(&g_ramdisk, f, read_buf, sizeof data); + if (!ok) { + printf(str_attach("\nfailed to read from file {cstr}\n"), f->name); + return -1; + } + + if (memcmp(read_buf, data, sizeof data) != 0) { + printf(str_attach("data mismatch between write and read\n")); + return -1; + } + printf(str_attach(" - OK!, output was:\n{cstr}\n"), read_buf); + + printf(str_attach("attempting to get a file that doesn't exist")); + { + struct file_path* f = file_map_get(&g_ramdisk.file_map, str_attach("asd")); + if (f) { + printf(str_attach("\ngot file when no such file should exist\n")); + return -1; + } + } + printf(str_attach(" - OK!\n")); + + uint8_t big_data[MAX_FILE_BLOCK_SIZE * 2 + 3] = {0}; + memset(big_data, 0xAB, sizeof big_data); + f = file_create(&g_ramdisk, str_attach("test_big_file")); + printf(str_attach("attempting to write more than a block to {cstr}"), f->name); + ok = file_write(&g_ramdisk, f, big_data, sizeof big_data); + if (!ok) { + printf(str_attach("\nfailed to write data larger than a block\n")); + return -1; + } + printf(str_attach(" - OK!\n")); + + printf(str_attach("trying to read from file {cstr}"), f->name); + uint8_t big_read_buf[sizeof big_data]; + ok = file_read(&g_ramdisk, f, big_read_buf, sizeof big_read_buf); + if (!ok) { + printf(str_attach("\nfailed to read from file {cstr}\n"), f->name); + return -1; + } + printf(str_attach(" - OK!\n")); + + return 0; +} diff --git a/src/kernel/file_system.h b/src/kernel/file_system.h new file mode 100644 index 0000000..e31394b --- /dev/null +++ b/src/kernel/file_system.h @@ -0,0 +1,60 @@ +#pragma once + +#include "bitmap.h" +#include "str.h" + +constexpr int FILE_NAME_MAX = 31; +constexpr int FILE_MAP_SIZE = 128; +constexpr int MAX_FILE_BLOCK_SIZE = 4096; + +enum file_attr : uint32_t { + FILE_PRESENT = 1<<0, +}; + +struct file_node { + struct file_node* next; + uint32_t block_offset; +}; + + +struct file_path { + uint32_t attr; + uint32_t size; + + uint32_t name_len; + char name[FILE_NAME_MAX+1]; + + struct file_node* node; +}; + +/* TODO: clean up this mess:*/ +struct file_system { + /* map of paths */ + struct file_path_map { + struct file_path entries[FILE_MAP_SIZE]; + uint32_t count; + } file_map; + + /* map of free blocks */ + struct bitmap free_blocks_bitmap; + + int block_size; + int block_count; + + /* virtual methods */ + int (*file_allocate_block)(struct file_system*); + bool (*file_write_block)(struct file_system*, uint32_t, const uint8_t *data); + bool (*file_read_block)(struct file_system*, uint32_t, uint8_t *output); +}; + + +int test_file_system(); + +struct file_path* file_open(struct file_system* fs, struct str path); + +bool file_write(struct file_system* fs, struct file_path* f, const uint8_t* data, size_t n); + +bool file_read(struct file_system* fs, struct file_path* f, uint8_t* out, int n); + + +extern struct file_system g_ramdisk; diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index afccc29..e1dafd3 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -128,7 +128,7 @@ void exception_handler_page_fault(struct interrupt_frame* frame, int err) static const struct str error_flag_str[page_fault_error_count] = { [present] = str_attach("Caused by a page-protection violation"), [write] = str_attach("Caused by a write access"), - [user] = str_attach("Caused while CPL == 3 (not neccessarily a privilige escalation)"), + [user] = str_attach("Caused by userspace (not neccessarily a privilige escalation)"), [reserved_write] = str_attach("One or more page directory entries contain reserved bits which are set to 1"), [instruction_fetch] = str_attach("Caused by an instruction fetch"), [protection_key] = str_attach("Caused by a protection-key violation"), @@ -139,7 +139,7 @@ void exception_handler_page_fault(struct interrupt_frame* frame, int err) static const struct str error_flag_zero_str[page_fault_error_count] = { [present] = str_attach("Caused by a non-present page"), [write] = str_attach("Caused by a read access"), - [user] = str_attach("Caused while CPL != 3"), + [user] = str_attach("Caused by ring 0"), }; for (size_t i = 0; i < page_fault_error_count; i++) { @@ -151,6 +151,8 @@ void exception_handler_page_fault(struct interrupt_frame* frame, int err) } printf(str_attach("\n")); + printf(str_attach("Instruction(s) that caused violation: {x32}\n"), *(uint32_t*)frame->ip); + print_interrupt_frame(frame); __asm__ volatile("cli; hlt"); diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 6de62b3..58d0f0d 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -20,23 +20,13 @@ #include "bitmap.h" #include "syscall.h" -#define syscall(number, b, c, d) \ - __asm__ volatile( \ - "int $0x80\n" \ - : \ - : "a"(number), \ - "b"(b), \ - "c"(c), \ - "d"(d) \ - : "memory" \ - ); - -static void user_mode_code(void*) +__attribute__((section(".userland-text"))) +void user_mode_code(void*) { - //printf(str_attach("hello from user-space before interrupt :)\n")); - //__asm__ volatile ("int $0x80"); - int output; - syscall(SYSCALL_PRINT, &str_attach("hello from ring 3\n"), 0, 0); + const char msg[] = "hello from ring 3\n"; + syscall(SYSCALL_PRINT, &str_attach(msg), 0, 0); + + volatile uint32_t a = *(uint32_t*)0; #if 0 syscall(SYSCALL_PRINT, &str_attach("trying to divide by zero :)\n"), 0, 0); #pragma GCC diagnostic push @@ -51,10 +41,30 @@ static void user_mode_code(void*) syscall(SYSCALL_EXIT, 0, 0, 0); - while (1) - ; + //#pragma GCC diagnostic push + //#pragma GCC diagnostic ignored "-Wunused-value" + //*(uint32_t*)0; + //#pragma GCC diagnostic pop } +typedef uint32_t pde_t; + +struct proc { + uint32_t (*page_directory)[1024]; + uint8_t* kernel_stack; + char name[16]; + uint32_t mem_size; + + // TODO + //struct trapframe *tf; + //struct context *context; // swtch() here to run process + //void *chan; // If non-zero, sleeping on chan + //int killed; // If non-zero, have been killed + //struct file *ofile[NOFILE]; // Open files + //struct inode *cwd; // Current directory +}; + + static void ring3_mode(segment_t udata_segment, segment_t ucode_segment, func_t callback) { __asm__ volatile ( @@ -164,6 +174,7 @@ void kernel_main(void) * Setup the TSS * ============= */ static uint8_t kernel_stack[KERNEL_STACK_SIZE]; + memset(&kernel.tss, 0, sizeof kernel.tss); kernel.tss.ss0 = segment(SEGMENT_KERNEL_DATA, SEGMENT_GDT, 0); kernel.tss.esp0 = (uint32_t)kernel_stack; tss_load(segment(SEGMENT_TASK_STATE, SEGMENT_GDT, 0)); @@ -242,30 +253,36 @@ void kernel_main(void) /** * Paging setup * ============ - * We align by 1<<12 because page directory and page table entries store - * addresses from bit 12-31 + * We align by (1<<12) == 4096 because page directory and page table + * entries store addresses from bit 12-31 * * For now give user access to pages to avoid a page fault, since it's not * implemented properly */ printf(str_attach("setting up paging...\n")); + printf(str_attach("kernel end: {bin}\n"), kernel_memory_end); - static uint32_t page_directory[1024] __attribute__((aligned(4096))); - static uint32_t page_table_0[1024] __attribute__((aligned(4096))); - _Static_assert(((uint32_t)page_directory & 0xfff) == 0); - _Static_assert(((uint32_t)page_table_0 & 0xfff) == 0); + static uint32_t page_directory[1024] __attribute__((aligned(4096))); + static uint32_t page_table_kernel[1024] __attribute__((aligned(4096))); + static uint32_t page_table_user[1024] __attribute__((aligned(4096))); + _Static_assert(((uint32_t)page_directory & 0xfff) == 0); + _Static_assert(((uint32_t)page_table_kernel & 0xfff) == 0); + _Static_assert(((uint32_t)page_table_user & 0xfff) == 0); for (size_t i = 0; i < sizeof page_directory / sizeof *page_directory; i++) { page_directory[i] = PDE_WRITE; /* no present bit */ } - page_directory[1023] = (uint32_t)page_directory; + page_directory[sizeof page_directory / sizeof *page_directory - 1] = (uint32_t)page_directory | PDE_WRITE | PDE_PRESENT; - for (size_t i = 0; i < sizeof page_table_0 / sizeof *page_table_0; i++) { - // for now this page table allows user-space code to access - // kernel-space memory (PTE_USER) - page_table_0[i] = PTE_ADDRESS(i) | PTE_WRITE | PTE_PRESENT | PTE_USER; + for (size_t i = 0; i < sizeof page_table_kernel / sizeof *page_table_kernel; i++) { + page_table_kernel[i] = (i<<12) | PTE_WRITE | PTE_PRESENT; } - page_directory[0] = ((uint32_t)page_table_0) | PDE_WRITE | PDE_PRESENT | PDE_USER_ACCESS; + page_directory[0] = ((uint32_t)page_table_kernel) | PDE_WRITE | PDE_PRESENT | PDE_USER_ACCESS; + + for (size_t i = 0; i < sizeof page_table_user / sizeof *page_table_user; i++) { + page_table_user[i] = ((uint32_t)kernel_memory_end + (i<<12)) | PTE_WRITE | PTE_PRESENT | PTE_USER; + } + page_directory[1] = ((uint32_t)page_table_user) | PDE_WRITE | PDE_PRESENT | PDE_USER_ACCESS; cr3_set((uint32_t)page_directory); cr0_flags_set(CR0_PAGING | CR0_PROTECTED_MODE); @@ -274,6 +291,14 @@ void kernel_main(void) printf(str_attach("starting code in ring 3...\n")); + /* Load userspace binary to memory */ + + printf(str_attach("address of userspace code: {x32} = {addr}\n"), user_mode_code, user_mode_code); + + printf(str_attach("im going ring 3 mode\n")); + + //new_proc(); + /* Finally go to ring 3 */ ring3_mode(segment(SEGMENT_USER_DATA, SEGMENT_GDT, 3), segment(SEGMENT_USER_CODE, SEGMENT_GDT, 3), @@ -283,4 +308,3 @@ void kernel_main(void) __asm__ volatile ("hlt"); } - diff --git a/src/kernel/malloc.h b/src/kernel/malloc.h new file mode 100644 index 0000000..dbe706f --- /dev/null +++ b/src/kernel/malloc.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +void* kalloc(size_t size); + +void kfree(void* ptr); + +void* krealloc(void* ptr, size_t size); diff --git a/src/kernel/serial.h b/src/kernel/serial.h new file mode 100644 index 0000000..822cff2 --- /dev/null +++ b/src/kernel/serial.h @@ -0,0 +1,192 @@ +#pragma once + +#include "str.h" + +/* + * TODO: consolidate memory mapped addresses into one header file, maybe per + * architecture + * */ + +//static uint8_t* SERIAL_COM1 = (uint8_t*)0x03F8; +//static uint8_t* SERIAL_COM2 = (uint8_t*)0x02F8; + +enum serial_port : uint16_t { + SERIAL_COM1 = 0x03F8, + SERIAL_COM2 = 0x02F8, +}; + +enum serial_port_offsets : uint8_t { + SERIAL_DATA_REGISTER = 0, + SERIAL_INTERRUPT = 1, + + SERIAL_DLAB_LSB = 0, /* when dlab is set this is the least + significant byte for the divisor*/ + SERIAL_DLAB_MSB = 1, /* ... and this is the most significant byte*/ + + SERIAL_INTERRUPT_IDENTIFICATION = 2, + SERIAL_FIFO_CONTROL = 2, + SERIAL_LINE_CONTROL = 3, + SERIAL_MODEM_CONTROL = 4, + SERIAL_LINE_STATUS = 5, + SERIAL_MODEM_STATUS = 6, + SERIAL_SCRATCH_REGISTER = 7, +}; + +enum serial_line_control : uint8_t { + SERIAL_LINE_DATA_5_BIT = 0b00<<0, + SERIAL_LINE_DATA_6_BIT = 0b01<<0, + SERIAL_LINE_DATA_7_BIT = 0b10<<0, + SERIAL_LINE_DATA_8_BIT = 0b11<<0, + + SERIAL_LINE_STOP_1 = 0<<2, + SERIAL_LINE_STOP_2 = 1<<2, + + SERIAL_LINE_PARITY_NONE = 0b000<<3, + SERIAL_LINE_PARITY_ODD = 0b001<<3, + SERIAL_LINE_PARITY_EVEN = 0b011<<3, + SERIAL_LINE_PARITY_MARK = 0b101<<3, + SERIAL_LINE_PARITY_SPACE = 0b111<<3, + + SERIAL_LINE_BREAK_ENABLE = 1<<6, + + SERIAL_LINE_DLAB = 1<<7, +}; + +enum serial_interrupt_control : uint8_t { + SERIAL_INTERRUPT_RECEIVED_DATA_AVAILABLE = 1<<0, + SERIAL_INTERRUPT_TRANSMITTER_HOLDING_REGISTER_EMPTY = 1<<1, + SERIAL_INTERRUPT_RECEIVER_LINE_STATUS = 1<<2, + SERIAL_INTERRUPT_MODEM_STATUS = 1<<3, +}; + +enum serial_fifo_control : uint8_t { + SERIAL_FIFO_ENABLE = 1<<0, + SERIAL_FIFO_CLEAR_RECEIVE = 1<<1, + SERIAL_FIFO_CLEAR_TRANSMIT = 1<<2, + SERIAL_FIFO_DMA_MODE = 1<<3, + SERIAL_FIFO_INTERRUPT_1_BYTE = 0<<6, + SERIAL_FIFO_INTERRUPT_4_BYTE = 1<<6, + SERIAL_FIFO_INTERRUPT_8_BYTE = 2<<6, + SERIAL_FIFO_INTERRUPT_14_BYTE = 3<<6, +}; + +enum serial_modem_control : uint8_t { + SERIAL_MODEM_DATA_READY = 1<<0, + SERIAL_MODEM_REQUEST_TO_SEND = 1<<1, + SERIAL_MODEM_OUT_1 = 1<<2, + SERIAL_MODEM_OUT_2 = 1<<3, + SERIAL_MODEM_ENABLE_IRQ = 1<<3, /* alias for out_2 */ + SERIAL_MODEM_LOOP = 1<<4, + SERIAL_MODEM_UNUSED_0 = 1<<5, + SERIAL_MODEM_UNUSED_1 = 1<<6, + SERIAL_MODEM_UNUSED_3 = 1<<7, +}; + +enum serial_line_status: uint8_t { + SERIAL_LINE_STATUS_DATA_READY = 1<<0, + SERIAL_LINE_STATUS_OVERRUN_ERROR = 1<<1, + SERIAL_LINE_STATUS_PARITY_ERROR = 1<<2, + SERIAL_LINE_STATUS_FRAMING_ERROR = 1<<3, + SERIAL_LINE_STATUS_BREAK_INDICATOR = 1<<4, + SERIAL_LINE_STATUS_TRANSMISSION_BUF_EMPTY = 1<<5, + SERIAL_LINE_STATUS_TRANSMITTER_EMPTY = 1<<6, + SERIAL_LINE_STATUS_IMPENDING_ERROR = 1<<7, +}; + +enum serial_modem_status : uint8_t { + SERIAL_MODEM_STATUS_DCTS = 1<<0, + SERIAL_MODEM_STATUS_DDSR = 1<<1, + SERIAL_MODEM_STATUS_TERI = 1<<2, + SERIAL_MODEM_STATUS_DDCD = 1<<3, + SERIAL_MODEM_STATUS_CTS = 1<<4, + SERIAL_MODEM_STATUS_DSR = 1<<5, + SERIAL_MODEM_STATUS_RI = 1<<6, + SERIAL_MODEM_STATUS_DCD = 1<<7, +}; + +static inline void outb(uint16_t port, uint8_t signal) +{ + __asm__ volatile ("outb %[signal], %[port]" + : + : [signal] "a" (signal), + [port] "Nd" (port) + : "memory"); +#ifdef ADD_IO_WAIT + /* do an I/O operation on an unused port to wait 1-4 microseconds */ + outb(0x80, 0); +#endif +} + +static inline uint8_t inb(uint16_t port) +{ + uint8_t ret; + __asm__ volatile ( "inb %[port], %[ret]" + : [ret] "=a"(ret) + : [port] "Nd"(port) + : "memory"); + return ret; +} + +static int serial_init(enum serial_port serial, uint16_t baud_divisor) +{ + outb(serial + SERIAL_INTERRUPT, SERIAL_LINE_DLAB); + outb(serial + SERIAL_LINE_CONTROL, SERIAL_LINE_DLAB); + outb(serial + SERIAL_DLAB_LSB, baud_divisor & 0x0F); + outb(serial + SERIAL_DLAB_MSB, (baud_divisor & 0xF0) >> 8); + outb(serial + SERIAL_LINE_CONTROL, SERIAL_LINE_DATA_8_BIT); + + outb(serial + SERIAL_FIFO_CONTROL, 0 + | SERIAL_FIFO_ENABLE + | SERIAL_FIFO_INTERRUPT_14_BYTE + | SERIAL_FIFO_CLEAR_RECEIVE); + + outb(serial + SERIAL_MODEM_CONTROL, 0 + | SERIAL_MODEM_DATA_READY + | SERIAL_MODEM_REQUEST_TO_SEND + | SERIAL_MODEM_ENABLE_IRQ); + + outb(serial + SERIAL_MODEM_CONTROL, 0 + | SERIAL_MODEM_REQUEST_TO_SEND + | SERIAL_MODEM_OUT_1 + | SERIAL_MODEM_OUT_2 + | SERIAL_MODEM_LOOP); + + constexpr uint8_t ping = 0xAE; + + outb(serial + SERIAL_DATA_REGISTER, ping); + + if (inb(serial + SERIAL_DATA_REGISTER) != ping) { + return -1; + } + + outb(serial + SERIAL_MODEM_CONTROL, 0 + | SERIAL_MODEM_REQUEST_TO_SEND + | SERIAL_MODEM_DATA_READY + | SERIAL_MODEM_OUT_1 + | SERIAL_MODEM_OUT_2 + ); + + return 0; +} + +static int serial_is_transmit_empty(enum serial_port port) { + return inb(port + SERIAL_LINE_STATUS) + & SERIAL_LINE_STATUS_TRANSMISSION_BUF_EMPTY; +} + +static void serial_putchar(enum serial_port port, char ch) +{ + while (serial_is_transmit_empty(port) == 0) + /* spin lock */; + + outb(port, ch); +} + +static void serial_write(enum serial_port port, struct str s) +{ + for (size_t i = 0; i < s.len; i++) { + serial_putchar(port, s.data[i]); + } +} + + diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c new file mode 100644 index 0000000..670a079 --- /dev/null +++ b/src/kernel/syscall.c @@ -0,0 +1,60 @@ + +#include "syscall.h" + +#include "libc.h" +#include "tty.h" +#include "interrupts.h" +#include "kernel_state.h" +#include "file_system.h" + +#define EXCEPTION_DEPTH_MAX 3 + +int syscall_main(int syscall_no, int a, int b, int c) +{ + int status = 0; + + switch (syscall_no) { + + case SYSCALL_PRINT: + { + terminal_write(*(struct str*)a); + } break; + + case SYSCALL_OPEN: + { + struct str path = *(struct str*)a; + struct file_path* f = file_open(&g_ramdisk, path); + } break; + + case SYSCALL_WRITE: + { + //bool file_write(struct file_system* fs, struct file_path* f, const uint8_t* data, size_t n); + struct file_path* filepath = (struct file_path*)a; + const uint8_t* data = (const uint8_t*)b; + const uint8_t n = (const uint8_t)c; + file_write(&g_ramdisk, filepath, data, n); + } break; + + case SYSCALL_READ: + { + //bool file_read(struct file_system* fs, struct file_path* f, uint8_t* out, int n); + struct file_path* filepath = (struct file_path*)a; + uint8_t* out = (uint8_t*)b; + const uint8_t n = (const uint8_t)c; + file_read(&g_ramdisk, filepath, out, n); + } break; + + case SYSCALL_EXIT: + { + + } break; + + default: + { + printf(str_attach("unknown syscall {i32}: a:{i32}, b:{i32}, c:{i32}\n"), syscall_no, a, b, c); + } break; + + } + + return 0; +} diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h new file mode 100644 index 0000000..7dfc51d --- /dev/null +++ b/src/kernel/syscall.h @@ -0,0 +1,20 @@ +#pragma once + +enum { + SYSCALL_PRINT, + SYSCALL_OPEN, + SYSCALL_READ, + SYSCALL_WRITE, + SYSCALL_EXIT, +}; + +#define syscall(number, b, c, d) \ + __asm__ volatile( \ + "int $0x80\n" \ + : \ + : "a"(number), \ + "b"(b), \ + "c"(c), \ + "d"(d) \ + : "memory" \ + ); diff --git a/src/kernel/syscall_handler.S b/src/kernel/syscall_handler.S new file mode 100644 index 0000000..9421ebd --- /dev/null +++ b/src/kernel/syscall_handler.S @@ -0,0 +1,30 @@ +.global interrupt_handler_syscall +.align 4 + +interrupt_handler_syscall: + +pushal // push all general purpose registers +cld // clear direction flag + +// Passing Parameters +// (https://gitlab.com/x86-psABIs/i386-ABI/-/wikis/uploads/14c05f1b1e156e0e46b61bfa7c1df1e2/intel386-psABI-2020-08-07.pdf) +// --------- +// Most parameters are passed on the stack. Parameters are pushed onto the +// stack in reverse order - the last argument in the parameter list has the +// highest address, that is, it is stored farthest away from the stack pointer +// at the time of the call. + + +push %edx +push %ecx +push %ebx +push %eax +call syscall_main +pop %eax +pop %ebx +pop %ecx +pop %edx + +popal // pop all general purpose registers + +iret diff --git a/src/lib/dump_asm.c b/src/lib/dump_asm.c new file mode 100644 index 0000000..a32b5b3 --- /dev/null +++ b/src/lib/dump_asm.c @@ -0,0 +1,493 @@ + +/* Encoding scheme: + X(map, opcode, ext, attrs, mnemonic, argc, a1, a2, a3, desc) + + map: MAP_PRIMARY, MAP_0F + opcode: 1-byte primary opcode (numeric literal) + ext: -1 if none, else digit (0..7) + attrs bitmask: see * below + + Operand shorthands: r8/16/32/64, rm8/16/32/64, m8/16/32/64, imm8/16/32, rel8/32, moffs8/16/32/64, AL/AX/EAX/RAX, rFLAGS. */ + +#include + +/* --- enums and flags --- */ +enum opcode_map { + MAP_PRIMARY = 0, + MAP_0F = 1, +}; + +enum arg_kind { + AK_NONE=0, + + // fixed regs + AK_AL, + AK_AX_EAX_RAX, // accumulator by operand size + + // gp regs + AK_R8, AK_R16, AK_R32, AK_R64, + AK_R16_32_64, // size-selected GPR + AK_R32_64, // 32 or 64 reg + + // reg/mem + AK_RM8, AK_RM16, AK_RM32, AK_RM64, + AK_RM16_32_64, + AK_RM8_16_32_64, + + // memory + AK_M, // memory, size from opcode/REX.W + AK_MOFFS8, + AK_MOFFS16_32_64, + + // immediates and rels + AK_IMM8, AK_IMM16, AK_IMM32, + AK_IMM_OPSIZE, // 16 or 32 by operand size + AK_REL8, AK_REL32, + + // special implicit operands + AK_CL, + AK_ONE +}; + +int arg_kind_bytes[] = { + [AK_NONE] = -9999, + [AK_AL] = 0, + [AK_AX_EAX_RAX] = 0, // accumulator by operand size + + // gp regs + [AK_R8] = 1, + [AK_R16] = 2, + [AK_R32] = 4, + [AK_R64] = 8, + [AK_R16_32_64] = -9999, // size-selected GPR + [AK_R32_64] = -9999, // 32 or 64 reg + + // reg/mem + [AK_RM8] = 1, + [AK_RM16] = 2, + [AK_RM32] = 4, + [AK_RM64] = 8, + [AK_RM16_32_64] = -9999, + [AK_RM8_16_32_64] = -9999, + + // memory + [AK_M] = -9999, // memory, size from opcode/REX.W + [AK_MOFFS8] = -9999, + [AK_MOFFS16_32_64] = -9999, + + // immediates and rels + [AK_IMM8] = 1, + [AK_IMM16] = 2, + [AK_IMM32] = 4, + [AK_IMM_OPSIZE] = -9999, // 16 or 32 by operand size + [AK_REL8] = 1, + [AK_REL32] = 4, + + // special implicit operands + [AK_CL] = 0, + [AK_ONE] = 0 +}; + +const char* arg_kind_str[] = { + [AK_NONE] = "NONE", + [AK_AL] = "AL", + [AK_AX_EAX_RAX] = "AX_EAX_RAX", + [AK_R8] = "R8", + [AK_R16] = "R16", + [AK_R32] = "R32", + [AK_R64] = "R64", + [AK_R16_32_64] = "R16_32_64", + [AK_R32_64] = "R32_64", + [AK_RM8] = "RM8", + [AK_RM16] = "RM16", + [AK_RM32] = "RM32", + [AK_RM64] = "RM64", + [AK_RM16_32_64] = "RM16_32_64", + [AK_RM8_16_32_64] = "RM8_16_32_64", + [AK_M] = "M", + [AK_MOFFS8] = "MOFFS8", + [AK_MOFFS16_32_64] = "MOFFS16_32_64", + [AK_IMM8] = "IMM8", + [AK_IMM16] = "IMM16", + [AK_IMM32] = "IMM32", + [AK_IMM_OPSIZE] = "IMM_OPSIZE", + [AK_REL8] = "REL8", + [AK_REL32] = "REL32", + [AK_CL] = "CL", + [AK_ONE] = "ONE", +}; + +enum { + AA_NONE = 0, + AA_SEXT = 1<<0, // sign-extended when used (e.g., 83 /? imm8) + AA_ZEXT = 1<<1 // zero-extended use +}; + +const char* arg_attr_str[] = { + [AA_NONE] = "NONE", + [AA_SEXT] = "SEXT", + [AA_ZEXT] = "ZEXT", + [AA_SEXT | AA_ZEXT] = "SEXT + ZEXT", +}; + +#define ARG(kind, attrs) ( ((uint32_t)(kind) << 16) | (uint32_t)(attrs) ) +#define ARG_KIND(a) (((a) >> 16) & ((1<<16)-1)) +#define ARG_ATTRS(a) ((a) & ((1<<16)-1)) +#define A(kind) ARG((kind), AA_NONE) +#define A0 ARG(AK_NONE, AA_NONE) + +enum { + MODRM = 1<<( 0 + 8), /* instruction uses ModRM */ + IMM8 = 1<<( 1 + 8), + IMM16 = 1<<( 2 + 8), + IMM32 = 1<<( 3 + 8), + REL8 = 1<<( 4 + 8), + REL32 = 1<<( 5 + 8), + MOFFS = 1<<( 6 + 8), /* moffs form (A0..A3) */ + PLUS_R = 1<<( 7 + 8), /* opcode is base + reg */ + ACCUM = 1<<( 8 + 8), /* accumulator special encoding */ + LOCK_OK = 1<<( 9 + 8), /* LOCK prefix allowed */ + REP_F3 = 1<<(10 + 8), /* REP/REPE F3 meaningful */ + REP_F2 = 1<<(11 + 8), /* REPNE F2 meaningful */ + REXW_OK = 1<<(12 + 8), /* 64-bit operand via REX.W */ +}; + +/* +X(opcode, map, ext, attrs, mnemonic, argc, a1, a2, a3, desc) +*/ +#define INSTRUCTION_LIST \ +X(0x00, MAP_PRIMARY, -1, MODRM|LOCK_OK, ADD, 2, A(AK_RM8), A(AK_R8), A0, "Add r8 to r/m8") \ +X(0x01, MAP_PRIMARY, -1, MODRM|LOCK_OK|REXW_OK, ADD, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "Add r to r/m") \ +X(0x02, MAP_PRIMARY, -1, MODRM, ADD, 2, A(AK_R8), A(AK_RM8), A0, "Add r/m8 to r8") \ +X(0x03, MAP_PRIMARY, -1, MODRM|REXW_OK, ADD, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Add r/m to r") \ +X(0x04, MAP_PRIMARY, -1, IMM8|ACCUM, ADD, 2, A(AK_AL), A(AK_IMM8), A0, "Add imm8 to AL") \ +X(0x05, MAP_PRIMARY, -1, IMM32|ACCUM|REXW_OK, ADD, 2, A(AK_AX_EAX_RAX), A(AK_IMM_OPSIZE), A0, "Add imm to accumulator") \ +X(0x08, MAP_PRIMARY, -1, MODRM|LOCK_OK, OR, 2, A(AK_RM8), A(AK_R8), A0, "OR r8 to r/m8") \ +X(0x09, MAP_PRIMARY, -1, MODRM|LOCK_OK|REXW_OK, OR, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "OR r to r/m") \ +X(0x0A, MAP_PRIMARY, -1, MODRM, OR, 2, A(AK_R8), A(AK_RM8), A0, "OR r/m8 to r8") \ +X(0x0B, MAP_PRIMARY, -1, MODRM|REXW_OK, OR, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "OR r/m to r") \ +X(0x0C, MAP_PRIMARY, -1, IMM8|ACCUM, OR, 2, A(AK_AL), A(AK_IMM8), A0, "OR imm8 to AL") \ +X(0x0D, MAP_PRIMARY, -1, IMM32|ACCUM|REXW_OK, OR, 2, A(AK_AX_EAX_RAX), A(AK_IMM_OPSIZE), A0, "OR imm to accumulator") \ +X(0x0F, MAP_PRIMARY, -1, 0, NOP_MULTI, 0, A0, A0, A0, "Multi-byte NOPs via 0F 1F */r") \ +X(0x0F, MAP_PRIMARY, -1, 0, TWO_BYTE_ESC, 0, A0, A0, A0, "Opcode map escape (placeholder)") \ +X(0x10, MAP_PRIMARY, -1, MODRM|LOCK_OK, ADC, 2, A(AK_RM8), A(AK_R8), A0, "Add with carry") \ +X(0x11, MAP_PRIMARY, -1, MODRM|LOCK_OK|REXW_OK, ADC, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "Add with carry") \ +X(0x12, MAP_PRIMARY, -1, MODRM, ADC, 2, A(AK_R8), A(AK_RM8), A0, "Add with carry") \ +X(0x13, MAP_PRIMARY, -1, MODRM|REXW_OK, ADC, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Add with carry") \ +X(0x14, MAP_PRIMARY, -1, IMM8|ACCUM, ADC, 2, A(AK_AL), A(AK_IMM8), A0, "ADC imm8 to AL") \ +X(0x15, MAP_PRIMARY, -1, IMM32|ACCUM|REXW_OK, ADC, 2, A(AK_AX_EAX_RAX), A(AK_IMM_OPSIZE), A0, "ADC imm to accumulator") \ +X(0x18, MAP_PRIMARY, -1, MODRM|LOCK_OK, SBB, 2, A(AK_RM8), A(AK_R8), A0, "Subtract with borrow") \ +X(0x19, MAP_PRIMARY, -1, MODRM|LOCK_OK|REXW_OK, SBB, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "Subtract with borrow") \ +X(0x1A, MAP_PRIMARY, -1, MODRM, SBB, 2, A(AK_R8), A(AK_RM8), A0, "Subtract with borrow") \ +X(0x1B, MAP_PRIMARY, -1, MODRM|REXW_OK, SBB, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Subtract with borrow") \ +X(0x1C, MAP_PRIMARY, -1, IMM8|ACCUM, SBB, 2, A(AK_AL), A(AK_IMM8), A0, "SBB imm8 to AL") \ +X(0x1D, MAP_PRIMARY, -1, IMM32|ACCUM|REXW_OK, SBB, 2, A(AK_AX_EAX_RAX), A(AK_IMM_OPSIZE), A0, "SBB imm to accumulator") \ +X(0x20, MAP_PRIMARY, -1, MODRM|LOCK_OK, AND, 2, A(AK_RM8), A(AK_R8), A0, "AND r8 to r/m8") \ +X(0x21, MAP_PRIMARY, -1, MODRM|LOCK_OK|REXW_OK, AND, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "AND r to r/m") \ +X(0x22, MAP_PRIMARY, -1, MODRM, AND, 2, A(AK_R8), A(AK_RM8), A0, "AND r/m8 to r8") \ +X(0x23, MAP_PRIMARY, -1, MODRM|REXW_OK, AND, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "AND r/m to r") \ +X(0x24, MAP_PRIMARY, -1, IMM8|ACCUM, AND, 2, A(AK_AL), A(AK_IMM8), A0, "AND imm8 to AL") \ +X(0x25, MAP_PRIMARY, -1, IMM32|ACCUM|REXW_OK, AND, 2, A(AK_AX_EAX_RAX), A(AK_IMM_OPSIZE), A0, "AND imm to accumulator") \ +X(0x27, MAP_PRIMARY, -1, 0, DAA, 0, A0, A0, A0, "Decimal adjust after add") \ +X(0x28, MAP_PRIMARY, -1, MODRM|LOCK_OK, SUB, 2, A(AK_RM8), A(AK_R8), A0, "Subtract r8 from r/m8") \ +X(0x29, MAP_PRIMARY, -1, MODRM|LOCK_OK|REXW_OK, SUB, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "Subtract r from r/m") \ +X(0x2A, MAP_PRIMARY, -1, MODRM, SUB, 2, A(AK_R8), A(AK_RM8), A0, "Subtract r/m8 from r8") \ +X(0x2B, MAP_PRIMARY, -1, MODRM|REXW_OK, SUB, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Subtract r/m from r") \ +X(0x2C, MAP_PRIMARY, -1, IMM8|ACCUM, SUB, 2, A(AK_AL), A(AK_IMM8), A0, "SUB imm8 from AL") \ +X(0x2D, MAP_PRIMARY, -1, IMM32|ACCUM|REXW_OK, SUB, 2, A(AK_AX_EAX_RAX), A(AK_IMM_OPSIZE), A0, "SUB imm from accumulator") \ +X(0x2F, MAP_PRIMARY, -1, 0, DAS, 0, A0, A0, A0, "Decimal adjust after sub") \ +X(0x30, MAP_PRIMARY, -1, MODRM|LOCK_OK, XOR, 2, A(AK_RM8), A(AK_R8), A0, "XOR r8 to r/m8") \ +X(0x31, MAP_0F, -1, 0, RDTSC, 0, A0, A0, A0, "Read time-stamp counter") \ +X(0x31, MAP_PRIMARY, -1, MODRM|LOCK_OK|REXW_OK, XOR, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "XOR r to r/m") \ +X(0x32, MAP_PRIMARY, -1, MODRM, XOR, 2, A(AK_R8), A(AK_RM8), A0, "XOR r/m8 to r8") \ +X(0x33, MAP_PRIMARY, -1, MODRM|REXW_OK, XOR, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "XOR r/m to r") \ +X(0x34, MAP_PRIMARY, -1, IMM8|ACCUM, XOR, 2, A(AK_AL), A(AK_IMM8), A0, "XOR imm8 to AL") \ +X(0x35, MAP_PRIMARY, -1, IMM32|ACCUM|REXW_OK, XOR, 2, A(AK_AX_EAX_RAX), A(AK_IMM_OPSIZE), A0, "XOR imm to accumulator") \ +X(0x37, MAP_PRIMARY, -1, 0, AAA, 0, A0, A0, A0, "ASCII adjust after add") \ +X(0x38, MAP_PRIMARY, -1, MODRM, CMP, 2, A(AK_RM8), A(AK_R8), A0, "Compare r/m8, r8") \ +X(0x39, MAP_PRIMARY, -1, MODRM|REXW_OK, CMP, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "Compare r/m, r") \ +X(0x3A, MAP_PRIMARY, -1, MODRM, CMP, 2, A(AK_R8), A(AK_RM8), A0, "Compare r8, r/m8") \ +X(0x3B, MAP_PRIMARY, -1, MODRM|REXW_OK, CMP, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Compare r, r/m") \ +X(0x3C, MAP_PRIMARY, -1, IMM8|ACCUM, CMP, 2, A(AK_AL), A(AK_IMM8), A0, "Compare AL, imm8") \ +X(0x3D, MAP_PRIMARY, -1, IMM32|ACCUM|REXW_OK, CMP, 2, A(AK_AX_EAX_RAX), A(AK_IMM_OPSIZE), A0, "Compare accumulator, imm") \ +X(0x3F, MAP_PRIMARY, -1, 0, AAS, 0, A0, A0, A0, "ASCII adjust after sub") \ +X(0x40, MAP_0F, -1, MODRM|REXW_OK, CMOVO, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move O") \ +X(0x41, MAP_0F, -1, MODRM|REXW_OK, CMOVNO, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move NO") \ +X(0x42, MAP_0F, -1, MODRM|REXW_OK, CMOVB, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move B") \ +X(0x43, MAP_0F, -1, MODRM|REXW_OK, CMOVAE, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move AE") \ +X(0x44, MAP_0F, -1, MODRM|REXW_OK, CMOVE, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move E") \ +X(0x45, MAP_0F, -1, MODRM|REXW_OK, CMOVNE, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move NE") \ +X(0x46, MAP_0F, -1, MODRM|REXW_OK, CMOVBE, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move BE") \ +X(0x47, MAP_0F, -1, MODRM|REXW_OK, CMOVA, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move A") \ +X(0x48, MAP_0F, -1, MODRM|REXW_OK, CMOVS, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move S") \ +X(0x49, MAP_0F, -1, MODRM|REXW_OK, CMOVNS, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move NS") \ +X(0x4A, MAP_0F, -1, MODRM|REXW_OK, CMOVP, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move P") \ +X(0x4B, MAP_0F, -1, MODRM|REXW_OK, CMOVNP, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move NP") \ +X(0x4C, MAP_0F, -1, MODRM|REXW_OK, CMOVL, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move L") \ +X(0x4D, MAP_0F, -1, MODRM|REXW_OK, CMOVGE, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move GE") \ +X(0x4E, MAP_0F, -1, MODRM|REXW_OK, CMOVLE, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move LE") \ +X(0x4F, MAP_0F, -1, MODRM|REXW_OK, CMOVG, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Conditional move G") \ +X(0x50, MAP_PRIMARY, -1, PLUS_R, PUSH, 1, A(AK_R64), A0, A0, "Push r64") \ +X(0x58, MAP_PRIMARY, -1, PLUS_R, POP, 1, A(AK_R64), A0, A0, "Pop into r64") \ +X(0x63, MAP_PRIMARY, -1, MODRM, MOVSXD, 2, A(AK_R64), A(AK_RM32), A0, "Sign-extend dword -> qword") \ +X(0x68, MAP_PRIMARY, -1, IMM32, PUSH, 1, ARG(AK_IMM32, AA_SEXT), A0, A0, "Push sign-extended imm32") \ +X(0x70, MAP_PRIMARY, -1, REL8, JO, 1, A(AK_REL8), A0, A0, "Jump if overflow") \ +X(0x71, MAP_PRIMARY, -1, REL8, JNO, 1, A(AK_REL8), A0, A0, "Jump if not overflow") \ +X(0x72, MAP_PRIMARY, -1, REL8, JB, 1, A(AK_REL8), A0, A0, "Jump if below") \ +X(0x73, MAP_PRIMARY, -1, REL8, JAE, 1, A(AK_REL8), A0, A0, "Jump if above or equal") \ +X(0x74, MAP_PRIMARY, -1, REL8, JE, 1, A(AK_REL8), A0, A0, "Jump if equal") \ +X(0x75, MAP_PRIMARY, -1, REL8, JNE, 1, A(AK_REL8), A0, A0, "Jump if not equal") \ +X(0x76, MAP_PRIMARY, -1, REL8, JBE, 1, A(AK_REL8), A0, A0, "Jump if below or equal") \ +X(0x77, MAP_PRIMARY, -1, REL8, JA, 1, A(AK_REL8), A0, A0, "Jump if above") \ +X(0x78, MAP_PRIMARY, -1, REL8, JS, 1, A(AK_REL8), A0, A0, "Jump if sign") \ +X(0x79, MAP_PRIMARY, -1, REL8, JNS, 1, A(AK_REL8), A0, A0, "Jump if not sign") \ +X(0x7A, MAP_PRIMARY, -1, REL8, JP, 1, A(AK_REL8), A0, A0, "Jump if parity") \ +X(0x7B, MAP_PRIMARY, -1, REL8, JNP, 1, A(AK_REL8), A0, A0, "Jump if not parity") \ +X(0x7C, MAP_PRIMARY, -1, REL8, JL, 1, A(AK_REL8), A0, A0, "Jump if less") \ +X(0x7D, MAP_PRIMARY, -1, REL8, JGE, 1, A(AK_REL8), A0, A0, "Jump if greater or equal") \ +X(0x7E, MAP_PRIMARY, -1, REL8, JLE, 1, A(AK_REL8), A0, A0, "Jump if less or equal") \ +X(0x7F, MAP_PRIMARY, -1, REL8, JG, 1, A(AK_REL8), A0, A0, "Jump if greater") \ +X(0x80, MAP_0F, -1, REL32, JO, 1, A(AK_REL32), A0, A0, "Near JO") \ +X(0x80, MAP_PRIMARY, 0, MODRM|IMM8|LOCK_OK, ADD, 2, A(AK_RM8), A(AK_IMM8), A0, "Add imm8") \ +X(0x80, MAP_PRIMARY, 1, MODRM|IMM8|LOCK_OK, OR, 2, A(AK_RM8), A(AK_IMM8), A0, "OR imm8") \ +X(0x80, MAP_PRIMARY, 2, MODRM|IMM8|LOCK_OK, ADC, 2, A(AK_RM8), A(AK_IMM8), A0, "ADC imm8") \ +X(0x80, MAP_PRIMARY, 3, MODRM|IMM8|LOCK_OK, SBB, 2, A(AK_RM8), A(AK_IMM8), A0, "SBB imm8") \ +X(0x80, MAP_PRIMARY, 4, MODRM|IMM8|LOCK_OK, AND, 2, A(AK_RM8), A(AK_IMM8), A0, "AND imm8") \ +X(0x80, MAP_PRIMARY, 5, MODRM|IMM8|LOCK_OK, SUB, 2, A(AK_RM8), A(AK_IMM8), A0, "SUB imm8") \ +X(0x80, MAP_PRIMARY, 6, MODRM|IMM8|LOCK_OK, XOR, 2, A(AK_RM8), A(AK_IMM8), A0, "XOR imm8") \ +X(0x80, MAP_PRIMARY, 7, MODRM|IMM8, CMP, 2, A(AK_RM8), A(AK_IMM8), A0, "Compare r/m8, imm8") \ +X(0x81, MAP_0F, -1, REL32, JNO, 1, A(AK_REL32), A0, A0, "Near JNO") \ +X(0x81, MAP_PRIMARY, 0, MODRM|IMM32|LOCK_OK|REXW_OK, ADD, 2, A(AK_RM16_32_64), A(AK_IMM_OPSIZE), A0, "Add imm") \ +X(0x81, MAP_PRIMARY, 1, MODRM|IMM32|LOCK_OK|REXW_OK, OR, 2, A(AK_RM16_32_64), A(AK_IMM_OPSIZE), A0, "OR imm") \ +X(0x81, MAP_PRIMARY, 2, MODRM|IMM32|LOCK_OK|REXW_OK, ADC, 2, A(AK_RM16_32_64), A(AK_IMM_OPSIZE), A0, "ADC imm") \ +X(0x81, MAP_PRIMARY, 3, MODRM|IMM32|LOCK_OK|REXW_OK, SBB, 2, A(AK_RM16_32_64), A(AK_IMM_OPSIZE), A0, "SBB imm") \ +X(0x81, MAP_PRIMARY, 4, MODRM|IMM32|LOCK_OK|REXW_OK, AND, 2, A(AK_RM16_32_64), A(AK_IMM_OPSIZE), A0, "AND imm") \ +X(0x81, MAP_PRIMARY, 5, MODRM|IMM32|LOCK_OK|REXW_OK, SUB, 2, A(AK_RM16_32_64), A(AK_IMM_OPSIZE), A0, "SUB imm") \ +X(0x81, MAP_PRIMARY, 6, MODRM|IMM32|LOCK_OK|REXW_OK, XOR, 2, A(AK_RM16_32_64), A(AK_IMM_OPSIZE), A0, "XOR imm") \ +X(0x81, MAP_PRIMARY, 7, MODRM|IMM32|REXW_OK, CMP, 2, A(AK_RM16_32_64), A(AK_IMM_OPSIZE), A0, "Compare r/m, imm") \ +X(0x83, MAP_PRIMARY, 0, MODRM|IMM8|LOCK_OK|REXW_OK, ADD, 2, A(AK_RM16_32_64), ARG(AK_IMM8, AA_SEXT), A0, "Add sign-imm8") \ +X(0x83, MAP_PRIMARY, 1, MODRM|IMM8|LOCK_OK|REXW_OK, OR, 2, A(AK_RM16_32_64), ARG(AK_IMM8, AA_SEXT), A0, "OR sign-imm8") \ +X(0x83, MAP_PRIMARY, 2, MODRM|IMM8|LOCK_OK|REXW_OK, ADC, 2, A(AK_RM16_32_64), ARG(AK_IMM8, AA_SEXT), A0, "ADC sign-imm8") \ +X(0x83, MAP_PRIMARY, 3, MODRM|IMM8|LOCK_OK|REXW_OK, SBB, 2, A(AK_RM16_32_64), ARG(AK_IMM8, AA_SEXT), A0, "SBB sign-imm8") \ +X(0x83, MAP_PRIMARY, 4, MODRM|IMM8|LOCK_OK|REXW_OK, AND, 2, A(AK_RM16_32_64), ARG(AK_IMM8, AA_SEXT), A0, "AND sign-imm8") \ +X(0x83, MAP_PRIMARY, 5, MODRM|IMM8|LOCK_OK|REXW_OK, SUB, 2, A(AK_RM16_32_64), ARG(AK_IMM8, AA_SEXT), A0, "SUB sign-imm8") \ +X(0x83, MAP_PRIMARY, 6, MODRM|IMM8|LOCK_OK|REXW_OK, XOR, 2, A(AK_RM16_32_64), ARG(AK_IMM8, AA_SEXT), A0, "XOR sign-imm8") \ +X(0x83, MAP_PRIMARY, 7, MODRM|IMM8|REXW_OK, CMP, 2, A(AK_RM16_32_64), ARG(AK_IMM8, AA_SEXT), A0, "Compare r/m, sign-imm8") \ +X(0x84, MAP_PRIMARY, -1, MODRM, TEST, 2, A(AK_RM8), A(AK_R8), A0, "TEST r/m8, r8") \ +X(0x85, MAP_PRIMARY, -1, MODRM|REXW_OK, TEST, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "TEST r/m, r") \ +X(0x87, MAP_PRIMARY, -1, MODRM|REXW_OK|LOCK_OK, XCHG, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Exchange") \ +X(0x88, MAP_PRIMARY, -1, MODRM|LOCK_OK, MOV, 2, A(AK_RM8), A(AK_R8), A0, "Move r8 -> r/m8") \ +X(0x89, MAP_PRIMARY, -1, MODRM|LOCK_OK|REXW_OK, MOV, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "Move r -> r/m") \ +X(0x8A, MAP_PRIMARY, -1, MODRM, MOV, 2, A(AK_R8), A(AK_RM8), A0, "Move r/m8 -> r8") \ +X(0x8B, MAP_PRIMARY, -1, MODRM|REXW_OK, MOV, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Move r/m -> r") \ +X(0x8D, MAP_PRIMARY, -1, MODRM|REXW_OK, LEA, 2, A(AK_R16_32_64), A(AK_M), A0, "Load effective address") \ +X(0x8F, MAP_PRIMARY, -1, 0, NOP_ALIAS, 0, A0, A0, A0, "Used as POP r/m64; no pure LEA alias here") \ +X(0x8F, MAP_PRIMARY, 0, MODRM, POP, 1, A(AK_RM64), A0, A0, "Pop into r/m64") \ +X(0x90, MAP_0F, -1, MODRM, SETO, 1, A(AK_RM8), A0, A0, "Set on overflow") \ +X(0x90, MAP_PRIMARY, -1, 0, NOP, 0, A0, A0, A0, "No operation") \ +X(0x90, MAP_PRIMARY, -1, 0, PAUSE, 0, A0, A0, A0, "PAUSE uses F3 90 prefix") \ +X(0x90, MAP_PRIMARY, -1, PLUS_R|ACCUM, XCHG, 2, A(AK_AX_EAX_RAX), A(AK_R16_32_64), A0, "Exchange with accumulator") \ +X(0x91, MAP_0F, -1, MODRM, SETNO, 1, A(AK_RM8), A0, A0, "Set on not overflow") \ +X(0x92, MAP_0F, -1, MODRM, SETB, 1, A(AK_RM8), A0, A0, "Set on below/CF=1") \ +X(0x93, MAP_0F, -1, MODRM, SETAE, 1, A(AK_RM8), A0, A0, "Set on above or equal") \ +X(0x94, MAP_0F, -1, MODRM, SETE, 1, A(AK_RM8), A0, A0, "Set on equal") \ +X(0x95, MAP_0F, -1, MODRM, SETNE, 1, A(AK_RM8), A0, A0, "Set on not equal") \ +X(0x96, MAP_0F, -1, MODRM, SETBE, 1, A(AK_RM8), A0, A0, "Set on below or equal") \ +X(0x97, MAP_0F, -1, MODRM, SETA, 1, A(AK_RM8), A0, A0, "Set on above") \ +X(0x98, MAP_0F, -1, MODRM, SETS, 1, A(AK_RM8), A0, A0, "Set on sign") \ +X(0x98, MAP_PRIMARY, -1, 0, CBW, 0, A0, A0, A0, "AL->AX (size-propagates)") \ +X(0x99, MAP_0F, -1, MODRM, SETNS, 1, A(AK_RM8), A0, A0, "Set on not sign") \ +X(0x99, MAP_PRIMARY, -1, 0, CWD, 0, A0, A0, A0, "AX->DX:AX (CDQ/CQO by size)") \ +X(0x9A, MAP_0F, -1, MODRM, SETP, 1, A(AK_RM8), A0, A0, "Set on parity") \ +X(0x9A, MAP_PRIMARY, -1, 0, FARCALL, 0, A0, A0, A0, "Far call (not common in 64-bit)") \ +X(0x9B, MAP_0F, -1, MODRM, SETNP, 1, A(AK_RM8), A0, A0, "Set on not parity") \ +X(0x9B, MAP_PRIMARY, -1, 0, FWAIT, 0, A0, A0, A0, "Wait (legacy alias WAIT)") \ +X(0x9C, MAP_0F, -1, MODRM, SETL, 1, A(AK_RM8), A0, A0, "Set on less") \ +X(0x9C, MAP_PRIMARY, -1, 0, PUSHF, 0, A0, A0, A0, "Push rFLAGS (alias)") \ +X(0x9C, MAP_PRIMARY, -1, 0, PUSHFQ, 0, A0, A0, A0, "Push RFLAGS") \ +X(0x9D, MAP_0F, -1, MODRM, SETGE, 1, A(AK_RM8), A0, A0, "Set on greater or equal") \ +X(0x9D, MAP_PRIMARY, -1, 0, POPF, 0, A0, A0, A0, "Pop rFLAGS (alias)") \ +X(0x9D, MAP_PRIMARY, -1, 0, POPFQ, 0, A0, A0, A0, "Pop RFLAGS") \ +X(0x9E, MAP_0F, -1, MODRM, SETLE, 1, A(AK_RM8), A0, A0, "Set on less or equal") \ +X(0x9E, MAP_PRIMARY, -1, 0, SAHF, 0, A0, A0, A0, "Store AH into RFLAGS") \ +X(0x9F, MAP_0F, -1, MODRM, SETG, 1, A(AK_RM8), A0, A0, "Set on greater") \ +X(0x9F, MAP_PRIMARY, -1, 0, LAHF, 0, A0, A0, A0, "Load AH with RFLAGS") \ +X(0xA0, MAP_PRIMARY, -1, MOFFS|ACCUM, MOV, 2, A(AK_AL), A(AK_MOFFS8), A0, "Load moffs8 -> AL") \ +X(0xA1, MAP_PRIMARY, -1, MOFFS|ACCUM|REXW_OK, MOV, 2, A(AK_AX_EAX_RAX), A(AK_MOFFS16_32_64), A0, "Load moffs -> RAX") \ +X(0xA2, MAP_0F, -1, 0, CPUID, 0, A0, A0, A0, "Query CPU ID") \ +X(0xA2, MAP_PRIMARY, -1, MOFFS|ACCUM, MOV, 2, A(AK_MOFFS8), A(AK_AL), A0, "Store AL -> moffs8") \ +X(0xA3, MAP_0F, -1, MODRM|REXW_OK, BT, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "Bit test") \ +X(0xA3, MAP_PRIMARY, -1, MOFFS|ACCUM|REXW_OK, MOV, 2, A(AK_MOFFS16_32_64), A(AK_AX_EAX_RAX), A0, "Store RAX -> moffs") \ +X(0xA4, MAP_PRIMARY, -1, REP_F3|REP_F2, MOVSB, 0, A0, A0, A0, "Move byte string") \ +X(0xA5, MAP_PRIMARY, -1, REP_F3|REP_F2|REXW_OK, MOVSWD, 0, A0, A0, A0, "Move string (word/dword/qword by size)") \ +X(0xA6, MAP_PRIMARY, -1, REP_F3|REP_F2, CMPSB, 0, A0, A0, A0, "Compare byte string") \ +X(0xA7, MAP_PRIMARY, -1, REP_F3|REP_F2|REXW_OK, CMPSWD, 0, A0, A0, A0, "Compare string") \ +X(0xA8, MAP_PRIMARY, -1, IMM8|ACCUM, TEST, 2, A(AK_AL), A(AK_IMM8), A0, "TEST AL, imm8") \ +X(0xA9, MAP_PRIMARY, -1, IMM32|ACCUM|REXW_OK, TEST, 2, A(AK_AX_EAX_RAX), A(AK_IMM_OPSIZE), A0, "TEST accumulator, imm") \ +X(0xAA, MAP_PRIMARY, -1, REP_F3|REP_F2, STOSB, 0, A0, A0, A0, "Store byte string") \ +X(0xAB, MAP_0F, -1, MODRM|LOCK_OK|REXW_OK, BTS, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "Bit test and set") \ +X(0xAB, MAP_PRIMARY, -1, REP_F3|REP_F2|REXW_OK, STOSWD, 0, A0, A0, A0, "Store string") \ +X(0xAC, MAP_PRIMARY, -1, REP_F3|REP_F2, LODSB, 0, A0, A0, A0, "Load byte string") \ +X(0xAD, MAP_PRIMARY, -1, REP_F3|REP_F2|REXW_OK, LODSWD, 0, A0, A0, A0, "Load string") \ +X(0xAE, MAP_PRIMARY, -1, REP_F3|REP_F2, SCASB, 0, A0, A0, A0, "Scan byte string") \ +X(0xAF, MAP_PRIMARY, -1, REP_F3|REP_F2|REXW_OK, SCASWD, 0, A0, A0, A0, "Scan string") \ +X(0xB3, MAP_0F, -1, MODRM|LOCK_OK|REXW_OK, BTR, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "Bit test and reset") \ +X(0xB6, MAP_0F, -1, MODRM|REXW_OK, MOVZX, 2, A(AK_R16_32_64), A(AK_RM8), A0, "Zero-extend byte") \ +X(0xB7, MAP_0F, -1, MODRM|REXW_OK, MOVZX, 2, A(AK_R16_32_64), A(AK_RM16), A0, "Zero-extend word") \ +X(0xBA, MAP_0F, 4, MODRM|IMM8|REXW_OK, BT, 2, A(AK_RM16_32_64), A(AK_IMM8), A0, "Bit test imm") \ +X(0xBA, MAP_0F, 5, MODRM|IMM8|LOCK_OK|REXW_OK, BTS, 2, A(AK_RM16_32_64), A(AK_IMM8), A0, "Bit test and set imm") \ +X(0xBA, MAP_0F, 6, MODRM|IMM8|LOCK_OK|REXW_OK, BTR, 2, A(AK_RM16_32_64), A(AK_IMM8), A0, "Bit test and reset imm") \ +X(0xBA, MAP_0F, 7, MODRM|IMM8|LOCK_OK|REXW_OK, BTC, 2, A(AK_RM16_32_64), A(AK_IMM8), A0, "Bit test and complement imm")\ +X(0xBB, MAP_0F, -1, MODRM|LOCK_OK|REXW_OK, BTC, 2, A(AK_RM16_32_64), A(AK_R16_32_64), A0, "Bit test and complement") \ +X(0xBC, MAP_0F, -1, MODRM|REXW_OK, BSF, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Bit scan forward") \ +X(0xBD, MAP_0F, -1, MODRM|REXW_OK, BSR, 2, A(AK_R16_32_64), A(AK_RM16_32_64), A0, "Bit scan reverse") \ +X(0xBE, MAP_0F, -1, MODRM|REXW_OK, MOVSX, 2, A(AK_R16_32_64), A(AK_RM8), A0, "Sign-extend byte") \ +X(0xBF, MAP_0F, -1, MODRM|REXW_OK, MOVSX, 2, A(AK_R16_32_64), A(AK_RM16), A0, "Sign-extend word") \ +X(0xC0, MAP_0F, -1, MODRM|REXW_OK|LOCK_OK, XADD, 2, A(AK_RM8_16_32_64), A(AK_R16_32_64), A0, "Exchange and add") \ +X(0xC0, MAP_PRIMARY, 0, MODRM|IMM8, ROL, 2, A(AK_RM8), A(AK_IMM8), A0, "Rotate left by imm8") \ +X(0xC0, MAP_PRIMARY, 1, MODRM|IMM8, ROR, 2, A(AK_RM8), A(AK_IMM8), A0, "Rotate right by imm8") \ +X(0xC0, MAP_PRIMARY, 4, MODRM|IMM8, SHL, 2, A(AK_RM8), A(AK_IMM8), A0, "Shift left by imm8") \ +X(0xC0, MAP_PRIMARY, 5, MODRM|IMM8, SHR, 2, A(AK_RM8), A(AK_IMM8), A0, "Logical shift right by imm8")\ +X(0xC0, MAP_PRIMARY, 7, MODRM|IMM8, SAR, 2, A(AK_RM8), A(AK_IMM8), A0, "Arithmetic shift right by imm8")\ +X(0xC1, MAP_PRIMARY, 0, MODRM|IMM8|REXW_OK, ROL, 2, A(AK_RM16_32_64), A(AK_IMM8), A0, "Rotate left by imm8") \ +X(0xC1, MAP_PRIMARY, 1, MODRM|IMM8|REXW_OK, ROR, 2, A(AK_RM16_32_64), A(AK_IMM8), A0, "Rotate right by imm8") \ +X(0xC1, MAP_PRIMARY, 4, MODRM|IMM8|REXW_OK, SHL, 2, A(AK_RM16_32_64), A(AK_IMM8), A0, "Shift left by imm8") \ +X(0xC1, MAP_PRIMARY, 5, MODRM|IMM8|REXW_OK, SHR, 2, A(AK_RM16_32_64), A(AK_IMM8), A0, "Logical shift right by imm8")\ +X(0xC1, MAP_PRIMARY, 7, MODRM|IMM8|REXW_OK, SAR, 2, A(AK_RM16_32_64), A(AK_IMM8), A0, "Arithmetic shift right by imm8")\ +X(0xC2, MAP_PRIMARY, -1, IMM16, RET, 1, A(AK_IMM16), A0, A0, "Return and pop imm16") \ +X(0xC3, MAP_PRIMARY, -1, 0, RET, 0, A0, A0, A0, "Return") \ +X(0xC6, MAP_PRIMARY, 0, MODRM|IMM8|LOCK_OK, MOV, 2, A(AK_RM8), A(AK_IMM8), A0, "Move imm8 -> r/m8") \ +X(0xC7, MAP_PRIMARY, 0, MODRM|IMM32|LOCK_OK|REXW_OK, MOV, 2, A(AK_RM16_32_64), A(AK_IMM_OPSIZE), A0, "Move imm -> r/m") \ +X(0xC8, MAP_0F, -1, PLUS_R|REXW_OK, BSWAP, 1, A(AK_R32_64), A0, A0, "Byte swap r32/r64") \ +X(0xD0, MAP_PRIMARY, 0, MODRM, ROL, 2, A(AK_RM8), A(AK_ONE), A0, "Rotate left by 1") \ +X(0xD0, MAP_PRIMARY, 1, MODRM, ROR, 2, A(AK_RM8), A(AK_ONE), A0, "Rotate right by 1") \ +X(0xD0, MAP_PRIMARY, 2, MODRM, RCL, 2, A(AK_RM8), A(AK_ONE), A0, "Rotate through CF left by 1")\ +X(0xD0, MAP_PRIMARY, 3, MODRM, RCR, 2, A(AK_RM8), A(AK_ONE), A0, "Rotate through CF right by 1")\ +X(0xD0, MAP_PRIMARY, 4, MODRM, SHL, 2, A(AK_RM8), A(AK_ONE), A0, "Shift left by 1") \ +X(0xD0, MAP_PRIMARY, 5, MODRM, SHR, 2, A(AK_RM8), A(AK_ONE), A0, "Logical shift right by 1") \ +X(0xD0, MAP_PRIMARY, 7, MODRM, SAR, 2, A(AK_RM8), A(AK_ONE), A0, "Arithmetic shift right by 1")\ +X(0xD1, MAP_PRIMARY, 0, MODRM|REXW_OK, ROL, 2, A(AK_RM16_32_64), A(AK_ONE), A0, "Rotate left by 1") \ +X(0xD1, MAP_PRIMARY, 1, MODRM|REXW_OK, ROR, 2, A(AK_RM16_32_64), A(AK_ONE), A0, "Rotate right by 1") \ +X(0xD1, MAP_PRIMARY, 2, MODRM|REXW_OK, RCL, 2, A(AK_RM16_32_64), A(AK_ONE), A0, "Rotate through CF left by 1")\ +X(0xD1, MAP_PRIMARY, 3, MODRM|REXW_OK, RCR, 2, A(AK_RM16_32_64), A(AK_ONE), A0, "Rotate through CF right by 1")\ +X(0xD1, MAP_PRIMARY, 4, MODRM|REXW_OK, SHL, 2, A(AK_RM16_32_64), A(AK_ONE), A0, "Shift left by 1") \ +X(0xD1, MAP_PRIMARY, 5, MODRM|REXW_OK, SHR, 2, A(AK_RM16_32_64), A(AK_ONE), A0, "Logical shift right by 1") \ +X(0xD1, MAP_PRIMARY, 7, MODRM|REXW_OK, SAR, 2, A(AK_RM16_32_64), A(AK_ONE), A0, "Arithmetic shift right by 1")\ +X(0xD2, MAP_PRIMARY, 0, MODRM, ROL, 2, A(AK_RM8), A(AK_CL), A0, "Rotate left by CL") \ +X(0xD2, MAP_PRIMARY, 1, MODRM, ROR, 2, A(AK_RM8), A(AK_CL), A0, "Rotate right by CL") \ +X(0xD2, MAP_PRIMARY, 2, MODRM, RCL, 2, A(AK_RM8), A(AK_CL), A0, "Rotate through CF left by CL")\ +X(0xD2, MAP_PRIMARY, 3, MODRM, RCR, 2, A(AK_RM8), A(AK_CL), A0, "Rotate through CF right by CL")\ +X(0xD2, MAP_PRIMARY, 4, MODRM, SHL, 2, A(AK_RM8), A(AK_CL), A0, "Shift left by CL") \ +X(0xD2, MAP_PRIMARY, 5, MODRM, SHR, 2, A(AK_RM8), A(AK_CL), A0, "Logical shift right by CL") \ +X(0xD2, MAP_PRIMARY, 7, MODRM, SAR, 2, A(AK_RM8), A(AK_CL), A0, "Arithmetic shift right by CL")\ +X(0xD3, MAP_PRIMARY, 0, MODRM|REXW_OK, ROL, 2, A(AK_RM16_32_64), A(AK_CL), A0, "Rotate left by CL") \ +X(0xD3, MAP_PRIMARY, 1, MODRM|REXW_OK, ROR, 2, A(AK_RM16_32_64), A(AK_CL), A0, "Rotate right by CL") \ +X(0xD3, MAP_PRIMARY, 2, MODRM|REXW_OK, RCL, 2, A(AK_RM16_32_64), A(AK_CL), A0, "Rotate through CF left by CL")\ +X(0xD3, MAP_PRIMARY, 3, MODRM|REXW_OK, RCR, 2, A(AK_RM16_32_64), A(AK_CL), A0, "Rotate through CF right by CL")\ +X(0xD3, MAP_PRIMARY, 4, MODRM|REXW_OK, SHL, 2, A(AK_RM16_32_64), A(AK_CL), A0, "Shift left by CL") \ +X(0xD3, MAP_PRIMARY, 5, MODRM|REXW_OK, SHR, 2, A(AK_RM16_32_64), A(AK_CL), A0, "Logical shift right by CL") \ +X(0xD3, MAP_PRIMARY, 7, MODRM|REXW_OK, SAR, 2, A(AK_RM16_32_64), A(AK_CL), A0, "Arithmetic shift right by CL")\ +X(0xD7, MAP_PRIMARY, -1, 0, XLAT, 0, A0, A0, A0, "Table look-up translation") \ +X(0xE8, MAP_PRIMARY, -1, REL32, CALL, 1, A(AK_REL32), A0, A0, "Near call rel32") \ +X(0xE9, MAP_PRIMARY, -1, REL32, JMP, 1, A(AK_REL32), A0, A0, "Near jump rel32") \ +X(0xEA, MAP_PRIMARY, -1, 0, FARJMP, 0, A0, A0, A0, "Far jump (not common in 64-bit)") \ +X(0xEB, MAP_PRIMARY, -1, REL8, JMP, 1, A(AK_REL8), A0, A0, "Short jump rel8") \ +X(0xF4, MAP_PRIMARY, -1, 0, HLT, 0, A0, A0, A0, "Halt") \ +X(0xF5, MAP_PRIMARY, -1, 0, CMC, 0, A0, A0, A0, "Complement CF") \ +X(0xF6, MAP_PRIMARY, 0, MODRM|IMM8, TEST, 2, A(AK_RM8), A(AK_IMM8), A0, "TEST r/m8, imm8") \ +X(0xF6, MAP_PRIMARY, 2, MODRM, NOT, 1, A(AK_RM8), A0, A0, "Invert r/m8") \ +X(0xF6, MAP_PRIMARY, 3, MODRM, NEG, 1, A(AK_RM8), A0, A0, "Two's-complement r/m8") \ +X(0xF6, MAP_PRIMARY, 4, MODRM, MUL, 1, A(AK_RM8), A0, A0, "Unsigned mul AL * r/m8 -> AX") \ +X(0xF6, MAP_PRIMARY, 5, MODRM, IMUL, 1, A(AK_RM8), A0, A0, "Signed mul AL * r/m8 -> AX") \ +X(0xF6, MAP_PRIMARY, 6, MODRM, DIV, 1, A(AK_RM8), A0, A0, "Unsigned div AX / r/m8") \ +X(0xF6, MAP_PRIMARY, 7, MODRM, IDIV, 1, A(AK_RM8), A0, A0, "Signed div AX / r/m8") \ +X(0xF7, MAP_PRIMARY, 0, MODRM|IMM32|REXW_OK, TEST, 2, A(AK_RM16_32_64), A(AK_IMM_OPSIZE), A0, "TEST r/m, imm") \ +X(0xF7, MAP_PRIMARY, 2, MODRM|REXW_OK, NOT, 1, A(AK_RM16_32_64), A0, A0, "Invert r/m") \ +X(0xF7, MAP_PRIMARY, 3, MODRM|REXW_OK, NEG, 1, A(AK_RM16_32_64), A0, A0, "Two's-complement r/m") \ +X(0xF7, MAP_PRIMARY, 4, MODRM|REXW_OK, MUL, 1, A(AK_RM16_32_64), A0, A0, "Unsigned mul accumulator") \ +X(0xF7, MAP_PRIMARY, 5, MODRM|REXW_OK, IMUL, 1, A(AK_RM16_32_64), A0, A0, "Signed mul accumulator") \ +X(0xF7, MAP_PRIMARY, 6, MODRM|REXW_OK, DIV, 1, A(AK_RM16_32_64), A0, A0, "Unsigned division") \ +X(0xF7, MAP_PRIMARY, 7, MODRM|REXW_OK, IDIV, 1, A(AK_RM16_32_64), A0, A0, "Signed division") \ +X(0xF8, MAP_PRIMARY, -1, 0, CLC, 0, A0, A0, A0, "Clear CF") \ +X(0xF9, MAP_PRIMARY, -1, 0, STC, 0, A0, A0, A0, "Set CF") \ +X(0xFA, MAP_PRIMARY, -1, 0, CLI, 0, A0, A0, A0, "Clear IF") \ +X(0xFB, MAP_PRIMARY, -1, 0, STI, 0, A0, A0, A0, "Set IF") \ +X(0xFC, MAP_PRIMARY, -1, 0, CLD, 0, A0, A0, A0, "Clear DF") \ +X(0xFD, MAP_PRIMARY, -1, 0, STD, 0, A0, A0, A0, "Set DF") \ +X(0xFE, MAP_PRIMARY, 0, MODRM|LOCK_OK, INC, 1, A(AK_RM8), A0, A0, "Increment r/m8") \ +X(0xFE, MAP_PRIMARY, 1, MODRM|LOCK_OK, DEC, 1, A(AK_RM8), A0, A0, "Decrement r/m8") \ +X(0xFF, MAP_PRIMARY, 0, MODRM|LOCK_OK|REXW_OK, INC, 1, A(AK_RM16_32_64), A0, A0, "Increment r/m") \ +X(0xFF, MAP_PRIMARY, 1, MODRM|LOCK_OK|REXW_OK, DEC, 1, A(AK_RM16_32_64), A0, A0, "Decrement r/m") \ +X(0xFF, MAP_PRIMARY, 2, MODRM|REXW_OK, CALL, 1, A(AK_RM16_32_64), A0, A0, "Indirect call") \ +X(0xFF, MAP_PRIMARY, 4, MODRM|REXW_OK, JMP, 1, A(AK_RM16_32_64), A0, A0, "Indirect jump") \ +X(0xFF, MAP_PRIMARY, 6, MODRM, PUSH, 1, A(AK_RM64), A0, A0, "Push r/m64") \ + +struct instruction { + uint8_t opcode; + int32_t ext; + uint32_t attrs; + uint32_t argc; + const char* mnemonic; + uint32_t args[3]; + const char* desc; +}; + +#define XSTR(x) #x +#define STR(x) XSTR(x) + +static const struct instruction byte_to_instruction[] = { +#define X(_opcode, _map, _ext, _attrs, _mnemonic, _argc, _a1, _a2, _a3, _desc) \ + [_opcode] = {.opcode = _opcode, .ext = _ext, .attrs = _attrs, .mnemonic = STR(_mnemonic), .argc = _argc, .args = {_a1, _a2, _a3}, .desc = _desc}, + INSTRUCTION_LIST +#undef X +}; + +#include + +int main() +{ + int c; + while ((c = fgetc(stdin)) != EOF) { + if (c < 0 || c > sizeof byte_to_instruction / sizeof *byte_to_instruction) { + fprintf(stderr, "unsupported byte 0x%02x, aborting\n", c); + break; + } + struct instruction ins = byte_to_instruction[c]; + if (ins.opcode == 0) { + fprintf(stderr, "unsupported byte 0x%02x, aborting\n", c); + break; + } + + printf("OP: %s (0x%02x)\n", ins.mnemonic, ins.opcode); + + for (int i = 0; i < ins.argc; i++) { + uint32_t arg = ins.args[i]; + enum arg_kind kind = ARG_KIND(arg); + int attr = ARG_ATTRS(arg); + + union { + unsigned char bytes[4]; + int32_t i32; + int32_t u32; + } val = {0}; + + //for (int i = 3; i > 0; i--) { + for (int i = 0; i < arg_kind_bytes[kind]; i++) { + c = fgetc(stdin); + if (c == EOF) goto end; + val.bytes[i] = c; + } + + switch (kind) { + default: + { + printf("ARG: kind: %s, attrs: %s, val: 0x%02x\n", arg_kind_str[kind], arg_attr_str[attr], val.u32); + } break; + } + } + + } + +end: + return 0; +} diff --git a/src/lib/dump_asm.h b/src/lib/dump_asm.h new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/printf.c b/src/lib/printf.c index ea4ebf0..8b2a6e9 100644 --- a/src/lib/printf.c +++ b/src/lib/printf.c @@ -109,6 +109,26 @@ static int print_b32(struct printf_state* s, int padding, char pad_char) return print_long(n, alphabet, false, padding, pad_char); } +static int print_linear_address(struct printf_state* s) +{ + int padding = 0; + char pad_char = '\0'; + uint32_t addr = va_arg(s->ap, uint32_t); + + uint32_t dir = addr >> 22; + uint32_t page = (addr & ((1<<22)-1)) >> 12; + uint32_t offset = (addr & ((1<<12)-1)); + + struct str alphabet = str_attach("01"); + int n = 0; + n += print_long(dir, alphabet, false, padding, pad_char); + terminal_putchar(' '); + n += print_long(page, alphabet, false, padding, pad_char); + terminal_putchar(' '); + n += print_long(offset, alphabet, false, padding, pad_char); + return n; +} + static int print_str(struct printf_state* s, int padding, char pad_char) { // TODO: implement padding @@ -246,6 +266,12 @@ static int parse_format_cmd(struct printf_state* s) case 'str': return print_str(s, pad, pad_char); + case 'bin': + return print_b32(s, pad, pad_char); + + case 'addr': + return print_linear_address(s); + case 'char': return print_char(s);