From bca632cc1c329a45dde32f476b0ef9f6ef5db05a Mon Sep 17 00:00:00 2001 From: Justin Wind Date: Sat, 21 Apr 2012 12:37:17 -0700 Subject: [PATCH] initial support for a display module, incomplete 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 | 4 +- chargen-4x8.h | 276 ++++++++++++++++++++++++++++++++++++++++++++++++++ dcpu16.c | 16 ++- dcpu16.h | 13 ++- display.c | 238 +++++++++++++++++++++++++++++++++++++++++++ display.h | 12 +++ vm-dcpu16.c | 41 ++++++++ 7 files changed, 589 insertions(+), 11 deletions(-) create mode 100644 chargen-4x8.h create mode 100644 display.c create mode 100644 display.h diff --git a/Makefile b/Makefile index fd05c43..0da0a0e 100644 --- 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 index 0000000..f29cb04 --- /dev/null +++ b/chargen-4x8.h @@ -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 '' */ + { 0x0e, 0x32, 0x0e, 0x00 }, +}; + +#define CHARGEN_4x8_GLYPHS_NUM ( (sizeof chargen_4x8_glyphs) / (sizeof chargen_4x8_glyphs[0]) ) + +#endif /* CHARGEN_4X8_H_KMZC3DOG */ diff --git a/dcpu16.c b/dcpu16.c index b88cf05..165b589 100644 --- 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 diff --git a/dcpu16.h b/dcpu16.h index b1b563d..73592d9 100644 --- a/dcpu16.h +++ b/dcpu16.h @@ -1,6 +1,8 @@ #ifndef DCPU16_H_3XXIQQG2 #define DCPU16_H_3XXIQQG2 +#include + /* 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 index 0000000..37db1d9 --- /dev/null +++ b/display.c @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include + +#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 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 */ diff --git a/vm-dcpu16.c b/vm-dcpu16.c index 537452a..1e3a496 100644 --- a/vm-dcpu16.c +++ b/vm-dcpu16.c @@ -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 } }; -- 2.43.2