/[gxemul]/upstream/0.3.8/src/devices/dev_vga.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.3.8/src/devices/dev_vga.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 23 - (show annotations)
Mon Oct 8 16:19:43 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 33963 byte(s)
0.3.8
1 /*
2 * Copyright (C) 2004-2006 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: dev_vga.c,v 1.95 2006/01/01 13:17:18 debug Exp $
29 *
30 * VGA charcell and graphics device.
31 *
32 * It should work with 80x25 and 40x25 text modes, and with a few graphics
33 * modes as long as no fancy VGA features are used.
34 */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "console.h"
41 #include "cpu.h"
42 #include "devices.h"
43 #include "machine.h"
44 #include "memory.h"
45 #include "misc.h"
46
47 #include "vga.h"
48
49 /* These are generated from binary font files: */
50 #include "fonts/font8x8.c"
51 #include "fonts/font8x10.c"
52 #include "fonts/font8x16.c"
53
54
55 /* For videomem -> framebuffer updates: */
56 #define VGA_TICK_SHIFT 18
57
58 #define MAX_RETRACE_SCANLINES 420
59 #define N_IS1_READ_THRESHOLD 50
60
61 #define GFX_ADDR_WINDOW 0x18000
62
63 #define VGA_FB_ADDR 0x1c00000000ULL
64
65 #define MODE_CHARCELL 1
66 #define MODE_GRAPHICS 2
67
68 #define GRAPHICS_MODE_8BIT 1
69 #define GRAPHICS_MODE_4BIT 2
70
71 struct vga_data {
72 uint64_t videomem_base;
73 uint64_t control_base;
74
75 struct vfb_data *fb;
76 uint32_t fb_size;
77
78 int fb_max_x; /* pixels */
79 int fb_max_y; /* pixels */
80 int max_x; /* charcells or pixels */
81 int max_y; /* charcells or pixels */
82
83 /* Selects charcell mode or graphics mode: */
84 int cur_mode;
85
86 /* Common for text and graphics modes: */
87 int pixel_repx, pixel_repy;
88
89 /* Textmode: */
90 int font_width;
91 int font_height;
92 unsigned char *font;
93 size_t charcells_size;
94 unsigned char *charcells; /* 2 bytes per char */
95 unsigned char *charcells_outputed; /* text */
96 unsigned char *charcells_drawn; /* framebuffer */
97
98 /* Graphics: */
99 int graphics_mode;
100 int bits_per_pixel;
101 unsigned char *gfx_mem;
102 uint32_t gfx_mem_size;
103
104 /* Registers: */
105 int attribute_state; /* 0 or 1 */
106 unsigned char attribute_reg_select;
107 unsigned char attribute_reg[256];
108
109 unsigned char misc_output_reg;
110
111 unsigned char sequencer_reg_select;
112 unsigned char sequencer_reg[256];
113
114 unsigned char graphcontr_reg_select;
115 unsigned char graphcontr_reg[256];
116
117 unsigned char crtc_reg_select;
118 unsigned char crtc_reg[256];
119
120 unsigned char palette_read_index;
121 char palette_read_subindex;
122 unsigned char palette_write_index;
123 char palette_write_subindex;
124
125 int current_retrace_line;
126 int input_status_1;
127
128 /* Palette per scanline during retrace: */
129 unsigned char *retrace_palette;
130 int use_palette_per_line;
131 int64_t n_is1_reads;
132
133 /* Misc.: */
134 int console_handle;
135
136 int cursor_x;
137 int cursor_y;
138
139 int modified;
140 int palette_modified;
141 int update_x1;
142 int update_y1;
143 int update_x2;
144 int update_y2;
145 };
146
147
148 /*
149 * recalc_cursor_position():
150 *
151 * Should be called whenever the cursor location _or_ the display
152 * base has been changed.
153 */
154 static void recalc_cursor_position(struct vga_data *d)
155 {
156 int base = (d->crtc_reg[VGA_CRTC_START_ADDR_HIGH] << 8)
157 + d->crtc_reg[VGA_CRTC_START_ADDR_LOW];
158 int ofs = d->crtc_reg[VGA_CRTC_CURSOR_LOCATION_HIGH] * 256 +
159 d->crtc_reg[VGA_CRTC_CURSOR_LOCATION_LOW];
160 ofs -= base;
161 d->cursor_x = ofs % d->max_x;
162 d->cursor_y = ofs / d->max_x;
163 }
164
165
166 /*
167 * register_reset():
168 *
169 * Resets many registers to sane values.
170 */
171 static void register_reset(struct vga_data *d)
172 {
173 /* Home cursor and start at the top: */
174 d->crtc_reg[VGA_CRTC_CURSOR_LOCATION_HIGH] =
175 d->crtc_reg[VGA_CRTC_CURSOR_LOCATION_LOW] = 0;
176 d->crtc_reg[VGA_CRTC_START_ADDR_HIGH] =
177 d->crtc_reg[VGA_CRTC_START_ADDR_LOW] = 0;
178
179 recalc_cursor_position(d);
180
181 /* Reset cursor scanline stuff: */
182 d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_START] = d->font_height - 2;
183 d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_END] = d->font_height - 1;
184
185 d->sequencer_reg[VGA_SEQ_MAP_MASK] = 0x0f;
186 d->graphcontr_reg[VGA_GRAPHCONTR_MASK] = 0xff;
187
188 d->misc_output_reg = VGA_MISC_OUTPUT_IOAS;
189 d->n_is1_reads = 0;
190 }
191
192
193 static void c_putstr(struct vga_data *d, char *s)
194 {
195 while (*s)
196 console_putchar(d->console_handle, *s++);
197 }
198
199
200 /*
201 * reset_palette():
202 */
203 static void reset_palette(struct vga_data *d, int grayscale)
204 {
205 int i, r, g, b;
206
207 /* TODO: default values for entry 16..255? */
208 for (i=16; i<256; i++)
209 d->fb->rgb_palette[i*3 + 0] = d->fb->rgb_palette[i*3 + 1] =
210 d->fb->rgb_palette[i*3 + 2] = (i & 15) * 4;
211 d->palette_modified = 1;
212 i = 0;
213
214 if (grayscale) {
215 for (r=0; r<2; r++)
216 for (g=0; g<2; g++)
217 for (b=0; b<2; b++) {
218 d->fb->rgb_palette[i + 0] =
219 d->fb->rgb_palette[i + 1] =
220 d->fb->rgb_palette[i + 2] =
221 (r+g+b) * 0xaa / 3;
222 d->fb->rgb_palette[i + 8*3 + 0] =
223 d->fb->rgb_palette[i + 8*3 + 1] =
224 d->fb->rgb_palette[i + 8*3 + 2] =
225 (r+g+b) * 0xaa / 3 + 0x55;
226 i+=3;
227 }
228 return;
229 }
230
231 for (r=0; r<2; r++)
232 for (g=0; g<2; g++)
233 for (b=0; b<2; b++) {
234 d->fb->rgb_palette[i + 0] = r * 0xaa;
235 d->fb->rgb_palette[i + 1] = g * 0xaa;
236 d->fb->rgb_palette[i + 2] = b * 0xaa;
237 i+=3;
238 }
239 for (r=0; r<2; r++)
240 for (g=0; g<2; g++)
241 for (b=0; b<2; b++) {
242 d->fb->rgb_palette[i + 0] = r * 0xaa + 0x55;
243 d->fb->rgb_palette[i + 1] = g * 0xaa + 0x55;
244 d->fb->rgb_palette[i + 2] = b * 0xaa + 0x55;
245 i+=3;
246 }
247 }
248
249
250 /*
251 * vga_update_textmode():
252 *
253 * Called from vga_update() when use_x11 is false. This causes modified
254 * character cells to be "simulated" by outputing ANSI escape sequences
255 * that draw the characters in a terminal window instead.
256 */
257 static void vga_update_textmode(struct machine *machine,
258 struct vga_data *d, int base, int start, int end)
259 {
260 char s[50];
261 int i, oldcolor = -1, printed_last = 0;
262
263 for (i=start; i<=end; i+=2) {
264 unsigned char ch = d->charcells[base+i];
265 int fg = d->charcells[base+i+1] & 15;
266 int bg = (d->charcells[base+i+1] >> 4) & 15;
267 /* top bit of bg = blink */
268 int x = (i/2) % d->max_x;
269 int y = (i/2) / d->max_x;
270
271 if (d->charcells[base+i] == d->charcells_outputed[i] &&
272 d->charcells[base+i+1] == d->charcells_outputed[i+1]) {
273 printed_last = 0;
274 continue;
275 }
276
277 d->charcells_outputed[i] = d->charcells[base+i];
278 d->charcells_outputed[i+1] = d->charcells[base+i+1];
279
280 if (!printed_last || x == 0) {
281 snprintf(s, sizeof(s), "\033[%i;%iH", y + 1, x + 1);
282 c_putstr(d, s);
283 }
284 if (oldcolor < 0 || (bg<<4)+fg != oldcolor || !printed_last) {
285 snprintf(s, sizeof(s), "\033[0;"); c_putstr(d, s);
286
287 switch (fg & 7) {
288 case 0: c_putstr(d, "30"); break;
289 case 1: c_putstr(d, "34"); break;
290 case 2: c_putstr(d, "32"); break;
291 case 3: c_putstr(d, "36"); break;
292 case 4: c_putstr(d, "31"); break;
293 case 5: c_putstr(d, "35"); break;
294 case 6: c_putstr(d, "33"); break;
295 case 7: c_putstr(d, "37"); break;
296 }
297 if (fg & 8)
298 c_putstr(d, ";1");
299 c_putstr(d, ";");
300 switch (bg & 7) {
301 case 0: c_putstr(d, "40"); break;
302 case 1: c_putstr(d, "44"); break;
303 case 2: c_putstr(d, "42"); break;
304 case 3: c_putstr(d, "46"); break;
305 case 4: c_putstr(d, "41"); break;
306 case 5: c_putstr(d, "45"); break;
307 case 6: c_putstr(d, "43"); break;
308 case 7: c_putstr(d, "47"); break;
309 }
310 /* TODO: blink */
311 c_putstr(d, "m");
312 }
313
314 if (ch >= 0x20 && ch != 127)
315 console_putchar(d->console_handle, ch);
316
317 oldcolor = (bg << 4) + fg;
318 printed_last = 1;
319 }
320
321 /* Restore the terminal's cursor position: */
322 snprintf(s, sizeof(s), "\033[%i;%iH", d->cursor_y + 1, d->cursor_x + 1);
323 c_putstr(d, s);
324 }
325
326
327 /*
328 * vga_update_graphics():
329 *
330 * This function should be called whenever any part of d->gfx_mem[] has
331 * been written to. It will redraw all pixels within the range x1,y1
332 * .. x2,y2 using the right palette.
333 */
334 static void vga_update_graphics(struct machine *machine, struct vga_data *d,
335 int x1, int y1, int x2, int y2)
336 {
337 int x, y, ix, iy, c, rx = d->pixel_repx, ry = d->pixel_repy;
338 unsigned char pixel[3];
339
340 for (y=y1; y<=y2; y++)
341 for (x=x1; x<=x2; x++) {
342 /* addr is where to read from VGA memory, addr2 is
343 where to write on the 24-bit framebuffer device */
344 int addr = (y * d->max_x + x) * d->bits_per_pixel;
345 switch (d->bits_per_pixel) {
346 case 8: addr >>= 3;
347 c = d->gfx_mem[addr];
348 pixel[0] = d->fb->rgb_palette[c*3+0];
349 pixel[1] = d->fb->rgb_palette[c*3+1];
350 pixel[2] = d->fb->rgb_palette[c*3+2];
351 break;
352 case 4: addr >>= 2;
353 if (addr & 1)
354 c = d->gfx_mem[addr >> 1] >> 4;
355 else
356 c = d->gfx_mem[addr >> 1] & 0xf;
357 pixel[0] = d->fb->rgb_palette[c*3+0];
358 pixel[1] = d->fb->rgb_palette[c*3+1];
359 pixel[2] = d->fb->rgb_palette[c*3+2];
360 break;
361 }
362 for (iy=y*ry; iy<(y+1)*ry; iy++)
363 for (ix=x*rx; ix<(x+1)*rx; ix++) {
364 uint32_t addr2 = (d->fb_max_x * iy
365 + ix) * 3;
366 if (addr2 < d->fb_size)
367 dev_fb_access(machine->cpus[0],
368 machine->memory, addr2,
369 pixel, sizeof(pixel),
370 MEM_WRITE, d->fb);
371 }
372 }
373 }
374
375
376 /*
377 * vga_update_text():
378 *
379 * This function should be called whenever any part of d->charcells[] has
380 * been written to. It will redraw all characters within the range x1,y1
381 * .. x2,y2 using the right palette.
382 */
383 static void vga_update_text(struct machine *machine, struct vga_data *d,
384 int x1, int y1, int x2, int y2)
385 {
386 int fg, bg, x,y, subx, line;
387 size_t i, start, end, base;
388 int font_size = d->font_height;
389 int font_width = d->font_width;
390 unsigned char *pal = d->fb->rgb_palette;
391
392 if (d->pixel_repx * font_width > 8*8) {
393 fatal("[ too large font ]\n");
394 return;
395 }
396
397 /* Hm... I'm still using the old start..end code: */
398 start = (d->max_x * y1 + x1) * 2;
399 end = (d->max_x * y2 + x2) * 2;
400
401 start &= ~1;
402 end |= 1;
403
404 if (end >= d->charcells_size)
405 end = d->charcells_size - 1;
406
407 base = ((d->crtc_reg[VGA_CRTC_START_ADDR_HIGH] << 8)
408 + d->crtc_reg[VGA_CRTC_START_ADDR_LOW]) * 2;
409
410 if (!machine->use_x11)
411 vga_update_textmode(machine, d, base, start, end);
412
413 for (i=start; i<=end; i+=2) {
414 unsigned char ch = d->charcells[i + base];
415
416 if (!d->palette_modified && d->charcells_drawn[i] == ch &&
417 d->charcells_drawn[i+1] == d->charcells[i+base+1])
418 continue;
419
420 d->charcells_drawn[i] = ch;
421 d->charcells_drawn[i+1] = d->charcells[i + base + 1];
422
423 fg = d->charcells[i+base + 1] & 15;
424 bg = (d->charcells[i+base + 1] >> 4) & 7;
425
426 /* Blink is hard to do :-), but inversion might be ok too: */
427 if (d->charcells[i+base + 1] & 128) {
428 int tmp = fg; fg = bg; bg = tmp;
429 }
430
431 x = (i/2) % d->max_x; x *= font_width;
432 y = (i/2) / d->max_x; y *= font_size;
433
434 /* Draw the character: */
435 for (line = 0; line < font_size; line++) {
436 /* hardcoded for max 8 scaleup... :-) */
437 unsigned char rgb_line[3 * 8 * 8];
438 int iy;
439
440 for (subx = 0; subx < font_width; subx++) {
441 int ix, color_index;
442
443 if (d->use_palette_per_line) {
444 int sline = d->pixel_repy * (line+y);
445 if (sline < MAX_RETRACE_SCANLINES)
446 pal = d->retrace_palette
447 + sline * 256*3;
448 else
449 pal = d->fb->rgb_palette;
450 }
451
452 if (d->font[ch * font_size + line] &
453 (128 >> subx))
454 color_index = fg;
455 else
456 color_index = bg;
457
458 for (ix=0; ix<d->pixel_repx; ix++)
459 memcpy(rgb_line + (subx*d->pixel_repx +
460 ix) * 3, &pal[color_index * 3], 3);
461 }
462
463 for (iy=0; iy<d->pixel_repy; iy++) {
464 uint32_t addr = (d->fb_max_x * (d->pixel_repy *
465 (line+y) + iy) + x * d->pixel_repx) * 3;
466 if (addr >= d->fb_size)
467 continue;
468 dev_fb_access(machine->cpus[0],
469 machine->memory, addr, rgb_line,
470 3 * font_width, MEM_WRITE, d->fb);
471 }
472 }
473 }
474 }
475
476
477 /*
478 * vga_update_cursor():
479 */
480 static void vga_update_cursor(struct machine *machine, struct vga_data *d)
481 {
482 int onoff = 1, height = d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_END]
483 - d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_START] + 1;
484
485 if (d->cur_mode != MODE_CHARCELL)
486 onoff = 0;
487
488 if (d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_START] >
489 d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_END]) {
490 onoff = 0;
491 height = 1;
492 }
493
494 if (d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_START] >= d->font_height)
495 onoff = 0;
496
497 dev_fb_setcursor(d->fb,
498 d->cursor_x * d->font_width * d->pixel_repx, (d->cursor_y *
499 d->font_height + d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_START]) *
500 d->pixel_repy, onoff, d->font_width * d->pixel_repx, height *
501 d->pixel_repy);
502 }
503
504
505 /*
506 * dev_vga_tick():
507 */
508 void dev_vga_tick(struct cpu *cpu, void *extra)
509 {
510 struct vga_data *d = extra;
511 int64_t low = -1, high;
512
513 vga_update_cursor(cpu->machine, d);
514
515 /* TODO: text vs graphics tick? */
516 memory_device_dyntrans_access(cpu, cpu->mem, extra,
517 (uint64_t *) &low, (uint64_t *) &high);
518
519 if (low != -1) {
520 int base = ((d->crtc_reg[VGA_CRTC_START_ADDR_HIGH] << 8)
521 + d->crtc_reg[VGA_CRTC_START_ADDR_LOW]) * 2;
522 int new_u_y1, new_u_y2;
523 debug("[ dev_vga_tick: dyntrans access, %llx .. %llx ]\n",
524 (long long)low, (long long)high);
525 low -= base;
526 high -= base;
527 d->update_x1 = 0;
528 d->update_x2 = d->max_x - 1;
529 new_u_y1 = (low/2) / d->max_x;
530 new_u_y2 = ((high/2) / d->max_x) + 1;
531 if (new_u_y1 < d->update_y1)
532 d->update_y1 = new_u_y1;
533 if (new_u_y2 > d->update_y2)
534 d->update_y2 = new_u_y2;
535 if (d->update_y1 < 0)
536 d->update_y1 = 0;
537 if (d->update_y2 >= d->max_y)
538 d->update_y2 = d->max_y - 1;
539 d->modified = 1;
540 }
541
542 if (d->n_is1_reads > N_IS1_READ_THRESHOLD &&
543 d->retrace_palette != NULL) {
544 d->use_palette_per_line = 1;
545 d->update_x1 = 0;
546 d->update_x2 = d->max_x - 1;
547 d->update_y1 = 0;
548 d->update_y2 = d->max_y - 1;
549 d->modified = 1;
550 } else {
551 if (d->use_palette_per_line) {
552 d->use_palette_per_line = 0;
553 d->update_x1 = 0;
554 d->update_x2 = d->max_x - 1;
555 d->update_y1 = 0;
556 d->update_y2 = d->max_y - 1;
557 d->modified = 1;
558 }
559 }
560
561 if (!cpu->machine->use_x11) {
562 /* NOTE: 2 > 0, so this only updates the cursor, no
563 character cells. */
564 vga_update_textmode(cpu->machine, d, 0, 2, 0);
565 }
566
567 if (d->modified) {
568 if (d->cur_mode == MODE_CHARCELL)
569 vga_update_text(cpu->machine, d, d->update_x1,
570 d->update_y1, d->update_x2, d->update_y2);
571 else
572 vga_update_graphics(cpu->machine, d, d->update_x1,
573 d->update_y1, d->update_x2, d->update_y2);
574
575 d->palette_modified = 0;
576 d->modified = 0;
577 d->update_x1 = 999999;
578 d->update_x2 = -1;
579 d->update_y1 = 999999;
580 d->update_y2 = -1;
581 }
582
583 if (d->n_is1_reads > N_IS1_READ_THRESHOLD)
584 d->n_is1_reads = 0;
585 }
586
587
588 /*
589 * vga_graphics_access():
590 *
591 * Reads and writes to the VGA video memory (pixels).
592 */
593 DEVICE_ACCESS(vga_graphics)
594 {
595 struct vga_data *d = extra;
596 int j, x=0, y=0, x2=0, y2=0, modified = 0;
597 size_t i;
598
599 if (relative_addr + len >= GFX_ADDR_WINDOW)
600 return 0;
601
602 if (d->cur_mode != MODE_GRAPHICS)
603 return 1;
604
605 switch (d->graphics_mode) {
606 case GRAPHICS_MODE_8BIT:
607 y = relative_addr / d->max_x;
608 x = relative_addr % d->max_x;
609 y2 = (relative_addr+len-1) / d->max_x;
610 x2 = (relative_addr+len-1) % d->max_x;
611
612 if (writeflag == MEM_WRITE) {
613 memcpy(d->gfx_mem + relative_addr, data, len);
614 modified = 1;
615 } else
616 memcpy(data, d->gfx_mem + relative_addr, len);
617 break;
618 case GRAPHICS_MODE_4BIT:
619 y = relative_addr * 8 / d->max_x;
620 x = relative_addr * 8 % d->max_x;
621 y2 = ((relative_addr+len)*8-1) / d->max_x;
622 x2 = ((relative_addr+len)*8-1) % d->max_x;
623 /* TODO: color stuff */
624
625 /* Read/write d->gfx_mem in 4-bit color: */
626 if (writeflag == MEM_WRITE) {
627 /* i is byte index to write, j is bit index */
628 for (i=0; i<len; i++)
629 for (j=0; j<8; j++) {
630 int pixelmask = 1 << (7-j);
631 int b = data[i] & pixelmask;
632 int m = d->sequencer_reg[
633 VGA_SEQ_MAP_MASK] & 0x0f;
634 uint32_t addr = (y * d->max_x + x +
635 i*8 + j) * d->bits_per_pixel / 8;
636 unsigned char byte;
637 if (!(d->graphcontr_reg[
638 VGA_GRAPHCONTR_MASK] & pixelmask))
639 continue;
640 if (addr >= d->gfx_mem_size)
641 continue;
642 byte = d->gfx_mem[addr];
643 if (b && j&1)
644 byte |= m << 4;
645 if (b && !(j&1))
646 byte |= m;
647 if (!b && j&1)
648 byte &= ~(m << 4);
649 if (!b && !(j&1))
650 byte &= ~m;
651 d->gfx_mem[addr] = byte;
652 }
653 modified = 1;
654 } else {
655 fatal("TODO: 4 bit graphics read, mask=0x%02x\n",
656 d->sequencer_reg[VGA_SEQ_MAP_MASK]);
657 for (i=0; i<len; i++)
658 data[i] = random();
659 }
660 break;
661 default:fatal("dev_vga: Unimplemented graphics mode %i\n",
662 d->graphics_mode);
663 cpu->running = 0;
664 }
665
666 if (modified) {
667 d->modified = 1;
668 if (x < d->update_x1) d->update_x1 = x;
669 if (x > d->update_x2) d->update_x2 = x;
670 if (y < d->update_y1) d->update_y1 = y;
671 if (y > d->update_y2) d->update_y2 = y;
672 if (x2 < d->update_x1) d->update_x1 = x2;
673 if (x2 > d->update_x2) d->update_x2 = x2;
674 if (y2 < d->update_y1) d->update_y1 = y2;
675 if (y2 > d->update_y2) d->update_y2 = y2;
676 if (y != y2) {
677 d->update_x1 = 0;
678 d->update_x2 = d->max_x - 1;
679 }
680 }
681 return 1;
682 }
683
684
685 /*
686 * dev_vga_access():
687 *
688 * Reads and writes to the VGA video memory (charcells).
689 */
690 DEVICE_ACCESS(vga)
691 {
692 struct vga_data *d = extra;
693 uint64_t idata = 0, odata = 0;
694 int x, y, x2, y2, r, base;
695 size_t i;
696
697 if (writeflag == MEM_WRITE)
698 idata = memory_readmax64(cpu, data, len);
699
700 base = ((d->crtc_reg[VGA_CRTC_START_ADDR_HIGH] << 8)
701 + d->crtc_reg[VGA_CRTC_START_ADDR_LOW]) * 2;
702 r = relative_addr - base;
703 y = r / (d->max_x * 2);
704 x = (r/2) % d->max_x;
705 y2 = (r+len-1) / (d->max_x * 2);
706 x2 = ((r+len-1)/2) % d->max_x;
707
708 if (relative_addr + len - 1 < d->charcells_size) {
709 if (writeflag == MEM_WRITE) {
710 for (i=0; i<len; i++) {
711 int old = d->charcells[relative_addr + i];
712 if (old != data[i]) {
713 d->charcells[relative_addr + i] =
714 data[i];
715 d->modified = 1;
716 }
717 }
718
719 if (d->modified) {
720 if (x < d->update_x1) d->update_x1 = x;
721 if (x > d->update_x2) d->update_x2 = x;
722 if (y < d->update_y1) d->update_y1 = y;
723 if (y > d->update_y2) d->update_y2 = y;
724 if (x2 < d->update_x1) d->update_x1 = x2;
725 if (x2 > d->update_x2) d->update_x2 = x2;
726 if (y2 < d->update_y1) d->update_y1 = y2;
727 if (y2 > d->update_y2) d->update_y2 = y2;
728
729 if (y != y2) {
730 d->update_x1 = 0;
731 d->update_x2 = d->max_x - 1;
732 }
733 }
734 } else
735 memcpy(data, d->charcells + relative_addr, len);
736 return 1;
737 }
738
739 switch (relative_addr) {
740 default:
741 if (writeflag==MEM_READ) {
742 debug("[ vga: read from 0x%08lx ]\n",
743 (long)relative_addr);
744 } else {
745 debug("[ vga: write to 0x%08lx: 0x%08x ]\n",
746 (long)relative_addr, idata);
747 }
748 }
749
750 if (writeflag == MEM_READ)
751 memory_writemax64(cpu, data, len, odata);
752
753 return 1;
754 }
755
756
757 /*
758 * vga_crtc_reg_write():
759 *
760 * Writes to VGA CRTC registers.
761 */
762 static void vga_crtc_reg_write(struct machine *machine, struct vga_data *d,
763 int regnr, int idata)
764 {
765 int grayscale;
766
767 switch (regnr) {
768 case VGA_CRTC_CURSOR_SCANLINE_START: /* 0x0a */
769 case VGA_CRTC_CURSOR_SCANLINE_END: /* 0x0b */
770 break;
771 case VGA_CRTC_START_ADDR_HIGH: /* 0x0c */
772 case VGA_CRTC_START_ADDR_LOW: /* 0x0d */
773 d->update_x1 = 0;
774 d->update_x2 = d->max_x - 1;
775 d->update_y1 = 0;
776 d->update_y2 = d->max_y - 1;
777 d->modified = 1;
778 recalc_cursor_position(d);
779 break;
780 case VGA_CRTC_CURSOR_LOCATION_HIGH: /* 0x0e */
781 case VGA_CRTC_CURSOR_LOCATION_LOW: /* 0x0f */
782 recalc_cursor_position(d);
783 break;
784 case 0xff:
785 grayscale = 0;
786 switch (d->crtc_reg[0xff]) {
787 case 0x00:
788 grayscale = 1;
789 case 0x01:
790 d->cur_mode = MODE_CHARCELL;
791 d->max_x = 40; d->max_y = 25;
792 d->pixel_repx = machine->x11_scaleup * 2;
793 d->pixel_repy = machine->x11_scaleup;
794 d->font_width = 8;
795 d->font_height = 16;
796 d->font = font8x16;
797 break;
798 case 0x02:
799 grayscale = 1;
800 case 0x03:
801 d->cur_mode = MODE_CHARCELL;
802 d->max_x = 80; d->max_y = 25;
803 d->pixel_repx = d->pixel_repy = machine->x11_scaleup;
804 d->font_width = 8;
805 d->font_height = 16;
806 d->font = font8x16;
807 break;
808 case 0x08:
809 d->cur_mode = MODE_GRAPHICS;
810 d->max_x = 160; d->max_y = 200;
811 d->graphics_mode = GRAPHICS_MODE_4BIT;
812 d->bits_per_pixel = 4;
813 d->pixel_repx = 4 * machine->x11_scaleup;
814 d->pixel_repy = 2 * machine->x11_scaleup;
815 break;
816 case 0x09:
817 case 0x0d:
818 d->cur_mode = MODE_GRAPHICS;
819 d->max_x = 320; d->max_y = 200;
820 d->graphics_mode = GRAPHICS_MODE_4BIT;
821 d->bits_per_pixel = 4;
822 d->pixel_repx = d->pixel_repy =
823 2 * machine->x11_scaleup;
824 break;
825 case 0x0e:
826 d->cur_mode = MODE_GRAPHICS;
827 d->max_x = 640; d->max_y = 200;
828 d->graphics_mode = GRAPHICS_MODE_4BIT;
829 d->bits_per_pixel = 4;
830 d->pixel_repx = machine->x11_scaleup;
831 d->pixel_repy = machine->x11_scaleup * 2;
832 break;
833 case 0x10:
834 d->cur_mode = MODE_GRAPHICS;
835 d->max_x = 640; d->max_y = 350;
836 d->graphics_mode = GRAPHICS_MODE_4BIT;
837 d->bits_per_pixel = 4;
838 d->pixel_repx = d->pixel_repy = machine->x11_scaleup;
839 break;
840 case 0x12:
841 d->cur_mode = MODE_GRAPHICS;
842 d->max_x = 640; d->max_y = 480;
843 d->graphics_mode = GRAPHICS_MODE_4BIT;
844 d->bits_per_pixel = 4;
845 d->pixel_repx = d->pixel_repy = machine->x11_scaleup;
846 break;
847 case 0x13:
848 d->cur_mode = MODE_GRAPHICS;
849 d->max_x = 320; d->max_y = 200;
850 d->graphics_mode = GRAPHICS_MODE_8BIT;
851 d->bits_per_pixel = 8;
852 d->pixel_repx = d->pixel_repy =
853 2 * machine->x11_scaleup;
854 break;
855 default:
856 fatal("TODO! video mode change hack (mode 0x%02x)\n",
857 d->crtc_reg[0xff]);
858 exit(1);
859 }
860
861 if (d->cur_mode == MODE_CHARCELL) {
862 dev_fb_resize(d->fb, d->max_x * d->font_width *
863 d->pixel_repx, d->max_y * d->font_height *
864 d->pixel_repy);
865 d->fb_size = d->max_x * d->pixel_repx * d->font_width *
866 d->max_y * d->pixel_repy * d->font_height * 3;
867 } else {
868 dev_fb_resize(d->fb, d->max_x * d->pixel_repx,
869 d->max_y * d->pixel_repy);
870 d->fb_size = d->max_x * d->pixel_repx *
871 d->max_y * d->pixel_repy * 3;
872 }
873
874 if (d->gfx_mem != NULL)
875 free(d->gfx_mem);
876 d->gfx_mem_size = 1;
877 if (d->cur_mode == MODE_GRAPHICS)
878 d->gfx_mem_size = d->max_x * d->max_y /
879 (d->graphics_mode == GRAPHICS_MODE_8BIT? 1 : 2);
880 d->gfx_mem = malloc(d->gfx_mem_size);
881
882 /* Clear screen and reset the palette: */
883 memset(d->charcells_outputed, 0, d->charcells_size);
884 memset(d->charcells_drawn, 0, d->charcells_size);
885 memset(d->gfx_mem, 0, d->gfx_mem_size);
886 d->update_x1 = 0;
887 d->update_x2 = d->max_x - 1;
888 d->update_y1 = 0;
889 d->update_y2 = d->max_y - 1;
890 d->modified = 1;
891 reset_palette(d, grayscale);
892 register_reset(d);
893 break;
894 default:fatal("[ vga_crtc_reg_write: regnr=0x%02x idata=0x%02x ]\n",
895 regnr, idata);
896 }
897 }
898
899
900 /*
901 * vga_sequencer_reg_write():
902 *
903 * Writes to VGA Sequencer registers.
904 */
905 static void vga_sequencer_reg_write(struct machine *machine, struct vga_data *d,
906 int regnr, int idata)
907 {
908 switch (regnr) {
909 case VGA_SEQ_RESET:
910 case VGA_SEQ_MAP_MASK:
911 case VGA_SEQ_SEQUENCER_MEMORY_MODE:
912 debug("[ vga_sequencer_reg_write: select %i: TODO ]\n", regnr);
913 break;
914 default:fatal("[ vga_sequencer_reg_write: select %i ]\n", regnr);
915 /* cpu->running = 0; */
916 }
917 }
918
919
920 /*
921 * vga_graphcontr_reg_write():
922 *
923 * Writes to VGA Graphics Controller registers.
924 */
925 static void vga_graphcontr_reg_write(struct machine *machine,
926 struct vga_data *d, int regnr, int idata)
927 {
928 switch (regnr) {
929 case VGA_GRAPHCONTR_READMAPSELECT:
930 case VGA_GRAPHCONTR_GRAPHICSMODE:
931 case VGA_GRAPHCONTR_MISC:
932 case VGA_GRAPHCONTR_MASK:
933 debug("[ vga_graphcontr_reg_write: select %i: TODO ]\n", regnr);
934 break;
935 default:fatal("[ vga_graphcontr_reg_write: select %i ]\n", regnr);
936 /* cpu->running = 0; */
937 }
938 }
939
940
941 /*
942 * vga_attribute_reg_write():
943 *
944 * Writes to VGA Attribute registers.
945 */
946 static void vga_attribute_reg_write(struct machine *machine, struct vga_data *d,
947 int regnr, int idata)
948 {
949 /* 0-15 are palette registers: TODO */
950 if (regnr >= 0 && regnr <= 0xf)
951 return;
952
953 switch (regnr) {
954 default:fatal("[ vga_attribute_reg_write: select %i ]\n", regnr);
955 /* cpu->running = 0; */
956 }
957 }
958
959
960 /*
961 * dev_vga_ctrl_access():
962 *
963 * Reads and writes of the VGA control registers.
964 */
965 DEVICE_ACCESS(vga_ctrl)
966 {
967 struct vga_data *d = extra;
968 size_t i;
969 uint64_t idata = 0, odata = 0;
970
971 for (i=0; i<len; i++) {
972 idata = data[i];
973
974 /* 0x3C0 + relative_addr... */
975
976 switch (relative_addr) {
977
978 case VGA_ATTRIBUTE_ADDR: /* 0x00 */
979 switch (d->attribute_state) {
980 case 0: if (writeflag == MEM_READ)
981 odata = d->attribute_reg_select;
982 else {
983 d->attribute_reg_select = 1;
984 d->attribute_state = 1;
985 }
986 break;
987 case 1: d->attribute_state = 0;
988 d->attribute_reg[d->attribute_reg_select] =
989 idata;
990 vga_attribute_reg_write(cpu->machine, d,
991 d->attribute_reg_select, idata);
992 break;
993 }
994 break;
995 case VGA_ATTRIBUTE_DATA_READ: /* 0x01 */
996 if (writeflag == MEM_WRITE)
997 fatal("[ dev_vga: WARNING: Write to "
998 "VGA_ATTRIBUTE_DATA_READ? ]\n");
999 else {
1000 if (d->attribute_state == 0)
1001 fatal("[ dev_vga: WARNING: Read from "
1002 "VGA_ATTRIBUTE_DATA_READ, but no"
1003 " register selected? ]\n");
1004 else
1005 odata = d->attribute_reg[
1006 d->attribute_reg_select];
1007 }
1008 break;
1009
1010 case VGA_MISC_OUTPUT_W: /* 0x02 */
1011 if (writeflag == MEM_WRITE)
1012 d->misc_output_reg = idata;
1013 else {
1014 /* Reads: Input Status 0 */
1015 odata = 0x00;
1016 }
1017 break;
1018
1019 case VGA_SEQUENCER_ADDR: /* 0x04 */
1020 if (writeflag == MEM_READ)
1021 odata = d->sequencer_reg_select;
1022 else
1023 d->sequencer_reg_select = idata;
1024 break;
1025 case VGA_SEQUENCER_DATA: /* 0x05 */
1026 if (writeflag == MEM_READ)
1027 odata = d->sequencer_reg[
1028 d->sequencer_reg_select];
1029 else {
1030 d->sequencer_reg[d->
1031 sequencer_reg_select] = idata;
1032 vga_sequencer_reg_write(cpu->machine, d,
1033 d->sequencer_reg_select, idata);
1034 }
1035 break;
1036
1037 case VGA_DAC_ADDR_READ: /* 0x07 */
1038 if (writeflag == MEM_WRITE) {
1039 d->palette_read_index = idata;
1040 d->palette_read_subindex = 0;
1041 } else {
1042 debug("[ dev_vga: WARNING: Read from "
1043 "VGA_DAC_ADDR_READ? TODO ]\n");
1044 /* TODO */
1045 }
1046 break;
1047 case VGA_DAC_ADDR_WRITE: /* 0x08 */
1048 if (writeflag == MEM_WRITE) {
1049 d->palette_write_index = idata;
1050 d->palette_write_subindex = 0;
1051
1052 /* TODO: Is this correct? */
1053 d->palette_read_index = idata;
1054 d->palette_read_subindex = 0;
1055 } else {
1056 fatal("[ dev_vga: WARNING: Read from "
1057 "VGA_DAC_ADDR_WRITE? ]\n");
1058 odata = d->palette_write_index;
1059 }
1060 break;
1061 case VGA_DAC_DATA: /* 0x09 */
1062 if (writeflag == MEM_WRITE) {
1063 int new = (idata & 63) << 2;
1064 int old = d->fb->rgb_palette[d->
1065 palette_write_index*3+d->
1066 palette_write_subindex];
1067 d->fb->rgb_palette[d->palette_write_index * 3 +
1068 d->palette_write_subindex] = new;
1069 /* Redraw whole screen, if the
1070 palette changed: */
1071 if (new != old) {
1072 d->modified = 1;
1073 d->palette_modified = 1;
1074 d->update_x1 = d->update_y1 = 0;
1075 d->update_x2 = d->max_x - 1;
1076 d->update_y2 = d->max_y - 1;
1077 }
1078 d->palette_write_subindex ++;
1079 if (d->palette_write_subindex == 3) {
1080 d->palette_write_index ++;
1081 d->palette_write_subindex = 0;
1082 }
1083 } else {
1084 odata = (d->fb->rgb_palette[d->
1085 palette_read_index * 3 +
1086 d->palette_read_subindex] >> 2) & 63;
1087 d->palette_read_subindex ++;
1088 if (d->palette_read_subindex == 3) {
1089 d->palette_read_index ++;
1090 d->palette_read_subindex = 0;
1091 }
1092 }
1093 break;
1094
1095 case VGA_MISC_OUTPUT_R:
1096 odata = d->misc_output_reg;
1097 break;
1098
1099 case VGA_GRAPHCONTR_ADDR: /* 0x0e */
1100 if (writeflag == MEM_READ)
1101 odata = d->graphcontr_reg_select;
1102 else
1103 d->graphcontr_reg_select = idata;
1104 break;
1105 case VGA_GRAPHCONTR_DATA: /* 0x0f */
1106 if (writeflag == MEM_READ)
1107 odata = d->graphcontr_reg[
1108 d->graphcontr_reg_select];
1109 else {
1110 d->graphcontr_reg[d->
1111 graphcontr_reg_select] = idata;
1112 vga_graphcontr_reg_write(cpu->machine, d,
1113 d->graphcontr_reg_select, idata);
1114 }
1115 break;
1116
1117 case VGA_CRTC_ADDR: /* 0x14 */
1118 if (writeflag == MEM_READ)
1119 odata = d->crtc_reg_select;
1120 else
1121 d->crtc_reg_select = idata;
1122 break;
1123 case VGA_CRTC_DATA: /* 0x15 */
1124 if (writeflag == MEM_READ)
1125 odata = d->crtc_reg[d->crtc_reg_select];
1126 else {
1127 d->crtc_reg[d->crtc_reg_select] = idata;
1128 vga_crtc_reg_write(cpu->machine, d,
1129 d->crtc_reg_select, idata);
1130 }
1131 break;
1132
1133 case VGA_INPUT_STATUS_1: /* 0x1A */
1134 odata = 0;
1135 d->n_is1_reads ++;
1136 d->current_retrace_line ++;
1137 d->current_retrace_line %= (MAX_RETRACE_SCANLINES * 8);
1138 /* Whenever we are "inside" a scan line, copy the
1139 current palette into retrace_palette[][]: */
1140 if ((d->current_retrace_line & 7) == 7) {
1141 if (d->retrace_palette == NULL &&
1142 d->n_is1_reads > N_IS1_READ_THRESHOLD) {
1143 d->retrace_palette = malloc(
1144 MAX_RETRACE_SCANLINES * 256*3);
1145 if (d->retrace_palette == NULL) {
1146 fatal("out of memory\n");
1147 exit(1);
1148 }
1149 }
1150 if (d->retrace_palette != NULL)
1151 memcpy(d->retrace_palette + (d->
1152 current_retrace_line >> 3) * 256*3,
1153 d->fb->rgb_palette, d->cur_mode ==
1154 MODE_CHARCELL? (16*3) : (256*3));
1155 }
1156 /* These need to go on and off, to fake the
1157 real vertical and horizontal retrace info. */
1158 if (d->current_retrace_line < 20*8)
1159 odata |= VGA_IS1_DISPLAY_VRETRACE;
1160 else {
1161 if ((d->current_retrace_line & 7) == 0)
1162 odata = VGA_IS1_DISPLAY_DISPLAY_DISABLE;
1163 }
1164 break;
1165
1166 default:
1167 if (writeflag==MEM_READ) {
1168 debug("[ vga_ctrl: read from 0x%08lx ]\n",
1169 (long)relative_addr);
1170 } else {
1171 debug("[ vga_ctrl: write to 0x%08lx: 0x%08x"
1172 " ]\n", (long)relative_addr, (int)idata);
1173 }
1174 }
1175
1176 if (writeflag == MEM_READ)
1177 data[i] = odata;
1178
1179 /* For multi-byte accesses: */
1180 relative_addr ++;
1181 }
1182
1183 return 1;
1184 }
1185
1186
1187 /*
1188 * dev_vga_init():
1189 *
1190 * Register a VGA text console device. max_x and max_y could be something
1191 * like 80 and 25, respectively.
1192 */
1193 void dev_vga_init(struct machine *machine, struct memory *mem,
1194 uint64_t videomem_base, uint64_t control_base, char *name)
1195 {
1196 struct vga_data *d;
1197 size_t i;
1198 size_t allocsize;
1199
1200 d = malloc(sizeof(struct vga_data));
1201 if (d == NULL) {
1202 fprintf(stderr, "out of memory\n");
1203 exit(1);
1204 }
1205 memset(d, 0, sizeof(struct vga_data));
1206
1207 d->console_handle = console_start_slave(machine, "vga",
1208 CONSOLE_OUTPUT_ONLY);
1209
1210 d->videomem_base = videomem_base;
1211 d->control_base = control_base;
1212 d->max_x = 80;
1213 d->max_y = 25;
1214 d->cur_mode = MODE_CHARCELL;
1215 d->crtc_reg[0xff] = 0x03;
1216 d->charcells_size = 0x8000;
1217 d->gfx_mem_size = 1; /* Nothing, as we start in text mode */
1218 d->pixel_repx = d->pixel_repy = machine->x11_scaleup;
1219
1220 /* Allocate in full pages, to make it possible to use bintrans: */
1221 allocsize = ((d->charcells_size-1) | (machine->arch_pagesize-1)) + 1;
1222 d->charcells = malloc(d->charcells_size);
1223 d->charcells_outputed = malloc(d->charcells_size);
1224 d->charcells_drawn = malloc(d->charcells_size);
1225 d->gfx_mem = malloc(d->gfx_mem_size);
1226 if (d->charcells == NULL || d->charcells_outputed == NULL ||
1227 d->charcells_drawn == NULL || d->gfx_mem == NULL) {
1228 fprintf(stderr, "out of memory in dev_vga_init()\n");
1229 exit(1);
1230 }
1231
1232 memset(d->charcells_drawn, 0, d->charcells_size);
1233
1234 for (i=0; i<d->charcells_size; i+=2) {
1235 d->charcells[i] = ' ';
1236 d->charcells[i+1] = 0x07; /* Default color */
1237 d->charcells_drawn[i] = ' ';
1238 d->charcells_drawn[i+1] = 0x07;
1239 }
1240
1241 memset(d->charcells_outputed, 0, d->charcells_size);
1242 memset(d->gfx_mem, 0, d->gfx_mem_size);
1243
1244 d->font = font8x16;
1245 d->font_width = 8;
1246 d->font_height = 16;
1247
1248 d->fb_max_x = d->pixel_repx * d->max_x;
1249 d->fb_max_y = d->pixel_repy * d->max_y;
1250 if (d->cur_mode == MODE_CHARCELL) {
1251 d->fb_max_x *= d->font_width;
1252 d->fb_max_y *= d->font_height;
1253 }
1254
1255 memory_device_register(mem, "vga_charcells", videomem_base + 0x18000,
1256 allocsize, dev_vga_access, d, DM_DYNTRANS_OK |
1257 DM_DYNTRANS_WRITE_OK | DM_READS_HAVE_NO_SIDE_EFFECTS,
1258 d->charcells);
1259 memory_device_register(mem, "vga_gfx", videomem_base, GFX_ADDR_WINDOW,
1260 dev_vga_graphics_access, d, DM_DEFAULT |
1261 DM_READS_HAVE_NO_SIDE_EFFECTS, d->gfx_mem);
1262 memory_device_register(mem, "vga_ctrl", control_base,
1263 32, dev_vga_ctrl_access, d, DM_DEFAULT, NULL);
1264
1265 d->fb = dev_fb_init(machine, mem, VGA_FB_ADDR, VFB_GENERIC,
1266 d->fb_max_x, d->fb_max_y, d->fb_max_x, d->fb_max_y, 24, "VGA");
1267 d->fb_size = d->fb_max_x * d->fb_max_y * 3;
1268
1269 reset_palette(d, 0);
1270
1271 /* This will force an initial redraw/resynch: */
1272 d->update_x1 = 0;
1273 d->update_x2 = d->max_x - 1;
1274 d->update_y1 = 0;
1275 d->update_y2 = d->max_y - 1;
1276 d->modified = 1;
1277
1278 machine_add_tickfunction(machine, dev_vga_tick, d, VGA_TICK_SHIFT);
1279
1280 register_reset(d);
1281
1282 vga_update_cursor(machine, d);
1283 }
1284

  ViewVC Help
Powered by ViewVC 1.1.26