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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (show annotations)
Mon Oct 8 16:17:52 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 13688 byte(s)
0.3.1
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.35 2005/03/05 12:31:26 debug Exp $
29 *
30 * VGA text console device.
31 *
32 * A few ugly hacks are used. The default resolution is 640x480, which
33 * means that the following font sizes and text resolutions can be used:
34 *
35 * 8x16 80 x 30
36 * 8x10 (with the last line repeated twice) 80 x 43
37 * 8x8 80 x 60
38 *
39 * There is only a mode switch when actual non-space text is written outside
40 * the current window.
41 */
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include "cpu.h"
48 #include "devices.h"
49 #include "machine.h"
50 #include "memory.h"
51 #include "misc.h"
52
53 /* These are generated from binary font files: */
54 #include "fonts/font8x8.c"
55 #include "fonts/font8x10.c"
56 #include "fonts/font8x16.c"
57
58
59 /* For bintranslated videomem -> framebuffer updates: */
60 #define VGA_TICK_SHIFT 14
61
62 #define VGA_MEM_MAXY 60
63 #define VGA_MEM_ALLOCY 67
64
65
66 #define VGA_FB_ADDR 0x1230000000ULL
67
68 struct vga_data {
69 uint64_t videomem_base;
70 uint64_t control_base;
71
72 struct vfb_data *fb;
73
74 int font_size;
75 unsigned char *font;
76
77 int max_x;
78 int max_y;
79 size_t videomem_size;
80 unsigned char *videomem; /* 2 bytes per char */
81
82 unsigned char selected_register;
83 unsigned char reg[256];
84
85 int palette_index;
86 int palette_subindex;
87
88 int cursor_x;
89 int cursor_y;
90
91 int modified;
92 int update_x1;
93 int update_y1;
94 int update_x2;
95 int update_y2;
96 };
97
98
99 /*
100 * vga_update():
101 *
102 * This function should be called whenever any part of d->videomem[] has
103 * been written to. It will redraw all characters within the range x1,y1
104 * .. x2,y2 using the right palette.
105 */
106 static void vga_update(struct machine *machine, struct vga_data *d,
107 int x1, int y1, int x2, int y2)
108 {
109 int fg, bg, i, x,y, subx, line, start, end;
110
111 /* Hm... I'm still using the old start..end code: */
112 start = (d->max_x * y1 + x1) * 2;
113 end = (d->max_x * y2 + x2) * 2;
114
115 start &= ~1;
116 end |= 1;
117
118 if (end >= d->videomem_size)
119 end = d->videomem_size - 1;
120
121 for (i=start; i<=end; i+=2) {
122 unsigned char ch = d->videomem[i];
123 fg = d->videomem[i+1] & 15;
124 bg = (d->videomem[i+1] >> 4) & 7;
125
126 /* Blink is hard to do :-), but inversion might be ok too: */
127 if (d->videomem[i+1] & 128) {
128 int tmp = fg; fg = bg; bg = tmp;
129 }
130
131 x = (i/2) % d->max_x; x *= 8;
132 y = (i/2) / d->max_x; y *= d->font_size;
133
134 for (line = 0; line < d->font_size; line++) {
135 for (subx = 0; subx < 8; subx++) {
136 unsigned char pixel[3];
137 int addr, line2readfrom = line;
138 int actualfontheight = d->font_size;
139
140 if (d->font_size == 11) {
141 actualfontheight = 10;
142 if (line == 10)
143 line2readfrom = 9;
144 }
145
146 addr = (d->max_x*8 * (line+y) + x + subx)
147 * 3;
148
149 pixel[0] = d->fb->rgb_palette[bg * 3 + 0];
150 pixel[1] = d->fb->rgb_palette[bg * 3 + 1];
151 pixel[2] = d->fb->rgb_palette[bg * 3 + 2];
152
153 if (d->font[ch * actualfontheight +
154 line2readfrom] & (128 >> subx)) {
155 pixel[0] = d->fb->rgb_palette
156 [fg * 3 + 0];
157 pixel[1] = d->fb->rgb_palette
158 [fg * 3 + 1];
159 pixel[2] = d->fb->rgb_palette
160 [fg * 3 + 2];
161 }
162
163 /* TODO: don't hardcode */
164 if (addr < 640 * 480 *3)
165 dev_fb_access(machine->cpus[0],
166 machine->memory, addr, &pixel[0],
167 sizeof(pixel), MEM_WRITE, d->fb);
168 }
169 }
170 }
171 }
172
173
174 /*
175 * vga_update_cursor():
176 */
177 static void vga_update_cursor(struct vga_data *d)
178 {
179 /* TODO: Don't hardcode the cursor size. */
180 dev_fb_setcursor(d->fb,
181 d->cursor_x * 8, d->cursor_y * d->font_size +
182 d->font_size - 4, 1, 8, 3);
183 }
184
185
186 /*
187 * dev_vga_tick():
188 */
189 void dev_vga_tick(struct cpu *cpu, void *extra)
190 {
191 struct vga_data *d = extra;
192 uint64_t low = (uint64_t)-1, high;
193
194 memory_device_bintrans_access(cpu, cpu->mem, extra, &low, &high);
195
196 if ((int64_t)low != -1) {
197 debug("[ dev_vga_tick: bintrans access, %llx .. %llx ]\n",
198 (long long)low, (long long)high);
199 d->update_x1 = 0;
200 d->update_x2 = d->max_x - 1;
201 d->update_y1 = (low/2) / d->max_x;
202 d->update_y2 = ((high/2) / d->max_x) + 1;
203 if (d->update_y2 >= d->max_y)
204 d->update_y2 = d->max_y - 1;
205 d->modified = 1;
206 }
207
208 if (d->modified) {
209 vga_update(cpu->machine, d,
210 d->update_x1, d->update_y1, d->update_x2, d->update_y2);
211
212 d->modified = 0;
213 d->update_x1 = 999999;
214 d->update_x2 = -1;
215 d->update_y1 = 999999;
216 d->update_y2 = -1;
217 }
218 }
219
220
221 /*
222 * dev_vga_access():
223 *
224 * Reads and writes to the VGA video memory.
225 */
226 int dev_vga_access(struct cpu *cpu, struct memory *mem, uint64_t relative_addr,
227 unsigned char *data, size_t len, int writeflag, void *extra)
228 {
229 struct vga_data *d = extra;
230 uint64_t idata = 0, odata = 0;
231 int i, x, y, x2, y2;
232
233 idata = memory_readmax64(cpu, data, len);
234
235 y = relative_addr / (d->max_x * 2);
236 x = (relative_addr/2) % d->max_x;
237
238 y2 = (relative_addr+len-1) / (d->max_x * 2);
239 x2 = ((relative_addr+len-1)/2) % d->max_x;
240
241 /*
242 * Switch fonts? This is an ugly hack which only switches when
243 * parts of the video ram is accessed that are outside the current
244 * screen. (Specially "crafted" :-) to work with Windows NT.)
245 */
246 if (writeflag && (idata & 255) != 0x20 && (relative_addr & 1) == 0) {
247 if (y >= 43 && d->font_size > 8) {
248 /* Switch to 8x8 font: */
249 debug("SWITCHING to 8x8 font\n");
250 d->font_size = 8;
251 d->font = font8x8;
252 d->max_y = VGA_MEM_MAXY;
253 vga_update(cpu->machine, d, 0, 0,
254 d->max_x - 1, d->max_y - 1);
255 vga_update_cursor(d);
256 } else if (y >= 30 && d->font_size > 11) {
257 /* Switch to 8x10 font: */
258 debug("SWITCHING to 8x10 font\n");
259 d->font_size = 11; /* NOTE! 11 */
260 d->font = font8x10;
261 vga_update(cpu->machine, d, 0, 0,
262 d->max_x - 1, d->max_y - 1);
263 d->max_y = 43;
264 vga_update_cursor(d);
265 }
266 }
267
268 if (relative_addr < d->videomem_size) {
269 if (writeflag == MEM_WRITE) {
270 for (i=0; i<len; i++) {
271 int old = d->videomem[relative_addr + i];
272 if (old != data[i]) {
273 d->videomem[relative_addr + i] =
274 data[i];
275 d->modified = 1;
276 }
277 }
278
279 if (d->modified) {
280 if (x < d->update_x1) d->update_x1 = x;
281 if (x > d->update_x2) d->update_x2 = x;
282 if (y < d->update_y1) d->update_y1 = y;
283 if (y > d->update_y2) d->update_y2 = y;
284
285 if (x2 < d->update_x1) d->update_x1 = x2;
286 if (x2 > d->update_x2) d->update_x2 = x2;
287 if (y2 < d->update_y1) d->update_y1 = y2;
288 if (y2 > d->update_y2) d->update_y2 = y2;
289 }
290 } else
291 memcpy(data, d->videomem + relative_addr, len);
292 return 1;
293 }
294
295 switch (relative_addr) {
296 default:
297 if (writeflag==MEM_READ) {
298 debug("[ vga: read from 0x%08lx ]\n",
299 (long)relative_addr);
300 } else {
301 debug("[ vga: write to 0x%08lx: 0x%08x ]\n",
302 (long)relative_addr, idata);
303 }
304 }
305
306 if (writeflag == MEM_READ)
307 memory_writemax64(cpu, data, len, odata);
308
309 return 1;
310 }
311
312
313 /*
314 * vga_reg_write():
315 *
316 * Writes to VGA control registers.
317 */
318 static void vga_reg_write(struct vga_data *d, int regnr, int idata)
319 {
320 int ofs;
321
322 switch (regnr) {
323 case 0x0e:
324 case 0x0f:
325 ofs = d->reg[0x0e] * 256 + d->reg[0x0f];
326 d->cursor_x = ofs % d->max_x;
327 d->cursor_y = ofs / d->max_x;
328 vga_update_cursor(d);
329 break;
330 default:
331 debug("[ vga_reg_write: regnr=0x%02x idata=0x%02x ]\n",
332 regnr, idata);
333 }
334 }
335
336
337 /*
338 * dev_vga_ctrl_access():
339 *
340 * Reads and writes of the VGA control registers.
341 */
342 int dev_vga_ctrl_access(struct cpu *cpu, struct memory *mem,
343 uint64_t relative_addr, unsigned char *data, size_t len,
344 int writeflag, void *extra)
345 {
346 struct vga_data *d = extra;
347 uint64_t idata = 0, odata = 0;
348
349 idata = memory_readmax64(cpu, data, len);
350
351 switch (relative_addr) {
352 case 0x01: /* "Other video attributes" */
353 odata = 0xff; /* ? */
354 break;
355 case 0x08:
356 if (writeflag == MEM_WRITE) {
357 d->palette_index = idata;
358 d->palette_subindex = 0;
359 } else {
360 odata = d->palette_index;
361 }
362 break;
363 case 0x09:
364 if (writeflag == MEM_WRITE) {
365 int new = (idata & 63) << 2;
366 int old = d->fb->rgb_palette[d->palette_index * 3 +
367 d->palette_subindex];
368 d->fb->rgb_palette[d->palette_index * 3 +
369 d->palette_subindex] = new;
370 /* Redraw whole screen, if the palette changed: */
371 if (new != old) {
372 d->modified = 1;
373 d->update_x1 = d->update_y1 = 0;
374 d->update_x2 = d->max_x - 1;
375 d->update_y2 = d->max_y - 1;
376 }
377 } else {
378 odata = (d->fb->rgb_palette[d->palette_index * 3 +
379 d->palette_subindex] >> 2) & 63;
380 }
381 d->palette_subindex ++;
382 if (d->palette_subindex == 3) {
383 d->palette_index ++;
384 d->palette_subindex = 0;
385 }
386 d->palette_index &= 255;
387 break;
388 case 0x0c: /* VGA graphics 1 position */
389 odata = 1; /* ? */
390 break;
391 case 0x14: /* register select */
392 if (writeflag == MEM_READ)
393 odata = d->selected_register;
394 else
395 d->selected_register = idata;
396 break;
397 case 0x15: if (writeflag == MEM_READ)
398 odata = d->reg[d->selected_register];
399 else {
400 d->reg[d->selected_register] = idata;
401 vga_reg_write(d, d->selected_register, idata);
402 }
403 break;
404 case 0x1a: /* Status register */
405 odata = 1; /* Display enabled */
406 /* odata |= 16; */ /* Vertical retrace */
407 break;
408 default:
409 if (writeflag==MEM_READ) {
410 fatal("[ vga_ctrl: read from 0x%08lx ]\n",
411 (long)relative_addr);
412 } else {
413 static int warning = 0;
414 warning ++;
415 if (warning > 2)
416 break;
417 if (warning > 1) {
418 fatal("[ vga_ctrl: multiple unimplemented wr"
419 "ites, ignoring warnings from now on ]\n");
420 break;
421 }
422 fatal("[ vga_ctrl: write to 0x%08lx: 0x%08x ]\n",
423 (long)relative_addr, idata);
424 }
425 }
426
427 if (writeflag == MEM_READ)
428 memory_writemax64(cpu, data, len, odata);
429
430 return 1;
431 }
432
433
434 /*
435 * dev_vga_init():
436 *
437 * Register a VGA text console device. max_x and max_y could be something
438 * like 80 and 25, respectively.
439 */
440 void dev_vga_init(struct machine *machine, struct memory *mem,
441 uint64_t videomem_base, uint64_t control_base, int max_x, int max_y,
442 char *name)
443 {
444 struct vga_data *d;
445 int r,g,b,i, x,y;
446 size_t allocsize;
447
448 d = malloc(sizeof(struct vga_data));
449 if (d == NULL) {
450 fprintf(stderr, "out of memory\n");
451 exit(1);
452 }
453 memset(d, 0, sizeof(struct vga_data));
454 d->videomem_base = videomem_base;
455 d->control_base = control_base;
456 d->max_x = max_x;
457 d->max_y = max_y;
458 d->videomem_size = max_x * VGA_MEM_MAXY * 2;
459
460 /* Allocate in 4KB pages, to make it possible to use bintrans: */
461 allocsize = ((d->videomem_size - 1) | 0xfff) + 1;
462 d->videomem = malloc(d->videomem_size);
463 if (d->videomem == NULL) {
464 fprintf(stderr, "out of memory in dev_vga_init()\n");
465 exit(1);
466 }
467
468 for (y=0; y<VGA_MEM_MAXY; y++) {
469 char s[81];
470 #ifdef VERSION
471 strcpy(s, " GXemul-" VERSION);
472 #else
473 strcpy(s, " GXemul");
474 #endif
475 memset(s+strlen(s), ' ', 80 - strlen(s));
476 memcpy(s+79-strlen(name), name, strlen(name));
477 s[80] = 0;
478
479 for (x=0; x<max_x; x++) {
480 char ch = ' ';
481 if (y == 0)
482 ch = s[x];
483 i = (x + max_x * y) * 2;
484 d->videomem[i] = ch;
485
486 /* Default color: */
487 d->videomem[i+1] = y==0? 0x70 : 0x07;
488 }
489 }
490
491 d->font_size = 16;
492 d->font = font8x16;
493
494 d->fb = dev_fb_init(machine, mem, VGA_FB_ADDR, VFB_GENERIC,
495 8*max_x, 16*max_y, 8*max_x, 16*max_y, 24, "VGA", 0);
496
497 i = 0;
498 for (r=0; r<2; r++)
499 for (g=0; g<2; g++)
500 for (b=0; b<2; b++) {
501 d->fb->rgb_palette[i + 0] = r * 0xaa;
502 d->fb->rgb_palette[i + 1] = g * 0xaa;
503 d->fb->rgb_palette[i + 2] = b * 0xaa;
504 i+=3;
505 }
506 for (r=0; r<2; r++)
507 for (g=0; g<2; g++)
508 for (b=0; b<2; b++) {
509 d->fb->rgb_palette[i + 0] = r * 0xaa + 0x55;
510 d->fb->rgb_palette[i + 1] = g * 0xaa + 0x55;
511 d->fb->rgb_palette[i + 2] = b * 0xaa + 0x55;
512 i+=3;
513 }
514
515 memory_device_register(mem, "vga_mem", videomem_base, allocsize,
516 dev_vga_access, d, MEM_BINTRANS_OK
517 /* | MEM_BINTRANS_WRITE_OK <-- This works with OpenBSD/arc, but not
518 with Windows NT yet. Why? */
519 ,
520 d->videomem);
521 memory_device_register(mem, "vga_ctrl", control_base,
522 32, dev_vga_ctrl_access, d, MEM_DEFAULT, NULL);
523
524 /* Make sure that the first line is in synch. */
525 vga_update(machine, d, 0, 0, d->max_x - 1, 0);
526
527 d->update_x1 = 999999;
528 d->update_x2 = -1;
529 d->update_y1 = 999999;
530 d->update_y2 = -1;
531 d->modified = 0;
532
533 machine_add_tickfunction(machine, dev_vga_tick, d, VGA_TICK_SHIFT);
534 }
535

  ViewVC Help
Powered by ViewVC 1.1.26