/[gxemul]/upstream/0.3.7/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.7/src/devices/dev_vga.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26