gateware

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 5bef5d98cf39d15a54fde93298eb14ef40eb18fb
parent a31e3a9143ece6a8584fb6ef933b1e6eb4227714
Author: Brian Swetland <swetland@frotz.net>
Date:   Fri, 30 Nov 2018 16:26:46 -0800

isa16v5: new assembler and disassembler

Also fix the constant encodings in the ISAv5 doc.

Diffstat:
Mdocs/isa16v5.txt | 23+++++++++++------------
Asrc/a16v5.c | 720+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/d16v5.c | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 919 insertions(+), 12 deletions(-)

diff --git a/docs/isa16v5.txt b/docs/isa16v5.txt @@ -3,11 +3,11 @@ instruction encodings alu opcodes FEDC BA9 876 543 210 ffff bbb aaa ccc 000 ALU Rc, Ra, Rb 0000 AND r = x & y siii iii aaa ccc 001 ADD Rc, Ra, si7 0001 ORR r = x | y -siii iii iii ccc 010 MOV Rc, si10 0010 XOR r = x ^ y +siii iii jjj ccc 010 MOV Rc, si10 0010 XOR r = x ^ y siii iii aaa ccc 011 LW Rc, [Ra, si7] 0011 NOT r = ~x -szii iii iii bbb 100 BZ/BNZ Rb, si9 0100 ADD r = x + y +siii iii zjj bbb 100 BZ/BNZ Rb, si9 0100 ADD r = x + y siii iii aaa bbb 101 SW Rb, [Ra, si7] 0101 SUB r = x - y -siii iii iii jjl 110 B/BL si12 0110 SLT r = x < y (signed) +siii iii jjj kkl 110 B/BL si12 0110 SLT r = x < y (signed) 0xxx 000 aaa xxl 111 B/BL Ra 0111 SLU r = x < y (unsigned) 0xxx 001 xxx xxx 111 NOP 1000 SHL 1|4 0 010 111 1001 SHR 1|4 @@ -18,18 +18,17 @@ siii iii iii jjl 110 B/BL si12 0110 SLT r = x < y (signed) 0xff 111 aaa ccc 111 SHL/SHR/ROL/ROR Rc, Ra, 4 1110 SWP r = {x[7:0], y[15:8]} 1iii iii aaa ccc 111 MHI Rc, Ra, si7[5:0] 1111 MHI r = {y[5:0], x[9:0]} -aliases immediate forms (shared bits) ----------------------------------------------- ------------------------------- -MOV Rc, Ra -> AND Rc, Ra, Ra si7 ssssssssssiiiiii -SNE Rc, Ra, Rb -> XOR Rc, Ra, Rb si10 sssssssiiiiiiiii -SGE Rc, Ra, Rb -> SLT Rc, Rb, Ra si9 ssssssssiiiiiiii -SGU Rc, Ra, Rb -> SLU Rc, Rb, Ra si12 sssssjjiiiiiiiii +aliases immediate forms (shared bits) +----------------------------------- ------------------------------- +MOV Rc, Ra -> AND Rc, Ra, Ra si7 siiiiiixxxxxxxxx -> ssssssssssiiiiii +SNE Rc, Ra, Rb -> XOR Rc, Ra, Rb si9 siiiiiixjjxxxxxx -> ssssssssjjiiiiii +SGE Rc, Ra, Rb -> SLT Rc, Rb, Ra si10 siiiiiijjjxxxxxx -> sssssssjjjiiiiii +SGU Rc, Ra, Rb -> SLU Rc, Rb, Ra si12 siiiiiijjjkkxxxx -> ssssskkjjjiiiiii + + ir[2:0] x00=si9 xx1=si7 010=si10 110=si12 some implementation notes ------------------------- -ir[2] 0=bsel-hi, 1=bsel-lo -ir[0] 0=imm=si10 1=imm=si7 -ir[1] 0=bim=si9 1=bim=si12 alu.x = regs.a | pc (b) alu.y = regs.b | imm alu.op = ffff | 00ff(shifts) | 0100(lw/sw/b) | 1111 (mhi) diff --git a/src/a16v5.c b/src/a16v5.c @@ -0,0 +1,720 @@ +// Copyright 2015, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0. + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <strings.h> +#include <string.h> + +typedef unsigned u32; +typedef unsigned short u16; + +static unsigned linenumber = 0; +static char linestring[256]; +static char *filename; + +FILE *ofp = 0; + +void die(const char *fmt, ...) { + va_list ap; + fprintf(stderr,"%s:%d: ", filename, linenumber); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr,"\n"); + if (linestring[0]) + fprintf(stderr,"%s:%d: >> %s <<\n", filename, linenumber, linestring); + exit(1); +} + +// various fields +#define _OP(n) (((n) & 7) << 0) +#define _C(n) (((n) & 7) << 3) +#define _A(n) (((n) & 7) << 6) +#define _B(n) (((n) & 7) << 9) +#define _F(n) (((n) & 15) << 12) +#define _I7(n) (((n) & 0x7F) << 9) + +// instr: siiiiii-jj------ +// imm sjjiiiiii +static inline unsigned _I9(unsigned n) { + return ((n & 0x3F) << 9) | (n & 0xC0) | ((n & 0x100) << 7); +} + +// instr: siiiiiijjj------ +// imm sjjjiiiiii +static inline unsigned _I10(unsigned n) { + return ((n & 0x3F) << 9) | (n & 0x1C0) | ((n & 0x200) << 6); +} + +// instr: siiiiiijjjkk---- +// imm skkjjjiiiiii +static inline unsigned _I12(unsigned n) { + return ((n & 0x3F) << 9) | (n & 0x1C0) | ((n & 0x600) >> 5) | ((n & 0x800) << 4); +} + +static inline unsigned _U6(unsigned n) { + return ((n & 3) << 6) | ((n & 0x38) << 9); +} + +int is_unsigned6(unsigned n) { + return ((n & 0xFFC0) == 0); +} + +int is_signed7(unsigned n) { + n &= 0xFFFFFFC0; + return ((n == 0) || (n == 0xFFFFFFC0)); +} +int is_signed9(unsigned n) { + n &= 0xFFFFFF00; + return ((n == 0) || (n == 0xFFFFFF00)); +} +int is_signed10(unsigned n) { + n &= 0xFFFFFE00; + return ((n == 0) || (n == 0xFFFFFE00)); +} +int is_signed12(unsigned n) { + n &= 0xFFFFF800; + return ((n == 0) || (n == 0xFFFFF800)); +} + +u16 rom[65535]; +u16 PC = 0; + +#define TYPE_PCREL_S9 1 +#define TYPE_PCREL_S12 2 +#define TYPE_ABS_U16 3 + +struct fixup { + struct fixup *next; + unsigned pc; + unsigned type; +}; + +struct label { + struct label *next; + struct fixup *fixups; + const char *name; + unsigned pc; + unsigned defined; +}; + +struct label *labels; +struct fixup *fixups; + +void fixup_branch(const char *name, int addr, int btarget, int type) { + unsigned n; + + switch(type) { + case TYPE_PCREL_S9: + n = btarget - addr - 1; + if (!is_signed9(n)) break; + rom[addr] |= _I9(n); + return; + case TYPE_PCREL_S12: + n = btarget - addr - 1; + if (!is_signed12(n)) break; + rom[addr] |= _I12(n); + return; + case TYPE_ABS_U16: + rom[addr] = btarget; + return; + default: + die("unknown branch type %d\n",type); + } + die("label '%s' at %08x is out of range of %08x\n", name, btarget, addr); +} + +void setlabel(const char *name, unsigned pc) { + struct label *l; + struct fixup *f; + + for (l = labels; l; l = l->next) { + if (!strcasecmp(l->name, name)) { + if (l->defined) die("cannot redefine '%s'", name); + l->pc = pc; + l->defined = 1; + for (f = l->fixups; f; f = f->next) { + fixup_branch(name, f->pc, l->pc, f->type); + } + return; + } + } + l = malloc(sizeof(*l)); + l->name = strdup(name); + l->pc = pc; + l->fixups = 0; + l->defined = 1; + l->next = labels; + labels = l; +} + +const char *getlabel(unsigned pc) { + struct label *l; + for (l = labels; l; l = l->next) + if (l->pc == pc) + return l->name; + return 0; +} + +void uselabel(const char *name, unsigned pc, unsigned type) { + struct label *l; + struct fixup *f; + + for (l = labels; l; l = l->next) { + if (!strcasecmp(l->name, name)) { + if (l->defined) { + fixup_branch(name, pc, l->pc, type); + return; + } else { + goto add_fixup; + } + } + } + l = malloc(sizeof(*l)); + l->name = strdup(name); + l->pc = 0; + l->fixups = 0; + l->defined = 0; + l->next = labels; + labels = l; +add_fixup: + f = malloc(sizeof(*f)); + f->pc = pc; + f->type = type; + f->next = l->fixups; + l->fixups = f; +} + +void checklabels(void) { + struct label *l; + for (l = labels; l; l = l->next) { + if (!l->defined) { + die("undefined label '%s'", l->name); + } + } +} + +void disassemble(char *buf, unsigned pc, unsigned instr); + +void emit(unsigned instr) { + rom[PC++] = instr; +} + +void save(const char *fn) { + const char *name; + unsigned n; + char dis[128]; + + FILE *fp = fopen(fn, "w"); + if (!fp) die("cannot write to '%s'", fn); + for (n = 0; n < PC; n++) { + disassemble(dis, n, rom[n]); + name = getlabel(n); + if (name) { + fprintf(fp, "%04x // %04x: %-25s <- %s\n", rom[n], n, dis, name); + } else { + fprintf(fp, "%04x // %04x: %s\n", rom[n], n, dis); + } + } + fclose(fp); +} + +#define MAXTOKEN 32 + +enum tokens { + tEOL, + tCOMMA, tCOLON, tOBRACK, tCBRACK, tDOT, tHASH, tSTRING, tNUMBER, + tAND, tORR, tXOR, tNOT, tADD, rSUB, tSLT, tSLU, + tSHL, tSHR, tROL, tROR, tMUL, tDUP, tSWP, tMHI, + tLW, tSW, tLC, tSC, tB, tBL, tBZ, tBNZ, + tMOV, tSGE, tSGU, tSNE, tNOP, tHALT, + tR0, tR1, tR2, tR3, tR4, tR5, tR6, tR7, + tSP, tLR, + tEQU, tWORD, tASCII, tASCIIZ, + NUMTOKENS, +}; + +char *tnames[] = { + "<EOL>", + ",", ":", "[", "]", ".", "#", "<STRING>", "<NUMBER>", + "AND", "ORR", "XOR", "NOT", "ADD", "SUB", "SLT", "SLU", + "SHL", "SHR", "ROL", "ROR", "MUL", "DUP", "SWP", "MHI", + "LW", "SW", "LC", "SC", "B", "BL", "BZ", "BNZ", + "MOV", "SGE", "SGU", "SNE", "NOP", "HALT", + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "SP", "LR", + "EQU", "WORD", "STRING", "ASCIIZ" +}; + +#define FIRST_ALU_OP tAND +#define LAST_ALU_OP tMHI +#define FIRST_REGISTER tR0 +#define LAST_REGISTER tLR + +int is_reg(unsigned tok) { + return ((tok >= FIRST_REGISTER) && (tok <= LAST_REGISTER)); +} + +int is_alu_op(unsigned tok) { + return ((tok >= FIRST_ALU_OP) && (tok <= LAST_ALU_OP)); +} + +unsigned to_func(unsigned tok) { + return tok - FIRST_ALU_OP; +} + +unsigned to_reg(unsigned tok) { + if (tok == tLR) return 7; + if (tok == tSP) return 6; + return tok - FIRST_REGISTER; +} + +int is_stopchar(unsigned x) { + switch (x) { + case 0: + case ' ': + case '\t': + case '\r': + case '\n': + case ',': + case ':': + case '[': + case ']': + case '.': + case '"': + case '#': + return 1; + default: + return 0; + } +} +int is_eoschar(unsigned x) { + switch (x) { + case 0: + case '\t': + case '\r': + case '"': + return 1; + default: + return 0; + } +} + +int tokenize(char *line, unsigned *tok, unsigned *num, char **str) { + char *s; + int count = 0; + unsigned x, n, neg; + linenumber++; + + for (;;) { + x = *line; + again: + if (count == 31) die("line too complex"); + + switch (x) { + case 0: + goto alldone; + case ' ': + case '\t': + case '\r': + case '\n': + line++; + continue; + case '/': + if (line[1] == '/') + goto alldone; + case ';': + goto alldone; + case ',': + str[count] = ","; + num[count] = 0; + tok[count++] = tCOMMA; + line++; + continue; + case ':': + str[count] = ":"; + num[count] = 0; + tok[count++] = tCOLON; + line++; + continue; + case '[': + str[count] = "["; + num[count] = 0; + tok[count++] = tOBRACK; + line++; + continue; + case ']': + str[count] = "]"; + num[count] = 0; + tok[count++] = tCBRACK; + line++; + continue; + case '.': + str[count] = "."; + num[count] = 0; + tok[count++] = tDOT; + line++; + continue; + case '#': + str[count] = "#"; + num[count] = 0; + tok[count++] = tHASH; + line++; + continue; + case '"': + str[count] = ++line; + num[count] = 0; + tok[count++] = tSTRING; + while (!is_eoschar(*line)) line++; + if (*line != '"') + die("unterminated string"); + *line++ = 0; + continue; + } + + s = line++; + while (!is_stopchar(*line)) line++; + + /* save the stopchar */ + x = *line; + *line = 0; + + neg = (s[0] == '-'); + if (neg && isdigit(s[1])) s++; + + str[count] = s; + if (isdigit(s[0])) { + num[count] = strtoul(s, 0, 0); + if(neg) num[count] = -num[count]; + tok[count++] = tNUMBER; + goto again; + } + if (isalpha(s[0])) { + num[count] = 0; + for (n = tNUMBER + 1; n < NUMTOKENS; n++) { + if (!strcasecmp(s, tnames[n])) { + str[count] = tnames[n]; + tok[count++] = n; + goto again; + } + } + + while (*s) { + if (!isalnum(*s) && (*s != '_')) + die("invalid character '%c' in identifier", *s); + s++; + } + tok[count++] = tSTRING; + goto again; + } + die("invalid character '%c'", s[0]); + } + +alldone: + str[count] = ""; + num[count] = 0; + tok[count++] = tEOL; + return count; +} + +void expect(unsigned expected, unsigned got) { + if (expected != got) + die("expected %s, got %s", tnames[expected], tnames[got]); +} + +void expect_register(unsigned got) { + if (!is_reg(got)) + die("expected register, got %s", tnames[got]); +} + +#define REG(n) (tnames[FIRST_REGISTER + (n)]) + +#define OP_ALU_RC_RA_RB 0x0000 +#define OP_ADD_RC_RA_S7 0x0001 +#define OP_MOV_RC_S10 0x0002 +#define OP_LW_RC_RA_S7 0x0003 +#define OP_BNZ_RC_S9 0x0004 +#define OP_BZ_RC_S9 0x0104 +#define OP_SW_RC_RA_S7 0x0005 +#define OP_B_S12 0x0006 +#define OP_BL_S12 0x000E +#define OP_B_RA 0x0007 +#define OP_BL_RA 0x000F +#define OP_NOP 0x0207 +#define OP_LC_RC_U6 0x0807 +#define OP_SC_RC_U6 0x0A07 +#define OP_SHL_RC_RA_1 0x0C07 +#define OP_SHR_RC_RA_1 0x1C07 +#define OP_ROL_RC_RA_1 0x2C07 +#define OP_ROR_RC_RA_1 0x3C07 +#define OP_MHI_RC_RA_S7 0x8007 + +#define ALU_AND 0 +#define ALU_ORR 1 +#define ALU_XOR 2 +#define ALU_NOT 3 +#define ALU_ADD 4 +#define ALU_SUB 5 +#define ALU_SLT 6 +#define ALU_SLU 7 +#define ALU_SHL 8 +#define ALU_SHR 9 +#define ALU_ROL 10 +#define ALU_ROR 11 +#define ALU_MUL 12 +#define ALU_DUP 13 +#define ALU_SWP 14 +#define ALU_MHI 15 + +#define T0 tok[0] +#define T1 tok[1] +#define T2 tok[2] +#define T3 tok[3] +#define T4 tok[4] +#define T5 tok[5] +#define T6 tok[6] +#define T7 tok[7] + +void assemble_line(int n, unsigned *tok, unsigned *num, char **str) { + unsigned instr = 0; + unsigned tmp; + if (T0 == tSTRING) { + if (T1 == tCOLON) { + setlabel(str[0], PC); + tok += 2; + num += 2; + str += 2; + n -= 2; + } else { + die("unexpected identifier '%s'", str[0]); + } + } + + switch(T0) { + case tEOL: + /* blank lines are fine */ + return; + case tNOP: + emit(OP_NOP); + return; + case tNOT: + expect_register(T1); + expect(tCOMMA, T2); + expect_register(T3); + emit(OP_ALU_RC_RA_RB | _F(ALU_NOT) | _C(to_reg(T1)) | _A(to_reg(T3))); + return; + case tMOV: + expect_register(T1); + expect(tCOMMA, T2); + if (is_reg(T3)) { + emit(OP_ALU_RC_RA_RB | _F(ALU_AND) | _C(to_reg(T1)) | _A(to_reg(T3)) | _B(to_reg(T3))); + return; + } + expect(tNUMBER, T3); + emit(OP_MOV_RC_S10 | _C(to_reg(T1)) | _I10(num[3])); + if (!is_signed10(num[3])) { + // load high bits if needed + emit(OP_MHI_RC_RA_S7 | _C(to_reg(T1)) | _A(to_reg(T1)) | _I7(num[3] >> 10)); + } + return; + case tMHI: + expect_register(T1); + expect(tCOMMA, T2); + if (tok[3] == tNUMBER) { + if (num[3] & 0xFFC0) { + die("constant out of range for MHI"); + } + emit(OP_MHI_RC_RA_S7 | _C(to_reg(T1)) | _A(to_reg(T1)) | _I7(num[3])); + return; + } + // will be handled by general ALU path + break; + case tSHL: + case tSHR: + case tROL: + case tROR: + switch (T0) { + case tSHL: instr = OP_SHL_RC_RA_1; break; + case tSHR: instr = OP_SHR_RC_RA_1; break; + case tROL: instr = OP_ROL_RC_RA_1; break; + case tROR: instr = OP_ROR_RC_RA_1; break; + } + expect_register(T1); + expect(tCOMMA, T2); + expect_register(T3); + expect(tCOMMA, T4); + expect(tNUMBER, T5); + if (num[5] == 4) { + instr |= 0x200; + } else if(num[5] != 1) { + die("shift/rotate immediate not 1 or 4"); + } + emit(instr | _C(to_reg(T1)) | _A(to_reg(T3))); + return; + case tLW: + case tSW: + instr = (T0 == tLW ? OP_LW_RC_RA_S7 : OP_SW_RC_RA_S7); + expect_register(T1); + expect(tCOMMA, T2); + expect(tOBRACK, T3); + expect_register(T4); + if (T5 == tCOMMA) { + expect(tNUMBER, T6); + expect(tCBRACK, T7); + tmp = num[6]; + } else { + expect(tCBRACK, T5); + tmp = 0; + } + if (!is_signed7(tmp)) die("index too large"); + emit(instr | _C(to_reg(T1)) | _A(to_reg(T4)) | _I7(tmp)); + return; + case tLC: + case tSC: + instr = (T0 == tLC ? OP_LC_RC_U6 : OP_SC_RC_U6); + expect_register(T1); + expect(tCOMMA, T2); + expect(tNUMBER, T3); + if (!is_unsigned6(num[3])) die("invalid control register"); + emit(instr | _C(to_reg(T1)) | _U6(num[3])); + return; + case tB: + case tBL: + if (is_reg(T1)) { + instr = (T0 == tB) ? OP_B_RA : OP_BL_RA; + emit(instr | _A(to_reg(T1))); + } else { + instr = (T0 == tB) ? OP_B_S12 : OP_BL_S12; + if (T1 == tSTRING) { + emit(instr); + uselabel(str[1], PC - 1, TYPE_PCREL_S12); + } else if (T1 == tDOT) { + emit(instr | _I12(-1)); + } else { + die("expected register or address"); + } + } + return; + case tBZ: + case tBNZ: + instr = (T0 == tBZ) ? OP_BZ_RC_S9 : OP_BNZ_RC_S9; + expect_register(T1); + expect(tCOMMA, T2); + if (T3 == tSTRING) { + emit(instr | _C(to_reg(T1))); + uselabel(str[3], PC - 1, TYPE_PCREL_S9); + } else if (T3 == tDOT) { + emit(instr | _C(to_reg(T1)) | _I9(-1)); + } else { + die("expected register or address"); + } + return; + case tHALT: + emit(0xFFFF); //TODO + return; + case tWORD: + tmp = 1; + for (;;) { + if (tok[tmp] == tSTRING) { + emit(0); + uselabel(str[tmp++], PC - 1, TYPE_ABS_U16); + } else { + expect(tNUMBER, tok[tmp]); + emit(num[tmp++]); + } + if (tok[tmp] != tCOMMA) + break; + tmp++; + } + return; + case tASCII: + case tASCIIZ: { + unsigned n = 0, c = 0; + const unsigned char *s = (void*) str[1]; + expect(tSTRING, tok[1]); + while (*s) { + n |= ((*s) << (c++ * 8)); + if (c == 2) { + emit(n); + n = 0; + c = 0; + } + s++; + } + emit(n); + return; + } + } + if (is_alu_op(T0)) { + expect_register(T1); + expect(T2, tCOMMA); + expect_register(T3); + expect(T4, tCOMMA); + if ((tok[5] == tNUMBER) && (T0 == tADD)) { + if (!is_signed7(num[5])) { + die("add immediate must be +/-128"); + } + emit(OP_ADD_RC_RA_S7 | _C(to_reg(T1)) | _A(to_reg(T3)) | _I7(num[5])); + return; + } + expect_register(T5); + emit(OP_ALU_RC_RA_RB | _C(to_reg(T1)) | _A(to_reg(T3)) | _B(to_reg(T5)) | _F(to_func(T0))); + return; + } + + die("HUH"); +} + +void assemble(const char *fn) { + FILE *fp; + char line[256]; + int n; + + unsigned tok[MAXTOKEN]; + unsigned num[MAXTOKEN]; + char *str[MAXTOKEN]; + char *s; + + fp = fopen(fn, "r"); + if (!fp) die("cannot open '%s'", fn); + + while (fgets(line, sizeof(line)-1, fp)) { + strcpy(linestring, line); + s = linestring; + while (*s) { + if ((*s == '\r') || (*s == '\n')) *s = 0; + else s++; + } + n = tokenize(line, tok, num, str); +#if DEBUG + { + int i + printf("%04d: (%02d) ", linenumber, n); + for (i = 0; i < n; i++) + printf("%s ", tnames[tok[i]]); + printf("\n"); + } +#endif + assemble_line(n, tok, num, str); + } +} + +int main(int argc, char **argv) { + const char *outname = "out.hex"; + filename = argv[1]; + + if (argc < 2) + die("no file specified"); + if (argc == 3) + outname = argv[2]; + + assemble(filename); + linestring[0] = 0; + checklabels(); + save(outname); + + return 0; +} diff --git a/src/d16v5.c b/src/d16v5.c @@ -0,0 +1,188 @@ +// Copyright 2018, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0. + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <strings.h> +#include <string.h> + +typedef unsigned u32; +typedef unsigned short u16; + +char *append(char *buf, const char *s) { + while (*s) + *buf++ = *s++; + return buf; +} +char *append_u16(char *buf, unsigned n) { + sprintf(buf, "%04x", n & 0xFFFF); + return buf + strlen(buf); +} +char *append_int(char *buf, int n) { + sprintf(buf, "%d", n); + return buf + strlen(buf); +} + +const char *regname[] = { + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", +}; + +const char *alufunc[] = { + "AND", "ORR", "XOR", "NOT", "ADD", "SUB", "SLT", "SLU", + "SHL", "SHR", "ROL", "ROR", "MUL", "DUP", "SWP", "MHI", +}; + +void printinst(char *buf, unsigned pc, unsigned instr, const char *fmt, unsigned verbose) { + char *start = buf; + char note[64]; + note[0] = 0; + + unsigned c = (instr >> 3) & 7; + unsigned a = (instr >> 6) & 7; + unsigned b = (instr >> 9) & 7; + unsigned f = (instr >> 12) & 15; + + // immediates + int s7 = (instr >> 9) & 0x3F; + int s9 = ((instr >> 9) & 0x3F) | (instr & 0xC0); + int s10 = ((instr >> 9) & 0x3F) | (instr & 0x1C0); + int s12 = ((instr >> 9) & 0x3F) | (instr & 0x1C0) | ((instr & 0x30) << 5); + unsigned u6 = ((instr >> 6) & 0x7) | ((instr >> 9) & 0x38); + + // sign-extend + if (instr & 0x8000) { + s7 |= 0xFFFFFF80; + s9 |= 0xFFFFFF00; + s10 |= 0xFFFFFE00; + s12 |= 0xFFFFF800; + } + + while (*fmt) { + if (*fmt != '@') { + *buf++ = *fmt++; + continue; + } + switch (*++fmt) { + case 'A': + buf = append(buf, regname[a]); + break; + case 'B': + buf = append(buf, regname[b]); + break; + case 'C': + buf = append(buf, regname[c]); + break; + case 'F': + buf = append(buf, alufunc[f]); + break; + case '7': // si7 + buf = append_u16(buf, s7); + sprintf(note, "(%d)", s7); + break; + case '9': // si9 (pcrel) + buf = append_u16(buf, pc + s9 + 1); + sprintf(note, "(%d)", pc + s9 + 1); + break; + case '0': // si10 + buf = append_u16(buf, s10); + sprintf(note, "(%d)", s10); + break; + case '2': // si12 (pcrel) + buf = append_u16(buf, pc + s12 + 1); + sprintf(note, "(%d)", pc + s12 + 1); + break; + case 'h': // only used by mhi + buf = append_u16(buf, s7 & 0x3F); + sprintf(note, "(%d)", s7 & 0x3F); + break; + case 'u': // only used by LC/SC + buf = append_u16(buf, u6); + sprintf(note, "(%d)", u6); + case 0: + goto done; + } + fmt++; + } +done: + if (verbose && note[0]) { + while ((buf - start) < 22) *buf++ = ' '; + strcpy(buf, note); + buf += strlen(note); + } + *buf = 0; +} + +struct { + u16 mask; + u16 value; + const char *fmt; +} decode[] = { + { 0b1111000000000111, 0b0011000000000000, "NOT @C, @A" }, + { 0b1111111111000111, 0b0000000000000000, "MOV @C, @A" }, + { 0b1111111111000111, 0b0000001001000000, "MOV @C, @A" }, + { 0b1111111111000111, 0b0000010010000000, "MOV @C, @A" }, + { 0b1111111111000111, 0b0000011011000000, "MOV @C, @A" }, + { 0b1111111111000111, 0b0000100100000000, "MOV @C, @A" }, + { 0b1111111111000111, 0b0000101101000000, "MOV @C, @A" }, + { 0b1111111111000111, 0b0000110110000000, "MOV @C, @A" }, + { 0b1111111111000111, 0b0000111111000000, "MOV @C, @A" }, + { 0b0000000000000111, 0b0000000000000000, "@F @C, @A, @B" }, + { 0b0000000000000111, 0b0000000000000001, "ADD @C, @A, @7" }, + { 0b0000000000000111, 0b0000000000000010, "MOV @C, @0" }, + { 0b0000000000000111, 0b0000000000000011, "LW @C, [@A, @7]" }, + { 0b0000000100000111, 0b0000000000000100, "BNZ @C, @9" }, + { 0b0000000100000111, 0b0000000100000100, "BZ @C, @9" }, + { 0b0000000000000111, 0b0000000000000101, "SW @C, [@A, @7]" }, + { 0b0000000000001111, 0b0000000000000110, "B @2" }, + { 0b0000000000001111, 0b0000000000001110, "BL @2" }, + { 0b1000111000001111, 0b0000000000000111, "B @A" }, + { 0b1000111000001111, 0b0000000000001111, "BL @A" }, + { 0b1000111000000111, 0b0000001000000111, "NOP" }, + { 0b1000111000000111, 0b0000010000000111, "RSV0" }, + { 0b1000111000000111, 0b0000011000000111, "RSV1" }, + { 0b1000111000000111, 0b0000100000000111, "LC @c, @6" }, + { 0b1000111000000111, 0b0000101000000111, "SC @c, @6" }, + { 0b1111111000000111, 0b0000110000000111, "SHL @C, @A, 1" }, + { 0b1111111000000111, 0b0001110000000111, "SHR @C, @A, 1" }, + { 0b1111111000000111, 0b0010110000000111, "ROL @C, @A, 1" }, + { 0b1111111000000111, 0b0011110000000111, "ROR @C, @A, 1" }, + { 0b1111111000000111, 0b0000111000000111, "SHL @C, @A, 4" }, + { 0b1111111000000111, 0b0001111000000111, "SHR @C, @A, 4" }, + { 0b1111111000000111, 0b0010111000000111, "ROL @C, @A, 4" }, + { 0b1111111000000111, 0b0011111000000111, "ROR @C, @A, 4" }, + { 0b1000000000000111, 0b1000000000000111, "MHI @C, @A, @h" }, + { 0b0000000000000000, 0b0000000000000000, "UND" }, +}; + +static void disassemble0(char *buf, unsigned pc, unsigned instr, unsigned verbose) { + int n = 0; + for (n = 0 ;; n++) { + if ((instr & decode[n].mask) == decode[n].value) { + printinst(buf, pc, instr, decode[n].fmt, verbose); + return; + } + } + buf[0] = 0; +} + +void disassemble(char *buf, unsigned pc, unsigned instr) { + disassemble0(buf, pc, instr, 1); +} + +#ifdef STANDALONE +int main(int argc, char **argv) { + char buf[256]; + char line[1024]; + while (fgets(line, 1024, stdin)) { + unsigned insn = 0xFFFF; + unsigned pc = 0; + sscanf(line, "%04x%04x", &insn, &pc); + disassemble0(buf, pc, insn, 0); + printf("%s\n", buf); + fflush(stdout); + } + return 0; +} +#endif