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