merge
[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 /* lem1802
22 *
23 * TODO:
24 * multiple vnc displays
25 */
26
27 #define MSG_(__level__, __vm__, ...) do { ((__vm__) ? ((struct dcpu16 *)(__vm__))->msg_cb_ : dcpu16_msg_)(__level__, __VA_ARGS__); } while (0)
28 #define MSG_INFO(__vm__, ...) MSG_(DCPU16_MSG_INFO, __vm__, __VA_ARGS__)
29 #define MSG_ERROR(__vm__, ...) MSG_(DCPU16_MSG_ERROR, __vm__, __VA_ARGS__)
30 #ifdef DEBUG
31 #define MSG_DEBUG(__vm__, ...) MSG_(DCPU16_MSG_DEBUG, __vm__, __VA_ARGS__)
32 #else /* DEBUG */
33 #define MSG_DEBUG(__vm__, ...) do { } while (0)
34 #endif /* DEBUG */
35
36 #ifdef WANT_VARIADIC_VOIDP_CAST
37 #define VOIDP(__x__) ((void *)(__x__))
38 #define VOIDFP(__x__) ((void *)(size_t)(__x__))
39 #else
40 #define VOIDP(__x__) (__x__)
41 #define VOIDFP(__x__) (__x__)
42 #endif
43
44 #define LEM1802_POWER_ON_CYCLES 100000 /* 'about one second' */
45
46 #define PIX_X 160 /* pixels in display */
47 #define PIX_Y 128 /* pixels in display */
48 #define PIX_BORDER 16 /* border pixels from edge to first tile */
49 #define CELL_X_SZ 4
50 #define CELL_Y_SZ 8
51 #define CELL_X ((PIX_X - (2 * PIX_BORDER)) / CELL_X_SZ) /* tiles in display */
52 #define CELL_Y ((PIX_Y - (2 * PIX_BORDER)) / CELL_Y_SZ) /* tiles in display */
53
54 #define PALETTE_ENTRIES 16
55 static const DCPU16_WORD palette_default_[PALETTE_ENTRIES] = {
56 0x0000, /* black */
57 0x000a, /* blue */
58 0x00a0, /* green */
59 0x00aa, /* cyan */
60 0x0a05, /* red */
61 0x0a0f, /* magenta */
62 0x0aa5, /* yellow */
63 0x0aaf, /* pale blue */
64 0x0555, /* grey */
65 0x055f, /* light blue */
66 0x05f5, /* light green*/
67 0x05ff, /* light cyan */
68 0x0f55, /* light red */
69 0x0f5f, /* light magenta */
70 0x0ff5, /* light yellow */
71 0x0fff /* white */
72 };
73
74 struct pixel_ {
75 unsigned char r;
76 unsigned char g;
77 unsigned char b;
78 unsigned char a; /* unused */
79 };
80
81 struct lem1802_ {
82 long long cycle_activated; /* running since */
83 long long cycles_until_active_; /* for tracking power-up delay */
84
85 DCPU16_WORD video_base;
86 DCPU16_WORD font_base;
87 DCPU16_WORD palette_base;
88 DCPU16_WORD border_color;
89 struct pixel_ *pixbuf;
90
91 unsigned int refresh_rate; /* redraw every n cycles */
92 unsigned int refresh_tally_; /* tick */
93
94 unsigned int blink_rate; /* toggle every n cycles? still figuring this out.. */
95 unsigned int blink_tally_; /* tick */
96 unsigned int blink_state;
97
98 enum cycle_state_ {
99 CYCLE_IDLE,
100 CYCLE_COPY_TO_RAM,
101 } cycle_state_;
102 const DCPU16_WORD *cycle_state_copy_src_ptr_;
103 DCPU16_WORD cycle_state_copy_dst_addr_;
104 size_t cycle_state_copy_words_;
105
106 int (*render)(void *, struct pixel_ *, size_t, size_t);
107 void *renderer_data;
108 };
109
110 static
111 long long power_on_cycles_(void) {
112 struct tv;
113 long long r = 0;
114
115 #if WANT_DELAY_START
116 gettimeofday(&tv, NULL);
117 /* consider the 'about' in 'about one second' to be +/- 10% */
118 r += LEM1802_POWER_ON_CYCLES - (LEM1802_POWER_ON_CYCLES / 10);
119 r += tv.tv_usec % (LEM1802_POWER_ON_CYCLES / 5);
120 #endif
121
122 return r;
123 }
124
125 static inline
126 void pixel_color_(struct pixel_ *pix, DCPU16_WORD color) {
127 unsigned char x;
128
129 x = (color >> 0) & 0x000f;
130 pix->r = x | (x << 4);
131
132 x = (color >> 4) & 0x000f;
133 pix->g = x | (x << 4);
134
135 x = (color >> 8) & 0x000f;
136 pix->b = x | (x << 4);
137
138 x = (color >> 12) & 0x000f;
139 pix->a = x | (x << 4);
140 }
141
142 static
143 void pixbuf_border_paint_(struct pixel_ *pixbuf, struct pixel_ *border) {
144 size_t x, y, i;
145
146 MSG_DEBUG(NULL, "%s>> painting border", __func__);
147
148 /* top */
149 for (y = 0; y < PIX_BORDER; y++) {
150 for (x = 0; x < PIX_X; x++) {
151 i = (y * PIX_X) + x;
152 pixbuf[i] = *border;
153 i = ((PIX_Y - y) * PIX_X) + x;
154 pixbuf[i] = *border;
155 }
156 }
157
158 /* sides */
159 for (y = PIX_BORDER; y < (PIX_Y - PIX_BORDER + 1); y++)
160 for (x = 0; x < PIX_BORDER; x++) {
161 i = (y * PIX_X) + x;
162 pixbuf[i] = *border;
163 pixbuf[i + (PIX_X - PIX_BORDER)] = *border;
164 }
165 }
166
167 static
168 void font_tile_paint_(struct pixel_ *p, struct pixel_ *fg, struct pixel_ *bg, DCPU16_WORD *tile) {
169 size_t pix_x, pix_y;
170 unsigned char *font_bitmap = (unsigned char *)tile;
171
172 #if 0
173 MSG_DEBUG(NULL, "%s>> fg:(%u,%u,%u) bg:(%u,%u,%u) font_bitmap:%02x %02x %02x %02x", __func__,
174 fg->r, fg->g, fg->b,
175 bg->r, bg->g, bg->b,
176 font_bitmap[0], font_bitmap[1], font_bitmap[2], font_bitmap[3]);
177 #endif
178
179 for (pix_x = 0; pix_x < CELL_X_SZ; pix_x++) {
180 for (pix_y = 0; pix_y < CELL_Y_SZ; pix_y++) {
181 if ((font_bitmap[pix_x] >> pix_y) & 0x01)
182 p[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = *fg;
183 else
184 p[((CELL_Y_SZ - pix_y - 1) * PIX_X) + pix_x] = *bg;
185 }
186 }
187 }
188
189 static
190 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) {
191 struct pixel_ *tilestart = pixbuf; /* start of display */
192 unsigned int cell_x = addr % (PIX_X / CELL_X_SZ),
193 cell_y = addr / (PIX_X / CELL_X_SZ);
194 struct pixel_ fg, bg;
195 DCPU16_WORD *font_bitmap;
196 int blink;
197
198 cell_x = (addr - base) % CELL_X;
199 cell_y = (addr - base) / CELL_X;
200
201 #if 0
202 MSG_DEBUG(NULL, "%s>> addr:0x%04x col:%u row:%u v:%hu",
203 __func__,
204 addr,
205 cell_x, cell_y, mem[addr]);
206 #endif
207
208 blink = mem[addr] & 0x0080;
209
210 /* tiles take two words each */
211 font_bitmap = tiles + (2 * (mem[addr] & 0x7f));
212
213 pixel_color_(&bg, palette[(mem[addr] >> 8) & 0x0f]);
214 if (blink && blink_state)
215 pixel_color_(&fg, palette[(mem[addr] >> 8) & 0x0f]);
216 else
217 pixel_color_(&fg, palette[(mem[addr] >> 12) & 0x0f]);
218
219 tilestart += (PIX_X * PIX_BORDER); /* skip top border */
220 tilestart += (CELL_Y_SZ * PIX_X) * cell_y; /* skip down to row */
221
222 tilestart += PIX_BORDER; /* skip side border */
223 tilestart += (CELL_X_SZ) * cell_x; /* skip to column */
224
225 font_tile_paint_(tilestart, &fg, &bg, font_bitmap);
226 }
227
228 static
229 void lem1802_pixbuf_refresh_full_(struct lem1802_ *display, DCPU16_WORD *mem) {
230 struct pixel_ border;
231 size_t tile;
232
233 #if 0
234 MSG_DEBUG(NULL, "%s>> video_base:0x%04x", __func__, display->video_base);
235 #endif
236
237 if (display->cycles_until_active_) {
238 /* show cute power-up sequence.. */
239 memset(display->pixbuf, 0, PIX_X * PIX_Y * sizeof *display->pixbuf);
240 return;
241 }
242
243 if (display->video_base == 0) {
244 /* disconnected, blank display */
245 memset(display->pixbuf, 0, PIX_X * PIX_Y * sizeof *display->pixbuf);
246 return;
247 }
248
249 pixel_color_(&border, display->border_color);
250 pixbuf_border_paint_(display->pixbuf, &border);
251
252 for (tile = 0; tile < CELL_X * CELL_Y; tile++) {
253 pixbuf_addr_paint_(display->pixbuf,
254 mem,
255 display->video_base,
256 display->video_base + tile,
257 display->palette_base ? mem + display->palette_base : (DCPU16_WORD *)palette_default_,
258 display->font_base ? mem + display->font_base : (DCPU16_WORD *)chargen_4x8_glyphs,
259 display->blink_state);
260 }
261 }
262
263 static
264 int pixbuf_render_pnm_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
265 FILE *f = (FILE *)data;
266 size_t i;
267 fprintf(f, "P6\n"
268 "%lu %lu\n"
269 "255\n",
270 x, y);
271 for (i = 0; i < x * y; i++) {
272 fwrite(&pixbuf[i].r, 1, 1, f);
273 fwrite(&pixbuf[i].g, 1, 1, f);
274 fwrite(&pixbuf[i].b, 1, 1, f);
275 }
276 fclose(f);
277
278 return 0;
279 }
280
281 #ifdef HAVE_LIBPNG
282 static
283 int pixbuf_render_png_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
284 FILE *f = (FILE *)data;
285 int retval = 0;
286 png_structp png;
287 png_infop info;
288 size_t i;
289
290 png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
291 if (png == NULL) {
292 goto f_done;
293 }
294
295 info = png_create_info_struct(png);
296 if (info == NULL) {
297 png_destroy_write_struct(&png, (png_infopp)NULL);
298 goto f_done;
299 }
300
301 if (setjmp(png_jmpbuf(png))) {
302 png_destroy_write_struct(&png, &info);
303 goto f_done;
304 }
305
306 png_init_io(png, f);
307 png_set_IHDR(png, info, x, y, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
308 png_set_invert_alpha(png);
309 png_write_info(png, info);
310 for (i = 0; i < y; i++) {
311 png_write_row(png, (unsigned char *)(pixbuf + (i * x)));
312 }
313 png_write_end(png, info);
314
315 png_destroy_write_struct(&png, &info);
316
317 f_done:
318 fclose(f);
319
320 return retval;
321 }
322 #endif /* HAVE_LIBPNG */
323
324 #ifdef HAVE_LIBVNCSERVER
325 /* create and return a new screen, easiest to do here because we know the screen dimensions */
326 static
327 rfbScreenInfoPtr lem1802_rfb_new_(int argc, char *argv[]) {
328 rfbScreenInfoPtr s;
329 int paddedWidth = PIX_X + ( (PIX_X & 3) ? (4 - (PIX_X & 3)) : 0 );
330 int height = PIX_Y;
331 int bitsPerSample = 8;
332 int samplesPerPixel = 3;
333 int bytesPerPixel = 4;
334
335 s = rfbGetScreen(&argc, argv, paddedWidth, height, bitsPerSample, samplesPerPixel, bytesPerPixel);
336 if (s == NULL)
337 return NULL;
338
339 s->alwaysShared = TRUE;
340
341 #if 0
342 s->httpDir = "../classes";
343 #endif
344
345 MSG_DEBUG(NULL, "%s>> s:%p", __func__, VOIDP(s));
346 MSG_DEBUG(NULL, "%s>> s->kbdAddEvent:%p s->frameBuffer:%p", __func__, VOIDFP(s->kbdAddEvent), s->frameBuffer);
347
348 return s;
349 }
350
351
352 /* notify rfb server that pixels may have changed */
353 static
354 int pixbuf_render_vnc_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
355 rfbScreenInfoPtr s = (rfbScreenInfoPtr)data;
356 int retval = 0;
357
358 (void)pixbuf;
359
360 MSG_DEBUG(NULL, "%s>> s:%p", __func__, s);
361
362 /* derp */
363 if (s)
364 rfbMarkRectAsModified(s, 0, 0, x, y);
365
366 MSG_DEBUG(NULL, "%s>>", __func__);
367
368 return retval;
369 }
370 #endif /* HAVE_LIBVNCSERVER */
371
372
373 static
374 void lem1802_reset_(struct dcpu16 *vm, struct dcpu16_hw *hw) {
375 struct lem1802_ *display = (struct lem1802_ *)hw->data;
376
377 (void)vm;
378
379 display->cycle_activated = 0;
380 display->video_base = 0;
381 display->font_base = 0;
382 display->palette_base = 0;
383 display->border_color = 0;
384
385 memset(display->pixbuf, 0, PIX_X * PIX_Y * sizeof *display->pixbuf);
386
387 display->refresh_tally_ = 0;
388 display->blink_tally_ = 0;
389 display->blink_state = 0;
390
391 display->cycle_state_ = 0;
392
393 MSG_DEBUG(vm, "%s>>", __func__);
394 }
395
396 static
397 void lem1802_cycle_(struct dcpu16 *vm, struct dcpu16_hw *hw) {
398 struct lem1802_ *display = (struct lem1802_ *)hw->data;
399
400 (void)vm;
401 /*
402 for more cycle-accuracy, could step through video memory, if set,
403 one word per clock..
404 for now just count cycles and issue a full refresh/render
405 every so often
406 */
407
408 display->blink_tally_++;
409 if (display->blink_tally_ >= display->blink_rate) {
410 display->blink_tally_ = 0;
411 display->blink_state ^= 1;
412 MSG_DEBUG(vm, "%s>> blink:%u (%u cycles)", __func__, display->blink_state, display->blink_rate);
413 }
414
415 display->refresh_tally_++;
416 if (display->refresh_tally_ >= display->refresh_rate) {
417 display->refresh_tally_ = 0;
418 if (display->render)
419 MSG_DEBUG(vm, "%s>> refresh", __func__);
420 lem1802_pixbuf_refresh_full_(display, vm->ram);
421 display->render(display->renderer_data, display->pixbuf, PIX_X, PIX_Y);
422 }
423
424 switch (display->cycle_state_) {
425 case CYCLE_IDLE:
426 break;
427
428 case CYCLE_COPY_TO_RAM:
429 MSG_DEBUG(vm, "%s>> copy_to_ram words:%zu src:%p dst_addr:0x%04x",
430 __func__,
431 display->cycle_state_copy_words_,
432 VOIDP(display->cycle_state_copy_src_ptr_),
433 display->cycle_state_copy_dst_addr_);
434 vm->ram[display->cycle_state_copy_dst_addr_] = *display->cycle_state_copy_src_ptr_;
435 display->cycle_state_copy_dst_addr_++;
436 display->cycle_state_copy_src_ptr_++;
437 display->cycle_state_copy_words_--;
438 if (display->cycle_state_copy_words_ == 0) {
439 display->cycle_state_ = CYCLE_IDLE;
440 }
441 break;
442 }
443
444 if (display->cycles_until_active_) {
445 display->cycles_until_active_--;
446 if (display->cycles_until_active_ == 0) {
447 MSG_DEBUG(vm, "%s>> display now active", __func__);
448 }
449 }
450 }
451
452 static
453 void lem1802_hwi_(struct dcpu16 *vm, struct dcpu16_hw *hw) {
454 struct lem1802_ *display = (struct lem1802_ *)hw->data;
455 DCPU16_WORD reg_a = vm->reg[DCPU16_REG_A];
456 DCPU16_WORD reg_b = vm->reg[DCPU16_REG_B];
457
458 MSG_DEBUG(vm, "%s>> A:0x%04x B:0x%04x", __func__, reg_a, reg_b);
459
460 switch (reg_a) {
461 case 0: /* MEM_MAP_SCREEN */
462 if (display->cycle_activated == 0 && reg_b) {
463 display->cycle_activated = vm->cycle_;
464 display->cycles_until_active_ = power_on_cycles_();
465 }
466 display->video_base = reg_b;
467 if (reg_b == 0)
468 display->cycle_activated = 0;
469 MSG_DEBUG(vm, "%s>> video_base:0x%04x", __func__, reg_b);
470 break;
471
472 case 1: /* MEM_MAP_FONT */
473 display->font_base = reg_b;
474 MSG_DEBUG(vm, "%s>> font_base:0x%04x", __func__, reg_b);
475 break;
476
477 case 2: /* MEM_MAP_PALETTE */
478 display->palette_base = reg_b;
479 MSG_DEBUG(vm, "%s>> palette_base:0x%04x", __func__, reg_b);
480 break;
481
482 case 3: /* SET_BORDER_COLOR */
483 display->border_color = reg_b & 0x000f;
484 MSG_DEBUG(vm, "%s>> border_color_index:0x%01x", __func__, reg_b & 0x000f);
485 break;
486
487 case 4: /* MEM_DUMP_FONT */
488 display->cycle_state_copy_src_ptr_ = (DCPU16_WORD *)chargen_4x8_glyphs;
489 display->cycle_state_copy_dst_addr_ = reg_b;
490 display->cycle_state_copy_words_ = 256;
491 display->cycle_state_ = CYCLE_COPY_TO_RAM;
492 MSG_DEBUG(vm, "%s>> copying default font into 0x%04x - 0x%04x", __func__, reg_b, reg_b + 256);
493 dcpu16_cycle_inc(vm, 256);
494 break;
495
496 case 5: /* MEM_DUMP_PALETTE */
497 display->cycle_state_copy_src_ptr_ = palette_default_;
498 display->cycle_state_copy_dst_addr_ = reg_b;
499 display->cycle_state_copy_words_ = 16;
500 display->cycle_state_ = CYCLE_COPY_TO_RAM;
501 MSG_DEBUG(vm, "%s>> copying default palette into 0x%04x - 0x%04x", __func__, reg_b, reg_b + 16);
502 dcpu16_cycle_inc(vm, 16);
503 break;
504 }
505 }
506
507 static struct renderer_ {
508 char *name;
509 char *args;
510 int (*renderer)(void *, struct pixel_ *, size_t, size_t);
511 } lem1802_renderers_[] = {
512 { "pnm", "filename", pixbuf_render_pnm_ },
513 #ifdef HAVE_LIBPNG
514 { "png", "filename", pixbuf_render_png_ },
515 #endif /* HAVE_LIBPNG */
516 #ifdef HAVE_LIBVNCSERVER
517 { "vnc", "", pixbuf_render_vnc_ },
518 #endif /* HAVE_LIBVNCSERVER */
519 { "none", "", NULL },
520 { NULL, NULL, NULL }
521 };
522
523 static
524 char *lem1802_renderers_iter_(void **iterp, char **name, char **args) {
525 struct renderer_ **r = (struct renderer_ **)iterp;
526
527 if (*r == NULL)
528 *r = lem1802_renderers_;
529 else
530 (*r)++;
531
532 if ((*r)->name == NULL) {
533 *r = NULL;
534 return NULL;
535 }
536
537 *name = (*r)->name;
538 *args = (*r)->args;
539
540 return (*r)->name;
541 }
542
543 static
544 int lem1802_data_init_(struct dcpu16_hw *hw, void *data) {
545 (void)data;
546
547 hw->data = calloc(1, sizeof(struct lem1802_));
548 if (hw->data == NULL) {
549 MSG_ERROR(hw->vm, "%s():%s", "calloc", strerror(errno));
550 return -1;
551 }
552
553 ((struct lem1802_ *)(hw->data))->pixbuf = calloc(PIX_X * PIX_Y, sizeof *((struct lem1802_ *)(hw->data))->pixbuf);
554 if (((struct lem1802_ *)(hw->data))->pixbuf == NULL) {
555 MSG_ERROR(hw->vm, "%s():%s", "calloc", strerror(errno));
556 free(hw->data);
557 hw->data = NULL;
558 return -1;
559 }
560
561 ((struct lem1802_ *)(hw->data))->refresh_rate = 1666;
562 ((struct lem1802_ *)(hw->data))->blink_rate = 75000;
563
564 return 0;
565 }
566
567 static
568 void lem1802_data_free_(struct dcpu16_hw *hw) {
569 if (hw) {
570 if (hw->data) {
571 if (((struct lem1802_ *)(hw->data))->pixbuf) {
572 free(((struct lem1802_ *)(hw->data))->pixbuf);
573 ((struct lem1802_ *)(hw->data))->pixbuf = NULL;
574 }
575 free(hw->data);
576 hw->data = NULL;
577 }
578 }
579 }
580
581 static struct dcpu16_hw_ctl_cmd ctl_[] = {
582 { "blink_rate", "const unsigned int *", "unsigned int *", "sets or gets cycles per blink toggle" },
583 { "refresh_rate", "const unsigned int *", "unsigned int *", "sets or gets cycles per screen refresh" },
584 #ifdef HAVE_LIBVNCSERVER
585 { "new_rfbScreen", "struct { int argc; char **argv;} *", "rfbScreenInfoPtr *", "allocates a new rfb screen" },
586 { "associate_rfbScreen", "rfbScreenInfoPtr", "NULL", "associates this lem1802 instance with an rfb display" },
587 #endif /* HAVE_LIBVNCSERVER */
588 { "renderers_iter", "void **", "struct {char *name; char *args;} *", "returns the next renderer this module is capable of using" },
589 { "renderer", "const char *", "NULL", "sets this lem1802 instance to use renderer" },
590 { "renderer_data", "void *", "NULL", "sets renderer-specific data" },
591 { NULL, NULL, NULL, NULL }
592 };
593
594 static
595 int lem1802_data_ctl_(struct dcpu16_hw *hw, const char *cmd, void *data_in, void *data_out) {
596 if (strcmp(cmd, "blink_rate") == 0) {
597 struct lem1802_ *display = (struct lem1802_ *)hw->data;
598 const unsigned int *rate_in = (const unsigned int *)data_in;
599 unsigned int *rate_out = (unsigned int *)data_out;
600
601 if (rate_out) {
602 *rate_out = display->blink_rate;
603 }
604
605 if (rate_in) {
606 display->blink_rate = *rate_in;
607 }
608
609 MSG_DEBUG(hw->vm, "%s>> %s now:%u was:%u", __func__, "blink_rate", *rate_in, *rate_out);
610
611 return 0;
612 }
613
614 if (strcmp(cmd, "refresh_rate") == 0) {
615 struct lem1802_ *display = (struct lem1802_ *)hw->data;
616 const unsigned int *rate_in = (const unsigned int *)data_in;
617 unsigned int *rate_out = (unsigned int *)data_out;
618
619 if (rate_out) {
620 *rate_out = display->refresh_rate;
621 }
622
623 if (rate_in) {
624 display->refresh_rate = *rate_in;
625 }
626
627 MSG_DEBUG(hw->vm, "%s>> %s now:%u was:%u", __func__, "refresh_rate", *rate_in, *rate_out);
628
629 return 0;
630 }
631
632 #ifdef HAVE_LIBVNCSERVER
633 if (strcmp(cmd, "new_rfbScreen") == 0) {
634 struct args_ { int argc; char **argv;} *in = (struct args_ *)data_in;
635 rfbScreenInfoPtr *s_out = (rfbScreenInfoPtr *)data_out;
636
637 if (in == NULL || s_out == NULL)
638 return -EFAULT;
639
640 *s_out = lem1802_rfb_new_(in->argc, in->argv);
641
642 MSG_DEBUG(NULL, "%s>> %s s:%p", __func__, "new_rfbScreen", *s_out);
643
644 return 0;
645 }
646
647 if (strcmp(cmd, "associate_rfbScreen") == 0) {
648 struct lem1802_ *display = (struct lem1802_ *)hw->data;
649 rfbScreenInfoPtr rfbScreen = (rfbScreenInfoPtr)data_in;
650 (void)data_out;
651
652 if (rfbScreen == NULL)
653 return -EFAULT;
654
655 rfbScreen->desktopName = "NYA ELEKTRISKA LEM1802";
656 rfbScreen->frameBuffer = (char *)display->pixbuf;
657
658 MSG_DEBUG(hw->vm, "%s>> %s rfbScreen:%p", __func__, "associate_rfbScreen", rfbScreen);
659
660 return 0;
661 }
662 #endif /* HAVE_LIBVNCSERVER */
663
664 if (strcmp(cmd, "renderers_iter") == 0) {
665 void **iterp = (void **)data_in;
666 struct packed_out_ {
667 char *name;
668 char *args;
669 } *parg = (struct packed_out_ *)data_out;
670
671 if (iterp == NULL || parg == NULL)
672 return -EFAULT;
673
674 (void)lem1802_renderers_iter_(iterp, &parg->name, &parg->args);
675
676 MSG_DEBUG(hw->vm, "%s>> %s", __func__, "renderers_iter");
677
678 return 0;
679 }
680
681 if (strcmp(cmd, "renderer") == 0) {
682 struct lem1802_ *display = (struct lem1802_ *)hw->data;
683 char *renderer = (char *)data_in;
684 (void)data_out;
685 struct renderer_ *r;
686
687 for (r = lem1802_renderers_; r->renderer; r++) {
688 if (strcmp(renderer, r->name) == 0) {
689 display->render = r->renderer;
690 MSG_DEBUG(hw->vm, "%s>> renderer set to %s", __func__, renderer);
691 return 0;
692 }
693 }
694
695 MSG_ERROR(hw->vm, "unknown renderer '%s'", renderer);
696
697 return -ENOENT;
698 }
699
700 if (strcmp(cmd, "renderer_data") == 0) {
701 struct lem1802_ *display = (struct lem1802_ *)hw->data;
702 (void)data_out;
703
704 display->renderer_data = data_in;
705
706 MSG_DEBUG(hw->vm, "%s>> %s data:%p", __func__, "renderer_data", data_in);
707
708 return 0;
709 }
710
711 return -EINVAL;
712 }
713
714
715 struct dcpu16_hw_module dcpu16_hw_module_lem1802 = {
716 .name_ = "LEM1802 - Low Energy Monitor",
717
718 .id_l = 0xf615,
719 .id_h = 0x7349,
720 .ver = 0x1802,
721 .mfg_l = 0x8b36,
722 .mfg_h = 0x1c6c,
723 .hwi = lem1802_hwi_,
724 .cycle = lem1802_cycle_,
725 .reset = lem1802_reset_,
726
727 .data_init = lem1802_data_init_,
728 .data_free = lem1802_data_free_,
729 .ctl = lem1802_data_ctl_,
730 .ctl_cmd = ctl_,
731 };
732