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 return VGA_InputStatus1;
148 video_outb(int port, u_int8_t value)
151 #define row (CursRow0)
152 #define col (CursCol0)
157 case CRTC_IndexPortColor:
160 case CRTC_DataPortColor:
161 VGA_CRTC[crtc_index] = value;
162 switch (crtc_index) {
163 case CRTC_CurLocHi: /* Update cursor position in BIOS */
164 cp = row * DpyCols + col;
170 case CRTC_CurLocLo: /* Update cursor position in BIOS */
171 cp = row * DpyCols + col;
178 debug(D_VIDEO, "outb 0x%04x, 0x%02x at index 0x%02x\n",
179 port, value, crtc_index);
182 case CRTC_IndexPortMono: /* Not used */
184 case CRTC_DataPortMono: /* Not used */
190 VGA_ATC[atc_index] = value;
193 debug(D_VIDEO, "outb 0x%04x, 0x%02x at index 0x%02x\n",
194 port, value, crtc_index);
198 set_atc_index = 1 - set_atc_index;
204 VGA_TSC[tsc_index] = value;
207 debug(D_VIDEO, "outb 0x%04x, 0x%02x at index 0x%02x\n",
208 port, value, crtc_index);
216 VGA_GDC[gdc_index] = value;
220 debug(D_VIDEO, "outb 0x%04x, 0x%02x at index 0x%02x\n",
221 port, value, crtc_index);
228 debug(D_ALWAYS, "Unknown port 0x%4x\n", port);
240 /* If we are running under X, get a connection to the X server and create
241 an empty window of size (1, 1). It makes a couple of init functions a
246 /* Set VGA emulator to a sane state */
249 /* Initialize mode 3 (text, 80x25, 16 colors) */
253 /* Define all known I/O port handlers */
255 define_input_port_handler(CRTC_IndexPortColor, video_inb);
256 define_input_port_handler(CRTC_DataPortColor, video_inb);
257 define_input_port_handler(ATC_ReadPort, video_inb);
258 define_input_port_handler(TSC_IndexPort, video_inb);
259 define_input_port_handler(TSC_DataPort, video_inb);
260 define_input_port_handler(GDC_IndexPort, video_inb);
261 define_input_port_handler(GDC_DataPort, video_inb);
263 define_output_port_handler(CRTC_IndexPortColor, video_outb);
264 define_output_port_handler(CRTC_DataPortColor, video_outb);
265 define_output_port_handler(ATC_WritePort, video_outb);
266 define_output_port_handler(TSC_IndexPort, video_outb);
267 define_output_port_handler(TSC_DataPort, video_outb);
268 define_output_port_handler(GDC_IndexPort, video_outb);
269 define_output_port_handler(GDC_DataPort, video_outb);
272 redirect0 = isatty(0) == 0 || !xmode ;
273 redirect1 = isatty(1) == 0 || !xmode ;
274 redirect2 = isatty(2) == 0 || !xmode ;
289 * Put the Video Save Table Pointer @ C000:0000
290 * Put the Secondary Video Save Table Pointer @ C000:0020
291 * Put the Display Combination Code table @ C000:0040
292 * Put the Video Parameter table @ C000:1000 - C000:2FFF
294 BIOS_SaveTablePointer = 0xC0000000;
296 vsp = (struct VideoSaveTable *)0xC0000L;
297 memset(vsp, 0, sizeof(struct VideoSaveTable));
298 svsp = (struct SecondaryVideoSaveTable *)0xC0020L;
300 vsp->video_parameter_table[0] = 0x1000;
301 vsp->video_parameter_table[1] = 0xC000;
303 vsp->secondary_save_table[0] = 0x0020;
304 vsp->secondary_save_table[1] = 0xC000;
306 svsp->display_combination_code_table[0] = 0x0040;
307 svsp->display_combination_code_table[1] = 0xC000;
309 p = (u_char *)0xC0040;
310 *p++ = 2; /* Only support 2 combinations currently */
311 *p++ = 1; /* Version # */
312 *p++ = 8; /* We won't use more than type 8 */
313 *p++ = 0; /* Reserved */
314 *p++ = 0; *p++ = 0; /* No Display No Display */
315 *p++ = 0; *p++ = 8; /* No Display VGA Color */
317 memcpy((void *)0xC1000, videoparams, sizeof(videoparams));
318 ivec[0x1d] = 0xC0001000L; /* Video Parameter Table */
320 ivec[0x42] = ivec[0x10]; /* Copy of video interrupt */
322 /* Put the current font at C000:3000; the pixels are copied in
323 'tty.c:load_font()'. */
324 ivec[0x1f] = 0xC0003000L;
325 ivec[0x43] = 0xC0003000L;
327 BIOSDATA[0x8a] = 1; /* Index into DCC table */
329 vec = insert_softint_trampoline();
331 register_callback(vec, int10, "int 10");
334 /* Initialize the VGA emulator
336 XXX This is not nearly finished right now.
343 /* Zero-fill 'dac_rgb' on allocation; the default (EGA) table has only
345 dac_rgb = (struct dac_colors *)calloc(256, sizeof(struct dac_colors));
347 err(1, "Get memory for dac_rgb");
349 /* Copy the default DAC table to a working copy we can trash. */
350 for (i = 0; i < 64; i++)
351 dac_rgb[i] = dac_default64[i]; /* Structure copy */
353 /* Point 'palette[]' to the Attribute Controller space. We will only use
354 the first 16 slots. */
357 /* Get memory for the video RAM and adjust the plane pointers. */
358 vram = calloc(256 * 1024, 1); /* XXX */
360 warn("Could not get video memory; graphics modes not available.");
362 /* XXX There is probably a more efficient memory layout... */
364 vplane1 = vram + 0x10000;
365 vplane2 = vram + 0x20000;
366 vplane3 = vram + 0x30000;
370 * Initialize the requested video mode.
373 /* Indices into the video parameter table. We will use that array to
374 initialize the registers on startup and when the video mode changes. */
379 #define MiscOutput_Ofs 9
385 int idx; /* Index into vmode */
386 int pidx; /* Index into videoparams */
388 debug(D_VIDEO, "Set video mode to 0x%02x\n", mode);
390 idx = find_vmode(mode & 0x7f);
391 if (idx == -1 || vmodelist[idx].type == NOMODE)
392 err(1, "Mode 0x%02x is not supported", mode);
393 vmode = vmodelist[idx];
394 pidx = vmode.paramindex;
396 /* Preset VGA registers. */
397 memcpy(VGA_CRTC, (u_int8_t *)&videoparams[pidx][CRTC_Ofs],
399 memcpy(VGA_ATC, (u_int8_t *)&videoparams[pidx][ATC_Ofs],
401 /* Warning: the video parameter table does not contain the Sequencer's
402 Reset register. Its default value is 0x03.*/
403 VGA_TSC[TSC_Reset] = 0x03;
404 memcpy(VGA_TSC + 1, (u_int8_t *)&videoparams[pidx][TSC_Ofs],
405 sizeof(VGA_TSC) - 1);
406 memcpy(VGA_GDC, (u_int8_t *)&videoparams[pidx][GDC_Ofs],
408 VGA_MiscOutput = videoparams[pidx][MiscOutput_Ofs];
411 if ((VGA_ATC[ATC_ModeCtrl] & 1) == 1 && vmode.type == TEXT)
412 err(1, "Text mode requested, but ATC switched to graphics mode!");
413 if ((VGA_ATC[ATC_ModeCtrl] & 1) == 0 && vmode.type == GRAPHICS)
414 err(1, "Graphics mode requested, but ATC switched to text mode!");
416 VideoMode = mode & 0x7f;
417 DpyCols = (u_int16_t)videoparams[pidx][0];
418 DpyPageSize = *(u_int16_t *)&videoparams[pidx][3];
436 CursStart = VGA_CRTC[CRTC_CursStart];
437 CursEnd = VGA_CRTC[CRTC_CursEnd];
439 DpyRows = videoparams[pidx][1];
440 CharHeight = videoparams[pidx][2];
442 CRTCPort = vmode.numcolors > 1 ? CRTC_IndexPortColor : CRTC_IndexPortMono;
443 NumColors = vmode.numcolors;
444 NumPages = vmode.numpages;
445 VertResolution = vmode.vrescode;
446 vmem = (u_int16_t *)vmode.vmemaddr;
448 /* Copy VGA related BIOS variables from 'vga_status'. */
449 memcpy(&BIOS_VideoMode, &VideoMode, 33);
450 BIOS_DpyRows = DpyRows;
451 BIOS_CharHeight = CharHeight;
454 /* Load 'pixels[]' from default DAC values. */
458 xfont = vmode.fontname;
461 /* Resize window if necessary. */
465 /* Mmap video memory for the graphics modes. Write access to 0xa0000 -
466 0xaffff will generate a T_PAGEFAULT trap in VM86 mode (aside: why not a
467 SIGSEGV?), which is handled in 'trap.c:sigbus()'. */
468 if (vmode.type == GRAPHICS) {
469 vmem = mmap((void *)0xa0000, 64 * 1024, PROT_READ,
470 MAP_ANON | MAP_FIXED | MAP_INHERIT | MAP_SHARED, -1, 0);
472 fatal("Could not mmap() video memory");
474 /* Create an XImage to display the graphics screen. */
482 /* Initialize video memory with black background, white foreground */
484 for (i = 0; i < DpyPageSize / 2; ++i)
491 /* Find the requested mode in the 'vmodelist' table. This function returns the
492 index into this table; we will also use the index for accessing the
493 'videoparams' array. */
494 int find_vmode(int mode)
498 for (i = 0; i < NUMMODES; i++)
499 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 /* Write a byte to the video memory. 'vga_write()' is called from
538 'cpu.c:write_word()' and will emulate the VGA write modes. Not all four
539 modes are implemented yet, nor are the addressing modes (odd/even, chain4).
540 (NB: I think the latter will have to be done in 'tty_graphics_update()').
543 vga_write(u_int32_t addr, u_int8_t val)
546 u_int8_t *latch0, *latch1, *latch2, *latch3;
547 u_int8_t c0, c1, c2, c3;
548 u_int8_t m0, m1, m2, m3;
553 debug(D_VIDEO, "Write 0x%02x to 0x%x\n", val, addr);
554 debug(D_VIDEO, "GDC: ");
555 for (i = 0; i < sizeof(VGA_GDC); i++)
556 debug(D_VIDEO, "%02x ", VGA_GDC[i]);
557 debug(D_VIDEO, "\n");
558 debug(D_VIDEO, "TSC: ");
559 for (i = 0; i < sizeof(VGA_TSC); i++)
560 debug(D_VIDEO, "%02x ", VGA_TSC[i]);
561 debug(D_VIDEO, "\n");
564 /* 'addr' lies between 0xa0000 and 0xaffff */
565 dst = addr - 0xa0000;
568 latch0 = vplane0 + dst;
569 latch1 = vplane1 + dst;
570 latch2 = vplane2 + dst;
571 latch3 = vplane3 + dst;
578 /* select write mode */
579 switch (VGA_GDC[GDC_Mode] & 3) {
581 /* XXX to do: Enable Set Reset register */
583 mask = VGA_GDC[GDC_BitMask];
585 /* select function */
586 switch (VGA_GDC[GDC_DataRotate] & 0x18) {
587 case 0x00: /* replace */
588 m0 = VGA_GDC[GDC_SetReset] & 1 ? mask : 0x00;
589 m1 = VGA_GDC[GDC_SetReset] & 2 ? mask : 0x00;
590 m2 = VGA_GDC[GDC_SetReset] & 4 ? mask : 0x00;
591 m3 = VGA_GDC[GDC_SetReset] & 8 ? mask : 0x00;
604 m0 = VGA_GDC[GDC_SetReset] & 1 ? 0xff : ~mask;
605 m1 = VGA_GDC[GDC_SetReset] & 2 ? 0xff : ~mask;
606 m2 = VGA_GDC[GDC_SetReset] & 4 ? 0xff : ~mask;
607 m3 = VGA_GDC[GDC_SetReset] & 8 ? 0xff : ~mask;
615 m0 = VGA_GDC[GDC_SetReset] & 1 ? mask : 0x00;
616 m1 = VGA_GDC[GDC_SetReset] & 2 ? mask : 0x00;
617 m2 = VGA_GDC[GDC_SetReset] & 4 ? mask : 0x00;
618 m3 = VGA_GDC[GDC_SetReset] & 8 ? mask : 0x00;
626 m0 = VGA_GDC[GDC_SetReset] & 1 ? mask : 0x00;
627 m1 = VGA_GDC[GDC_SetReset] & 2 ? mask : 0x00;
628 m2 = VGA_GDC[GDC_SetReset] & 4 ? mask : 0x00;
629 m3 = VGA_GDC[GDC_SetReset] & 8 ? mask : 0x00;
642 mask = VGA_GDC[GDC_BitMask];
644 /* select function */
645 switch (VGA_GDC[GDC_DataRotate] & 0x18) {
646 case 0x00: /* replace */
647 m0 = (val & 1 ? 0xff : 0x00) & mask;
648 m1 = (val & 2 ? 0xff : 0x00) & mask;
649 m2 = (val & 4 ? 0xff : 0x00) & mask;
650 m3 = (val & 8 ? 0xff : 0x00) & mask;
663 m0 = (val & 1 ? 0xff : 0x00) | ~mask;
664 m1 = (val & 2 ? 0xff : 0x00) | ~mask;
665 m2 = (val & 4 ? 0xff : 0x00) | ~mask;
666 m3 = (val & 8 ? 0xff : 0x00) | ~mask;
674 m0 = (val & 1 ? 0xff : 0x00) & mask;
675 m1 = (val & 2 ? 0xff : 0x00) & mask;
676 m2 = (val & 4 ? 0xff : 0x00) & mask;
677 m3 = (val & 8 ? 0xff : 0x00) & mask;
685 m0 = (val & 1 ? 0xff : 0x00) & mask;
686 m1 = (val & 2 ? 0xff : 0x00) & mask;
687 m2 = (val & 4 ? 0xff : 0x00) & mask;
688 m3 = (val & 8 ? 0xff : 0x00) & mask;
702 /* write back changed byte, depending on Map Mask register */
703 if (VGA_TSC[TSC_MapMask] & 1)
705 if (VGA_TSC[TSC_MapMask] & 2)
707 if (VGA_TSC[TSC_MapMask] & 4)
709 if (VGA_TSC[TSC_MapMask] & 8)