diff --git a/.gitignore b/.gitignore index 7478875..bf48e85 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ cscope.out *.bin *.hex *.o +a16 +dcpu diff --git a/assembler.c b/assembler.c index 4ef8760..59673d3 100644 --- a/assembler.c +++ b/assembler.c @@ -64,6 +64,7 @@ enum outformat { OUTFORMAT_PRETTY, OUTFORMAT_HEX, OUTFORMAT_BINARY, + OUTFORMAT_C, }; void die(const char *fmt, ...) { @@ -149,7 +150,9 @@ enum tokens { tR0, tR1, tR2, tR3, tR4, tR5, tR6, tR7, tSET, tADD, tSUB, tMUL, tDIV, tMOD, tSHL, tSHR, tAND, tBOR, tXOR, tIFE, tIFN, tIFG, tIFB, - tJSR, + tJSR, tR8, tR9, tR10, tR11, tR12, tR13, tINT, + tIAG, tIAS, tRFI, tIAQ, tR14, tR15, tR16, + tHWN, tHWQ, tHWI, tPOP, tPEEK, tPUSH, tSP, tPC, tO, tJMP, tMOV, tNOP, tDATA, tDAT, tDW, tWORD, @@ -161,7 +164,9 @@ static const char *tnames[] = { "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "SET", "ADD", "SUB", "MUL", "DIV", "MOD", "SHL", "SHR", "AND", "BOR", "XOR", "IFE", "IFN", "IFG", "IFB", - "JSR", + "JSR", "R8", "R9", "R10", "R11", "R12", "R13", "INT", + "IAG", "IAS", "RFI", "IAQ", "R14", "R15", "R16", + "HWN", "HWQ", "HWI", "POP", "PEEK", "PUSH", "SP", "PC", "O", "JMP", "MOV", "NOP", "DATA", "DAT", "DW", "WORD", @@ -275,7 +280,7 @@ void assemble_imm_or_label(void) { } int assemble_operand(void) { - u16 n; + u16 n = 0; next(); switch (token) { @@ -353,6 +358,14 @@ int assemble_operand(void) { return 0; } +void assemble_unop(void) { + int pc = PC++; + int a; + int op = token - (tJSR - 1); + a = assemble_operand(); + image[pc] = (op << 4) | (a << 10); +} + void assemble_binop(void) { u16 pc = PC++; int a, b; @@ -389,7 +402,6 @@ void assemble_jump(void) { } void assemble(const char *fn) { - u16 pc, n; fin = fopen(fn, "r"); filename = fn; linenumber = 0; @@ -424,10 +436,10 @@ void assemble(const char *fn) { case tPUSH: case tPOP: case tNOP: assemble_binop(); continue; - case tJSR: - pc = PC++; - n = assemble_operand(); - image[pc] = (n << 10) | 0x0010; + case tJSR: case tINT: case tIAG: case tIAS: + case tRFI: case tIAQ: case tHWN: case tHWQ: + case tHWI: + assemble_unop(); continue; default: die("unexpected: %s", tnames[token]); @@ -437,6 +449,8 @@ void assemble(const char *fn) { fclose(fin); } +#ifndef A16_EMBEDDED + void emit(const char *fn, enum outformat format) { FILE *fp; u16 *pc = image; @@ -469,6 +483,17 @@ void emit(const char *fn, enum outformat format) { } else if (format == OUTFORMAT_BINARY) { /* XXX handle host endian */ fwrite(pc, sizeof(*pc), 1, fp); + } else if (format == OUTFORMAT_C) { + if (note[pc-image] == 'd') { + fprintf(fp, "0x%04x,\n", *pc); + dis = pc + 1; + } else if (pc == dis) { + char out[128]; + dis = disassemble(pc, out); + fprintf(fp, "0x%04x,\t// 0x%04x:\t%s\n", *pc, (unsigned)(pc-image), out); + } else { + fprintf(fp, "0x%04x, \n", *pc); + } } pc++; } @@ -515,6 +540,8 @@ int main(int argc, char **argv) { oformat = OUTFORMAT_HEX; } else if (!strcasecmp(optarg, "pretty")) { oformat = OUTFORMAT_PRETTY; + } else if (!strcasecmp(optarg, "c")) { + oformat = OUTFORMAT_C; } else { usage(argc, argv); return 1; @@ -547,4 +574,5 @@ int main(int argc, char **argv) { } return 0; } +#endif diff --git a/dcpu.c b/dcpu.c index 859f6df..db951c5 100644 --- a/dcpu.c +++ b/dcpu.c @@ -38,16 +38,16 @@ extern u16 *disassemble(u16 *pc, char *out); void dumpheader(void) { fprintf(stderr, - "PC SP OV A B C X Y Z I J Instruction\n" - "---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -----------\n"); + "PC SP OV IAQ A B C X Y Z I J Instruction\n" + "---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -----------\n"); } void dumpstate(struct dcpu *d) { char out[128]; disassemble(d->m + d->pc, out); fprintf(stderr, - "%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %s\n", - d->pc, d->sp, d->ov, + "%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %s\n", + d->pc, d->sp, d->ov, d->iaq_ind, d->r[0], d->r[1], d->r[2], d->r[3], d->r[4], d->r[5], d->r[6], d->r[7], out); diff --git a/disassemble.c b/disassemble.c index f5c836a..4aa4af8 100644 --- a/disassemble.c +++ b/disassemble.c @@ -42,6 +42,13 @@ static const char *opcode[] = { "XXX", "MOV", "ADD", "SUB", "MUL", "DIV", "MOD", "SHL", "SHR", "AND", "BOR", "XOR", "IFE", "IFN", "IFG", "IFB", }; + +static const char *extended_opcode[] = { + "YYY", "JSR", "RS1", "RS2", "RS3", "RS4", "RS5", "RS6", + "INT", "IAG", "IAS", "RFI", "IAQ", "RS7", "RS8", "RS9", + "HWN", "HWQ", "HWI", +}; + static const char regs[8] = "ABCXYZIJ"; static u16 *dis_operand(u16 *pc, u16 n, char *out) { @@ -82,8 +89,8 @@ u16 *disassemble(u16 *pc, char *out) { pc = dis_operand(pc, b, out+strlen(out)); return pc; } - if (a == 1) { - sprintf(out,"JSR "); + if (a > 0 && a <= 0x12) { + sprintf(out,"%s ",extended_opcode[a]); pc = dis_operand(pc, b, out+strlen(out)); return pc; } diff --git a/emulator.c b/emulator.c index bd944e0..cfc8a7c 100644 --- a/emulator.c +++ b/emulator.c @@ -34,12 +34,12 @@ #include #include #include +#include #include #include #include "emulator.h" -extern u16 *disassemble(u16 *pc, char *out); static u16 lit[0x20] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -93,12 +93,74 @@ void dcpu_skip(struct dcpu *d) { d->pc += skiptable[(op >> 4) & 31]; } +void dcpu_interrupt(struct dcpu *d, u16 src) { + // we assume we are not called with a dequed interrupt when qeueing is on... + if (d->iaq_en) { + if (d->iaq_ind < 256) { + d->iaq[d->iaq_ind++] = src; + } + else { + printf("iaq overflow src=0x%04x\n", src); + } + } else { + d->m[--(d->sp)] = d->r[0]; + d->m[--(d->sp)] = d->pc; + d->pc = d->ia; + d->r[0] = src; + d->iaq_en = true; + } +} + +int dcpu_add_module(struct dcpu *d, struct module *m) { + int index = d->module_count; + + if (index >= MAX_MODULES) { + printf("Out of modules!\n"); + return -1; + } + + d->module_count++; + d->modules[index] = m; + + return index; +} + +void dcpu_start_modules(struct dcpu *d) { + for (int i = 0; i < d->module_count; i++) { + d->modules[i]->start(d, d->modules[i]); + } +} + +void dcpu_stop_modules(struct dcpu *d) { + for (int i = 0; i < d->module_count; i++) { + d->modules[i]->stop(d, d->modules[i]); + } +} + +void dcpu_idle_modules(struct dcpu *d) { + for (int i = 0; i < d->module_count; i++) { + d->modules[i]->idle(d, d->modules[i]); + } +} + void dcpu_step(struct dcpu *d) { - u16 op = d->m[d->pc++]; + u16 op; u16 dst; u32 res; u16 a, b, *aa; + if (d->stop) { + return; + } + + // if iaq is off, deal with any queued interrupts (max 1 per instruction) + if (!d->iaq_en && d->iaq_ind > 0) { + dcpu_interrupt(d, d->iaq[--d->iaq_ind]); + return; + } + + op = d->m[d->pc++]; + if ((op & 0xF) == 0) goto extended; aa = dcpu_opr(d, dst = (op >> 4) & 0x3F); @@ -107,7 +169,7 @@ void dcpu_step(struct dcpu *d) { switch (op & 0xF) { case 0x1: res = b; break; - case 0x2: res = a + b; d->ov = res >> 16; break; + case 0x2: res = a + b; d->ov = res >> 16; break; case 0x3: res = a - b; d->ov = res >> 16; break; case 0x4: res = a * b; d->ov = res >> 16; break; case 0x5: if (b) { res = a / b; } else { res = 0; } d->ov = res >> 16; break; @@ -127,14 +189,62 @@ void dcpu_step(struct dcpu *d) { return; extended: - a = *dcpu_opr(d, op >> 10); + aa = dcpu_opr(d, op >> 10); + a = *aa; switch ((op >> 4) & 0x3F) { case 0x01: d->m[--(d->sp)] = d->pc; d->pc = a; return; + case 0x08: + if (d->ia) { + dcpu_interrupt(d, a); + } + return; + case 0x09: + *aa = d->ia; + return; + case 0x0a: + d->ia = a; + return; + case 0x0b: + d->iaq_en = false; + d->pc = d->m[d->sp++]; + d->r[0] = d->m[d->sp++]; + return; + case 0x0c: + d->iaq_en = a != 0; + return; + case 0x10: + *aa = d->module_count; + return; + case 0x11: + if (a >= d->module_count) { + printf("invalid module index: %d\n", a); + d->r[0] = 0; + d->r[1] = 0; + d->r[2] = 0; + d->r[3] = 0; + d->r[4] = 0; + } else { + d->modules[a]->hwq(d, d->modules[a]); + } + return; + case 0x12: + if (a >= d->module_count) { + printf("invalid module index: %d\n", a); + d->r[0] = 0; + d->r[1] = 0; + d->r[2] = 0; + d->r[3] = 0; + d->r[4] = 0; + } else { + d->modules[a]->hwi(d, d->modules[a]); + } + return; default: fprintf(stderr, "< ILLEGAL OPCODE >\n"); - exit(0); + d->stop = true; + return; } } diff --git a/emulator.h b/emulator.h index 5c93641..0f2c0d5 100644 --- a/emulator.h +++ b/emulator.h @@ -29,19 +29,53 @@ #ifndef _DCPU_H_ #define _DCPU_H_ +#ifdef __cplusplus +extern "C" { +#endif + typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; +#ifndef DCPU_WORDS +#define DCPU_WORDS 65536 +#endif + +#define MAX_MODULES 16 + +struct dcpu; + +struct module { + void (*hwq)(struct dcpu * d, struct module * module); + void (*hwi)(struct dcpu * d, struct module * module); + void (*start)(struct dcpu * d, struct module * module); + void (*stop)(struct dcpu * d, struct module * module); + void (*idle)(struct dcpu * d, struct module * module); +}; + struct dcpu { u16 r[8]; u16 pc; u16 sp; u16 ov; - u16 unused; - u16 m[65536]; + u16 stop; + u16 ia; + u16 iaq_en; + u16 iaq_ind; + u16 iaq[256]; + u16 module_count; + u16 m[DCPU_WORDS]; + struct module * modules[MAX_MODULES]; }; +int dcpu_add_module(struct dcpu *d, struct module *m); +void dcpu_start_modules(struct dcpu *d); +void dcpu_stop_modules(struct dcpu *d); + void dcpu_step(struct dcpu *d); +#ifdef __cplusplus +}; +#endif + #endif diff --git a/tests/compass.s b/tests/compass.s new file mode 100644 index 0000000..180baec --- /dev/null +++ b/tests/compass.s @@ -0,0 +1,121 @@ + + JSR initMotors + + ; turn left until we face "North" +:goNorth + JSR isNorth + IFE A, 1 + SET PC, goWest + JSR turnLeft + SET PC, goNorth + +:goWest + JSR stop + JSR isWest + +:end + WORD 0xeee0 + +:isNorth + ; north is defined as X >= 300 && y <= -300 + ; unsigned comparisons though! + SET A, 0 + HWI [nav_mod] + IFG 300, A + SET PC, notDir + SET A, 1 + HWI [nav_mod] + IFG A, -300 + SET PC, notDir + SET PC, isDir + +:isWest + ; west is defined as X >= 300 && -100 < y < 100 + SET A, 0 + HWI [nav_mod] + IFG 300, A + SET PC, notDir + SET A, 1 + HWI [nav_mod] + IFG -100, A ; A < -100 + SET PC, notDir + ; check for A < 0 + IFG A, 0x7FFF + SET PC, isDir + IFG A, 100 ; A > 100 + SET PC, notDir + SET PC, isDir + +:isDir + SET A, 1 + SET PC, POP + +:notDir + SET A, 0 + SET PC, POP + +; Motor Functions +; IO7: Right Motor Direction +; IO8: Left Motor Direction +; IO9: Right Motor Power +; IO10: Left Motor Direction + +:initMotors +; all forward and no power + ; clear power + SET A, 3 + SET B, 0x0600 + HWI 1 + ; set direction - forward + SET A, 2 + SET B, 0x0180 + HWI 1 + ; output all + SET A, 1 + SET B, 0x0780 + HWI 1 + SET PC, POP + +:goForwards + ; set direction - forward + SET A, 2 + SET B, 0x0180 + HWI 1 + ; set power + SET A, 2 + SET B, 0x0600 + HWI 1 + SET PC, POP + +:goBackwards + SET PC, POP + +:turnLeft + ; set right forward, left back + SET A, 2 + SET B, 0x0100 + HWI 1 + SET A, 3 + SET B, 0x0080 + HWI 1 + ; set power + SET A, 2 + SET B, 0x0600 + HWI 1 + SET PC, POP + +:turnRight + SET PC, POP + +:stop + ; clear power + SET A, 3 + SET B, 0x0600 + HWI 1 + SET PC, POP + +:hello + DAT "Hello World\n", 0 + +:nav_mod + DAT 2 diff --git a/tests/hwTest.s b/tests/hwTest.s new file mode 100644 index 0000000..013040a --- /dev/null +++ b/tests/hwTest.s @@ -0,0 +1,123 @@ + +; query number of HW devices + SET A, 0x30 + JSR countHW + JSR initMotors + +; query X mag sensor + SET A, 0x00 + HWI [nav_mod] + SET A, 0x01 + HWI [nav_mod] + SET A, 0x02 + HWI [nav_mod] + + SET A, 0x06 + INT 0x3 + IAS intHandler + INT 0x3 + IAQ 1 + INT 0x3 + SET C, 0 + IAQ C + MOV A, 0x2 + INT 0x3 + SET A, 1 + IAG B + SET A, 1 + SET B, 2 + SET C, 3 + SET X, 4 + SET Y, 5 + SET Z, 6 + SET I, 7 + SET J, 8 + HWQ 0 + HWQ 1 + HWQ 2 + JSR goForwards + SET A, hello + JSR printString + JSR stop +:end + WORD 0xeee0 + +:countHW + HWN A + SET PC, POP + +:intHandler + SET A, 0x31 + RFI 0 + +:printString + SET PUSH, B + + SET B, A +:nextChar + SET A, [B] + IFE A, 0 + SET PC, loopDone + ADD B, 1 + HWI 0 + SET PC, nextChar + +:loopDone + SET B, POP + SET PC, POP + +; Motor Functions +; IO7: Right Motor Direction +; IO8: Left Motor Direction +; IO9: Right Motor Power +; IO10: Left Motor Direction + +:initMotors +; all forward and no power + ; clear power + SET A, 3 + SET B, 0x0600 + HWI 1 + ; set direction - forward + SET A, 2 + SET B, 0x0180 + HWI 1 + ; output all + SET A, 1 + SET B, 0x0780 + HWI 1 + SET PC, POP + +:goForwards + ; set direction - forward + SET A, 2 + SET B, 0x0180 + HWI 1 + ; set power + SET A, 2 + SET B, 0x0600 + HWI 1 + SET PC, POP + +; FIXME: Implement these... (dummy change to verify github authentication working again...) +:goBackwards + SET PC, POP + +:turnLeft + SET PC, POP + +:turnRight + SET PC, POP + +:stop + ; clear power + SET A, 3 + SET B, 0x0600 + HWI 1 + SET PC, POP + +:hello + DAT "Hello World\n", 0 + +:nav_mod + DAT 2 diff --git a/tests/ledTest.s b/tests/ledTest.s new file mode 100644 index 0000000..19dfffd --- /dev/null +++ b/tests/ledTest.s @@ -0,0 +1,52 @@ + + ; set + JSR initLED + + JSR setLED + JSR delay5 + JSR clearLED + JSR delay5 + JSR setLED + JSR delay5 + JSR clearLED + JSR delay5 + JSR setLED + JSR delay5 + JSR clearLED + JSR delay5 + JSR setLED + JSR delay5 + JSR clearLED + JSR delay5 + JSR setLED + JSR delay5 + JSR clearLED + WORD 0xeee0 + +:delay5 + SET A, 10 +:delay + IFE A, 0 + SET PC, POP + SUB A, 1 + set PC, delay + +:setLED + SET A, 2 + SET B, 0x4000 ; D14 + HWI 1 + set PC, POP + +:clearLED + SET A, 3 + SET B, 0x4000 ; D14 + HWI 1 + set PC, POP + +:initLED + JSR clearLED + ; output + SET A, 1 + SET B, 0x4000 ; D14 + HWI 1 + set PC, POP