aboutsummaryrefslogtreecommitdiff
path: root/src/asm/x86_64.c
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-08-12 09:48:55 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commitea97370f973374f863e4296c2bb872be8b5235a3 (patch)
treebcf74846c250dd5b1f84049622ed2766605365e7 /src/asm/x86_64.c
parent4ca3b74621c3608de42a91730a71892d9d7c27b5 (diff)
Before interpreter. Cleanup build script. Begin writing code analyzer tool to find common mistakes
Diffstat (limited to 'src/asm/x86_64.c')
-rw-r--r--src/asm/x86_64.c385
1 files changed, 0 insertions, 385 deletions
diff --git a/src/asm/x86_64.c b/src/asm/x86_64.c
deleted file mode 100644
index e246fbc..0000000
--- a/src/asm/x86_64.c
+++ /dev/null
@@ -1,385 +0,0 @@
-#include "../../include/asm/x86_64.h"
-
-#include "../../include/std/mem.h"
-#include "../../include/std/log.h"
-#include <errno.h>
-#include <assert.h>
-#include <stdio.h>
-
-#include <sys/mman.h>
-
-#define REX_W 0x48
-
-void asm_ptr_init(AsmPtr *self, Reg64 base) {
- self->base = base;
- self->index = -1;
- self->disp = 0;
- self->scale = 0;
-}
-
-void asm_ptr_init_index(AsmPtr *self, Reg64 base, Reg64 index) {
- self->base = base;
- self->index = index;
- self->disp = 0;
- self->scale = 0;
-}
-
-void asm_ptr_init_disp(AsmPtr *self, Reg64 base, i32 disp) {
- self->base = base;
- self->index = -1;
- self->disp = disp;
- self->scale = 0;
-}
-
-void asm_ptr_init_index_disp(AsmPtr *self, Reg64 base, Reg64 index, i32 disp) {
- self->base = base;
- self->index = index;
- self->disp = disp;
- self->scale = 0;
-}
-
-int asm_init(Asm *self) {
- self->size = am_pagesize();
- amal_log_debug("asm: page size: %u", self->size);
- self->code = mmap(NULL, self->size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if(self->code == MAP_FAILED)
- return -errno;
- self->code_it = self->code;
- return 0;
-}
-
-void asm_deinit(Asm *self) {
- if(self->code)
- munmap(self->code, self->size);
- self->code = NULL;
- self->code_it = NULL;
- self->size = 0;
-}
-
-static void asm_print_code_hex(Asm *self) {
- u8 *ptr;
- int off;
- ptr = self->code;
- off = 0;
- while(ptr != self->code_it) {
- printf("%02x", *ptr);
- ++ptr;
- ++off;
- if(off == 8) {
- putc('\n', stdout);
- off = 0;
- } else {
- putc(' ', stdout);
- }
- }
- if(off != 0)
- putc('\n', stdout);
-}
-
-int asm_execute(Asm *self) {
- void (*func)();
- if(mprotect(self->code, self->size, PROT_READ | PROT_EXEC) != 0)
- return -errno;
-
- asm_print_code_hex(self);
-
- /* TODO: Verify if this is valid on all platforms. According to ISO C standard it isn't? */
- *(void**)(&func) = self->code;
- func();
- return 0;
-}
-
-/* TODO: See how this can be optimized */
-static CHECK_RESULT int asm_ensure_capacity(Asm *self, usize size) {
- usize current_offset;
- current_offset = (u8*)self->code_it - (u8*)self->code;
- if(current_offset + size > self->size) {
- void *new_mem;
- usize new_size;
- new_size = self->size + am_pagesize();
- new_mem = mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if(self->code == MAP_FAILED)
- return -errno;
-
- am_memcpy(new_mem, self->code, self->size);
- self->code = new_mem;
- self->size = new_size;
- self->code_it = (u8*)self->code + current_offset;
- }
- return 0;
-}
-
-#ifdef DEBUG
-static isize asm_get_capacity_left(Asm *self) {
- return (isize)self->size - (isize)((u8*)self->code_it - (u8*)self->code);
-}
-#endif
-
-int asm_nop(Asm *self) {
- return_if_error(asm_ensure_capacity(self, 1));
- *self->code_it++ = 0x90;
- return 0;
-}
-
-static i32 abs_i32(i32 value) {
- return value >= 0 ? value : -value;
-}
-
-/*
-TODO: Implement 1 and 2 byte displacement?
-There has to be at least 6 bytes left in the asm buffer before calling this function.
-*/
-static void asm_rm(Asm *self, AsmPtr *mem, Reg64 reg) {
- u8 rm_byte;
- u8 disp_bytes;
- assert(asm_get_capacity_left(self) >= 6);
- if((int)mem->index != -1) {
- u8 sib_offset;
- if(mem->disp == 0) {
- rm_byte = 0x04;
- disp_bytes = 0;
- } else if(abs_i32(mem->disp) <= INT8_MAX) {
- rm_byte = 0x44;
- disp_bytes = 1;
- } else {
- rm_byte = 0x84;
- disp_bytes = 4;
- }
-
- #ifdef DEBUG
- if(mem->scale != 0 && mem->scale != 2 && mem->scale != 4 && mem->scale != 8) {
- amal_log_error("Invalid scale %d, expected 0, 2, 4, or 8", mem->scale);
- assert(bool_false);
- }
- #endif
- assert(mem->base != RBP && "TODO: Implement RBP base for sib byte. RBP is special and requires different logic");
- sib_offset = (mem->scale << 5) + 8*mem->index + mem->base;
-
- *self->code_it++ = rm_byte;
- *self->code_it++ = sib_offset;
- } else {
- if(mem->disp == 0) {
- if(mem->base == RBP) {
- rm_byte = 0x45;
- disp_bytes = 1;
- } else {
- rm_byte = mem->base;
- disp_bytes = 0;
- }
- } else if(abs_i32(mem->disp) <= INT8_MAX) {
- rm_byte = 0x40 + mem->base;
- disp_bytes = 1;
- } else {
- rm_byte = 0x80 + mem->base;
- disp_bytes = 4;
- }
- *self->code_it++ = (reg << 3) | rm_byte;
- }
-
- am_memcpy(self->code_it, &mem->disp, disp_bytes);
- self->code_it += disp_bytes;
-}
-
-/* There has to be at least 1 byte left in the asm buffer before calling this function. */
-static void asm_rr(Asm *self, Reg64 dst, Reg64 src) {
- assert(asm_get_capacity_left(self) >= 1);
- *self->code_it++ = 0xC0 + dst + 8*src;
-}
-
-/* TODO: Implement 1 and 2 byte immediate? */
-int asm_mov_mi(Asm *self, AsmPtr *dst, i32 immediate) {
- /* 12 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
- return_if_error(asm_ensure_capacity(self, 12));
- *self->code_it++ = REX_W;
- *self->code_it++ = 0xC7;
- asm_rm(self, dst, 0);
- am_memcpy(self->code_it, &immediate, sizeof(immediate));
- self->code_it += sizeof(immediate);
- return 0;
-}
-
-int asm_mov_mr(Asm *self, AsmPtr *dst, Reg64 src) {
- /* 8 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
- return_if_error(asm_ensure_capacity(self, 8));
- *self->code_it++ = REX_W;
- *self->code_it++ = 0x89;
- asm_rm(self, dst, src);
- return 0;
-}
-
-int asm_mov_rm(Asm *self, Reg64 dst, AsmPtr *src) {
- /* 8 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
- return_if_error(asm_ensure_capacity(self, 8));
- *self->code_it++ = REX_W;
- *self->code_it++ = 0x8B;
- asm_rm(self, src, dst);
- return 0;
-}
-
-int asm_mov_ri(Asm *self, Reg64 dst, i64 immediate) {
- /* 10 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
- return_if_error(asm_ensure_capacity(self, 10));
- *self->code_it++ = REX_W;
- *self->code_it++ = 0xB8 + dst;
- am_memcpy(self->code_it, &immediate, sizeof(immediate));
- self->code_it += sizeof(immediate);
- return 0;
-}
-
-int asm_mov_rr(Asm *self, Reg64 dst, Reg64 src) {
- /* 3 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
- return_if_error(asm_ensure_capacity(self, 3));
- *self->code_it++ = REX_W;
- *self->code_it++ = 0x89;
- asm_rr(self, dst, src);
- return 0;
-}
-
-int asm_add_rr(Asm *self, Reg64 dst, Reg64 src) {
- /* 3 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
- return_if_error(asm_ensure_capacity(self, 3));
- *self->code_it++ = REX_W;
- *self->code_it++ = 0x01;
- asm_rr(self, dst, src);
- return 0;
-}
-
-int asm_sub_rr(Asm *self, Reg64 dst, Reg64 src) {
- /* 3 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
- return_if_error(asm_ensure_capacity(self, 3));
- *self->code_it++ = REX_W;
- *self->code_it++ = 0x29;
- asm_rr(self, dst, src);
- return 0;
-}
-
-int asm_imul_rr(Asm *self, Reg64 dst, Reg64 src) {
- /* 3 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
- return_if_error(asm_ensure_capacity(self, 4));
- *self->code_it++ = REX_W;
- *self->code_it++ = 0x0F;
- *self->code_it++ = 0xAF;
- asm_rr(self, dst, src);
- return 0;
-}
-
-int asm_pushr(Asm *self, Reg64 reg) {
- return_if_error(asm_ensure_capacity(self, 1));
- *self->code_it++ = 0x50 + reg;
- return 0;
-}
-
-int asm_popr(Asm *self, Reg64 reg) {
- return_if_error(asm_ensure_capacity(self, 1));
- *self->code_it++ = 0x58 + reg;
- return 0;
-}
-
-/* /r */
-#define DEFINE_INS_RM(mnemonic, opcode) \
-int asm_##mnemonic##_rm32(Asm *self, Reg32 dst, Reg32 src) { \
- return_if_error(asm_ensure_capacity(self, 2)); \
- *self->code_it++ = opcode; \
- *self->code_it++ = 0xC0 + 8*dst + src; \
- return 0; \
-} \
- \
-int asm_##mnemonic##_rm64(Asm *self, Reg64 dst, Reg64 src) { \
- return_if_error(asm_ensure_capacity(self, 1)); \
- *self->code_it++ = REX_W; \
- return asm_##mnemonic##_rm32(self, (Reg32)dst, (Reg32)src); \
-}
-
-DEFINE_INS_RM(mov, 0x8B)
-DEFINE_INS_RM(add, 0x03)
-DEFINE_INS_RM(sub, 0x2B)
-DEFINE_INS_RM(and, 0x23)
-DEFINE_INS_RM(or, 0x0B)
-DEFINE_INS_RM(xor, 0x33)
-DEFINE_INS_RM(cmp, 0x3B)
-
-/*
- /number
- The number is called the extension, a number from 0 to 7;
- It's a number used to extend the opcode type, since the instruction only uses
- one register the other register can be encoded for that.
-*/
-#define DEFINE_INS_EXT_IMM(mnemonic, extension) \
-int asm_##mnemonic##_rm32_imm(Asm *self, Reg32 reg, i32 immediate) { \
- if(abs_i32(immediate) <= INT8_MAX) { \
- return_if_error(asm_ensure_capacity(self, 3)); \
- *self->code_it++ = 0x83; \
- *self->code_it++ = 0xC0 + 8*extension + reg; \
- *self->code_it++ = (u8)immediate; \
- } else { \
- return_if_error(asm_ensure_capacity(self, 6)); \
- *self->code_it++ = 0x81; \
- *self->code_it++ = 0xC0 + 8*extension + reg; \
- am_memcpy(self->code_it, &immediate, sizeof(immediate)); \
- self->code_it += sizeof(immediate); \
- } \
- return 0; \
-} \
- \
-int asm_##mnemonic##_rm64_imm(Asm *self, Reg64 reg, i32 immediate) { \
- return_if_error(asm_ensure_capacity(self, 1)); \
- *self->code_it++ = REX_W; \
- return asm_##mnemonic##_rm32_imm(self, (Reg32)reg, immediate); \
-}
-
-DEFINE_INS_EXT_IMM(add, 0)
-DEFINE_INS_EXT_IMM(or, 1)
-DEFINE_INS_EXT_IMM(adc, 2)
-DEFINE_INS_EXT_IMM(sbb, 3)
-DEFINE_INS_EXT_IMM(and, 4)
-DEFINE_INS_EXT_IMM(sub, 5)
-DEFINE_INS_EXT_IMM(xor, 6)
-DEFINE_INS_EXT_IMM(cmp, 7)
-
-/*
- /number
- The number is called the extension, a number from 0 to 7;
- It's a number used to extend the opcode type, since the instruction only uses
- one register the other register can be encoded for that.
-*/
-#define DEFINE_INS_SHIFT_IMM8(mnemonic, extension) \
-int asm_##mnemonic##_rm32_imm(Asm *self, Reg32 reg, i8 immediate) { \
- if(immediate == 1) { \
- return_if_error(asm_ensure_capacity(self, 2)); \
- *self->code_it++ = 0xC1; \
- *self->code_it++ = 0xC0 + 8*reg + extension; \
- } else { \
- return_if_error(asm_ensure_capacity(self, 3)); \
- *self->code_it++ = 0xD1; \
- *self->code_it++ = 0xC0 + 8*reg + extension; \
- *self->code_it++ = immediate; \
- } \
- return 0; \
-} \
- \
-int asm_##mnemonic##_rm64_imm(Asm *self, Reg64 reg, i8 immediate) { \
- return_if_error(asm_ensure_capacity(self, 1)); \
- *self->code_it++ = REX_W; \
- return asm_##mnemonic##_rm32_imm(self, (Reg32)reg, immediate); \
-}
-
-DEFINE_INS_SHIFT_IMM8(rol, 0)
-DEFINE_INS_SHIFT_IMM8(ror, 1)
-DEFINE_INS_SHIFT_IMM8(rcl, 2)
-DEFINE_INS_SHIFT_IMM8(rcr, 3)
-DEFINE_INS_SHIFT_IMM8(shl, 4)
-DEFINE_INS_SHIFT_IMM8(shr, 5)
-/*DEFINE_INS_SHIFT_IMM8(shl, 6)*/
-DEFINE_INS_SHIFT_IMM8(sar, 7)
-
-int asm_ret(Asm *self, u16 bytes) {
- if(bytes == 0) {
- return_if_error(asm_ensure_capacity(self, 1));
- *self->code_it++ = 0xC3;
- } else {
- return_if_error(asm_ensure_capacity(self, 3));
- *self->code_it++ = 0xC2;
- am_memcpy(self->code_it, &bytes, sizeof(bytes));
- }
- return 0;
-}