Skip to content
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@

CFLAGS := -std=c99 -Wall -Wextra
CC ?= gcc

all: dcpu a16

dcpu: emulator.c disassemble.c
gcc -Wall -o dcpu emulator.c disassemble.c
$(CC) $(CFLAGS) -o $@ emulator.c disassemble.c

a16: assembler.c disassemble.c
gcc -Wall -o a16 assembler.c disassemble.c
$(CC) $(CFLAGS) -o $@ assembler.c disassemble.c

clean:
rm -f dcpu a16
69 changes: 60 additions & 9 deletions assembler.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <strings.h>
#include <getopt.h>

typedef uint16_t u16;
Expand Down Expand Up @@ -145,43 +146,43 @@ enum tokens {
tXXX, tSET, tADD, tSUB, tMUL, tDIV, tMOD, tSHL,
tSHR, tAND, tBOR, tXOR, tIFE, tIFN, tIFG, tIFB,
tJSR,
tPOP, tPEEK, tPUSH, tSP, tPC, tO,
tPOP, tPEEK, tPUSH, tSP, tPC, tO, tDAT,
tWORD,
tCOMMA, tOBRACK, tCBRACK, tCOLON, tPLUS,
tSTRING, tNUMBER, tEOF,
tCOMMA, tOBRACK, tCBRACK, tCOLON, tPLUS, tQUOTE,
tSTRING, tDATA, tNUMBER, tEOL, tEOF,
};
static const char *tnames[] = {
"A", "B", "C", "X", "Y", "Z", "I", "J",
"XXX", "SET", "ADD", "SUB", "MUL", "DIV", "MOD", "SHL",
"SHR", "AND", "BOR", "XOR", "IFE", "IFN", "IFG", "IFB",
"JSR",
"POP", "PEEK", "PUSH", "SP", "PC", "O",
"POP", "PEEK", "PUSH", "SP", "PC", "O", "DAT",
"WORD",
",", "[", "]", ":", "+",
"<STRING>", "<NUMBER>", "<EOF>",
",", "[", "]", ":", "+", "\"",
"<STRING>", "<DATA>", "<NUMBER>", "<EOL>", "<EOF>",
};
#define LASTKEYWORD tWORD

int _next(void) {
char c;
nextline:
if (!*lineptr) {
if (feof(fin)) return tEOF;
if (fgets(linebuffer, 128, fin) == 0) return tEOF;
lineptr = linebuffer;
linenumber++;
}
while (*lineptr <= ' ') {
if (*lineptr == 0) goto nextline;
if (*lineptr == 0) return tEOL;
lineptr++;
}
switch ((c = *lineptr++)) {
case ',': return tCOMMA;
case '+': return tPLUS;
case '[': return tOBRACK;
case ']': return tCBRACK;
case '"': return tQUOTE;
case ':': return tCOLON;
case '/': case ';': *lineptr = 0; goto nextline;
case '/': case ';': *lineptr = 0; return tEOL;
default:
if (isdigit(c) || ((c == '-') && isdigit(*lineptr))) {
tnumber = strtoul(lineptr-1, &lineptr, 0);
Expand Down Expand Up @@ -232,6 +233,50 @@ void assemble_imm_or_label(void) {
}
}

char unescape(char c) {
switch (c) {
case '\\': case '"': return c;
case '0': return 0x00; case 'b': return 0x08;
case 't': return 0x09; case 'n': return 0x0a;
case 'r': return 0x0d; case 'e': return 0x1b;
default: die("unknown escape sequence: \\%c", c);
}
return 0;
}

void assemble_data(void) {
int argc = -1;
nextarg:
argc++;
next();

if (argc % 2) {
if (token == tEOL) return;
if (token == tCOMMA) goto nextarg;
die("expected comma or newline");
}

/* 0x0000 */
if (token == tNUMBER)
image[PC++] = tnumber;

else if (token == tQUOTE) {
/* "literal \"strings\" with \n escape chars" */
while (*lineptr) {
char c = *lineptr++;
if (c == '"') goto nextarg;
/* check for escaped characters */
if (c == '\\')
c = unescape(*lineptr++);
image[PC++] = (u16)c;
}
} else {
die("expecting <NUMBER> or <DATA>, found %s", tnames[token]);
}

goto nextarg;
}

int assemble_operand(void) {
u16 n;
next();
Expand Down Expand Up @@ -311,10 +356,15 @@ void assemble(const char *fn) {
switch (token) {
case tEOF:
goto done;
case tEOL:
continue;
case tCOLON:
expect(tSTRING);
set_label(tstring, PC);
continue;
case tDAT:
assemble_data();
continue;
case tWORD:
assemble_imm_or_label();
continue;
Expand Down Expand Up @@ -375,6 +425,7 @@ void emit(const char *fn, enum outformat format) {

static void usage(int argc, char **argv)
{
(void) argc;
fprintf(stderr, "usage: %s [-o output] [-O output_format] <input file(s)>\n", argv[0]);
fprintf(stderr, "\toutput_format can be one of: pretty, hex, binary\n");
}
Expand Down
53 changes: 41 additions & 12 deletions emulator.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
#include <string.h>
#include <ctype.h>

#define SCREEN_REGION (0x8000)
#define SCREEN_WIDTH (36)
#define SCREEN_HEIGHT (12)

typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
Expand Down Expand Up @@ -104,7 +108,7 @@ void dcpu_skip(struct dcpu *d) {
d->pc += skiptable[(op >> 4) & 31];
}

void dcpu_step(struct dcpu *d) {
int dcpu_step(struct dcpu *d) {
u16 op = d->m[d->pc++];
u16 dst;
u32 res;
Expand All @@ -128,26 +132,27 @@ void dcpu_step(struct dcpu *d) {
case 0x9: res = a & b; break;
case 0xA: res = a | b; break;
case 0xB: res = a ^ b; break;
case 0xC: if (a!=b) dcpu_skip(d); return;
case 0xD: if (a==b) dcpu_skip(d); return;
case 0xE: if (a<=b) dcpu_skip(d); return;
case 0xF: if ((a&b)==0) dcpu_skip(d); return;
case 0xC: if (a!=b) dcpu_skip(d); return 1;
case 0xD: if (a==b) dcpu_skip(d); return 1;
case 0xE: if (a<=b) dcpu_skip(d); return 1;
case 0xF: if ((a&b)==0) dcpu_skip(d); return 1;
}

if (dst < 0x1f) *aa = res;
return;
return 1;

extended:
a = *dcpu_opr(d, op >> 10);
switch ((op >> 4) & 0x3F) {
case 0x01:
d->m[--(d->sp)] = d->pc;
d->pc = a;
return;
return 1;
default:
fprintf(stderr, "< ILLEGAL OPCODE >\n");
exit(0);
return 0;
}
return 1;
}

void dumpheader(void) {
Expand All @@ -156,6 +161,27 @@ void dumpheader(void) {
"---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -----------\n");
}

void dumpboxhl(void) {
fputc('+', stderr);
for (int i = SCREEN_WIDTH; i--;)
fputc('-', stderr);
fprintf(stderr, "+\n");
}

void dumpscreen(struct dcpu *d) {
dumpboxhl();
for (int y = 0; y < SCREEN_HEIGHT; y++) {
fputc('|', stderr);
for (int x = 0; x < SCREEN_WIDTH; x++) {
u16 c = d->m[SCREEN_REGION + (y * SCREEN_WIDTH) + x];
c = c & 0xff;
fputc(isprint(c) ? c : ' ', stderr);
}
fprintf(stderr, "|\n");
}
dumpboxhl();
}

void dumpstate(struct dcpu *d) {
char out[128];
disassemble(d->m + d->pc, out);
Expand Down Expand Up @@ -191,10 +217,13 @@ int main(int argc, char **argv) {
load(&d, argc > 1 ? argv[1] : "out.hex");

dumpheader();
for (;;) {
dumpstate(&d);
dcpu_step(&d);
}
do {
//dumpscreen(&d);
dumpstate(&d);
} while (dcpu_step(&d));

dumpscreen(&d);

return 0;
}

41 changes: 41 additions & 0 deletions tests/colours.dc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
; Notch's second "hello word" program.
; http://i.imgur.com/XIXc4.jpg
; Supposed to show formatting.
:start
set i, 0
set j, 0
set b, 0xf100

:nextchar
set a, [data+i]
ife a, 0
set PC, end
ifg a, 0xff
set PC, setcolor
bor a, b
set [0x8000+j], a
add i, 1
add j, 1
set PC, nextchar

:setcolor
set b, a
and b, 0xff
shl b, 8
ifg a, 0x1ff
add b, 0x80 ; Add high bit to each character we color. So the character must be 7-bit ASCII
add i, 1
set PC, nextchar

:data
dat 0x170, "Hello ", 0x2e1, "world", 0x170, ", how are you?", 0
; Color format:
; After processing:
; 0x170 -> b = 0x7000 -> 0111 0000 0XXX XXXX = white(grey) on black
; 0x2e1 -> b = 0xe180 -> 1110 0001 1XXX XXXX = yellow on blue
; b gets OR'd with each character.
; ANSI says: black is 0, white is 7, yellow is 3, blue is 4
; If black is 0 and grey is 7, it's <FORE> <BACK> <EXTRA BIT> or <FORE> <EXTRA BIT> <BACK>
:end
;set PC, start
WORD 0xeee0
File renamed without changes.
42 changes: 42 additions & 0 deletions tests/helloworld.dc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
; Try some basic stuff
SET A, 0x30 ; 7c01 0030
SET [0x1000], 0x20 ; 7de1 1000 0020
SUB A, [0x1000] ; 7803 1000
IFN A, 0x10 ; c00d
SET PC, crash ; 7dc1 001a [*]

; Do a loopy thing
SET I, 10 ; a861
SET A, 0x2000 ; 7c01 2000
:loop SET [0x2000+I], [A] ; 2161 2000
SUB I, 1 ; 8463
IFN I, 0 ; 806d
SET PC, loop ; 7dc1 000d [*]

; Call a subroutine
SET X, 0x4 ; 9031
JSR testsub ; 7c10 0018 [*]
SET PC, print ; 7dc1 001a [*]

:testsub SHL X, 4 ; 9037
SET PC, POP ; 61c1

; "Hello, world!"
; Set 0x8000 - 0x8180 to an ASCII value to output to console
:print
SET [0x8000], 72
SET [0x8001], 101
SET [0x8002], 108
SET [0x8003], 108
SET [0x8004], 111
SET [0x8005], 44
SET [0x8006], 32
SET [0x8007], 119
SET [0x8008], 111
SET [0x8009], 114
SET [0x800a], 108
SET [0x800b], 100
SET [0x800c], 33

:crash
WORD 0xeee0