2 * Copyright (c) 2001 The FreeBSD Project, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY The FreeBSD Project, Inc. AND CONTRIBUTORS
15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL The FreeBSD Project, Inc. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/types.h>
46 u_int8_t VGA_CRTC[CRTC_Size];
47 u_int8_t VGA_ATC[ATC_Size];
48 u_int8_t VGA_TSC[TSC_Size];
49 u_int8_t VGA_GDC[GDC_Size];
51 /* VGA status information */
52 u_int8_t vga_status[64];
54 /* Table of supported video modes. */
55 vmode_t vmodelist[] = {
56 {0x00, 0x17, TEXT, 16, 8, 2, 0xb8000, FONT8x16},
57 {0x01, 0x17, TEXT, 16, 8, 2, 0xb8000, FONT8x16},
58 {0x02, 0x18, TEXT, 16, 8, 2, 0xb8000, FONT8x16},
59 {0x03, 0x18, TEXT, 16, 8, 2, 0xb8000, FONT8x16},
60 {0x04, 0x04, GRAPHICS, 4, 1, 0, 0xb8000, FONT8x8},
61 {0x05, 0x05, GRAPHICS, 4, 1, 0, 0xb8000, FONT8x8},
62 {0x06, 0x06, GRAPHICS, 2, 1, 0, 0xb8000, FONT8x8},
63 {0x07, 0x19, TEXT, 1, 8, 2, 0xb0000, FONT8x16},
64 {0x08, 0x08, NOMODE, 0, 0, 0, 0, 0},
65 {0x09, 0x09, NOMODE, 0, 0, 0, 0, 0},
66 {0x0a, 0x0a, NOMODE, 0, 0, 0, 0, 0},
67 {0x0b, 0x0b, NOMODE, 0, 0, 0, 0, 0},
68 {0x0c, 0x0c, NOMODE, 0, 0, 0, 0, 0},
69 {0x0d, 0x0d, GRAPHICS, 16, 8, 0, 0xa0000, FONT8x8},
70 {0x0e, 0x0e, GRAPHICS, 16, 4, 0, 0xa0000, FONT8x8},
71 {0x0f, 0x11, GRAPHICS, 1, 2, 1, 0xa0000, FONT8x14},
72 {0x10, 0x12, GRAPHICS, 16, 2, 1, 0xa0000, FONT8x14},
73 {0x11, 0x1a, GRAPHICS, 2, 1, 3, 0xa0000, FONT8x16},
74 {0x12, 0x1b, GRAPHICS, 16, 1, 3, 0xa0000, FONT8x16},
75 /* {0x13, 0x1c, GRAPHICS, 256, 1, 0, 0xa0000, FONT8x8}, */
78 #define NUMMODES (sizeof(vmodelist) / sizeof(vmode_t))
83 static void init_vga(void);
84 static u_int8_t video_inb(int);
85 static void video_outb(int, u_int8_t);
88 * Local types and variables
91 /* Save Table and assorted variables */
92 struct VideoSaveTable {
93 u_short video_parameter_table[2];
94 u_short parameter_dynamic_save_area[2]; /* Not used */
95 u_short alphanumeric_character_set_override[2]; /* Not used */
96 u_short graphics_character_set_override[2]; /* Not used */
97 u_short secondary_save_table[2]; /* Not used */
101 struct SecondaryVideoSaveTable {
103 u_short display_combination_code_table[2];
104 u_short alphanumeric_character_set_override[2]; /* Not used */
105 u_short user_palette_profile_table[2]; /* Not used */
109 struct VideoSaveTable *vsp;
110 struct SecondaryVideoSaveTable *svsp;
113 * Read and write the VGA port
116 /* Save the selected index register */
117 static u_int8_t crtc_index, atc_index, tsc_index, gdc_index;
118 /* Toggle between index and data on port ATC_WritePort */
119 static u_int8_t set_atc_index = 1;
125 case CRTC_DataPortColor:
126 return VGA_CRTC[crtc_index];
127 case CRTC_IndexPortColor:
130 return VGA_ATC[atc_index];
132 return VGA_TSC[tsc_index];
136 return VGA_GDC[gdc_index];
139 case VGA_InputStatus1Port:
141 VGA_InputStatus1 = (VGA_InputStatus1 + 1) & 15;
142 return VGA_InputStatus1;
149 video_outb(int port, u_int8_t value)
152 #define row (CursRow0)
153 #define col (CursCol0)
158 case CRTC_IndexPortColor:
161 case CRTC_DataPortColor:
162 VGA_CRTC[crtc_index] = value;
163 switch (crtc_index) {
164 case CRTC_CurLocHi: /* Update cursor position in BIOS */
165 cp = row * DpyCols + col;
171 case CRTC_CurLocLo: /* Update cursor position in BIOS */
172 cp = row * DpyCols + col;
179 debug(D_VIDEO, "VGA: outb 0x%04x, 0x%02x at index 0x%02x\n",
180 port, value, crtc_index);
183 case CRTC_IndexPortMono: /* Not used */
185 case CRTC_DataPortMono: /* Not used */
191 VGA_ATC[atc_index] = value;
194 debug(D_VIDEO, "VGA: outb 0x%04x, 0x%02x at index 0x%02x\n",
195 port, value, crtc_index);
199 set_atc_index = 1 - set_atc_index;
205 VGA_TSC[tsc_index] = value;
208 debug(D_VIDEO, "VGA: outb 0x%04x, 0x%02x at index 0x%02x\n",
209 port, value, crtc_index);
217 VGA_GDC[gdc_index] = value;
221 debug(D_VIDEO, "VGA: outb 0x%04x, 0x%02x at index 0x%02x\n",
222 port, value, crtc_index);
229 debug(D_ALWAYS, "VGA: Unknown port 0x%4x\n", port);
241 /* If we are running under X, get a connection to the X server and create
242 an empty window of size (1, 1). It makes a couple of init functions a
247 /* Set VGA emulator to a sane state */
250 /* Initialize mode 3 (text, 80x25, 16 colors) */
254 /* Define all known I/O port handlers */
256 define_input_port_handler(CRTC_IndexPortColor, video_inb);
257 define_input_port_handler(CRTC_DataPortColor, video_inb);
258 define_input_port_handler(ATC_ReadPort, video_inb);
259 define_input_port_handler(TSC_IndexPort, video_inb);
260 define_input_port_handler(TSC_DataPort, video_inb);
261 define_input_port_handler(GDC_IndexPort, video_inb);
262 define_input_port_handler(GDC_DataPort, video_inb);
263 define_input_port_handler(VGA_InputStatus1Port, video_inb);
265 define_output_port_handler(CRTC_IndexPortColor, video_outb);
266 define_output_port_handler(CRTC_DataPortColor, video_outb);
267 define_output_port_handler(ATC_WritePort, video_outb);
268 define_output_port_handler(TSC_IndexPort, video_outb);
269 define_output_port_handler(TSC_DataPort, video_outb);
270 define_output_port_handler(GDC_IndexPort, video_outb);
271 define_output_port_handler(GDC_DataPort, video_outb);
274 redirect0 = isatty(0) == 0 || !xmode ;
275 redirect1 = isatty(1) == 0 || !xmode ;
276 redirect2 = isatty(2) == 0 || !xmode ;
291 * Put the Video Save Table Pointer @ C000:0000
292 * Put the Secondary Video Save Table Pointer @ C000:0020
293 * Put the Display Combination Code table @ C000:0040
294 * Put the Video Parameter table @ C000:1000 - C000:2FFF
296 BIOS_SaveTablePointer = 0xC0000000;
298 vsp = (struct VideoSaveTable *)0xC0000L;
299 memset(vsp, 0, sizeof(struct VideoSaveTable));
300 svsp = (struct SecondaryVideoSaveTable *)0xC0020L;
302 vsp->video_parameter_table[0] = 0x1000;
303 vsp->video_parameter_table[1] = 0xC000;
305 vsp->secondary_save_table[0] = 0x0020;
306 vsp->secondary_save_table[1] = 0xC000;
308 svsp->display_combination_code_table[0] = 0x0040;
309 svsp->display_combination_code_table[1] = 0xC000;
311 p = (u_char *)0xC0040;
312 *p++ = 2; /* Only support 2 combinations currently */
313 *p++ = 1; /* Version # */
314 *p++ = 8; /* We won't use more than type 8 */
315 *p++ = 0; /* Reserved */
316 *p++ = 0; *p++ = 0; /* No Display No Display */
317 *p++ = 0; *p++ = 8; /* No Display VGA Color */
319 memcpy((void *)0xC1000, videoparams, sizeof(videoparams));
320 ivec[0x1d] = 0xC0001000L; /* Video Parameter Table */
322 ivec[0x42] = ivec[0x10]; /* Copy of video interrupt */
324 /* Put the current font at C000:3000; the pixels are copied in
325 'tty.c:load_font()'. */
326 ivec[0x1f] = 0xC0003000L;
327 ivec[0x43] = 0xC0003000L;
329 BIOSDATA[0x8a] = 1; /* Index into DCC table */
331 vec = insert_softint_trampoline();
333 register_callback(vec, int10, "int 10");
336 /* Initialize the VGA emulator
338 XXX This is not nearly finished right now.
345 /* Zero-fill 'dac_rgb' on allocation; the default (EGA) table has only
347 dac_rgb = (struct dac_colors *)calloc(256, sizeof(struct dac_colors));
349 err(1, "Get memory for dac_rgb");
351 /* Copy the default DAC table to a working copy we can trash. */
352 for (i = 0; i < 64; i++)
353 dac_rgb[i] = dac_default64[i]; /* Structure copy */
355 /* Point 'palette[]' to the Attribute Controller space. We will only use
356 the first 16 slots. */
359 /* Get memory for the video RAM and adjust the plane pointers. */
360 vram = calloc(256 * 1024, 1); /* XXX */
362 warn("Could not get video memory; graphics modes not available.");
364 /* XXX There is probably a more efficient memory layout... */
366 vplane1 = vram + 0x10000;
367 vplane2 = vram + 0x20000;
368 vplane3 = vram + 0x30000;
370 VGA_InputStatus1 = 0;
374 * Initialize the requested video mode.
377 /* Indices into the video parameter table. We will use that array to
378 initialize the registers on startup and when the video mode changes. */
383 #define MiscOutput_Ofs 9
389 int idx; /* Index into vmode */
390 int pidx; /* Index into videoparams */
392 debug(D_VIDEO, "VGA: Set video mode to 0x%02x\n", mode);
394 idx = find_vmode(mode & 0x7f);
395 if (idx == -1 || vmodelist[idx].type == NOMODE)
396 err(1, "Mode 0x%02x is not supported", mode);
397 vmode = vmodelist[idx];
398 pidx = vmode.paramindex;
400 /* Preset VGA registers. */
401 memcpy(VGA_CRTC, (u_int8_t *)&videoparams[pidx][CRTC_Ofs],
403 memcpy(VGA_ATC, (u_int8_t *)&videoparams[pidx][ATC_Ofs],
405 /* Warning: the video parameter table does not contain the Sequencer's
406 Reset register. Its default value is 0x03.*/
407 VGA_TSC[TSC_Reset] = 0x03;
408 memcpy(VGA_TSC + 1, (u_int8_t *)&videoparams[pidx][TSC_Ofs],
409 sizeof(VGA_TSC) - 1);
410 memcpy(VGA_GDC, (u_int8_t *)&videoparams[pidx][GDC_Ofs],
412 VGA_MiscOutput = videoparams[pidx][MiscOutput_Ofs];
415 if ((VGA_ATC[ATC_ModeCtrl] & 1) == 1 && vmode.type == TEXT)
416 err(1, "Text mode requested, but ATC switched to graphics mode!");
417 if ((VGA_ATC[ATC_ModeCtrl] & 1) == 0 && vmode.type == GRAPHICS)
418 err(1, "Graphics mode requested, but ATC switched to text mode!");
420 VideoMode = mode & 0x7f;
421 DpyCols = (u_int16_t)videoparams[pidx][0];
422 DpyPageSize = *(u_int16_t *)&videoparams[pidx][3];
440 CursStart = VGA_CRTC[CRTC_CursStart];
441 CursEnd = VGA_CRTC[CRTC_CursEnd];
443 DpyRows = videoparams[pidx][1];
444 CharHeight = videoparams[pidx][2];
446 CRTCPort = vmode.numcolors > 1 ? CRTC_IndexPortColor : CRTC_IndexPortMono;
447 NumColors = vmode.numcolors;
448 NumPages = vmode.numpages;
449 VertResolution = vmode.vrescode;
450 vmem = (u_int16_t *)vmode.vmemaddr;
452 /* Copy VGA related BIOS variables from 'vga_status'. */
453 memcpy(&BIOS_VideoMode, &VideoMode, 33);
454 BIOS_DpyRows = DpyRows;
455 BIOS_CharHeight = CharHeight;
457 /* Load 'pixels[]' from default DAC values. */
461 xfont = vmode.fontname;
464 /* Resize window if necessary. */
467 /* Mmap video memory for the graphics modes. Write access to 0xa0000 -
468 0xaffff will generate a T_PAGEFAULT trap in VM86 mode (aside: why not a
469 SIGSEGV?), which is handled in 'trap.c:sigbus()'. */
470 if (vmode.type == GRAPHICS) {
471 vmem = mmap((void *)0xa0000, 64 * 1024, PROT_NONE,
472 MAP_ANON | MAP_FIXED | MAP_SHARED, -1, 0);
474 fatal("Could not mmap() video memory");
476 /* Create an XImage to display the graphics screen. */
484 /* Initialize video memory with black background, white foreground */
486 for (i = 0; i < DpyPageSize / 2; ++i)
493 /* Find the requested mode in the 'vmodelist' table. This function returns the
494 index into this table; we will also use the index for accessing the
495 'videoparams' array. */
496 int find_vmode(int mode)
500 for (i = 0; i < NUMMODES; i++)
501 if (vmodelist[i].modenumber == mode)
507 /* Handle access to the graphics memory.
509 Simply changing the protection for the memory is not enough, unfortunately.
510 It would only work for the 256 color modes, where a memory byte contains
511 the color value of one pixel. The 16 color modes (and 4 color modes) make
512 use of four bit planes which overlay the first 64K of video memory. The
513 bits are distributed into these bit planes according to the GDC state, so
514 we will have to emulate the CPU instructions (see 'cpu.c:emu_instr()').
516 Handling the 256 color modes will be a bit easier, once we support those at
519 vmem_pageflt(struct sigframe *sf)
521 regcontext_t *REGS = (regcontext_t *)(&sf->sf_uc.uc_mcontext);
523 /* The ATC's Mode Control register tells us whether 4 or 8 color bits are
525 if (VGA_ATC[ATC_ModeCtrl] & (1 << 6)) {
526 /* 256 colors, allow writes; the protection will be set back to
527 PROT_READ at the next display update */
528 mprotect(vmem, 64 * 1024, PROT_READ | PROT_WRITE);
532 /* There's no need to change the protection in the 16 color modes, we will
533 write to 'vram'. Just emulate the next instruction. */
534 return emu_instr(REGS);
537 /* We need to keep track of the latches' contents.*/
538 static u_int8_t latch0, latch1, latch2, latch3;
540 /* Read a byte from the video memory. 'vga_read()' is called from
541 'cpu.c:read_byte()' and will emulate the VGA read modes. */
543 vga_read(u_int32_t addr)
547 /* 'addr' lies between 0xa0000 and 0xaffff. */
548 dst = addr - 0xa0000;
551 latch0 = vplane0[dst];
552 latch1 = vplane1[dst];
553 latch2 = vplane2[dst];
554 latch3 = vplane3[dst];
556 /* Select read mode. */
557 if ((VGA_GDC[GDC_Mode] & 0x80) == 0)
558 /* Read Mode 0; return the byte from the selected bit plane. */
559 return vram[dst + (VGA_GDC[GDC_ReadMapSelect] & 3) * 0x10000];
562 debug(D_ALWAYS, "VGA: Read Mode 1 not implemented\n");
566 /* Write a byte to the video memory. 'vga_write()' is called from
567 'cpu.c:write_word()' and will emulate the VGA write modes. Not all four
568 modes are implemented yet, nor are the addressing modes (odd/even, chain4).
569 (NB: I think the latter will have to be done in 'tty_graphics_update()').
572 vga_write(u_int32_t addr, u_int8_t val)
575 u_int8_t c0, c1, c2, c3;
576 u_int8_t m0, m1, m2, m3;
582 debug(D_VIDEO, "VGA: Write 0x%02x to 0x%x\n", val, addr);
583 debug(D_VIDEO, " GDC: ");
584 for (i = 0; i < sizeof(VGA_GDC); i++)
585 debug(D_VIDEO, "%02x ", VGA_GDC[i]);
586 debug(D_VIDEO, "\n");
587 debug(D_VIDEO, " TSC: ");
588 for (i = 0; i < sizeof(VGA_TSC); i++)
589 debug(D_VIDEO, "%02x ", VGA_TSC[i]);
590 debug(D_VIDEO, "\n");
593 /* 'addr' lies between 0xa0000 and 0xaffff. */
594 dst = addr - 0xa0000;
601 /* Select write mode. */
602 switch (VGA_GDC[GDC_Mode] & 3) {
604 mask = VGA_GDC[GDC_BitMask];
606 if (VGA_GDC[GDC_DataRotate] & 7)
607 debug(D_ALWAYS, "VGA: Data Rotate != 0\n");
609 /* Select function. */
610 switch (VGA_GDC[GDC_DataRotate] & 0x18) {
611 case 0x00: /* replace */
612 m0 = VGA_GDC[GDC_SetReset] & 1 ? mask : 0x00;
613 m1 = VGA_GDC[GDC_SetReset] & 2 ? mask : 0x00;
614 m2 = VGA_GDC[GDC_SetReset] & 4 ? mask : 0x00;
615 m3 = VGA_GDC[GDC_SetReset] & 8 ? mask : 0x00;
617 c0 = VGA_GDC[GDC_EnableSetReset] & 1 ? c0 & ~mask : val & ~mask;
618 c1 = VGA_GDC[GDC_EnableSetReset] & 2 ? c1 & ~mask : val & ~mask;
619 c2 = VGA_GDC[GDC_EnableSetReset] & 4 ? c2 & ~mask : val & ~mask;
620 c3 = VGA_GDC[GDC_EnableSetReset] & 8 ? c3 & ~mask : val & ~mask;
628 m0 = VGA_GDC[GDC_SetReset] & 1 ? 0xff : ~mask;
629 m1 = VGA_GDC[GDC_SetReset] & 2 ? 0xff : ~mask;
630 m2 = VGA_GDC[GDC_SetReset] & 4 ? 0xff : ~mask;
631 m3 = VGA_GDC[GDC_SetReset] & 8 ? 0xff : ~mask;
633 c0 = VGA_GDC[GDC_EnableSetReset] & 1 ? c0 & m0 : val & m0;
634 c1 = VGA_GDC[GDC_EnableSetReset] & 2 ? c1 & m1 : val & m1;
635 c2 = VGA_GDC[GDC_EnableSetReset] & 4 ? c2 & m2 : val & m2;
636 c3 = VGA_GDC[GDC_EnableSetReset] & 8 ? c3 & m3 : val & m3;
639 m0 = VGA_GDC[GDC_SetReset] & 1 ? mask : 0x00;
640 m1 = VGA_GDC[GDC_SetReset] & 2 ? mask : 0x00;
641 m2 = VGA_GDC[GDC_SetReset] & 4 ? mask : 0x00;
642 m3 = VGA_GDC[GDC_SetReset] & 8 ? mask : 0x00;
644 c0 = VGA_GDC[GDC_EnableSetReset] & 1 ? c0 | m0 : val | m0;
645 c1 = VGA_GDC[GDC_EnableSetReset] & 2 ? c1 | m1 : val | m1;
646 c2 = VGA_GDC[GDC_EnableSetReset] & 4 ? c2 | m2 : val | m2;
647 c3 = VGA_GDC[GDC_EnableSetReset] & 8 ? c3 | m3 : val | m3;
650 m0 = VGA_GDC[GDC_SetReset] & 1 ? mask : 0x00;
651 m1 = VGA_GDC[GDC_SetReset] & 2 ? mask : 0x00;
652 m2 = VGA_GDC[GDC_SetReset] & 4 ? mask : 0x00;
653 m3 = VGA_GDC[GDC_SetReset] & 8 ? mask : 0x00;
655 c0 = VGA_GDC[GDC_EnableSetReset] & 1 ? c0 ^ m0 : val ^ m0;
656 c1 = VGA_GDC[GDC_EnableSetReset] & 2 ? c1 ^ m1 : val ^ m1;
657 c2 = VGA_GDC[GDC_EnableSetReset] & 4 ? c2 ^ m2 : val ^ m2;
658 c3 = VGA_GDC[GDC_EnableSetReset] & 8 ? c3 ^ m3 : val ^ m3;
663 /* Just copy the latches' content to the desired destination
667 mask = VGA_GDC[GDC_BitMask];
669 /* select function */
670 switch (VGA_GDC[GDC_DataRotate] & 0x18) {
671 case 0x00: /* replace */
672 m0 = (val & 1 ? 0xff : 0x00) & mask;
673 m1 = (val & 2 ? 0xff : 0x00) & mask;
674 m2 = (val & 4 ? 0xff : 0x00) & mask;
675 m3 = (val & 8 ? 0xff : 0x00) & mask;
688 m0 = (val & 1 ? 0xff : 0x00) | ~mask;
689 m1 = (val & 2 ? 0xff : 0x00) | ~mask;
690 m2 = (val & 4 ? 0xff : 0x00) | ~mask;
691 m3 = (val & 8 ? 0xff : 0x00) | ~mask;
699 m0 = (val & 1 ? 0xff : 0x00) & mask;
700 m1 = (val & 2 ? 0xff : 0x00) & mask;
701 m2 = (val & 4 ? 0xff : 0x00) & mask;
702 m3 = (val & 8 ? 0xff : 0x00) & mask;
710 m0 = (val & 1 ? 0xff : 0x00) & mask;
711 m1 = (val & 2 ? 0xff : 0x00) & mask;
712 m2 = (val & 4 ? 0xff : 0x00) & mask;
713 m3 = (val & 8 ? 0xff : 0x00) & mask;
724 debug(D_ALWAYS, "VGA: Write Mode 3 not implemented\n");
728 /* Write back changed byte, depending on Map Mask register. */
729 if (VGA_TSC[TSC_MapMask] & 1)
731 if (VGA_TSC[TSC_MapMask] & 2)
733 if (VGA_TSC[TSC_MapMask] & 4)
735 if (VGA_TSC[TSC_MapMask] & 8)