fixed bug with instruction decoding in dcpu, fixed bugs in display
[dcpu16] / hw_lem1802.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <errno.h>
5
6 #ifdef HAVE_LIBPNG
7 #include <setjmp.h>
8 #include <png.h>
9 #endif /* HAVE_LIBPNG */
10
11 #ifdef HAVE_LIBVNCSERVER
12 #include <rfb/rfb.h>
13 #include <rfb/keysym.h>
14 #endif /* HAVE_LIBVNCSERVER */
15
16 #include "dcpu16.h"
17 #include "chargen-4x8.h"
18 #include "hw_lem1802.h"
19
20 #ifdef DEBUG
21 #define TRACE(...) do { printf("[debug] "); printf(__VA_ARGS__); printf("\n"); } while (0)
22 #else /* DEBUG */
23 #define TRACE(...) do {} while (0)
24 #endif /* DEBUG */
25
26 static dcpu16_hw_signal_t lem1802_reset_;
27 static dcpu16_hw_signal_t lem1802_cycle_;
28 static dcpu16_hw_signal_t lem1802_hwi_;
29 static struct dcpu16_hw hw_ = {
30 .name_ = "LEM1802 - Low Energy Monitor",
31 .id_l = 0xf615,
32 .id_h = 0x7349,
33 .ver = 0x1802,
34 .mfg_l = 0x8b36,
35 .mfg_h = 0x1c6c,
36 .hwi = lem1802_hwi_,
37 .cycle = lem1802_cycle_,
38 .reset = lem1802_reset_,
39 .data = (struct lem1802_ *)NULL
40 };
41
42 #define PIX_X 160 /* pixels in display */
43 #define PIX_Y 128 /* pixels in display */
44 #define PIX_BORDER 16 /* border pixels from edge to first tile */
45 #define CELL_X_SZ 4
46 #define CELL_Y_SZ 8
47 #define CELL_X ((PIX_X - (2 * PIX_BORDER)) / CELL_X_SZ) /* tiles in display */
48 #define CELL_Y ((PIX_Y - (2 * PIX_BORDER)) / CELL_Y_SZ) /* tiles in display */
49
50 #define PALETTE_ENTRIES 16
51 static const DCPU16_WORD palette_default_[PALETTE_ENTRIES] = {
52 0x0000, /* black */
53 0x000a, /* blue */
54 0x00a0, /* green */
55 0x00aa, /* cyan */
56 0x0a05, /* red */
57 0x0a0f, /* magenta */
58 0x0aa5, /* yellow */
59 0x0aaf, /* pale blue */
60 0x0555, /* grey */
61 0x055f, /* light blue */
62 0x05f5, /* light green*/
63 0x05ff, /* light cyan */
64 0x0f55, /* light red */
65 0x0f5f, /* light magenta */
66 0x0ff5, /* light yellow */
67 0x0fff, /* white */
68 };
69
70 struct pixel_ {
71 unsigned char r;
72 unsigned char g;
73 unsigned char b;
74 unsigned char a;
75 };
76
77 struct lem1802_ {
78 long long cycle_activated; /* for tracking 'turn on delay' */
79
80 DCPU16_WORD video_base;
81 DCPU16_WORD font_base;
82 DCPU16_WORD palette_base;
83 DCPU16_WORD border_color;
84 struct pixel_ *pixbuf;
85
86 unsigned int refresh_rate; /* redraw every n cycles */
87 unsigned int refresh_tally_; /* tick */
88
89 unsigned int blink_rate; /* toggle every n cycles? still figuring this out.. */
90 unsigned int blink_tally_; /* tick */
91 unsigned int blink_state;
92
93 int (*render)(void *, struct pixel_ *, size_t, size_t);
94 void *renderer_data;
95 };
96
97 static inline
98 void pixel_color_(struct pixel_ *pix, DCPU16_WORD color) {
99 pix->r = (color & 0x000f) | ((color & 0x000f) << 8);
100 pix->g = ((color & 0x00f0) >> 8) | (color & 0x00f0);
101 pix->b = ((color & 0x0f00) >> 8) | ((color & 0x0f00) >> 16);
102 pix->a = ((color & 0xf000) >> 16) | ((color & 0xf000) >> 24);
103 }
104
105 static
106 void pixbuf_border_paint_(struct pixel_ *pixbuf, struct pixel_ *border) {
107 size_t x, y, i;
108
109 TRACE("%s>> painted border", __func__);
110
111 /* top */
112 for (y = 0; y < PIX_BORDER; y++) {
113 for (x = 0; x < PIX_X; x++) {
114 i = (y * PIX_X) + x;
115 pixbuf[i] = *border;
116 i = ((PIX_Y - y) * PIX_X) + x;
117 pixbuf[i] = *border;
118 }
119 }
120
121 /* sides */
122 for (y = PIX_BORDER; y < (PIX_Y - PIX_BORDER + 1); y++)
123 for (x = 0; x < PIX_BORDER; x++) {
124 i = (y * PIX_X) + x;
125 pixbuf[i] = *border;
126 pixbuf[i + (PIX_X - PIX_BORDER)] = *border;
127 }
128 }
129
130 static
131 void font_tile_paint_(struct pixel_ *p, struct pixel_ *fg, struct pixel_ *bg, DCPU16_WORD *tile) {
132 size_t pix_x, pix_y;
133 unsigned char *font_bitmap = (unsigned char *)tile;
134
135 TRACE("%s>> fg:(%u,%u,%u) bg:(%u,%u,%u) font_bitmap:%02x %02x %02x %02x", __func__,
136 fg->r, fg->g, fg->b,
137 bg->r, bg->g, bg->b,
138 font_bitmap[0], font_bitmap[1], font_bitmap[2], font_bitmap[3]);
139
140 for (pix_x = 0; pix_x < CELL_X_SZ; pix_x++) {
141 for (pix_y = 0; pix_y < CELL_Y_SZ; pix_y++) {
142 if ((font_bitmap[pix_x] >> pix_y) & 0x01)
143 p[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = *fg;
144 else
145 p[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = *bg;
146 }
147 }
148 }
149
150 static
151 void pixbuf_addr_paint_(struct pixel_ *pixbuf, DCPU16_WORD *mem, DCPU16_WORD base, DCPU16_WORD addr, DCPU16_WORD *palette, DCPU16_WORD *tiles, unsigned int blink_state) {
152 struct pixel_ *tilestart = pixbuf; /* start of display */
153 unsigned int cell_x = addr % (PIX_X / CELL_X_SZ),
154 cell_y = addr / (PIX_X / CELL_X_SZ);
155 struct pixel_ fg, bg;
156 DCPU16_WORD *font_bitmap;
157 int blink;
158
159 cell_x = (addr - base) % CELL_X;
160 cell_y = (addr - base) / CELL_X;
161
162 TRACE("%s>> addr:0x%04x col:%u row:%u v:%hu",
163 __func__,
164 addr,
165 cell_x, cell_y, mem[addr]);
166
167 blink = mem[addr] & 0x0080;
168
169 /* tiles take two words each */
170 font_bitmap = tiles + (2 * (mem[addr] & 0x7f));
171
172 pixel_color_(&bg, palette[(mem[addr] >> 8) & 0x0f]);
173 if (blink && blink_state)
174 pixel_color_(&fg, palette[(mem[addr] >> 8) & 0x0f]);
175 else
176 pixel_color_(&fg, palette[(mem[addr] >> 12) & 0x0f]);
177
178 tilestart += (PIX_X * PIX_BORDER); /* skip top border */
179 tilestart += (CELL_Y_SZ * PIX_X) * cell_y; /* skip down to row */
180
181 tilestart += PIX_BORDER; /* skip side border */
182 tilestart += (CELL_X_SZ) * cell_x; /* skip to column */
183
184 font_tile_paint_(tilestart, &fg, &bg, font_bitmap);
185 }
186
187 static
188 void lem1802_pixbuf_refresh_full_(struct lem1802_ *display, DCPU16_WORD *mem) {
189 struct pixel_ border;
190 size_t tile;
191
192 TRACE("%s>> video_base:0x%04x", __func__, display->video_base);
193
194 if (display->video_base == 0) {
195 /* disconnected, blank display. static might be fun, too */
196 memset(display->pixbuf, 0, PIX_X * PIX_Y * sizeof *display->pixbuf);
197 return;
198 }
199
200 pixel_color_(&border, display->border_color);
201 pixbuf_border_paint_(display->pixbuf, &border);
202
203 for (tile = 0; tile < CELL_X * CELL_Y; tile++) {
204 pixbuf_addr_paint_(display->pixbuf,
205 mem,
206 display->video_base,
207 display->video_base + tile,
208 display->palette_base ? mem + display->palette_base : palette_default_,
209 display->font_base ? mem + display->font_base : (unsigned short *)chargen_4x8_glyphs,
210 display->blink_state);
211 }
212 }
213
214 static
215 int pixbuf_render_pnm_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
216 FILE *f = (FILE *)data;
217 size_t i;
218 fprintf(f, "P6\n"
219 "%lu %lu\n"
220 "255\n",
221 x, y);
222 for (i = 0; i < x * y; i++) {
223 fwrite(&pixbuf[i].r, 1, 1, f);
224 fwrite(&pixbuf[i].g, 1, 1, f);
225 fwrite(&pixbuf[i].b, 1, 1, f);
226 }
227 fclose(f);
228
229 return 0;
230 }
231
232 #ifdef HAVE_LIBPNG
233 static
234 void pixbuf_render_png_user_warning_(png_structp png, png_const_charp msg) {
235 (void)png, (void)msg;
236 }
237 static
238 void pixbuf_render_png_user_error_(png_structp png, png_const_charp msg) {
239 (void)png, (void)msg;
240 }
241 static
242 int pixbuf_render_png_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
243 FILE *f = (FILE *)data;
244 int retval = 0;
245 png_structp png;
246 png_infop info;
247 size_t i;
248
249 png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, pixbuf_render_png_user_error_, pixbuf_render_png_user_warning_);
250 if (png == NULL) {
251 goto f_done;
252 }
253
254 info = png_create_info_struct(png);
255 if (info == NULL) {
256 png_destroy_write_struct(&png, (png_infopp)NULL);
257 goto f_done;
258 }
259
260 if (setjmp(png_jmpbuf(png))) {
261 png_destroy_write_struct(&png, &info);
262 goto f_done;
263 }
264
265 png_init_io(png, f);
266 png_set_IHDR(png, info, x, y, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
267 png_set_invert_alpha(png);
268 png_write_info(png, info);
269 for (i = 0; i < y; i++) {
270 png_write_row(png, (unsigned char *)(pixbuf + (i * x)));
271 }
272 png_write_end(png, info);
273
274 png_destroy_write_struct(&png, &info);
275
276 f_done:
277 fclose(f);
278
279 return retval;
280 }
281 #endif /* HAVE_LIBPNG */
282
283 #ifdef HAVE_LIBVNCSERVER
284 struct vnc_server_ {
285 rfbScreenInfoPtr rfbScreen;
286 };
287
288 /* create and return a new struct vnc_server_, for use as a vnc renderer's generic data */
289 void *lem1802_vnc_init_data(int argc, char *argv[], struct dcpu16_hw *hw) {
290 struct vnc_server_ *s;
291 struct pixel_ *pixbuf = ((struct lem1802_ *)(hw->data))->pixbuf;
292 int paddedWidth = PIX_X + ( (PIX_X & 3) ? (4 - (PIX_X & 3)) : 0 );
293 int height = PIX_Y;
294 int bitsPerSample = 8;
295 int samplesPerPixel = 3;
296 int bytesPerPixel = 4;
297
298 s = calloc(1, sizeof *s);
299 if (s == NULL)
300 return NULL;
301
302 s->rfbScreen = rfbGetScreen(&argc, argv, paddedWidth, height, bitsPerSample, samplesPerPixel, bytesPerPixel);
303 if (s->rfbScreen == NULL) {
304 free(s);
305 return NULL;
306 }
307
308 s->rfbScreen->desktopName = "NYA ELEKTRISKA LEM1802";
309 s->rfbScreen->alwaysShared = TRUE;
310
311 #if 0
312 s->rfbScreen->kbdAddEvent = HandleKey; */
313 s->rfbScreen->httpDir = "../classes";
314 #endif
315
316 s->rfbScreen->frameBuffer = (char *)pixbuf;
317
318 rfbInitServer(s->rfbScreen);
319
320 rfbRunEventLoop(s->rfbScreen,-1,TRUE);
321
322 TRACE("%s>> initialized new vnc server", __func__);
323
324 return s;
325 }
326
327 static
328 int pixbuf_render_vnc_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
329 int retval = 0;
330 struct vnc_server_ *s = (struct vnc_server_ *)data;
331
332 (void)pixbuf;
333
334 /* derp */
335 rfbMarkRectAsModified(s->rfbScreen,0,0,x,y);
336
337 TRACE("%s>>", __func__);
338
339 return retval;
340 }
341 #endif /* HAVE_LIBVNCSERVER */
342
343
344 static
345 void lem1802_reset_(struct dcpu16 *vm, void *data) {
346 struct lem1802_ *display = (struct lem1802_ *)data;
347
348 (void)vm;
349
350 display->cycle_activated = 0;
351 display->video_base = 0;
352 display->font_base = 0;
353 display->palette_base = 0;
354 display->border_color = 0;
355
356 memset(display->pixbuf, 0, PIX_X * PIX_Y * sizeof *display->pixbuf);
357
358 display->refresh_tally_ = 0;
359 display->blink_tally_ = 0;
360 display->blink_state = 0;
361
362 #if DEBUG
363 vm->trace_cb_("%s>>", __func__);
364 #endif /* DEBUG */
365 }
366
367 static
368 void lem1802_cycle_(struct dcpu16 *vm, void *data) {
369 struct lem1802_ *display = (struct lem1802_ *)data;
370
371 (void)vm;
372 /*
373 maybe just step through video memory (if set)
374 one word per clock..? could just cheat and
375 use accounting callbacks..
376
377 for now just count cycles and issue a full refresh/render
378 every so often
379 */
380
381 display->blink_tally_++;
382 if (display->blink_tally_ >= display->blink_rate) {
383 display->blink_tally_ = 0;
384 display->blink_state ^= 1;
385 TRACE("%s>> blink:%u (%u cycles)", __func__, display->blink_state, display->blink_rate);
386 }
387
388 display->refresh_tally_++;
389 if (display->refresh_tally_ >= display->refresh_rate) {
390 display->refresh_tally_ = 0;
391 if (display->render)
392 TRACE("%s>> refresh", __func__);
393 lem1802_pixbuf_refresh_full_(display, vm->ram);
394 display->render(display->renderer_data, display->pixbuf, PIX_X, PIX_Y);
395 }
396 }
397
398 static
399 void lem1802_hwi_(struct dcpu16 *vm, void *data) {
400 struct lem1802_ *display = (struct lem1802_ *)data;
401 DCPU16_WORD reg_a = vm->reg[DCPU16_REG_A];
402 DCPU16_WORD reg_b = vm->reg[DCPU16_REG_B];
403 size_t i;
404
405 TRACE("%s>> A:0x%04x B:0x%04x", __func__, reg_a, reg_b);
406
407 switch (reg_a) {
408 case 0: /* MEM_MAP_SCREEN */
409 if (display->cycle_activated == 0 && reg_b) {
410 display->cycle_activated = vm->cycle;
411 }
412 display->video_base = reg_b;
413 if (reg_b == 0)
414 display->cycle_activated = 0;
415 break;
416
417 case 1: /* MEM_MAP_FONT */
418 display->font_base = reg_b;
419 break;
420
421 case 2: /* MEM_MAP_PALETTE */
422 display->palette_base = reg_b;
423 break;
424
425 case 3: /* SET_BORDER_COLOR */
426 display->border_color = reg_b & 0x000f;
427 break;
428
429 case 4: /* MEM_DUMP_FONT */
430 for (i = 0; i < 128 ; i++) {
431 vm->ram[reg_b] = chargen_4x8_glyphs[reg_b][0] << 8;
432 vm->ram[reg_b] |= chargen_4x8_glyphs[reg_b][1];
433 reg_b += 1;
434 vm->ram[reg_b] = chargen_4x8_glyphs[reg_b][2] << 8;
435 vm->ram[reg_b] |= chargen_4x8_glyphs[reg_b][3];
436 reg_b += 1;
437 }
438 vm->cycle += 256;
439 break;
440
441 case 5: /* MEM_DUMP_PALETTE */
442 for (i = 0; i < 16; i++) {
443 vm->ram[reg_b] = palette_default_[i];
444 reg_b += 1;
445 }
446 vm->cycle += 16;
447 break;
448 }
449 }
450
451 static struct renderer_ {
452 char *name;
453 char *args;
454 int (*renderer)(void *, struct pixel_ *, size_t, size_t);
455 } lem1802_renderers_[] = {
456 { "pnm", "filename", pixbuf_render_pnm_ },
457 #ifdef HAVE_LIBPNG
458 { "png", "filename", pixbuf_render_png_ },
459 #endif /* HAVE_LIBPNG */
460 #ifdef HAVE_LIBVNCSERVER
461 { "vnc", "", pixbuf_render_vnc_ },
462 #endif /* HAVE_LIBVNCSERVER */
463 { "none", "", NULL },
464 { NULL, NULL, NULL }
465 };
466
467 int lem1802_renderer_set(struct dcpu16_hw *hw, const char *renderer, void *data) {
468 struct renderer_ *r;
469
470 for (r = lem1802_renderers_; r->renderer; r++) {
471 if (strcmp(renderer, r->name) == 0) {
472 ((struct lem1802_ *)(hw->data))->render = r->renderer;
473 ((struct lem1802_ *)(hw->data))->renderer_data = data;
474 TRACE("%s>> renderer set to %s", __func__, renderer);
475 return 0;
476 }
477 }
478
479 return -1;
480 }
481
482 char *lem1802_renderers_iter(void **iterp, char **name, char **args) {
483 struct renderer_ **r = (struct renderer_ **)iterp;
484
485 if (*r == NULL)
486 *r = lem1802_renderers_;
487 else
488 (*r)++;
489
490 if ((*r)->name == NULL) {
491 *r = NULL;
492 return NULL;
493 }
494
495 *name = (*r)->name;
496 *args = (*r)->args;
497
498 return (*r)->name;
499 }
500
501 /* instantitate a new display */
502 struct dcpu16_hw *lem1802_new(struct dcpu16 *vm) {
503 struct dcpu16_hw *hw;
504
505 hw = calloc(1, sizeof *hw);
506 if (hw == NULL) {
507 vm->warn_cb_("%s():%s", "calloc", strerror(errno));
508 return NULL;
509 }
510
511 memcpy(hw, &hw_, sizeof *hw);
512
513 hw->data = calloc(1, sizeof(struct dcpu16_hw));
514 if (hw->data == NULL) {
515 vm->warn_cb_("%s():%s", "calloc", strerror(errno));
516 free(hw);
517 return NULL;
518 }
519
520 ((struct lem1802_ *)(hw->data))->pixbuf = calloc(1, PIX_X * PIX_Y * sizeof *((struct lem1802_ *)(hw->data))->pixbuf);
521 if (((struct lem1802_ *)(hw->data))->pixbuf == NULL) {
522 vm->warn_cb_("%s():%s", "calloc", strerror(errno));
523 free(hw->data);
524 free(hw);
525 return NULL;
526 }
527
528 ((struct lem1802_ *)(hw->data))->refresh_rate = 1666;
529 ((struct lem1802_ *)(hw->data))->blink_rate = 75000;
530
531 return hw;
532 }
533
534 void lem1802_del(struct dcpu16_hw **hw) {
535 if (hw) {
536 if (*hw) {
537 if ((*hw)->data) {
538 if (((struct lem1802_ *)(*hw)->data)->pixbuf) {
539 free(((struct lem1802_ *)(*hw)->data)->pixbuf);
540 ((struct lem1802_ *)(*hw)->data)->pixbuf = NULL;
541 }
542 free((*hw)->data);
543 (*hw)->data = NULL;
544 }
545 free(*hw);
546 }
547 *hw = NULL;
548 }
549 }