9 #endif /* HAVE_LIBPNG */
11 #ifdef HAVE_LIBVNCSERVER
13 #include <rfb/keysym.h>
14 #endif /* HAVE_LIBVNCSERVER */
17 #include "chargen-4x8.h"
18 #include "hw_lem1802.h"
20 static dcpu16_hw_signal_t lem1802_reset_
;
21 static dcpu16_hw_signal_t lem1802_cycle_
;
22 static dcpu16_hw_signal_t lem1802_hwi_
;
23 static struct dcpu16_hw hw_
= {
24 .name_
= "LEM1802 - Low Energy Monitor",
31 .cycle
= lem1802_cycle_
,
32 .reset
= lem1802_reset_
,
33 .data
= (struct lem1802_
*)NULL
42 #define PALETTE_ENTRIES 16
43 static const DCPU16_WORD palette_default_
[PALETTE_ENTRIES
] = {
51 0x0aaf, /* pale blue */
53 0x055f, /* light blue */
54 0x05f5, /* light green*/
55 0x05ff, /* light cyan */
56 0x0f55, /* light red */
57 0x0f5f, /* light magenta */
58 0x0ff5, /* light yellow */
70 long long cycle_activated
; /* for tracking 'turn on delay' */
72 DCPU16_WORD video_base
;
73 DCPU16_WORD font_base
;
74 DCPU16_WORD palette_base
;
75 DCPU16_WORD border_color
;
76 struct pixel_
*pixbuf
;
78 unsigned int refresh_rate
; /* redraw every n cycles */
79 unsigned int refresh_tally_
; /* tick */
81 unsigned int blink_rate
; /* toggle every n cycles? still figuring this out.. */
82 unsigned int blink_tally_
; /* tick */
83 unsigned int blink_state
;
85 int (*render
)(void *, struct pixel_
*, size_t, size_t);
90 void pixel_color_(struct pixel_
*pix
, DCPU16_WORD color
) {
91 pix
->r
= (color
& 0x000f) | ((color
& 0x000f) << 8);
92 pix
->g
= ((color
& 0x00f0) >> 8) | (color
& 0x00f0);
93 pix
->b
= ((color
& 0x0f00) >> 8) | ((color
& 0x0f00) >> 16);
94 pix
->a
= ((color
& 0xf000) >> 16) | ((color
& 0xf000) >> 24);
98 void pixbuf_border_paint_(struct pixel_
*pixbuf
, struct pixel_
*border
) {
102 for (y
= 0; y
< PIX_BORDER
; y
++) {
103 for (x
= 0; x
< PIX_X
; x
++) {
106 i
= ((PIX_Y
- y
) * PIX_X
) + x
;
112 for (y
= PIX_BORDER
; y
< (PIX_Y
- PIX_BORDER
+ 1); y
++)
113 for (x
= 0; x
< PIX_BORDER
; x
++) {
116 pixbuf
[i
+ (PIX_X
- PIX_BORDER
)] = *border
;
121 void font_tile_paint_(struct pixel_
*p
, struct pixel_
*fg
, struct pixel_
*bg
, DCPU16_WORD
*tile
) {
123 unsigned char *font_bitmap
= (unsigned char *)tile
;
125 for (pix_x
= 0; pix_x
< CELL_X_SZ
; pix_x
++) {
126 for (pix_y
= 0; pix_y
< CELL_Y_SZ
; pix_y
++) {
127 if ((font_bitmap
[pix_x
] >> pix_y
) & 0x01)
128 p
[((CELL_Y_SZ
- pix_y
- 1) * PIX_X
) + pix_x
] = *fg
;
130 p
[((CELL_Y_SZ
- pix_y
- 1) * PIX_X
) + pix_x
] = *bg
;
136 void pixbuf_addr_paint_(struct pixel_
*pixbuf
, DCPU16_WORD
*mem
, DCPU16_WORD addr
, DCPU16_WORD
*palette
, DCPU16_WORD
*tiles
, unsigned int blink_state
) {
137 struct pixel_
*tilestart
= pixbuf
; /* start of display */
138 unsigned int cell_x
= addr
% (PIX_X
/ CELL_X_SZ
),
139 cell_y
= addr
/ (PIX_X
/ CELL_X_SZ
);
140 struct pixel_ fg
, bg
;
141 DCPU16_WORD
*font_bitmap
;
144 cell_x
= addr
% (PIX_X
/ CELL_X_SZ
);
145 cell_y
= addr
/ (PIX_X
/ CELL_X_SZ
);
147 blink
= mem
[addr
] & 0x0080;
149 /* tiles take two words each */
150 font_bitmap
= tiles
+ (2 * (mem
[addr
] & 0x7f));
152 pixel_color_(&bg
, palette
[(mem
[addr
] >> 8) & 0x0f]);
153 if (blink
&& blink_state
)
154 pixel_color_(&fg
, palette
[(mem
[addr
] >> 8) & 0x0f]);
156 pixel_color_(&fg
, palette
[(mem
[addr
] >> 12) & 0x0f]);
158 tilestart
+= (PIX_X
* PIX_BORDER
); /* skip top border */
159 tilestart
+= (CELL_Y_SZ
* PIX_X
) * cell_y
; /* skip down to row */
161 tilestart
+= PIX_BORDER
; /* skip side border */
162 tilestart
+= (CELL_X_SZ
) * cell_x
; /* skip to column */
164 font_tile_paint_(tilestart
, &fg
, &bg
, font_bitmap
);
168 void lem1802_pixbuf_refresh_full_(struct lem1802_
*display
, DCPU16_WORD
*mem
) {
169 struct pixel_ border
;
172 if (display
->video_base
== 0) {
173 /* disconnected, blank display. static might be fun, too */
174 memset(display
->pixbuf
, 0, PIX_X
* PIX_Y
* sizeof *display
->pixbuf
);
178 pixel_color_(&border
, display
->border_color
);
179 pixbuf_border_paint_(display
->pixbuf
, &border
);
181 for (tile
= 0; tile
< (PIX_X
/ CELL_X_SZ
) * (PIX_Y
/ CELL_Y_SZ
); tile
++) {
182 pixbuf_addr_paint_(display
->pixbuf
,
184 display
->video_base
+ tile
,
185 display
->palette_base
? mem
+ display
->palette_base
: palette_default_
,
186 display
->font_base
? mem
+ display
->font_base
: (unsigned short *)chargen_4x8_glyphs
,
187 display
->blink_state
);
192 int pixbuf_render_pnm_(void *data
, struct pixel_
*pixbuf
, size_t x
, size_t y
) {
193 FILE *f
= (FILE *)data
;
199 for (i
= 0; i
< x
* y
; i
++) {
200 fwrite(&pixbuf
[i
].r
, 1, 1, f
);
201 fwrite(&pixbuf
[i
].g
, 1, 1, f
);
202 fwrite(&pixbuf
[i
].b
, 1, 1, f
);
211 void pixbuf_render_png_user_warning_(png_structp png
, png_const_charp msg
) {
212 (void)png
, (void)msg
;
215 void pixbuf_render_png_user_error_(png_structp png
, png_const_charp msg
) {
216 (void)png
, (void)msg
;
219 int pixbuf_render_png_(void *data
, struct pixel_
*pixbuf
, size_t x
, size_t y
) {
220 FILE *f
= (FILE *)data
;
226 png
= png_create_write_struct(PNG_LIBPNG_VER_STRING
, NULL
, pixbuf_render_png_user_error_
, pixbuf_render_png_user_warning_
);
231 info
= png_create_info_struct(png
);
233 png_destroy_write_struct(&png
, (png_infopp
)NULL
);
237 if (setjmp(png_jmpbuf(png
))) {
238 png_destroy_write_struct(&png
, &info
);
243 png_set_IHDR(png
, info
, x
, y
, 8, PNG_COLOR_TYPE_RGB_ALPHA
, PNG_INTERLACE_NONE
, PNG_COMPRESSION_TYPE_DEFAULT
, PNG_FILTER_TYPE_DEFAULT
);
244 png_set_invert_alpha(png
);
245 png_write_info(png
, info
);
246 for (i
= 0; i
< y
; i
++) {
247 png_write_row(png
, (unsigned char *)(pixbuf
+ (i
* x
)));
249 png_write_end(png
, info
);
251 png_destroy_write_struct(&png
, &info
);
258 #endif /* HAVE_LIBPNG */
260 #ifdef HAVE_LIBVNCSERVER
262 rfbScreenInfoPtr rfbScreen
;
265 /* create and return a new struct vnc_server_, for use as a vnc renderer's generic data */
266 void *lem1802_vnc_init_data(int argc
, char *argv
[], struct dcpu16_hw
*hw
) {
267 struct vnc_server_
*s
;
268 struct pixel_
*pixbuf
= ((struct lem1802_
*)(hw
->data
))->pixbuf
;
269 int paddedWidth
= PIX_X
+ ( (PIX_X
& 3) ? (4 - (PIX_X
& 3)) : 0 );
271 int bitsPerSample
= 8;
272 int samplesPerPixel
= 3;
273 int bytesPerPixel
= 4;
275 s
= calloc(1, sizeof *s
);
279 s
->rfbScreen
= rfbGetScreen(&argc
, argv
, paddedWidth
, height
, bitsPerSample
, samplesPerPixel
, bytesPerPixel
);
280 if (s
->rfbScreen
== NULL
) {
285 s
->rfbScreen
->desktopName
= "lem1802";
286 s
->rfbScreen
->alwaysShared
= TRUE
;
289 s
->rfbScreen
->kbdAddEvent
= HandleKey
; */
290 s
->rfbScreen
->httpDir
= "../classes";
293 s
->rfbScreen
->frameBuffer
= (char *)pixbuf
;
295 rfbInitServer(s
->rfbScreen
);
297 rfbRunEventLoop(s
->rfbScreen
,-1,TRUE
);
303 int pixbuf_render_vnc_(void *data
, struct pixel_
*pixbuf
, size_t x
, size_t y
) {
305 struct vnc_server_
*s
= (struct vnc_server_
*)data
;
310 rfbMarkRectAsModified(s
->rfbScreen
,0,0,x
,y
);
314 #endif /* HAVE_LIBVNCSERVER */
318 void lem1802_reset_(struct dcpu16
*vm
, void *data
) {
319 struct lem1802_
*display
= (struct lem1802_
*)data
;
321 display
->cycle_activated
= vm
->cycle
;
323 display
->video_base
= 0;
324 display
->font_base
= 0;
325 display
->palette_base
= 0;
326 display
->border_color
= 0;
328 memset(display
->pixbuf
, 0, PIX_X
* PIX_Y
* sizeof *display
->pixbuf
);
330 display
->refresh_tally_
= 0;
331 display
->blink_tally_
= 0;
332 display
->blink_state
= 0;
336 void lem1802_cycle_(struct dcpu16
*vm
, void *data
) {
337 struct lem1802_
*display
= (struct lem1802_
*)data
;
341 maybe just step through video memory (if set)
342 one word per clock..? could just cheat and
343 use accounting callbacks..
345 for now just count cycles and issue a full refresh/render
349 display
->blink_tally_
++;
350 if (display
->blink_tally_
>= display
->blink_rate
)
351 display
->blink_state
^= 1;
353 display
->refresh_tally_
++;
354 if (display
->refresh_tally_
>= display
->refresh_rate
) {
355 display
->refresh_tally_
= 0;
357 display
->render(display
->renderer_data
, display
->pixbuf
, PIX_X
, PIX_Y
);
360 if (display
->render
) {
361 lem1802_pixbuf_refresh_full_(display
, vm
->ram
);
362 display
->render(display
->renderer_data
, display
->pixbuf
, PIX_X
, PIX_Y
);
367 void lem1802_hwi_(struct dcpu16
*vm
, void *data
) {
368 struct lem1802_
*display
= (struct lem1802_
*)data
;
369 DCPU16_WORD reg_a
= vm
->reg
[DCPU16_REG_A
];
370 DCPU16_WORD reg_b
= vm
->reg
[DCPU16_REG_B
];
374 case 0: /* MEM_MAP_SCREEN */
375 if (display
->cycle_activated
== 0 && reg_b
) {
376 display
->cycle_activated
= vm
->cycle
;
378 display
->video_base
= reg_b
;
381 case 1: /* MEM_MAP_FONT */
382 display
->font_base
= reg_b
;
385 case 2: /* MEM_MAP_PALETTE */
386 display
->palette_base
= reg_b
;
389 case 3: /* SET_BORDER_COLOR */
390 display
->border_color
= reg_b
& 0x000f;
393 case 4: /* MEM_DUMP_FONT */
394 for (i
= 0; i
< 128 ; i
++) {
395 vm
->ram
[reg_b
] = chargen_4x8_glyphs
[reg_b
][0] << 8;
396 vm
->ram
[reg_b
] |= chargen_4x8_glyphs
[reg_b
][1];
398 vm
->ram
[reg_b
] = chargen_4x8_glyphs
[reg_b
][2] << 8;
399 vm
->ram
[reg_b
] |= chargen_4x8_glyphs
[reg_b
][3];
405 case 5: /* MEM_DUMP_PALETTE */
406 for (i
= 0; i
< 16; i
++) {
407 vm
->ram
[reg_b
] = palette_default_
[i
];
415 static struct renderer_
{
418 int (*renderer
)(void *, struct pixel_
*, size_t, size_t);
419 } lem1802_renderers_
[] = {
420 { "pnm", "filename", pixbuf_render_pnm_
},
422 { "png", "filename", pixbuf_render_png_
},
423 #endif /* HAVE_LIBPNG */
424 #ifdef HAVE_LIBVNCSERVER
425 { "vnc", "", pixbuf_render_vnc_
},
426 #endif /* HAVE_LIBVNCSERVER */
427 { "none", "", NULL
},
431 int lem1802_renderer_set(struct dcpu16_hw
*hw
, const char *renderer
, void *data
) {
434 for (r
= lem1802_renderers_
; r
->renderer
; r
++) {
435 if (strcmp(renderer
, r
->name
) == 0) {
436 ((struct lem1802_
*)(hw
->data
))->render
= r
->renderer
;
437 ((struct lem1802_
*)(hw
->data
))->renderer_data
= data
;
445 char *lem1802_renderers_iter(void **iterp
, char **name
, char **args
) {
446 struct renderer_
**r
= (struct renderer_
**)iterp
;
449 *r
= lem1802_renderers_
;
453 if ((*r
)->name
== NULL
) {
464 /* instantitate a new display */
465 struct dcpu16_hw
*lem1802_new(struct dcpu16
*vm
) {
466 struct dcpu16_hw
*hw
;
468 hw
= calloc(1, sizeof *hw
);
470 vm
->warn_cb_("%s():%s", "calloc", strerror(errno
));
474 memcpy(hw
, &hw_
, sizeof *hw
);
476 hw
->data
= calloc(1, sizeof(struct dcpu16_hw
));
477 if (hw
->data
== NULL
) {
478 vm
->warn_cb_("%s():%s", "calloc", strerror(errno
));
483 ((struct lem1802_
*)(hw
->data
))->pixbuf
= calloc(1, PIX_X
* PIX_Y
* sizeof *((struct lem1802_
*)(hw
->data
))->pixbuf
);
484 if (((struct lem1802_
*)(hw
->data
))->pixbuf
== NULL
) {
485 vm
->warn_cb_("%s():%s", "calloc", strerror(errno
));
494 void lem1802_del(struct dcpu16_hw
**hw
) {
498 if (((struct lem1802_
*)(*hw
)->data
)->pixbuf
) {
499 free(((struct lem1802_
*)(*hw
)->data
)->pixbuf
);
500 ((struct lem1802_
*)(*hw
)->data
)->pixbuf
= NULL
;