initial support for a display module, incomplete
authorJustin Wind <justin.wind@gmail.com>
Sat, 21 Apr 2012 19:37:17 +0000 (12:37 -0700)
committerJustin Wind <justin.wind@gmail.com>
Sat, 21 Apr 2012 19:37:17 +0000 (12:37 -0700)
Added basic support for an optional display buffer output module, which is currently just written out to a PNM file on each screen update.
Tile rendering needs some further work&tests.

Makefile
chargen-4x8.h [new file with mode: 0644]
dcpu16.c
dcpu16.h
display.c [new file with mode: 0644]
display.h [new file with mode: 0644]
vm-dcpu16.c

index fd05c43b8d484609d68e7835d5512bf4629e4f60..0da0a0e2778f7a2c0a9ff465c33154836bbabc02 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ CC = clang
 endif
 
 PROGRAMS = as-dcpu16 vm-dcpu16
-SOURCES = common.c dcpu16.c as-dcpu16.c vm-dcpu16.c
+SOURCES = common.c dcpu16.c as-dcpu16.c vm-dcpu16.c display.c
 
 CPPFLAGS = -D_XOPEN_SOURCE=600
 CFLAGS = -g -Wall -Wextra -pedantic -std=c99
@@ -24,7 +24,7 @@ depend:       .depend
 include .depend
 
 
-vm-dcpu16:     vm-dcpu16.o dcpu16.o common.o
+vm-dcpu16:     vm-dcpu16.o dcpu16.o common.o display.o
 
 as-dcpu16:     as-dcpu16.o common.o
 
diff --git a/chargen-4x8.h b/chargen-4x8.h
new file mode 100644 (file)
index 0000000..f29cb04
--- /dev/null
@@ -0,0 +1,276 @@
+#ifndef CHARGEN_4X8_H_KMZC3DOG
+#define CHARGEN_4X8_H_KMZC3DOG
+
+/* 4x8 pixel default font map */
+
+const char chargen_4x8_glyphs[][4] = {
+    /* 00 */
+    { 0x10, 0x10, 0xf0, 0x00 },
+    /* 01 */
+    { 0x10, 0x10, 0xf0, 0x10 },
+    /* 02 */
+    { 0x10, 0x10, 0x1f, 0x10 },
+    /* 03 */
+    { 0x10, 0x10, 0xff, 0x10 },
+    /* 04 */
+    { 0x10, 0x10, 0x10, 0x10 },
+    /* 05 */
+    { 0x10, 0xff, 0x10, 0x10 },
+    /* 06 */
+    { 0x00, 0xff, 0x28, 0x28 },
+    /* 07 */
+    { 0xff, 0x00, 0xff, 0x10 },
+    /* 08 */
+    { 0xf8, 0x08, 0xe8, 0x28 },
+    /* 09 */
+    { 0x3f, 0x20, 0x2f, 0x28 },
+    /* 0a */
+    { 0xe8, 0x08, 0xe8, 0x28 },
+    /* 0b */
+    { 0x2f, 0x20, 0x2f, 0x20 },
+    /* 0c */
+    { 0xff, 0x00, 0xef, 0x2f },
+    /* 0d */
+    { 0x2f, 0x2f, 0x2f, 0x2f },
+    /* 0e */
+    { 0xef, 0x00, 0xef, 0xe8 },
+    /* 0f */
+    { 0x28, 0xe8, 0x28, 0x28 },
+    /* 10 */
+    { 0xf0, 0x10, 0xf0, 0x10 },
+    /* 11 */
+    { 0x28, 0x2f, 0x28, 0x28 },
+    /* 12 */
+    { 0x1f, 0x10, 0x1f, 0x10 },
+    /* 13 */
+    { 0xf0, 0x10, 0xf0, 0x10 },
+    /* 14 */
+    { 0x00, 0xf8, 0x28, 0x28 },
+    /* 15 */
+    { 0x00, 0x3f, 0x28, 0x28 },
+    /* 16 */
+    { 0x1f, 0x10, 0x1f, 0x10 },
+    /* 17 */
+    { 0xff, 0x10, 0xff, 0x10 },
+    /* 18 */
+    { 0x28, 0xff, 0x28, 0x28 },
+    /* 19 */
+    { 0x10, 0xf0, 0x00, 0x00 },
+    /* 1a */
+    { 0x00, 0x1f, 0x10, 0x10 },
+    /* 1b */
+    { 0xff, 0xff, 0xff, 0xff },
+    /* 1c */
+    { 0x0f, 0x0f, 0x0f, 0x0f },
+    /* 1d */
+    { 0xff, 0xff, 0x00, 0x00 },
+    /* 1e */
+    { 0x00, 0x00, 0xff ,0xff },
+    /* 1f */
+    { 0xf0, 0xf0, 0xf0, 0xf0 },
+
+    /* --- */
+
+    /* 20 ' ' */
+    { 0x00, 0x00, 0x00, 0x00 },
+    /* 21 '!' */
+    { 0x00, 0xfa, 0x00, 0x00 },
+    /* 22 '"' "*/
+    { 0xb0, 0x00, 0xb0, 0x00 },
+    /* 23 '#' */
+    { 0x7b, 0x28, 0x7b, 0x00 },
+    /* 24 '$' */
+    { 0x64, 0xd6, 0x4b, 0x00 },
+    /* 25 '%' */
+    { 0x86, 0x38, 0xb2, 0x00 },
+    /* 26 '&' */
+    { 0x6b, 0x94, 0x6e, 0x0a },
+    /* 27 ''' */
+    { 0x00, 0x40, 0x80, 0x00 },
+    /* 28 '(' */
+    { 0x38, 0x44, 0x82, 0x00 },
+    /* 29 ')' */
+    { 0x82, 0x44, 0x38, 0x00 },
+    /* 2a '*' */
+    { 0x54, 0x38, 0x54, 0x00 },
+    /* 2b '+' */
+    { 0x10, 0x7c, 0x10, 0x00 },
+    /* 2c ',' */
+    { 0x02, 0x04, 0x00, 0x00 },
+    /* 2d '-' */
+    { 0x10, 0x10, 0x10, 0x00 },
+    /* 2e '.' */
+    { 0x00, 0x02, 0x00, 0x00 },
+    /* 2f '/' */
+    { 0x06, 0x38, 0xc0, 0x00 },
+    /* 30 '0' */
+    { 0x7c, 0x82, 0x7c, 0x00 },
+    /* 31 '1' */
+    { 0x42, 0xfe, 0x02, 0x00 },
+    /* 32 '2' */
+    { 0x46, 0x9a, 0x62, 0x00 },
+    /* 33 '3' */
+    { 0x44, 0x92, 0x6c, 0x00 },
+    /* 34 '4' */
+    { 0xf0, 0x10, 0xfe, 0x00 },
+    /* 35 '5' */
+    { 0xe4, 0xa2, 0x9c, 0x00 },
+    /* 36 '6' */
+    { 0x7c, 0x92, 0x4c, 0x00 },
+    /* 37 '7' */
+    { 0x86, 0x98, 0xe0, 0x00 },
+    /* 38 '8' */
+    { 0x6c, 0x92, 0x6c, 0x00 },
+    /* 39 '9' */
+    { 0x64, 0x92, 0x7c, 0x00 },
+    /* 3a ':' */
+    { 0x00, 0x24, 0x00, 0x00 },
+    /* 3b ';' */
+    { 0x02, 0x24, 0x00, 0x00 },
+    /* 3c '<' */
+    { 0x10, 0x28, 0x44, 0x82 },
+    /* 3d '=' */
+    { 0x28, 0x28, 0x28, 0x00 },
+    /* 3e '>' */
+    { 0x82, 0x44, 0x28, 0x10 },
+    /* 3f '?' */
+    { 0x40, 0x9a, 0x60, 0x00 },
+
+    /* --- */
+
+    /* 40 '@' */
+    { 0x7c, 0x9a, 0x7a, 0x00 },
+    /* 41 'A' */
+    { 0x7e, 0x90, 0x7e, 0x00 },
+    /* 42 'B' */
+    { 0xfe, 0x92, 0x6c, 0x00 },
+    /* 43 'C' */
+    { 0x7c, 0x82, 0x44, 0x00 },
+    /* 44 'D' */
+    { 0xfe, 0x82, 0x7c, 0x00 },
+    /* 45 'E' */
+    { 0xfe, 0x92, 0x82, 0x00 },
+    /* 46 'F' */
+    { 0xfe, 0x90, 0x80, 0x00 },
+    /* 47 'G' */
+    { 0x7c, 0x92, 0x5c, 0x00 },
+    /* 48 'H' */
+    { 0xfe, 0x10, 0xfe, 0x00 },
+    /* 49 'I' */
+    { 0x82, 0xfe, 0x82, 0x00 },
+    /* 4a 'J' */
+    { 0x04, 0x02, 0xfc, 0x00 },
+    /* 4b 'K' */
+    { 0xfe, 0x30, 0xce, 0x00 },
+    /* 4c 'L' */
+    { 0xfe, 0x02, 0x02, 0x00 },
+    /* 4d 'M' */
+    { 0xfe, 0x60, 0xfe, 0x00 },
+    /* 4e 'N' */
+    { 0xfe, 0x80, 0x8e, 0x00 },
+    /* 4f 'O' */
+    { 0x7c, 0x82, 0x7c, 0x00 },
+    /* 50 'P' */
+    { 0xfe, 0x90, 0x60, 0x00 },
+    /* 51 'Q' */
+    { 0x7c, 0x82, 0x7d, 0x00 },
+    /* 52 'R' */
+    { 0xfe, 0x90, 0x6e, 0x00 },
+    /* 53 'S' */
+    { 0x64, 0x92, 0x4c, 0x00 },
+    /* 54 'T' */
+    { 0x80, 0xfe, 0x80, 0x00 },
+    /* 55 'U' */
+    { 0xfe, 0x02, 0xfe, 0x02 },
+    /* 56 'V' */
+    { 0xf8, 0x06, 0xf8, 0x00 },
+    /* 57 'W' */
+    { 0xfe, 0x0c, 0xfe, 0x00 },
+    /* 58 'X' */
+    { 0xee, 0x10, 0xee, 0x00 },
+    /* 59 'Y' */
+    { 0xe0, 0x1e, 0xe0, 0x00 },
+    /* 5a 'Z' */
+    { 0x8e, 0x92, 0xe2, 0x00 },
+    /* 5b '[' */
+    { 0x00, 0xfe, 0x82, 0x00 },
+    /* 5c '\' */
+    { 0xc0, 0x38, 0x06, 0x00 },
+    /* 5d ']' */
+    { 0x00, 0x82, 0xfe, 0x00 },
+    /* 5e '^' */
+    { 0x40, 0x80, 0x40, 0x00 },
+    /* 5f '_' */
+    { 0x01, 0x01, 0x01, 0x00 },
+
+    /* --- */
+
+    /* 60 '`' */
+    { 0x00, 0x80, 0x40, 0x00 },
+    /* 61 'a' */
+    { 0x24, 0x2a, 0x1e, 0x00 },
+    /* 62 'b' */
+    { 0xfe, 0x22, 0x1c, 0x00 },
+    /* 63 'c' */
+    { 0x1c, 0x22, 0x14, 0x00 },
+    /* 64 'd' */
+    { 0x1c, 0x22, 0xfe, 0x00 },
+    /* 65 'e' */
+    { 0x1c, 0x2a, 0x1a, 0x00 },
+    /* 66 'f' */
+    { 0x10, 0x77, 0x90, 0x00 },
+    /* 67 'g' */
+    { 0x12, 0x2a, 0x3a, 0x00 },
+    /* 68 'h' */
+    { 0xfe, 0x20, 0x1e, 0x00 },
+    /* 69 'i' */
+    { 0x22, 0xbe, 0x02, 0x00 },
+    /* 6a 'j' */
+    { 0x04, 0x02, 0xbc, 0x00 },
+    /* 6b 'k' */
+    { 0xfe, 0x08, 0x36, 0x00 },
+    /* 6c 'l' */
+    { 0x82, 0xfe, 0x02, 0x00 },
+    /* 6d 'm' */
+    { 0x3e, 0x18, 0x3e, 0x00 },
+    /* 6e 'n' */
+    { 0x3e, 0x20, 0x1e, 0x00 },
+    /* 6f 'o' */
+    { 0x1c, 0x22, 0x1c, 0x00 },
+    /* 70 'p' */
+    { 0x3e, 0x28, 0x10, 0x00 },
+    /* 71 'q' */
+    { 0x10, 0x28, 0x3e, 0x00 },
+    /* 72 'r' */
+    { 0x3e, 0x20, 0x10, 0x00 },
+    /* 73 's' */
+    { 0x12, 0x2a, 0x24, 0x00 },
+    /* 74 't' */
+    { 0x20, 0x7c, 0x22, 0x00 },
+    /* 75 'u' */
+    { 0x3c, 0x02, 0x37, 0x00 },
+    /* 76 'v' */
+    { 0x38, 0x06, 0x38, 0x00 },
+    /* 77 'w' */
+    { 0x3e, 0x0c, 0x3e, 0x00 },
+    /* 78 'x' */
+    { 0x36, 0x08, 0x36, 0x00 },
+    /* 79 'y' */
+    { 0x32, 0x0a, 0x3c, 0x00 },
+    /* 7a 'z' */
+    { 0x26, 0x2a, 0x32, 0x00 },
+    /* 7b '{' */
+    { 0x10, 0x6c, 0x82, 0x00 },
+    /* 7c '|' */
+    { 0x00, 0xee, 0x00, 0x00 },
+    /* 7d '}' */
+    { 0x82, 0x6c, 0x10, 0x00 },
+    /* 7e '~' */
+    { 0x40, 0x80, 0x40, 0x80 },
+    /* 7f '<delta>' */
+    { 0x0e, 0x32, 0x0e, 0x00 },
+};
+
+#define CHARGEN_4x8_GLYPHS_NUM ( (sizeof chargen_4x8_glyphs) / (sizeof chargen_4x8_glyphs[0]) )
+
+#endif /* CHARGEN_4X8_H_KMZC3DOG */
index b88cf0583a73148840a0b645e843578ded23fa03..165b58984e298d76a2cdce30334a2dc141826344 100644 (file)
--- a/dcpu16.c
+++ b/dcpu16.c
@@ -87,7 +87,7 @@ void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) {
  *  invokes callbacks for specified event
  */
 static inline
-void acct_event_(struct dcpu16 *vm, dcpu16_acct_event_ ev, DCPU16_WORD addr) {
+void acct_event_(struct dcpu16 *vm, dcpu16_acct_event ev, DCPU16_WORD addr) {
     struct dcpu16_acct_cb *cb = vm->cb_table_;
     size_t i;
 
@@ -763,7 +763,7 @@ void dcpu16_dump_ram(struct dcpu16 *d, DCPU16_WORD start, DCPU16_WORD end) {
  *  Register callback fn to be triggered whenever event matching any events
  *  in bitwise mask occur.
  */
-int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event_ mask, void (*fn)(struct dcpu16 *, dcpu16_acct_event_, DCPU16_WORD, void *), void *data) {
+int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event mask, void (*fn)(struct dcpu16 *, dcpu16_acct_event, DCPU16_WORD, void *), void *data) {
     struct dcpu16_acct_cb cb;
 
     cb.mask = mask;
@@ -788,12 +788,20 @@ int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event_ mask, void (*fn)(struc
 }
 
 /*  dcpu16_reset
- *  resets a dcpu16 instance to initial state
+ *  signals cpu to reset, clearing runstate and ram, then reload any init callbacks
  */
 void dcpu16_reset(struct dcpu16 *d) {
     if (!d) return;
 
-    memset(d, 0, sizeof *d);
+    d->cycle = 0;
+    memset(d->reg, 0, sizeof d->reg);
+    d->pc = 0;
+    d->sp = 0;
+    d->o = 0;
+    d->skip_ = 0;
+    memset(d->ram, 0, sizeof d->ram);
+
+    acct_event_(d, DCPU16_ACCT_EV_RESET, 0);
 }
 
 /*  dcpu16_new
index b1b563dfc3483a9d09052bd827d6e14c2f6bdb35..73592d9c358e907b1c8d1b7d62e17162ebd6045b 100644 (file)
--- a/dcpu16.h
+++ b/dcpu16.h
@@ -1,6 +1,8 @@
 #ifndef DCPU16_H_3XXIQQG2
 #define DCPU16_H_3XXIQQG2
 
+#include <stdlib.h>
+
 /* the target system's concept of a word */
 typedef unsigned short DCPU16_WORD;
 
@@ -17,20 +19,21 @@ struct dcpu16 {
     unsigned int skip_ : 1;             /* skip execution of next instruction */
     DCPU16_WORD ram[DCPU16_RAM];        /* memory */
     DCPU16_WORD reg_work_[2];           /* (private) work registers for holding literal values while decoding instructions */
-    struct dcpu16_acct_cb *cb_table_;   /* list of callbacks to invoke under certain conditions */
+    struct dcpu16_acct_cb *cb_table_;   /* list of callbacks to invoke for certain events */
     size_t cb_table_entries_;           /* callback list maintenance */
     size_t cb_table_allocated_;         /* callback list maintenance */
 };
 
 /* these are used for accounting/watchpointing/&c */
-typedef unsigned int dcpu16_acct_event_;
+typedef unsigned int dcpu16_acct_event;
 #define DCPU16_ACCT_EV_READ     (1<<1)
 #define DCPU16_ACCT_EV_WRITE    (1<<2)
 #define DCPU16_ACCT_EV_NOP      (1<<3)
+#define DCPU16_ACCT_EV_RESET    (1<<4)
 struct dcpu16_acct_cb {
-    void (*fn)(struct dcpu16 *, dcpu16_acct_event_ e, DCPU16_WORD addr, void *);
+    void (*fn)(struct dcpu16 *, dcpu16_acct_event e, DCPU16_WORD addr, void *);
     void *data;
-    dcpu16_acct_event_ mask;
+    dcpu16_acct_event mask;
 };
 
 /* instantiate a new core */
@@ -49,7 +52,7 @@ void dcpu16_dump_ram(struct dcpu16 *, DCPU16_WORD, DCPU16_WORD);
 DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *, DCPU16_WORD);
 
 /* register a callback for an accounting event */
-int dcpu16_acct_add(struct dcpu16 *, dcpu16_acct_event_ mask, void (*fn)(struct dcpu16 *, dcpu16_acct_event_, DCPU16_WORD, void *), void *data);
+int dcpu16_acct_add(struct dcpu16 *, dcpu16_acct_event mask, void (*fn)(struct dcpu16 *, dcpu16_acct_event, DCPU16_WORD, void *), void *data);
 
 /* execute the next instruction */
 void dcpu16_step(struct dcpu16 *);
diff --git a/display.c b/display.c
new file mode 100644 (file)
index 0000000..37db1d9
--- /dev/null
+++ b/display.c
@@ -0,0 +1,238 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "dcpu16.h"
+#include "display.h"
+#include "chargen-4x8.h"
+
+/* preliminary attempt at handling display */
+/* currently keeps display state in buffer, and just updates a PNM file on each screen update */
+
+#define DISPLAY_BASE 0x8000
+#define DISPLAY_END  0x8179
+#define DISPLAY_CELL_MAP 0x8180
+#define DISPLAY_MISC 0x8280
+
+#define CELL_X 32
+#define CELL_Y 12
+
+#define CELL_X_SZ 4
+#define CELL_Y_SZ 8
+
+#define PIX_X 160
+#define PIX_Y 128
+#define PIX_BORDER 16
+
+/* total display is 160 pixels by 128 pixels */
+/* active display is 32x12 cells (each 4x8 pixels), surrounded by 16 pixel border */
+/* cells are rendered from cell map, which is bitmap of two words, defining four eight-bit columns */
+
+struct pixel_ {
+    char r;
+    char g;
+    char b;
+};
+
+static inline
+DPIX pcolor_(unsigned int c) {
+    DPIX p = { 0, 0, 0 };
+
+    switch (c) {
+        case 0x1: p.r=0x00, p.g=0x00, p.b=0xaa; break;  /* dark blue */
+        case 0x2: p.r=0x00, p.g=0xaa, p.b=0x00; break;  /* green */
+        case 0x3: p.r=0x00, p.g=0xaa, p.b=0xaa; break;  /* cyan */
+        case 0x4: p.r=0xaa, p.g=0x00, p.b=0x00; break;  /* red */
+        case 0x5: p.r=0xaa, p.g=0x00, p.b=0xaa; break;  /* magenta */
+        case 0x6: p.r=0xaa, p.g=0xaa, p.b=0x55; break;  /* yellow [] */
+        case 0x7: p.r=0xaa, p.g=0xaa, p.b=0xff; break;  /* pale blue */
+        case 0x8: p.r=0x55, p.g=0x55, p.b=0x55; break;  /* grey */
+        case 0x9: p.r=0x55, p.g=0x55, p.b=0xff; break;  /* also blue */
+        case 0xa: p.r=0x55, p.g=0xff, p.b=0x55; break;  /* light green */
+        case 0xb: p.r=0x55, p.g=0xff, p.b=0xff; break;  /* light cyan */
+        case 0xc: p.r=0xff, p.g=0x55, p.b=0x55; break;  /* light red */
+        case 0xd: p.r=0xff, p.g=0x55, p.b=0xff; break;  /* light magenta */
+        case 0xe: p.r=0xff, p.g=0xff, p.b=0x55; break;  /* light yellow */
+        case 0xf: p.r=0xff, p.g=0xff, p.b=0xff; break;  /* white */
+        default:  p.r=0x00, p.g=0x00, p.b=0x00;         /* black */
+    }
+
+    return p;
+}
+
+/* should this just flood-fill entire display? */
+static inline
+void display_draw_border(DPIX *pixbuf, DPIX color) {
+    size_t x, y;
+    size_t i;
+
+    /* top */
+    for (y = 0; y < PIX_BORDER; y++) {
+        for (x = 0; x < PIX_X; x++) {
+            i = (y * PIX_X) + x;
+            pixbuf[i] = color;
+            i = ((PIX_Y - y) * PIX_X) + x;
+            pixbuf[i] = color;
+        }
+    }
+
+    /* sides */
+    for (y = PIX_BORDER; y < (PIX_Y - PIX_BORDER + 1); y++)
+        for (x = 0; x < PIX_BORDER; x++) {
+            i = (y * PIX_X) + x;
+            pixbuf[i] = color;
+            pixbuf[i + (PIX_X - PIX_BORDER)] = color;
+        }
+
+}
+
+/* render a character cell to the display */
+static inline
+void display_draw_cell(DPIX *pixbuf, DCPU16_WORD *cell_map, DCPU16_WORD index, int cell_x, int cell_y, DPIX fg, DPIX bg) {
+    DPIX *cellstart = pixbuf; /* start of display */
+    unsigned int pix_x, pix_y;
+    unsigned char *cell_bitmap = (unsigned char *)(cell_map + index);
+
+    assert(cell_x < CELL_X);
+    assert(cell_y < CELL_Y);
+
+    cellstart += (PIX_X * PIX_BORDER); /* skip top border */
+
+    cellstart += (CELL_Y_SZ) * cell_y; /* skip down to row */
+
+    cellstart += PIX_BORDER; /* skip side border */
+
+    cellstart += (CELL_X_SZ) * cell_x; /* skip to column */
+
+    for (pix_x = 0; pix_x < CELL_X_SZ; pix_x++) {
+        for (pix_y = 0; pix_y < CELL_Y_SZ; pix_y++) {
+            if ((cell_bitmap[pix_x] >> pix_y) & 0x01)
+                cellstart[(pix_y * PIX_X) + pix_x] = fg;
+            else
+                cellstart[(pix_y * PIX_X) + pix_x] = bg;
+        }
+    }
+}
+
+/* write pnm file */
+void display_pnm_write(DPIX *pixbuf, const char *filename) {
+    size_t i;
+    FILE *f;
+
+    f = fopen(filename, "w");
+    if (f == NULL) {
+        fprintf(stderr, "%s('%s'):%s\n", "fopen", filename, strerror(errno));
+        return;
+    }
+
+    /* write header... */
+    /* PNM binary */
+    /* x y */
+    /* max value */
+    fprintf(f, "P6\n%d %d\n255\n", PIX_X, PIX_Y);
+
+    /* write out image bytes in r g b order */
+    for (i = 0; i < PIX_X * PIX_Y; i++) {
+        fwrite(&pixbuf[i].r, 1, 1, f);
+        fwrite(&pixbuf[i].g, 1, 1, f);
+        fwrite(&pixbuf[i].b, 1, 1, f);
+    }
+
+    fclose(f);
+}
+
+
+/* the callback to register to be run on display init/reset */
+/* currently this populates the chargen map 'from rom'.. */
+/* and clears the display buffers */
+void display_reset_fn(struct dcpu16 *vm, dcpu16_acct_event e, DCPU16_WORD addr, void *data) {
+    DPIX *pixbuf = (DPIX *)data;
+
+    (void)e, (void)addr;
+
+    fprintf(stderr, "DEBUG: event:%u\n", e);
+
+    fprintf(stderr, "DEBUG: loading chargen map from 0x%04x to 0x%04x (%zuo)\n",
+            DISPLAY_CELL_MAP,
+            DISPLAY_CELL_MAP + (unsigned short)(sizeof chargen_4x8_glyphs / sizeof addr),
+            sizeof chargen_4x8_glyphs);
+    memcpy(vm->ram + DISPLAY_CELL_MAP, chargen_4x8_glyphs, sizeof chargen_4x8_glyphs);
+
+    fprintf(stderr, "DEBUG: clearing display buffer from 0x%04x to 0x%04x (%zuo)\n",
+            DISPLAY_BASE,
+            DISPLAY_END,
+            (DISPLAY_END - DISPLAY_BASE) * sizeof *(vm->ram));
+    memset(vm->ram + DISPLAY_BASE, 0, (DISPLAY_END - DISPLAY_BASE) * sizeof *(vm->ram));
+
+    fprintf(stderr, "DEBUG: clearing pixel buffer (%zuo)\n",
+            PIX_X * PIX_Y * sizeof *pixbuf);
+    memset(pixbuf, 0, PIX_X * PIX_Y * sizeof *pixbuf);
+}
+
+/* the callback to register with the cpu for watching memory updates */
+/* user data is an allocated display buffer */
+void display_fn(struct dcpu16 *vm, dcpu16_acct_event e, DCPU16_WORD addr, void *data) {
+    const char * const outfile = "dcpu16-display.pnm";
+    DPIX *pixbuf = (DPIX *)data;
+    unsigned char index, blink, bg, fg;
+    unsigned int cell_x, cell_y;
+
+    (void)e;
+
+    if (addr < DISPLAY_BASE || addr > DISPLAY_MISC)
+        return;
+
+    if (addr > DISPLAY_END && addr < DISPLAY_MISC) {
+        unsigned char updated_index = addr - DISPLAY_CELL_MAP;
+        /* a cell map was updated, refresh entire screen */
+        for (cell_x = 0; cell_x < CELL_X; cell_x++) {
+            for (cell_y = 0; cell_y < CELL_Y; cell_y++) {
+                addr = DISPLAY_BASE + cell_x + (cell_y * CELL_X);
+                index = vm->ram[addr] & 0x7f;
+                if (index != updated_index)
+                    continue;
+                blink = (vm->ram[addr] >> 7) & 0x01;
+                bg = (vm->ram[addr] >> 8) & 0x0f;
+                fg = (vm->ram[addr] >> 12) & 0x0f;
+                display_draw_cell(pixbuf, vm->ram + DISPLAY_CELL_MAP, index, cell_x, cell_y, pcolor_(fg), pcolor_(bg));
+            }
+        }
+
+        display_pnm_write(pixbuf, outfile);
+        return;
+    }
+
+    if (addr == DISPLAY_MISC) {
+        /* new border color */
+        char border = vm->ram[addr] & 0x0f;
+        fprintf(stderr, "display event: new border\n");
+        display_draw_border(pixbuf, pcolor_(border));
+
+        display_pnm_write(pixbuf, outfile);
+        return;
+    }
+
+    cell_x = (addr - DISPLAY_BASE) % CELL_X;
+    cell_y = (addr - DISPLAY_BASE) / CELL_X;
+
+    index = vm->ram[addr] & 0x7f;
+    blink = (vm->ram[addr] >> 7) & 0x01;
+    bg = (vm->ram[addr] >> 8) & 0x0f;
+    fg = (vm->ram[addr] >> 12) & 0x0f;
+
+    fprintf(stderr, "display event: cell %ux%u:%u '%c'\n", cell_x, cell_y, index, index);
+
+    display_draw_cell(pixbuf, vm->ram + DISPLAY_CELL_MAP, index, cell_x, cell_y, pcolor_(fg), pcolor_(bg));
+    display_pnm_write(pixbuf, outfile);
+}
+
+/* init the pixel buffer */
+DPIX *display_init_pixbuf(void) {
+    DPIX *pixbuf;
+
+    pixbuf = calloc(PIX_X * PIX_Y, sizeof *pixbuf);
+
+    return pixbuf;
+}
diff --git a/display.h b/display.h
new file mode 100644 (file)
index 0000000..f6e4461
--- /dev/null
+++ b/display.h
@@ -0,0 +1,12 @@
+#ifndef DISPLAY_H_MJYI1IAV
+#define DISPLAY_H_MJYI1IAV
+
+#include "dcpu16.h"
+
+typedef struct pixel_ DPIX;
+
+void display_reset_fn(struct dcpu16 *, dcpu16_acct_event, DCPU16_WORD, void *);
+void display_fn(struct dcpu16 *, dcpu16_acct_event, DCPU16_WORD, void *);
+DPIX *display_init_pixbuf(void);
+
+#endif /* DISPLAY_H_MJYI1IAV */
index 537452af9af8f11066f00dd43aa0eb33961eb00d..1e3a49656757f8e68cd90ebae13a12bd75018ed1 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "dcpu16.h"
 #include "common.h"
+#include "display.h"
 
 /*
  *  shell-like driver for dcpu16 core
@@ -364,6 +365,45 @@ COMMAND_HELP(run) {
                "May be interrupted with SIGINT.\n");
 }
 
+COMMAND_IMPL(display) {
+    (void)arg_count, (void)arg_vector;
+
+    static DPIX *pixbuf = NULL;
+
+    if (pixbuf) {
+        printf("display already enabled..\n");
+        return 0;
+    }
+
+    pixbuf = display_init_pixbuf();
+    if (pixbuf == NULL) {
+        fprintf(stderr, "failed to initialize display buffer\n");
+        return 0;
+    }
+
+    if (dcpu16_acct_add(vm, DCPU16_ACCT_EV_WRITE, display_fn, pixbuf)) {
+        fprintf(stderr, "failed to register display update callback\n");
+        return 0;
+    }
+
+    if (dcpu16_acct_add(vm, DCPU16_ACCT_EV_RESET, display_reset_fn, pixbuf)) {
+        fprintf(stderr, "failed to register display reset callback\n");
+        return 0;
+    }
+
+    /* init display as if reset occurred */
+    display_reset_fn(vm, DCPU16_ACCT_EV_RESET, 0, pixbuf);
+
+    return 0;
+}
+COMMAND_HELP(display) {
+    fprintf(f, "\tdisplay\n");
+    if (summary) return;
+
+    fprintf(f, "Begins updating a PNM of current display contents...\n"
+               "This is not a fully-baked feature...\n");
+}
+
 /* gather all these together into a searchable table */
 
 /* help command gets some assistance in declarations */
@@ -379,6 +419,7 @@ static struct command_ command_table_[] = {
     COMMAND_ENTRY(step, 0, 1),
     COMMAND_ENTRY(run, 0, 0),
     COMMAND_ENTRY(reset, 0, 0),
+    COMMAND_ENTRY(display, 0, 0),
     { NULL, 0, 0, NULL, NULL }
 };