#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 * sizeof index)); assert(cell_x < CELL_X); assert(cell_y < CELL_Y); cellstart += (PIX_X * PIX_BORDER); /* skip top border */ cellstart += (CELL_Y_SZ * PIX_X) * 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[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = fg; else cellstart[((CELL_Y_SZ - pix_y - 1) * 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; }