aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri Sokolyuk <demon@dim13.org>2012-04-16 19:07:07 +0000
committerDimitri Sokolyuk <demon@dim13.org>2012-04-16 19:07:07 +0000
commitf3b513a6a37e0dd2c109d3e31bdcb35ab716c6eb (patch)
tree566f8f4523f57fbd88ee6bed6527fc62a61276bb
DCPU-16 Assembler and Emulator
-rw-r--r--Makefile10
-rw-r--r--dcpu16.h47
-rw-r--r--emu.c305
-rw-r--r--gramar.y280
-rw-r--r--lexer.l121
-rw-r--r--main.c93
-rw-r--r--tui.c172
7 files changed, 1028 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6021e10
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+# $Id$
+
+PROG= dcpu
+SRCS= gramar.y lexer.l emu.c main.c tui.c
+NOMAN=
+LDADD+= -lcurses
+#DEBUG= -Wall -ggdb -pg
+#YFLAGS= -dtv
+
+.include <bsd.prog.mk>
diff --git a/dcpu16.h b/dcpu16.h
new file mode 100644
index 0000000..646dd5c
--- /dev/null
+++ b/dcpu16.h
@@ -0,0 +1,47 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2012 Dimitri Sokolyuk <demon@dim13.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, dATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DCPU16_H
+#define _DCPU16_H
+
+enum { A, B, C, X, Y, Z, I, J, PC, SP, O, Aux, nReg };
+
+enum { EXT, SET, ADD, SUB, MUL, DIV, MOD, SHL,
+ SHR, AND, BOR, XOR, IFE, IFN, IFG, IFB, nOpt };
+
+enum { Res, JSR, BRK, nExt };
+
+#define MEMSZ 0x10000
+
+#define DISP 0x8000
+#define DISPEND 0x8180
+
+#define CHARS 0x8180
+#define CHAREND 0x8280
+
+#define MISC 0x8280 /* bg color */
+#define MISCEND 0x8281
+
+#define KEYB 0x9000
+#define KEYP 0x9010
+
+/* display: 32x12 (128x96) + 16 pixel boarder, font 8x4 */
+
+unsigned short *compile(FILE *);
+int step(unsigned short *, unsigned short *);
+
+#endif
diff --git a/emu.c b/emu.c
new file mode 100644
index 0000000..9a4d947
--- /dev/null
+++ b/emu.c
@@ -0,0 +1,305 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2012 Dimitri Sokolyuk <demon@dim13.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, dATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include "dcpu16.h"
+
+unsigned short *mem;
+unsigned short *reg;
+
+unsigned short skip = 0;
+unsigned short run = 1;
+
+void ext(unsigned short *a, unsigned short *b);
+void set(unsigned short *a, unsigned short *b);
+void add(unsigned short *a, unsigned short *b);
+void sub(unsigned short *a, unsigned short *b);
+void mul(unsigned short *a, unsigned short *b);
+void div(unsigned short *a, unsigned short *b);
+void mod(unsigned short *a, unsigned short *b);
+void shl(unsigned short *a, unsigned short *b);
+void shr(unsigned short *a, unsigned short *b);
+void and(unsigned short *a, unsigned short *b);
+void bor(unsigned short *a, unsigned short *b);
+void xor(unsigned short *a, unsigned short *b);
+void ife(unsigned short *a, unsigned short *b);
+void ifn(unsigned short *a, unsigned short *b);
+void ifg(unsigned short *a, unsigned short *b);
+void ifb(unsigned short *a, unsigned short *b);
+
+void (*op[nOpt])(unsigned short *a, unsigned short *b) = {
+ [EXT] = ext,
+ [SET] = set,
+ [ADD] = add,
+ [SUB] = sub,
+ [MUL] = mul,
+ [DIV] = div,
+ [MOD] = mod,
+ [SHL] = shl,
+ [SHR] = shr,
+ [AND] = and,
+ [BOR] = bor,
+ [XOR] = xor,
+ [IFE] = ife,
+ [IFN] = ifn,
+ [IFG] = ifg,
+ [IFB] = ifb,
+};
+
+void jsr(unsigned short *a);
+void brk(unsigned short *a);
+
+void (*extop[nExt])(unsigned short *a) = {
+ [Res] = brk, /* die on wrong opcode */
+ [JSR] = jsr,
+ [BRK] = brk,
+};
+
+void
+ext(unsigned short *a, unsigned short *b)
+{
+ extop[*a](b);
+}
+
+void
+set(unsigned short *a, unsigned short *b)
+{
+ *a = *b;
+}
+
+void
+add(unsigned short *a, unsigned short *b)
+{
+ int tmp = *a;
+
+ tmp += *b;
+ reg[O] = tmp > 0xFFFF;
+
+ *a = tmp;
+}
+
+void
+sub(unsigned short *a, unsigned short *b)
+{
+ int tmp = *a;
+
+ tmp -= *b;
+ reg[O] = tmp < 0;
+
+ *a = tmp;
+}
+
+void
+mul(unsigned short *a, unsigned short *b)
+{
+ int tmp = *a;
+
+ tmp *= *b;
+ reg[O] = tmp >> 16;
+
+ *a = tmp;
+}
+
+void
+div(unsigned short *a, unsigned short *b)
+{
+ int tmp = *a;
+
+ if (*b == 0) {
+ reg[O] = 0;
+ *a = 0;
+ } else {
+ reg[O] = ((tmp << 16) / *b);
+ *a /= *b;
+ }
+}
+
+void
+mod(unsigned short *a, unsigned short *b)
+{
+ if (*b == 0)
+ *a = 0;
+ else
+ *a %= *b;
+}
+
+void
+shl(unsigned short *a, unsigned short *b)
+{
+ int tmp = *a;
+
+ reg[O] = ((tmp << *b) >> 16);
+ *a <<= *b;
+}
+
+void
+shr(unsigned short *a, unsigned short *b)
+{
+ int tmp = *a;
+
+ reg[O] = ((tmp << 16) >> *b);
+ *a >>= *b;
+}
+
+void
+and(unsigned short *a, unsigned short *b)
+{
+ *a &= *b;
+}
+
+void
+bor(unsigned short *a, unsigned short *b)
+{
+ *a |= *b;
+}
+
+void
+xor(unsigned short *a, unsigned short *b)
+{
+ *a ^= *b;
+}
+
+void
+ife(unsigned short *a, unsigned short *b)
+{
+ skip = !(*a == *b);
+}
+
+void
+ifn(unsigned short *a, unsigned short *b)
+{
+ skip = !(*a != *b);
+}
+
+void
+ifg(unsigned short *a, unsigned short *b)
+{
+ skip = !(*a > *b);
+}
+
+void
+ifb(unsigned short *a, unsigned short *b)
+{
+ skip = !(*a & *b);
+}
+
+void
+jsr(unsigned short *a)
+{
+ mem[--reg[SP]] = reg[PC];
+ reg[PC] = *a;
+}
+
+void
+brk(unsigned short *a)
+{
+ run = 0;
+}
+
+unsigned short *
+fetcharg(int a)
+{
+ switch (a) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ /* register */
+ return &reg[a];
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x0e:
+ case 0x0f:
+ /* [register] */
+ return &mem[reg[a - 0x08]];
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ /* [next word + register] */
+ return &mem[mem[reg[PC]++] + reg[a - 0x10]];
+ case 0x18:
+ /* pop */
+ return &mem[reg[SP]++];
+ case 0x19:
+ /* peek */
+ return &mem[reg[SP]];
+ case 0x1a:
+ /* push */
+ return &mem[--reg[SP]];
+ case 0x1b:
+ /* SP */
+ return &reg[SP];
+ case 0x1c:
+ /* PC */
+ return &reg[PC];
+ case 0x1d:
+ /* O */
+ return &reg[O];
+ case 0x1e:
+ /* [next word] */
+ return &mem[mem[reg[PC]++]];
+ case 0x1f:
+ /* next word */
+ return &mem[reg[PC]++];
+ default:
+ /* literal */
+ reg[Aux] = a - 0x20;
+ return &reg[Aux];
+ }
+}
+
+int
+step(unsigned short *m, unsigned short *r)
+{
+ unsigned short c, o, *a, *b, s;
+
+ if (!run)
+ return -1;
+
+ mem = m;
+ reg = r;
+
+ c = mem[reg[PC]++];
+ s = reg[SP]; /* store SP */
+
+ o = c & 0x0f;
+ reg[Aux] = (c >> 4) & 0x3f;
+ /* don't fetch first arg for extended opcodes */
+ a = o ? fetcharg(reg[Aux]) : &reg[Aux];
+ b = fetcharg((c >> 10) & 0x3f);
+
+ if (skip) {
+ skip = 0;
+ reg[SP] = s; /* restore SP on skipped opcode */
+ } else
+ op[o](a, b);
+
+ return 0;
+}
diff --git a/gramar.y b/gramar.y
new file mode 100644
index 0000000..61fdd1a
--- /dev/null
+++ b/gramar.y
@@ -0,0 +1,280 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2012 Dimitri Sokolyuk <demon@dim13.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, dATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <sys/queue.h>
+#include <stdio.h>
+#include <string.h>
+#include "dcpu16.h"
+
+extern int yylineno;
+extern FILE *yyin;
+int yylex(void);
+int yyparse(void);
+
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+struct {
+ unsigned short val;
+ char *label;
+} stack[0x100], ref[0x100];
+
+int sp = 0;
+int rp = 0;
+int pc = 0;
+
+unsigned short buffer[MEMSZ];
+char *label[MEMSZ];
+
+%}
+
+%union {
+ int ival;
+ char *sval;
+};
+
+%token A B C X Y Z I J
+%token POP PEEK PUSH SP PC O
+%token SET ADD SUB MUL DIV MOD SHL SHR AND BOR XOR IFE IFN IFG IFB
+%token DAT JSR BRK ORG JMP
+%token LBR RBR LBRACE RBRACE LPAR RPAR
+%token COMMA DP PLUS MINUS MULT
+%token DOT HASH MACRO INCLUDE
+%token <ival> NUMBER
+%token <sval> STRING QSTRING
+
+%type <ival> register opcode extended value expr
+
+%left PLUS MINUS
+%left MULT
+
+%%
+
+prog
+ : /* empty */
+ | prog statement
+ ;
+
+statement
+ : opcode value COMMA value
+ {
+ popop(($4 << 10) | ($2 << 4) | $1);
+ popall();
+ }
+ | opcode value
+ {
+ popop(($2 << 10) | $1);
+ popall();
+ }
+ | opcode { popop($1); }
+ | DP STRING { addref($2); }
+ | DAT data { popall(); }
+ | ORG expr { pc = $2; }
+ ;
+
+data
+ : /* empty */
+ | data block
+ | data COMMA block
+ ;
+
+expr
+ : NUMBER { $$ = $1; }
+ | expr PLUS expr { $$ = $1 + $3; }
+ | expr MINUS expr { $$ = $1 - $3; }
+ | expr MULT expr { $$ = $1 * $3; }
+ | LPAR expr RPAR { $$ = $2; }
+ ;
+
+block
+ : QSTRING
+ {
+ char *s = $1;
+ while (*s)
+ push(*s++, NULL);
+ }
+ | STRING { push(0, $1); }
+ | expr { push($1, NULL); }
+ ;
+
+value
+ : register { $$ = $1; }
+ | LBR register RBR { $$ = 0x08 + $2; }
+ | POP { $$ = 0x18; }
+ | PEEK { $$ = 0x19; }
+ | PUSH { $$ = 0x1a; }
+ | SP { $$ = 0x1b; }
+ | PC { $$ = 0x1c; }
+ | O { $$ = 0x1d; }
+ | LBR expr RBR
+ {
+ $$ = 0x1e;
+ push($2, NULL);
+ }
+ | LBR STRING RBR
+ {
+ $$ = 0x1e;
+ push(0, $2);
+ }
+ | LBR expr PLUS register RBR
+ {
+ $$ = 0x10 + $4;
+ push($2, NULL);
+ }
+ | LBR register PLUS expr RBR
+ {
+ $$ = 0x10 + $2;
+ push($4, NULL);
+ }
+ | LBR STRING PLUS register RBR
+ {
+ $$ = 0x10 + $4;
+ push(0, $2);
+ }
+ | LBR register PLUS STRING RBR
+ {
+ $$ = 0x10 + $2;
+ push(0, $4);
+ }
+ | expr
+ {
+ if ($1 < 0x20)
+ $$ = 0x20 + $1;
+ else {
+ $$ = 0x1f;
+ push($1, NULL);
+ }
+ }
+ | STRING
+ {
+ $$ = 0x1f;
+ push(0, $1);
+ }
+ ;
+
+register
+ : A { $$ = 0x00; }
+ | B { $$ = 0x01; }
+ | C { $$ = 0x02; }
+ | X { $$ = 0x03; }
+ | Y { $$ = 0x04; }
+ | Z { $$ = 0x05; }
+ | I { $$ = 0x06; }
+ | J { $$ = 0x07; }
+ ;
+
+opcode
+ : extended { $$ = $1 << 4; }
+ | SET { $$ = 0x01; }
+ | ADD { $$ = 0x02; }
+ | SUB { $$ = 0x03; }
+ | MUL { $$ = 0x04; }
+ | DIV { $$ = 0x05; }
+ | MOD { $$ = 0x06; }
+ | SHL { $$ = 0x07; }
+ | SHR { $$ = 0x08; }
+ | AND { $$ = 0x09; }
+ | BOR { $$ = 0x0a; }
+ | XOR { $$ = 0x0b; }
+ | IFE { $$ = 0x0c; }
+ | IFN { $$ = 0x0d; }
+ | IFG { $$ = 0x0e; }
+ | IFB { $$ = 0x0f; }
+ ;
+
+extended
+ : JSR { $$ = 0x01; }
+ | BRK { $$ = 0x02; }
+ ;
+
+
+%%
+
+void
+yyerror(const char *s)
+{
+ fprintf(stderr, "Line %d: %s\n", yylineno, s);
+ exit(1);
+}
+
+void
+push(int i, char *s)
+{
+ stack[sp].val = i;
+ stack[sp++].label = s;
+}
+
+void
+popop(int n)
+{
+ buffer[pc++] = n;
+}
+
+void
+popall(void)
+{
+ int n = sp;
+
+ while (sp > 0) {
+ buffer[pc] = stack[n - sp].val;
+ label[pc++] = stack[n - sp--].label;
+ }
+}
+
+void
+addref(char *s)
+{
+ ref[rp].label = s;
+ ref[rp++].val = pc;
+}
+
+int
+findref(char *s)
+{
+ int i;
+
+ for (i = 0; i < rp; i++)
+ if (strcmp(ref[i].label, s) == 0)
+ return ref[i].val;
+
+ return 0;
+}
+
+void
+restorerefs(void)
+{
+ int i;
+
+ for (i = 0; i < pc; i++)
+ if (label[i])
+ buffer[i] = findref(label[i]);
+}
+
+unsigned short *
+compile(FILE *fd)
+{
+#if YYDEBUG
+ yydebug = 1;
+#endif
+ yyin = fd;
+ yyparse();
+ restorerefs();
+
+ return buffer;
+}
diff --git a/lexer.l b/lexer.l
new file mode 100644
index 0000000..f157edf
--- /dev/null
+++ b/lexer.l
@@ -0,0 +1,121 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2012 Dimitri Sokolyuk <demon@dim13.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "y.tab.h"
+%}
+
+%option yylineno
+
+%x COMMENT QUOTED
+
+WS [ \t\r\n]+
+STR [A-Za-z0-9_]+
+HEX 0x[0-9A-Fa-f]+
+OCT 0[0-7]+
+DEC [0-9]+
+
+%%
+
+[Ss][Ee][Tt] return SET;
+[Aa][Dd][Dd] return ADD;
+[Ss][Uu][Bb] return SUB;
+[Mm][Uu][Ll] return MUL;
+[Dd][Ii][Vv] return DIV;
+[Mm][Oo][Dd] return MOD;
+[Ss][Hh][Ll] return SHL;
+[Ss][Hh][Rr] return SHR;
+[Aa][Nn][Dd] return AND;
+[Bb][Oo][Rr] return BOR;
+[Xx][Oo][Rr] return XOR;
+[Ii][Ff][Ee] return IFE;
+[Ii][Ff][Nn] return IFN;
+[Ii][Ff][Gg] return IFG;
+[Ii][Ff][Bb] return IFB;
+
+[Jj][Ss][Rr] return JSR;
+[Bb][Rr][Kk] return BRK;
+[Dd][Aa][Tt] return DAT;
+[Oo][Rr][Gg] return ORG;
+[Jj][Mm][Pp] return JMP;
+
+[Pp][Oo][Pp] return POP;
+[Pp][Ee][Ee][Kk] return PEEK;
+[Pp][Uu][Ss][Hh] return PUSH;
+[Ss][Pp] return SP;
+[Pp][Cc] return PC;
+[Oo] return O;
+
+[Aa] return A;
+[Bb] return B;
+[Cc] return C;
+[Xx] return X;
+[Yy] return Y;
+[Zz] return Z;
+[Ii] return I;
+[Jj] return J;
+
+"+" return PLUS;
+"-" return MINUS;
+"*" return MULT;
+"," return COMMA;
+":" return DP;
+"[" return LBR;
+"]" return RBR;
+"(" return LPAR;
+")" return RPAR;
+"{" return LBRACE;
+"}" return RBRACE;
+"#" return HASH;
+"." return DOT;
+
+[Mm][Aa][Cc][Rr][Oo] return MACRO;
+[Ii][Nn][Cc][Ll][Uu][Dd][Ee] return INCLUDE;
+
+";" BEGIN(COMMENT);
+<COMMENT>[\r\n]+ BEGIN(INITIAL);
+<COMMENT>. /* eat comments */
+
+"\"" BEGIN(QUOTED);
+<QUOTED>[^\"]+ {
+ yylval.sval = strdup(yytext);
+ return QSTRING;
+ }
+<QUOTED>"\"" BEGIN(INITIAL);
+
+{HEX}|{OCT}|{DEC} {
+ yylval.ival = strtol(yytext, (char **)NULL, 0);
+ return NUMBER;
+ }
+
+{STR} {
+ yylval.sval = strdup(yytext);
+ return STRING;
+ }
+
+{WS} /* eat whitespace */
+
+%%
+
+int
+yywrap(void)
+{
+ return 1;
+}
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..35a7411
--- /dev/null
+++ b/main.c
@@ -0,0 +1,93 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2012 Dimitri Sokolyuk <demon@dim13.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, dATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "dcpu16.h"
+
+void
+dumpcode(unsigned short *m)
+{
+ int i, k, sum;
+
+ for (i = 0; i < MEMSZ; i += 8) {
+ sum = 0;
+ for (k = 0; k < 8; k++)
+ sum += m[i + k];
+ if (!sum)
+ continue;
+ printf("%4.4x:", i);
+ for (k = 0; k < 8; k++)
+ printf("%5.4x", m[i + k]);
+ printf("\n");
+ }
+}
+
+void
+usage(void)
+{
+ extern char *__progname;
+
+ (void)fprintf(stderr, "Usage: %s [-gt]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ unsigned short *m;
+ unsigned short r[nReg] = { 0 };
+ FILE *fd;
+ int e_flag = 0;
+ int g_flag = 0;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "gt")) != -1)
+ switch (ch) {
+ case 'g':
+ g_flag = 1;
+ break;
+ case 't':
+ e_flag = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (!argc)
+ usage();
+
+ fd = fopen(*argv, "r");
+ if (!fd)
+ err(1, "cannot open file");
+
+ m = compile(fd);
+ fclose(fd);
+
+ if (e_flag)
+ tuiemu(m, r);
+ else
+ dumpcode(m);
+
+ return 0;
+}
diff --git a/tui.c b/tui.c
new file mode 100644
index 0000000..3a61c90
--- /dev/null
+++ b/tui.c
@@ -0,0 +1,172 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2012 Dimitri Sokolyuk <demon@dim13.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, dATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <curses.h>
+#include <unistd.h>
+#include "dcpu16.h"
+
+WINDOW *outbox, *out, *regbox, *regs, *code;
+
+void
+dumpmem(unsigned short *m)
+{
+ int i, k, sum, lines = 0;
+
+ wmove(code, 0, 0);
+
+ for (i = 0; i < MEMSZ; i += 8) {
+ if (++lines > 24)
+ break;
+ for (sum = 0, k = 0; k < 8; k++)
+ sum += m[i + k];
+ if (!sum)
+ continue;
+ wprintw(code, "%4.4x:", i);
+ for (k = 0; k < 8; k++)
+ wprintw(code, "%5.4x", m[i + k]);
+ wprintw(code, "\n");
+ }
+
+ wnoutrefresh(code);
+}
+
+void
+dumpdisp(unsigned short *m)
+{
+ int i;
+ char c, b, hbg, bg, hfg, fg, col;
+
+ wmove(out, 0, 0);
+
+ for (i = DISP; i < DISPEND; i++) {
+ c = m[i] & 0x7f;
+
+ b = (m[i] >> 7) & 0x01;
+ bg = (m[i] >> 8) & 0x07;
+ hbg = (m[i] >> 11) & 0x01;
+ fg = (m[i] >> 12) & 0x07;
+ hfg = (m[i] >> 15) & 0x01;
+
+ col = (fg << 3) | bg;
+
+ if (!(isascii(c) && isprint(c)))
+ c = ' ';
+
+ if (b)
+ wattron(out, A_BLINK);
+ if (hfg)
+ wattron(out, A_BOLD);
+ wattron(out, COLOR_PAIR(col));
+ waddch(out, c);
+ wattroff(out, COLOR_PAIR(col));
+ if (hfg)
+ wattroff(out, A_BOLD);
+ if (b)
+ wattroff(out, A_BLINK);
+ }
+
+ wnoutrefresh(out);
+}
+
+void
+dumpreg(unsigned short *mem, unsigned short *reg)
+{
+ wmove(regs, 0, 0);
+
+ mvwprintw(regs, 0, 0, " A: %4.4x [%4.4x]", reg[A], mem[reg[A]]);
+ mvwprintw(regs, 1, 0, " B: %4.4x [%4.4x]", reg[B], mem[reg[B]]);
+ mvwprintw(regs, 2, 0, " C: %4.4x [%4.4x]", reg[C], mem[reg[C]]);
+
+ mvwprintw(regs, 3, 0, " X: %4.4x [%4.4x]", reg[X], mem[reg[X]]);
+ mvwprintw(regs, 4, 0, " Y: %4.4x [%4.4x]", reg[Y], mem[reg[Y]]);
+ mvwprintw(regs, 5, 0, " Z: %4.4x [%4.4x]", reg[Z], mem[reg[Z]]);
+
+ mvwprintw(regs, 6, 0, " I: %4.4x [%4.4x]", reg[I], mem[reg[I]]);
+ mvwprintw(regs, 7, 0, " J: %4.4x [%4.4x]", reg[J], mem[reg[J]]);
+
+ mvwprintw(regs, 0, 16, "PC: %4.4x [%4.4x]", reg[PC], mem[reg[PC]]);
+ mvwprintw(regs, 1, 16, "SP: %4.4x [%4.4x]", reg[SP], mem[reg[SP]]);
+ mvwprintw(regs, 2, 16, " O: %4.4x [%4.4x]", reg[O], mem[reg[O]]);
+
+ wnoutrefresh(regs);
+}
+
+void
+init_colors()
+{
+ int i, j;
+ int I, J;
+
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++) {
+ /* Notch is jerk, why RGB istead of BGR??? */
+ I = (i >> 2) | (i & 0x02) | ((i << 2) & 0x04);
+ J = (j >> 2) | (j & 0x02) | ((j << 2) & 0x04);
+ init_pair((i << 3) | j, I, J);
+ }
+}
+
+void
+tuiemu(unsigned short *m, unsigned short *r)
+{
+ int ch;
+
+ initscr();
+
+ noecho();
+ nodelay(stdscr, TRUE);
+
+ start_color();
+ init_colors();
+
+ outbox = derwin(stdscr, 14, 34, 0, 46);
+ out = derwin(outbox, 12, 32, 1, 1);
+ box(outbox, 0, 0);
+ wnoutrefresh(outbox);
+
+ regbox = derwin(stdscr, 10, 34, 14, 46);
+ regs = derwin(regbox, 8, 32, 1, 1);
+ box(regbox, 0, 0);
+ wnoutrefresh(regbox);
+
+ code = derwin(stdscr, 24, 46, 0, 0);
+
+ m[KEYP] = KEYB;
+
+ while (step(m, r) != -1) {
+ dumpmem(m);
+ dumpdisp(m);
+ dumpreg(m, r);
+ if ((ch = wgetch(stdscr)) != ERR) {
+ m[m[KEYP]] = ch;
+ m[KEYP] = KEYB + (m[KEYP] + 1) % 0x10;
+ }
+ doupdate();
+ }
+
+ delwin(code);
+ delwin(regs);
+ delwin(regbox);
+ delwin(out);
+ delwin(outbox);
+
+ endwin();
+}