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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10 - (show annotations)
Mon Oct 8 16:18:27 2007 UTC (16 years, 5 months ago) by dpavlin
File MIME type: text/plain
File size: 32317 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.815 2005/06/27 23:04:35 debug Exp $
20050617	Experimenting some more with netbooting OpenBSD/sgi. Adding
		a hack which allows emulated ethernet networks to be
		distributed across multiple emulator processes.
20050618	Minor updates (documentation, dummy YAMON emulation, etc).
20050620	strcpy/strcat -> strlcpy/strlcat updates.
		Some more progress on evbmips (Malta).
20050621	Adding a section to doc/configfiles.html about ethernet
		emulation across multiple hosts.
		Beginning the work on the ARM translation engine (using the
		dynamic-but-not-binary translation method).
		Fixing a bintrans bug: 0x9fc00000 should always be treated as
		PROM area, just as 0xbfc00000 is.
		Minor progress on Malta emulation (the PCI-ISA bus).
20050622	NetBSD/evbmips can now be installed (using another emulated
		machine) and run (including userland and so on). :-)
		Spliting up the bintrans haddr_entry field into two (one for
		read, one for write). Probably not much of a speed increase,
		though.
		Updating some NetBSD 2.0 -> 2.0.2 in the documentation.
20050623	Minor updates (documentation, the TODO file, etc).
		gzipped kernels are now always automagically gunzipped when
		loaded.
20050624	Adding a dummy Playstation Portable (PSP) mode, just barely
		enough to run Hello World (in weird colors :-).
		Removing the -b command line option; old bintrans is enabled
		by default instead. It makes more sense.
		Trying to finally fix the non-working performance measurement
		thing (instr/second etc).
20050625	Continuing on the essential basics for ARM emulation. Two
		instructions seem to work, a branch and a simple "mov". (The
		mov arguments are not correct yet.) Performance is definitely
		reasonable.
		Various other minor updates.
		Adding the ARM "bl" instruction.
		Adding support for combining multiple ARM instructions into one
		function call. ("mov" + "mov" is the only one implemented so
		far, but it seems to work.)
		Cleaning up some IP32 interrupt things (crime/mace); disabling
		the PS/2 keyboard controller on IP32, so that NetBSD/sgimips
		boots into userland again.
20050626	Finally! NetBSD/sgimips netboots. Adding instructions to
		doc/guestoses.html on how to set up an nfs server etc.
		Various other minor fixes.
		Playstation Portable ".pbp" files can now be used directly.
		(The ELF part of the .pbp is extracted transparently.)
		Converting some sprintf -> snprintf.
		Adding some more instructions to the ARM disassembler.
20050627	More ARM updates. Adding some simple ldr(b), str(b),
		cmps, and conditional branch instructions, enough to run
		a simple Hello World program.
		All ARM instructions are now inlined/generated for all possible
		condition codes.
		Adding add and sub, and more load/store instructions.
		Removing dummy files: cpu_alpha.c, cpu_hppa.c, and cpu_sparc.c.
		Some minor documentation updates; preparing for a 0.3.4
		release. Updating some URLs.

==============  RELEASE 0.3.4  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26