[NTVDM]
[reactos.git] / subsystems / ntvdm / 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 "vga.h"
14 #include "bios.h"
15
16 /* PRIVATE VARIABLES **********************************************************/
17
18 static CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
19 static CONST DWORD MemoryLimit[] = { 0xAFFFF, 0xAFFFF, 0xB7FFF, 0xBFFFF };
20
21 static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] =
22 {
23 0x000000, 0x0000AA, 0x00AA00, 0x00AAAA, 0xAA0000, 0xAA00AA, 0xAA5500, 0xAAAAAA,
24 0x555555, 0x5555FF, 0x55FF55, 0x55FFFF, 0xFF5555, 0xFF55FF, 0xFFFF55, 0xFFFFFF,
25 0x000000, 0x101010, 0x202020, 0x353535, 0x454545, 0x555555, 0x656565, 0x757575,
26 0x8A8A8A, 0x9A9A9A, 0xAAAAAA, 0xBABABA, 0xCACACA, 0xDFDFDF, 0xEFEFEF, 0xFFFFFF,
27 0x0000FF, 0x4100FF, 0x8200FF, 0xBE00FF, 0xFF00FF, 0xFF00BE, 0xFF0082, 0xFF0041,
28 0xFF0000, 0xFF4100, 0xFF8200, 0xFFBE00, 0xFFFF00, 0xBEFF00, 0x82FF00, 0x41FF00,
29 0x00FF00, 0x00FF41, 0x00FF82, 0x00FFBE, 0x00FFFF, 0x00BEFF, 0x0082FF, 0x0041FF,
30 0x8282FF, 0x9E82FF, 0xBE82FF, 0xDF82FF, 0xFF82FF, 0xFF82DF, 0xFF82BE, 0xFF829E,
31 0xFF8282, 0xFF9E82, 0xFFBE82, 0xFFDF82, 0xFFFF82, 0xDFFF82, 0xBEFF82, 0x9EFF82,
32 0x82FF82, 0x82FF9E, 0x82FFBE, 0x82FFDF, 0x82FFFF, 0x82DFFF, 0x82BEFF, 0x829EFF,
33 0xBABAFF, 0xCABAFF, 0xDFBAFF, 0xEFBAFF, 0xFFBAFF, 0xFFBAEF, 0xFFBADF, 0xFFBACA,
34 0xFFBABA, 0xFFCABA, 0xFFDFBA, 0xFFEFBA, 0xFFFFBA, 0xEFFFBA, 0xDFFFBA, 0xCAFFBA,
35 0xBAFFBA, 0xBAFFCA, 0xBAFFDF, 0xBAFFEF, 0xBAFFFF, 0xBAEFFF, 0xBADFFF, 0xBACAFF,
36 0x000071, 0x1C0071, 0x390071, 0x550071, 0x710071, 0x710055, 0x710039, 0x71001C,
37 0x710000, 0x711C00, 0x713900, 0x715500, 0x717100, 0x557100, 0x397100, 0x1C7100,
38 0x007100, 0x00711C, 0x007139, 0x007155, 0x007171, 0x005571, 0x003971, 0x001C71,
39 0x393971, 0x453971, 0x553971, 0x613971, 0x713971, 0x713961, 0x713955, 0x713945,
40 0x713939, 0x714539, 0x715539, 0x716139, 0x717139, 0x617139, 0x557139, 0x457139,
41 0x397139, 0x397145, 0x397155, 0x397161, 0x397171, 0x396171, 0x395571, 0x394571,
42 0x515171, 0x595171, 0x615171, 0x695171, 0x715171, 0x715169, 0x715161, 0x715159,
43 0x715151, 0x715951, 0x716151, 0x716951, 0x717151, 0x697151, 0x617151, 0x597151,
44 0x517151, 0x517159, 0x517161, 0x517169, 0x517171, 0x516971, 0x516171, 0x515971,
45 0x000041, 0x100041, 0x200041, 0x310041, 0x410041, 0x410031, 0x410020, 0x410010,
46 0x410000, 0x411000, 0x412000, 0x413100, 0x414100, 0x314100, 0x204100, 0x104100,
47 0x004100, 0x004110, 0x004120, 0x004131, 0x004141, 0x003141, 0x002041, 0x001041,
48 0x202041, 0x282041, 0x312041, 0x392041, 0x412041, 0x412039, 0x412031, 0x412028,
49 0x412020, 0x412820, 0x413120, 0x413920, 0x414120, 0x394120, 0x314120, 0x284120,
50 0x204120, 0x204128, 0x204131, 0x204139, 0x204141, 0x203941, 0x203141, 0x202841,
51 0x2D2D41, 0x312D41, 0x352D41, 0x3D2D41, 0x412D41, 0x412D3D, 0x412D35, 0x412D31,
52 0x412D2D, 0x41312D, 0x41352D, 0x413D2D, 0x41412D, 0x3D412D, 0x35412D, 0x31412D,
53 0x2D412D, 0x2D4131, 0x2D4135, 0x2D413D, 0x2D4141, 0x2D3D41, 0x2D3541, 0x2D3141,
54 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000
55 };
56
57 static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
58 static BYTE VgaLatchRegisters[VGA_NUM_BANKS] = {0, 0, 0, 0};
59 static BYTE VgaMiscRegister;
60 static BYTE VgaSeqIndex = VGA_SEQ_RESET_REG;
61 static BYTE VgaSeqRegisters[VGA_SEQ_MAX_REG];
62 static BYTE VgaGcIndex = VGA_GC_RESET_REG;
63 static BYTE VgaGcRegisters[VGA_GC_MAX_REG];
64 static BYTE VgaCrtcIndex = VGA_CRTC_HORZ_TOTAL_REG;
65 static BYTE VgaCrtcRegisters[VGA_CRTC_MAX_REG];
66 static BYTE VgaAcIndex = VGA_AC_PAL_0_REG;
67 static BOOLEAN VgaAcLatch = FALSE;
68 static BYTE VgaAcRegisters[VGA_AC_MAX_REG];
69 static BYTE VgaDacIndex = 0;
70 static BOOLEAN VgaDacReadWrite = FALSE;
71 static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
72 static HPALETTE PaletteHandle = NULL;
73 static BOOLEAN InVerticalRetrace = FALSE;
74 static BOOLEAN InHorizontalRetrace = FALSE;
75 static HANDLE TextConsoleBuffer = NULL;
76 static HANDLE GraphicsConsoleBuffer = NULL;
77 static LPVOID ConsoleFramebuffer = NULL;
78 static HANDLE ConsoleMutex = NULL;
79 static BOOLEAN NeedsUpdate = FALSE;
80 static BOOLEAN ModeChanged = TRUE;
81 static BOOLEAN CursorMoved = FALSE;
82 static BOOLEAN PaletteChanged = FALSE;
83 static BOOLEAN TextMode = TRUE;
84 static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
85
86 /* PRIVATE FUNCTIONS **********************************************************/
87
88 static inline INT VgaGetAddressSize(VOID)
89 {
90 if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
91 {
92 /* Double-word addressing */
93 return 4;
94 }
95
96 if (VgaCrtcRegisters[VGA_CRTC_MODE_CONTROL_REG] & VGA_CRTC_MODE_CONTROL_BYTE)
97 {
98 /* Byte addressing */
99 return 1;
100 }
101
102 /* Word addressing */
103 return 2;
104 }
105
106 static inline DWORD VgaTranslateReadAddress(DWORD Address)
107 {
108 DWORD Offset = Address - VgaGetVideoBaseAddress();
109 BYTE Plane;
110
111 /* Check for chain-4 and odd-even mode */
112 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
113 {
114 /* The lowest two bits are the plane number */
115 Plane = Offset & 3;
116 Offset >>= 2;
117 }
118 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
119 {
120 /* The LSB is the plane number */
121 Plane = Offset & 1;
122 Offset >>= 1;
123 }
124 else
125 {
126 /* Use the read mode */
127 Plane = VgaGcRegisters[VGA_GC_READ_MAP_SEL_REG] & 0x03;
128 }
129
130 /* Multiply the offset by the address size */
131 Offset *= VgaGetAddressSize();
132
133 return Offset + Plane * VGA_BANK_SIZE;
134 }
135
136 static inline DWORD VgaTranslateWriteAddress(DWORD Address)
137 {
138 DWORD Offset = Address - VgaGetVideoBaseAddress();
139
140 /* Check for chain-4 and odd-even mode */
141 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
142 {
143 /* Shift the offset to the right by 2 */
144 Offset >>= 2;
145 }
146 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
147 {
148 /* Shift the offset to the right by 1 */
149 Offset >>= 1;
150 }
151
152 /* Multiply the offset by the address size */
153 Offset *= VgaGetAddressSize();
154
155 /* Return the offset on plane 0 */
156 return Offset;
157 }
158
159 static inline BYTE VgaTranslateByteForWriting(BYTE Data, BYTE Plane)
160 {
161 BYTE WriteMode = VgaGcRegisters[VGA_GC_MODE_REG] & 3;
162 BYTE LogicalOperation = (VgaGcRegisters[VGA_GC_ROTATE_REG] >> 3) & 3;
163 BYTE RotateCount = VgaGcRegisters[VGA_GC_ROTATE_REG] & 7;
164 BYTE BitMask = VgaGcRegisters[VGA_GC_BITMASK_REG];
165
166 if (WriteMode == 1)
167 {
168 /* In write mode 1 just return the latch register */
169 return VgaLatchRegisters[Plane];
170 }
171
172 if (WriteMode != 2)
173 {
174 /* Write modes 0 and 3 rotate the data to the right first */
175 Data = LOBYTE(((DWORD)Data >> RotateCount) | ((DWORD)Data << (8 - RotateCount)));
176 }
177 else
178 {
179 /* Write mode 2 expands the appropriate bit to all 8 bits */
180 Data = (Data & (1 << Plane)) ? 0xFF : 0x00;
181 }
182
183 if (WriteMode == 0)
184 {
185 /*
186 * In write mode 0, the enable set/reset register decides if the
187 * set/reset bit should be expanded to all 8 bits.
188 */
189 if (VgaGcRegisters[VGA_GC_ENABLE_RESET_REG] & (1 << Plane))
190 {
191 /* Copy the bit from the set/reset register to all 8 bits */
192 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
193 }
194 }
195
196 if (WriteMode != 3)
197 {
198 /* Write modes 0 and 2 then perform a logical operation on the data and latch */
199 if (LogicalOperation == 1) Data &= VgaLatchRegisters[Plane];
200 else if (LogicalOperation == 2) Data |= VgaLatchRegisters[Plane];
201 else if (LogicalOperation == 3) Data ^= VgaLatchRegisters[Plane];
202 }
203 else
204 {
205 /* For write mode 3, we AND the bitmask with the data, which is used as the new bitmask */
206 BitMask &= Data;
207
208 /* Then we expand the bit in the set/reset field */
209 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
210 }
211
212 /* Bits cleared in the bitmask are replaced with latch register bits */
213 Data = (Data & BitMask) | (VgaLatchRegisters[Plane] & (~BitMask));
214
215 /* Return the byte */
216 return Data;
217 }
218
219 static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
220 {
221 DPRINT("VgaMarkForUpdate: Row %d, Column %d\n", Row, Column);
222
223 /* Check if this is the first time the rectangle is updated */
224 if (!NeedsUpdate)
225 {
226 UpdateRectangle.Left = UpdateRectangle.Top = SHRT_MAX;
227 UpdateRectangle.Right = UpdateRectangle.Bottom = SHRT_MIN;
228 }
229
230 /* Expand the rectangle to include the point */
231 UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
232 UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
233 UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
234 UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
235
236 /* Set the update request flag */
237 NeedsUpdate = TRUE;
238 }
239
240 static VOID VgaWriteSequencer(BYTE Data)
241 {
242 ASSERT(VgaSeqIndex < VGA_SEQ_MAX_REG);
243
244 /* Save the value */
245 VgaSeqRegisters[VgaSeqIndex] = Data;
246 }
247
248 static VOID VgaWriteGc(BYTE Data)
249 {
250 ASSERT(VgaGcIndex < VGA_GC_MAX_REG);
251
252 /* Save the value */
253 VgaGcRegisters[VgaGcIndex] = Data;
254
255 /* Check the index */
256 switch (VgaGcIndex)
257 {
258 case VGA_GC_MISC_REG:
259 {
260 /* The GC misc register decides if it's text or graphics mode */
261 ModeChanged = TRUE;
262
263 break;
264 }
265 }
266 }
267
268 static VOID VgaWriteCrtc(BYTE Data)
269 {
270 ASSERT(VgaGcIndex < VGA_CRTC_MAX_REG);
271
272 /* Save the value */
273 VgaCrtcRegisters[VgaCrtcIndex] = Data;
274
275 /* Check the index */
276 switch (VgaCrtcIndex)
277 {
278 case VGA_CRTC_END_HORZ_DISP_REG:
279 case VGA_CRTC_VERT_DISP_END_REG:
280 case VGA_CRTC_OVERFLOW_REG:
281 {
282 /* The video mode has changed */
283 ModeChanged = TRUE;
284
285 break;
286 }
287
288 case VGA_CRTC_CURSOR_LOC_LOW_REG:
289 case VGA_CRTC_CURSOR_LOC_HIGH_REG:
290 case VGA_CRTC_CURSOR_START_REG:
291 case VGA_CRTC_CURSOR_END_REG:
292 {
293 /* Set the cursor moved flag */
294 CursorMoved = TRUE;
295
296 break;
297 }
298 }
299 }
300
301 static VOID VgaWriteDac(BYTE Data)
302 {
303 INT PaletteIndex;
304 PALETTEENTRY Entry;
305
306 /* Set the value */
307 VgaDacRegisters[VgaDacIndex] = Data;
308
309 /* Find the palette index */
310 PaletteIndex = VgaDacIndex / 3;
311
312 /* Fill the entry structure */
313 Entry.peRed = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3]);
314 Entry.peGreen = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 1]);
315 Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 2]);
316 Entry.peFlags = 0;
317
318 /* Update the palette entry */
319 SetPaletteEntries(PaletteHandle, PaletteIndex, 1, &Entry);
320
321 /* Set the palette changed flag */
322 PaletteChanged = TRUE;
323
324 /* Update the index */
325 VgaDacIndex++;
326 VgaDacIndex %= VGA_PALETTE_SIZE;
327 }
328
329 static VOID VgaWriteAc(BYTE Data)
330 {
331 ASSERT(VgaAcIndex < VGA_AC_MAX_REG);
332
333 /* Save the value */
334 VgaAcRegisters[VgaAcIndex] = Data;
335 }
336
337 static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
338 {
339 DWORD i;
340 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
341 BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
342 LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
343 LPWORD PaletteIndex = (LPWORD)(BitmapInfoBuffer + sizeof(BITMAPINFOHEADER));
344
345 /* Fill the bitmap info header */
346 ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
347 BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
348 BitmapInfo->bmiHeader.biWidth = Resolution->X;
349 BitmapInfo->bmiHeader.biHeight = Resolution->Y;
350 BitmapInfo->bmiHeader.biBitCount = 8;
351 BitmapInfo->bmiHeader.biPlanes = 1;
352 BitmapInfo->bmiHeader.biCompression = BI_RGB;
353 BitmapInfo->bmiHeader.biSizeImage = Resolution->X * Resolution->Y;
354
355 /* Fill the palette data */
356 for (i = 0; i < (VGA_PALETTE_SIZE / 3); i++) PaletteIndex[i] = (WORD)i;
357
358 /* Fill the console graphics buffer info */
359 GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE;
360 GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
361 GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
362
363 /* Create the buffer */
364 GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
365 FILE_SHARE_READ | FILE_SHARE_WRITE,
366 NULL,
367 CONSOLE_GRAPHICS_BUFFER,
368 &GraphicsBufferInfo);
369 if (GraphicsConsoleBuffer == INVALID_HANDLE_VALUE) return FALSE;
370
371 /* Save the framebuffer address and mutex */
372 ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap;
373 ConsoleMutex = GraphicsBufferInfo.hMutex;
374
375 /* Clear the framebuffer */
376 ZeroMemory(ConsoleFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
377
378 /* Set the active buffer */
379 SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer);
380
381 return TRUE;
382 }
383
384 static VOID VgaLeaveGraphicsMode(VOID)
385 {
386 /* Release the console framebuffer mutex if needed */
387 ReleaseMutex(ConsoleMutex);
388
389 /* Switch back to the text buffer */
390 SetConsoleActiveScreenBuffer(TextConsoleBuffer);
391
392 /* Cleanup the video data */
393 CloseHandle(ConsoleMutex);
394 ConsoleMutex = NULL;
395 CloseHandle(GraphicsConsoleBuffer);
396 GraphicsConsoleBuffer = NULL;
397 }
398
399 static BOOL VgaEnterTextMode(PCOORD Resolution)
400 {
401 /* Resize the console */
402 SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
403
404 /* Allocate a framebuffer */
405 ConsoleFramebuffer = HeapAlloc(GetProcessHeap(),
406 HEAP_ZERO_MEMORY,
407 Resolution->X * Resolution->Y
408 * sizeof(CHAR_INFO));
409 if (ConsoleFramebuffer == NULL)
410 {
411 DisplayMessage(L"An unexpected error occurred!\n");
412 VdmRunning = FALSE;
413 return FALSE;
414 }
415
416 return TRUE;
417 }
418
419 static VOID VgaLeaveTextMode(VOID)
420 {
421 /* Free the old framebuffer */
422 HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer);
423 ConsoleFramebuffer = NULL;
424 }
425
426 static VOID VgaUpdateMode(VOID)
427 {
428 COORD Resolution = VgaGetDisplayResolution();
429
430 if (!TextMode)
431 {
432 /* Leave the current graphics mode */
433 VgaLeaveGraphicsMode();
434 }
435 else
436 {
437 /* Leave the current text mode */
438 VgaLeaveTextMode();
439 }
440
441 /* Check if the new mode is alphanumeric */
442 if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA))
443 {
444 /* Enter new text mode */
445 if (!VgaEnterTextMode(&Resolution)) return;
446
447 /* Set the text mode flag */
448 TextMode = TRUE;
449 }
450 else
451 {
452 /* Enter 8-bit graphics mode */
453 if (!VgaEnterGraphicsMode(&Resolution)) return;
454
455 /* Clear the text mode flag */
456 TextMode = FALSE;
457
458 /* Set the palette */
459 SetConsolePalette(GraphicsConsoleBuffer,
460 PaletteHandle,
461 SYSPAL_NOSTATIC256);
462 }
463
464 /* Perform a full update */
465 NeedsUpdate = TRUE;
466 UpdateRectangle.Left = 0;
467 UpdateRectangle.Top = 0;
468 UpdateRectangle.Right = Resolution.X;
469 UpdateRectangle.Bottom = Resolution.Y;
470 }
471
472 static VOID VgaUpdateFramebuffer(VOID)
473 {
474 INT i, j, k;
475 COORD Resolution = VgaGetDisplayResolution();
476 INT AddressSize = VgaGetAddressSize();
477 DWORD Address = (VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG] << 8)
478 + VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG];
479 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
480
481 /* Check if this is text mode or graphics mode */
482 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
483 {
484 /* Graphics mode */
485 PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
486
487 /*
488 * Synchronize access to the graphics framebuffer
489 * with the console framebuffer mutex.
490 */
491 WaitForSingleObject(ConsoleMutex, INFINITE);
492
493 /* Loop through the scanlines */
494 for (i = 0; i < Resolution.Y; i++)
495 {
496 /* Loop through the pixels */
497 for (j = 0; j < Resolution.X; j++)
498 {
499 BYTE PixelData = 0;
500
501 /* Check the shifting mode */
502 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFT256)
503 {
504 /* 4 bits shifted from each plane */
505
506 /* Check if this is 16 or 256 color mode */
507 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
508 {
509 /* One byte per pixel */
510 PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
511 + (Address + (j / VGA_NUM_BANKS))
512 * AddressSize];
513 }
514 else
515 {
516 /* 4-bits per pixel */
517
518 PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
519 + (Address + (j / (VGA_NUM_BANKS * 2)))
520 * AddressSize];
521
522 /* Check if we should use the highest 4 bits or lowest 4 */
523 if (((j / VGA_NUM_BANKS) % 2) == 0)
524 {
525 /* Highest 4 */
526 PixelData >>= 4;
527 }
528 else
529 {
530 /* Lowest 4 */
531 PixelData &= 0x0F;
532 }
533 }
534 }
535 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFTREG)
536 {
537 /*
538 * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
539 * then 2 bits shifted from plane 1 and 3 for the next 4
540 */
541
542 // TODO: NOT IMPLEMENTED!
543 DPRINT1("Interleaved shift mode is not implemented!\n");
544 }
545 else
546 {
547 /* 1 bit shifted from each plane */
548
549 /* Check if this is 16 or 256 color mode */
550 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
551 {
552 /* 8 bits per pixel, 2 on each plane */
553
554 for (k = 0; k < VGA_NUM_BANKS; k++)
555 {
556 /* The data is on plane k, 4 pixels per byte */
557 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
558 + (Address + (j / 4)) * AddressSize];
559
560 /* The mask of the first bit in the pair */
561 BYTE BitMask = 1 << (((3 - (j % 4)) * 2) + 1);
562
563 /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
564 if (PlaneData & BitMask) PixelData |= 1 << k;
565
566 /* Bits 4, 5, 6 and 7 come from the second bit of the pair */
567 if (PlaneData & (BitMask >> 1)) PixelData |= 1 << (k + 4);
568 }
569 }
570 else
571 {
572 /* 4 bits per pixel, 1 on each plane */
573
574 for (k = 0; k < VGA_NUM_BANKS; k++)
575 {
576 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
577 + (Address + (j / 8)) * AddressSize];
578
579 /* If the bit on that plane is set, set it */
580 if (PlaneData & (1 << (7 - (j % 8)))) PixelData |= 1 << k;
581 }
582 }
583 }
584
585 /* Now check if the resulting pixel data has changed */
586 if (GraphicsBuffer[i * Resolution.X + j] != PixelData)
587 {
588 /* Yes, write the new value */
589 GraphicsBuffer[i * Resolution.X + j] = PixelData;
590
591 /* Mark the specified pixel as changed */
592 VgaMarkForUpdate(i, j);
593 }
594 }
595
596 /* Move to the next scanline */
597 Address += ScanlineSize;
598 }
599
600 /*
601 * Release the console framebuffer mutex
602 * so that we allow for repainting.
603 */
604 ReleaseMutex(ConsoleMutex);
605 }
606 else
607 {
608 /* Text mode */
609 PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
610
611 /* Loop through the scanlines */
612 for (i = 0; i < Resolution.Y; i++)
613 {
614 /* Loop through the characters */
615 for (j = 0; j < Resolution.X; j++)
616 {
617 DWORD CurrentAddr = LOWORD((Address + j) * AddressSize);
618 CHAR_INFO CharInfo;
619
620 /* Plane 0 holds the character itself */
621 CharInfo.Char.AsciiChar = VgaMemory[CurrentAddr];
622
623 /* Plane 1 holds the attribute */
624 CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE];
625
626 /* Now check if the resulting character data has changed */
627 if ((CharBuffer[i * Resolution.X + j].Char.AsciiChar != CharInfo.Char.AsciiChar)
628 || (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes))
629 {
630 /* Yes, write the new value */
631 CharBuffer[i * Resolution.X + j] = CharInfo;
632
633 /* Mark the specified pixel as changed */
634 VgaMarkForUpdate(i, j);
635 }
636 }
637
638 /* Move to the next scanline */
639 Address += ScanlineSize;
640 }
641 }
642 }
643
644 static VOID VgaUpdateTextCursor(VOID)
645 {
646 COORD Position;
647 CONSOLE_CURSOR_INFO CursorInfo;
648 BYTE CursorStart = VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x3F;
649 BYTE CursorEnd = VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] & 0x1F;
650 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
651 BYTE TextSize = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
652 WORD Location = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG],
653 VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG]);
654
655 if (CursorStart < CursorEnd)
656 {
657 /* Visible cursor */
658 CursorInfo.bVisible = TRUE;
659 CursorInfo.dwSize = (100 * (CursorEnd - CursorStart)) / TextSize;
660 }
661 else
662 {
663 /* No cursor */
664 CursorInfo.bVisible = FALSE;
665 CursorInfo.dwSize = 0;
666 }
667
668 /* Add the cursor skew to the location */
669 Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 3;
670
671 /* Find the coordinates of the new position */
672 Position.X = (WORD)(Location % ScanlineSize);
673 Position.Y = (WORD)(Location / ScanlineSize);
674
675 /* Update the physical cursor */
676 SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
677 SetConsoleCursorPosition(TextConsoleBuffer, Position);
678 }
679
680 /* PUBLIC FUNCTIONS ***********************************************************/
681
682 DWORD VgaGetVideoBaseAddress(VOID)
683 {
684 return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
685 }
686
687 DWORD VgaGetVideoLimitAddress(VOID)
688 {
689 return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
690 }
691
692 COORD VgaGetDisplayResolution(VOID)
693 {
694 COORD Resolution;
695 BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
696
697 /* The low 8 bits are in the display registers */
698 Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
699 Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG];
700
701 /* Set the top bits from the overflow register */
702 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8)
703 {
704 Resolution.Y |= 1 << 8;
705 }
706 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9)
707 {
708 Resolution.Y |= 1 << 9;
709 }
710
711 /* Increase the values by 1 */
712 Resolution.X++;
713 Resolution.Y++;
714
715 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
716 {
717 /* Multiply the horizontal resolution by the 9/8 dot mode */
718 Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
719 ? 8 : 9;
720
721 /* The horizontal resolution is halved in 8-bit mode */
722 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
723 }
724
725 /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
726 Resolution.Y /= MaximumScanLine;
727
728 /* Return the resolution */
729 return Resolution;
730 }
731
732 VOID VgaRefreshDisplay(VOID)
733 {
734 COORD Resolution = VgaGetDisplayResolution();
735
736 DPRINT("VgaRefreshDisplay\n");
737
738 if (ModeChanged)
739 {
740 /* Change the display mode */
741 VgaUpdateMode();
742
743 /* Reset the mode change flag */
744 ModeChanged = FALSE;
745 }
746
747 if (CursorMoved)
748 {
749 /* Change the text cursor location */
750 VgaUpdateTextCursor();
751
752 /* Reset the cursor move flag */
753 CursorMoved = FALSE;
754 }
755
756 if (PaletteChanged)
757 {
758 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
759 {
760 /* Set the graphics mode palette */
761 SetConsolePalette(GraphicsConsoleBuffer,
762 PaletteHandle,
763 SYSPAL_NOSTATIC256);
764 }
765
766 PaletteChanged = FALSE;
767 }
768
769 /* Update the contents of the framebuffer */
770 VgaUpdateFramebuffer();
771
772 /* Set the vertical retrace flag */
773 InVerticalRetrace = TRUE;
774
775 /* Ignore if there's nothing to update */
776 if (!NeedsUpdate) return;
777
778 DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
779 UpdateRectangle.Left,
780 UpdateRectangle.Top,
781 UpdateRectangle.Right,
782 UpdateRectangle.Bottom);
783
784 /* Check if this is text mode or graphics mode */
785 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
786 {
787 /* Graphics mode */
788
789 /* Redraw the screen */
790 InvalidateConsoleDIBits(GraphicsConsoleBuffer, &UpdateRectangle);
791 }
792 else
793 {
794 /* Text mode */
795 COORD Origin = { UpdateRectangle.Left, UpdateRectangle.Top };
796
797 /* Write the data to the console */
798 WriteConsoleOutputA(TextConsoleBuffer,
799 (PCHAR_INFO)ConsoleFramebuffer,
800 Resolution,
801 Origin,
802 &UpdateRectangle);
803
804 }
805
806 /* Clear the update flag */
807 NeedsUpdate = FALSE;
808 }
809
810 VOID VgaHorizontalRetrace(VOID)
811 {
812 /* Set the flag */
813 InHorizontalRetrace = TRUE;
814 }
815
816 VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
817 {
818 DWORD i;
819 DWORD VideoAddress;
820
821 DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n",
822 Address,
823 Size);
824
825 /* Ignore if video RAM access is disabled */
826 if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
827
828 /* Loop through each byte */
829 for (i = 0; i < Size; i++)
830 {
831 VideoAddress = VgaTranslateReadAddress(Address + i);
832
833 /* Load the latch registers */
834 VgaLatchRegisters[0] = VgaMemory[LOWORD(VideoAddress)];
835 VgaLatchRegisters[1] = VgaMemory[VGA_BANK_SIZE + LOWORD(VideoAddress)];
836 VgaLatchRegisters[2] = VgaMemory[(2 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
837 VgaLatchRegisters[3] = VgaMemory[(3 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
838
839 /* Copy the value to the buffer */
840 Buffer[i] = VgaMemory[VideoAddress];
841 }
842 }
843
844 VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
845 {
846 DWORD i, j;
847 DWORD VideoAddress;
848
849 DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n",
850 Address,
851 Size);
852
853 /* Ignore if video RAM access is disabled */
854 if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
855
856 /* Also ignore if write access to all planes is disabled */
857 if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return;
858
859 /* Loop through each byte */
860 for (i = 0; i < Size; i++)
861 {
862 VideoAddress = VgaTranslateWriteAddress(Address + i);
863
864 for (j = 0; j < VGA_NUM_BANKS; j++)
865 {
866 /* Make sure the page is writeable */
867 if (!(VgaSeqRegisters[VGA_SEQ_MASK_REG] & (1 << j))) continue;
868
869 /* Check if this is chain-4 mode */
870 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
871 {
872 if (((Address + i) & 3) != j)
873 {
874 /* This plane will not be accessed */
875 continue;
876 }
877 }
878
879 /* Check if this is odd-even mode */
880 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
881 {
882 if (((Address + i) & 1) != (j & 1))
883 {
884 /* This plane will not be accessed */
885 continue;
886 }
887 }
888
889 /* Copy the value to the VGA memory */
890 VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(Buffer[i], j);
891 }
892 }
893 }
894
895 BYTE VgaReadPort(WORD Port)
896 {
897 DPRINT("VgaReadPort: Port 0x%04X\n", Port);
898
899 switch (Port)
900 {
901 case VGA_AC_INDEX:
902 {
903 return VgaAcIndex;
904 }
905
906 case VGA_AC_READ:
907 {
908 return VgaAcRegisters[VgaAcIndex];
909 }
910
911 case VGA_SEQ_INDEX:
912 {
913 return VgaSeqIndex;
914 }
915
916 case VGA_SEQ_DATA:
917 {
918 return VgaSeqRegisters[VgaSeqIndex];
919 }
920
921 case VGA_DAC_READ_INDEX:
922 {
923 /* This returns the read/write state */
924 return VgaDacReadWrite ? 0 : 3;
925 }
926
927 case VGA_DAC_WRITE_INDEX:
928 {
929 return VgaDacIndex;
930 }
931
932 case VGA_DAC_DATA:
933 {
934 /* Ignore reads in write mode */
935 if (!VgaDacReadWrite)
936 {
937 BYTE Data = VgaDacRegisters[VgaDacIndex++];
938 VgaDacIndex %= VGA_PALETTE_SIZE;
939 return Data;
940 }
941
942 break;
943 }
944
945 case VGA_MISC_READ:
946 {
947 return VgaMiscRegister;
948 }
949
950 case VGA_CRTC_INDEX:
951 {
952 return VgaCrtcIndex;
953 }
954
955 case VGA_CRTC_DATA:
956 {
957 return VgaCrtcRegisters[VgaCrtcIndex];
958 }
959
960 case VGA_GC_INDEX:
961 {
962 return VgaGcIndex;
963 }
964
965 case VGA_GC_DATA:
966 {
967 return VgaGcRegisters[VgaGcIndex];
968 }
969
970 case VGA_STAT_MONO:
971 case VGA_STAT_COLOR:
972 {
973 BYTE Result = 0;
974
975 /* Reset the AC latch */
976 VgaAcLatch = FALSE;
977
978 /* Set a flag if there is a vertical or horizontal retrace */
979 if (InVerticalRetrace || InHorizontalRetrace) Result |= VGA_STAT_DD;
980
981 /* Set an additional flag if there was a vertical retrace */
982 if (InVerticalRetrace) Result |= VGA_STAT_VRETRACE;
983
984 /* Clear the flags */
985 InHorizontalRetrace = InVerticalRetrace = FALSE;
986
987 return Result;
988 }
989 }
990
991 return 0;
992 }
993
994 VOID VgaWritePort(WORD Port, BYTE Data)
995 {
996 DPRINT("VgaWritePort: Port 0x%04X, Data 0x%02X\n", Port, Data);
997
998 switch (Port)
999 {
1000 case VGA_AC_INDEX:
1001 {
1002 if (!VgaAcLatch)
1003 {
1004 /* Change the index */
1005 if (Data < VGA_AC_MAX_REG) VgaAcIndex = Data;
1006 }
1007 else
1008 {
1009 /* Write the data */
1010 VgaWriteAc(Data);
1011 }
1012
1013 /* Toggle the latch */
1014 VgaAcLatch = !VgaAcLatch;
1015
1016 break;
1017 }
1018
1019 case VGA_SEQ_INDEX:
1020 {
1021 /* Set the sequencer index register */
1022 if (Data < VGA_SEQ_MAX_REG) VgaSeqIndex = Data;
1023
1024 break;
1025 }
1026
1027 case VGA_SEQ_DATA:
1028 {
1029 /* Call the sequencer function */
1030 VgaWriteSequencer(Data);
1031
1032 break;
1033 }
1034
1035 case VGA_DAC_READ_INDEX:
1036 {
1037 VgaDacReadWrite = FALSE;
1038 VgaDacIndex = Data % VGA_PALETTE_SIZE;
1039
1040 break;
1041 }
1042
1043 case VGA_DAC_WRITE_INDEX:
1044 {
1045 VgaDacReadWrite = TRUE;
1046 VgaDacIndex = Data % VGA_PALETTE_SIZE;
1047
1048 break;
1049 }
1050
1051 case VGA_DAC_DATA:
1052 {
1053 /* Ignore writes in read mode */
1054 if (VgaDacReadWrite) VgaWriteDac(Data & 0x3F);
1055
1056 break;
1057 }
1058
1059 case VGA_MISC_WRITE:
1060 {
1061 VgaMiscRegister = Data;
1062
1063 break;
1064 }
1065
1066 case VGA_CRTC_INDEX:
1067 {
1068 /* Set the CRTC index register */
1069 if (Data < VGA_CRTC_MAX_REG) VgaCrtcIndex = Data;
1070
1071 break;
1072 }
1073
1074 case VGA_CRTC_DATA:
1075 {
1076 /* Call the CRTC function */
1077 VgaWriteCrtc(Data);
1078
1079 break;
1080 }
1081
1082 case VGA_GC_INDEX:
1083 {
1084 /* Set the GC index register */
1085 if (Data < VGA_GC_MAX_REG) VgaGcIndex = Data;
1086 break;
1087 }
1088
1089 case VGA_GC_DATA:
1090 {
1091 /* Call the GC function */
1092 VgaWriteGc(Data);
1093
1094 break;
1095 }
1096 }
1097 }
1098
1099 VOID VgaClearMemory(VOID)
1100 {
1101 ZeroMemory(VgaMemory, sizeof(VgaMemory));
1102 }
1103
1104 BOOLEAN VgaInitialize(HANDLE TextHandle)
1105 {
1106 INT i, j;
1107 COORD Resolution;
1108 INT AddressSize;
1109 DWORD ScanlineSize;
1110 COORD Origin = { 0, 0 };
1111 SMALL_RECT ScreenRect;
1112 PCHAR_INFO CharBuffer;
1113 DWORD Address = 0;
1114 DWORD CurrentAddr;
1115 LPLOGPALETTE Palette;
1116
1117 /* Set the global handle */
1118 TextConsoleBuffer = TextHandle;
1119
1120 /* Clear the VGA memory */
1121 ZeroMemory(VgaMemory, VGA_NUM_BANKS * VGA_BANK_SIZE);
1122
1123 /* Set the default video mode */
1124 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
1125 VgaUpdateMode();
1126 ModeChanged = FALSE;
1127
1128 /* Get the data */
1129 Resolution = VgaGetDisplayResolution();
1130 CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
1131 AddressSize = VgaGetAddressSize();
1132 ScreenRect.Left = ScreenRect.Top = 0;
1133 ScreenRect.Right = Resolution.X;
1134 ScreenRect.Bottom = Resolution.Y;
1135 ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
1136
1137 /* Read the data from the console into the framebuffer */
1138 ReadConsoleOutputA(TextConsoleBuffer,
1139 CharBuffer,
1140 Resolution,
1141 Origin,
1142 &ScreenRect);
1143
1144 /* Loop through the scanlines */
1145 for (i = 0; i < Resolution.Y; i++)
1146 {
1147 /* Loop through the characters */
1148 for (j = 0; j < Resolution.X; j++)
1149 {
1150 CurrentAddr = LOWORD((Address + j) * AddressSize);
1151
1152 /* Store the character in plane 0 */
1153 VgaMemory[CurrentAddr] = CharBuffer[i * Resolution.X + j].Char.AsciiChar;
1154
1155 /* Store the attribute in plane 1 */
1156 VgaMemory[CurrentAddr + VGA_BANK_SIZE] = (BYTE)CharBuffer[i * Resolution.X + j].Attributes;
1157 }
1158
1159 /* Move to the next scanline */
1160 Address += ScanlineSize;
1161 }
1162
1163 /* Allocate storage space for the palette */
1164 Palette = (LPLOGPALETTE)HeapAlloc(GetProcessHeap(),
1165 HEAP_ZERO_MEMORY,
1166 sizeof(LOGPALETTE)
1167 + VGA_MAX_COLORS * sizeof(PALETTEENTRY));
1168 if (Palette == NULL) return FALSE;
1169
1170 /* Initialize the palette */
1171 Palette->palVersion = 0x0100;
1172 Palette->palNumEntries = VGA_MAX_COLORS;
1173
1174 /* Copy the colors of the default palette to the DAC and console palette */
1175 for (i = 0; i < VGA_MAX_COLORS; i++)
1176 {
1177 /* Set the palette entries */
1178 Palette->palPalEntry[i].peRed = GetRValue(VgaDefaultPalette[i]);
1179 Palette->palPalEntry[i].peGreen = GetGValue(VgaDefaultPalette[i]);
1180 Palette->palPalEntry[i].peBlue = GetBValue(VgaDefaultPalette[i]);
1181
1182 /* Set the DAC registers */
1183 VgaDacRegisters[i * 3] = VGA_COLOR_TO_DAC(GetRValue(VgaDefaultPalette[i]));
1184 VgaDacRegisters[i * 3 + 1] = VGA_COLOR_TO_DAC(GetGValue(VgaDefaultPalette[i]));
1185 VgaDacRegisters[i * 3 + 2] = VGA_COLOR_TO_DAC(GetBValue(VgaDefaultPalette[i]));
1186 }
1187
1188 /* Create the palette */
1189 PaletteHandle = CreatePalette(Palette);
1190
1191 /* Free the palette */
1192 HeapFree(GetProcessHeap(), 0, Palette);
1193
1194 /* Return success */
1195 return TRUE;
1196 }
1197
1198 /* EOF */