--- /dev/null
+#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 */
--- /dev/null
+#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;
+}