[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / hardware / video / svga.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/hardware/video/svga.c
5 * PURPOSE: SuperVGA hardware emulation (Cirrus Logic CL-GD5434 compatible)
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "ntvdm.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 #include "emulator.h"
17 #include "svga.h"
18 #include <bios/vidbios.h>
19
20 #include "memory.h"
21 #include "io.h"
22 #include "clock.h"
23
24 #include "../../console/video.h"
25
26 /* PRIVATE VARIABLES **********************************************************/
27
28 static CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
29 static CONST DWORD MemorySize[] = { 0x20000, 0x10000, 0x08000, 0x08000 };
30
31 #define USE_REACTOS_COLORS
32 // #define USE_DOSBOX_COLORS
33
34 #if defined(USE_REACTOS_COLORS)
35
36 // ReactOS colors
37 static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] =
38 {
39 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA),
40 RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA),
41 RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF),
42 RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF),
43 RGB(0x00, 0x00, 0x00), RGB(0x10, 0x10, 0x10), RGB(0x20, 0x20, 0x20), RGB(0x35, 0x35, 0x35),
44 RGB(0x45, 0x45, 0x45), RGB(0x55, 0x55, 0x55), RGB(0x65, 0x65, 0x65), RGB(0x75, 0x75, 0x75),
45 RGB(0x8A, 0x8A, 0x8A), RGB(0x9A, 0x9A, 0x9A), RGB(0xAA, 0xAA, 0xAA), RGB(0xBA, 0xBA, 0xBA),
46 RGB(0xCA, 0xCA, 0xCA), RGB(0xDF, 0xDF, 0xDF), RGB(0xEF, 0xEF, 0xEF), RGB(0xFF, 0xFF, 0xFF),
47 RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x82, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF),
48 RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x82), RGB(0xFF, 0x00, 0x41),
49 RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x82, 0x00), RGB(0xFF, 0xBE, 0x00),
50 RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x82, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00),
51 RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x82), RGB(0x00, 0xFF, 0xBE),
52 RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x82, 0xFF), RGB(0x00, 0x41, 0xFF),
53 RGB(0x82, 0x82, 0xFF), RGB(0x9E, 0x82, 0xFF), RGB(0xBE, 0x82, 0xFF), RGB(0xDF, 0x82, 0xFF),
54 RGB(0xFF, 0x82, 0xFF), RGB(0xFF, 0x82, 0xDF), RGB(0xFF, 0x82, 0xBE), RGB(0xFF, 0x82, 0x9E),
55 RGB(0xFF, 0x82, 0x82), RGB(0xFF, 0x9E, 0x82), RGB(0xFF, 0xBE, 0x82), RGB(0xFF, 0xDF, 0x82),
56 RGB(0xFF, 0xFF, 0x82), RGB(0xDF, 0xFF, 0x82), RGB(0xBE, 0xFF, 0x82), RGB(0x9E, 0xFF, 0x82),
57 RGB(0x82, 0xFF, 0x82), RGB(0x82, 0xFF, 0x9E), RGB(0x82, 0xFF, 0xBE), RGB(0x82, 0xFF, 0xDF),
58 RGB(0x82, 0xFF, 0xFF), RGB(0x82, 0xDF, 0xFF), RGB(0x82, 0xBE, 0xFF), RGB(0x82, 0x9E, 0xFF),
59 RGB(0xBA, 0xBA, 0xFF), RGB(0xCA, 0xBA, 0xFF), RGB(0xDF, 0xBA, 0xFF), RGB(0xEF, 0xBA, 0xFF),
60 RGB(0xFF, 0xBA, 0xFF), RGB(0xFF, 0xBA, 0xEF), RGB(0xFF, 0xBA, 0xDF), RGB(0xFF, 0xBA, 0xCA),
61 RGB(0xFF, 0xBA, 0xBA), RGB(0xFF, 0xCA, 0xBA), RGB(0xFF, 0xDF, 0xBA), RGB(0xFF, 0xEF, 0xBA),
62 RGB(0xFF, 0xFF, 0xBA), RGB(0xEF, 0xFF, 0xBA), RGB(0xDF, 0xFF, 0xBA), RGB(0xCA, 0xFF, 0xBA),
63 RGB(0xBA, 0xFF, 0xBA), RGB(0xBA, 0xFF, 0xCA), RGB(0xBA, 0xFF, 0xDF), RGB(0xBA, 0xFF, 0xEF),
64 RGB(0xBA, 0xFF, 0xFF), RGB(0xBA, 0xEF, 0xFF), RGB(0xBA, 0xDF, 0xFF), RGB(0xBA, 0xCA, 0xFF),
65 RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x39, 0x00, 0x71), RGB(0x55, 0x00, 0x71),
66 RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x39), RGB(0x71, 0x00, 0x1C),
67 RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x39, 0x00), RGB(0x71, 0x55, 0x00),
68 RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x39, 0x71, 0x00), RGB(0x1C, 0x71, 0x00),
69 RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x39), RGB(0x00, 0x71, 0x55),
70 RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x39, 0x71), RGB(0x00, 0x1C, 0x71),
71 RGB(0x39, 0x39, 0x71), RGB(0x45, 0x39, 0x71), RGB(0x55, 0x39, 0x71), RGB(0x61, 0x39, 0x71),
72 RGB(0x71, 0x39, 0x71), RGB(0x71, 0x39, 0x61), RGB(0x71, 0x39, 0x55), RGB(0x71, 0x39, 0x45),
73 RGB(0x71, 0x39, 0x39), RGB(0x71, 0x45, 0x39), RGB(0x71, 0x55, 0x39), RGB(0x71, 0x61, 0x39),
74 RGB(0x71, 0x71, 0x39), RGB(0x61, 0x71, 0x39), RGB(0x55, 0x71, 0x39), RGB(0x45, 0x71, 0x39),
75 RGB(0x39, 0x71, 0x39), RGB(0x39, 0x71, 0x45), RGB(0x39, 0x71, 0x55), RGB(0x39, 0x71, 0x61),
76 RGB(0x39, 0x71, 0x71), RGB(0x39, 0x61, 0x71), RGB(0x39, 0x55, 0x71), RGB(0x39, 0x45, 0x71),
77 RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71),
78 RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59),
79 RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51),
80 RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51),
81 RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69),
82 RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71),
83 RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x31, 0x00, 0x41),
84 RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x31), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10),
85 RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x31, 0x00),
86 RGB(0x41, 0x41, 0x00), RGB(0x31, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00),
87 RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x31),
88 RGB(0x00, 0x41, 0x41), RGB(0x00, 0x31, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41),
89 RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x31, 0x20, 0x41), RGB(0x39, 0x20, 0x41),
90 RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x39), RGB(0x41, 0x20, 0x31), RGB(0x41, 0x20, 0x28),
91 RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x31, 0x20), RGB(0x41, 0x39, 0x20),
92 RGB(0x41, 0x41, 0x20), RGB(0x39, 0x41, 0x20), RGB(0x31, 0x41, 0x20), RGB(0x28, 0x41, 0x20),
93 RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x31), RGB(0x20, 0x41, 0x39),
94 RGB(0x20, 0x41, 0x41), RGB(0x20, 0x39, 0x41), RGB(0x20, 0x31, 0x41), RGB(0x20, 0x28, 0x41),
95 RGB(0x2D, 0x2D, 0x41), RGB(0x31, 0x2D, 0x41), RGB(0x35, 0x2D, 0x41), RGB(0x3D, 0x2D, 0x41),
96 RGB(0x41, 0x2D, 0x41), RGB(0x41, 0x2D, 0x3D), RGB(0x41, 0x2D, 0x35), RGB(0x41, 0x2D, 0x31),
97 RGB(0x41, 0x2D, 0x2D), RGB(0x41, 0x31, 0x2D), RGB(0x41, 0x35, 0x2D), RGB(0x41, 0x3D, 0x2D),
98 RGB(0x41, 0x41, 0x2D), RGB(0x3D, 0x41, 0x2D), RGB(0x35, 0x41, 0x2D), RGB(0x31, 0x41, 0x2D),
99 RGB(0x2D, 0x41, 0x2D), RGB(0x2D, 0x41, 0x31), RGB(0x2D, 0x41, 0x35), RGB(0x2D, 0x41, 0x3D),
100 RGB(0x2D, 0x41, 0x41), RGB(0x2D, 0x3D, 0x41), RGB(0x2D, 0x35, 0x41), RGB(0x2D, 0x31, 0x41),
101 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00),
102 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00)
103 };
104
105 #elif defined(USE_DOSBOX_COLORS)
106
107 // DOSBox colors
108 static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] =
109 {
110 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA),
111 RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA),
112 RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF),
113 RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF),
114 RGB(0x00, 0x00, 0x00), RGB(0x14, 0x14, 0x14), RGB(0x20, 0x20, 0x20), RGB(0x2C, 0x2C, 0x2C),
115 RGB(0x38, 0x38, 0x38), RGB(0x45, 0x45, 0x45), RGB(0x51, 0x51, 0x51), RGB(0x61, 0x61, 0x61),
116 RGB(0x71, 0x71, 0x71), RGB(0x82, 0x82, 0x82), RGB(0x92, 0x92, 0x92), RGB(0xA2, 0xA2, 0xA2),
117 RGB(0xB6, 0xB6, 0xB6), RGB(0xCB, 0xCB, 0xCB), RGB(0xE3, 0xE3, 0xE3), RGB(0xFF, 0xFF, 0xFF),
118 RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x7D, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF),
119 RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x7D), RGB(0xFF, 0x00, 0x41),
120 RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x7D, 0x00), RGB(0xFF, 0xBE, 0x00),
121 RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x7D, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00),
122 RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x7D), RGB(0x00, 0xFF, 0xBE),
123 RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x7D, 0xFF), RGB(0x00, 0x41, 0xFF),
124 RGB(0x7D, 0x7D, 0xFF), RGB(0x9E, 0x7D, 0xFF), RGB(0xBE, 0x7D, 0xFF), RGB(0xDF, 0x7D, 0xFF),
125 RGB(0xFF, 0x7D, 0xFF), RGB(0xFF, 0x7D, 0xDF), RGB(0xFF, 0x7D, 0xBE), RGB(0xFF, 0x7D, 0x9E),
126
127 RGB(0xFF, 0x7D, 0x7D), RGB(0xFF, 0x9E, 0x7D), RGB(0xFF, 0xBE, 0x7D), RGB(0xFF, 0xDF, 0x7D),
128 RGB(0xFF, 0xFF, 0x7D), RGB(0xDF, 0xFF, 0x7D), RGB(0xBE, 0xFF, 0x7D), RGB(0x9E, 0xFF, 0x7D),
129 RGB(0x7D, 0xFF, 0x7D), RGB(0x7D, 0xFF, 0x9E), RGB(0x7D, 0xFF, 0xBE), RGB(0x7D, 0xFF, 0xDF),
130 RGB(0x7D, 0xFF, 0xFF), RGB(0x7D, 0xDF, 0xFF), RGB(0x7D, 0xBE, 0xFF), RGB(0x7D, 0x9E, 0xFF),
131 RGB(0xB6, 0xB6, 0xFF), RGB(0xC7, 0xB6, 0xFF), RGB(0xDB, 0xB6, 0xFF), RGB(0xEB, 0xB6, 0xFF),
132 RGB(0xFF, 0xB6, 0xFF), RGB(0xFF, 0xB6, 0xEB), RGB(0xFF, 0xB6, 0xDB), RGB(0xFF, 0xB6, 0xC7),
133 RGB(0xFF, 0xB6, 0xB6), RGB(0xFF, 0xC7, 0xB6), RGB(0xFF, 0xDB, 0xB6), RGB(0xFF, 0xEB, 0xB6),
134 RGB(0xFF, 0xFF, 0xB6), RGB(0xEB, 0xFF, 0xB6), RGB(0xDB, 0xFF, 0xB6), RGB(0xC7, 0xFF, 0xB6),
135 RGB(0xB6, 0xFF, 0xB6), RGB(0xB6, 0xFF, 0xC7), RGB(0xB6, 0xFF, 0xDB), RGB(0xB6, 0xFF, 0xEB),
136 RGB(0xB6, 0xFF, 0xFF), RGB(0xB6, 0xEB, 0xFF), RGB(0xB6, 0xDB, 0xFF), RGB(0xB6, 0xC7, 0xFF),
137 RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x38, 0x00, 0x71), RGB(0x55, 0x00, 0x71),
138 RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x38), RGB(0x71, 0x00, 0x1C),
139 RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x38, 0x00), RGB(0x71, 0x55, 0x00),
140 RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x38, 0x71, 0x00), RGB(0x1C, 0x71, 0x00),
141 RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x38), RGB(0x00, 0x71, 0x55),
142 RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x38, 0x71), RGB(0x00, 0x1C, 0x71),
143
144 RGB(0x38, 0x38, 0x71), RGB(0x45, 0x38, 0x71), RGB(0x55, 0x38, 0x71), RGB(0x61, 0x38, 0x71),
145 RGB(0x71, 0x38, 0x71), RGB(0x71, 0x38, 0x61), RGB(0x71, 0x38, 0x55), RGB(0x71, 0x38, 0x45),
146 RGB(0x71, 0x38, 0x38), RGB(0x71, 0x45, 0x38), RGB(0x71, 0x55, 0x38), RGB(0x71, 0x61, 0x38),
147 RGB(0x71, 0x71, 0x38), RGB(0x61, 0x71, 0x38), RGB(0x55, 0x71, 0x38), RGB(0x45, 0x71, 0x38),
148 RGB(0x38, 0x71, 0x38), RGB(0x38, 0x71, 0x45), RGB(0x38, 0x71, 0x55), RGB(0x38, 0x71, 0x61),
149 RGB(0x38, 0x71, 0x71), RGB(0x38, 0x61, 0x71), RGB(0x38, 0x55, 0x71), RGB(0x38, 0x45, 0x71),
150 RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71),
151 RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59),
152 RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51),
153 RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51),
154 RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69),
155 RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71),
156 RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x30, 0x00, 0x41),
157 RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x30), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10),
158 RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x30, 0x00),
159 RGB(0x41, 0x41, 0x00), RGB(0x30, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00),
160
161 RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x30),
162 RGB(0x00, 0x41, 0x41), RGB(0x00, 0x30, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41),
163 RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x30, 0x20, 0x41), RGB(0x38, 0x20, 0x41),
164 RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x38), RGB(0x41, 0x20, 0x30), RGB(0x41, 0x20, 0x28),
165 RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x30, 0x20), RGB(0x41, 0x38, 0x20),
166 RGB(0x41, 0x41, 0x20), RGB(0x38, 0x41, 0x20), RGB(0x30, 0x41, 0x20), RGB(0x28, 0x41, 0x20),
167 RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x30), RGB(0x20, 0x41, 0x38),
168 RGB(0x20, 0x41, 0x41), RGB(0x20, 0x38, 0x41), RGB(0x20, 0x30, 0x41), RGB(0x20, 0x28, 0x41),
169 RGB(0x2C, 0x2C, 0x41), RGB(0x30, 0x2C, 0x41), RGB(0x34, 0x2C, 0x41), RGB(0x3C, 0x2C, 0x41),
170 RGB(0x41, 0x2C, 0x41), RGB(0x41, 0x2C, 0x3C), RGB(0x41, 0x2C, 0x34), RGB(0x41, 0x2C, 0x30),
171 RGB(0x41, 0x2C, 0x2C), RGB(0x41, 0x30, 0x2C), RGB(0x41, 0x34, 0x2C), RGB(0x41, 0x3C, 0x2C),
172 RGB(0x41, 0x41, 0x2C), RGB(0x3C, 0x41, 0x2C), RGB(0x34, 0x41, 0x2C), RGB(0x30, 0x41, 0x2C),
173 RGB(0x2C, 0x41, 0x2C), RGB(0x2C, 0x41, 0x30), RGB(0x2C, 0x41, 0x34), RGB(0x2C, 0x41, 0x3C),
174 RGB(0x2C, 0x41, 0x41), RGB(0x2C, 0x3C, 0x41), RGB(0x2C, 0x34, 0x41), RGB(0x2C, 0x30, 0x41),
175 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00),
176 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00)
177 };
178
179 #endif
180
181 /*
182 * Default 16-color palette for foreground and background
183 * (corresponding flags in comments).
184 * Taken from subsystems/win32/winsrv/consrv/frontends/gui/conwnd.c
185 */
186 static const COLORREF ConsoleColors[16] =
187 {
188 RGB(0, 0, 0), // (Black)
189 RGB(0, 0, 128), // BLUE
190 RGB(0, 128, 0), // GREEN
191 RGB(0, 128, 128), // BLUE | GREEN
192 RGB(128, 0, 0), // RED
193 RGB(128, 0, 128), // BLUE | RED
194 RGB(128, 128, 0), // GREEN | RED
195 RGB(192, 192, 192), // BLUE | GREEN | RED
196
197 RGB(128, 128, 128), // (Grey) INTENSITY
198 RGB(0, 0, 255), // BLUE | INTENSITY
199 RGB(0, 255, 0), // GREEN | INTENSITY
200 RGB(0, 255, 255), // BLUE | GREEN | INTENSITY
201 RGB(255, 0, 0), // RED | INTENSITY
202 RGB(255, 0, 255), // BLUE | RED | INTENSITY
203 RGB(255, 255, 0), // GREEN | RED | INTENSITY
204 RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY
205 };
206
207 /// ConsoleFramebuffer
208 static PVOID ActiveFramebuffer = NULL; // Active framebuffer, points to
209 // either TextFramebuffer or a
210 // valid graphics framebuffer.
211 static HPALETTE TextPaletteHandle = NULL;
212 static HPALETTE PaletteHandle = NULL;
213
214 /*
215 * Text mode -- we always keep a valid text mode framebuffer
216 * even if we are in graphics mode. This is needed in order
217 * to keep a consistent VGA state. However, each time the VGA
218 * detaches from the console (and reattaches to it later on),
219 * this text mode framebuffer is recreated.
220 */
221 static PCHAR_CELL TextFramebuffer = NULL;
222
223 /*
224 * Graphics mode
225 */
226 static PBYTE GraphicsFramebuffer = NULL;
227
228
229 // static HANDLE ConsoleMutex = NULL;
230 // /* DoubleVision support */
231 // static BOOLEAN DoubleWidth = FALSE;
232 // static BOOLEAN DoubleHeight = FALSE;
233
234
235
236
237
238 /*
239 * VGA Hardware
240 */
241 static BYTE VgaMemory[VGA_NUM_BANKS * SVGA_BANK_SIZE];
242
243 static BYTE VgaLatchRegisters[VGA_NUM_BANKS] = {0, 0, 0, 0};
244
245 static BYTE VgaMiscRegister;
246 static BYTE VgaFeatureRegister;
247
248 static BYTE VgaSeqIndex = VGA_SEQ_RESET_REG;
249 static BYTE VgaSeqRegisters[SVGA_SEQ_MAX_REG];
250
251 static BYTE VgaCrtcIndex = VGA_CRTC_HORZ_TOTAL_REG;
252 static BYTE VgaCrtcRegisters[SVGA_CRTC_MAX_REG];
253
254 static BYTE VgaGcIndex = VGA_GC_RESET_REG;
255 static BYTE VgaGcRegisters[SVGA_GC_MAX_REG];
256
257 static BOOLEAN VgaAcLatch = FALSE;
258 static BOOLEAN VgaAcPalDisable = TRUE;
259 static BYTE VgaAcIndex = VGA_AC_PAL_0_REG;
260 static BYTE VgaAcRegisters[VGA_AC_MAX_REG];
261
262 static BYTE VgaDacMask = 0xFF;
263 static BYTE VgaDacLatchCounter = 0;
264 static BYTE VgaDacLatch[3];
265
266 static BOOLEAN VgaDacReadWrite = FALSE;
267 static WORD VgaDacIndex = 0;
268 static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
269
270 // static VGA_REGISTERS VgaRegisters;
271
272 static ULONGLONG VerticalRetraceCycle = 0ULL;
273 static ULONGLONG HorizontalRetraceCycle = 0ULL;
274 static PHARDWARE_TIMER VSyncTimer;
275 static PHARDWARE_TIMER HSyncTimer;
276
277 static BOOLEAN NeedsUpdate = FALSE;
278 static BOOLEAN ModeChanged = FALSE;
279 static BOOLEAN CursorChanged = FALSE;
280 static BOOLEAN PaletteChanged = FALSE;
281
282 static UINT SvgaHdrCounter = 0;
283 static BYTE SvgaHiddenRegister = 0;
284
285 typedef enum _SCREEN_MODE
286 {
287 TEXT_MODE,
288 GRAPHICS_MODE
289 } SCREEN_MODE, *PSCREEN_MODE;
290
291 static SCREEN_MODE ScreenMode = TEXT_MODE;
292 static COORD CurrResolution = {0};
293
294 static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
295
296
297
298
299 /** HACK!! **/
300 #include "../../console/video.c"
301 /** HACK!! **/
302
303
304
305
306
307 /* PRIVATE FUNCTIONS **********************************************************/
308
309 static inline DWORD VgaGetVideoBaseAddress(VOID)
310 {
311 return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
312 }
313
314 static inline DWORD VgaGetAddressSize(VOID)
315 {
316 if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
317 {
318 /* Double-word addressing */
319 return 4; // sizeof(DWORD)
320 }
321 else if (VgaCrtcRegisters[VGA_CRTC_MODE_CONTROL_REG] & VGA_CRTC_MODE_CONTROL_BYTE)
322 {
323 /* Byte addressing */
324 return 1; // sizeof(BYTE)
325 }
326 else
327 {
328 /* Word addressing */
329 return 2; // sizeof(WORD)
330 }
331 }
332
333 static inline DWORD VgaTranslateReadAddress(DWORD Address)
334 {
335 DWORD Offset = LOWORD(Address - VgaGetVideoBaseAddress());
336 BYTE Plane;
337
338 /* Check for chain-4 and odd-even mode */
339 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
340 {
341 /* The lowest two bits are the plane number */
342 Plane = Offset & 0x03;
343 Offset &= ~3;
344 }
345 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
346 {
347 /* The LSB is the plane number */
348 Plane = Offset & 0x01;
349 Offset &= ~1;
350 }
351 else
352 {
353 /* Use the read mode */
354 Plane = VgaGcRegisters[VGA_GC_READ_MAP_SEL_REG] & 0x03;
355 }
356
357 /* Return the offset on plane 0 for read mode 1 */
358 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_READ) return Offset;
359 else return Offset + Plane * VGA_BANK_SIZE;
360 }
361
362 static inline DWORD VgaTranslateWriteAddress(DWORD Address)
363 {
364 DWORD Offset = LOWORD(Address - VgaGetVideoBaseAddress());
365
366 /* Check for chain-4 and odd-even mode */
367 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
368 {
369 /* Clear the lowest two bits since they're used to select the bank */
370 Offset &= ~3;
371 }
372 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
373 {
374 /* Clear the lowest bit since it's used to select odd/even */
375 Offset &= ~1;
376 }
377
378 /* Return the offset on plane 0 */
379 return Offset;
380 }
381
382 static inline BYTE VgaTranslateByteForWriting(BYTE Data, BYTE Plane)
383 {
384 BYTE WriteMode = VgaGcRegisters[VGA_GC_MODE_REG] & 0x03;
385 BYTE BitMask = VgaGcRegisters[VGA_GC_BITMASK_REG];
386
387 if (WriteMode == 1)
388 {
389 /* In write mode 1 just return the latch register */
390 return VgaLatchRegisters[Plane];
391 }
392
393 if (WriteMode != 2)
394 {
395 /* Write modes 0 and 3 rotate the data to the right first */
396 BYTE RotateCount = VgaGcRegisters[VGA_GC_ROTATE_REG] & 0x07;
397 Data = LOBYTE(((DWORD)Data >> RotateCount) | ((DWORD)Data << (8 - RotateCount)));
398 }
399 else
400 {
401 /* Write mode 2 expands the appropriate bit to all 8 bits */
402 Data = (Data & (1 << Plane)) ? 0xFF : 0x00;
403 }
404
405 if (WriteMode == 0)
406 {
407 /*
408 * In write mode 0, the enable set/reset register decides if the
409 * set/reset bit should be expanded to all 8 bits.
410 */
411 if (VgaGcRegisters[VGA_GC_ENABLE_RESET_REG] & (1 << Plane))
412 {
413 /* Copy the bit from the set/reset register to all 8 bits */
414 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
415 }
416 }
417
418 if (WriteMode != 3)
419 {
420 /* Write modes 0 and 2 then perform a logical operation on the data and latch */
421 BYTE LogicalOperation = (VgaGcRegisters[VGA_GC_ROTATE_REG] >> 3) & 0x03;
422
423 if (LogicalOperation == 1) Data &= VgaLatchRegisters[Plane];
424 else if (LogicalOperation == 2) Data |= VgaLatchRegisters[Plane];
425 else if (LogicalOperation == 3) Data ^= VgaLatchRegisters[Plane];
426 }
427 else
428 {
429 /* For write mode 3, we AND the bitmask with the data, which is used as the new bitmask */
430 BitMask &= Data;
431
432 /* Then we expand the bit in the set/reset field */
433 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
434 }
435
436 /* Bits cleared in the bitmask are replaced with latch register bits */
437 Data = (Data & BitMask) | (VgaLatchRegisters[Plane] & (~BitMask));
438
439 /* Return the byte */
440 return Data;
441 }
442
443 static inline ULONG VgaGetClockFrequency(VOID)
444 {
445 BYTE Numerator, Denominator;
446
447 if (VgaSeqRegisters[SVGA_SEQ_MCLK_REG] & SVGA_SEQ_MCLK_VCLK)
448 {
449 /* The VCLK is being generated using the MCLK */
450 ULONG Clock = (VGA_CLOCK_BASE * (VgaSeqRegisters[SVGA_SEQ_MCLK_REG] & 0x3F)) >> 3;
451
452 if (VgaSeqRegisters[SVGA_SEQ_VCLK3_DENOMINATOR_REG] & 1)
453 {
454 /* Use only half of the MCLK as the VCLK */
455 Clock >>= 1;
456 }
457
458 return Clock;
459 }
460
461 switch ((VgaMiscRegister >> 2) & 3)
462 {
463 case 0:
464 {
465 Numerator = VgaSeqRegisters[SVGA_SEQ_VCLK0_NUMERATOR_REG];
466 Denominator = VgaSeqRegisters[SVGA_SEQ_VCLK0_DENOMINATOR_REG];
467 break;
468 }
469
470 case 1:
471 {
472 Numerator = VgaSeqRegisters[SVGA_SEQ_VCLK1_NUMERATOR_REG];
473 Denominator = VgaSeqRegisters[SVGA_SEQ_VCLK1_DENOMINATOR_REG];
474 break;
475 }
476
477 case 2:
478 {
479 Numerator = VgaSeqRegisters[SVGA_SEQ_VCLK2_NUMERATOR_REG];
480 Denominator = VgaSeqRegisters[SVGA_SEQ_VCLK2_DENOMINATOR_REG];
481 break;
482 }
483
484 case 3:
485 {
486 Numerator = VgaSeqRegisters[SVGA_SEQ_VCLK3_NUMERATOR_REG];
487 Denominator = VgaSeqRegisters[SVGA_SEQ_VCLK3_DENOMINATOR_REG];
488 break;
489 }
490 }
491
492 /* The numerator is 7-bit */
493 Numerator &= ~(1 << 7);
494
495 /* If bit 7 is clear, the denominator is 5-bit */
496 if (!(Denominator & (1 << 7))) Denominator &= ~(1 << 6);
497
498 /* Bit 0 of the denominator is the post-scalar bit */
499 if (Denominator & 1) Denominator &= ~1;
500 else Denominator >>= 1;
501
502 /* Return the clock frequency in Hz */
503 return (VGA_CLOCK_BASE * Numerator) / Denominator;
504 }
505
506 static VOID VgaResetSequencer(VOID)
507 {
508 /* Lock extended SVGA registers */
509 VgaSeqRegisters[SVGA_SEQ_UNLOCK_REG] = SVGA_SEQ_LOCKED;
510
511 /* Initialize the VCLKs */
512 VgaSeqRegisters[SVGA_SEQ_VCLK0_NUMERATOR_REG] = 0x66;
513 VgaSeqRegisters[SVGA_SEQ_VCLK0_DENOMINATOR_REG] = 0x3B;
514 VgaSeqRegisters[SVGA_SEQ_VCLK1_NUMERATOR_REG] = 0x5B;
515 VgaSeqRegisters[SVGA_SEQ_VCLK1_DENOMINATOR_REG] = 0x2F;
516 VgaSeqRegisters[SVGA_SEQ_VCLK2_NUMERATOR_REG] = 0x45;
517 VgaSeqRegisters[SVGA_SEQ_VCLK2_DENOMINATOR_REG] = 0x30;
518 VgaSeqRegisters[SVGA_SEQ_VCLK3_NUMERATOR_REG] = 0x7E;
519 VgaSeqRegisters[SVGA_SEQ_VCLK3_DENOMINATOR_REG] = 0x33;
520
521 /* 50 MHz MCLK, not being used as the VCLK */
522 VgaSeqRegisters[SVGA_SEQ_MCLK_REG] = 0x1C;
523 }
524
525 static VOID VgaRestoreDefaultPalette(PPALETTEENTRY Entries, USHORT NumOfEntries)
526 {
527 USHORT i;
528
529 /* Copy the colors of the default palette to the DAC and console palette */
530 for (i = 0; i < NumOfEntries; i++)
531 {
532 /* Set the palette entries */
533 Entries[i].peRed = GetRValue(VgaDefaultPalette[i]);
534 Entries[i].peGreen = GetGValue(VgaDefaultPalette[i]);
535 Entries[i].peBlue = GetBValue(VgaDefaultPalette[i]);
536 Entries[i].peFlags = 0;
537
538 /* Set the DAC registers */
539 VgaDacRegisters[i * 3] = VGA_COLOR_TO_DAC(GetRValue(VgaDefaultPalette[i]));
540 VgaDacRegisters[i * 3 + 1] = VGA_COLOR_TO_DAC(GetGValue(VgaDefaultPalette[i]));
541 VgaDacRegisters[i * 3 + 2] = VGA_COLOR_TO_DAC(GetBValue(VgaDefaultPalette[i]));
542 }
543 }
544
545 static BOOLEAN VgaInitializePalette(VOID)
546 {
547 UINT i;
548 BOOLEAN Result = FALSE;
549 LPLOGPALETTE Palette, TextPalette;
550
551 /* Allocate storage space for the palettes */
552 Palette = RtlAllocateHeap(RtlGetProcessHeap(),
553 HEAP_ZERO_MEMORY,
554 sizeof(LOGPALETTE) +
555 VGA_MAX_COLORS * sizeof(PALETTEENTRY));
556 TextPalette = RtlAllocateHeap(RtlGetProcessHeap(),
557 HEAP_ZERO_MEMORY,
558 sizeof(LOGPALETTE) +
559 (VGA_AC_PAL_F_REG + 1) * sizeof(PALETTEENTRY));
560 if ((Palette == NULL) || (TextPalette == NULL)) goto Cleanup;
561
562 /* Initialize the palettes */
563 Palette->palVersion = TextPalette->palVersion = 0x0300;
564 Palette->palNumEntries = VGA_MAX_COLORS;
565 TextPalette->palNumEntries = VGA_AC_PAL_F_REG + 1;
566
567 /* Restore the default graphics palette */
568 VgaRestoreDefaultPalette(Palette->palPalEntry, Palette->palNumEntries);
569
570 /* Set the default text palette */
571 for (i = 0; i < TextPalette->palNumEntries; i++)
572 {
573 /* Set the palette entries */
574 TextPalette->palPalEntry[i].peRed = GetRValue(ConsoleColors[i]);
575 TextPalette->palPalEntry[i].peGreen = GetGValue(ConsoleColors[i]);
576 TextPalette->palPalEntry[i].peBlue = GetBValue(ConsoleColors[i]);
577 TextPalette->palPalEntry[i].peFlags = 0;
578 }
579
580 /* Create the palettes */
581 PaletteHandle = CreatePalette(Palette);
582 TextPaletteHandle = CreatePalette(TextPalette);
583
584 if (PaletteHandle != NULL && TextPaletteHandle != NULL)
585 {
586 /* The palettes have been created successfully */
587 Result = TRUE;
588 }
589
590 Cleanup:
591 /* Free the palettes */
592 if (Palette) RtlFreeHeap(RtlGetProcessHeap(), 0, Palette);
593 if (TextPalette) RtlFreeHeap(RtlGetProcessHeap(), 0, TextPalette);
594
595 if (!Result)
596 {
597 /* Something failed, delete the palettes */
598 if (PaletteHandle) DeleteObject(PaletteHandle);
599 if (TextPaletteHandle) DeleteObject(TextPaletteHandle);
600 }
601
602 return Result;
603 }
604
605 static VOID VgaResetPalette(VOID)
606 {
607 PALETTEENTRY Entries[VGA_MAX_COLORS];
608
609 /* Restore the default palette */
610 VgaRestoreDefaultPalette(Entries, VGA_MAX_COLORS);
611 SetPaletteEntries(PaletteHandle, 0, VGA_MAX_COLORS, Entries);
612 PaletteChanged = TRUE;
613 }
614
615 static BOOL VgaEnterNewMode(SCREEN_MODE NewScreenMode, PCOORD Resolution)
616 {
617 /* Check if the new mode is alphanumeric */
618 if (NewScreenMode == TEXT_MODE)
619 {
620 /* Enter new text mode */
621
622 if (!VgaConsoleCreateTextScreen(// &TextFramebuffer,
623 Resolution,
624 TextPaletteHandle))
625 {
626 DisplayMessage(L"An unexpected VGA error occurred while switching into text mode. Error: %u", GetLastError());
627 EmulatorTerminate();
628 return FALSE;
629 }
630
631 /* The active framebuffer is now the text framebuffer */
632 ActiveFramebuffer = TextFramebuffer;
633
634 /* Set the screen mode flag */
635 ScreenMode = TEXT_MODE;
636
637 return TRUE;
638 }
639 else
640 {
641 /* Enter graphics mode */
642
643 if (!VgaConsoleCreateGraphicsScreen(// &GraphicsFramebuffer,
644 Resolution,
645 PaletteHandle))
646 {
647 DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode. Error: %u", GetLastError());
648 EmulatorTerminate();
649 return FALSE;
650 }
651
652 /* The active framebuffer is now the graphics framebuffer */
653 ActiveFramebuffer = GraphicsFramebuffer;
654
655 /* Set the screen mode flag */
656 ScreenMode = GRAPHICS_MODE;
657
658 return TRUE;
659 }
660 }
661
662 static VOID VgaLeaveCurrentMode(VOID)
663 {
664 /* Leave the current video mode */
665 if (ScreenMode == GRAPHICS_MODE)
666 {
667 VgaConsoleDestroyGraphicsScreen();
668
669 /* Cleanup the video data */
670 GraphicsFramebuffer = NULL;
671 }
672 else
673 {
674 VgaConsoleDestroyTextScreen();
675
676 /* Cleanup the video data */
677 // TextFramebuffer = NULL;
678 // NEVER SET the ALWAYS-SET TextFramebuffer pointer to NULL!!
679 }
680
681 /* Reset the active framebuffer */
682 ActiveFramebuffer = NULL;
683 }
684
685 static VOID VgaChangeMode(VOID)
686 {
687 COORD NewResolution = VgaGetDisplayResolution();
688 SCREEN_MODE NewScreenMode =
689 !(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA) ? TEXT_MODE
690 : GRAPHICS_MODE;
691
692 /*
693 * Do not switch to a different screen mode + resolution if the new settings
694 * are the same as the old ones. Just repaint the full screen.
695 */
696 if ((ScreenMode == NewScreenMode) && // CurrResolution == NewResolution
697 (CurrResolution.X == NewResolution.X && CurrResolution.Y == NewResolution.Y))
698 {
699 goto Quit;
700 }
701
702 // FIXME: Wouldn't it be preferrable to switch to the new console SB
703 // *ONLY* if we succeeded in setting the new mode??
704
705 /* Leave the current video mode */
706 VgaLeaveCurrentMode(); // ScreenMode
707
708 /* Update the current resolution */
709 CurrResolution = NewResolution;
710
711 /* Change the screen mode */
712 if (!VgaEnterNewMode(NewScreenMode, &CurrResolution))
713 return;
714
715 Quit:
716
717 /* Trigger a full update of the screen */
718 NeedsUpdate = TRUE;
719 UpdateRectangle.Left = 0;
720 UpdateRectangle.Top = 0;
721 UpdateRectangle.Right = CurrResolution.X;
722 UpdateRectangle.Bottom = CurrResolution.Y;
723
724 /* Reset the mode change flag */
725 ModeChanged = FALSE;
726 }
727
728 static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
729 {
730 /* Check if this is the first time the rectangle is updated */
731 if (!NeedsUpdate)
732 {
733 UpdateRectangle.Left = UpdateRectangle.Top = MAXSHORT;
734 UpdateRectangle.Right = UpdateRectangle.Bottom = MINSHORT;
735 }
736
737 /* Expand the rectangle to include the point */
738 UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
739 UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
740 UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
741 UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
742
743 /* Set the update request flag */
744 NeedsUpdate = TRUE;
745 }
746
747 static VOID VgaUpdateFramebuffer(VOID)
748 {
749 SHORT i, j, k;
750 DWORD AddressSize = VgaGetAddressSize();
751 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
752 BYTE PresetRowScan = VgaCrtcRegisters[VGA_CRTC_PRESET_ROW_SCAN_REG] & 0x1F;
753 BYTE BytePanning = (VgaCrtcRegisters[VGA_CRTC_PRESET_ROW_SCAN_REG] >> 5) & 3;
754 DWORD Address = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG],
755 VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG])
756 + PresetRowScan * ScanlineSize
757 + BytePanning;
758 WORD LineCompare = VgaCrtcRegisters[VGA_CRTC_LINE_COMPARE_REG]
759 | ((VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_LC8) << 4);
760 BYTE PixelShift = VgaAcRegisters[VGA_AC_HORZ_PANNING_REG] & 0x0F;
761
762 /*
763 * If the console framebuffer is NULL, that means something
764 * went wrong earlier and this is the final display refresh.
765 */
766 if (ActiveFramebuffer == NULL) return;
767
768 /* Check if we are in text or graphics mode */
769 if (ScreenMode == GRAPHICS_MODE)
770 {
771 /* Graphics mode */
772 PBYTE GraphicsBuffer = (PBYTE)ActiveFramebuffer;
773 DWORD InterlaceHighBit = VGA_INTERLACE_HIGH_BIT;
774 SHORT X;
775
776 /*
777 * Synchronize access to the graphics framebuffer
778 * with the console framebuffer mutex.
779 */
780 WaitForSingleObject(ConsoleMutex, INFINITE);
781
782 /* Shift the high bit right by 1 in odd/even mode */
783 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
784 {
785 InterlaceHighBit >>= 1;
786 }
787
788 if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE)
789 {
790 /* Halve the line compare value */
791 LineCompare >>= 1;
792 }
793 else
794 {
795 /* Divide the line compare value by the maximum scan line */
796 LineCompare /= 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
797 }
798
799 /* Loop through the scanlines */
800 for (i = 0; i < CurrResolution.Y; i++)
801 {
802 if (i == LineCompare)
803 {
804 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_PPM)
805 {
806 /*
807 * Disable the pixel shift count and byte panning
808 * for the rest of the display cycle
809 */
810 PixelShift = 0;
811 BytePanning = 0;
812 }
813
814 /* Reset the address, but assume the preset row scan is 0 */
815 Address = BytePanning;
816 }
817
818 if ((VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) && (i & 1))
819 {
820 /* Odd-numbered line in interlaced mode - set the high bit */
821 Address |= InterlaceHighBit;
822 }
823
824 /* Loop through the pixels */
825 for (j = 0; j < CurrResolution.X; j++)
826 {
827 BYTE PixelData = 0;
828
829 /* Apply horizontal pixel panning */
830 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
831 {
832 X = j + (PixelShift >> 1);
833 }
834 else
835 {
836 X = j + PixelShift;
837 }
838
839 /* Check the shifting mode */
840 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFT256)
841 {
842 /* 4 bits shifted from each plane */
843
844 /* Check if this is 16 or 256 color mode */
845 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
846 {
847 /* One byte per pixel */
848 PixelData = VgaMemory[(X % VGA_NUM_BANKS) * VGA_BANK_SIZE
849 + LOWORD((Address + (X / VGA_NUM_BANKS))
850 * AddressSize)];
851 }
852 else
853 {
854 /* 4-bits per pixel */
855
856 PixelData = VgaMemory[(X % VGA_NUM_BANKS) * VGA_BANK_SIZE
857 + LOWORD((Address + (X / (VGA_NUM_BANKS * 2)))
858 * AddressSize)];
859
860 /* Check if we should use the highest 4 bits or lowest 4 */
861 if (((X / VGA_NUM_BANKS) % 2) == 0)
862 {
863 /* Highest 4 */
864 PixelData >>= 4;
865 }
866 else
867 {
868 /* Lowest 4 */
869 PixelData &= 0x0F;
870 }
871 }
872 }
873 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFTREG)
874 {
875 /* Check if this is 16 or 256 color mode */
876 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
877 {
878 // TODO: NOT IMPLEMENTED
879 DPRINT1("8-bit interleaved mode is not implemented!\n");
880 }
881 else
882 {
883 /*
884 * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
885 * then 2 bits shifted from plane 1 and 3 for the next 4
886 */
887 DWORD BankNumber = (X / 4) % 2;
888 DWORD Offset = Address + (X / 8);
889 BYTE LowPlaneData = VgaMemory[BankNumber * VGA_BANK_SIZE + LOWORD(Offset * AddressSize)];
890 BYTE HighPlaneData = VgaMemory[(BankNumber + 2) * VGA_BANK_SIZE + LOWORD(Offset * AddressSize)];
891
892 /* Extract the two bits from each plane */
893 LowPlaneData = (LowPlaneData >> (6 - ((X % 4) * 2))) & 0x03;
894 HighPlaneData = (HighPlaneData >> (6 - ((X % 4) * 2))) & 0x03;
895
896 /* Combine them into the pixel */
897 PixelData = LowPlaneData | (HighPlaneData << 2);
898 }
899 }
900 else
901 {
902 /* 1 bit shifted from each plane */
903
904 /* Check if this is 16 or 256 color mode */
905 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
906 {
907 /* 8 bits per pixel, 2 on each plane */
908
909 for (k = 0; k < VGA_NUM_BANKS; k++)
910 {
911 /* The data is on plane k, 4 pixels per byte */
912 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
913 + LOWORD((Address + (X / VGA_NUM_BANKS))
914 * AddressSize)];
915
916 /* The mask of the first bit in the pair */
917 BYTE BitMask = 1 << (((3 - (X % VGA_NUM_BANKS)) * 2) + 1);
918
919 /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
920 if (PlaneData & BitMask) PixelData |= 1 << k;
921
922 /* Bits 4, 5, 6 and 7 come from the second bit of the pair */
923 if (PlaneData & (BitMask >> 1)) PixelData |= 1 << (k + 4);
924 }
925 }
926 else
927 {
928 /* 4 bits per pixel, 1 on each plane */
929
930 for (k = 0; k < VGA_NUM_BANKS; k++)
931 {
932 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
933 + LOWORD((Address + (X / (VGA_NUM_BANKS * 2)))
934 * AddressSize)];
935
936 /* If the bit on that plane is set, set it */
937 if (PlaneData & (1 << (7 - (X % 8)))) PixelData |= 1 << k;
938 }
939 }
940 }
941
942 if (!(VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT))
943 {
944 /*
945 * In 16 color mode, the value is an index to the AC registers
946 * if external palette access is disabled, otherwise (in case
947 * of palette loading) it is a blank pixel.
948 */
949
950 if (VgaAcPalDisable)
951 {
952 if (!(VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_P54S))
953 {
954 /* Bits 4 and 5 are taken from the palette register */
955 PixelData = ((VgaAcRegisters[VGA_AC_COLOR_SEL_REG] << 4) & 0xC0)
956 | (VgaAcRegisters[PixelData & 0x0F] & 0x3F);
957 }
958 else
959 {
960 /* Bits 4 and 5 are taken from the color select register */
961 PixelData = (VgaAcRegisters[VGA_AC_COLOR_SEL_REG] << 4)
962 | (VgaAcRegisters[PixelData & 0x0F] & 0x0F);
963 }
964 }
965 else
966 {
967 PixelData = 0;
968 }
969 }
970
971 /* Take into account DoubleVision mode when checking for pixel updates */
972 if (DoubleWidth && DoubleHeight)
973 {
974 /* Now check if the resulting pixel data has changed */
975 if (GraphicsBuffer[(i * 2 * CurrResolution.X * 2) + (j * 2)] != PixelData)
976 {
977 /* Yes, write the new value */
978 GraphicsBuffer[(i * 2 * CurrResolution.X * 2) + (j * 2)] = PixelData;
979 GraphicsBuffer[(i * 2 * CurrResolution.X * 2) + (j * 2 + 1)] = PixelData;
980 GraphicsBuffer[((i * 2 + 1) * CurrResolution.X * 2) + (j * 2)] = PixelData;
981 GraphicsBuffer[((i * 2 + 1) * CurrResolution.X * 2) + (j * 2 + 1)] = PixelData;
982
983 /* Mark the specified pixel as changed */
984 VgaMarkForUpdate(i, j);
985 }
986 }
987 else if (DoubleWidth && !DoubleHeight)
988 {
989 /* Now check if the resulting pixel data has changed */
990 if (GraphicsBuffer[(i * CurrResolution.X * 2) + (j * 2)] != PixelData)
991 {
992 /* Yes, write the new value */
993 GraphicsBuffer[(i * CurrResolution.X * 2) + (j * 2)] = PixelData;
994 GraphicsBuffer[(i * CurrResolution.X * 2) + (j * 2 + 1)] = PixelData;
995
996 /* Mark the specified pixel as changed */
997 VgaMarkForUpdate(i, j);
998 }
999 }
1000 else if (!DoubleWidth && DoubleHeight)
1001 {
1002 /* Now check if the resulting pixel data has changed */
1003 if (GraphicsBuffer[(i * 2 * CurrResolution.X) + j] != PixelData)
1004 {
1005 /* Yes, write the new value */
1006 GraphicsBuffer[(i * 2 * CurrResolution.X) + j] = PixelData;
1007 GraphicsBuffer[((i * 2 + 1) * CurrResolution.X) + j] = PixelData;
1008
1009 /* Mark the specified pixel as changed */
1010 VgaMarkForUpdate(i, j);
1011 }
1012 }
1013 else // if (!DoubleWidth && !DoubleHeight)
1014 {
1015 /* Now check if the resulting pixel data has changed */
1016 if (GraphicsBuffer[i * CurrResolution.X + j] != PixelData)
1017 {
1018 /* Yes, write the new value */
1019 GraphicsBuffer[i * CurrResolution.X + j] = PixelData;
1020
1021 /* Mark the specified pixel as changed */
1022 VgaMarkForUpdate(i, j);
1023 }
1024 }
1025 }
1026
1027 if ((VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) && (i & 1))
1028 {
1029 /* Clear the high bit */
1030 Address &= ~InterlaceHighBit;
1031 }
1032
1033 if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) || (i & 1))
1034 {
1035 /* Move to the next scanline */
1036 Address += ScanlineSize;
1037 }
1038 }
1039
1040 /*
1041 * Release the console framebuffer mutex
1042 * so that we allow for repainting.
1043 */
1044 ReleaseMutex(ConsoleMutex);
1045 }
1046 else
1047 {
1048 /* Text mode */
1049 DWORD CurrentAddr;
1050 PCHAR_CELL CharBuffer = (PCHAR_CELL)ActiveFramebuffer;
1051 CHAR_CELL CharInfo;
1052
1053 /*
1054 * Technically, the horizontal panning and preset row count should
1055 * affect text mode too. However, it works on pixels and not characters,
1056 * so we can't support it currently.
1057 */
1058
1059 /* Loop through the scanlines */
1060 for (i = 0; i < CurrResolution.Y; i++)
1061 {
1062 /* Loop through the characters */
1063 for (j = 0; j < CurrResolution.X; j++)
1064 {
1065 CurrentAddr = LOWORD((Address + j) * AddressSize);
1066
1067 /* Plane 0 holds the character itself */
1068 CharInfo.Char = VgaMemory[CurrentAddr];
1069
1070 /* Plane 1 holds the attribute */
1071 CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE];
1072
1073 /* Now check if the resulting character data has changed */
1074 if ((CharBuffer[i * CurrResolution.X + j].Char != CharInfo.Char) ||
1075 (CharBuffer[i * CurrResolution.X + j].Attributes != CharInfo.Attributes))
1076 {
1077 /* Yes, write the new value */
1078 CharBuffer[i * CurrResolution.X + j] = CharInfo;
1079
1080 /* Mark the specified cell as changed */
1081 VgaMarkForUpdate(i, j);
1082 }
1083 }
1084
1085 /* Move to the next scanline */
1086 Address += ScanlineSize;
1087 }
1088 }
1089 }
1090
1091 static VOID VgaUpdateTextCursor(VOID)
1092 {
1093 BOOL CursorVisible = !(VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x20);
1094 BYTE CursorStart = VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x1F;
1095 BYTE CursorEnd = VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] & 0x1F;
1096
1097 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
1098 BYTE TextSize = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
1099 WORD Location = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG],
1100 VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG]);
1101
1102 /* Just return if we are not in text mode */
1103 if (ScreenMode != TEXT_MODE) return;
1104
1105 /* Add the cursor skew to the location */
1106 Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 0x03;
1107
1108 VgaConsoleUpdateTextCursor(CursorVisible, CursorStart, CursorEnd,
1109 TextSize, ScanlineSize, Location);
1110
1111 /* Reset the cursor changed flag */
1112 CursorChanged = FALSE;
1113 }
1114
1115 static BYTE WINAPI VgaReadPort(USHORT Port)
1116 {
1117 DPRINT("VgaReadPort: Port 0x%X\n", Port);
1118
1119 if (Port != VGA_DAC_MASK) SvgaHdrCounter = 0;
1120
1121 switch (Port)
1122 {
1123 case VGA_MISC_READ:
1124 return VgaMiscRegister;
1125
1126 case VGA_INSTAT0_READ:
1127 return 0; // Not implemented
1128
1129 case VGA_INSTAT1_READ_MONO:
1130 case VGA_INSTAT1_READ_COLOR:
1131 {
1132 BYTE Result = 0;
1133 BOOLEAN Vsync, Hsync;
1134 ULONGLONG Cycles = GetCycleCount();
1135 ULONG CyclesPerMicrosecond = (ULONG)((GetCycleSpeed() + 500000ULL) / 1000000ULL);
1136 ULONG Dots = (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & 1) ? 9 : 8;
1137 ULONG Clock = VgaGetClockFrequency() / 1000000;
1138 ULONG HorizTotalDots = ((ULONG)VgaCrtcRegisters[VGA_CRTC_HORZ_TOTAL_REG] + 5) * Dots;
1139 ULONG VblankStart, VblankEnd, HblankStart, HblankEnd;
1140 ULONG HblankDuration, VblankDuration;
1141
1142 /* Calculate the vertical blanking duration in cycles */
1143 VblankStart = VgaCrtcRegisters[VGA_CRTC_START_VERT_BLANKING_REG] & 0x7F;
1144 VblankEnd = VgaCrtcRegisters[VGA_CRTC_END_VERT_BLANKING_REG] & 0x7F;
1145 if (VblankEnd < VblankStart) VblankEnd |= 0x80;
1146 VblankDuration = ((VblankEnd - VblankStart) * HorizTotalDots
1147 * CyclesPerMicrosecond + (Clock >> 1)) / Clock;
1148
1149 /* Calculate the horizontal blanking duration in cycles */
1150 HblankStart = VgaCrtcRegisters[VGA_CRTC_START_HORZ_BLANKING_REG] & 0x1F;
1151 HblankEnd = VgaCrtcRegisters[VGA_CRTC_END_HORZ_BLANKING_REG] & 0x1F;
1152 if (HblankEnd < HblankStart) HblankEnd |= 0x20;
1153 HblankDuration = ((HblankEnd - HblankStart) * Dots
1154 * CyclesPerMicrosecond + (Clock >> 1)) / Clock;
1155
1156 Vsync = (Cycles - VerticalRetraceCycle) < (ULONGLONG)VblankDuration;
1157 Hsync = (Cycles - HorizontalRetraceCycle) < (ULONGLONG)HblankDuration;
1158
1159 /* Reset the AC latch */
1160 VgaAcLatch = FALSE;
1161
1162 /* Reverse the polarity, if needed */
1163 if (VgaMiscRegister & VGA_MISC_VSYNCP) Vsync = !Vsync;
1164 if (VgaMiscRegister & VGA_MISC_HSYNCP) Hsync = !Hsync;
1165
1166 /* Set a flag if there is a vertical or horizontal retrace */
1167 if (Vsync || Hsync) Result |= VGA_STAT_DD;
1168
1169 /* Set an additional flag if there was a vertical retrace */
1170 if (Vsync) Result |= VGA_STAT_VRETRACE;
1171
1172 return Result;
1173 }
1174
1175 case VGA_FEATURE_READ:
1176 return VgaFeatureRegister;
1177
1178 case VGA_AC_INDEX:
1179 return VgaAcIndex;
1180
1181 case VGA_AC_READ:
1182 return VgaAcRegisters[VgaAcIndex];
1183
1184 case VGA_SEQ_INDEX:
1185 return VgaSeqIndex;
1186
1187 case VGA_SEQ_DATA:
1188 return VgaSeqRegisters[VgaSeqIndex];
1189
1190 case VGA_DAC_MASK:
1191 {
1192 if (SvgaHdrCounter == 4)
1193 {
1194 SvgaHdrCounter = 0;
1195 return SvgaHiddenRegister;
1196 }
1197 else
1198 {
1199 SvgaHdrCounter++;
1200 return VgaDacMask;
1201 }
1202 }
1203
1204 case VGA_DAC_READ_INDEX:
1205 /* This returns the read/write state */
1206 return (VgaDacReadWrite ? 0 : 3);
1207
1208 case VGA_DAC_WRITE_INDEX:
1209 return VgaDacIndex;
1210
1211 case VGA_DAC_DATA:
1212 {
1213 /* Ignore reads in write mode */
1214 if (!VgaDacReadWrite)
1215 {
1216 BYTE Data = VgaDacRegisters[VgaDacIndex * 3 + VgaDacLatchCounter];
1217 VgaDacLatchCounter++;
1218
1219 if (VgaDacLatchCounter == 3)
1220 {
1221 /* Reset the latch counter and increment the palette index */
1222 VgaDacLatchCounter = 0;
1223 VgaDacIndex++;
1224 VgaDacIndex %= VGA_MAX_COLORS;
1225 }
1226
1227 return Data;
1228 }
1229
1230 break;
1231 }
1232
1233 case VGA_CRTC_INDEX_MONO:
1234 case VGA_CRTC_INDEX_COLOR:
1235 return VgaCrtcIndex;
1236
1237 case VGA_CRTC_DATA_MONO:
1238 case VGA_CRTC_DATA_COLOR:
1239 return VgaCrtcRegisters[VgaCrtcIndex];
1240
1241 case VGA_GC_INDEX:
1242 return VgaGcIndex;
1243
1244 case VGA_GC_DATA:
1245 return VgaGcRegisters[VgaGcIndex];
1246
1247 default:
1248 DPRINT1("VgaReadPort: Unknown port 0x%X\n", Port);
1249 break;
1250 }
1251
1252 return 0;
1253 }
1254
1255 static inline VOID VgaWriteSequencer(BYTE Data)
1256 {
1257 /* Save the value */
1258 VgaSeqRegisters[VgaSeqIndex & VGA_SEQ_INDEX_MASK] = Data;
1259
1260 /* Check the index */
1261 switch (VgaSeqIndex & VGA_SEQ_INDEX_MASK)
1262 {
1263 case SVGA_SEQ_UNLOCK_REG:
1264 {
1265 if ((Data & SVGA_SEQ_UNLOCK_MASK) == SVGA_SEQ_UNLOCKED)
1266 {
1267 /* Unlock SVGA extensions */
1268 VgaSeqRegisters[SVGA_SEQ_UNLOCK_REG] = SVGA_SEQ_UNLOCKED;
1269 }
1270 else
1271 {
1272 /* Lock SVGA extensions */
1273 VgaSeqRegisters[SVGA_SEQ_UNLOCK_REG] = SVGA_SEQ_LOCKED;
1274 }
1275
1276 break;
1277 }
1278 }
1279 }
1280
1281 static inline VOID VgaWriteGc(BYTE Data)
1282 {
1283 /* Save the value */
1284 VgaGcRegisters[VgaGcIndex & VGA_GC_INDEX_MASK] = Data;
1285
1286 /* Check the index */
1287 switch (VgaGcIndex & VGA_GC_INDEX_MASK)
1288 {
1289 case VGA_GC_MISC_REG:
1290 {
1291 /* Remove any existing VGA memory hook */
1292 MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
1293
1294 if (VgaMiscRegister & VGA_MISC_RAM_ENABLED)
1295 {
1296 UCHAR MemoryMap = (VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03;
1297
1298 /* Register a memory hook */
1299 MemInstallFastMemoryHook((PVOID)MemoryBase[MemoryMap],
1300 MemorySize[MemoryMap],
1301 VgaReadMemory,
1302 VgaWriteMemory);
1303 }
1304
1305 /* The GC misc register decides if it's text or graphics mode */
1306 ModeChanged = TRUE;
1307 break;
1308 }
1309 }
1310 }
1311
1312 static inline VOID VgaWriteCrtc(BYTE Data)
1313 {
1314 /* Save the value */
1315 VgaCrtcRegisters[VgaCrtcIndex & VGA_CRTC_INDEX_MASK] = Data;
1316
1317 /* Check the index */
1318 switch (VgaCrtcIndex & VGA_CRTC_INDEX_MASK)
1319 {
1320 case VGA_CRTC_END_HORZ_DISP_REG:
1321 case VGA_CRTC_VERT_DISP_END_REG:
1322 case VGA_CRTC_OVERFLOW_REG:
1323 case VGA_CRTC_MAX_SCAN_LINE_REG:
1324 {
1325 /* The video mode has changed */
1326 ModeChanged = TRUE;
1327 break;
1328 }
1329
1330 case VGA_CRTC_CURSOR_LOC_LOW_REG:
1331 case VGA_CRTC_CURSOR_LOC_HIGH_REG:
1332 case VGA_CRTC_CURSOR_START_REG:
1333 case VGA_CRTC_CURSOR_END_REG:
1334 {
1335 /* Set the cursor changed flag */
1336 CursorChanged = TRUE;
1337 break;
1338 }
1339 }
1340 }
1341
1342 static inline VOID VgaWriteDac(BYTE Data)
1343 {
1344 UINT i;
1345 PALETTEENTRY Entry;
1346
1347 /* Store the value in the latch */
1348 VgaDacLatch[VgaDacLatchCounter++] = Data;
1349 if (VgaDacLatchCounter < 3) return;
1350
1351 /* Reset the latch counter */
1352 VgaDacLatchCounter = 0;
1353
1354 /* Set the DAC register values */
1355 VgaDacRegisters[VgaDacIndex * 3] = VgaDacLatch[0];
1356 VgaDacRegisters[VgaDacIndex * 3 + 1] = VgaDacLatch[1];
1357 VgaDacRegisters[VgaDacIndex * 3 + 2] = VgaDacLatch[2];
1358
1359 /* Fill the entry structure */
1360 Entry.peRed = VGA_DAC_TO_COLOR(VgaDacLatch[0]);
1361 Entry.peGreen = VGA_DAC_TO_COLOR(VgaDacLatch[1]);
1362 Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacLatch[2]);
1363 Entry.peFlags = 0;
1364
1365 /* Update the palette entry */
1366 SetPaletteEntries(PaletteHandle, VgaDacIndex, 1, &Entry);
1367
1368 /* Check which text palette entries are affected */
1369 for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
1370 {
1371 if (VgaAcRegisters[i] == VgaDacIndex)
1372 {
1373 /* Update the text palette entry */
1374 SetPaletteEntries(TextPaletteHandle, i, 1, &Entry);
1375 }
1376 }
1377
1378 /* Set the palette changed flag */
1379 PaletteChanged = TRUE;
1380
1381 /* Update the index */
1382 VgaDacIndex++;
1383 VgaDacIndex %= VGA_MAX_COLORS;
1384 }
1385
1386 static inline VOID VgaWriteAc(BYTE Data)
1387 {
1388 PALETTEENTRY Entry;
1389
1390 ASSERT(VgaAcIndex < VGA_AC_MAX_REG);
1391
1392 /* Save the value */
1393 if (VgaAcIndex <= VGA_AC_PAL_F_REG)
1394 {
1395 if (VgaAcPalDisable) return;
1396
1397 // DbgPrint(" AC Palette Writing %d to index %d\n", Data, VgaAcIndex);
1398 if (VgaAcRegisters[VgaAcIndex] != Data)
1399 {
1400 /* Update the AC register */
1401 VgaAcRegisters[VgaAcIndex] = Data;
1402
1403 /* Fill the entry structure */
1404 Entry.peRed = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3]);
1405 Entry.peGreen = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3 + 1]);
1406 Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3 + 2]);
1407 Entry.peFlags = 0;
1408
1409 /* Update the palette entry and set the palette change flag */
1410 SetPaletteEntries(TextPaletteHandle, VgaAcIndex, 1, &Entry);
1411 PaletteChanged = TRUE;
1412 }
1413 }
1414 else
1415 {
1416 VgaAcRegisters[VgaAcIndex] = Data;
1417 }
1418 }
1419
1420 static VOID WINAPI VgaWritePort(USHORT Port, BYTE Data)
1421 {
1422 DPRINT("VgaWritePort: Port 0x%X, Data 0x%02X\n", Port, Data);
1423
1424 switch (Port)
1425 {
1426 case VGA_MISC_WRITE:
1427 {
1428 VgaMiscRegister = Data;
1429
1430 if (VgaMiscRegister & 0x01)
1431 {
1432 /* Color emulation */
1433 DPRINT1("Color emulation\n");
1434
1435 /* Register the new I/O Ports */
1436 RegisterIoPort(0x3D4, VgaReadPort, VgaWritePort); // VGA_CRTC_INDEX_COLOR
1437 RegisterIoPort(0x3D5, VgaReadPort, VgaWritePort); // VGA_CRTC_DATA_COLOR
1438 RegisterIoPort(0x3DA, VgaReadPort, VgaWritePort); // VGA_INSTAT1_READ_COLOR, VGA_FEATURE_WRITE_COLOR
1439
1440 /* Unregister the old ones */
1441 UnregisterIoPort(0x3B4); // VGA_CRTC_INDEX_MONO
1442 UnregisterIoPort(0x3B5); // VGA_CRTC_DATA_MONO
1443 UnregisterIoPort(0x3BA); // VGA_INSTAT1_READ_MONO, VGA_FEATURE_WRITE_MONO
1444 }
1445 else
1446 {
1447 /* Monochrome emulation */
1448 DPRINT1("Monochrome emulation\n");
1449
1450 /* Register the new I/O Ports */
1451 RegisterIoPort(0x3B4, VgaReadPort, VgaWritePort); // VGA_CRTC_INDEX_MONO
1452 RegisterIoPort(0x3B5, VgaReadPort, VgaWritePort); // VGA_CRTC_DATA_MONO
1453 RegisterIoPort(0x3BA, VgaReadPort, VgaWritePort); // VGA_INSTAT1_READ_MONO, VGA_FEATURE_WRITE_MONO
1454
1455 /* Unregister the old ones */
1456 UnregisterIoPort(0x3D4); // VGA_CRTC_INDEX_COLOR
1457 UnregisterIoPort(0x3D5); // VGA_CRTC_DATA_COLOR
1458 UnregisterIoPort(0x3DA); // VGA_INSTAT1_READ_COLOR, VGA_FEATURE_WRITE_COLOR
1459 }
1460
1461 /* Remove any existing VGA memory hook */
1462 MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
1463
1464 if (VgaMiscRegister & VGA_MISC_RAM_ENABLED)
1465 {
1466 UCHAR MemoryMap = (VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03;
1467
1468 /* Register a memory hook */
1469 MemInstallFastMemoryHook((PVOID)MemoryBase[MemoryMap],
1470 MemorySize[MemoryMap],
1471 VgaReadMemory,
1472 VgaWriteMemory);
1473 }
1474
1475 break;
1476 }
1477
1478 case VGA_FEATURE_WRITE_MONO:
1479 case VGA_FEATURE_WRITE_COLOR:
1480 {
1481 VgaFeatureRegister = Data;
1482 break;
1483 }
1484
1485 case VGA_AC_INDEX:
1486 // case VGA_AC_WRITE:
1487 {
1488 if (!VgaAcLatch)
1489 {
1490 /* Change the index */
1491 BYTE Index = Data & 0x1F;
1492 if (Index < VGA_AC_MAX_REG) VgaAcIndex = Index;
1493
1494 /*
1495 * Change palette protection by checking for
1496 * the Palette Address Source bit.
1497 */
1498 VgaAcPalDisable = (Data & 0x20) ? TRUE : FALSE;
1499 }
1500 else
1501 {
1502 /* Write the data */
1503 VgaWriteAc(Data);
1504 }
1505
1506 /* Toggle the latch */
1507 VgaAcLatch = !VgaAcLatch;
1508 break;
1509 }
1510
1511 case VGA_SEQ_INDEX:
1512 {
1513 /* Set the sequencer index register */
1514 if ((Data & 0x1F) < SVGA_SEQ_MAX_UNLOCKED_REG
1515 && (Data & 0x1F) != VGA_SEQ_MAX_REG)
1516 {
1517 VgaSeqIndex = Data;
1518 }
1519
1520 break;
1521 }
1522
1523 case VGA_SEQ_DATA:
1524 {
1525 /* Call the sequencer function */
1526 VgaWriteSequencer(Data);
1527 break;
1528 }
1529
1530 case VGA_DAC_MASK:
1531 {
1532 if (SvgaHdrCounter == 4) SvgaHiddenRegister = Data;
1533 else VgaDacMask = Data;
1534
1535 break;
1536 }
1537
1538 case VGA_DAC_READ_INDEX:
1539 {
1540 VgaDacReadWrite = FALSE;
1541 VgaDacIndex = Data;
1542 VgaDacLatchCounter = 0;
1543 break;
1544 }
1545
1546 case VGA_DAC_WRITE_INDEX:
1547 {
1548 VgaDacReadWrite = TRUE;
1549 VgaDacIndex = Data;
1550 VgaDacLatchCounter = 0;
1551 break;
1552 }
1553
1554 case VGA_DAC_DATA:
1555 {
1556 /* Ignore writes in read mode */
1557 if (VgaDacReadWrite) VgaWriteDac(Data & 0x3F);
1558 break;
1559 }
1560
1561 case VGA_CRTC_INDEX_MONO:
1562 case VGA_CRTC_INDEX_COLOR:
1563 {
1564 /* Set the CRTC index register */
1565 if (((Data & VGA_CRTC_INDEX_MASK) < SVGA_CRTC_MAX_UNLOCKED_REG)
1566 && ((Data & VGA_CRTC_INDEX_MASK) < SVGA_CRTC_UNUSED0_REG
1567 || (Data & VGA_CRTC_INDEX_MASK) > SVGA_CRTC_UNUSED6_REG)
1568 && (Data & VGA_CRTC_INDEX_MASK) != SVGA_CRTC_UNUSED7_REG)
1569 {
1570 VgaCrtcIndex = Data;
1571 }
1572
1573 break;
1574 }
1575
1576 case VGA_CRTC_DATA_MONO:
1577 case VGA_CRTC_DATA_COLOR:
1578 {
1579 /* Call the CRTC function */
1580 VgaWriteCrtc(Data);
1581 break;
1582 }
1583
1584 case VGA_GC_INDEX:
1585 {
1586 /* Set the GC index register */
1587 if ((Data & VGA_GC_INDEX_MASK) < SVGA_GC_MAX_UNLOCKED_REG
1588 && (Data & VGA_GC_INDEX_MASK) != SVGA_GC_UNUSED0_REG
1589 && ((Data & VGA_GC_INDEX_MASK) < SVGA_GC_UNUSED1_REG
1590 || (Data & VGA_GC_INDEX_MASK) > SVGA_GC_UNUSED10_REG))
1591 {
1592 VgaGcIndex = Data;
1593 }
1594
1595 break;
1596 }
1597
1598 case VGA_GC_DATA:
1599 {
1600 /* Call the GC function */
1601 VgaWriteGc(Data);
1602 break;
1603 }
1604
1605 default:
1606 DPRINT1("VgaWritePort: Unknown port 0x%X, Data 0x%02X\n", Port, Data);
1607 break;
1608 }
1609
1610 SvgaHdrCounter = 0;
1611 }
1612
1613 static VOID FASTCALL VgaVerticalRetrace(ULONGLONG ElapsedTime)
1614 {
1615 UNREFERENCED_PARAMETER(ElapsedTime);
1616
1617 /* Set the vertical retrace cycle */
1618 VerticalRetraceCycle = GetCycleCount();
1619
1620 /* If nothing has changed, just return */
1621 // if (!ModeChanged && !CursorChanged && !PaletteChanged && !NeedsUpdate)
1622 // return;
1623
1624 /* Change the display mode */
1625 if (ModeChanged) VgaChangeMode();
1626
1627 /* Change the text cursor appearance */
1628 if (CursorChanged) VgaUpdateTextCursor();
1629
1630 if (PaletteChanged)
1631 {
1632 /* Trigger a full update of the screen */
1633 NeedsUpdate = TRUE;
1634 UpdateRectangle.Left = 0;
1635 UpdateRectangle.Top = 0;
1636 UpdateRectangle.Right = CurrResolution.X;
1637 UpdateRectangle.Bottom = CurrResolution.Y;
1638
1639 PaletteChanged = FALSE;
1640 }
1641
1642 /* Update the contents of the framebuffer */
1643 VgaUpdateFramebuffer();
1644
1645 /* Ignore if there's nothing to update */
1646 if (!NeedsUpdate) return;
1647
1648 DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
1649 UpdateRectangle.Left,
1650 UpdateRectangle.Top,
1651 UpdateRectangle.Right,
1652 UpdateRectangle.Bottom);
1653
1654 VgaConsoleRepaintScreen(&UpdateRectangle);
1655
1656 /* Clear the update flag */
1657 NeedsUpdate = FALSE;
1658 }
1659
1660 static VOID FASTCALL VgaHorizontalRetrace(ULONGLONG ElapsedTime)
1661 {
1662 UNREFERENCED_PARAMETER(ElapsedTime);
1663
1664 /* Set the cycle */
1665 HorizontalRetraceCycle = GetCycleCount();
1666 }
1667
1668 /* PUBLIC FUNCTIONS ***********************************************************/
1669
1670 COORD VgaGetDisplayResolution(VOID)
1671 {
1672 COORD Resolution;
1673 BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
1674
1675 /* The low 8 bits are in the display registers */
1676 Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
1677 Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG];
1678
1679 /* Set the top bits from the overflow register */
1680 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8)
1681 {
1682 Resolution.Y |= 1 << 8;
1683 }
1684 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9)
1685 {
1686 Resolution.Y |= 1 << 9;
1687 }
1688
1689 /* Increase the values by 1 */
1690 Resolution.X++;
1691 Resolution.Y++;
1692
1693 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
1694 {
1695 /* In "High Resolution" mode, the width of a character is always 8 pixels */
1696 if (VgaSeqRegisters[SVGA_SEQ_EXT_MODE_REG] & SVGA_SEQ_EXT_MODE_HIGH_RES)
1697 {
1698 Resolution.X *= 8;
1699 }
1700 else
1701 {
1702 /* Multiply the horizontal resolution by the 9/8 dot mode */
1703 Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
1704 ? 8 : 9;
1705
1706 /* The horizontal resolution is halved in 8-bit mode */
1707 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
1708 }
1709 }
1710
1711 if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE)
1712 {
1713 /* Halve the vertical resolution */
1714 Resolution.Y >>= 1;
1715 }
1716 else
1717 {
1718 /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
1719 Resolution.Y /= MaximumScanLine;
1720 }
1721
1722 /* Return the resolution */
1723 return Resolution;
1724 }
1725
1726 VOID VgaRefreshDisplay(VOID)
1727 {
1728 VgaVerticalRetrace(0);
1729 }
1730
1731 VOID FASTCALL VgaReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
1732 {
1733 DWORD i, j;
1734 DWORD VideoAddress;
1735 PUCHAR BufPtr = (PUCHAR)Buffer;
1736
1737 DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n", Address, Size);
1738
1739 /* Ignore if video RAM access is disabled */
1740 if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return;
1741
1742 if (!(VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_READ))
1743 {
1744 /* Loop through each byte */
1745 for (i = 0; i < Size; i++)
1746 {
1747 VideoAddress = VgaTranslateReadAddress(Address + i);
1748
1749 /* Copy the value to the buffer */
1750 BufPtr[i] = VgaMemory[VideoAddress];
1751 }
1752 }
1753 else
1754 {
1755 /* Loop through each byte */
1756 for (i = 0; i < Size; i++)
1757 {
1758 BYTE Result = 0xFF;
1759
1760 /* This should always return a plane 0 address for read mode 1 */
1761 VideoAddress = VgaTranslateReadAddress(Address + i);
1762
1763 for (j = 0; j < VGA_NUM_BANKS; j++)
1764 {
1765 /* Don't consider ignored banks */
1766 if (!(VgaGcRegisters[VGA_GC_COLOR_IGNORE_REG] & (1 << j))) continue;
1767
1768 if (VgaGcRegisters[VGA_GC_COLOR_COMPARE_REG] & (1 << j))
1769 {
1770 /* Comparing with 11111111 */
1771 Result &= VgaMemory[j * VGA_BANK_SIZE + LOWORD(VideoAddress)];
1772 }
1773 else
1774 {
1775 /* Comparing with 00000000 */
1776 Result &= ~(VgaMemory[j * VGA_BANK_SIZE + LOWORD(VideoAddress)]);
1777 }
1778 }
1779
1780 /* Copy the value to the buffer */
1781 BufPtr[i] = Result;
1782 }
1783 }
1784
1785 /* Load the latch registers */
1786 VgaLatchRegisters[0] = VgaMemory[LOWORD(VideoAddress)];
1787 VgaLatchRegisters[1] = VgaMemory[VGA_BANK_SIZE + LOWORD(VideoAddress)];
1788 VgaLatchRegisters[2] = VgaMemory[(2 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
1789 VgaLatchRegisters[3] = VgaMemory[(3 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
1790 }
1791
1792 BOOLEAN FASTCALL VgaWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
1793 {
1794 DWORD i, j;
1795 DWORD VideoAddress;
1796 PUCHAR BufPtr = (PUCHAR)Buffer;
1797
1798 DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n", Address, Size);
1799
1800 /* Ignore if video RAM access is disabled */
1801 if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return TRUE;
1802
1803 /* Also ignore if write access to all planes is disabled */
1804 if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return TRUE;
1805
1806 /* Loop through each byte */
1807 for (i = 0; i < Size; i++)
1808 {
1809 VideoAddress = VgaTranslateWriteAddress(Address + i);
1810
1811 for (j = 0; j < VGA_NUM_BANKS; j++)
1812 {
1813 /* Make sure the page is writeable */
1814 if (!(VgaSeqRegisters[VGA_SEQ_MASK_REG] & (1 << j))) continue;
1815
1816 /* Check if this is chain-4 mode */
1817 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
1818 {
1819 if (((Address + i) & 0x03) != j)
1820 {
1821 /* This plane will not be accessed */
1822 continue;
1823 }
1824 }
1825
1826 /* Check if this is odd-even mode */
1827 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
1828 {
1829 if (((Address + i) & 0x01) != (j & 1))
1830 {
1831 /* This plane will not be accessed */
1832 continue;
1833 }
1834 }
1835
1836 /* Copy the value to the VGA memory */
1837 VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(BufPtr[i], j);
1838 }
1839 }
1840
1841 return TRUE;
1842 }
1843
1844 VOID VgaClearMemory(VOID)
1845 {
1846 RtlZeroMemory(VgaMemory, sizeof(VgaMemory));
1847 }
1848
1849 VOID VgaWriteTextModeFont(UINT FontNumber, CONST UCHAR* FontData, UINT Height)
1850 {
1851 UINT i, j;
1852 PUCHAR FontMemory = (PUCHAR)&VgaMemory[VGA_BANK_SIZE * VGA_FONT_BANK + (FontNumber * VGA_FONT_SIZE)];
1853
1854 ASSERT(Height <= VGA_MAX_FONT_HEIGHT);
1855
1856 for (i = 0 ; i < VGA_FONT_CHARACTERS; i++)
1857 {
1858 /* Write the character */
1859 for (j = 0; j < Height; j++)
1860 {
1861 FontMemory[i * VGA_MAX_FONT_HEIGHT + j] = FontData[i * Height + j];
1862 }
1863
1864 /* Clear the unused part */
1865 for (j = Height; j < VGA_MAX_FONT_HEIGHT; j++)
1866 {
1867 FontMemory[i * VGA_MAX_FONT_HEIGHT + j] = 0;
1868 }
1869 }
1870 }
1871
1872 BOOLEAN VgaInitialize(HANDLE TextHandle)
1873 {
1874 if (!VgaConsoleInitialize(TextHandle)) return FALSE;
1875
1876 /* Clear the SEQ, GC, CRTC and AC registers */
1877 RtlZeroMemory(VgaSeqRegisters , sizeof(VgaSeqRegisters ));
1878 RtlZeroMemory(VgaGcRegisters , sizeof(VgaGcRegisters ));
1879 RtlZeroMemory(VgaCrtcRegisters, sizeof(VgaCrtcRegisters));
1880 RtlZeroMemory(VgaAcRegisters , sizeof(VgaAcRegisters ));
1881
1882 /* Initialize the VGA palette and fail if it isn't successfully created */
1883 if (!VgaInitializePalette()) return FALSE;
1884 /***/ VgaResetPalette(); /***/
1885
1886 /* Reset the sequencer */
1887 VgaResetSequencer();
1888
1889 /* Clear the VGA memory */
1890 VgaClearMemory();
1891
1892 /* Register the I/O Ports */
1893 RegisterIoPort(0x3CC, VgaReadPort, NULL); // VGA_MISC_READ
1894 RegisterIoPort(0x3C2, VgaReadPort, VgaWritePort); // VGA_MISC_WRITE, VGA_INSTAT0_READ
1895 RegisterIoPort(0x3CA, VgaReadPort, NULL); // VGA_FEATURE_READ
1896 RegisterIoPort(0x3C0, VgaReadPort, VgaWritePort); // VGA_AC_INDEX, VGA_AC_WRITE
1897 RegisterIoPort(0x3C1, VgaReadPort, NULL); // VGA_AC_READ
1898 RegisterIoPort(0x3C4, VgaReadPort, VgaWritePort); // VGA_SEQ_INDEX
1899 RegisterIoPort(0x3C5, VgaReadPort, VgaWritePort); // VGA_SEQ_DATA
1900 RegisterIoPort(0x3C6, VgaReadPort, VgaWritePort); // VGA_DAC_MASK
1901 RegisterIoPort(0x3C7, VgaReadPort, VgaWritePort); // VGA_DAC_READ_INDEX
1902 RegisterIoPort(0x3C8, VgaReadPort, VgaWritePort); // VGA_DAC_WRITE_INDEX
1903 RegisterIoPort(0x3C9, VgaReadPort, VgaWritePort); // VGA_DAC_DATA
1904 RegisterIoPort(0x3CE, VgaReadPort, VgaWritePort); // VGA_GC_INDEX
1905 RegisterIoPort(0x3CF, VgaReadPort, VgaWritePort); // VGA_GC_DATA
1906
1907 /* CGA ports for compatibility, unimplemented */
1908 RegisterIoPort(0x3D8, VgaReadPort, VgaWritePort); // CGA_MODE_CTRL_REG
1909 RegisterIoPort(0x3D9, VgaReadPort, VgaWritePort); // CGA_PAL_CTRL_REG
1910
1911 HSyncTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, HZ_TO_NS(31469), VgaHorizontalRetrace);
1912 VSyncTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, HZ_TO_NS(60), VgaVerticalRetrace);
1913
1914 /* Return success */
1915 return TRUE;
1916 }
1917
1918 VOID VgaCleanup(VOID)
1919 {
1920 /* Do a final display refresh */
1921 VgaRefreshDisplay();
1922
1923 DestroyHardwareTimer(VSyncTimer);
1924 DestroyHardwareTimer(HSyncTimer);
1925
1926 /* Leave the current video mode */
1927 VgaLeaveCurrentMode(); // ScreenMode
1928
1929 MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
1930
1931 VgaConsoleCleanup();
1932 }
1933
1934 /* EOF */