]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/doscmd/video.c
This commit was generated by cvs2svn to compensate for changes in r80588,
[FreeBSD/FreeBSD.git] / usr.bin / doscmd / video.c
1 /*
2  * Copyright (c) 2001 The FreeBSD Project, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/types.h>
30 #include <sys/mman.h>
31 #include <err.h>
32 #include <paths.h>
33 #include <unistd.h>
34
35 #include "doscmd.h"
36 #include "AsyncIO.h"
37 #include "tty.h"
38 #include "video.h"
39 #include "vparams.h"
40
41 /*
42  * Global variables
43  */
44
45 /* VGA registers */
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];
50
51 /* VGA status information */
52 u_int8_t vga_status[64];
53
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}, */
76 };
77
78 #define NUMMODES        (sizeof(vmodelist) / sizeof(vmode_t))
79
80 /*
81  * Local functions
82  */
83 static void     init_vga(void);
84 static u_int8_t video_inb(int);
85 static void     video_outb(int, u_int8_t);
86
87 /*
88  * Local types and variables
89  */
90
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 */
98     u_short     mbz[4];
99 };
100
101 struct SecondaryVideoSaveTable {
102     u_short     length;
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 */
106     u_short     mbz[6];
107 };
108
109 struct VideoSaveTable *vsp;
110 struct SecondaryVideoSaveTable *svsp;
111
112 /*
113  * Read and write the VGA port
114  */
115
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;
120
121 static u_int8_t
122 video_inb(int port)
123 {
124     switch(port) {
125     case CRTC_DataPortColor:
126         return VGA_CRTC[crtc_index];
127     case CRTC_IndexPortColor:
128         return crtc_index;
129     case ATC_ReadPort:
130         return VGA_ATC[atc_index];
131     case TSC_DataPort:
132         return VGA_TSC[tsc_index];
133     case TSC_IndexPort:
134         return tsc_index;
135     case GDC_DataPort:
136         return VGA_GDC[gdc_index];
137     case GDC_IndexPort:
138         return gdc_index;
139     case VGA_InputStatus1Port:
140         set_atc_index = 1;
141         return VGA_InputStatus1;
142     default:
143         return 0;
144     }
145 }
146
147 static void
148 video_outb(int port, u_int8_t value)
149 {
150 /* XXX */
151 #define row     (CursRow0)
152 #define col     (CursCol0)
153         
154     int cp;
155         
156     switch (port) {
157     case CRTC_IndexPortColor:
158         crtc_index = value;
159         break;
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;
165             cp &= 0xff;
166             cp |= value << 8;
167             row = cp / DpyCols;
168             col = cp % DpyCols;
169             break;
170         case CRTC_CurLocLo:     /* Update cursor position in BIOS */
171             cp = row * DpyCols + col;
172             cp &= 0xff00;
173             cp |= value;
174             row = cp / DpyCols;
175             col = cp % DpyCols;
176             break;
177         default:
178             debug(D_VIDEO, "outb 0x%04x, 0x%02x at index 0x%02x\n",
179                   port, value, crtc_index);
180             break;
181         }
182     case CRTC_IndexPortMono:    /* Not used */
183         break;
184     case CRTC_DataPortMono:     /* Not used */
185         break;
186     case ATC_WritePort:
187         if (set_atc_index)
188             atc_index = value;
189         else {
190             VGA_ATC[atc_index] = value;
191             switch (atc_index) {
192             default:
193                 debug(D_VIDEO, "outb 0x%04x, 0x%02x at index 0x%02x\n",
194                       port, value, crtc_index);
195                 break;
196             }
197         }
198         set_atc_index = 1 - set_atc_index;
199         break;
200     case TSC_IndexPort:
201         tsc_index = value;
202         break;
203     case TSC_DataPort:
204         VGA_TSC[tsc_index] = value;
205         switch (tsc_index) {
206         default:
207             debug(D_VIDEO, "outb 0x%04x, 0x%02x at index 0x%02x\n",
208                   port, value, crtc_index);
209             break;
210         }
211         break;
212     case GDC_IndexPort:
213         gdc_index = value;
214         break;
215     case GDC_DataPort:
216         VGA_GDC[gdc_index] = value;
217 #if 0
218         switch (gdc_index) {
219         default:
220             debug(D_VIDEO, "outb 0x%04x, 0x%02x at index 0x%02x\n",
221                   port, value, crtc_index);
222
223             break;
224         }
225 #endif
226         break;
227     default:
228         debug(D_ALWAYS, "Unknown port 0x%4x\n", port);
229         break;
230     }
231         
232     return;
233 #undef row
234 #undef col
235 }
236
237 void
238 video_init()
239 {
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
242        lot easier. */
243     if (xmode) {
244         init_window();
245
246         /* Set VGA emulator to a sane state */
247         init_vga();
248         
249         /* Initialize mode 3 (text, 80x25, 16 colors) */
250         init_mode(3);
251     }
252     
253     /* Define all known I/O port handlers */
254     if (!raw_kbd) {
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);
262                 
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);
270     }
271         
272     redirect0 = isatty(0) == 0 || !xmode ;
273     redirect1 = isatty(1) == 0 || !xmode ;
274     redirect2 = isatty(2) == 0 || !xmode ;
275
276     return;
277 }
278
279 void
280 video_bios_init()
281 {
282     u_char *p;
283     u_long vec;
284
285     if (raw_kbd)
286         return;
287
288     /*
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
293      */
294     BIOS_SaveTablePointer = 0xC0000000;
295
296     vsp = (struct VideoSaveTable *)0xC0000L;
297     memset(vsp, 0, sizeof(struct VideoSaveTable));
298     svsp = (struct SecondaryVideoSaveTable *)0xC0020L;
299
300     vsp->video_parameter_table[0] = 0x1000;
301     vsp->video_parameter_table[1] = 0xC000;
302
303     vsp->secondary_save_table[0] = 0x0020;
304     vsp->secondary_save_table[1] = 0xC000;
305
306     svsp->display_combination_code_table[0] = 0x0040;
307     svsp->display_combination_code_table[1] = 0xC000;
308
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 */
316
317     memcpy((void *)0xC1000, videoparams, sizeof(videoparams));
318     ivec[0x1d] = 0xC0001000L;   /* Video Parameter Table */
319
320     ivec[0x42] = ivec[0x10];    /* Copy of video interrupt */
321
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;
326
327     BIOSDATA[0x8a] = 1;        /* Index into DCC table */
328
329     vec = insert_softint_trampoline();
330     ivec[0x10] = vec;
331     register_callback(vec, int10, "int 10");
332 }
333
334 /* Initialize the VGA emulator
335
336    XXX This is not nearly finished right now.
337 */
338 static void
339 init_vga(void)
340 {
341     int i;
342
343     /* Zero-fill 'dac_rgb' on allocation; the default (EGA) table has only
344        64 entries. */
345     dac_rgb = (struct dac_colors *)calloc(256, sizeof(struct dac_colors));
346     if (dac_rgb == NULL)
347         err(1, "Get memory for dac_rgb");
348
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 */
352
353     /* Point 'palette[]' to the Attribute Controller space. We will only use
354        the first 16 slots. */
355     palette = VGA_ATC;
356
357     /* Get memory for the video RAM and adjust the plane pointers. */
358     vram = calloc(256 * 1024, 1);       /* XXX */
359     if (vram == NULL)
360         warn("Could not get video memory; graphics modes not available.");
361
362     /* XXX There is probably a more efficient memory layout... */
363     vplane0 = vram;
364     vplane1 = vram + 0x10000;
365     vplane2 = vram + 0x20000;
366     vplane3 = vram + 0x30000;
367 }
368
369 /*
370  * Initialize the requested video mode.
371  */
372
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. */
375 #define CRTC_Ofs        10
376 #define ATC_Ofs         35
377 #define TSC_Ofs         5
378 #define GDC_Ofs         55
379 #define MiscOutput_Ofs  9
380
381 void
382 init_mode(int mode)
383 {
384     vmode_t vmode;
385     int idx;                    /* Index into vmode */
386     int pidx;                   /* Index into videoparams */
387     
388     debug(D_VIDEO, "Set video mode to 0x%02x\n", mode);
389
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;
395     
396     /* Preset VGA registers. */
397     memcpy(VGA_CRTC, (u_int8_t *)&videoparams[pidx][CRTC_Ofs],
398            sizeof(VGA_CRTC));
399     memcpy(VGA_ATC, (u_int8_t *)&videoparams[pidx][ATC_Ofs],
400            sizeof(VGA_ATC));
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],
407            sizeof(VGA_GDC));
408     VGA_MiscOutput = videoparams[pidx][MiscOutput_Ofs];
409
410     /* Paranoia */
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!");
415     
416     VideoMode = mode & 0x7f;
417     DpyCols = (u_int16_t)videoparams[pidx][0];
418     DpyPageSize = *(u_int16_t *)&videoparams[pidx][3];
419     ActivePageOfs = 0;
420     CursCol0 = 0;
421     CursRow0 = 0;
422     CursCol1 = 0;
423     CursRow1 = 0;
424     CursCol2 = 0;
425     CursRow2 = 0;
426     CursCol3 = 0;
427     CursRow3 = 0;
428     CursCol4 = 0;
429     CursRow4 = 0;
430     CursCol5 = 0;
431     CursRow5 = 0;
432     CursCol6 = 0;
433     CursRow6 = 0;
434     CursCol7 = 0;
435     CursRow7 = 0;
436     CursStart = VGA_CRTC[CRTC_CursStart];
437     CursEnd = VGA_CRTC[CRTC_CursEnd];
438     ActivePage = 0;
439     DpyRows = videoparams[pidx][1];
440     CharHeight = videoparams[pidx][2];
441
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;
447         
448     /* Copy VGA related BIOS variables from 'vga_status'. */
449     memcpy(&BIOS_VideoMode, &VideoMode, 33);
450     BIOS_DpyRows = DpyRows;
451     BIOS_CharHeight = CharHeight;
452
453     _BlockIO();
454     /* Load 'pixels[]' from default DAC values. */
455     update_pixels();
456         
457     /* Update font. */
458     xfont = vmode.fontname;
459     load_font();
460     
461     /* Resize window if necessary. */
462     resize_window();
463     _UnblockIO();
464     
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);
471         if (vmem == NULL)
472             fatal("Could not mmap() video memory");
473
474         /* Create an XImage to display the graphics screen. */
475         get_ximage();
476     } else {
477         int i;
478         
479         get_lines();
480         if (mode & 0x80)
481             return;
482         /* Initialize video memory with black background, white foreground */
483         vattr = 0x0700;
484         for (i = 0; i < DpyPageSize / 2; ++i)
485             vmem[i] = vattr;
486     }
487
488     return;
489 }
490
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)
495 {
496     int i;
497
498     for (i = 0; i < NUMMODES; i++)
499         if (vmodelist[i].modenumber == mode)
500             return i;
501         
502     return -1;
503 }
504
505
506
507 /* Handle access to the graphics memory.
508
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()').
515
516    Handling the 256 color modes will be a bit easier, once we support those at
517    all. */
518 int
519 vmem_pageflt(struct sigframe *sf)
520 {
521     regcontext_t *REGS = (regcontext_t *)(&sf->sf_uc.uc_mcontext);
522
523     /* The ATC's Mode Control register tells us whether 4 or 8 color bits are
524        used */
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);
529         return 0;
530     }
531
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);
535 }
536
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()').
541    */
542 void
543 vga_write(u_int32_t addr, u_int8_t val)
544 {
545     u_int32_t dst;
546     u_int8_t *latch0, *latch1, *latch2, *latch3;
547     u_int8_t c0, c1, c2, c3;
548     u_int8_t m0, m1, m2, m3;
549     u_int8_t mask;
550 #if 0
551     int i;
552     
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");
562 #endif
563     
564     /* 'addr' lies between 0xa0000 and 0xaffff */
565     dst = addr - 0xa0000;
566
567     /* fill latches */
568     latch0 = vplane0 + dst;
569     latch1 = vplane1 + dst;
570     latch2 = vplane2 + dst;
571     latch3 = vplane3 + dst;
572
573     c0 = *latch0;
574     c1 = *latch1;
575     c2 = *latch2;
576     c3 = *latch3;
577     
578     /* select write mode */
579     switch (VGA_GDC[GDC_Mode] & 3) {
580     case 0:
581         /* XXX to do: Enable Set Reset register */
582         
583         mask = VGA_GDC[GDC_BitMask];
584
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;
592
593             c0 &= ~mask;
594             c1 &= ~mask;
595             c2 &= ~mask;
596             c3 &= ~mask;
597     
598             c0 |= m0;
599             c1 |= m1;
600             c2 |= m2;
601             c3 |= m3;
602             break;
603         case 0x08:              /* and */
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;
608
609             c0 &= m0;
610             c1 &= m1;
611             c2 &= m2;
612             c3 &= m3;
613             break;
614         case 0x10:              /* or */
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;
619
620             c0 |= m0;
621             c1 |= m1;
622             c2 |= m2;
623             c3 |= m3;
624             break;
625         case 0x18:              /* xor */
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;
630
631             c0 ^= m0;
632             c1 ^= m1;
633             c2 ^= m2;
634             c3 ^= m3;
635             break;
636         }
637         break;
638     case 1:
639         /* not yet */
640         break;
641     case 2:
642         mask = VGA_GDC[GDC_BitMask];
643
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;
651
652             c0 &= ~mask;
653             c1 &= ~mask;
654             c2 &= ~mask;
655             c3 &= ~mask;
656     
657             c0 |= m0;
658             c1 |= m1;
659             c2 |= m2;
660             c3 |= m3;
661             break;
662         case 0x08:              /* AND */
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;
667
668             c0 &= m0;
669             c1 &= m1;
670             c2 &= m2;
671             c3 &= m3;
672             break;
673         case 0x10:              /* OR */
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;
678
679             c0 |= m0;
680             c1 |= m1;
681             c2 |= m2;
682             c3 |= m3;
683             break;
684         case 0x18:              /* XOR */
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;
689
690             c0 ^= m0;
691             c1 ^= m1;
692             c2 ^= m2;
693             c3 ^= m3;
694             break;
695         }
696         break;
697     case 3:
698         /* not yet */
699         break;
700     }
701
702     /* write back changed byte, depending on Map Mask register */
703     if (VGA_TSC[TSC_MapMask] & 1)
704         *latch0 = c0;
705     if (VGA_TSC[TSC_MapMask] & 2)
706         *latch1 = c1;
707     if (VGA_TSC[TSC_MapMask] & 4)
708         *latch2 = c2;
709     if (VGA_TSC[TSC_MapMask] & 8)
710         *latch3 = c3;
711     
712     return;
713 }