aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Chambers <[email protected]>2021-10-06 01:22:20 +1300
committerAndrew Chambers <[email protected]>2021-10-06 01:22:20 +1300
commit7358b6242d46247286e9fec949795ddf098d1d3a (patch)
tree19a20e1097e7059b7132593a909d711db1057605
parente0b52ff23ca304d581f80f0d9bd2de76fb042d03 (diff)
Refactor modrm commands.
-rw-r--r--asm.peg33
-rw-r--r--main.c239
-rw-r--r--minias.h12
3 files changed, 174 insertions, 110 deletions
diff --git a/asm.peg b/asm.peg
index 5bcd972..56466a3 100644
--- a/asm.peg
+++ b/asm.peg
@@ -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; }
diff --git a/main.c b/main.c
index 0aa059a..ce8b666 100644
--- a/main.c
+++ b/main.c
@@ -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: {
diff --git a/minias.h b/minias.h
index 9889733..126c842 100644
--- a/minias.h
+++ b/minias.h
@@ -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;