diff options
| author | Andrew Chambers <[email protected]> | 2021-10-06 01:22:20 +1300 |
|---|---|---|
| committer | Andrew Chambers <[email protected]> | 2021-10-06 01:22:20 +1300 |
| commit | 7358b6242d46247286e9fec949795ddf098d1d3a (patch) | |
| tree | 19a20e1097e7059b7132593a909d711db1057605 | |
| parent | e0b52ff23ca304d581f80f0d9bd2de76fb042d03 (diff) | |
Refactor modrm commands.
| -rw-r--r-- | asm.peg | 33 | ||||
| -rw-r--r-- | main.c | 239 | ||||
| -rw-r--r-- | minias.h | 12 |
3 files changed, 174 insertions, 110 deletions
@@ -23,23 +23,30 @@ instr = "nop"{ $$.kind = ASM_NOP; } | "leave" { $$.kind = ASM_LEAVE; } | "ret" { $$.kind = ASM_RET; } | i:jmp { $$ = i; } - | i:add { $$ = i; } + | i:mod-rm-binop { $$ = i; } jmp = "jmp" ws i:ident { $$.jmp = (Jmp){.kind = ASM_JMP, .target = i.ident.name}; } -add = "addq" ws s:r-m64 ws? ',' ws? d:r64 - { $$.add = (Add){ .kind = ASM_ADD, .type = 'q', .src = dupv(&s), .dst = dupv(&d) } } - | "addq" ws s:r64 ws? ',' ws? d:r-m64 - { $$.add = (Add){ .kind = ASM_ADD, .type = 'q', .src = dupv(&s), .dst = dupv(&d) } } - | "addq" ws s:imm ws? ',' ws? d:r-m64 - { $$.add = (Add){ .kind = ASM_ADD, .type = 'q', .src = dupv(&s), .dst = dupv(&d) } } - | "addl" ws s:r-m32 ws? ',' ws? d:r32 - { $$.add = (Add){ .kind = ASM_ADD, .type = 'l', .src = dupv(&s), .dst = dupv(&d) } } - | "addl" ws s:r32 ws? ',' ws? d:r-m32 - { $$.add = (Add){ .kind = ASM_ADD, .type = 'l', .src = dupv(&s), .dst = dupv(&d) } } - | "addl" ws s:imm ws? ',' ws? d:r-m32 - { $$.add = (Add){ .kind = ASM_ADD, .type = 'l', .src = dupv(&s), .dst = dupv(&d) } } +mod-rm-binop = "add" a:mod-rm-binop-args { $$ = a; $$.kind = ASM_ADD; } + | "and" a:mod-rm-binop-args { $$ = a; $$.kind = ASM_AND;} + | "sub" a:mod-rm-binop-args { $$ = a; $$.kind = ASM_SUB;} + | "xor" a:mod-rm-binop-args { $$ = a; $$.kind = ASM_XOR;} + + +mod-rm-binop-args = + "q" ws s:r-m64 ws? ',' ws? d:r64 + { $$.modrmbinop = (ModRMBinop){ .type = 'q', .src = dupv(&s), .dst = dupv(&d) } } + | "q" ws s:r64 ws? ',' ws? d:r-m64 + { $$.modrmbinop = (ModRMBinop){ .type = 'q', .src = dupv(&s), .dst = dupv(&d) } } + | "q" ws s:imm ws? ',' ws? d:r-m64 + { $$.modrmbinop = (ModRMBinop){ .type = 'q', .src = dupv(&s), .dst = dupv(&d) } } + | "l" ws s:r-m32 ws? ',' ws? d:r32 + { $$.modrmbinop = (ModRMBinop){ .type = 'l', .src = dupv(&s), .dst = dupv(&d) } } + | "l" ws s:r32 ws? ',' ws? d:r-m32 + { $$.modrmbinop = (ModRMBinop){ .type = 'l', .src = dupv(&s), .dst = dupv(&d) } } + | "l" ws s:imm ws? ',' ws? d:r-m32 + { $$.modrmbinop = (ModRMBinop){ .type = 'l', .src = dupv(&s), .dst = dupv(&d) } } r-m64 = r:r64 { $$ = r; } | m:m { $$ = m; } @@ -177,15 +177,11 @@ static void sw(uint32_t w) { secaddbytes(cursection, buf, sizeof(buf)); } -/* Compose a ModR/M byte. */ -static uint8_t modrm(uint8_t mod, uint8_t regop, uint8_t rm) { - return ((mod & 3) << 6) | ((regop & 7) << 3) | (rm & 7); -} +static int isregkind(AsmKind k) { return k > ASM_REG_BEGIN && k < ASM_REG_END; } /* Is one of the r$n style registers. */ -static uint8_t isextr64(AsmKind k) { return k >= ASM_R8 && k <= ASM_R15; } +static int isextr64(AsmKind k) { return k >= ASM_R8 && k <= ASM_R15; } -/* Convert an ASM_KIND to register bits */ static uint8_t r64bits(AsmKind k) { if (isextr64(k)) { return (1 << 4) | ((k - ASM_R8) & 0xff); @@ -196,9 +192,20 @@ static uint8_t r64bits(AsmKind k) { static uint8_t r32bits(AsmKind k) { return (k - ASM_EAX) & 0xff; } +/* Convert an ASM_KIND to register bits */ +static uint8_t rbits(AsmKind k) { + if (k >= ASM_RAX && k <= ASM_R15) { + return r64bits(k); + } else if (k >= ASM_EAX && k <= ASM_EDI) { + return r32bits(k); + } else { + fatal("BUG: kind not a register"); + } + return 0; +} + #define REX(W, R, X, B) \ ((1 << 6) | (!!(W) << 3) | (!!(R) << 2) | (!!(X) << 1) | (!!(B) << 0)) -#define REX_W REX(1, 0, 0, 0) static void assemble() { Symbol *sym; @@ -250,101 +257,141 @@ static void assemble() { case ASM_RET: sb(0xc3); break; - case ASM_ADD: { - Add *add = &v->add; - switch (add->type) { - case 'l': - switch (add->src->kind) { - case ASM_IMM: - if (add->dst->kind == ASM_MEMARG) { - uint8_t rbits = r64bits(add->dst->memarg.reg); - if (rbits & (1 << 4)) { - // 64 bit register, need rex. - sb(REX(0, 0, 0, rbits & (1 << 4))); - } - if ((rbits & 7) == 4) { - fatal("%d: cannot address destination", l->lineno); - } - if (add->dst->memarg.c == 0 && add->dst->memarg.l == NULL) { - if ((rbits & 7) == 5) { /* BP style registers need displacement */ - sb3(0x81, modrm(0x01, 0x00, rbits), 0x00); - } else { - sb2(0x81, modrm(0x00, 0x00, rbits)); - } - } else { - fatal("TODO mem arg with disp"); - } - } else { // imm, r32 - sb2(0x81, modrm(0x03, 0x00, r32bits(add->dst->kind))); - } - sw(0); - break; - break; - case ASM_MEMARG: - fatal("TODO"); - break; - default: - switch (add->src->kind) { - case ASM_MEMARG: - fatal("TODO"); - break; - default: { // r32, r32 - uint8_t srcbits = r32bits(add->src->kind); - uint8_t dstbits = r32bits(add->dst->kind); - sb2(0x03, modrm(0x03, dstbits, srcbits)); - break; - } + case ASM_ADD: + case ASM_AND: + case ASM_SUB: + case ASM_XOR: { + + ModRMBinop *op; + Memarg *memarg; + uint8_t opcode; + uint8_t rex, mod, reg, rm, sib; + int64_t disp; + int wantsib; + int dispsz; + int64_t imm; + int immsz; + + op = &v->modrmbinop; + memarg = NULL; + mod = 0x03; + wantsib = 0; + sib = 0; + dispsz = 0; + immsz = 0; + + if (op->src->kind == ASM_MEMARG) { + memarg = &op->src->memarg; + } else if (op->dst->kind == ASM_MEMARG) { + memarg = &op->dst->memarg; + } + + if (memarg) { + rm = rbits(memarg->reg); + + /* We cannot address ESP/RSP/... */ + if ((rm & 7) == 4) + fatal("addressing mode unrepresentable"); + + if (memarg->c == 0 && memarg->l == NULL) { + if ((rm & 7) == 5) { // BP style registers need displacement + mod = 0x01; + wantsib = 1; + sib = 0; + disp = 0; + dispsz = 1; + } else { + mod = 0x00; } - break; + } else { + fatal("unreachable"); } - break; - case 'q': - switch (add->src->kind) { - case ASM_IMM: - if (add->dst->kind == ASM_MEMARG) { - uint8_t rbits = r64bits(add->dst->memarg.reg); - uint8_t rex = REX(1, 0, 0, rbits & (1 << 4)); - if ((rbits & 7) == 4) { - fatal("%d: cannot address destination", l->lineno); - } - if (add->dst->memarg.c == 0 && add->dst->memarg.l == NULL) { - if ((rbits & 7) == 5) { /* BP style registers need displacement */ - sb4(rex, 0x81, modrm(0x01, 0x00, rbits), 0x00); - } else { - sb3(rex, 0x81, modrm(0x00, 0x00, rbits)); - } - } else { - fatal("TODO mem arg with disp"); - } - } else { // imm, r64 - uint8_t rbits = r64bits(add->dst->kind); - uint8_t rex = REX(1, 0, 0, rbits & (1 << 4)); - sb3(rex, 0x81, modrm(0x03, 0x00, rbits)); - } - sw(0); - break; - case ASM_MEMARG: - fatal("TODO"); - break; - default: - switch (add->src->kind) { - case ASM_MEMARG: - fatal("TODO"); - break; - default: { // r64, r64 - uint8_t srcbits = r64bits(add->src->kind); - uint8_t dstbits = r64bits(add->dst->kind); - uint8_t rex = REX(1, dstbits & (1 << 4), 0, srcbits & (1 << 4)); - sb3(rex, 0x03, modrm(0x03, dstbits, srcbits)); - break; - } - } - break; + } + + if (isregkind(op->src->kind) && isregkind(op->dst->kind)) { + + if (op->kind == ASM_ADD) + opcode = 0x03; + else if (op->kind == ASM_SUB) + opcode = 0x2b; + else { + fatal("unreachable"); + } + + reg = rbits(op->dst->kind); + rm = rbits(op->src->kind); + + } else if (op->src->kind == ASM_IMM) { + + if (op->kind == ASM_ADD) { + opcode = 0x81; + reg = 0x00; + } else if (op->kind == ASM_SUB) { + opcode = 0x81; + reg = 0x05; + } else { + fatal("unreachable"); } + + if (memarg) { + rm = rbits(memarg->reg); + } else { + rm = rbits(op->dst->kind); + } + + } else if (isregkind(op->src->kind)) { + assert(memarg); + + if (op->kind == ASM_ADD) { + opcode = 0x01; + } else if (op->kind == ASM_SUB) { + opcode = 0x29; + } else { + fatal("unreachable"); + } + + reg = rbits(op->src->kind); + + } else if (op->src->kind == ASM_MEMARG) { + fatal("todo"); + } else { + fatal("unreachable"); + } + + rex = REX(op->type == 'q', reg & (1 << 4), 0, rm & (1 << 4)); + + if (rex != REX(0, 0, 0, 0)) { + sb(rex); + } + + sb2(opcode, ((mod & 3) << 6) | ((reg & 7) << 3) | (rm & 7)); + + if (wantsib) { + sb(sib); + } + + switch (dispsz) { + case 1: + sb((uint8_t)disp); + break; + case 4: + sw((uint32_t)disp); break; default: - fatal("unknown type"); + fatal("unreachable"); } + + switch (immsz) { + case 1: + sb((uint8_t)imm); + break; + case 4: + sw((uint32_t)imm); + break; + default: + fatal("unreachable"); + } + break; } case ASM_JMP: { @@ -48,7 +48,11 @@ typedef enum { ASM_JMP, ASM_LEAVE, ASM_ADD, + ASM_AND, + ASM_SUB, + ASM_XOR, // Registers, order matters. + ASM_REG_BEGIN, ASM_EAX, ASM_ECX, ASM_EDX, @@ -73,6 +77,7 @@ typedef enum { ASM_R13, ASM_R14, ASM_R15, + ASM_REG_END, } AsmKind; typedef union Parsev Parsev; @@ -130,7 +135,10 @@ typedef struct { char type; Parsev *src; Parsev *dst; -} Add; +} ModRMBinop; + +typedef ModRMBinop Add; +typedef ModRMBinop Sub; union Parsev { AsmKind kind; @@ -138,7 +146,9 @@ union Parsev { Globl globl; Balign balign; Memarg memarg; + ModRMBinop modrmbinop; Add add; + Sub sub; Jmp jmp; Byte byte; Imm imm; |
