This commit is contained in:
2025-10-15 23:20:23 +02:00
parent 4f1ffac3f9
commit 9c89bbcfc2
14 changed files with 1255 additions and 39 deletions

View File

@@ -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 #
###################

View File

@@ -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)
}
}

291
src/kernel/file_system.c Normal file
View File

@@ -0,0 +1,291 @@
#include "file_system.h"
#include <stdint.h>
#include <limits.h>
#include <stddef.h>
#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;
}

60
src/kernel/file_system.h Normal file
View File

@@ -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;

View File

@@ -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");

View File

@@ -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 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_0 & 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");
}

9
src/kernel/malloc.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#include <stddef.h>
void* kalloc(size_t size);
void kfree(void* ptr);
void* krealloc(void* ptr, size_t size);

192
src/kernel/serial.h Normal file
View File

@@ -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]);
}
}

60
src/kernel/syscall.c Normal file
View File

@@ -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;
}

20
src/kernel/syscall.h Normal file
View File

@@ -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" \
);

View File

@@ -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

493
src/lib/dump_asm.c Normal file
View File

@@ -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 <stdint.h>
/* --- 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 <stdio.h>
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;
}

0
src/lib/dump_asm.h Normal file
View File

View File

@@ -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);