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