10 #endif /* HAVE_LIBPNG */
12 #ifdef HAVE_LIBVNCSERVER
14 #include <rfb/keysym.h>
15 #endif /* HAVE_LIBVNCSERVER */
18 #include "chargen-4x8.h"
19 #include "hw_lem1802.h"
24 * multiple vnc displays
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__)
31 #define MSG_DEBUG(__vm__, ...) MSG_(DCPU16_MSG_DEBUG, __vm__, __VA_ARGS__)
33 #define MSG_DEBUG(__vm__, ...) do { } while (0)
36 #ifdef WANT_VARIADIC_VOIDP_CAST
37 #define VOIDP(__x__) ((void *)(__x__))
38 #define VOIDFP(__x__) ((void *)(size_t)(__x__))
40 #define VOIDP(__x__) (__x__)
41 #define VOIDFP(__x__) (__x__)
44 #define LEM1802_POWER_ON_CYCLES 100000 /* 'about one second' */
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 */
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 */
54 #define PALETTE_ENTRIES 16
55 static const DCPU16_WORD palette_default_
[PALETTE_ENTRIES
] = {
63 0x0aaf, /* pale blue */
65 0x055f, /* light blue */
66 0x05f5, /* light green*/
67 0x05ff, /* light cyan */
68 0x0f55, /* light red */
69 0x0f5f, /* light magenta */
70 0x0ff5, /* light yellow */
78 unsigned char a
; /* unused */
82 long long cycle_activated
; /* running since */
83 long long cycles_until_active_
; /* for tracking power-up delay */
85 DCPU16_WORD video_base
;
86 DCPU16_WORD font_base
;
87 DCPU16_WORD palette_base
;
88 DCPU16_WORD border_color
;
89 struct pixel_
*pixbuf
;
91 unsigned int refresh_rate
; /* redraw every n cycles */
92 unsigned int refresh_tally_
; /* tick */
94 unsigned int blink_rate
; /* toggle every n cycles? still figuring this out.. */
95 unsigned int blink_tally_
; /* tick */
96 unsigned int blink_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_
;
106 int (*render
)(void *, struct pixel_
*, size_t, size_t);
111 long long power_on_cycles_(void) {
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);
126 void pixel_color_(struct pixel_
*pix
, DCPU16_WORD color
) {
129 x
= (color
>> 0) & 0x000f;
130 pix
->r
= x
| (x
<< 4);
132 x
= (color
>> 4) & 0x000f;
133 pix
->g
= x
| (x
<< 4);
135 x
= (color
>> 8) & 0x000f;
136 pix
->b
= x
| (x
<< 4);
138 x
= (color
>> 12) & 0x000f;
139 pix
->a
= x
| (x
<< 4);
143 void pixbuf_border_paint_(struct pixel_
*pixbuf
, struct pixel_
*border
) {
146 MSG_DEBUG(NULL
, "%s>> painting border", __func__
);
149 for (y
= 0; y
< PIX_BORDER
; y
++) {
150 for (x
= 0; x
< PIX_X
; x
++) {
153 i
= ((PIX_Y
- y
) * PIX_X
) + x
;
159 for (y
= PIX_BORDER
; y
< (PIX_Y
- PIX_BORDER
+ 1); y
++)
160 for (x
= 0; x
< PIX_BORDER
; x
++) {
163 pixbuf
[i
+ (PIX_X
- PIX_BORDER
)] = *border
;
168 void font_tile_paint_(struct pixel_
*p
, struct pixel_
*fg
, struct pixel_
*bg
, DCPU16_WORD
*tile
) {
170 unsigned char *font_bitmap
= (unsigned char *)tile
;
173 MSG_DEBUG(NULL
, "%s>> fg:(%u,%u,%u) bg:(%u,%u,%u) font_bitmap:%02x %02x %02x %02x", __func__
,
176 font_bitmap
[0], font_bitmap
[1], font_bitmap
[2], font_bitmap
[3]);
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
;
184 p
[((CELL_Y_SZ
- pix_y
- 1) * PIX_X
) + pix_x
] = *bg
;
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
;
198 cell_x
= (addr
- base
) % CELL_X
;
199 cell_y
= (addr
- base
) / CELL_X
;
202 MSG_DEBUG(NULL
, "%s>> addr:0x%04x col:%u row:%u v:%hu",
205 cell_x
, cell_y
, mem
[addr
]);
208 blink
= mem
[addr
] & 0x0080;
210 /* tiles take two words each */
211 font_bitmap
= tiles
+ (2 * (mem
[addr
] & 0x7f));
213 pixel_color_(&bg
, palette
[(mem
[addr
] >> 8) & 0x0f]);
214 if (blink
&& blink_state
)
215 pixel_color_(&fg
, palette
[(mem
[addr
] >> 8) & 0x0f]);
217 pixel_color_(&fg
, palette
[(mem
[addr
] >> 12) & 0x0f]);
219 tilestart
+= (PIX_X
* PIX_BORDER
); /* skip top border */
220 tilestart
+= (CELL_Y_SZ
* PIX_X
) * cell_y
; /* skip down to row */
222 tilestart
+= PIX_BORDER
; /* skip side border */
223 tilestart
+= (CELL_X_SZ
) * cell_x
; /* skip to column */
225 font_tile_paint_(tilestart
, &fg
, &bg
, font_bitmap
);
229 void lem1802_pixbuf_refresh_full_(struct lem1802_
*display
, DCPU16_WORD
*mem
) {
230 struct pixel_ border
;
234 MSG_DEBUG(NULL
, "%s>> video_base:0x%04x", __func__
, display
->video_base
);
237 if (display
->cycles_until_active_
) {
238 /* show cute power-up sequence.. */
239 memset(display
->pixbuf
, 0, PIX_X
* PIX_Y
* sizeof *display
->pixbuf
);
243 if (display
->video_base
== 0) {
244 /* disconnected, blank display */
245 memset(display
->pixbuf
, 0, PIX_X
* PIX_Y
* sizeof *display
->pixbuf
);
249 pixel_color_(&border
, display
->border_color
);
250 pixbuf_border_paint_(display
->pixbuf
, &border
);
252 for (tile
= 0; tile
< CELL_X
* CELL_Y
; tile
++) {
253 pixbuf_addr_paint_(display
->pixbuf
,
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
);
264 int pixbuf_render_pnm_(void *data
, struct pixel_
*pixbuf
, size_t x
, size_t y
) {
265 FILE *f
= (FILE *)data
;
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
);
283 int pixbuf_render_png_(void *data
, struct pixel_
*pixbuf
, size_t x
, size_t y
) {
284 FILE *f
= (FILE *)data
;
290 png
= png_create_write_struct(PNG_LIBPNG_VER_STRING
, NULL
, NULL
, NULL
);
295 info
= png_create_info_struct(png
);
297 png_destroy_write_struct(&png
, (png_infopp
)NULL
);
301 if (setjmp(png_jmpbuf(png
))) {
302 png_destroy_write_struct(&png
, &info
);
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
)));
313 png_write_end(png
, info
);
315 png_destroy_write_struct(&png
, &info
);
322 #endif /* HAVE_LIBPNG */
324 #ifdef HAVE_LIBVNCSERVER
325 /* create and return a new screen, easiest to do here because we know the screen dimensions */
327 rfbScreenInfoPtr
lem1802_rfb_new_(int argc
, char *argv
[]) {
329 int paddedWidth
= PIX_X
+ ( (PIX_X
& 3) ? (4 - (PIX_X
& 3)) : 0 );
331 int bitsPerSample
= 8;
332 int samplesPerPixel
= 3;
333 int bytesPerPixel
= 4;
335 s
= rfbGetScreen(&argc
, argv
, paddedWidth
, height
, bitsPerSample
, samplesPerPixel
, bytesPerPixel
);
339 s
->alwaysShared
= TRUE
;
342 s
->httpDir
= "../classes";
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
);
352 /* notify rfb server that pixels may have changed */
354 int pixbuf_render_vnc_(void *data
, struct pixel_
*pixbuf
, size_t x
, size_t y
) {
355 rfbScreenInfoPtr s
= (rfbScreenInfoPtr
)data
;
360 MSG_DEBUG(NULL
, "%s>> s:%p", __func__
, s
);
364 rfbMarkRectAsModified(s
, 0, 0, x
, y
);
366 MSG_DEBUG(NULL
, "%s>>", __func__
);
370 #endif /* HAVE_LIBVNCSERVER */
374 void lem1802_reset_(struct dcpu16
*vm
, struct dcpu16_hw
*hw
) {
375 struct lem1802_
*display
= (struct lem1802_
*)hw
->data
;
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;
385 memset(display
->pixbuf
, 0, PIX_X
* PIX_Y
* sizeof *display
->pixbuf
);
387 display
->refresh_tally_
= 0;
388 display
->blink_tally_
= 0;
389 display
->blink_state
= 0;
391 display
->cycle_state_
= 0;
393 MSG_DEBUG(vm
, "%s>>", __func__
);
397 void lem1802_cycle_(struct dcpu16
*vm
, struct dcpu16_hw
*hw
) {
398 struct lem1802_
*display
= (struct lem1802_
*)hw
->data
;
402 for more cycle-accuracy, could step through video memory, if set,
404 for now just count cycles and issue a full refresh/render
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
);
415 display
->refresh_tally_
++;
416 if (display
->refresh_tally_
>= display
->refresh_rate
) {
417 display
->refresh_tally_
= 0;
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
);
424 switch (display
->cycle_state_
) {
428 case CYCLE_COPY_TO_RAM
:
429 MSG_DEBUG(vm
, "%s>> copy_to_ram words:%zu src:%p dst_addr:0x%04x",
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
;
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__
);
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
];
458 MSG_DEBUG(vm
, "%s>> A:0x%04x B:0x%04x", __func__
, reg_a
, reg_b
);
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_();
466 display
->video_base
= reg_b
;
468 display
->cycle_activated
= 0;
471 case 1: /* MEM_MAP_FONT */
472 display
->font_base
= reg_b
;
475 case 2: /* MEM_MAP_PALETTE */
476 display
->palette_base
= reg_b
;
479 case 3: /* SET_BORDER_COLOR */
480 display
->border_color
= reg_b
& 0x000f;
483 case 4: /* MEM_DUMP_FONT */
484 display
->cycle_state_copy_src_ptr_
= (DCPU16_WORD
*)chargen_4x8_glyphs
;
485 display
->cycle_state_copy_dst_addr_
= reg_b
;
486 display
->cycle_state_copy_words_
= 256;
487 display
->cycle_state_
= CYCLE_COPY_TO_RAM
;
488 dcpu16_cycle_inc(vm
, 256);
491 case 5: /* MEM_DUMP_PALETTE */
492 display
->cycle_state_copy_src_ptr_
= palette_default_
;
493 display
->cycle_state_copy_dst_addr_
= reg_b
;
494 display
->cycle_state_copy_words_
= 16;
495 display
->cycle_state_
= CYCLE_COPY_TO_RAM
;
496 dcpu16_cycle_inc(vm
, 16);
501 static struct renderer_
{
504 int (*renderer
)(void *, struct pixel_
*, size_t, size_t);
505 } lem1802_renderers_
[] = {
506 { "pnm", "filename", pixbuf_render_pnm_
},
508 { "png", "filename", pixbuf_render_png_
},
509 #endif /* HAVE_LIBPNG */
510 #ifdef HAVE_LIBVNCSERVER
511 { "vnc", "", pixbuf_render_vnc_
},
512 #endif /* HAVE_LIBVNCSERVER */
513 { "none", "", NULL
},
518 char *lem1802_renderers_iter_(void **iterp
, char **name
, char **args
) {
519 struct renderer_
**r
= (struct renderer_
**)iterp
;
522 *r
= lem1802_renderers_
;
526 if ((*r
)->name
== NULL
) {
538 int lem1802_data_init_(struct dcpu16_hw
*hw
, void *data
) {
541 hw
->data
= calloc(1, sizeof(struct lem1802_
));
542 if (hw
->data
== NULL
) {
543 MSG_ERROR(hw
->vm
, "%s():%s", "calloc", strerror(errno
));
547 ((struct lem1802_
*)(hw
->data
))->pixbuf
= calloc(PIX_X
* PIX_Y
, sizeof *((struct lem1802_
*)(hw
->data
))->pixbuf
);
548 if (((struct lem1802_
*)(hw
->data
))->pixbuf
== NULL
) {
549 MSG_ERROR(hw
->vm
, "%s():%s", "calloc", strerror(errno
));
555 ((struct lem1802_
*)(hw
->data
))->refresh_rate
= 1666;
556 ((struct lem1802_
*)(hw
->data
))->blink_rate
= 75000;
562 void lem1802_data_free_(struct dcpu16_hw
*hw
) {
565 if (((struct lem1802_
*)(hw
->data
))->pixbuf
) {
566 free(((struct lem1802_
*)(hw
->data
))->pixbuf
);
567 ((struct lem1802_
*)(hw
->data
))->pixbuf
= NULL
;
575 static struct dcpu16_hw_ctl_cmd ctl_
[] = {
576 { "blink_rate", "const unsigned int *", "unsigned int *", "sets or gets cycles per blink toggle" },
577 { "refresh_rate", "const unsigned int *", "unsigned int *", "sets or gets cycles per screen refresh" },
578 #ifdef HAVE_LIBVNCSERVER
579 { "new_rfbScreen", "struct { int argc; char **argv;} *", "rfbScreenInfoPtr *", "allocates a new rfb screen" },
580 { "associate_rfbScreen", "rfbScreenInfoPtr", "NULL", "associates this lem1802 instance with an rfb display" },
581 #endif /* HAVE_LIBVNCSERVER */
582 { "renderers_iter", "void **", "struct {char *name; char *args;} *", "returns the next renderer this module is capable of using" },
583 { "renderer", "const char *", "NULL", "sets this lem1802 instance to use renderer" },
584 { "renderer_data", "void *", "NULL", "sets renderer-specific data" },
585 { NULL
, NULL
, NULL
, NULL
}
589 int lem1802_data_ctl_(struct dcpu16_hw
*hw
, const char *cmd
, void *data_in
, void *data_out
) {
590 if (strcmp(cmd
, "blink_rate") == 0) {
591 struct lem1802_
*display
= (struct lem1802_
*)hw
->data
;
592 const unsigned int *rate_in
= (const unsigned int *)data_in
;
593 unsigned int *rate_out
= (unsigned int *)data_out
;
596 *rate_out
= display
->blink_rate
;
600 display
->blink_rate
= *rate_in
;
606 if (strcmp(cmd
, "refresh_rate") == 0) {
607 struct lem1802_
*display
= (struct lem1802_
*)hw
->data
;
608 const unsigned int *rate_in
= (const unsigned int *)data_in
;
609 unsigned int *rate_out
= (unsigned int *)data_out
;
612 *rate_out
= display
->refresh_rate
;
616 display
->refresh_rate
= *rate_in
;
622 #ifdef HAVE_LIBVNCSERVER
623 if (strcmp(cmd
, "new_rfbScreen") == 0) {
624 struct args_
{ int argc
; char **argv
;} *in
= (struct args_
*)data_in
;
625 rfbScreenInfoPtr
*s_out
= (rfbScreenInfoPtr
*)data_out
;
627 if (in
== NULL
|| s_out
== NULL
)
630 *s_out
= lem1802_rfb_new_(in
->argc
, in
->argv
);
635 if (strcmp(cmd
, "associate_rfbScreen") == 0) {
636 struct lem1802_
*display
= (struct lem1802_
*)hw
->data
;
637 rfbScreenInfoPtr rfbScreen
= (rfbScreenInfoPtr
)data_in
;
640 if (rfbScreen
== NULL
)
643 rfbScreen
->desktopName
= "NYA ELEKTRISKA LEM1802";
644 rfbScreen
->frameBuffer
= (char *)display
->pixbuf
;
648 #endif /* HAVE_LIBVNCSERVER */
650 if (strcmp(cmd
, "renderers_iter") == 0) {
651 void **iterp
= (void **)data_in
;
655 } *parg
= (struct packed_out_
*)data_out
;
657 if (iterp
== NULL
|| parg
== NULL
)
660 (void)lem1802_renderers_iter_(iterp
, &parg
->name
, &parg
->args
);
665 if (strcmp(cmd
, "renderer") == 0) {
666 struct lem1802_
*display
= (struct lem1802_
*)hw
->data
;
667 char *renderer
= (char *)data_in
;
671 for (r
= lem1802_renderers_
; r
->renderer
; r
++) {
672 if (strcmp(renderer
, r
->name
) == 0) {
673 display
->render
= r
->renderer
;
674 MSG_DEBUG(hw
->vm
, "%s>> renderer set to %s", __func__
, renderer
);
679 MSG_ERROR(hw
->vm
, "unknown renderer '%s'", renderer
);
684 if (strcmp(cmd
, "renderer_data") == 0) {
685 struct lem1802_
*display
= (struct lem1802_
*)hw
->data
;
688 display
->renderer_data
= data_in
;
697 struct dcpu16_hw_module dcpu16_hw_module_lem1802
= {
698 .name_
= "LEM1802 - Low Energy Monitor",
706 .cycle
= lem1802_cycle_
,
707 .reset
= lem1802_reset_
,
709 .data_init
= lem1802_data_init_
,
710 .data_free
= lem1802_data_free_
,
711 .ctl
= lem1802_data_ctl_
,