summaryrefslogtreecommitdiff
path: root/src/qcommon
diff options
context:
space:
mode:
Diffstat (limited to 'src/qcommon')
-rw-r--r--src/qcommon/vm_x86_64.c1097
-rw-r--r--src/qcommon/vm_x86_64_assembler.c1463
2 files changed, 0 insertions, 2560 deletions
diff --git a/src/qcommon/vm_x86_64.c b/src/qcommon/vm_x86_64.c
deleted file mode 100644
index 64e582fc..00000000
--- a/src/qcommon/vm_x86_64.c
+++ /dev/null
@@ -1,1097 +0,0 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2013 Darklegion Development
-Copyright (C) 2005 Ludwig Nussel <ludwig.nussel@web.de>
-
-This file is part of Tremulous.
-
-Tremulous is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Tremulous is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Tremulous; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-// vm_x86_64.c -- load time compiler and execution environment for x86-64
-
-#include "vm_local.h"
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <time.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdarg.h>
-
-#include <inttypes.h>
-
-#ifdef __WIN64__
- #include <windows.h>
- #define CROSSCALL __attribute__ ((sysv_abi))//fool the vm we're SYSV ABI
- //#define __USE_MINGW_ANSI_STDIO 1 //very slow - avoid if possible
-#else
- #include <sys/mman.h>
- #include <sys/wait.h>
- #define VM_X86_64_MMAP
- #define CROSSCALL
-#endif
-
-//#define DEBUG_VM
-
-#ifdef DEBUG_VM
-#define Dfprintf(fd, args...) fprintf(fd, ##args)
-static FILE* qdasmout;
-#else
-#define Dfprintf(args...)
-#endif
-
-#define VM_FREEBUFFERS(vm) do {assembler_init(0); VM_Destroy_Compiled(vm);} while(0)
-
-void assembler_set_output(char* buf);
-size_t assembler_get_code_size(void);
-void assembler_init(int pass);
-void assemble_line(const char* input, size_t len);
-
-static void VM_Destroy_Compiled(vm_t* self);
-
-/*
-
- |=====================|
- ^ dataMask ^- programStack rdi
- |
- +- r8
-
- eax scratch
- rbx/bl opStack offset
- ecx scratch (required for shifts)
- edx scratch (required for divisions)
- rsi scratch
- rdi program frame pointer (programStack)
- r8 pointer data (vm->dataBase)
- r9 opStack base (opStack)
- r10 start of generated code
-*/
-
-
-static intptr_t CROSSCALL callAsmCall(intptr_t callProgramStack, int64_t callSyscallNum)
-{
- vm_t *savedVM;
- intptr_t ret = 0x77;
- intptr_t args[MAX_VMSYSCALL_ARGS];
-// int iargs[MAX_VMSYSCALL_ARGS];
- int i;
-
-// Dfprintf(stderr, "callAsmCall(%ld, %ld)\n", callProgramStack, callSyscallNum);
-// Com_Printf("-> callAsmCall %s, level %d, num %ld\n", currentVM->name, currentVM->callLevel, callSyscallNum);
-
- savedVM = currentVM;
-
- // save the stack to allow recursive VM entry
- currentVM->programStack = callProgramStack - 4;
-
- args[0] = callSyscallNum;
-// iargs[0] = callSyscallNum;
- for(i = 0; i < ARRAY_LEN(args)-1; ++i)
- {
-// iargs[i+1] = *(int *)((byte *)currentVM->dataBase + callProgramStack + 8 + 4*i);
- args[i+1] = *(int *)((byte *)currentVM->dataBase + callProgramStack + 8 + 4*i);
- }
- ret = currentVM->systemCall(args);
-
- currentVM = savedVM;
-// Com_Printf("<- callAsmCall %s, level %d, num %ld\n", currentVM->name, currentVM->callLevel, callSyscallNum);
-
- return ret;
-}
-
-#ifdef DEBUG_VM
-static char *opnames[256] = {
- "OP_UNDEF",
-
- "OP_IGNORE",
-
- "OP_BREAK",
-
- "OP_ENTER",
- "OP_LEAVE",
- "OP_CALL",
- "OP_PUSH",
- "OP_POP",
-
- "OP_CONST",
-
- "OP_LOCAL",
-
- "OP_JUMP",
-
- //-------------------
-
- "OP_EQ",
- "OP_NE",
-
- "OP_LTI",
- "OP_LEI",
- "OP_GTI",
- "OP_GEI",
-
- "OP_LTU",
- "OP_LEU",
- "OP_GTU",
- "OP_GEU",
-
- "OP_EQF",
- "OP_NEF",
-
- "OP_LTF",
- "OP_LEF",
- "OP_GTF",
- "OP_GEF",
-
- //-------------------
-
- "OP_LOAD1",
- "OP_LOAD2",
- "OP_LOAD4",
- "OP_STORE1",
- "OP_STORE2",
- "OP_STORE4",
- "OP_ARG",
-
- "OP_BLOCK_COPY",
-
- //-------------------
-
- "OP_SEX8",
- "OP_SEX16",
-
- "OP_NEGI",
- "OP_ADD",
- "OP_SUB",
- "OP_DIVI",
- "OP_DIVU",
- "OP_MODI",
- "OP_MODU",
- "OP_MULI",
- "OP_MULU",
-
- "OP_BAND",
- "OP_BOR",
- "OP_BXOR",
- "OP_BCOM",
-
- "OP_LSH",
- "OP_RSHI",
- "OP_RSHU",
-
- "OP_NEGF",
- "OP_ADDF",
- "OP_SUBF",
- "OP_DIVF",
- "OP_MULF",
-
- "OP_CVIF",
- "OP_CVFI"
-};
-#endif // DEBUG_VM
-
-static unsigned char op_argsize[256] =
-{
- [OP_ENTER] = 4,
- [OP_LEAVE] = 4,
- [OP_CONST] = 4,
- [OP_LOCAL] = 4,
- [OP_EQ] = 4,
- [OP_NE] = 4,
- [OP_LTI] = 4,
- [OP_LEI] = 4,
- [OP_GTI] = 4,
- [OP_GEI] = 4,
- [OP_LTU] = 4,
- [OP_LEU] = 4,
- [OP_GTU] = 4,
- [OP_GEU] = 4,
- [OP_EQF] = 4,
- [OP_NEF] = 4,
- [OP_LTF] = 4,
- [OP_LEF] = 4,
- [OP_GTF] = 4,
- [OP_GEF] = 4,
- [OP_ARG] = 1,
- [OP_BLOCK_COPY] = 4,
-};
-
-static __attribute__ ((format (printf, 1, 2))) void emit(const char* fmt, ...)
-{
- va_list ap;
- char line[4096];
- va_start(ap, fmt);
- Q_vsnprintf(line, sizeof(line), fmt, ap);
- va_end(ap);
- assemble_line(line, strlen(line));
-}
-
-#ifdef DEBUG_VM
-#define RANGECHECK(reg, bytes) \
- emit("movl %%" #reg ", %%ecx"); \
- emit("andl $0x%x, %%ecx", vm->dataMask &~(bytes-1)); \
- emit("cmpl %%" #reg ", %%ecx"); \
- emit("jz rc_ok_i_%08x", instruction); \
- emit("movq $%"PRIu64", %%rax", (intptr_t) memviolation); \
- emit("callq *%%rax"); \
- emit("rc_ok_i_%08x:", instruction)
-#elif 1
-// check is too expensive, so just confine memory access
-#define RANGECHECK(reg, bytes) \
- emit("andl $0x%x, %%" #reg, vm->dataMask &~(bytes-1))
-#else
-#define RANGECHECK(reg, bytes)
-#endif
-
-#define STACK_PUSH(bytes) \
- emit("addb $0x%x, %%bl", bytes >> 2); \
-
-#define STACK_POP(bytes) \
- emit("subb $0x%x, %%bl", bytes >> 2); \
-
-#define CHECK_INSTR_REG(reg) \
- emit("cmpl $%u, %%"#reg, header->instructionCount); \
- emit("jb jmp_ok_i_%08x", instruction); \
- emit("movq $%"PRIu64", %%rax", (intptr_t)jmpviolation); \
- emit("callq *%%rax"); \
- emit("jmp_ok_i_%08x:", instruction)
-
-#define PREPARE_JMP(reg) \
- CHECK_INSTR_REG(reg); \
- emit("movq $%"PRIu64", %%rsi", (intptr_t)vm->instructionPointers); \
- emit("movl (%%rsi, %%rax, 8), %%eax"); \
- emit("addq %%r10, %%rax")
-
-#define CHECK_INSTR(nr) \
- do { if(nr < 0 || nr >= header->instructionCount) { \
- VM_FREEBUFFERS(vm); \
- Com_Error( ERR_DROP, \
- "%s: jump target 0x%x out of range at offset %d", __func__, nr, pc ); \
- } } while(0)
-
-#define JMPIARG() \
- CHECK_INSTR(iarg); \
- emit("movq $%"PRIu64", %%rax", vm->codeBase+vm->instructionPointers[iarg]); \
- emit("jmpq *%%rax")
-
-#define CONST_OPTIMIZE
-#ifdef CONST_OPTIMIZE
-#define MAYBE_EMIT_CONST() \
- if (got_const) \
- { \
- got_const = 0; \
- vm->instructionPointers[instruction-1] = assembler_get_code_size(); \
- STACK_PUSH(4); \
- emit("movl $%d, (%%r9, %%rbx, 4)", const_value); \
- }
-#else
-#define MAYBE_EMIT_CONST()
-#endif
-
-// integer compare and jump
-#define IJ(op) \
- MAYBE_EMIT_CONST(); \
- STACK_POP(8); \
- emit("movl 4(%%r9, %%rbx, 4), %%eax"); \
- emit("cmpl 8(%%r9, %%rbx, 4), %%eax"); \
- emit(op " i_%08x", instruction+1); \
- JMPIARG(); \
- neednilabel = 1
-
-#ifdef USE_X87
-#define FJ(bits, op) \
- MAYBE_EMIT_CONST(); \
- STACK_POP(8); \
- emit("flds 4(%%r9, %%rbx, 4)");\
- emit("fcomps 8(%%r9, %%rbx, 4)");\
- emit("fnstsw %%ax");\
- emit("testb $" #bits ", %%ah");\
- emit(op " i_%08x", instruction+1);\
- JMPIARG(); \
- neednilabel = 1
-#define XJ(x)
-#else
-#define FJ(x, y)
-#define XJ(op) \
- MAYBE_EMIT_CONST(); \
- STACK_POP(8); \
- emit("movss 4(%%r9, %%rbx, 4), %%xmm0");\
- emit("ucomiss 8(%%r9, %%rbx, 4), %%xmm0");\
- emit("jp i_%08x", instruction+1);\
- emit(op " i_%08x", instruction+1);\
- JMPIARG(); \
- neednilabel = 1
-#endif
-
-#define SIMPLE(op) \
- MAYBE_EMIT_CONST(); \
- emit("movl (%%r9, %%rbx, 4), %%eax"); \
- STACK_POP(4); \
- emit(op " %%eax, (%%r9, %%rbx, 4)")
-
-#ifdef USE_X87
-#define FSIMPLE(op) \
- MAYBE_EMIT_CONST(); \
- STACK_POP(4); \
- emit("flds (%%r9, %%rbx, 4)"); \
- emit(op " 4(%%r9, %%rbx, 4)"); \
- emit("fstps (%%r9, %%rbx, 4)")
-#define XSIMPLE(op)
-#else
-#define FSIMPLE(op)
-#define XSIMPLE(op) \
- MAYBE_EMIT_CONST(); \
- STACK_POP(4); \
- emit("movss (%%r9, %%rbx, 4), %%xmm0"); \
- emit(op " 4(%%r9, %%rbx, 4), %%xmm0"); \
- emit("movss %%xmm0, (%%r9, %%rbx, 4)")
-#endif
-
-#define SHIFT(op) \
- MAYBE_EMIT_CONST(); \
- STACK_POP(4); \
- emit("movl 4(%%r9, %%rbx, 4), %%ecx"); \
- emit("movl (%%r9, %%rbx, 4), %%eax"); \
- emit(op " %%cl, %%eax"); \
- emit("movl %%eax, (%%r9, %%rbx, 4)")
-
-#ifdef DEBUG_VM
-#define NOTIMPL(x) \
- do { Com_Error(ERR_DROP, "instruction not implemented: %s", opnames[x]); } while(0)
-#else
-#define NOTIMPL(x) \
- do { Com_Printf(S_COLOR_RED "instruction not implemented: %x\n", x); vm->compiled = qfalse; return; } while(0)
-#endif
-
-static void* getentrypoint(vm_t* vm)
-{
- return vm->codeBase;
-}
-
-static __attribute__ ((noreturn)) void CROSSCALL eop(void)
-{
- Com_Error(ERR_DROP, "End of program reached without return!");
- exit(1);
-}
-
-static __attribute__ ((noreturn)) void CROSSCALL jmpviolation(void)
-{
- Com_Error(ERR_DROP, "Program tried to execute code outside VM");
- exit(1);
-}
-
-#ifdef DEBUG_VM
-static __attribute__ ((noreturn)) void CROSSCALL memviolation(void)
-{
- Com_Error(ERR_DROP, "Program tried to access memory outside VM, or unaligned memory access");
- exit(1);
-}
-
-static __attribute__ ((noreturn)) void CROSSCALL opstackviolation(void)
-{
- Com_Error(ERR_DROP, "Program corrupted the VM opStack");
- exit(1);
-}
-#endif
-
-/*
-=================
-VM_Compile
-=================
-*/
-void VM_Compile( vm_t *vm, vmHeader_t *header ) {
- unsigned char op;
- int pc;
- unsigned instruction;
- char* code;
- unsigned iarg = 0;
- unsigned char barg = 0;
- int neednilabel = 0;
- struct timeval tvstart = {0, 0};
-#ifdef DEBUG_VM
- char fn_d[MAX_QPATH]; // disassembled
-#endif
-
- int pass;
- size_t compiledOfs = 0;
-
- // const optimization
- unsigned got_const = 0, const_value = 0;
-
- vm->codeBase = NULL;
-
- gettimeofday(&tvstart, NULL);
-
- for (pass = 0; pass < 2; ++pass) {
-
- if(pass)
- {
- compiledOfs = assembler_get_code_size();
- vm->codeLength = compiledOfs;
-
- #ifdef VM_X86_64_MMAP
- vm->codeBase = mmap(NULL, compiledOfs, PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
- if(vm->codeBase == MAP_FAILED)
- Com_Error(ERR_FATAL, "VM_CompileX86_64: can't mmap memory");
- #elif __WIN64__
- // allocate memory with write permissions under windows.
- vm->codeBase = VirtualAlloc(NULL, compiledOfs, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
- if(!vm->codeBase)
- Com_Error(ERR_FATAL, "VM_CompileX86_64: VirtualAlloc failed");
- #else
- vm->codeBase = malloc(compiledOfs);
- if(!vm_codeBase)
- Com_Error(ERR_FATAL, "VM_CompileX86_64: Failed to allocate memory");
- #endif
-
- assembler_set_output((char*)vm->codeBase);
- }
-
- assembler_init(pass);
-
-#ifdef DEBUG_VM
- strcpy(fn_d,vm->name);
- strcat(fn_d, ".qdasm");
-
- qdasmout = fopen(fn_d, "w");
-#endif
-
- // translate all instructions
- pc = 0;
- code = (char *)header + header->codeOffset;
-
- for ( instruction = 0; instruction < header->instructionCount; ++instruction )
- {
- op = code[ pc ];
- ++pc;
-
- vm->instructionPointers[instruction] = assembler_get_code_size();
-
- /* store current instruction number in r15 for debugging */
-#if DEBUG_VM0
- emit("nop");
- emit("movq $%d, %%r15", instruction);
- emit("nop");
-#endif
-
- if(op_argsize[op] == 4)
- {
- iarg = *(int*)(code+pc);
- pc += 4;
- Dfprintf(qdasmout, "%s %8u\n", opnames[op], iarg);
- }
- else if(op_argsize[op] == 1)
- {
- barg = code[pc++];
- Dfprintf(qdasmout, "%s %8hu\n", opnames[op], barg);
- }
- else
- {
- Dfprintf(qdasmout, "%s\n", opnames[op]);
- }
-
- if(neednilabel)
- {
- emit("i_%08x:", instruction);
- neednilabel = 0;
- }
-
- switch ( op )
- {
- case OP_UNDEF:
- NOTIMPL(op);
- break;
- case OP_IGNORE:
- MAYBE_EMIT_CONST();
- emit("nop");
- break;
- case OP_BREAK:
- MAYBE_EMIT_CONST();
- emit("int3");
- break;
- case OP_ENTER:
- MAYBE_EMIT_CONST();
- emit("subl $%d, %%edi", iarg);
- break;
- case OP_LEAVE:
- MAYBE_EMIT_CONST();
- emit("addl $%d, %%edi", iarg); // get rid of stack frame
- emit("ret");
- break;
- case OP_CALL:
- RANGECHECK(edi, 4);
- emit("movl $%d, (%%r8, %%rdi, 1)", instruction+1); // save next instruction
-
- if(got_const)
- {
- if ((int) const_value >= 0)
- {
- CHECK_INSTR(const_value);
- emit("movq $%"PRIu64", %%rax", vm->codeBase+vm->instructionPointers[const_value]);
- emit("callq *%%rax");
- got_const = 0;
- break;
- }
- }
- else
- {
- MAYBE_EMIT_CONST();
- emit("movl (%%r9, %%rbx, 4), %%eax"); // get instr from stack
- STACK_POP(4);
-
- emit("orl %%eax, %%eax");
- emit("jl callSyscall%d", instruction);
-
- PREPARE_JMP(eax);
- emit("callq *%%rax");
-
- emit("jmp i_%08x", instruction+1);
- emit("callSyscall%d:", instruction);
- }
-
-// emit("fnsave 4(%%r9, %%rsi, 1)");
- emit("push %%rdi");
- emit("push %%r8");
- emit("push %%r9");
- emit("push %%r10");
- emit("movq %%rsp, %%rsi"); // we need to align the stack pointer
- emit("subq $8, %%rsi"); // |
- emit("andq $127, %%rsi"); // |
- emit("subq %%rsi, %%rsp"); // <-+
- emit("push %%rsi");
- if(got_const) {
- got_const = 0;
- emit("movq $%u, %%rsi", -1-const_value); // second argument in rsi
- } else {
- emit("notl %%eax"); // convert to actual number
- // first argument already in rdi
- emit("movq %%rax, %%rsi"); // second argument in rsi
- }
- emit("movq $%"PRIu64", %%rax", (intptr_t) callAsmCall);
- emit("callq *%%rax");
- emit("pop %%rsi");
- emit("addq %%rsi, %%rsp");
- emit("pop %%r10");
- emit("pop %%r9");
- emit("pop %%r8");
- emit("pop %%rdi");
-// emit("frstor 4(%%r9, %%rsi, 1)");
- STACK_PUSH(4);
- emit("movl %%eax, (%%r9, %%rbx, 4)"); // store return value
- neednilabel = 1;
- break;
- case OP_PUSH:
- MAYBE_EMIT_CONST();
- STACK_PUSH(4);
- break;
- case OP_POP:
- MAYBE_EMIT_CONST();
- STACK_POP(4);
- break;
- case OP_CONST:
- MAYBE_EMIT_CONST();
-#ifdef CONST_OPTIMIZE
- got_const = 1;
- const_value = iarg;
-#else
- STACK_PUSH(4);
- emit("movl $%d, (%%r9, %%rbx, 4)", iarg);
-#endif
- break;
- case OP_LOCAL:
- MAYBE_EMIT_CONST();
- emit("movl %%edi, %%esi");
- emit("addl $%d,%%esi", iarg);
- STACK_PUSH(4);
- emit("movl %%esi, (%%r9, %%rbx, 4)");
- break;
- case OP_JUMP:
- if(got_const) {
- iarg = const_value;
- got_const = 0;
- JMPIARG();
- } else {
- emit("movl (%%r9, %%rbx, 4), %%eax"); // get instr from stack
- STACK_POP(4);
-
- PREPARE_JMP(eax);
- emit("jmp *%%rax");
- }
- break;
- case OP_EQ:
- IJ("jne");
- break;
- case OP_NE:
- IJ("je");
- break;
- case OP_LTI:
- IJ("jnl");
- break;
- case OP_LEI:
- IJ("jnle");
- break;
- case OP_GTI:
- IJ("jng");
- break;
- case OP_GEI:
- IJ("jnge");
- break;
- case OP_LTU:
- IJ("jnb");
- break;
- case OP_LEU:
- IJ("jnbe");
- break;
- case OP_GTU:
- IJ("jna");
- break;
- case OP_GEU:
- IJ("jnae");
- break;
- case OP_EQF:
- FJ(0x40, "jz");
- XJ("jnz");
- break;
- case OP_NEF:
- FJ(0x40, "jnz");
-#ifndef USE_X87
- MAYBE_EMIT_CONST();
- STACK_POP(8);
- emit("movss 4(%%r9, %%rbx, 4), %%xmm0");
- emit("ucomiss 8(%%r9, %%rbx, 4), %%xmm0");
- emit("jp dojump_i_%08x", instruction);
- emit("jz i_%08x", instruction+1);
- emit("dojump_i_%08x:", instruction);
- JMPIARG();
- neednilabel = 1;
-#endif
- break;
- case OP_LTF:
- FJ(0x01, "jz");
- XJ("jnc");
- break;
- case OP_LEF:
- FJ(0x41, "jz");
- XJ("ja");
- break;
- case OP_GTF:
- FJ(0x41, "jnz");
- XJ("jbe");
- break;
- case OP_GEF:
- FJ(0x01, "jnz");
- XJ("jb");
- break;
- case OP_LOAD1:
- MAYBE_EMIT_CONST();
- emit("movl (%%r9, %%rbx, 4), %%eax"); // get value from stack
- RANGECHECK(eax, 1);
- emit("movb (%%r8, %%rax, 1), %%al"); // deref into eax
- emit("andq $255, %%rax");
- emit("movl %%eax, (%%r9, %%rbx, 4)"); // store on stack
- break;
- case OP_LOAD2:
- MAYBE_EMIT_CONST();
- emit("movl (%%r9, %%rbx, 4), %%eax"); // get value from stack
- RANGECHECK(eax, 2);
- emit("movw (%%r8, %%rax, 1), %%ax"); // deref into eax
- emit("movl %%eax, (%%r9, %%rbx, 4)"); // store on stack
- break;
- case OP_LOAD4:
- MAYBE_EMIT_CONST();
- emit("movl (%%r9, %%rbx, 4), %%eax"); // get value from stack
- RANGECHECK(eax, 4); // not a pointer!?
- emit("movl (%%r8, %%rax, 1), %%eax"); // deref into eax
- emit("movl %%eax, (%%r9, %%rbx, 4)"); // store on stack
- break;
- case OP_STORE1:
- MAYBE_EMIT_CONST();
- emit("movl (%%r9, %%rbx, 4), %%eax"); // get value from stack
- STACK_POP(8);
- emit("andq $255, %%rax");
- emit("movl 4(%%r9, %%rbx, 4), %%esi"); // get pointer from stack
- RANGECHECK(esi, 1);
- emit("movb %%al, (%%r8, %%rsi, 1)"); // store in memory
- break;
- case OP_STORE2:
- MAYBE_EMIT_CONST();
- emit("movl (%%r9, %%rbx, 4), %%eax"); // get value from stack
- STACK_POP(8);
- emit("movl 4(%%r9, %%rbx, 4), %%esi"); // get pointer from stack
- RANGECHECK(esi, 2);
- emit("movw %%ax, (%%r8, %%rsi, 1)"); // store in memory
- break;
- case OP_STORE4:
- MAYBE_EMIT_CONST();
- emit("movl (%%r9, %%rbx, 4), %%eax"); // get value from stack
- STACK_POP(8);
- emit("movl 4(%%r9, %%rbx, 4), %%esi"); // get pointer from stack
- RANGECHECK(esi, 4);
- emit("movl %%eax, (%%r8, %%rsi, 1)"); // store in memory
- break;
- case OP_ARG:
- MAYBE_EMIT_CONST();
- emit("movl (%%r9, %%rbx, 4), %%eax"); // get value from stack
- STACK_POP(4);
- emit("movl $0x%hx, %%esi", barg);
- emit("addl %%edi, %%esi");
- RANGECHECK(esi, 4);
- emit("movl %%eax, (%%r8,%%rsi, 1)"); // store in args space
- break;
- case OP_BLOCK_COPY:
-
- MAYBE_EMIT_CONST();
- STACK_POP(8);
- emit("push %%rdi");
- emit("push %%r8");
- emit("push %%r9");
- emit("push %%r10");
- emit("movq %%rsp, %%rsi"); // we need to align the stack pointer
- emit("subq $8, %%rsi"); // |
- emit("andq $127, %%rsi"); // |
- emit("subq %%rsi, %%rsp"); // <-+
- emit("push %%rsi");
- emit("movl 4(%%r9, %%rbx, 4), %%edi"); // 1st argument dest
- emit("movl 8(%%r9, %%rbx, 4), %%rsi"); // 2nd argument src
- emit("movl $%d, %%edx", iarg); // 3rd argument count
- emit("movq $%"PRIu64", %%rax", (intptr_t) VM_BlockCopy);
- emit("callq *%%rax");
- emit("pop %%rsi");
- emit("addq %%rsi, %%rsp");
- emit("pop %%r10");
- emit("pop %%r9");
- emit("pop %%r8");
- emit("pop %%rdi");
-
- break;
- case OP_SEX8:
- MAYBE_EMIT_CONST();
- emit("movw (%%r9, %%rbx, 4), %%ax");
- emit("andq $255, %%rax");
- emit("cbw");
- emit("cwde");
- emit("movl %%eax, (%%r9, %%rbx, 4)");
- break;
- case OP_SEX16:
- MAYBE_EMIT_CONST();
- emit("movw (%%r9, %%rbx, 4), %%ax");
- emit("cwde");
- emit("movl %%eax, (%%r9, %%rbx, 4)");
- break;
- case OP_NEGI:
- MAYBE_EMIT_CONST();
- emit("negl (%%r9, %%rbx, 4)");
- break;
- case OP_ADD:
- SIMPLE("addl");
- break;
- case OP_SUB:
- SIMPLE("subl");
- break;
- case OP_DIVI:
- MAYBE_EMIT_CONST();
- STACK_POP(4);
- emit("movl (%%r9, %%rbx, 4), %%eax");
- emit("cdq");
- emit("idivl 4(%%r9, %%rbx, 4)");
- emit("movl %%eax, (%%r9, %%rbx, 4)");
- break;
- case OP_DIVU:
- MAYBE_EMIT_CONST();
- STACK_POP(4);
- emit("movl (%%r9, %%rbx, 4), %%eax");
- emit("xorq %%rdx, %%rdx");
- emit("divl 4(%%r9, %%rbx, 4)");
- emit("movl %%eax, (%%r9, %%rbx, 4)");
- break;
- case OP_MODI:
- MAYBE_EMIT_CONST();
- STACK_POP(4);
- emit("movl (%%r9, %%rbx, 4), %%eax");
- emit("xorl %%edx, %%edx");
- emit("cdq");
- emit("idivl 4(%%r9, %%rbx, 4)");
- emit("movl %%edx, (%%r9, %%rbx, 4)");
- break;
- case OP_MODU:
- MAYBE_EMIT_CONST();
- STACK_POP(4);
- emit("movl (%%r9, %%rbx, 4), %%eax");
- emit("xorl %%edx, %%edx");
- emit("divl 4(%%r9, %%rbx, 4)");
- emit("movl %%edx, (%%r9, %%rbx, 4)");
- break;
- case OP_MULI:
- MAYBE_EMIT_CONST();
- STACK_POP(4);
- emit("movl (%%r9, %%rbx, 4), %%eax");
- emit("imull 4(%%r9, %%rbx, 4)");
- emit("movl %%eax, (%%r9, %%rbx, 4)");
- break;
- case OP_MULU:
- MAYBE_EMIT_CONST();
- STACK_POP(4);
- emit("movl (%%r9, %%rbx, 4), %%eax");
- emit("mull 4(%%r9, %%rbx, 4)");
- emit("movl %%eax, (%%r9, %%rbx, 4)");
- break;
- case OP_BAND:
- SIMPLE("andl");
- break;
- case OP_BOR:
- SIMPLE("orl");
- break;
- case OP_BXOR:
- SIMPLE("xorl");
- break;
- case OP_BCOM:
- MAYBE_EMIT_CONST();
- emit("notl (%%r9, %%rbx, 4)");
- break;
- case OP_LSH:
- SHIFT("shl");
- break;
- case OP_RSHI:
- SHIFT("sarl");
- break;
- case OP_RSHU:
- SHIFT("shrl");
- break;
- case OP_NEGF:
- MAYBE_EMIT_CONST();
-#ifdef USE_X87
- emit("flds (%%r9, %%rbx, 4)");
- emit("fchs");
- emit("fstps (%%r9, %%rbx, 4)");
-#else
- emit("movl $0x80000000, %%eax");
- emit("xorl %%eax, (%%r9, %%rbx, 4)");
-#endif
- break;
- case OP_ADDF:
- FSIMPLE("fadds");
- XSIMPLE("addss");
- break;
- case OP_SUBF:
- FSIMPLE("fsubs");
- XSIMPLE("subss");
- break;
- case OP_DIVF:
- FSIMPLE("fdivs");
- XSIMPLE("divss");
- break;
- case OP_MULF:
- FSIMPLE("fmuls");
- XSIMPLE("mulss");
- break;
- case OP_CVIF:
- MAYBE_EMIT_CONST();
-#ifdef USE_X87
- emit("filds (%%r9, %%rbx, 4)");
- emit("fstps (%%r9, %%rbx, 4)");
-#else
- emit("movl (%%r9, %%rbx, 4), %%eax");
- emit("cvtsi2ss %%eax, %%xmm0");
- emit("movss %%xmm0, (%%r9, %%rbx, 4)");
-#endif
- break;
- case OP_CVFI:
- MAYBE_EMIT_CONST();
-#ifdef USE_X87
- emit("flds (%%r9, %%rbx, 4)");
- emit("fnstcw 4(%%r9, %%rbx, 4)");
- emit("movw $0x0F7F, 8(%%r9, %%rbx, 4)"); // round toward zero
- emit("fldcw 8(%%r9, %%rbx, 4)");
- emit("fistpl (%%r9, %%rbx, 4)");
- emit("fldcw 4(%%r9, %%rbx, 4)");
-#else
- emit("movss (%%r9, %%rbx, 4), %%xmm0");
- emit("cvttss2si %%xmm0, %%eax");
- emit("movl %%eax, (%%r9, %%rbx, 4)");
-#endif
- break;
- default:
- NOTIMPL(op);
- break;
- }
-
-
- }
-
- if(got_const)
- {
- VM_FREEBUFFERS(vm);
- Com_Error(ERR_DROP, "leftover const");
- }
-
- emit("movq $%"PRIu64", %%rax", (intptr_t) eop);
- emit("callq *%%rax");
-
- } // pass loop
-
- assembler_init(0);
-
- #ifdef VM_X86_64_MMAP
- if(mprotect(vm->codeBase, compiledOfs, PROT_READ|PROT_EXEC))
- Com_Error(ERR_FATAL, "VM_CompileX86_64: mprotect failed");
- #elif __WIN64__
- {
- DWORD oldProtect = 0;
-
- // remove write permissions; give exec permision
- if(!VirtualProtect(vm->codeBase, compiledOfs, PAGE_EXECUTE_READ, &oldProtect))
- Com_Error(ERR_FATAL, "VM_CompileX86_64: VirtualProtect failed");
- }
- #endif
-
- vm->destroy = VM_Destroy_Compiled;
-
-#ifdef DEBUG_VM
- fflush(qdasmout);
- fclose(qdasmout);
-
-#if 0
- strcpy(fn_d,vm->name);
- strcat(fn_d, ".bin");
- qdasmout = fopen(fn_d, "w");
- fwrite(vm->codeBase, compiledOfs, 1, qdasmout);
- fflush(qdasmout);
- fclose(qdasmout);
-#endif
-#endif
-
- #ifndef __WIN64__ //timersub and gettimeofday
- if(vm->compiled)
- {
- struct timeval tvdone = {0, 0};
- struct timeval dur = {0, 0};
- Com_Printf( "VM file %s compiled to %i bytes of code (%p - %p)\n", vm->name, vm->codeLength, vm->codeBase, vm->codeBase+vm->codeLength );
-
- gettimeofday(&tvdone, NULL);
- timersub(&tvdone, &tvstart, &dur);
- Com_Printf( "compilation took %"PRIu64".%06"PRIu64" seconds\n", dur.tv_sec, dur.tv_usec );
- }
- #endif
-}
-
-
-void VM_Destroy_Compiled(vm_t* self)
-{
- if(self && self->codeBase)
- {
-#ifdef VM_X86_64_MMAP
- munmap(self->codeBase, self->codeLength);
-#elif __WIN64__
- VirtualFree(self->codeBase, 0, MEM_RELEASE);
-#else
- free(self->codeBase);
-#endif
- }
-}
-
-/*
-==============
-VM_CallCompiled
-
-This function is called directly by the generated code
-==============
-*/
-
-#ifdef DEBUG_VM
-static char* memData;
-#endif
-
-int VM_CallCompiled(vm_t *vm, int *args)
-{
- int stack[OPSTACK_SIZE + 15];
- int programCounter;
- int programStack;
- int stackOnEntry;
- long opStackRet;
- byte *image;
- void *entryPoint;
- int *opStack;
- int arg;
-
- currentVM = vm;
-
-// Com_Printf("entering %s level %d, call %d, arg1 = 0x%x\n", vm->name, vm->callLevel, args[0], args[1]);
-
- // interpret the code
- vm->currentlyInterpreting = qtrue;
-
-// callMask = vm->dataMask;
-
- // we might be called recursively, so this might not be the very top
- programStack = vm->programStack;
- stackOnEntry = programStack;
-
- // set up the stack frame
- image = vm->dataBase;
-#ifdef DEBUG_VM
- memData = (char*)image;
-#endif
-
- programCounter = 0;
-
- programStack -= ( 8 + 4 * MAX_VMMAIN_ARGS );
-
- for ( arg = 0; arg < MAX_VMMAIN_ARGS; arg++ )
- *(int *)&image[ programStack + 8 + arg * 4 ] = args[ arg ];
-
- *(int *)&image[ programStack + 4 ] = 0x77777777; // return stack
- *(int *)&image[ programStack ] = -1; // will terminate the loop on return
-
- // off we go into generated code...
- entryPoint = getentrypoint(vm);
- opStack = PADP(stack, 16);
-
- *opStack = 0xDEADBEEF;
- opStackRet = 0;
-
- __asm__ __volatile__ (
- " movq %4,%%r8 \r\n" \
- " movq %3,%%r9 \r\n" \
- " movq %2,%%r10 \r\n" \
- " push %%r15 \r\n" \
- " push %%r14 \r\n" \
- " push %%r13 \r\n" \
- " push %%r12 \r\n" \
- " subq $24, %%rsp # fix alignment as call pushes one value \r\n" \
- " callq *%%r10 \r\n" \
- " addq $24, %%rsp \r\n" \
- " pop %%r12 \r\n" \
- " pop %%r13 \r\n" \
- " pop %%r14 \r\n" \
- " pop %%r15 \r\n"
- : "+D" (programStack), "+b" (opStackRet)
- : "g" (entryPoint), "g" (opStack), "g" (vm->dataBase), "g" (programStack)
- : "%rsi", "%rax", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%xmm0"
- );
-
- if(opStackRet != 1 || *opStack != 0xDEADBEEF)
- Com_Error(ERR_DROP, "opStack corrupted in compiled code (offset %ld)", opStackRet);
-
- if ( programStack != stackOnEntry - ( 8 + 4 * MAX_VMMAIN_ARGS ) ) {
- Com_Error( ERR_DROP, "programStack corrupted in compiled code" );
- }
-
-// Com_Printf("exiting %s level %d\n", vm->name, vm->callLevel);
- vm->programStack = stackOnEntry;
-
- return stack[1];
-}
diff --git a/src/qcommon/vm_x86_64_assembler.c b/src/qcommon/vm_x86_64_assembler.c
deleted file mode 100644
index 2a2808a8..00000000
--- a/src/qcommon/vm_x86_64_assembler.c
+++ /dev/null
@@ -1,1463 +0,0 @@
-/*
-===========================================================================
-vm_x86_64_assembler.c -- assembler for x86-64
-
-Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2013 Darklegion Development
-Copyright (C) 2007 Ludwig Nussel <ludwig.nussel@suse.de>, Novell inc.
-
-Tremulous is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Tremulous is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Tremulous; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-#define _ISOC99_SOURCE
-
-#include "vm_local.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-
-#include <inttypes.h>
-
-// Ignore __attribute__ on non-gcc platforms
-#ifndef __GNUC__
-#ifndef __attribute__
-#define __attribute__(x)
-#endif
-#endif
-
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-
-static char* out;
-static unsigned compiledOfs;
-static unsigned assembler_pass;
-
-static const char* cur_line;
-
-static FILE* fout;
-
-#define crap(fmt, args...) do { \
- _crap(__FUNCTION__, fmt, ##args); \
-} while(0)
-
-#define CRAP_INVALID_ARGS crap("invalid arguments %s, %s", argtype2str(arg1.type),argtype2str(arg2.type));
-
-#ifdef DEBUG
-#define debug(fmt, args...) printf(fmt, ##args)
-#else
-#define debug(fmt, args...)
-#endif
-
-static __attribute__ ((noreturn)) __attribute__ ((format (printf, 2, 3))) void _crap(const char* func, const char* fmt, ...)
-{
- va_list ap;
- fprintf(stderr, "%s() - ", func);
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fputc('\n', stderr);
- if(cur_line && cur_line[0])
- fprintf(stderr, "-> %s\n", cur_line);
- exit(1);
-}
-
-static void emit1(unsigned char v)
-{
- int writecnt;
-
- if(assembler_pass)
- {
- out[compiledOfs++] = v;
-
- if(fout)
- writecnt = fwrite(&v, 1, 1, fout);
-
- debug("%02hx ", v);
- }
- else
- {
- ++compiledOfs;
- }
-}
-
-static inline void emit2(u16 v)
-{
- emit1(v&0xFF);
- emit1((v>>8)&0xFF);
-}
-
-static inline void emit4(u32 v)
-{
- emit1(v&0xFF);
- emit1((v>>8)&0xFF);
- emit1((v>>16)&0xFF);
- emit1((v>>24)&0xFF);
-}
-
-static inline void emit8(u64 v)
-{
- emit4(v&0xFFFFFFFF);
- emit4((v>>32)&0xFFFFFFFF);
-}
-
-enum {
- REX_W = 0x08,
- REX_R = 0x04,
- REX_X = 0x02,
- REX_B = 0x01,
-};
-
-enum {
- MODRM_MOD_00 = 0x00,
- MODRM_MOD_01 = 0x01 << 6,
- MODRM_MOD_10 = 0x02 << 6,
- MODRM_MOD_11 = 0x03 << 6,
- MODRM_RM_SIB = 0x04,
-};
-
-typedef enum
-{
- T_NONE = 0x00,
- T_REGISTER = 0x01,
- T_IMMEDIATE = 0x02,
- T_MEMORY = 0x04,
- T_LABEL = 0x08,
- T_ABSOLUTE = 0x80
-} argtype_t;
-
-typedef enum {
- R_8 = 0x100,
- R_16 = 0x200,
- R_64 = 0x800,
- R_MSZ = 0xF00, // size mask
- R_XMM = 0x2000, // xmm register. year, sucks
- R_EAX = 0x00,
- R_EBX = 0x03,
- R_ECX = 0x01,
- R_EDX = 0x02,
- R_ESI = 0x06,
- R_EDI = 0x07,
- R_ESP = 0x04,
- R_RAX = R_EAX | R_64,
- R_RBX = R_EBX | R_64,
- R_RCX = R_ECX | R_64,
- R_RDX = R_EDX | R_64,
- R_RSI = R_ESI | R_64,
- R_RDI = R_EDI | R_64,
- R_RSP = R_ESP | R_64,
- R_R8 = 0x08 | R_64,
- R_R9 = 0x09 | R_64,
- R_R10 = 0x0A | R_64,
- R_R15 = 0x0F | R_64,
- R_AL = R_EAX | R_8,
- R_AX = R_EAX | R_16,
- R_BL = R_EBX | R_8,
- R_CL = R_ECX | R_8,
- R_XMM0 = 0x00 | R_XMM,
- R_MGP = 0x0F, // mask for general purpose registers
-} reg_t;
-
-typedef enum {
- MODRM_SIB = 0,
- MODRM_NOSIB = 0x3,
-} modrm_sib_t;
-
-typedef struct {
- unsigned disp;
- argtype_t basetype;
- union {
- u64 imm;
- reg_t reg;
- } base;
- argtype_t indextype;
- union {
- u64 imm;
- reg_t reg;
- } index;
- unsigned scale;
-} memref_t;
-
-#define LABELLEN 32
-
-typedef struct {
- argtype_t type;
- union {
- u64 imm;
- reg_t reg;
- memref_t mem;
- char label[LABELLEN];
- } v;
- int absolute:1;
-} arg_t;
-
-typedef void (*emitfunc)(const char* op, arg_t arg1, arg_t arg2, void* data);
-
-typedef struct {
- char* mnemonic;
- emitfunc func;
- void* data;
-} op_t;
-
-typedef struct {
- u8 xmmprefix;
- u8 subcode; // in modrm
- u8 rmcode; // opcode for reg/mem, reg
- u8 mrcode; // opcode for reg, reg/mem
- u8 rcode8; // opcode for reg8/mem8
- u8 rcode; // opcode for reg/mem
-} opparam_t;
-
-static opparam_t params_add = { subcode: 0, rmcode: 0x01, };
-static opparam_t params_or = { subcode: 1, rmcode: 0x09, };
-static opparam_t params_and = { subcode: 4, rmcode: 0x21, };
-static opparam_t params_sub = { subcode: 5, rmcode: 0x29, };
-static opparam_t params_xor = { subcode: 6, rmcode: 0x31, };
-static opparam_t params_cmp = { subcode: 7, rmcode: 0x39, mrcode: 0x3b, };
-static opparam_t params_dec = { subcode: 1, rcode: 0xff, rcode8: 0xfe, };
-static opparam_t params_sar = { subcode: 7, rcode: 0xd3, rcode8: 0xd2, };
-static opparam_t params_shl = { subcode: 4, rcode: 0xd3, rcode8: 0xd2, };
-static opparam_t params_shr = { subcode: 5, rcode: 0xd3, rcode8: 0xd2, };
-static opparam_t params_idiv = { subcode: 7, rcode: 0xf7, rcode8: 0xf6, };
-static opparam_t params_div = { subcode: 6, rcode: 0xf7, rcode8: 0xf6, };
-static opparam_t params_imul = { subcode: 5, rcode: 0xf7, rcode8: 0xf6, };
-static opparam_t params_mul = { subcode: 4, rcode: 0xf7, rcode8: 0xf6, };
-static opparam_t params_neg = { subcode: 3, rcode: 0xf7, rcode8: 0xf6, };
-static opparam_t params_not = { subcode: 2, rcode: 0xf7, rcode8: 0xf6, };
-
-static opparam_t params_cvtsi2ss = { xmmprefix: 0xf3, rmcode: 0x2a };
-static opparam_t params_cvttss2si = { xmmprefix: 0xf3, rmcode: 0x2c };
-static opparam_t params_addss = { xmmprefix: 0xf3, mrcode: 0x58 };
-static opparam_t params_divss = { xmmprefix: 0xf3, mrcode: 0x5e };
-static opparam_t params_movss = { xmmprefix: 0xf3, mrcode: 0x10, rmcode: 0x11 };
-static opparam_t params_mulss = { xmmprefix: 0xf3, mrcode: 0x59 };
-static opparam_t params_subss = { xmmprefix: 0xf3, mrcode: 0x5c };
-static opparam_t params_ucomiss = { mrcode: 0x2e };
-
-/* ************************* */
-
-static unsigned hashkey(const char *string, unsigned len) {
- unsigned register hash, i;
-
- hash = 0;
- for (i = 0; i < len && string[i] != '\0'; ++i) {
- hash += string[i] * (119 + i);
- }
- hash = (hash ^ (hash >> 10) ^ (hash >> 20));
- return hash;
-}
-
-struct hashentry {
- char* label;
- unsigned address;
- struct hashentry* next;
-};
-static struct hashentry* labelhash[1021];
-
-// no dup check!
-static void hash_add_label(const char* label, unsigned address)
-{
- struct hashentry* h;
- unsigned i = hashkey(label, -1U);
- int labellen;
-
- i %= ARRAY_LEN(labelhash);
- h = Z_Malloc(sizeof(struct hashentry));
-
- labellen = strlen(label) + 1;
- h->label = Z_Malloc(labellen);
- memcpy(h->label, label, labellen);
-
- h->address = address;
- h->next = labelhash[i];
- labelhash[i] = h;
-}
-
-static unsigned lookup_label(const char* label)
-{
- struct hashentry* h;
- unsigned i = hashkey(label, -1U);
- i %= ARRAY_LEN(labelhash);
- for(h = labelhash[i]; h; h = h->next )
- {
- if(!strcmp(h->label, label))
- return h->address;
- }
- if(assembler_pass)
- crap("label %s undefined", label);
- return 0;
-}
-
-static void labelhash_free(void)
-{
- struct hashentry* h;
- unsigned i;
- unsigned z = 0, min = -1U, max = 0, t = 0;
- for ( i = 0; i < ARRAY_LEN(labelhash); ++i)
- {
- unsigned n = 0;
- h = labelhash[i];
- while(h)
- {
- struct hashentry* next = h->next;
- Z_Free(h->label);
- Z_Free(h);
- h = next;
- ++n;
- }
- t+=n;
- if(!n) ++z;
- //else printf("%u\n", n);
- min = MIN(min, n);
- max = MAX(max, n);
- }
- printf("total %u, hsize %"PRIu64", zero %u, min %u, max %u\n", t, ARRAY_LEN(labelhash), z, min, max);
- memset(labelhash, 0, sizeof(labelhash));
-}
-
-/* ************************* */
-
-
-static const char* argtype2str(argtype_t t)
-{
- switch(t)
- {
- case T_NONE: return "none";
- case T_REGISTER: return "register";
- case T_IMMEDIATE: return "immediate";
- case T_MEMORY: return "memory";
- case T_LABEL: return "label";
- default: crap("invalid type");
- }
- /* not reached */
- return T_NONE;
-}
-
-/* ************************* */
-
-static inline int iss8(int64_t v)
-{
- return (SCHAR_MIN <= v && v <= SCHAR_MAX);
-}
-
-static inline int isu8(u64 v)
-{
- return (v <= UCHAR_MAX);
-}
-
-static inline int iss16(int64_t v)
-{
- return (SHRT_MIN <= v && v <= SHRT_MAX);
-}
-
-static inline int isu16(u64 v)
-{
- return (v <= USHRT_MAX);
-}
-
-static inline int iss32(int64_t v)
-{
- return (INT_MIN <= v && v <= INT_MAX);
-}
-
-static inline int isu32(u64 v)
-{
- return (v <= UINT_MAX);
-}
-
-static void emit_opsingle(const char* mnemonic, arg_t arg1, arg_t arg2, void* data)
-{
- u8 op = (u8)((uint64_t) data);
-
- if(arg1.type != T_NONE || arg2.type != T_NONE)
- CRAP_INVALID_ARGS;
-
- emit1(op);
-}
-
-static void emit_opsingle16(const char* mnemonic, arg_t arg1, arg_t arg2, void* data)
-{
- emit1(0x66);
- emit_opsingle(mnemonic, arg1, arg2, data);
-}
-
-static void compute_rexmodrmsib(u8* rex_r, u8* modrm_r, u8* sib_r, arg_t* arg1, arg_t* arg2)
-{
- u8 rex = 0;
- u8 modrm = 0;
- u8 sib = 0;
-
- if((arg1->type == T_REGISTER && arg2->type == T_REGISTER)
- && ((arg1->v.reg & R_MSZ) != (arg2->v.reg & R_MSZ))
- && !((arg1->v.reg & R_XMM) || (arg2->v.reg & R_XMM)))
- crap("both registers must be of same width");
-
- if((arg1->type == T_REGISTER && arg1->v.reg & R_64)
- || (arg2->type == T_REGISTER && arg2->v.reg & R_64))
- {
- rex |= REX_W;
- }
-
- if(arg1->type == T_REGISTER)
- {
- if((arg1->v.reg & R_MGP) > 0x07)
- rex |= REX_R;
-
- modrm |= (arg1->v.reg & 0x07) << 3;
- }
-
- if(arg2->type == T_REGISTER)
- {
- if((arg2->v.reg & R_MGP) > 0x07)
- rex |= REX_B;
-
- modrm |= (arg2->v.reg & 0x07);
- }
-
- if(arg2->type == T_MEMORY)
- {
- if((arg2->v.mem.basetype == T_REGISTER && !(arg2->v.mem.base.reg & R_64))
- || (arg2->v.mem.indextype == T_REGISTER && !(arg2->v.mem.index.reg & R_64)))
- {
- crap("only 64bit base/index registers are %x %x", arg2->v.mem.base.reg, arg2->v.mem.index.reg);
- }
-
- if(arg2->v.mem.indextype == T_REGISTER)
- {
- modrm |= MODRM_RM_SIB;
- if(!arg2->v.mem.disp)
- {
- modrm |= MODRM_MOD_00;
- }
- else if(iss8(arg2->v.mem.disp))
- {
- modrm |= MODRM_MOD_01;
- }
- else if(isu32(arg2->v.mem.disp))
- {
- modrm |= MODRM_MOD_10;
- }
- else
- {
- crap("invalid displacement");
- }
-
- if((arg2->v.mem.index.reg & R_MGP) > 0x07)
- rex |= REX_X;
-
- if((arg2->v.mem.base.reg & R_MGP) > 0x07)
- rex |= REX_B;
-
- if(arg2->v.mem.basetype != T_REGISTER)
- crap("base must be register");
- switch(arg2->v.mem.scale)
- {
- case 1: break;
- case 2: sib |= 1 << 6; break;
- case 4: sib |= 2 << 6; break;
- case 8: sib |= 3 << 6; break;
- }
- sib |= (arg2->v.mem.index.reg & 0x07) << 3;
- sib |= (arg2->v.mem.base.reg & 0x07);
- }
- else if(arg2->v.mem.indextype == T_NONE)
- {
- if(!arg2->v.mem.disp)
- {
- modrm |= MODRM_MOD_00;
- }
- else if(iss8(arg2->v.mem.disp))
- {
- modrm |= MODRM_MOD_01;
- }
- else if(isu32(arg2->v.mem.disp))
- {
- modrm |= MODRM_MOD_10;
- }
- else
- {
- crap("invalid displacement");
- }
-
- if(arg2->v.mem.basetype != T_REGISTER)
- crap("todo: base != register");
-
- if((arg2->v.mem.base.reg & R_MGP) > 0x07)
- rex |= REX_B;
-
- modrm |= arg2->v.mem.base.reg & 0x07;
- }
- else
- {
- crap("invalid indextype");
- }
- }
- else
- {
- modrm |= MODRM_MOD_11;
- }
-
- if(rex)
- rex |= 0x40; // XXX
-
- *rex_r = rex;
- *modrm_r = modrm;
- *sib_r = sib;
-}
-
-static void maybe_emit_displacement(arg_t* arg)
-{
- if(arg->type != T_MEMORY)
- return;
-
- if(arg->v.mem.disp)
- {
- if(iss8(arg->v.mem.disp))
- {
- emit1((u8)arg->v.mem.disp);
- }
- else if(isu32(arg->v.mem.disp))
- {
- emit4(arg->v.mem.disp);
- }
- else
- {
- crap("invalid displacement");
- }
- }
-}
-
-/* one byte operator with register added to operator */
-static void emit_opreg(const char* mnemonic, arg_t arg1, arg_t arg2, void* data)
-{
- u8 op = (u8)((uint64_t) data);
-
- if(arg1.type != T_REGISTER || arg2.type != T_NONE)
- CRAP_INVALID_ARGS;
-
- if((arg1.v.reg & R_MGP) > 0x07)
- emit1(0x40 | REX_B);
-
- op |= (arg1.v.reg & 0x07);
-
- emit1(op);
-}
-
-/* operator which operates on reg/mem */
-static void emit_op_rm(const char* mnemonic, arg_t arg1, arg_t arg2, void* data)
-{
- u8 rex, modrm, sib;
- opparam_t* params = data;
-
- if((arg1.type != T_REGISTER && arg1.type != T_MEMORY) || arg2.type != T_NONE)
- CRAP_INVALID_ARGS;
-
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg2, &arg1);
-
- modrm |= params->subcode << 3;
-
- if(arg1.v.reg & R_16)
- emit1(0x66);
-
- if(rex) emit1(rex);
- if(arg1.v.reg & R_8)
- emit1(params->rcode8); // op reg8/mem8,
- else
- emit1(params->rcode); // op reg/mem,
- emit1(modrm);
- if((modrm & 0x07) == MODRM_RM_SIB)
- emit1(sib);
-
- maybe_emit_displacement(&arg1);
-}
-
-/* operator which operates on reg/mem with cl */
-static void emit_op_rm_cl(const char* mnemonic, arg_t arg1, arg_t arg2, void* data)
-{
- u8 rex, modrm, sib;
- opparam_t* params = data;
-
- if(arg2.type != T_REGISTER || arg1.type != T_REGISTER)
- CRAP_INVALID_ARGS;
-
- if((arg1.v.reg & R_MGP) != R_ECX && !(arg1.v.reg & R_8))
- crap("only cl register is valid");
-
- arg1.type = T_NONE; // don't complain, we know it's cl anyways
-
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg1, &arg2);
-
- modrm |= params->subcode << 3;
-
- if(arg2.v.reg & R_16)
- emit1(0x66);
-
- if(rex) emit1(rex);
- if(arg2.v.reg & R_8)
- emit1(params->rcode8); // op reg8/mem8,
- else
- emit1(params->rcode); // op reg/mem,
- emit1(modrm);
- if((modrm & 0x07) == MODRM_RM_SIB)
- emit1(sib);
-
- maybe_emit_displacement(&arg2);
-}
-
-static void emit_mov(const char* mnemonic, arg_t arg1, arg_t arg2, void* data)
-{
- u8 rex = 0;
- u8 modrm = 0;
- u8 sib = 0;
-
- if(arg1.type == T_IMMEDIATE && arg2.type == T_REGISTER)
- {
- u8 op = 0xb8;
-
- if(arg2.v.reg & R_8)
- {
- if(!iss8(arg1.v.imm))
- crap("value too large for 8bit register");
-
- op = 0xb0;
- }
- else if(arg2.v.reg & R_16)
- {
- if(!isu16(arg1.v.imm))
- crap("value too large for 16bit register");
- emit1(0x66);
- }
- else if(!(arg2.v.reg & R_64))
- {
- if(!isu32(arg1.v.imm))
- crap("value too large for 32bit register");
- }
-
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg1, &arg2);
-
- if(rex) emit1(rex);
-
- op |= (arg2.v.reg & 0x07);
-
- emit1(op);
-
- if(arg2.v.reg & R_8) emit1(arg1.v.imm);
- else if(arg2.v.reg & R_16) emit2(arg1.v.imm);
- else if(arg2.v.reg & R_64) emit8(arg1.v.imm);
- else emit4(arg1.v.imm);
- }
- else if(arg1.type == T_IMMEDIATE && arg2.type == T_MEMORY)
- {
- if(!iss32(arg1.v.imm))
- {
- crap("only 32bit immediates supported");
- }
-
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg1, &arg2);
- if(rex) emit1(rex);
- emit1(0xc7); // mov reg/mem, imm
- emit1(modrm);
- if((modrm & 0x07) == MODRM_RM_SIB)
- emit1(sib);
-
- emit4(arg1.v.imm);
- }
- else if(arg1.type == T_REGISTER && arg2.type == T_REGISTER) // XXX: same as next
- {
- if(arg1.type != T_REGISTER || arg2.type != T_REGISTER)
- crap("both args must be registers");
-
- if((arg1.v.reg & R_MSZ) != (arg2.v.reg & R_MSZ))
- crap("both registers must be same width");
-
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg1, &arg2);
-
- if(rex) emit1(rex);
- emit1(0x89); // mov reg reg/mem,
- emit1(modrm);
- }
- else if(arg1.type == T_REGISTER && arg2.type == T_MEMORY)
- {
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg1, &arg2);
-
- if(arg1.v.reg & R_16)
- emit1(0x66);
-
- if(rex) emit1(rex);
- if(arg1.v.reg & R_8)
- emit1(0x88); // mov reg reg/mem,
- else
- emit1(0x89); // mov reg reg/mem,
- emit1(modrm);
- if((modrm & 0x07) == MODRM_RM_SIB)
- emit1(sib);
-
- maybe_emit_displacement(&arg2);
- }
- else if(arg1.type == T_MEMORY && arg2.type == T_REGISTER)
- {
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg2, &arg1);
-
- if(arg2.v.reg & R_16)
- emit1(0x66);
-
- if(rex) emit1(rex);
- if(arg2.v.reg & R_8)
- emit1(0x8a); // mov reg/mem, reg
- else
- emit1(0x8b); // mov reg/mem, reg
- emit1(modrm);
- if((modrm & 0x07) == MODRM_RM_SIB)
- emit1(sib);
-
- maybe_emit_displacement(&arg1);
- }
- else
- CRAP_INVALID_ARGS;
-}
-
-static void emit_subaddand(const char* mnemonic, arg_t arg1, arg_t arg2, void* data)
-{
- u8 rex = 0;
- u8 modrm = 0;
- u8 sib = 0;
-
- opparam_t* params = data;
-
- if(arg1.type == T_IMMEDIATE && arg2.type == T_REGISTER)
- {
- if(!iss32(arg1.v.imm))
- {
- crap("only 8 and 32 bit immediates supported");
- }
-
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg1, &arg2);
- modrm |= params->subcode << 3;
-
- if(rex) emit1(rex);
-
- if(arg2.v.reg & R_8)
- {
- emit1(0x80); // sub reg8/mem8, imm8
- emit1(modrm);
- emit1(arg1.v.imm & 0xFF);
- }
- else if(iss8(arg1.v.imm))
- {
- emit1(0x83); // sub reg/mem, imm8
- emit1(modrm);
- emit1(arg1.v.imm & 0xFF);
- }
- else
- {
- emit1(0x81); // sub reg/mem, imm32
- emit1(modrm);
- emit4(arg1.v.imm);
- }
- }
- else if(arg1.type == T_REGISTER && (arg2.type == T_MEMORY || arg2.type == T_REGISTER))
- {
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg1, &arg2);
-
- if(rex) emit1(rex);
- emit1(params->rmcode); // sub reg/mem, reg
- emit1(modrm);
- if(arg2.type == T_MEMORY && (modrm & 0x07) == MODRM_RM_SIB)
- emit1(sib);
-
- maybe_emit_displacement(&arg2);
- }
- else if(arg1.type == T_MEMORY && arg2.type == T_REGISTER && params->mrcode)
- {
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg2, &arg1);
-
- if(rex) emit1(rex);
- emit1(params->mrcode); // sub reg, reg/mem
- emit1(modrm);
- if((modrm & 0x07) == MODRM_RM_SIB)
- emit1(sib);
-
- maybe_emit_displacement(&arg1);
- }
- else
- CRAP_INVALID_ARGS;
-}
-
-static void emit_condjump(const char* mnemonic, arg_t arg1, arg_t arg2, void* data)
-{
- unsigned off;
- int disp;
- unsigned char opcode = (unsigned char)(((uint64_t)data)&0xFF);
-
- if(arg1.type != T_LABEL || arg2.type != T_NONE)
- crap("%s: argument must be label", mnemonic);
-
- emit1(opcode);
-
- off = lookup_label(arg1.v.label);
- disp = off-(compiledOfs+1);
- if(assembler_pass && abs(disp) > 127)
- crap("cannot jump that far (%x -> %x = %x)", compiledOfs, off, disp);
-
- emit1(disp);
-}
-
-static void emit_jmp(const char* mnemonic, arg_t arg1, arg_t arg2, void* data)
-{
- if((arg1.type != T_LABEL && arg1.type != T_REGISTER && arg1.type != T_MEMORY) || arg2.type != T_NONE)
- CRAP_INVALID_ARGS;
-
- if(arg1.type == T_LABEL)
- {
- unsigned off;
- int disp;
-
- off = lookup_label(arg1.v.label);
- disp = off-(compiledOfs+5);
- emit1(0xe9);
- emit4(disp);
- }
- else
- {
- u8 rex, modrm, sib;
-
- if(arg1.type == T_REGISTER)
- {
- if(!arg1.absolute)
- crap("jmp must be absolute");
-
- if((arg1.v.reg & R_64) != R_64)
- crap("register must be 64bit");
-
- arg1.v.reg ^= R_64; // no rex required for call
- }
-
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg2, &arg1);
-
- modrm |= 0x4 << 3;
-
- if(rex) emit1(rex);
- emit1(0xff);
- emit1(modrm);
- if((modrm & 0x07) == MODRM_RM_SIB)
- emit1(sib);
- maybe_emit_displacement(&arg1);
- }
-}
-
-static void emit_call(const char* mnemonic, arg_t arg1, arg_t arg2, void* data)
-{
- u8 rex, modrm, sib;
-
- if((arg1.type != T_REGISTER && arg1.type != T_IMMEDIATE) || arg2.type != T_NONE)
- CRAP_INVALID_ARGS;
-
- if(arg1.type == T_REGISTER)
- {
- if(!arg1.absolute)
- crap("call must be absolute");
-
- if((arg1.v.reg & R_64) != R_64)
- crap("register must be 64bit");
-
- arg1.v.reg ^= R_64; // no rex required for call
-
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg2, &arg1);
-
- modrm |= 0x2 << 3;
-
- if(rex) emit1(rex);
- emit1(0xff);
- emit1(modrm);
- }
- else
- {
- if(!isu32(arg1.v.imm))
- crap("must be 32bit argument");
- emit1(0xe8);
- emit4(arg1.v.imm);
- }
-}
-
-
-static void emit_twobyte(const char* mnemonic, arg_t arg1, arg_t arg2, void* data)
-{
- u8 rex, modrm, sib;
-
- opparam_t* params = data;
-
- if(arg1.type == T_REGISTER && (arg2.type == T_MEMORY || arg2.type == T_REGISTER))
- {
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg1, &arg2);
-
- if(params->xmmprefix) emit1(params->xmmprefix);
- if(rex) emit1(rex);
- emit1(0x0f);
- emit1(params->rmcode); // sub reg/mem, reg
- emit1(modrm);
- if((modrm & 0x07) == MODRM_RM_SIB)
- emit1(sib);
-
- maybe_emit_displacement(&arg2);
- }
- else if(arg1.type == T_MEMORY && arg2.type == T_REGISTER && params->mrcode)
- {
- compute_rexmodrmsib(&rex, &modrm, &sib, &arg2, &arg1);
-
- if(params->xmmprefix) emit1(params->xmmprefix);
- if(rex) emit1(rex);
- emit1(0x0f);
- emit1(params->mrcode); // sub reg, reg/mem
- emit1(modrm);
- if((modrm & 0x07) == MODRM_RM_SIB)
- emit1(sib);
-
- maybe_emit_displacement(&arg1);
- }
- else
- CRAP_INVALID_ARGS;
-}
-
-static int ops_sorted = 0;
-static op_t ops[] = {
- { "addb", emit_subaddand, &params_add },
- { "addl", emit_subaddand, &params_add },
- { "addq", emit_subaddand, &params_add },
- { "addss", emit_twobyte, &params_addss },
- { "andb", emit_subaddand, &params_and },
- { "andl", emit_subaddand, &params_and },
- { "andq", emit_subaddand, &params_and },
- { "callq", emit_call, NULL },
- { "cbw", emit_opsingle16, (void*)0x98 },
- { "cdq", emit_opsingle, (void*)0x99 },
- { "cmpb", emit_subaddand, &params_cmp },
- { "cmpl", emit_subaddand, &params_cmp },
- { "cmpq", emit_subaddand, &params_cmp },
- { "cvtsi2ss", emit_twobyte, &params_cvtsi2ss },
- { "cvttss2si", emit_twobyte, &params_cvttss2si },
- { "cwde", emit_opsingle, (void*)0x98 },
- { "decl", emit_op_rm, &params_dec },
- { "decq", emit_op_rm, &params_dec },
- { "divl", emit_op_rm, &params_div },
- { "divq", emit_op_rm, &params_div },
- { "divss", emit_twobyte, &params_divss },
- { "idivl", emit_op_rm, &params_idiv },
- { "imull", emit_op_rm, &params_imul },
- { "int3", emit_opsingle, (void*)0xcc },
- { "ja", emit_condjump, (void*)0x77 },
- { "jbe", emit_condjump, (void*)0x76 },
- { "jb", emit_condjump, (void*)0x72 },
- { "je", emit_condjump, (void*)0x74 },
- { "jl", emit_condjump, (void*)0x7c },
- { "jmp", emit_jmp, NULL },
- { "jmpq", emit_jmp, NULL },
- { "jnae", emit_condjump, (void*)0x72 },
- { "jna", emit_condjump, (void*)0x76 },
- { "jnbe", emit_condjump, (void*)0x77 },
- { "jnb", emit_condjump, (void*)0x73 },
- { "jnc", emit_condjump, (void*)0x73 },
- { "jne", emit_condjump, (void*)0x75 },
- { "jnge", emit_condjump, (void*)0x7c },
- { "jng", emit_condjump, (void*)0x7e },
- { "jnle", emit_condjump, (void*)0x7f },
- { "jnl", emit_condjump, (void*)0x7d },
- { "jnz", emit_condjump, (void*)0x75 },
- { "jp", emit_condjump, (void*)0x7a },
- { "jz", emit_condjump, (void*)0x74 },
- { "movb", emit_mov, NULL },
- { "movl", emit_mov, NULL },
- { "movq", emit_mov, NULL },
- { "movss", emit_twobyte, &params_movss },
- { "movw", emit_mov, NULL },
- { "mull", emit_op_rm, &params_mul },
- { "mulss", emit_twobyte, &params_mulss },
- { "negl", emit_op_rm, &params_neg },
- { "negq", emit_op_rm, &params_neg },
- { "nop", emit_opsingle, (void*)0x90 },
- { "notl", emit_op_rm, &params_not },
- { "notq", emit_op_rm, &params_not },
- { "orb", emit_subaddand, &params_or },
- { "orl", emit_subaddand, &params_or },
- { "orq", emit_subaddand, &params_or },
- { "pop", emit_opreg, (void*)0x58 },
- { "push", emit_opreg, (void*)0x50 },
- { "ret", emit_opsingle, (void*)0xc3 },
- { "sarl", emit_op_rm_cl, &params_sar },
- { "shl", emit_op_rm_cl, &params_shl },
- { "shrl", emit_op_rm_cl, &params_shr },
- { "subb", emit_subaddand, &params_sub },
- { "subl", emit_subaddand, &params_sub },
- { "subq", emit_subaddand, &params_sub },
- { "subss", emit_twobyte, &params_subss },
- { "ucomiss", emit_twobyte, &params_ucomiss },
- { "xorb", emit_subaddand, &params_xor },
- { "xorl", emit_subaddand, &params_xor },
- { "xorq", emit_subaddand, &params_xor },
- { NULL, NULL, NULL }
-};
-
-static int opsort(const void* A, const void* B)
-{
- const op_t* a = A;
- const op_t* b = B;
- return strcmp(a->mnemonic, b->mnemonic);
-}
-
-static op_t* getop(const char* n)
-{
-#if 0
- op_t* o = ops;
- while(o->mnemonic)
- {
- if(!strcmp(o->mnemonic, n))
- return o;
- ++o;
- }
-
-#else
- unsigned int m, t, b;
- int r;
- t = ARRAY_LEN(ops)-1;
-
- r = m = -1;
-
- do
- {
- if(r < 0)
- b = m + 1;
- else
- t = m - 1;
-
- m = ((t - b) >> 1) + b;
-
- if((r = strcmp(ops[m].mnemonic, n)) == 0)
- return &ops[m];
- } while(b <= t && t);
-#endif
-
- return NULL;
-}
-
-static reg_t parsereg(const char* str)
-{
- const char* s = str;
- if(*s == 'a' && s[1] == 'l' && !s[2])
- {
- return R_AL;
- }
- else if(*s == 'a' && s[1] == 'x' && !s[2])
- {
- return R_AX;
- }
- if(*s == 'b' && s[1] == 'l' && !s[2])
- {
- return R_BL;
- }
- if(*s == 'c' && s[1] == 'l' && !s[2])
- {
- return R_CL;
- }
- if(*s == 'x')
- {
- if(!strcmp(s, "xmm0"))
- return R_XMM0;
- }
- else if(*s == 'r' && s[1])
- {
- ++s;
- if(s[1] == 'x')
- {
- switch(*s++)
- {
- case 'a': return R_RAX;
- case 'b': return R_RBX;
- case 'c': return R_RCX;
- case 'd': return R_RDX;
- }
- }
- else if(s[1] == 'i')
- {
- switch(*s++)
- {
- case 's': return R_RSI;
- case 'd': return R_RDI;
- }
- }
- else if(s[0] == 's' && s[1] == 'p' && !s[2])
- {
- return R_RSP;
- }
- else if(*s == '8' && !s[1])
- return R_R8;
- else if(*s == '9' && !s[1])
- return R_R9;
- else if(*s == '1' && s[1] == '0')
- return R_R10;
- else if(*s == '1' && s[1] == '5')
- return R_R15;
- }
- else if(*s == 'e' && s[1])
- {
- ++s;
- if(s[1] == 'x')
- {
- switch(*s++)
- {
- case 'a': return R_EAX;
- case 'b': return R_EBX;
- case 'c': return R_ECX;
- case 'd': return R_EDX;
- }
- }
- else if(s[1] == 'i')
- {
- switch(*s++)
- {
- case 's': return R_ESI;
- case 'd': return R_EDI;
- }
- }
- }
-
- crap("invalid register %s", str);
-
- return 0;
-}
-
-typedef enum {
- TOK_LABEL = 0x80,
- TOK_INT = 0x81,
- TOK_END = 0x82,
- TOK_INVALID = 0x83,
-} token_t;
-
-static unsigned char nexttok(const char** str, char* label, u64* val)
-{
- const char* s = *str;
-
- if(label) *label = 0;
- if(val) *val = 0;
-
- while(*s && *s == ' ') ++s;
-
- if(!*s)
- {
- return TOK_END;
- }
- else if(*s == '$' || *s == '*' || *s == '%' || *s == '-' || *s == ')' || *s == '(' || *s == ',')
- {
- *str = s+1;
- return *s;
- }
- else if(*s >= 'a' && *s <= 'z')
- {
- size_t a = strspn(s+1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
- if(a+1 >= LABELLEN)
- crap("label %s too long", s);
- if(label)
- {
- strncpy(label, s, a+1);
- label[a+1] = 0;
- }
- *str = s+a+1;
- return TOK_LABEL;
- }
- else if(*s >= '0' && *s <= '9')
- {
- char* endptr = NULL;
- u64 v = strtoull(s, &endptr, 0);
- if(endptr && (endptr-s == 0))
- crap("invalid integer %s", s);
- if(val) *val = v;
- *str = endptr;
- return TOK_INT;
- }
- crap("can't parse '%s'", *str);
- return TOK_INVALID;
-}
-
-static arg_t parsearg(const char** str)
-{
- arg_t arg;
- const char* s = *str;
- char label[20];
- u64 val;
- int negative = 1;
- unsigned ttype;
-
- arg.type = T_NONE;
- arg.absolute = 0;
-
- while(*s && *s == ' ') ++s;
-
- switch(nexttok(&s, label, &val))
- {
- case '$' :
- ttype = nexttok(&s, NULL, &val);
- if(ttype == '-')
- {
- negative = -1;
- ttype = nexttok(&s, NULL, &val);
- }
- if(ttype != TOK_INT)
- crap("expected integer");
- arg.type = T_IMMEDIATE;
- arg.v.imm = negative * val;
- break;
- case '*' :
- if((ttype = nexttok(&s, NULL, NULL)) != '%')
- {
- if(ttype == '(')
- goto tok_memory;
- crap("expected '%%'");
- }
- arg.absolute = 1;
- /* fall through */
- case '%' :
- if(nexttok(&s, label, &val) != TOK_LABEL)
- crap("expected label");
- arg.type = T_REGISTER;
- arg.v.reg = parsereg(label);
- break;
- case TOK_LABEL:
- arg.type = T_LABEL;
- strncpy(arg.v.label, label, LABELLEN);
- break;
- case '-':
- negative = -1;
- if(nexttok(&s, NULL, &val) != TOK_INT)
- crap("expected integer");
- /* fall through */
- case TOK_INT:
- if(nexttok(&s, label, NULL) != '(')
- crap("expected '('"); // mov to/from fixed address not supported
- /* fall through */
- case '(':
-tok_memory:
- arg.type = T_MEMORY;
- arg.v.mem.indextype = T_NONE;
- arg.v.mem.disp = negative * val;
- ttype = nexttok(&s, label, &val);
- if(ttype == '%' && nexttok(&s, label, &val) != TOK_LABEL)
- {
- crap("expected register");
- }
- if (ttype == '%')
- {
- arg.v.mem.basetype = T_REGISTER;
- arg.v.mem.base.reg = parsereg(label);
- }
- else if (ttype == TOK_INT)
- {
- arg.v.mem.basetype = T_IMMEDIATE;
- arg.v.mem.base.imm = val;
- }
- if((ttype = nexttok(&s, NULL, NULL)) == ',')
- {
- ttype = nexttok(&s, label, &val);
- if(ttype == '%' && nexttok(&s, label, &val) != TOK_LABEL)
- {
- crap("expected register");
- }
- if (ttype == '%')
- {
- arg.v.mem.indextype = T_REGISTER;
- arg.v.mem.index.reg = parsereg(label);
- }
- else if (ttype == TOK_INT)
- {
- crap("index must be register");
- arg.v.mem.indextype = T_IMMEDIATE;
- arg.v.mem.index.imm = val;
- }
- if(nexttok(&s, NULL, NULL) != ',')
- crap("expected ','");
- if(nexttok(&s, NULL, &val) != TOK_INT)
- crap("expected integer");
- if(val != 1 && val != 2 && val != 4 && val != 8)
- crap("scale must 1, 2, 4 or 8");
- arg.v.mem.scale = val;
-
- ttype = nexttok(&s, NULL, NULL);
- }
- if(ttype != ')')
- {
- crap("expected ')' or ','");
- }
- break;
- default:
- crap("invalid token %hu in %s", *(unsigned char*)s, *str);
- break;
- }
-
- *str = s;
-
- return arg;
-}
-
-/* ************************* */
-
-void assembler_init(int pass)
-{
- compiledOfs = 0;
- assembler_pass = pass;
- if(!pass)
- {
- labelhash_free();
- cur_line = NULL;
- }
- if(!ops_sorted)
- {
- ops_sorted = 1;
- qsort(ops, ARRAY_LEN(ops)-1, sizeof(ops[0]), opsort);
- }
-}
-
-size_t assembler_get_code_size(void)
-{
- return compiledOfs;
-}
-
-void assembler_set_output(char* buf)
-{
- out = buf;
-}
-
-void assemble_line(const char* input, size_t len)
-{
- char line[4096];
- char* s;
- op_t* o;
- char* opn;
- arg_t arg1, arg2;
-
- arg1.type = T_NONE;
- arg2.type = T_NONE;
- opn = NULL;
- o = NULL;
-
- if(len < 1)
- return;
-
- if(len >= sizeof(line))
- crap("line too long");
-
- memcpy(line, input, sizeof(line));
- cur_line = input;
-
- if(line[len-1] == '\n') line[--len] = 0;
- if(line[len-1] == ':')
- {
- line[--len] = 0;
- if(assembler_pass)
- debug("%s: 0x%x\n", line, compiledOfs);
- else
- hash_add_label(line, compiledOfs);
- }
- else
- {
- opn = line;
- s = strchr(line, ' ');
- if(s)
- {
- *s++ = 0;
- arg1 = parsearg((const char**)&s);
- if(*s)
- {
- if(*s != ',')
- crap("expected ',', got '%c'", *s);
- ++s;
- arg2 = parsearg((const char**)&s);
- }
- }
-
- if(!opn)
- {
- crap("no operator in %s", line);
- }
-
- o = getop(opn);
- if(!o)
- {
- crap("cannot handle op %s", opn);
- }
- o->func(opn, arg1, arg2, o->data);
- if(assembler_pass)
- debug(" - %s%s", cur_line, cur_line[strlen(cur_line)-1]=='\n'?"":"\n");
- }
-}
-
-#ifdef SA_STANDALONE
-int main(int argc, char* argv[])
-{
- char line[4096];
- size_t len;
- int pass;
- FILE* file = NULL;
-
- if(argc < 2)
- {
- crap("specify file");
- }
-
- file = fopen(argv[1], "r");
- if(!file)
- {
- crap("can't open file");
- }
-
- if(argc > 2)
- {
- fout = fopen(argv[2], "w");
- if(!fout)
- {
- crap("can't open %s for writing", argv[2]);
- }
- }
-
- for(pass = 0; pass < 2; ++pass)
- {
- if(fseek(file, 0, SEEK_SET))
- crap("can't rewind file");
-
- if(pass)
- {
- char* b = malloc(assembler_get_code_size());
- if(!b)
- crap("cannot allocate memory");
- assembler_set_output(b);
- }
-
- assembler_init(pass);
-
- while(fgets(line, sizeof(line), file))
- {
- len = strlen(line);
- if(!len) continue;
-
- assemble_line(line, len);
- }
- }
-
- assembler_init(0);
-
- fclose(file);
-
- return 0;
-}
-#endif