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"
21 #define TRACE(...) do { printf("[debug] "); printf(__VA_ARGS__); printf("\n"); } while (0)
23 #define TRACE(...) do {} while (0)
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",
37 .cycle
= lem1802_cycle_
,
38 .reset
= lem1802_reset_
,
39 .data
= (struct lem1802_
*)NULL
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 */
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 */
50 #define PALETTE_ENTRIES 16
51 static const DCPU16_WORD palette_default_
[PALETTE_ENTRIES
] = {
59 0x0aaf, /* pale blue */
61 0x055f, /* light blue */
62 0x05f5, /* light green*/
63 0x05ff, /* light cyan */
64 0x0f55, /* light red */
65 0x0f5f, /* light magenta */
66 0x0ff5, /* light yellow */
78 long long cycle_activated
; /* for tracking 'turn on delay' */
80 DCPU16_WORD video_base
;
81 DCPU16_WORD font_base
;
82 DCPU16_WORD palette_base
;
83 DCPU16_WORD border_color
;
84 struct pixel_
*pixbuf
;
86 unsigned int refresh_rate
; /* redraw every n cycles */
87 unsigned int refresh_tally_
; /* tick */
89 unsigned int blink_rate
; /* toggle every n cycles? still figuring this out.. */
90 unsigned int blink_tally_
; /* tick */
91 unsigned int blink_state
;
93 int (*render
)(void *, struct pixel_
*, size_t, size_t);
98 void pixel_color_(struct pixel_
*pix
, DCPU16_WORD color
) {
101 x
= (color
>> 0) & 0x000f;
102 pix
->r
= x
| (x
<< 4);
104 x
= (color
>> 4) & 0x000f;
105 pix
->g
= x
| (x
<< 4);
107 x
= (color
>> 8) & 0x000f;
108 pix
->b
= x
| (x
<< 4);
110 x
= (color
>> 12) & 0x000f;
111 pix
->a
= x
| (x
<< 4);
115 void pixbuf_border_paint_(struct pixel_
*pixbuf
, struct pixel_
*border
) {
118 TRACE("%s>> painted border", __func__
);
121 for (y
= 0; y
< PIX_BORDER
; y
++) {
122 for (x
= 0; x
< PIX_X
; x
++) {
125 i
= ((PIX_Y
- y
) * PIX_X
) + x
;
131 for (y
= PIX_BORDER
; y
< (PIX_Y
- PIX_BORDER
+ 1); y
++)
132 for (x
= 0; x
< PIX_BORDER
; x
++) {
135 pixbuf
[i
+ (PIX_X
- PIX_BORDER
)] = *border
;
140 void font_tile_paint_(struct pixel_
*p
, struct pixel_
*fg
, struct pixel_
*bg
, DCPU16_WORD
*tile
) {
142 unsigned char *font_bitmap
= (unsigned char *)tile
;
145 TRACE("%s>> fg:(%u,%u,%u) bg:(%u,%u,%u) font_bitmap:%02x %02x %02x %02x", __func__
,
148 font_bitmap
[0], font_bitmap
[1], font_bitmap
[2], font_bitmap
[3]);
151 for (pix_x
= 0; pix_x
< CELL_X_SZ
; pix_x
++) {
152 for (pix_y
= 0; pix_y
< CELL_Y_SZ
; pix_y
++) {
153 if ((font_bitmap
[pix_x
] >> pix_y
) & 0x01)
154 p
[((CELL_Y_SZ
- pix_y
- 1) * PIX_X
) + pix_x
] = *fg
;
156 p
[((CELL_Y_SZ
- pix_y
- 1) * PIX_X
) + pix_x
] = *bg
;
162 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
) {
163 struct pixel_
*tilestart
= pixbuf
; /* start of display */
164 unsigned int cell_x
= addr
% (PIX_X
/ CELL_X_SZ
),
165 cell_y
= addr
/ (PIX_X
/ CELL_X_SZ
);
166 struct pixel_ fg
, bg
;
167 DCPU16_WORD
*font_bitmap
;
170 cell_x
= (addr
- base
) % CELL_X
;
171 cell_y
= (addr
- base
) / CELL_X
;
174 TRACE("%s>> addr:0x%04x col:%u row:%u v:%hu",
177 cell_x
, cell_y
, mem
[addr
]);
180 blink
= mem
[addr
] & 0x0080;
182 /* tiles take two words each */
183 font_bitmap
= tiles
+ (2 * (mem
[addr
] & 0x7f));
185 pixel_color_(&bg
, palette
[(mem
[addr
] >> 8) & 0x0f]);
186 if (blink
&& blink_state
)
187 pixel_color_(&fg
, palette
[(mem
[addr
] >> 8) & 0x0f]);
189 pixel_color_(&fg
, palette
[(mem
[addr
] >> 12) & 0x0f]);
191 tilestart
+= (PIX_X
* PIX_BORDER
); /* skip top border */
192 tilestart
+= (CELL_Y_SZ
* PIX_X
) * cell_y
; /* skip down to row */
194 tilestart
+= PIX_BORDER
; /* skip side border */
195 tilestart
+= (CELL_X_SZ
) * cell_x
; /* skip to column */
197 font_tile_paint_(tilestart
, &fg
, &bg
, font_bitmap
);
201 void lem1802_pixbuf_refresh_full_(struct lem1802_
*display
, DCPU16_WORD
*mem
) {
202 struct pixel_ border
;
206 TRACE("%s>> video_base:0x%04x", __func__
, display
->video_base
);
209 if (display
->video_base
== 0) {
210 /* disconnected, blank display. static might be fun, too */
211 memset(display
->pixbuf
, 0, PIX_X
* PIX_Y
* sizeof *display
->pixbuf
);
215 pixel_color_(&border
, display
->border_color
);
216 pixbuf_border_paint_(display
->pixbuf
, &border
);
218 for (tile
= 0; tile
< CELL_X
* CELL_Y
; tile
++) {
219 pixbuf_addr_paint_(display
->pixbuf
,
222 display
->video_base
+ tile
,
223 display
->palette_base
? mem
+ display
->palette_base
: palette_default_
,
224 display
->font_base
? mem
+ display
->font_base
: (unsigned short *)chargen_4x8_glyphs
,
225 display
->blink_state
);
230 int pixbuf_render_pnm_(void *data
, struct pixel_
*pixbuf
, size_t x
, size_t y
) {
231 FILE *f
= (FILE *)data
;
237 for (i
= 0; i
< x
* y
; i
++) {
238 fwrite(&pixbuf
[i
].r
, 1, 1, f
);
239 fwrite(&pixbuf
[i
].g
, 1, 1, f
);
240 fwrite(&pixbuf
[i
].b
, 1, 1, f
);
249 int pixbuf_render_png_(void *data
, struct pixel_
*pixbuf
, size_t x
, size_t y
) {
250 FILE *f
= (FILE *)data
;
256 png
= png_create_write_struct(PNG_LIBPNG_VER_STRING
, NULL
, NULL
, NULL
);
261 info
= png_create_info_struct(png
);
263 png_destroy_write_struct(&png
, (png_infopp
)NULL
);
267 if (setjmp(png_jmpbuf(png
))) {
268 png_destroy_write_struct(&png
, &info
);
273 png_set_IHDR(png
, info
, x
, y
, 8, PNG_COLOR_TYPE_RGB_ALPHA
, PNG_INTERLACE_NONE
, PNG_COMPRESSION_TYPE_DEFAULT
, PNG_FILTER_TYPE_DEFAULT
);
274 png_set_invert_alpha(png
);
275 png_write_info(png
, info
);
276 for (i
= 0; i
< y
; i
++) {
277 png_write_row(png
, (unsigned char *)(pixbuf
+ (i
* x
)));
279 png_write_end(png
, info
);
281 png_destroy_write_struct(&png
, &info
);
288 #endif /* HAVE_LIBPNG */
290 #ifdef HAVE_LIBVNCSERVER
291 /* create and return a new screen, easiest to do here because we know the screen dimensions */
292 rfbScreenInfoPtr
lem1802_rfb_new(int argc
, char *argv
[]) {
294 int paddedWidth
= PIX_X
+ ( (PIX_X
& 3) ? (4 - (PIX_X
& 3)) : 0 );
296 int bitsPerSample
= 8;
297 int samplesPerPixel
= 3;
298 int bytesPerPixel
= 4;
300 s
= rfbGetScreen(&argc
, argv
, paddedWidth
, height
, bitsPerSample
, samplesPerPixel
, bytesPerPixel
);
304 s
->alwaysShared
= TRUE
;
307 s
->httpDir
= "../classes";
310 TRACE("%s>> s:%p", __func__
, s
);
311 TRACE("%s>> s->kbdAddEvent:%p s->frameBuffer:%p", __func__
, s
->kbdAddEvent
, s
->frameBuffer
);
316 /* set up a new screen to see our pixels */
317 void lem1802_vnc_associate(struct dcpu16_hw
*hw
, rfbScreenInfoPtr s
) {
318 struct lem1802_
*display
= (struct lem1802_
*)hw
->data
;
320 s
->desktopName
= "NYA ELEKTRISKA LEM1802";
321 s
->frameBuffer
= (char *)display
->pixbuf
;
323 TRACE("%s>> s:%p\n", __func__
, s
);
326 /* notify rfb server that pixels may have changed */
328 int pixbuf_render_vnc_(void *data
, struct pixel_
*pixbuf
, size_t x
, size_t y
) {
329 rfbScreenInfoPtr s
= (rfbScreenInfoPtr
)data
;
335 rfbMarkRectAsModified(s
, 0, 0, x
, y
);
337 TRACE("%s>>", __func__
);
341 #endif /* HAVE_LIBVNCSERVER */
345 void lem1802_reset_(struct dcpu16
*vm
, void *data
) {
346 struct lem1802_
*display
= (struct lem1802_
*)data
;
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;
356 memset(display
->pixbuf
, 0, PIX_X
* PIX_Y
* sizeof *display
->pixbuf
);
358 display
->refresh_tally_
= 0;
359 display
->blink_tally_
= 0;
360 display
->blink_state
= 0;
363 vm
->trace_cb_("%s>>", __func__
);
368 void lem1802_cycle_(struct dcpu16
*vm
, void *data
) {
369 struct lem1802_
*display
= (struct lem1802_
*)data
;
373 maybe just step through video memory (if set)
374 one word per clock..? could just cheat and
375 use accounting callbacks..
377 for now just count cycles and issue a full refresh/render
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
);
388 display
->refresh_tally_
++;
389 if (display
->refresh_tally_
>= display
->refresh_rate
) {
390 display
->refresh_tally_
= 0;
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
);
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
];
405 TRACE("%s>> A:0x%04x B:0x%04x", __func__
, reg_a
, reg_b
);
408 case 0: /* MEM_MAP_SCREEN */
409 if (display
->cycle_activated
== 0 && reg_b
) {
410 display
->cycle_activated
= vm
->cycle
;
412 display
->video_base
= reg_b
;
414 display
->cycle_activated
= 0;
417 case 1: /* MEM_MAP_FONT */
418 display
->font_base
= reg_b
;
421 case 2: /* MEM_MAP_PALETTE */
422 display
->palette_base
= reg_b
;
425 case 3: /* SET_BORDER_COLOR */
426 display
->border_color
= reg_b
& 0x000f;
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];
434 vm
->ram
[reg_b
] = chargen_4x8_glyphs
[reg_b
][2] << 8;
435 vm
->ram
[reg_b
] |= chargen_4x8_glyphs
[reg_b
][3];
441 case 5: /* MEM_DUMP_PALETTE */
442 for (i
= 0; i
< 16; i
++) {
443 vm
->ram
[reg_b
] = palette_default_
[i
];
451 static struct renderer_
{
454 int (*renderer
)(void *, struct pixel_
*, size_t, size_t);
455 } lem1802_renderers_
[] = {
456 { "pnm", "filename", pixbuf_render_pnm_
},
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
},
467 int lem1802_renderer_set(struct dcpu16_hw
*hw
, const char *renderer
, void *data
) {
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
);
482 char *lem1802_renderers_iter(void **iterp
, char **name
, char **args
) {
483 struct renderer_
**r
= (struct renderer_
**)iterp
;
486 *r
= lem1802_renderers_
;
490 if ((*r
)->name
== NULL
) {
501 /* instantitate a new display */
502 struct dcpu16_hw
*lem1802_new(struct dcpu16
*vm
) {
503 struct dcpu16_hw
*hw
;
505 hw
= calloc(1, sizeof *hw
);
507 vm
->warn_cb_("%s():%s", "calloc", strerror(errno
));
511 memcpy(hw
, &hw_
, sizeof *hw
);
513 hw
->data
= calloc(1, sizeof(struct dcpu16_hw
));
514 if (hw
->data
== NULL
) {
515 vm
->warn_cb_("%s():%s", "calloc", strerror(errno
));
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
));
528 ((struct lem1802_
*)(hw
->data
))->refresh_rate
= 1666;
529 ((struct lem1802_
*)(hw
->data
))->blink_rate
= 75000;
536 void lem1802_del(struct dcpu16_hw
**hw
) {
540 if (((struct lem1802_
*)(*hw
)->data
)->pixbuf
) {
541 free(((struct lem1802_
*)(*hw
)->data
)->pixbuf
);
542 ((struct lem1802_
*)(*hw
)->data
)->pixbuf
= NULL
;