[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / hardware / video / vga.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: vga.c
5 * PURPOSE: VGA hardware emulation
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "ntvdm.h"
14 #include "emulator.h"
15 #include "vga.h"
16 #include <bios/vidbios.h>
17
18 #include "memory.h"
19 #include "io.h"
20 #include "clock.h"
21
22 /* PRIVATE VARIABLES **********************************************************/
23
24 static CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
25 static CONST DWORD MemorySize[] = { 0x20000, 0x10000, 0x8000, 0x8000 };
26
27 /*
28 * Activate this line if you want to use the real
29 * RegisterConsoleVDM API of ReactOS/Windows.
30 */
31 // #define USE_REAL_REGISTERCONSOLEVDM
32
33 #define USE_REACTOS_COLORS
34 // #define USE_DOSBOX_COLORS
35
36 #if defined(USE_REACTOS_COLORS)
37
38 // ReactOS colors
39 static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] =
40 {
41 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA),
42 RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA),
43 RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF),
44 RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF),
45 RGB(0x00, 0x00, 0x00), RGB(0x10, 0x10, 0x10), RGB(0x20, 0x20, 0x20), RGB(0x35, 0x35, 0x35),
46 RGB(0x45, 0x45, 0x45), RGB(0x55, 0x55, 0x55), RGB(0x65, 0x65, 0x65), RGB(0x75, 0x75, 0x75),
47 RGB(0x8A, 0x8A, 0x8A), RGB(0x9A, 0x9A, 0x9A), RGB(0xAA, 0xAA, 0xAA), RGB(0xBA, 0xBA, 0xBA),
48 RGB(0xCA, 0xCA, 0xCA), RGB(0xDF, 0xDF, 0xDF), RGB(0xEF, 0xEF, 0xEF), RGB(0xFF, 0xFF, 0xFF),
49 RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x82, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF),
50 RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x82), RGB(0xFF, 0x00, 0x41),
51 RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x82, 0x00), RGB(0xFF, 0xBE, 0x00),
52 RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x82, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00),
53 RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x82), RGB(0x00, 0xFF, 0xBE),
54 RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x82, 0xFF), RGB(0x00, 0x41, 0xFF),
55 RGB(0x82, 0x82, 0xFF), RGB(0x9E, 0x82, 0xFF), RGB(0xBE, 0x82, 0xFF), RGB(0xDF, 0x82, 0xFF),
56 RGB(0xFF, 0x82, 0xFF), RGB(0xFF, 0x82, 0xDF), RGB(0xFF, 0x82, 0xBE), RGB(0xFF, 0x82, 0x9E),
57 RGB(0xFF, 0x82, 0x82), RGB(0xFF, 0x9E, 0x82), RGB(0xFF, 0xBE, 0x82), RGB(0xFF, 0xDF, 0x82),
58 RGB(0xFF, 0xFF, 0x82), RGB(0xDF, 0xFF, 0x82), RGB(0xBE, 0xFF, 0x82), RGB(0x9E, 0xFF, 0x82),
59 RGB(0x82, 0xFF, 0x82), RGB(0x82, 0xFF, 0x9E), RGB(0x82, 0xFF, 0xBE), RGB(0x82, 0xFF, 0xDF),
60 RGB(0x82, 0xFF, 0xFF), RGB(0x82, 0xDF, 0xFF), RGB(0x82, 0xBE, 0xFF), RGB(0x82, 0x9E, 0xFF),
61 RGB(0xBA, 0xBA, 0xFF), RGB(0xCA, 0xBA, 0xFF), RGB(0xDF, 0xBA, 0xFF), RGB(0xEF, 0xBA, 0xFF),
62 RGB(0xFF, 0xBA, 0xFF), RGB(0xFF, 0xBA, 0xEF), RGB(0xFF, 0xBA, 0xDF), RGB(0xFF, 0xBA, 0xCA),
63 RGB(0xFF, 0xBA, 0xBA), RGB(0xFF, 0xCA, 0xBA), RGB(0xFF, 0xDF, 0xBA), RGB(0xFF, 0xEF, 0xBA),
64 RGB(0xFF, 0xFF, 0xBA), RGB(0xEF, 0xFF, 0xBA), RGB(0xDF, 0xFF, 0xBA), RGB(0xCA, 0xFF, 0xBA),
65 RGB(0xBA, 0xFF, 0xBA), RGB(0xBA, 0xFF, 0xCA), RGB(0xBA, 0xFF, 0xDF), RGB(0xBA, 0xFF, 0xEF),
66 RGB(0xBA, 0xFF, 0xFF), RGB(0xBA, 0xEF, 0xFF), RGB(0xBA, 0xDF, 0xFF), RGB(0xBA, 0xCA, 0xFF),
67 RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x39, 0x00, 0x71), RGB(0x55, 0x00, 0x71),
68 RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x39), RGB(0x71, 0x00, 0x1C),
69 RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x39, 0x00), RGB(0x71, 0x55, 0x00),
70 RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x39, 0x71, 0x00), RGB(0x1C, 0x71, 0x00),
71 RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x39), RGB(0x00, 0x71, 0x55),
72 RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x39, 0x71), RGB(0x00, 0x1C, 0x71),
73 RGB(0x39, 0x39, 0x71), RGB(0x45, 0x39, 0x71), RGB(0x55, 0x39, 0x71), RGB(0x61, 0x39, 0x71),
74 RGB(0x71, 0x39, 0x71), RGB(0x71, 0x39, 0x61), RGB(0x71, 0x39, 0x55), RGB(0x71, 0x39, 0x45),
75 RGB(0x71, 0x39, 0x39), RGB(0x71, 0x45, 0x39), RGB(0x71, 0x55, 0x39), RGB(0x71, 0x61, 0x39),
76 RGB(0x71, 0x71, 0x39), RGB(0x61, 0x71, 0x39), RGB(0x55, 0x71, 0x39), RGB(0x45, 0x71, 0x39),
77 RGB(0x39, 0x71, 0x39), RGB(0x39, 0x71, 0x45), RGB(0x39, 0x71, 0x55), RGB(0x39, 0x71, 0x61),
78 RGB(0x39, 0x71, 0x71), RGB(0x39, 0x61, 0x71), RGB(0x39, 0x55, 0x71), RGB(0x39, 0x45, 0x71),
79 RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71),
80 RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59),
81 RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51),
82 RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51),
83 RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69),
84 RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71),
85 RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x31, 0x00, 0x41),
86 RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x31), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10),
87 RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x31, 0x00),
88 RGB(0x41, 0x41, 0x00), RGB(0x31, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00),
89 RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x31),
90 RGB(0x00, 0x41, 0x41), RGB(0x00, 0x31, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41),
91 RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x31, 0x20, 0x41), RGB(0x39, 0x20, 0x41),
92 RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x39), RGB(0x41, 0x20, 0x31), RGB(0x41, 0x20, 0x28),
93 RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x31, 0x20), RGB(0x41, 0x39, 0x20),
94 RGB(0x41, 0x41, 0x20), RGB(0x39, 0x41, 0x20), RGB(0x31, 0x41, 0x20), RGB(0x28, 0x41, 0x20),
95 RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x31), RGB(0x20, 0x41, 0x39),
96 RGB(0x20, 0x41, 0x41), RGB(0x20, 0x39, 0x41), RGB(0x20, 0x31, 0x41), RGB(0x20, 0x28, 0x41),
97 RGB(0x2D, 0x2D, 0x41), RGB(0x31, 0x2D, 0x41), RGB(0x35, 0x2D, 0x41), RGB(0x3D, 0x2D, 0x41),
98 RGB(0x41, 0x2D, 0x41), RGB(0x41, 0x2D, 0x3D), RGB(0x41, 0x2D, 0x35), RGB(0x41, 0x2D, 0x31),
99 RGB(0x41, 0x2D, 0x2D), RGB(0x41, 0x31, 0x2D), RGB(0x41, 0x35, 0x2D), RGB(0x41, 0x3D, 0x2D),
100 RGB(0x41, 0x41, 0x2D), RGB(0x3D, 0x41, 0x2D), RGB(0x35, 0x41, 0x2D), RGB(0x31, 0x41, 0x2D),
101 RGB(0x2D, 0x41, 0x2D), RGB(0x2D, 0x41, 0x31), RGB(0x2D, 0x41, 0x35), RGB(0x2D, 0x41, 0x3D),
102 RGB(0x2D, 0x41, 0x41), RGB(0x2D, 0x3D, 0x41), RGB(0x2D, 0x35, 0x41), RGB(0x2D, 0x31, 0x41),
103 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00),
104 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00)
105 };
106
107 #elif defined(USE_DOSBOX_COLORS)
108
109 // DOSBox colors
110 static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] =
111 {
112 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA),
113 RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA),
114 RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF),
115 RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF),
116 RGB(0x00, 0x00, 0x00), RGB(0x14, 0x14, 0x14), RGB(0x20, 0x20, 0x20), RGB(0x2C, 0x2C, 0x2C),
117 RGB(0x38, 0x38, 0x38), RGB(0x45, 0x45, 0x45), RGB(0x51, 0x51, 0x51), RGB(0x61, 0x61, 0x61),
118 RGB(0x71, 0x71, 0x71), RGB(0x82, 0x82, 0x82), RGB(0x92, 0x92, 0x92), RGB(0xA2, 0xA2, 0xA2),
119 RGB(0xB6, 0xB6, 0xB6), RGB(0xCB, 0xCB, 0xCB), RGB(0xE3, 0xE3, 0xE3), RGB(0xFF, 0xFF, 0xFF),
120 RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x7D, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF),
121 RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x7D), RGB(0xFF, 0x00, 0x41),
122 RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x7D, 0x00), RGB(0xFF, 0xBE, 0x00),
123 RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x7D, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00),
124 RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x7D), RGB(0x00, 0xFF, 0xBE),
125 RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x7D, 0xFF), RGB(0x00, 0x41, 0xFF),
126 RGB(0x7D, 0x7D, 0xFF), RGB(0x9E, 0x7D, 0xFF), RGB(0xBE, 0x7D, 0xFF), RGB(0xDF, 0x7D, 0xFF),
127 RGB(0xFF, 0x7D, 0xFF), RGB(0xFF, 0x7D, 0xDF), RGB(0xFF, 0x7D, 0xBE), RGB(0xFF, 0x7D, 0x9E),
128
129 RGB(0xFF, 0x7D, 0x7D), RGB(0xFF, 0x9E, 0x7D), RGB(0xFF, 0xBE, 0x7D), RGB(0xFF, 0xDF, 0x7D),
130 RGB(0xFF, 0xFF, 0x7D), RGB(0xDF, 0xFF, 0x7D), RGB(0xBE, 0xFF, 0x7D), RGB(0x9E, 0xFF, 0x7D),
131 RGB(0x7D, 0xFF, 0x7D), RGB(0x7D, 0xFF, 0x9E), RGB(0x7D, 0xFF, 0xBE), RGB(0x7D, 0xFF, 0xDF),
132 RGB(0x7D, 0xFF, 0xFF), RGB(0x7D, 0xDF, 0xFF), RGB(0x7D, 0xBE, 0xFF), RGB(0x7D, 0x9E, 0xFF),
133 RGB(0xB6, 0xB6, 0xFF), RGB(0xC7, 0xB6, 0xFF), RGB(0xDB, 0xB6, 0xFF), RGB(0xEB, 0xB6, 0xFF),
134 RGB(0xFF, 0xB6, 0xFF), RGB(0xFF, 0xB6, 0xEB), RGB(0xFF, 0xB6, 0xDB), RGB(0xFF, 0xB6, 0xC7),
135 RGB(0xFF, 0xB6, 0xB6), RGB(0xFF, 0xC7, 0xB6), RGB(0xFF, 0xDB, 0xB6), RGB(0xFF, 0xEB, 0xB6),
136 RGB(0xFF, 0xFF, 0xB6), RGB(0xEB, 0xFF, 0xB6), RGB(0xDB, 0xFF, 0xB6), RGB(0xC7, 0xFF, 0xB6),
137 RGB(0xB6, 0xFF, 0xB6), RGB(0xB6, 0xFF, 0xC7), RGB(0xB6, 0xFF, 0xDB), RGB(0xB6, 0xFF, 0xEB),
138 RGB(0xB6, 0xFF, 0xFF), RGB(0xB6, 0xEB, 0xFF), RGB(0xB6, 0xDB, 0xFF), RGB(0xB6, 0xC7, 0xFF),
139 RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x38, 0x00, 0x71), RGB(0x55, 0x00, 0x71),
140 RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x38), RGB(0x71, 0x00, 0x1C),
141 RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x38, 0x00), RGB(0x71, 0x55, 0x00),
142 RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x38, 0x71, 0x00), RGB(0x1C, 0x71, 0x00),
143 RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x38), RGB(0x00, 0x71, 0x55),
144 RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x38, 0x71), RGB(0x00, 0x1C, 0x71),
145
146 RGB(0x38, 0x38, 0x71), RGB(0x45, 0x38, 0x71), RGB(0x55, 0x38, 0x71), RGB(0x61, 0x38, 0x71),
147 RGB(0x71, 0x38, 0x71), RGB(0x71, 0x38, 0x61), RGB(0x71, 0x38, 0x55), RGB(0x71, 0x38, 0x45),
148 RGB(0x71, 0x38, 0x38), RGB(0x71, 0x45, 0x38), RGB(0x71, 0x55, 0x38), RGB(0x71, 0x61, 0x38),
149 RGB(0x71, 0x71, 0x38), RGB(0x61, 0x71, 0x38), RGB(0x55, 0x71, 0x38), RGB(0x45, 0x71, 0x38),
150 RGB(0x38, 0x71, 0x38), RGB(0x38, 0x71, 0x45), RGB(0x38, 0x71, 0x55), RGB(0x38, 0x71, 0x61),
151 RGB(0x38, 0x71, 0x71), RGB(0x38, 0x61, 0x71), RGB(0x38, 0x55, 0x71), RGB(0x38, 0x45, 0x71),
152 RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71),
153 RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59),
154 RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51),
155 RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51),
156 RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69),
157 RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71),
158 RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x30, 0x00, 0x41),
159 RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x30), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10),
160 RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x30, 0x00),
161 RGB(0x41, 0x41, 0x00), RGB(0x30, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00),
162
163 RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x30),
164 RGB(0x00, 0x41, 0x41), RGB(0x00, 0x30, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41),
165 RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x30, 0x20, 0x41), RGB(0x38, 0x20, 0x41),
166 RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x38), RGB(0x41, 0x20, 0x30), RGB(0x41, 0x20, 0x28),
167 RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x30, 0x20), RGB(0x41, 0x38, 0x20),
168 RGB(0x41, 0x41, 0x20), RGB(0x38, 0x41, 0x20), RGB(0x30, 0x41, 0x20), RGB(0x28, 0x41, 0x20),
169 RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x30), RGB(0x20, 0x41, 0x38),
170 RGB(0x20, 0x41, 0x41), RGB(0x20, 0x38, 0x41), RGB(0x20, 0x30, 0x41), RGB(0x20, 0x28, 0x41),
171 RGB(0x2C, 0x2C, 0x41), RGB(0x30, 0x2C, 0x41), RGB(0x34, 0x2C, 0x41), RGB(0x3C, 0x2C, 0x41),
172 RGB(0x41, 0x2C, 0x41), RGB(0x41, 0x2C, 0x3C), RGB(0x41, 0x2C, 0x34), RGB(0x41, 0x2C, 0x30),
173 RGB(0x41, 0x2C, 0x2C), RGB(0x41, 0x30, 0x2C), RGB(0x41, 0x34, 0x2C), RGB(0x41, 0x3C, 0x2C),
174 RGB(0x41, 0x41, 0x2C), RGB(0x3C, 0x41, 0x2C), RGB(0x34, 0x41, 0x2C), RGB(0x30, 0x41, 0x2C),
175 RGB(0x2C, 0x41, 0x2C), RGB(0x2C, 0x41, 0x30), RGB(0x2C, 0x41, 0x34), RGB(0x2C, 0x41, 0x3C),
176 RGB(0x2C, 0x41, 0x41), RGB(0x2C, 0x3C, 0x41), RGB(0x2C, 0x34, 0x41), RGB(0x2C, 0x30, 0x41),
177 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00),
178 RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00)
179 };
180
181 #endif
182
183 /*
184 * Default 16-color palette for foreground and background
185 * (corresponding flags in comments).
186 * Taken from subsystems/win32/winsrv/consrv/frontends/gui/conwnd.c
187 */
188 static const COLORREF ConsoleColors[16] =
189 {
190 RGB(0, 0, 0), // (Black)
191 RGB(0, 0, 128), // BLUE
192 RGB(0, 128, 0), // GREEN
193 RGB(0, 128, 128), // BLUE | GREEN
194 RGB(128, 0, 0), // RED
195 RGB(128, 0, 128), // BLUE | RED
196 RGB(128, 128, 0), // GREEN | RED
197 RGB(192, 192, 192), // BLUE | GREEN | RED
198
199 RGB(128, 128, 128), // (Grey) INTENSITY
200 RGB(0, 0, 255), // BLUE | INTENSITY
201 RGB(0, 255, 0), // GREEN | INTENSITY
202 RGB(0, 255, 255), // BLUE | GREEN | INTENSITY
203 RGB(255, 0, 0), // RED | INTENSITY
204 RGB(255, 0, 255), // BLUE | RED | INTENSITY
205 RGB(255, 255, 0), // GREEN | RED | INTENSITY
206 RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY
207 };
208
209 /*
210 * Console interface -- VGA-mode-agnostic
211 */
212 typedef struct _CHAR_CELL
213 {
214 CHAR Char;
215 BYTE Attributes;
216 } CHAR_CELL, *PCHAR_CELL;
217 C_ASSERT(sizeof(CHAR_CELL) == 2);
218
219 static LPVOID ConsoleFramebuffer = NULL; // Active framebuffer, points to
220 // either TextFramebuffer or a
221 // valid graphics framebuffer.
222 static HPALETTE TextPaletteHandle = NULL;
223 static HPALETTE PaletteHandle = NULL;
224
225 static HANDLE StartEvent = NULL;
226 static HANDLE EndEvent = NULL;
227 static HANDLE AnotherEvent = NULL;
228
229 static CONSOLE_CURSOR_INFO OrgConsoleCursorInfo;
230 static CONSOLE_SCREEN_BUFFER_INFO OrgConsoleBufferInfo;
231
232
233 /*
234 * Text mode -- we always keep a valid text mode framebuffer
235 * even if we are in graphics mode. This is needed in order
236 * to keep a consistent VGA state.
237 */
238 static CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
239 static COORD TextResolution = {0};
240 static PCHAR_CELL TextFramebuffer = NULL;
241 static HANDLE TextConsoleBuffer = NULL;
242
243 /* Graphics mode */
244 static HANDLE GraphicsConsoleBuffer = NULL;
245 static HANDLE ConsoleMutex = NULL;
246 /* DoubleVision support */
247 static BOOLEAN DoubleWidth = FALSE;
248 static BOOLEAN DoubleHeight = FALSE;
249
250 static PHARDWARE_TIMER VSyncTimer;
251 static PHARDWARE_TIMER HSyncTimer;
252
253 /*
254 * VGA Hardware
255 */
256 static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
257
258 static BYTE VgaLatchRegisters[VGA_NUM_BANKS] = {0, 0, 0, 0};
259
260 static BYTE VgaMiscRegister;
261 static BYTE VgaFeatureRegister;
262
263 static BYTE VgaSeqIndex = VGA_SEQ_RESET_REG;
264 static BYTE VgaSeqRegisters[VGA_SEQ_MAX_REG];
265
266 static BYTE VgaCrtcIndex = VGA_CRTC_HORZ_TOTAL_REG;
267 static BYTE VgaCrtcRegisters[VGA_CRTC_MAX_REG];
268
269 static BYTE VgaGcIndex = VGA_GC_RESET_REG;
270 static BYTE VgaGcRegisters[VGA_GC_MAX_REG];
271
272 static BOOLEAN VgaAcLatch = FALSE;
273 static BOOLEAN VgaAcPalDisable = TRUE;
274 static BYTE VgaAcIndex = VGA_AC_PAL_0_REG;
275 static BYTE VgaAcRegisters[VGA_AC_MAX_REG];
276
277 static BYTE VgaDacMask = 0xFF;
278
279 static BOOLEAN VgaDacReadWrite = FALSE;
280 static WORD VgaDacIndex = 0;
281 static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
282
283 // static VGA_REGISTERS VgaRegisters;
284
285 static ULONGLONG VerticalRetraceCycle = 0ULL;
286 static ULONGLONG HorizontalRetraceCycle = 0ULL;
287
288 static BOOLEAN NeedsUpdate = FALSE;
289 static BOOLEAN ModeChanged = FALSE;
290 static BOOLEAN CursorChanged = FALSE;
291 static BOOLEAN PaletteChanged = FALSE;
292
293 typedef enum _SCREEN_MODE
294 {
295 TEXT_MODE,
296 GRAPHICS_MODE
297 } SCREEN_MODE, *PSCREEN_MODE;
298
299 static SCREEN_MODE ScreenMode = TEXT_MODE;
300 static COORD CurrResolution = {0};
301
302 static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
303
304 /* RegisterConsoleVDM EMULATION ***********************************************/
305
306 #include <ntddvdeo.h>
307
308 #ifdef USE_REAL_REGISTERCONSOLEVDM
309
310 #define __RegisterConsoleVDM RegisterConsoleVDM
311 #define __InvalidateConsoleDIBits InvalidateConsoleDIBits
312
313 #else
314
315 /*
316 * This private buffer, per-console, is used by
317 * RegisterConsoleVDM and InvalidateConsoleDIBits.
318 */
319 static COORD VDMBufferSize = {0};
320 static PCHAR_CELL VDMBuffer = NULL;
321
322 static PCHAR_INFO CharBuff = NULL; // This is a hack, which is unneeded
323 // for the real RegisterConsoleVDM and
324 // InvalidateConsoleDIBits
325
326 BOOL
327 WINAPI
328 __RegisterConsoleVDM(IN DWORD dwRegisterFlags,
329 IN HANDLE hStartHardwareEvent,
330 IN HANDLE hEndHardwareEvent,
331 IN HANDLE hErrorHardwareEvent,
332 IN DWORD dwUnusedVar,
333 OUT LPDWORD lpVideoStateLength,
334 OUT PVOID* lpVideoState, // PVIDEO_HARDWARE_STATE_HEADER*
335 IN PVOID lpUnusedBuffer,
336 IN DWORD dwUnusedBufferLength,
337 IN COORD dwVDMBufferSize,
338 OUT PVOID* lpVDMBuffer)
339 {
340 UNREFERENCED_PARAMETER(hErrorHardwareEvent);
341 UNREFERENCED_PARAMETER(dwUnusedVar);
342 UNREFERENCED_PARAMETER(lpVideoStateLength);
343 UNREFERENCED_PARAMETER(lpVideoState);
344 UNREFERENCED_PARAMETER(lpUnusedBuffer);
345 UNREFERENCED_PARAMETER(dwUnusedBufferLength);
346
347 SetLastError(0);
348 DPRINT1("__RegisterConsoleVDM(%d)\n", dwRegisterFlags);
349
350 if (lpVDMBuffer == NULL) return FALSE;
351
352 if (dwRegisterFlags != 0)
353 {
354 // if (hStartHardwareEvent == NULL || hEndHardwareEvent == NULL) return FALSE;
355 if (VDMBuffer != NULL) return FALSE;
356
357 VDMBufferSize = dwVDMBufferSize;
358
359 /* HACK: Cache -- to be removed in the real implementation */
360 CharBuff = RtlAllocateHeap(RtlGetProcessHeap(),
361 HEAP_ZERO_MEMORY,
362 VDMBufferSize.X * VDMBufferSize.Y
363 * sizeof(*CharBuff));
364 ASSERT(CharBuff);
365
366 VDMBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
367 HEAP_ZERO_MEMORY,
368 VDMBufferSize.X * VDMBufferSize.Y
369 * sizeof(*VDMBuffer));
370 *lpVDMBuffer = VDMBuffer;
371 return (VDMBuffer != NULL);
372 }
373 else
374 {
375 /* HACK: Cache -- to be removed in the real implementation */
376 if (CharBuff) RtlFreeHeap(RtlGetProcessHeap(), 0, CharBuff);
377 CharBuff = NULL;
378
379 if (VDMBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, VDMBuffer);
380 VDMBuffer = NULL;
381
382 VDMBufferSize.X = VDMBufferSize.Y = 0;
383
384 return TRUE;
385 }
386 }
387
388 BOOL
389 __InvalidateConsoleDIBits(IN HANDLE hConsoleOutput,
390 IN PSMALL_RECT lpRect)
391 {
392 if ((hConsoleOutput == TextConsoleBuffer) && (VDMBuffer != NULL))
393 {
394 /* HACK: Write the cached data to the console */
395
396 COORD Origin = { lpRect->Left, lpRect->Top };
397 SHORT i, j;
398
399 ASSERT(CharBuff);
400
401 for (i = 0; i < VDMBufferSize.Y; i++)
402 {
403 for (j = 0; j < VDMBufferSize.X; j++)
404 {
405 CharBuff[i * VDMBufferSize.X + j].Char.AsciiChar = VDMBuffer[i * VDMBufferSize.X + j].Char;
406 CharBuff[i * VDMBufferSize.X + j].Attributes = VDMBuffer[i * VDMBufferSize.X + j].Attributes;
407 }
408 }
409
410 WriteConsoleOutputA(hConsoleOutput,
411 CharBuff,
412 VDMBufferSize,
413 Origin,
414 lpRect);
415 }
416
417 return InvalidateConsoleDIBits(hConsoleOutput, lpRect);
418 }
419
420 #endif
421
422 /* PRIVATE FUNCTIONS **********************************************************/
423
424 static inline DWORD VgaGetAddressSize(VOID);
425 static VOID VgaUpdateTextCursor(VOID);
426
427 static inline DWORD VgaGetVideoBaseAddress(VOID)
428 {
429 return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
430 }
431
432 static VOID VgaUpdateCursorPosition(VOID)
433 {
434 /*
435 * Update the cursor position in the VGA registers.
436 */
437 WORD Offset = ConsoleInfo.dwCursorPosition.Y * TextResolution.X +
438 ConsoleInfo.dwCursorPosition.X;
439
440 VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG] = LOBYTE(Offset);
441 VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG] = HIBYTE(Offset);
442
443 // VidBiosSyncCursorPosition();
444 VgaUpdateTextCursor();
445 }
446
447 static BOOL VgaAttachToConsoleInternal(PCOORD Resolution)
448 {
449 BOOL Success;
450 ULONG Length = 0;
451 PVIDEO_HARDWARE_STATE_HEADER State;
452
453 #ifdef USE_REAL_REGISTERCONSOLEVDM
454 PCHAR_INFO CharBuff = NULL;
455 #endif
456 SHORT i, j;
457 DWORD AddressSize, ScanlineSize;
458 DWORD Address = 0;
459 DWORD CurrentAddr;
460 SMALL_RECT ConRect;
461 COORD Origin = { 0, 0 };
462
463 ASSERT(TextFramebuffer == NULL);
464
465 TextResolution = *Resolution;
466
467 /*
468 * Windows 2k3 winsrv.dll calls NtVdmControl(VdmQueryVdmProcess == 14, &ConsoleHandle);
469 * in the two following APIs:
470 * SrvRegisterConsoleVDM (corresponding Win32 API: RegisterConsoleVDM)
471 * SrvVDMConsoleOperation (corresponding Win32 API: )
472 * to check whether the current process is a VDM process, and fails otherwise
473 * with the error 0xC0000022 (STATUS_ACCESS_DENIED).
474 *
475 * It is worth it to notice that also basesrv.dll does the same only for the
476 * BaseSrvIsFirstVDM API...
477 */
478
479 Success =
480 __RegisterConsoleVDM(1,
481 StartEvent,
482 EndEvent,
483 AnotherEvent, // NULL,
484 0,
485 &Length, // NULL, <-- putting this (and null in the next var) makes the API returning error 12 "ERROR_INVALID_ACCESS"
486 (PVOID*)&State, // NULL,
487 NULL,
488 0,
489 TextResolution,
490 (PVOID*)&TextFramebuffer);
491 if (!Success)
492 {
493 DisplayMessage(L"RegisterConsoleVDM failed with error %d\n", GetLastError());
494 EmulatorTerminate();
495 return FALSE;
496 }
497
498 #ifdef USE_REAL_REGISTERCONSOLEVDM
499 CharBuff = RtlAllocateHeap(RtlGetProcessHeap(),
500 HEAP_ZERO_MEMORY,
501 TextResolution.X * TextResolution.Y
502 * sizeof(*CharBuff));
503 ASSERT(CharBuff);
504 #endif
505
506 /*
507 * Resize the console
508 */
509 ConRect.Left = 0;
510 ConRect.Top = ConsoleInfo.srWindow.Top;
511 ConRect.Right = ConRect.Left + Resolution->X - 1;
512 ConRect.Bottom = ConRect.Top + Resolution->Y - 1;
513 /*
514 * Use this trick to effectively resize the console buffer and window,
515 * because:
516 * - SetConsoleScreenBufferSize fails if the new console screen buffer size
517 * is smaller than the current console window size, and:
518 * - SetConsoleWindowInfo fails if the new console window size is larger
519 * than the current console screen buffer size.
520 */
521 SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
522 SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect);
523 SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
524 /* Update the saved console information */
525 GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
526
527 /*
528 * Copy console data into VGA memory
529 */
530
531 /* Get the data */
532 AddressSize = VgaGetAddressSize();
533 ConRect.Left = ConRect.Top = 0;
534 ConRect.Right = TextResolution.X;
535 ConRect.Bottom = TextResolution.Y;
536 ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
537
538 /* Read the data from the console into the framebuffer... */
539 ReadConsoleOutputA(TextConsoleBuffer,
540 CharBuff,
541 TextResolution,
542 Origin,
543 &ConRect);
544
545 /* ... and copy the framebuffer into the VGA memory */
546
547 /* Loop through the scanlines */
548 for (i = 0; i < TextResolution.Y; i++)
549 {
550 /* Loop through the characters */
551 for (j = 0; j < TextResolution.X; j++)
552 {
553 CurrentAddr = LOWORD((Address + j) * AddressSize);
554
555 /* Store the character in plane 0 */
556 VgaMemory[CurrentAddr] = CharBuff[i * TextResolution.X + j].Char.AsciiChar;
557
558 /* Store the attribute in plane 1 */
559 VgaMemory[CurrentAddr + VGA_BANK_SIZE] = (BYTE)CharBuff[i * TextResolution.X + j].Attributes;
560 }
561
562 /* Move to the next scanline */
563 Address += ScanlineSize;
564 }
565
566 #ifdef USE_REAL_REGISTERCONSOLEVDM
567 if (CharBuff) RtlFreeHeap(RtlGetProcessHeap(), 0, CharBuff);
568 #endif
569
570 VgaUpdateCursorPosition();
571
572 return TRUE;
573 }
574
575 static BOOL IsConsoleHandle(HANDLE hHandle)
576 {
577 DWORD dwMode;
578
579 /* Check whether the handle may be that of a console... */
580 if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR)
581 return FALSE;
582
583 /*
584 * It may be. Perform another test... The idea comes from the
585 * MSDN description of the WriteConsole API:
586 *
587 * "WriteConsole fails if it is used with a standard handle
588 * that is redirected to a file. If an application processes
589 * multilingual output that can be redirected, determine whether
590 * the output handle is a console handle (one method is to call
591 * the GetConsoleMode function and check whether it succeeds).
592 * If the handle is a console handle, call WriteConsole. If the
593 * handle is not a console handle, the output is redirected and
594 * you should call WriteFile to perform the I/O."
595 */
596 return GetConsoleMode(hHandle, &dwMode);
597 }
598
599 static inline DWORD VgaGetAddressSize(VOID)
600 {
601 if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
602 {
603 /* Double-word addressing */
604 return 4; // sizeof(DWORD)
605 }
606 else if (VgaCrtcRegisters[VGA_CRTC_MODE_CONTROL_REG] & VGA_CRTC_MODE_CONTROL_BYTE)
607 {
608 /* Byte addressing */
609 return 1; // sizeof(BYTE)
610 }
611 else
612 {
613 /* Word addressing */
614 return 2; // sizeof(WORD)
615 }
616 }
617
618 static inline DWORD VgaTranslateReadAddress(DWORD Address)
619 {
620 DWORD Offset = LOWORD(Address - VgaGetVideoBaseAddress());
621 BYTE Plane;
622
623 /* Check for chain-4 and odd-even mode */
624 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
625 {
626 /* The lowest two bits are the plane number */
627 Plane = Offset & 0x03;
628 Offset &= ~3;
629 }
630 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
631 {
632 /* The LSB is the plane number */
633 Plane = Offset & 0x01;
634 Offset &= ~1;
635 }
636 else
637 {
638 /* Use the read mode */
639 Plane = VgaGcRegisters[VGA_GC_READ_MAP_SEL_REG] & 0x03;
640 }
641
642 /* Return the offset on plane 0 for read mode 1 */
643 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_READ) return Offset;
644 else return Offset + Plane * VGA_BANK_SIZE;
645 }
646
647 static inline DWORD VgaTranslateWriteAddress(DWORD Address)
648 {
649 DWORD Offset = LOWORD(Address - VgaGetVideoBaseAddress());
650
651 /* Check for chain-4 and odd-even mode */
652 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
653 {
654 /* Clear the lowest two bits since they're used to select the bank */
655 Offset &= ~3;
656 }
657 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
658 {
659 /* Clear the lowest bit since it's used to select odd/even */
660 Offset &= ~1;
661 }
662
663 /* Return the offset on plane 0 */
664 return Offset;
665 }
666
667 static inline BYTE VgaTranslateByteForWriting(BYTE Data, BYTE Plane)
668 {
669 BYTE WriteMode = VgaGcRegisters[VGA_GC_MODE_REG] & 0x03;
670 BYTE BitMask = VgaGcRegisters[VGA_GC_BITMASK_REG];
671
672 if (WriteMode == 1)
673 {
674 /* In write mode 1 just return the latch register */
675 return VgaLatchRegisters[Plane];
676 }
677
678 if (WriteMode != 2)
679 {
680 /* Write modes 0 and 3 rotate the data to the right first */
681 BYTE RotateCount = VgaGcRegisters[VGA_GC_ROTATE_REG] & 0x07;
682 Data = LOBYTE(((DWORD)Data >> RotateCount) | ((DWORD)Data << (8 - RotateCount)));
683 }
684 else
685 {
686 /* Write mode 2 expands the appropriate bit to all 8 bits */
687 Data = (Data & (1 << Plane)) ? 0xFF : 0x00;
688 }
689
690 if (WriteMode == 0)
691 {
692 /*
693 * In write mode 0, the enable set/reset register decides if the
694 * set/reset bit should be expanded to all 8 bits.
695 */
696 if (VgaGcRegisters[VGA_GC_ENABLE_RESET_REG] & (1 << Plane))
697 {
698 /* Copy the bit from the set/reset register to all 8 bits */
699 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
700 }
701 }
702
703 if (WriteMode != 3)
704 {
705 /* Write modes 0 and 2 then perform a logical operation on the data and latch */
706 BYTE LogicalOperation = (VgaGcRegisters[VGA_GC_ROTATE_REG] >> 3) & 0x03;
707
708 if (LogicalOperation == 1) Data &= VgaLatchRegisters[Plane];
709 else if (LogicalOperation == 2) Data |= VgaLatchRegisters[Plane];
710 else if (LogicalOperation == 3) Data ^= VgaLatchRegisters[Plane];
711 }
712 else
713 {
714 /* For write mode 3, we AND the bitmask with the data, which is used as the new bitmask */
715 BitMask &= Data;
716
717 /* Then we expand the bit in the set/reset field */
718 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
719 }
720
721 /* Bits cleared in the bitmask are replaced with latch register bits */
722 Data = (Data & BitMask) | (VgaLatchRegisters[Plane] & (~BitMask));
723
724 /* Return the byte */
725 return Data;
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 VgaRestoreDefaultPalette(PPALETTEENTRY Entries, USHORT NumOfEntries)
748 {
749 USHORT i;
750
751 /* Copy the colors of the default palette to the DAC and console palette */
752 for (i = 0; i < NumOfEntries; i++)
753 {
754 /* Set the palette entries */
755 Entries[i].peRed = GetRValue(VgaDefaultPalette[i]);
756 Entries[i].peGreen = GetGValue(VgaDefaultPalette[i]);
757 Entries[i].peBlue = GetBValue(VgaDefaultPalette[i]);
758 Entries[i].peFlags = 0;
759
760 /* Set the DAC registers */
761 VgaDacRegisters[i * 3] = VGA_COLOR_TO_DAC(GetRValue(VgaDefaultPalette[i]));
762 VgaDacRegisters[i * 3 + 1] = VGA_COLOR_TO_DAC(GetGValue(VgaDefaultPalette[i]));
763 VgaDacRegisters[i * 3 + 2] = VGA_COLOR_TO_DAC(GetBValue(VgaDefaultPalette[i]));
764 }
765 }
766
767 static BOOLEAN VgaInitializePalette(VOID)
768 {
769 UINT i;
770 BOOLEAN Result = FALSE;
771 LPLOGPALETTE Palette, TextPalette;
772
773 /* Allocate storage space for the palettes */
774 Palette = RtlAllocateHeap(RtlGetProcessHeap(),
775 HEAP_ZERO_MEMORY,
776 sizeof(LOGPALETTE) +
777 VGA_MAX_COLORS * sizeof(PALETTEENTRY));
778 TextPalette = RtlAllocateHeap(RtlGetProcessHeap(),
779 HEAP_ZERO_MEMORY,
780 sizeof(LOGPALETTE) +
781 (VGA_AC_PAL_F_REG + 1) * sizeof(PALETTEENTRY));
782 if ((Palette == NULL) || (TextPalette == NULL)) goto Cleanup;
783
784 /* Initialize the palettes */
785 Palette->palVersion = TextPalette->palVersion = 0x0300;
786 Palette->palNumEntries = VGA_MAX_COLORS;
787 TextPalette->palNumEntries = VGA_AC_PAL_F_REG + 1;
788
789 /* Restore the default graphics palette */
790 VgaRestoreDefaultPalette(Palette->palPalEntry, Palette->palNumEntries);
791
792 /* Set the default text palette */
793 for (i = 0; i < TextPalette->palNumEntries; i++)
794 {
795 /* Set the palette entries */
796 TextPalette->palPalEntry[i].peRed = GetRValue(ConsoleColors[i]);
797 TextPalette->palPalEntry[i].peGreen = GetGValue(ConsoleColors[i]);
798 TextPalette->palPalEntry[i].peBlue = GetBValue(ConsoleColors[i]);
799 TextPalette->palPalEntry[i].peFlags = 0;
800 }
801
802 /* Create the palettes */
803 PaletteHandle = CreatePalette(Palette);
804 TextPaletteHandle = CreatePalette(TextPalette);
805
806 if (PaletteHandle != NULL && TextPaletteHandle != NULL)
807 {
808 /* The palettes have been created successfully */
809 Result = TRUE;
810 }
811
812 Cleanup:
813 /* Free the palettes */
814 if (Palette) RtlFreeHeap(RtlGetProcessHeap(), 0, Palette);
815 if (TextPalette) RtlFreeHeap(RtlGetProcessHeap(), 0, TextPalette);
816
817 if (!Result)
818 {
819 /* Something failed, delete the palettes */
820 if (PaletteHandle) DeleteObject(PaletteHandle);
821 if (TextPaletteHandle) DeleteObject(TextPaletteHandle);
822 }
823
824 return Result;
825 }
826
827 static VOID VgaResetPalette(VOID)
828 {
829 PALETTEENTRY Entries[VGA_MAX_COLORS];
830
831 /* Restore the default palette */
832 VgaRestoreDefaultPalette(Entries, VGA_MAX_COLORS);
833 SetPaletteEntries(PaletteHandle, 0, VGA_MAX_COLORS, Entries);
834 PaletteChanged = TRUE;
835 }
836
837 static VOID VgaSetActiveScreenBuffer(HANDLE ScreenBuffer)
838 {
839 /* Set the active buffer */
840 SetConsoleActiveScreenBuffer(ScreenBuffer);
841
842 /* Reinitialize the VDM menu */
843 DestroyVdmMenu();
844 CreateVdmMenu(ScreenBuffer);
845 }
846
847 static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
848 {
849 DWORD i;
850 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
851 BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
852 LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
853 LPWORD PaletteIndex = (LPWORD)(BitmapInfo->bmiColors);
854
855 LONG Width = Resolution->X;
856 LONG Height = Resolution->Y;
857
858 /* Use DoubleVision mode if the resolution is too small */
859 DoubleWidth = (Width < VGA_MINIMUM_WIDTH);
860 if (DoubleWidth) Width *= 2;
861 DoubleHeight = (Height < VGA_MINIMUM_HEIGHT);
862 if (DoubleHeight) Height *= 2;
863
864 /* Fill the bitmap info header */
865 RtlZeroMemory(&BitmapInfo->bmiHeader, sizeof(BitmapInfo->bmiHeader));
866 BitmapInfo->bmiHeader.biSize = sizeof(BitmapInfo->bmiHeader);
867 BitmapInfo->bmiHeader.biWidth = Width;
868 BitmapInfo->bmiHeader.biHeight = Height;
869 BitmapInfo->bmiHeader.biBitCount = 8;
870 BitmapInfo->bmiHeader.biPlanes = 1;
871 BitmapInfo->bmiHeader.biCompression = BI_RGB;
872 BitmapInfo->bmiHeader.biSizeImage = Width * Height /* * 1 == biBitCount / 8 */;
873
874 /* Fill the palette data */
875 for (i = 0; i < (VGA_PALETTE_SIZE / 3); i++) PaletteIndex[i] = (WORD)i;
876
877 /* Fill the console graphics buffer info */
878 GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE;
879 GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
880 GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
881
882 /* Create the buffer */
883 GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
884 FILE_SHARE_READ | FILE_SHARE_WRITE,
885 NULL,
886 CONSOLE_GRAPHICS_BUFFER,
887 &GraphicsBufferInfo);
888 if (GraphicsConsoleBuffer == INVALID_HANDLE_VALUE) return FALSE;
889
890 /* Save the framebuffer address and mutex */
891 ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap;
892 ConsoleMutex = GraphicsBufferInfo.hMutex;
893
894 /* Clear the framebuffer */
895 RtlZeroMemory(ConsoleFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
896
897 /* Set the active buffer */
898 VgaSetActiveScreenBuffer(GraphicsConsoleBuffer);
899
900 /* Set the graphics mode palette */
901 SetConsolePalette(GraphicsConsoleBuffer,
902 PaletteHandle,
903 SYSPAL_NOSTATIC256);
904
905 /* Set the screen mode flag */
906 ScreenMode = GRAPHICS_MODE;
907
908 return TRUE;
909 }
910
911 static VOID VgaLeaveGraphicsMode(VOID)
912 {
913 /* Release the console framebuffer mutex */
914 ReleaseMutex(ConsoleMutex);
915
916 /* Switch back to the default console text buffer */
917 // VgaSetActiveScreenBuffer(TextConsoleBuffer);
918
919 /* Cleanup the video data */
920 CloseHandle(ConsoleMutex);
921 ConsoleMutex = NULL;
922 ConsoleFramebuffer = NULL;
923 CloseHandle(GraphicsConsoleBuffer);
924 GraphicsConsoleBuffer = NULL;
925
926 DoubleWidth = FALSE;
927 DoubleHeight = FALSE;
928 }
929
930 static BOOL VgaEnterTextMode(PCOORD Resolution)
931 {
932 /* Switch to the text buffer */
933 VgaSetActiveScreenBuffer(TextConsoleBuffer);
934
935 /* Adjust the text framebuffer if we changed the resolution */
936 if (TextResolution.X != Resolution->X ||
937 TextResolution.Y != Resolution->Y)
938 {
939 VgaDetachFromConsole(TRUE);
940
941 /*
942 * VgaAttachToConsoleInternal sets TextResolution to the
943 * new resolution and updates ConsoleInfo.
944 */
945 if (!VgaAttachToConsoleInternal(Resolution))
946 {
947 DisplayMessage(L"An unexpected error occurred!\n");
948 EmulatorTerminate();
949 return FALSE;
950 }
951 }
952 else
953 {
954 VgaUpdateCursorPosition();
955 }
956
957 /* The active framebuffer is now the text framebuffer */
958 ConsoleFramebuffer = TextFramebuffer;
959
960 /*
961 * Set the text mode palette.
962 *
963 * WARNING: This call should fail on Windows (and therefore
964 * we get the default palette and our external behaviour is
965 * just like Windows' one), but it should success on ReactOS
966 * (so that we get console palette changes even for text-mode
967 * screen-buffers, which is a new feature on ReactOS).
968 */
969 SetConsolePalette(TextConsoleBuffer,
970 TextPaletteHandle,
971 SYSPAL_NOSTATIC256);
972
973 /* Set the screen mode flag */
974 ScreenMode = TEXT_MODE;
975
976 return TRUE;
977 }
978
979 static VOID VgaLeaveTextMode(VOID)
980 {
981 /* Reset the active framebuffer */
982 ConsoleFramebuffer = NULL;
983 }
984
985 static VOID VgaChangeMode(VOID)
986 {
987 COORD NewResolution = VgaGetDisplayResolution();
988 SCREEN_MODE NewScreenMode =
989 !(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA) ? TEXT_MODE
990 : GRAPHICS_MODE;
991
992 /*
993 * No need to switch to a different screen mode + resolution
994 * if the new ones are the same as the old ones.
995 */
996 if ((ScreenMode == NewScreenMode) &&
997 (CurrResolution.X == NewResolution.X && CurrResolution.Y == NewResolution.Y))
998 {
999 goto Quit;
1000 }
1001
1002 if (ScreenMode == GRAPHICS_MODE)
1003 {
1004 /* Leave the current graphics mode */
1005 VgaLeaveGraphicsMode();
1006 }
1007 else
1008 {
1009 /* Leave the current text mode */
1010 VgaLeaveTextMode();
1011 }
1012
1013 /* Update the current resolution */
1014 CurrResolution = NewResolution;
1015
1016 /* The new screen mode will be updated via the VgaEnterText/GraphicsMode functions */
1017
1018 /* Check if the new mode is alphanumeric */
1019 if (NewScreenMode == TEXT_MODE)
1020 {
1021 /* Enter new text mode */
1022 if (!VgaEnterTextMode(&CurrResolution))
1023 {
1024 DisplayMessage(L"An unexpected VGA error occurred while switching into text mode. Error: %u", GetLastError());
1025 EmulatorTerminate();
1026 return;
1027 }
1028 }
1029 else
1030 {
1031 /* Enter graphics mode */
1032 if (!VgaEnterGraphicsMode(&CurrResolution))
1033 {
1034 DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode. Error: %u", GetLastError());
1035 EmulatorTerminate();
1036 return;
1037 }
1038 }
1039
1040 Quit:
1041
1042 /* Trigger a full update of the screen */
1043 NeedsUpdate = TRUE;
1044 UpdateRectangle.Left = 0;
1045 UpdateRectangle.Top = 0;
1046 UpdateRectangle.Right = CurrResolution.X;
1047 UpdateRectangle.Bottom = CurrResolution.Y;
1048
1049 /* Reset the mode change flag */
1050 ModeChanged = FALSE;
1051 }
1052
1053 static VOID VgaUpdateFramebuffer(VOID)
1054 {
1055 SHORT i, j, k;
1056 DWORD AddressSize = VgaGetAddressSize();
1057 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
1058 BYTE PresetRowScan = VgaCrtcRegisters[VGA_CRTC_PRESET_ROW_SCAN_REG] & 0x1F;
1059 DWORD Address = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG],
1060 VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG])
1061 + PresetRowScan * ScanlineSize
1062 + ((VgaCrtcRegisters[VGA_CRTC_PRESET_ROW_SCAN_REG] >> 5) & 3);
1063
1064 /*
1065 * If console framebuffer is NULL, that means something went wrong
1066 * earlier and this is the final display refresh.
1067 */
1068 if (ConsoleFramebuffer == NULL) return;
1069
1070 /* Check if we are in text or graphics mode */
1071 if (ScreenMode == GRAPHICS_MODE)
1072 {
1073 /* Graphics mode */
1074 PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
1075 DWORD InterlaceHighBit = VGA_INTERLACE_HIGH_BIT;
1076 SHORT X;
1077
1078 /*
1079 * Synchronize access to the graphics framebuffer
1080 * with the console framebuffer mutex.
1081 */
1082 WaitForSingleObject(ConsoleMutex, INFINITE);
1083
1084 /* Shift the high bit right by 1 in odd/even mode */
1085 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
1086 {
1087 InterlaceHighBit >>= 1;
1088 }
1089
1090 /* Loop through the scanlines */
1091 for (i = 0; i < CurrResolution.Y; i++)
1092 {
1093 if ((VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) && (i & 1))
1094 {
1095 /* Odd-numbered line in interlaced mode - set the high bit */
1096 Address |= InterlaceHighBit;
1097 }
1098
1099 /* Loop through the pixels */
1100 for (j = 0; j < CurrResolution.X; j++)
1101 {
1102 BYTE PixelData = 0;
1103
1104 /* Apply horizontal pixel panning */
1105 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
1106 {
1107 X = j + ((VgaAcRegisters[VGA_AC_HORZ_PANNING_REG] & 0x0F) >> 1);
1108 }
1109 else
1110 {
1111 X = j + (VgaAcRegisters[VGA_AC_HORZ_PANNING_REG] & 0x0F);
1112 }
1113
1114 /* Check the shifting mode */
1115 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFT256)
1116 {
1117 /* 4 bits shifted from each plane */
1118
1119 /* Check if this is 16 or 256 color mode */
1120 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
1121 {
1122 /* One byte per pixel */
1123 PixelData = VgaMemory[(X % VGA_NUM_BANKS) * VGA_BANK_SIZE
1124 + LOWORD((Address + (X / VGA_NUM_BANKS))
1125 * AddressSize)];
1126 }
1127 else
1128 {
1129 /* 4-bits per pixel */
1130
1131 PixelData = VgaMemory[(X % VGA_NUM_BANKS) * VGA_BANK_SIZE
1132 + LOWORD((Address + (X / (VGA_NUM_BANKS * 2)))
1133 * AddressSize)];
1134
1135 /* Check if we should use the highest 4 bits or lowest 4 */
1136 if (((X / VGA_NUM_BANKS) % 2) == 0)
1137 {
1138 /* Highest 4 */
1139 PixelData >>= 4;
1140 }
1141 else
1142 {
1143 /* Lowest 4 */
1144 PixelData &= 0x0F;
1145 }
1146 }
1147 }
1148 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFTREG)
1149 {
1150 /* Check if this is 16 or 256 color mode */
1151 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
1152 {
1153 // TODO: NOT IMPLEMENTED
1154 DPRINT1("8-bit interleaved mode is not implemented!\n");
1155 }
1156 else
1157 {
1158 /*
1159 * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
1160 * then 2 bits shifted from plane 1 and 3 for the next 4
1161 */
1162 DWORD BankNumber = (X / 4) % 2;
1163 DWORD Offset = Address + (X / 8);
1164 BYTE LowPlaneData = VgaMemory[BankNumber * VGA_BANK_SIZE + LOWORD(Offset * AddressSize)];
1165 BYTE HighPlaneData = VgaMemory[(BankNumber + 2) * VGA_BANK_SIZE + LOWORD(Offset * AddressSize)];
1166
1167 /* Extract the two bits from each plane */
1168 LowPlaneData = (LowPlaneData >> (6 - ((X % 4) * 2))) & 0x03;
1169 HighPlaneData = (HighPlaneData >> (6 - ((X % 4) * 2))) & 0x03;
1170
1171 /* Combine them into the pixel */
1172 PixelData = LowPlaneData | (HighPlaneData << 2);
1173 }
1174 }
1175 else
1176 {
1177 /* 1 bit shifted from each plane */
1178
1179 /* Check if this is 16 or 256 color mode */
1180 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
1181 {
1182 /* 8 bits per pixel, 2 on each plane */
1183
1184 for (k = 0; k < VGA_NUM_BANKS; k++)
1185 {
1186 /* The data is on plane k, 4 pixels per byte */
1187 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
1188 + LOWORD((Address + (X / VGA_NUM_BANKS))
1189 * AddressSize)];
1190
1191 /* The mask of the first bit in the pair */
1192 BYTE BitMask = 1 << (((3 - (X % VGA_NUM_BANKS)) * 2) + 1);
1193
1194 /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
1195 if (PlaneData & BitMask) PixelData |= 1 << k;
1196
1197 /* Bits 4, 5, 6 and 7 come from the second bit of the pair */
1198 if (PlaneData & (BitMask >> 1)) PixelData |= 1 << (k + 4);
1199 }
1200 }
1201 else
1202 {
1203 /* 4 bits per pixel, 1 on each plane */
1204
1205 for (k = 0; k < VGA_NUM_BANKS; k++)
1206 {
1207 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
1208 + LOWORD((Address + (X / (VGA_NUM_BANKS * 2)))
1209 * AddressSize)];
1210
1211 /* If the bit on that plane is set, set it */
1212 if (PlaneData & (1 << (7 - (X % 8)))) PixelData |= 1 << k;
1213 }
1214 }
1215 }
1216
1217 if (!(VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT))
1218 {
1219 /*
1220 * In 16 color mode, the value is an index to the AC registers
1221 * if external palette access is disabled, otherwise (in case
1222 * of palette loading) it is a blank pixel.
1223 */
1224 PixelData = (VgaAcPalDisable ? VgaAcRegisters[PixelData & 0x0F]
1225 : 0);
1226 }
1227
1228 /* Take into account DoubleVision mode when checking for pixel updates */
1229 if (DoubleWidth && DoubleHeight)
1230 {
1231 /* Now check if the resulting pixel data has changed */
1232 if (GraphicsBuffer[(i * 2 * CurrResolution.X * 2) + (j * 2)] != PixelData)
1233 {
1234 /* Yes, write the new value */
1235 GraphicsBuffer[(i * 2 * CurrResolution.X * 2) + (j * 2)] = PixelData;
1236 GraphicsBuffer[(i * 2 * CurrResolution.X * 2) + (j * 2 + 1)] = PixelData;
1237 GraphicsBuffer[((i * 2 + 1) * CurrResolution.X * 2) + (j * 2)] = PixelData;
1238 GraphicsBuffer[((i * 2 + 1) * CurrResolution.X * 2) + (j * 2 + 1)] = PixelData;
1239
1240 /* Mark the specified pixel as changed */
1241 VgaMarkForUpdate(i, j);
1242 }
1243 }
1244 else if (DoubleWidth && !DoubleHeight)
1245 {
1246 /* Now check if the resulting pixel data has changed */
1247 if (GraphicsBuffer[(i * CurrResolution.X * 2) + (j * 2)] != PixelData)
1248 {
1249 /* Yes, write the new value */
1250 GraphicsBuffer[(i * CurrResolution.X * 2) + (j * 2)] = PixelData;
1251 GraphicsBuffer[(i * CurrResolution.X * 2) + (j * 2 + 1)] = PixelData;
1252
1253 /* Mark the specified pixel as changed */
1254 VgaMarkForUpdate(i, j);
1255 }
1256 }
1257 else if (!DoubleWidth && DoubleHeight)
1258 {
1259 /* Now check if the resulting pixel data has changed */
1260 if (GraphicsBuffer[(i * 2 * CurrResolution.X) + j] != PixelData)
1261 {
1262 /* Yes, write the new value */
1263 GraphicsBuffer[(i * 2 * CurrResolution.X) + j] = PixelData;
1264 GraphicsBuffer[((i * 2 + 1) * CurrResolution.X) + j] = PixelData;
1265
1266 /* Mark the specified pixel as changed */
1267 VgaMarkForUpdate(i, j);
1268 }
1269 }
1270 else // if (!DoubleWidth && !DoubleHeight)
1271 {
1272 /* Now check if the resulting pixel data has changed */
1273 if (GraphicsBuffer[i * CurrResolution.X + j] != PixelData)
1274 {
1275 /* Yes, write the new value */
1276 GraphicsBuffer[i * CurrResolution.X + j] = PixelData;
1277
1278 /* Mark the specified pixel as changed */
1279 VgaMarkForUpdate(i, j);
1280 }
1281 }
1282 }
1283
1284 if ((VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) && (i & 1))
1285 {
1286 /* Clear the high bit */
1287 Address &= ~InterlaceHighBit;
1288 }
1289
1290 if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) || (i & 1))
1291 {
1292 /* Move to the next scanline */
1293 Address += ScanlineSize;
1294 }
1295 }
1296
1297 /*
1298 * Release the console framebuffer mutex
1299 * so that we allow for repainting.
1300 */
1301 ReleaseMutex(ConsoleMutex);
1302 }
1303 else
1304 {
1305 /* Text mode */
1306 DWORD CurrentAddr;
1307 PCHAR_CELL CharBuffer = (PCHAR_CELL)ConsoleFramebuffer;
1308 CHAR_CELL CharInfo;
1309
1310 /*
1311 * Technically, the horizontal panning and preset row count should
1312 * affect text mode too. However, it works on pixels and not characters,
1313 * so we can't support it currently.
1314 */
1315
1316 /* Loop through the scanlines */
1317 for (i = 0; i < CurrResolution.Y; i++)
1318 {
1319 /* Loop through the characters */
1320 for (j = 0; j < CurrResolution.X; j++)
1321 {
1322 CurrentAddr = LOWORD((Address + j) * AddressSize);
1323
1324 /* Plane 0 holds the character itself */
1325 CharInfo.Char = VgaMemory[CurrentAddr];
1326
1327 /* Plane 1 holds the attribute */
1328 CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE];
1329
1330 /* Now check if the resulting character data has changed */
1331 if ((CharBuffer[i * CurrResolution.X + j].Char != CharInfo.Char) ||
1332 (CharBuffer[i * CurrResolution.X + j].Attributes != CharInfo.Attributes))
1333 {
1334 /* Yes, write the new value */
1335 CharBuffer[i * CurrResolution.X + j] = CharInfo;
1336
1337 /* Mark the specified cell as changed */
1338 VgaMarkForUpdate(i, j);
1339 }
1340 }
1341
1342 /* Move to the next scanline */
1343 Address += ScanlineSize;
1344 }
1345 }
1346 }
1347
1348 static VOID VgaUpdateTextCursor(VOID)
1349 {
1350 COORD Position;
1351 CONSOLE_CURSOR_INFO CursorInfo;
1352
1353 BOOL CursorVisible = !(VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x20);
1354 BYTE CursorStart = VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x1F;
1355 BYTE CursorEnd = VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] & 0x1F;
1356
1357 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
1358 BYTE TextSize = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
1359 WORD Location = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG],
1360 VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG]);
1361
1362 /* Just return if we are not in text mode */
1363 if (ScreenMode != TEXT_MODE) return;
1364
1365 if (CursorStart < CursorEnd)
1366 {
1367 /* Visible cursor */
1368 CursorInfo.bVisible = CursorVisible;
1369 CursorInfo.dwSize = (100 * (CursorEnd - CursorStart)) / TextSize;
1370 }
1371 else
1372 {
1373 /* Hidden cursor */
1374 CursorInfo.bVisible = FALSE;
1375 CursorInfo.dwSize = 1; // The size needs to be non-null in order SetConsoleCursorInfo to succeed.
1376 }
1377
1378 /* Add the cursor skew to the location */
1379 Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 0x03;
1380
1381 /* Find the coordinates of the new position */
1382 Position.X = (SHORT)(Location % ScanlineSize);
1383 Position.Y = (SHORT)(Location / ScanlineSize);
1384
1385 DPRINT("VgaUpdateTextCursor: X = %d ; Y = %d\n", Position.X, Position.Y);
1386
1387 /* Update the physical cursor */
1388 SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
1389 SetConsoleCursorPosition(TextConsoleBuffer, Position);
1390
1391 /* Reset the cursor changed flag */
1392 CursorChanged = FALSE;
1393 }
1394
1395 static BYTE WINAPI VgaReadPort(USHORT Port)
1396 {
1397 DPRINT("VgaReadPort: Port 0x%X\n", Port);
1398
1399 switch (Port)
1400 {
1401 case VGA_MISC_READ:
1402 return VgaMiscRegister;
1403
1404 case VGA_INSTAT0_READ:
1405 return 0; // Not implemented
1406
1407 case VGA_INSTAT1_READ_MONO:
1408 case VGA_INSTAT1_READ_COLOR:
1409 {
1410 BYTE Result = 0;
1411 BOOLEAN Vsync, Hsync;
1412 ULONGLONG Cycles = GetCycleCount();
1413 ULONG CyclesPerMicrosecond = (ULONG)((GetCycleSpeed() + 500000ULL) / 1000000ULL);
1414 ULONG Dots = (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & 1) ? 9 : 8;
1415 ULONG Clock = ((VgaMiscRegister >> 2) & 1) ? 28 : 25;
1416 ULONG HorizTotalDots = ((ULONG)VgaCrtcRegisters[VGA_CRTC_HORZ_TOTAL_REG] + 5) * Dots;
1417 ULONG VblankStart, VblankEnd, HblankStart, HblankEnd;
1418 ULONG HblankDuration, VblankDuration;
1419
1420 /* Calculate the vertical blanking duration in cycles */
1421 VblankStart = VgaCrtcRegisters[VGA_CRTC_START_VERT_BLANKING_REG] & 0x7F;
1422 VblankEnd = VgaCrtcRegisters[VGA_CRTC_END_VERT_BLANKING_REG] & 0x7F;
1423 if (VblankEnd < VblankStart) VblankEnd |= 0x80;
1424 VblankDuration = ((VblankEnd - VblankStart) * HorizTotalDots
1425 * CyclesPerMicrosecond + (Clock >> 1)) / Clock;
1426
1427 /* Calculate the horizontal blanking duration in cycles */
1428 HblankStart = VgaCrtcRegisters[VGA_CRTC_START_HORZ_BLANKING_REG] & 0x1F;
1429 HblankEnd = VgaCrtcRegisters[VGA_CRTC_END_HORZ_BLANKING_REG] & 0x1F;
1430 if (HblankEnd < HblankStart) HblankEnd |= 0x20;
1431 HblankDuration = ((HblankEnd - HblankStart) * Dots
1432 * CyclesPerMicrosecond + (Clock >> 1)) / Clock;
1433
1434 Vsync = (Cycles - VerticalRetraceCycle) < (ULONGLONG)VblankDuration;
1435 Hsync = (Cycles - HorizontalRetraceCycle) < (ULONGLONG)HblankDuration;
1436
1437 /* Reset the AC latch */
1438 VgaAcLatch = FALSE;
1439
1440 /* Reverse the polarity, if needed */
1441 if (VgaMiscRegister & VGA_MISC_VSYNCP) Vsync = !Vsync;
1442 if (VgaMiscRegister & VGA_MISC_HSYNCP) Hsync = !Hsync;
1443
1444 /* Set a flag if there is a vertical or horizontal retrace */
1445 if (Vsync || Hsync) Result |= VGA_STAT_DD;
1446
1447 /* Set an additional flag if there was a vertical retrace */
1448 if (Vsync) Result |= VGA_STAT_VRETRACE;
1449
1450 return Result;
1451 }
1452
1453 case VGA_FEATURE_READ:
1454 return VgaFeatureRegister;
1455
1456 case VGA_AC_INDEX:
1457 return VgaAcIndex;
1458
1459 case VGA_AC_READ:
1460 return VgaAcRegisters[VgaAcIndex];
1461
1462 case VGA_SEQ_INDEX:
1463 return VgaSeqIndex;
1464
1465 case VGA_SEQ_DATA:
1466 return VgaSeqRegisters[VgaSeqIndex];
1467
1468 case VGA_DAC_MASK:
1469 return VgaDacMask;
1470
1471 case VGA_DAC_READ_INDEX:
1472 /* This returns the read/write state */
1473 return (VgaDacReadWrite ? 0 : 3);
1474
1475 case VGA_DAC_WRITE_INDEX:
1476 return (VgaDacIndex / 3);
1477
1478 case VGA_DAC_DATA:
1479 {
1480 /* Ignore reads in write mode */
1481 if (!VgaDacReadWrite)
1482 {
1483 BYTE Data = VgaDacRegisters[VgaDacIndex++];
1484 VgaDacIndex %= VGA_PALETTE_SIZE;
1485 return Data;
1486 }
1487
1488 break;
1489 }
1490
1491 case VGA_CRTC_INDEX_MONO:
1492 case VGA_CRTC_INDEX_COLOR:
1493 return VgaCrtcIndex;
1494
1495 case VGA_CRTC_DATA_MONO:
1496 case VGA_CRTC_DATA_COLOR:
1497 return VgaCrtcRegisters[VgaCrtcIndex];
1498
1499 case VGA_GC_INDEX:
1500 return VgaGcIndex;
1501
1502 case VGA_GC_DATA:
1503 return VgaGcRegisters[VgaGcIndex];
1504
1505 default:
1506 DPRINT1("VgaReadPort: Unknown port 0x%X\n", Port);
1507 break;
1508 }
1509
1510 return 0;
1511 }
1512
1513 static inline VOID VgaWriteSequencer(BYTE Data)
1514 {
1515 ASSERT(VgaSeqIndex < VGA_SEQ_MAX_REG);
1516
1517 /* Save the value */
1518 VgaSeqRegisters[VgaSeqIndex] = Data;
1519 }
1520
1521 static inline VOID VgaWriteGc(BYTE Data)
1522 {
1523 ASSERT(VgaGcIndex < VGA_GC_MAX_REG);
1524
1525 /* Save the value */
1526 VgaGcRegisters[VgaGcIndex] = Data;
1527
1528 /* Check the index */
1529 switch (VgaGcIndex)
1530 {
1531 case VGA_GC_MISC_REG:
1532 {
1533 /* Remove any existing VGA memory hook */
1534 MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
1535
1536 if (VgaMiscRegister & VGA_MISC_RAM_ENABLED)
1537 {
1538 UCHAR MemoryMap = (VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03;
1539
1540 /* Register a memory hook */
1541 MemInstallFastMemoryHook((PVOID)MemoryBase[MemoryMap],
1542 MemorySize[MemoryMap],
1543 VgaReadMemory,
1544 VgaWriteMemory);
1545 }
1546
1547 /* The GC misc register decides if it's text or graphics mode */
1548 ModeChanged = TRUE;
1549 break;
1550 }
1551 }
1552 }
1553
1554 static inline VOID VgaWriteCrtc(BYTE Data)
1555 {
1556 ASSERT(VgaGcIndex < VGA_CRTC_MAX_REG);
1557
1558 /* Save the value */
1559 VgaCrtcRegisters[VgaCrtcIndex] = Data;
1560
1561 /* Check the index */
1562 switch (VgaCrtcIndex)
1563 {
1564 case VGA_CRTC_END_HORZ_DISP_REG:
1565 case VGA_CRTC_VERT_DISP_END_REG:
1566 case VGA_CRTC_OVERFLOW_REG:
1567 case VGA_CRTC_MAX_SCAN_LINE_REG:
1568 {
1569 /* The video mode has changed */
1570 ModeChanged = TRUE;
1571 break;
1572 }
1573
1574 case VGA_CRTC_CURSOR_LOC_LOW_REG:
1575 case VGA_CRTC_CURSOR_LOC_HIGH_REG:
1576 case VGA_CRTC_CURSOR_START_REG:
1577 case VGA_CRTC_CURSOR_END_REG:
1578 {
1579 /* Set the cursor changed flag */
1580 CursorChanged = TRUE;
1581 break;
1582 }
1583 }
1584 }
1585
1586 static inline VOID VgaWriteDac(BYTE Data)
1587 {
1588 UINT i, PaletteIndex;
1589 PALETTEENTRY Entry;
1590
1591 /* Set the value */
1592 VgaDacRegisters[VgaDacIndex] = Data;
1593
1594 /* Find the palette index */
1595 PaletteIndex = VgaDacIndex / 3;
1596
1597 /* Fill the entry structure */
1598 Entry.peRed = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3]);
1599 Entry.peGreen = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 1]);
1600 Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 2]);
1601 Entry.peFlags = 0;
1602
1603 /* Update the palette entry */
1604 SetPaletteEntries(PaletteHandle, PaletteIndex, 1, &Entry);
1605
1606 /* Check which text palette entries are affected */
1607 for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
1608 {
1609 if (VgaAcRegisters[i] == PaletteIndex)
1610 {
1611 /* Update the text palette entry */
1612 SetPaletteEntries(TextPaletteHandle, i, 1, &Entry);
1613 }
1614 }
1615
1616 /* Set the palette changed flag */
1617 PaletteChanged = TRUE;
1618
1619 /* Update the index */
1620 VgaDacIndex++;
1621 VgaDacIndex %= VGA_PALETTE_SIZE;
1622 }
1623
1624 static inline VOID VgaWriteAc(BYTE Data)
1625 {
1626 PALETTEENTRY Entry;
1627
1628 ASSERT(VgaAcIndex < VGA_AC_MAX_REG);
1629
1630 /* Save the value */
1631 if (VgaAcIndex <= VGA_AC_PAL_F_REG)
1632 {
1633 if (VgaAcPalDisable) return;
1634
1635 // DbgPrint(" AC Palette Writing %d to index %d\n", Data, VgaAcIndex);
1636 if (VgaAcRegisters[VgaAcIndex] != Data)
1637 {
1638 /* Update the AC register */
1639 VgaAcRegisters[VgaAcIndex] = Data;
1640
1641 /* Fill the entry structure */
1642 Entry.peRed = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3]);
1643 Entry.peGreen = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3 + 1]);
1644 Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3 + 2]);
1645 Entry.peFlags = 0;
1646
1647 /* Update the palette entry and set the palette change flag */
1648 SetPaletteEntries(TextPaletteHandle, VgaAcIndex, 1, &Entry);
1649 PaletteChanged = TRUE;
1650 }
1651 }
1652 else
1653 {
1654 VgaAcRegisters[VgaAcIndex] = Data;
1655 }
1656 }
1657
1658 static VOID WINAPI VgaWritePort(USHORT Port, BYTE Data)
1659 {
1660 DPRINT("VgaWritePort: Port 0x%X, Data 0x%02X\n", Port, Data);
1661
1662 switch (Port)
1663 {
1664 case VGA_MISC_WRITE:
1665 {
1666 VgaMiscRegister = Data;
1667
1668 if (VgaMiscRegister & 0x01)
1669 {
1670 /* Color emulation */
1671 DPRINT1("Color emulation\n");
1672
1673 /* Register the new I/O Ports */
1674 RegisterIoPort(0x3D4, VgaReadPort, VgaWritePort); // VGA_CRTC_INDEX_COLOR
1675 RegisterIoPort(0x3D5, VgaReadPort, VgaWritePort); // VGA_CRTC_DATA_COLOR
1676 RegisterIoPort(0x3DA, VgaReadPort, VgaWritePort); // VGA_INSTAT1_READ_COLOR, VGA_FEATURE_WRITE_COLOR
1677
1678 /* Unregister the old ones */
1679 UnregisterIoPort(0x3B4); // VGA_CRTC_INDEX_MONO
1680 UnregisterIoPort(0x3B5); // VGA_CRTC_DATA_MONO
1681 UnregisterIoPort(0x3BA); // VGA_INSTAT1_READ_MONO, VGA_FEATURE_WRITE_MONO
1682 }
1683 else
1684 {
1685 /* Monochrome emulation */
1686 DPRINT1("Monochrome emulation\n");
1687
1688 /* Register the new I/O Ports */
1689 RegisterIoPort(0x3B4, VgaReadPort, VgaWritePort); // VGA_CRTC_INDEX_MONO
1690 RegisterIoPort(0x3B5, VgaReadPort, VgaWritePort); // VGA_CRTC_DATA_MONO
1691 RegisterIoPort(0x3BA, VgaReadPort, VgaWritePort); // VGA_INSTAT1_READ_MONO, VGA_FEATURE_WRITE_MONO
1692
1693 /* Unregister the old ones */
1694 UnregisterIoPort(0x3D4); // VGA_CRTC_INDEX_COLOR
1695 UnregisterIoPort(0x3D5); // VGA_CRTC_DATA_COLOR
1696 UnregisterIoPort(0x3DA); // VGA_INSTAT1_READ_COLOR, VGA_FEATURE_WRITE_COLOR
1697 }
1698
1699 /* Remove any existing VGA memory hook */
1700 MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
1701
1702 if (VgaMiscRegister & VGA_MISC_RAM_ENABLED)
1703 {
1704 UCHAR MemoryMap = (VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03;
1705
1706 /* Register a memory hook */
1707 MemInstallFastMemoryHook((PVOID)MemoryBase[MemoryMap],
1708 MemorySize[MemoryMap],
1709 VgaReadMemory,
1710 VgaWriteMemory);
1711 }
1712
1713 break;
1714 }
1715
1716 case VGA_FEATURE_WRITE_MONO:
1717 case VGA_FEATURE_WRITE_COLOR:
1718 {
1719 VgaFeatureRegister = Data;
1720 break;
1721 }
1722
1723 case VGA_AC_INDEX:
1724 // case VGA_AC_WRITE:
1725 {
1726 if (!VgaAcLatch)
1727 {
1728 /* Change the index */
1729 BYTE Index = Data & 0x1F;
1730 if (Index < VGA_AC_MAX_REG) VgaAcIndex = Index;
1731
1732 /*
1733 * Change palette protection by checking for
1734 * the Palette Address Source bit.
1735 */
1736 VgaAcPalDisable = (Data & 0x20) ? TRUE : FALSE;
1737 }
1738 else
1739 {
1740 /* Write the data */
1741 VgaWriteAc(Data);
1742 }
1743
1744 /* Toggle the latch */
1745 VgaAcLatch = !VgaAcLatch;
1746 break;
1747 }
1748
1749 case VGA_SEQ_INDEX:
1750 {
1751 /* Set the sequencer index register */
1752 if (Data < VGA_SEQ_MAX_REG) VgaSeqIndex = Data;
1753 break;
1754 }
1755
1756 case VGA_SEQ_DATA:
1757 {
1758 /* Call the sequencer function */
1759 VgaWriteSequencer(Data);
1760 break;
1761 }
1762
1763 case VGA_DAC_MASK:
1764 {
1765 VgaDacMask = Data;
1766 break;
1767 }
1768
1769 case VGA_DAC_READ_INDEX:
1770 {
1771 VgaDacReadWrite = FALSE;
1772 VgaDacIndex = Data * 3;
1773 break;
1774 }
1775
1776 case VGA_DAC_WRITE_INDEX:
1777 {
1778 VgaDacReadWrite = TRUE;
1779 VgaDacIndex = Data * 3;
1780 break;
1781 }
1782
1783 case VGA_DAC_DATA:
1784 {
1785 /* Ignore writes in read mode */
1786 if (VgaDacReadWrite) VgaWriteDac(Data & 0x3F);
1787 break;
1788 }
1789
1790 case VGA_CRTC_INDEX_MONO:
1791 case VGA_CRTC_INDEX_COLOR:
1792 {
1793 /* Set the CRTC index register */
1794 if (Data < VGA_CRTC_MAX_REG) VgaCrtcIndex = Data;
1795 break;
1796 }
1797
1798 case VGA_CRTC_DATA_MONO:
1799 case VGA_CRTC_DATA_COLOR:
1800 {
1801 /* Call the CRTC function */
1802 VgaWriteCrtc(Data);
1803 break;
1804 }
1805
1806 case VGA_GC_INDEX:
1807 {
1808 /* Set the GC index register */
1809 if (Data < VGA_GC_MAX_REG) VgaGcIndex = Data;
1810 break;
1811 }
1812
1813 case VGA_GC_DATA:
1814 {
1815 /* Call the GC function */
1816 VgaWriteGc(Data);
1817 break;
1818 }
1819
1820 default:
1821 DPRINT1("VgaWritePort: Unknown port 0x%X, Data 0x%02X\n", Port, Data);
1822 break;
1823 }
1824 }
1825
1826 static VOID FASTCALL VgaVerticalRetrace(ULONGLONG ElapsedTime)
1827 {
1828 HANDLE ConsoleBufferHandle = NULL;
1829
1830 UNREFERENCED_PARAMETER(ElapsedTime);
1831
1832 /* Set the vertical retrace cycle */
1833 VerticalRetraceCycle = GetCycleCount();
1834
1835 /* If nothing has changed, just return */
1836 // if (!ModeChanged && !CursorChanged && !PaletteChanged && !NeedsUpdate)
1837 // return;
1838
1839 /* Change the display mode */
1840 if (ModeChanged) VgaChangeMode();
1841
1842 /* Change the text cursor appearance */
1843 if (CursorChanged) VgaUpdateTextCursor();
1844
1845 if (PaletteChanged)
1846 {
1847 /* Trigger a full update of the screen */
1848 NeedsUpdate = TRUE;
1849 UpdateRectangle.Left = 0;
1850 UpdateRectangle.Top = 0;
1851 UpdateRectangle.Right = CurrResolution.X;
1852 UpdateRectangle.Bottom = CurrResolution.Y;
1853
1854 PaletteChanged = FALSE;
1855 }
1856
1857 /* Update the contents of the framebuffer */
1858 VgaUpdateFramebuffer();
1859
1860 /* Ignore if there's nothing to update */
1861 if (!NeedsUpdate) return;
1862
1863 DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
1864 UpdateRectangle.Left,
1865 UpdateRectangle.Top,
1866 UpdateRectangle.Right,
1867 UpdateRectangle.Bottom);
1868
1869 /* Check if we are in text or graphics mode */
1870 if (ScreenMode == GRAPHICS_MODE)
1871 {
1872 /* Graphics mode */
1873 ConsoleBufferHandle = GraphicsConsoleBuffer;
1874
1875 /* In DoubleVision mode, scale the update rectangle */
1876 if (DoubleWidth)
1877 {
1878 UpdateRectangle.Left *= 2;
1879 UpdateRectangle.Right = UpdateRectangle.Right * 2 + 1;
1880 }
1881 if (DoubleHeight)
1882 {
1883 UpdateRectangle.Top *= 2;
1884 UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1;
1885 }
1886 }
1887 else
1888 {
1889 /* Text mode */
1890 ConsoleBufferHandle = TextConsoleBuffer;
1891 }
1892
1893 /* Redraw the screen */
1894 __InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
1895
1896 /* Clear the update flag */
1897 NeedsUpdate = FALSE;
1898 }
1899
1900 static VOID FASTCALL VgaHorizontalRetrace(ULONGLONG ElapsedTime)
1901 {
1902 UNREFERENCED_PARAMETER(ElapsedTime);
1903
1904 /* Set the cycle */
1905 HorizontalRetraceCycle = GetCycleCount();
1906 }
1907
1908 /* PUBLIC FUNCTIONS ***********************************************************/
1909
1910 COORD VgaGetDisplayResolution(VOID)
1911 {
1912 COORD Resolution;
1913 BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
1914
1915 /* The low 8 bits are in the display registers */
1916 Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
1917 Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG];
1918
1919 /* Set the top bits from the overflow register */
1920 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8)
1921 {
1922 Resolution.Y |= 1 << 8;
1923 }
1924 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9)
1925 {
1926 Resolution.Y |= 1 << 9;
1927 }
1928
1929 /* Increase the values by 1 */
1930 Resolution.X++;
1931 Resolution.Y++;
1932
1933 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
1934 {
1935 /* Multiply the horizontal resolution by the 9/8 dot mode */
1936 Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
1937 ? 8 : 9;
1938
1939 /* The horizontal resolution is halved in 8-bit mode */
1940 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
1941 }
1942
1943 if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE)
1944 {
1945 /* Halve the vertical resolution */
1946 Resolution.Y >>= 1;
1947 }
1948 else
1949 {
1950 /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
1951 Resolution.Y /= MaximumScanLine;
1952 }
1953
1954 /* Return the resolution */
1955 return Resolution;
1956 }
1957
1958 BOOLEAN VgaGetDoubleVisionState(PBOOLEAN Horizontal, PBOOLEAN Vertical)
1959 {
1960 if (GraphicsConsoleBuffer == NULL) return FALSE;
1961 if (Horizontal) *Horizontal = DoubleWidth;
1962 if (Vertical) *Vertical = DoubleHeight;
1963 return TRUE;
1964 }
1965
1966 VOID VgaRefreshDisplay(VOID)
1967 {
1968 VgaVerticalRetrace(0);
1969 }
1970
1971 VOID FASTCALL VgaReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
1972 {
1973 DWORD i, j;
1974 DWORD VideoAddress;
1975 PUCHAR BufPtr = (PUCHAR)Buffer;
1976
1977 DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n", Address, Size);
1978
1979 /* Ignore if video RAM access is disabled */
1980 if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return;
1981
1982 if (!(VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_READ))
1983 {
1984 /* Loop through each byte */
1985 for (i = 0; i < Size; i++)
1986 {
1987 VideoAddress = VgaTranslateReadAddress(Address + i);
1988
1989 /* Copy the value to the buffer */
1990 BufPtr[i] = VgaMemory[VideoAddress];
1991 }
1992 }
1993 else
1994 {
1995 /* Loop through each byte */
1996 for (i = 0; i < Size; i++)
1997 {
1998 BYTE Result = 0xFF;
1999
2000 /* This should always return a plane 0 address for read mode 1 */
2001 VideoAddress = VgaTranslateReadAddress(Address + i);
2002
2003 for (j = 0; j < VGA_NUM_BANKS; j++)
2004 {
2005 /* Don't consider ignored banks */
2006 if (!(VgaGcRegisters[VGA_GC_COLOR_IGNORE_REG] & (1 << j))) continue;
2007
2008 if (VgaGcRegisters[VGA_GC_COLOR_COMPARE_REG] & (1 << j))
2009 {
2010 /* Comparing with 11111111 */
2011 Result &= VgaMemory[j * VGA_BANK_SIZE + LOWORD(VideoAddress)];
2012 }
2013 else
2014 {
2015 /* Comparing with 00000000 */
2016 Result &= ~(VgaMemory[j * VGA_BANK_SIZE + LOWORD(VideoAddress)]);
2017 }
2018 }
2019
2020 /* Copy the value to the buffer */
2021 BufPtr[i] = Result;
2022 }
2023 }
2024
2025 /* Load the latch registers */
2026 VgaLatchRegisters[0] = VgaMemory[LOWORD(VideoAddress)];
2027 VgaLatchRegisters[1] = VgaMemory[VGA_BANK_SIZE + LOWORD(VideoAddress)];
2028 VgaLatchRegisters[2] = VgaMemory[(2 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
2029 VgaLatchRegisters[3] = VgaMemory[(3 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
2030 }
2031
2032 BOOLEAN FASTCALL VgaWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
2033 {
2034 DWORD i, j;
2035 DWORD VideoAddress;
2036 PUCHAR BufPtr = (PUCHAR)Buffer;
2037
2038 DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n", Address, Size);
2039
2040 /* Ignore if video RAM access is disabled */
2041 if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return TRUE;
2042
2043 /* Also ignore if write access to all planes is disabled */
2044 if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return TRUE;
2045
2046 /* Loop through each byte */
2047 for (i = 0; i < Size; i++)
2048 {
2049 VideoAddress = VgaTranslateWriteAddress(Address + i);
2050
2051 for (j = 0; j < VGA_NUM_BANKS; j++)
2052 {
2053 /* Make sure the page is writeable */
2054 if (!(VgaSeqRegisters[VGA_SEQ_MASK_REG] & (1 << j))) continue;
2055
2056 /* Check if this is chain-4 mode */
2057 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
2058 {
2059 if (((Address + i) & 0x03) != j)
2060 {
2061 /* This plane will not be accessed */
2062 continue;
2063 }
2064 }
2065
2066 /* Check if this is odd-even mode */
2067 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
2068 {
2069 if (((Address + i) & 0x01) != (j & 1))
2070 {
2071 /* This plane will not be accessed */
2072 continue;
2073 }
2074 }
2075
2076 /* Copy the value to the VGA memory */
2077 VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(BufPtr[i], j);
2078 }
2079 }
2080
2081 return TRUE;
2082 }
2083
2084 VOID VgaClearMemory(VOID)
2085 {
2086 RtlZeroMemory(VgaMemory, sizeof(VgaMemory));
2087 }
2088
2089 VOID VgaWriteFont(UINT FontNumber, CONST UCHAR* FontData, UINT Height)
2090 {
2091 UINT i, j;
2092 PUCHAR FontMemory = (PUCHAR)&VgaMemory[VGA_BANK_SIZE * VGA_FONT_BANK + (FontNumber * VGA_FONT_SIZE)];
2093
2094 ASSERT(Height <= VGA_MAX_FONT_HEIGHT);
2095
2096 for (i = 0 ; i < VGA_FONT_CHARACTERS; i++)
2097 {
2098 /* Write the character */
2099 for (j = 0; j < Height; j++)
2100 {
2101 FontMemory[i * VGA_MAX_FONT_HEIGHT + j] = FontData[i * Height + j];
2102 }
2103
2104 /* Clear the unused part */
2105 for (j = Height; j < VGA_MAX_FONT_HEIGHT; j++)
2106 {
2107 FontMemory[i * VGA_MAX_FONT_HEIGHT + j] = 0;
2108 }
2109 }
2110 }
2111
2112 VOID ScreenEventHandler(PWINDOW_BUFFER_SIZE_RECORD ScreenEvent)
2113 {
2114 DPRINT1("Screen events not handled\n");
2115 }
2116
2117 BOOL VgaAttachToConsole(VOID)
2118 {
2119 //
2120 // FIXME: We should go back to the saved screen state
2121 //
2122 if (TextResolution.X == 0 || TextResolution.Y == 0)
2123 DPRINT1("VgaAttachToConsole -- TextResolution uninitialized\n");
2124
2125 if (TextResolution.X == 0) TextResolution.X = 80;
2126 if (TextResolution.Y == 0) TextResolution.Y = 25;
2127
2128 return VgaAttachToConsoleInternal(&TextResolution);
2129 }
2130
2131 VOID VgaDetachFromConsole(BOOL ChangingMode)
2132 {
2133 ULONG dummyLength;
2134 PVOID dummyPtr;
2135 COORD dummySize = {0};
2136
2137 //
2138 // FIXME: We should save the screen state
2139 //
2140
2141 __RegisterConsoleVDM(0,
2142 NULL,
2143 NULL,
2144 NULL,
2145 0,
2146 &dummyLength,
2147 &dummyPtr,
2148 NULL,
2149 0,
2150 dummySize,
2151 &dummyPtr);
2152
2153 TextFramebuffer = NULL;
2154
2155 if (!ChangingMode)
2156 {
2157 SMALL_RECT ConRect;
2158
2159 /* Restore the old screen buffer */
2160 VgaSetActiveScreenBuffer(TextConsoleBuffer);
2161
2162 /* Restore the original console size */
2163 ConRect.Left = 0;
2164 ConRect.Top = 0;
2165 ConRect.Right = ConRect.Left + OrgConsoleBufferInfo.srWindow.Right - OrgConsoleBufferInfo.srWindow.Left;
2166 ConRect.Bottom = ConRect.Top + OrgConsoleBufferInfo.srWindow.Bottom - OrgConsoleBufferInfo.srWindow.Top ;
2167 /*
2168 * See the following trick explanation in VgaAttachToConsoleInternal.
2169 */
2170 SetConsoleScreenBufferSize(TextConsoleBuffer, OrgConsoleBufferInfo.dwSize);
2171 SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect);
2172 SetConsoleScreenBufferSize(TextConsoleBuffer, OrgConsoleBufferInfo.dwSize);
2173
2174 /* Restore the original cursor shape */
2175 SetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo);
2176 }
2177 }
2178
2179 BOOLEAN VgaInitialize(HANDLE TextHandle)
2180 {
2181 /* Save the default text-mode console output handle */
2182 if (!IsConsoleHandle(TextHandle)) return FALSE;
2183 TextConsoleBuffer = TextHandle;
2184
2185 /* Save the original cursor and console screen buffer information */
2186 if (!GetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo) ||
2187 !GetConsoleScreenBufferInfo(TextConsoleBuffer, &OrgConsoleBufferInfo))
2188 {
2189 return FALSE;
2190 }
2191 ConsoleInfo = OrgConsoleBufferInfo;
2192
2193 /* Initialize the VGA palette and fail if it isn't successfully created */
2194 if (!VgaInitializePalette()) return FALSE;
2195 /***/ VgaResetPalette(); /***/
2196
2197 /* Switch to the text buffer */
2198 VgaSetActiveScreenBuffer(TextConsoleBuffer);
2199
2200 /* Clear the VGA memory */
2201 VgaClearMemory();
2202
2203 /* Register the I/O Ports */
2204 RegisterIoPort(0x3CC, VgaReadPort, NULL); // VGA_MISC_READ
2205 RegisterIoPort(0x3C2, VgaReadPort, VgaWritePort); // VGA_MISC_WRITE, VGA_INSTAT0_READ
2206 RegisterIoPort(0x3CA, VgaReadPort, NULL); // VGA_FEATURE_READ
2207 RegisterIoPort(0x3C0, VgaReadPort, VgaWritePort); // VGA_AC_INDEX, VGA_AC_WRITE
2208 RegisterIoPort(0x3C1, VgaReadPort, NULL); // VGA_AC_READ
2209 RegisterIoPort(0x3C4, VgaReadPort, VgaWritePort); // VGA_SEQ_INDEX
2210 RegisterIoPort(0x3C5, VgaReadPort, VgaWritePort); // VGA_SEQ_DATA
2211 RegisterIoPort(0x3C6, VgaReadPort, VgaWritePort); // VGA_DAC_MASK
2212 RegisterIoPort(0x3C7, VgaReadPort, VgaWritePort); // VGA_DAC_READ_INDEX
2213 RegisterIoPort(0x3C8, VgaReadPort, VgaWritePort); // VGA_DAC_WRITE_INDEX
2214 RegisterIoPort(0x3C9, VgaReadPort, VgaWritePort); // VGA_DAC_DATA
2215 RegisterIoPort(0x3CE, VgaReadPort, VgaWritePort); // VGA_GC_INDEX
2216 RegisterIoPort(0x3CF, VgaReadPort, VgaWritePort); // VGA_GC_DATA
2217
2218 /* CGA ports for compatibility, unimplemented */
2219 RegisterIoPort(0x3D8, VgaReadPort, VgaWritePort); // CGA_MODE_CTRL_REG
2220 RegisterIoPort(0x3D9, VgaReadPort, VgaWritePort); // CGA_PAL_CTRL_REG
2221
2222 HSyncTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, HZ_TO_NS(31469), VgaHorizontalRetrace);
2223 VSyncTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, HZ_TO_NS(60), VgaVerticalRetrace);
2224
2225 /* Return success */
2226 return TRUE;
2227 }
2228
2229 VOID VgaCleanup(VOID)
2230 {
2231 DestroyHardwareTimer(VSyncTimer);
2232 DestroyHardwareTimer(HSyncTimer);
2233
2234 if (ScreenMode == GRAPHICS_MODE)
2235 {
2236 /* Leave the current graphics mode */
2237 VgaLeaveGraphicsMode();
2238 }
2239 else
2240 {
2241 /* Leave the current text mode */
2242 VgaLeaveTextMode();
2243 }
2244
2245 VgaDetachFromConsole(FALSE);
2246 MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
2247
2248 CloseHandle(AnotherEvent);
2249 CloseHandle(EndEvent);
2250 CloseHandle(StartEvent);
2251 }
2252
2253 /* EOF */