[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 BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
22 static BYTE VgaLatchRegisters[VGA_NUM_BANKS] = {0, 0, 0, 0};
23 static BYTE VgaMiscRegister;
24 static BYTE VgaSeqIndex = VGA_SEQ_RESET_REG;
25 static BYTE VgaSeqRegisters[VGA_SEQ_MAX_REG];
26 static BYTE VgaGcIndex = VGA_GC_RESET_REG;
27 static BYTE VgaGcRegisters[VGA_GC_MAX_REG];
28 static BYTE VgaCrtcIndex = VGA_CRTC_HORZ_TOTAL_REG;
29 static BYTE VgaCrtcRegisters[VGA_CRTC_MAX_REG];
30 static BYTE VgaAcIndex = VGA_AC_PAL_0_REG;
31 static BOOLEAN VgaAcLatch = FALSE;
32 static BYTE VgaAcRegisters[VGA_AC_MAX_REG];
33 static BYTE VgaDacIndex = 0;
34 static BOOLEAN VgaDacReadWrite = FALSE;
35 static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
36 static BOOLEAN InVerticalRetrace = FALSE;
37 static BOOLEAN InHorizontalRetrace = FALSE;
38 static HANDLE TextConsoleBuffer = NULL;
39 static HANDLE GraphicsConsoleBuffer = NULL;
40 static LPVOID ConsoleFramebuffer = NULL;
41 static HANDLE ConsoleMutex = NULL;
42 static BOOLEAN NeedsUpdate = FALSE;
43 static BOOLEAN ModeChanged = TRUE;
44 static BOOLEAN CursorMoved = FALSE;
45 static BOOLEAN TextMode = TRUE;
46 static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
47
48 /* PRIVATE FUNCTIONS **********************************************************/
49
50 static inline INT VgaGetAddressSize(VOID)
51 {
52 if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
53 {
54 /* Double-word addressing */
55 return 4;
56 }
57
58 if (VgaCrtcRegisters[VGA_CRTC_MODE_CONTROL_REG] & VGA_CRTC_MODE_CONTROL_BYTE)
59 {
60 /* Byte addressing */
61 return 1;
62 }
63
64 /* Word addressing */
65 return 2;
66 }
67
68 static inline DWORD VgaTranslateReadAddress(DWORD Address)
69 {
70 DWORD Offset = Address - VgaGetVideoBaseAddress();
71 BYTE Plane;
72
73 /* Check for chain-4 and odd-even mode */
74 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
75 {
76 /* The lowest two bits are the plane number */
77 Plane = Offset & 3;
78 Offset >>= 2;
79 }
80 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
81 {
82 /* The LSB is the plane number */
83 Plane = Offset & 1;
84 Offset >>= 1;
85 }
86 else
87 {
88 /* Use the read mode */
89 Plane = VgaGcRegisters[VGA_GC_READ_MAP_SEL_REG] & 0x03;
90 }
91
92 /* Multiply the offset by the address size */
93 Offset *= VgaGetAddressSize();
94
95 return Offset + Plane * VGA_BANK_SIZE;
96 }
97
98 static inline DWORD VgaTranslateWriteAddress(DWORD Address)
99 {
100 DWORD Offset = Address - VgaGetVideoBaseAddress();
101
102 /* Check for chain-4 and odd-even mode */
103 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
104 {
105 /* Shift the offset to the right by 2 */
106 Offset >>= 2;
107 }
108 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
109 {
110 /* Shift the offset to the right by 1 */
111 Offset >>= 1;
112 }
113
114 /* Multiply the offset by the address size */
115 Offset *= VgaGetAddressSize();
116
117 /* Return the offset on plane 0 */
118 return Offset;
119 }
120
121 static inline BYTE VgaTranslateByteForWriting(BYTE Data, BYTE Plane)
122 {
123 BYTE WriteMode = VgaGcRegisters[VGA_GC_MODE_REG] & 3;
124 BYTE LogicalOperation = (VgaGcRegisters[VGA_GC_ROTATE_REG] >> 3) & 3;
125 BYTE RotateCount = VgaGcRegisters[VGA_GC_ROTATE_REG] & 7;
126 BYTE BitMask = VgaGcRegisters[VGA_GC_BITMASK_REG];
127
128 if (WriteMode == 1)
129 {
130 /* In write mode 1 just return the latch register */
131 return VgaLatchRegisters[Plane];
132 }
133
134 if (WriteMode != 2)
135 {
136 /* Write modes 0 and 3 rotate the data to the right first */
137 Data = LOBYTE(((DWORD)Data >> RotateCount) | ((DWORD)Data << (8 - RotateCount)));
138 }
139 else
140 {
141 /* Write mode 2 expands the appropriate bit to all 8 bits */
142 Data = (Data & (1 << Plane)) ? 0xFF : 0x00;
143 }
144
145 if (WriteMode == 0)
146 {
147 /*
148 * In write mode 0, the enable set/reset register decides if the
149 * set/reset bit should be expanded to all 8 bits.
150 */
151 if (VgaGcRegisters[VGA_GC_ENABLE_RESET_REG] & (1 << Plane))
152 {
153 /* Copy the bit from the set/reset register to all 8 bits */
154 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
155 }
156 }
157
158 if (WriteMode != 3)
159 {
160 /* Write modes 0 and 2 then perform a logical operation on the data and latch */
161 if (LogicalOperation == 1) Data &= VgaLatchRegisters[Plane];
162 else if (LogicalOperation == 2) Data |= VgaLatchRegisters[Plane];
163 else if (LogicalOperation == 3) Data ^= VgaLatchRegisters[Plane];
164 }
165 else
166 {
167 /* For write mode 3, we AND the bitmask with the data, which is used as the new bitmask */
168 BitMask &= Data;
169
170 /* Then we expand the bit in the set/reset field */
171 Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
172 }
173
174 /* Bits cleared in the bitmask are replaced with latch register bits */
175 Data = (Data & BitMask) | (VgaLatchRegisters[Plane] & (~BitMask));
176
177 /* Return the byte */
178 return Data;
179 }
180
181 static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
182 {
183 DPRINT("VgaMarkForUpdate: Row %d, Column %d\n", Row, Column);
184
185 /* Check if this is the first time the rectangle is updated */
186 if (!NeedsUpdate)
187 {
188 UpdateRectangle.Left = UpdateRectangle.Top = SHRT_MAX;
189 UpdateRectangle.Right = UpdateRectangle.Bottom = SHRT_MIN;
190 }
191
192 /* Expand the rectangle to include the point */
193 UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
194 UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
195 UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
196 UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
197
198 /* Set the update request flag */
199 NeedsUpdate = TRUE;
200 }
201
202 static VOID VgaWriteSequencer(BYTE Data)
203 {
204 ASSERT(VgaSeqIndex < VGA_SEQ_MAX_REG);
205
206 /* Save the value */
207 VgaSeqRegisters[VgaSeqIndex] = Data;
208 }
209
210 static VOID VgaWriteGc(BYTE Data)
211 {
212 ASSERT(VgaGcIndex < VGA_GC_MAX_REG);
213
214 /* Save the value */
215 VgaGcRegisters[VgaGcIndex] = Data;
216
217 /* Check the index */
218 switch (VgaGcIndex)
219 {
220 case VGA_GC_MISC_REG:
221 {
222 /* The GC misc register decides if it's text or graphics mode */
223 ModeChanged = TRUE;
224
225 break;
226 }
227 }
228 }
229
230 static VOID VgaWriteCrtc(BYTE Data)
231 {
232 ASSERT(VgaGcIndex < VGA_CRTC_MAX_REG);
233
234 /* Save the value */
235 VgaCrtcRegisters[VgaCrtcIndex] = Data;
236
237 /* Check the index */
238 switch (VgaCrtcIndex)
239 {
240 case VGA_CRTC_END_HORZ_DISP_REG:
241 case VGA_CRTC_VERT_DISP_END_REG:
242 case VGA_CRTC_OVERFLOW_REG:
243 {
244 /* The video mode has changed */
245 ModeChanged = TRUE;
246
247 break;
248 }
249
250 case VGA_CRTC_CURSOR_LOC_LOW_REG:
251 case VGA_CRTC_CURSOR_LOC_HIGH_REG:
252 case VGA_CRTC_CURSOR_START_REG:
253 case VGA_CRTC_CURSOR_END_REG:
254 {
255 /* Set the cursor moved flag */
256 CursorMoved = TRUE;
257
258 break;
259 }
260 }
261 }
262
263 static VOID VgaWriteDac(BYTE Data)
264 {
265 /* Set the value */
266 VgaDacRegisters[VgaDacIndex++] = Data;
267 VgaDacIndex %= VGA_PALETTE_SIZE;
268
269 // TODO: Change the palette!
270 }
271
272 static VOID VgaWriteAc(BYTE Data)
273 {
274 ASSERT(VgaAcIndex < VGA_AC_MAX_REG);
275
276 /* Save the value */
277 VgaAcRegisters[VgaAcIndex] = Data;
278 }
279
280 static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
281 {
282 DWORD i;
283 CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
284 BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
285 LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
286 LPWORD PaletteIndex = (LPWORD)(BitmapInfoBuffer + sizeof(BITMAPINFOHEADER));
287
288 /* Fill the bitmap info header */
289 ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
290 BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
291 BitmapInfo->bmiHeader.biWidth = Resolution->X;
292 BitmapInfo->bmiHeader.biHeight = Resolution->Y;
293 BitmapInfo->bmiHeader.biBitCount = 8;
294 BitmapInfo->bmiHeader.biPlanes = 1;
295 BitmapInfo->bmiHeader.biCompression = BI_RGB;
296 BitmapInfo->bmiHeader.biSizeImage = Resolution->X * Resolution->Y;
297
298 /* Fill the palette data */
299 for (i = 0; i < (VGA_PALETTE_SIZE / 3); i++) PaletteIndex[i] = (WORD)i;
300
301 /* Fill the console graphics buffer info */
302 GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE;
303 GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
304 GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
305
306 /* Create the buffer */
307 GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
308 FILE_SHARE_READ | FILE_SHARE_WRITE,
309 NULL,
310 CONSOLE_GRAPHICS_BUFFER,
311 &GraphicsBufferInfo);
312 if (GraphicsConsoleBuffer == INVALID_HANDLE_VALUE) return FALSE;
313
314 /* Save the framebuffer address and mutex */
315 ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap;
316 ConsoleMutex = GraphicsBufferInfo.hMutex;
317
318 /* Clear the framebuffer */
319 ZeroMemory(ConsoleFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
320
321 /* Set the active buffer */
322 SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer);
323
324 return TRUE;
325 }
326
327 static VOID VgaLeaveGraphicsMode(VOID)
328 {
329 /* Release the console framebuffer mutex if needed */
330 ReleaseMutex(ConsoleMutex);
331
332 /* Switch back to the text buffer */
333 SetConsoleActiveScreenBuffer(TextConsoleBuffer);
334
335 /* Cleanup the video data */
336 CloseHandle(ConsoleMutex);
337 ConsoleMutex = NULL;
338 CloseHandle(GraphicsConsoleBuffer);
339 GraphicsConsoleBuffer = NULL;
340 }
341
342 static BOOL VgaEnterTextMode(PCOORD Resolution)
343 {
344 /* Resize the console */
345 SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
346
347 /* Allocate a framebuffer */
348 ConsoleFramebuffer = HeapAlloc(GetProcessHeap(),
349 HEAP_ZERO_MEMORY,
350 Resolution->X * Resolution->Y
351 * sizeof(CHAR_INFO));
352 if (ConsoleFramebuffer == NULL)
353 {
354 DisplayMessage(L"An unexpected error occurred!\n");
355 VdmRunning = FALSE;
356 return FALSE;
357 }
358
359 return TRUE;
360 }
361
362 static VOID VgaLeaveTextMode(VOID)
363 {
364 /* Free the old framebuffer */
365 HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer);
366 ConsoleFramebuffer = NULL;
367 }
368
369 static VOID VgaUpdateMode(VOID)
370 {
371 COORD Resolution = VgaGetDisplayResolution();
372
373 if (!TextMode)
374 {
375 /* Leave the current graphics mode */
376 VgaLeaveGraphicsMode();
377 }
378 else
379 {
380 /* Leave the current text mode */
381 VgaLeaveTextMode();
382 }
383
384 /* Check if the new mode is alphanumeric */
385 if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA))
386 {
387 /* Enter new text mode */
388 if (!VgaEnterTextMode(&Resolution)) return;
389
390 /* Set the text mode flag */
391 TextMode = TRUE;
392 }
393 else
394 {
395 /* Enter 8-bit graphics mode */
396 if (!VgaEnterGraphicsMode(&Resolution)) return;
397
398 /* Clear the text mode flag */
399 TextMode = FALSE;
400 }
401
402 /* Perform a full update */
403 NeedsUpdate = TRUE;
404 UpdateRectangle.Left = 0;
405 UpdateRectangle.Top = 0;
406 UpdateRectangle.Right = Resolution.X;
407 UpdateRectangle.Bottom = Resolution.Y;
408 }
409
410 static VOID VgaUpdateFramebuffer(VOID)
411 {
412 INT i, j, k;
413 COORD Resolution = VgaGetDisplayResolution();
414 INT AddressSize = VgaGetAddressSize();
415 DWORD Address = (VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG] << 8)
416 + VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG];
417 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
418
419 /* Check if this is text mode or graphics mode */
420 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
421 {
422 /* Graphics mode */
423 PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
424
425 /*
426 * Synchronize access to the graphics framebuffer
427 * with the console framebuffer mutex.
428 */
429 WaitForSingleObject(ConsoleMutex, INFINITE);
430
431 /* Loop through the scanlines */
432 for (i = 0; i < Resolution.Y; i++)
433 {
434 /* Loop through the pixels */
435 for (j = 0; j < Resolution.X; j++)
436 {
437 BYTE PixelData = 0;
438
439 /* Check the shifting mode */
440 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFT256)
441 {
442 /* 4 bits shifted from each plane */
443
444 /* Check if this is 16 or 256 color mode */
445 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
446 {
447 /* One byte per pixel */
448 PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
449 + (Address + (j / VGA_NUM_BANKS))
450 * AddressSize];
451 }
452 else
453 {
454 /* 4-bits per pixel */
455
456 PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
457 + (Address + (j / (VGA_NUM_BANKS * 2)))
458 * AddressSize];
459
460 /* Check if we should use the highest 4 bits or lowest 4 */
461 if (((j / VGA_NUM_BANKS) % 2) == 0)
462 {
463 /* Highest 4 */
464 PixelData >>= 4;
465 }
466 else
467 {
468 /* Lowest 4 */
469 PixelData &= 0x0F;
470 }
471 }
472 }
473 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFTREG)
474 {
475 /*
476 * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
477 * then 2 bits shifted from plane 1 and 3 for the next 4
478 */
479
480 // TODO: NOT IMPLEMENTED!
481 DPRINT1("Interleaved shift mode is not implemented!\n");
482 }
483 else
484 {
485 /* 1 bit shifted from each plane */
486
487 /* Check if this is 16 or 256 color mode */
488 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
489 {
490 /* 8 bits per pixel, 2 on each plane */
491
492 for (k = 0; k < VGA_NUM_BANKS; k++)
493 {
494 /* The data is on plane k, 4 pixels per byte */
495 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
496 + (Address + (j / 4)) * AddressSize];
497
498 /* The mask of the first bit in the pair */
499 BYTE BitMask = 1 << (((3 - (j % 4)) * 2) + 1);
500
501 /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
502 if (PlaneData & BitMask) PixelData |= 1 << k;
503
504 /* Bits 4, 5, 6 and 7 come from the second bit of the pair */
505 if (PlaneData & (BitMask >> 1)) PixelData |= 1 << (k + 4);
506 }
507 }
508 else
509 {
510 /* 4 bits per pixel, 1 on each plane */
511
512 for (k = 0; k < VGA_NUM_BANKS; k++)
513 {
514 BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
515 + (Address + (j / 8)) * AddressSize];
516
517 /* If the bit on that plane is set, set it */
518 if (PlaneData & (1 << (7 - (j % 8)))) PixelData |= 1 << k;
519 }
520 }
521 }
522
523 /* Now check if the resulting pixel data has changed */
524 if (GraphicsBuffer[i * Resolution.X + j] != PixelData)
525 {
526 /* Yes, write the new value */
527 GraphicsBuffer[i * Resolution.X + j] = PixelData;
528
529 /* Mark the specified pixel as changed */
530 VgaMarkForUpdate(i, j);
531 }
532 }
533
534 /* Move to the next scanline */
535 Address += ScanlineSize;
536 }
537
538 /*
539 * Release the console framebuffer mutex
540 * so that we allow for repainting.
541 */
542 ReleaseMutex(ConsoleMutex);
543 }
544 else
545 {
546 /* Text mode */
547 PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
548
549 /* Loop through the scanlines */
550 for (i = 0; i < Resolution.Y; i++)
551 {
552 /* Loop through the characters */
553 for (j = 0; j < Resolution.X; j++)
554 {
555 DWORD CurrentAddr = LOWORD((Address + j) * AddressSize);
556 CHAR_INFO CharInfo;
557
558 /* Plane 0 holds the character itself */
559 CharInfo.Char.AsciiChar = VgaMemory[CurrentAddr];
560
561 /* Plane 1 holds the attribute */
562 CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE];
563
564 /* Now check if the resulting character data has changed */
565 if ((CharBuffer[i * Resolution.X + j].Char.AsciiChar != CharInfo.Char.AsciiChar)
566 || (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes))
567 {
568 /* Yes, write the new value */
569 CharBuffer[i * Resolution.X + j] = CharInfo;
570
571 /* Mark the specified pixel as changed */
572 VgaMarkForUpdate(i, j);
573 }
574 }
575
576 /* Move to the next scanline */
577 Address += ScanlineSize;
578 }
579 }
580 }
581
582 static VOID VgaUpdateTextCursor(VOID)
583 {
584 COORD Position;
585 CONSOLE_CURSOR_INFO CursorInfo;
586 BYTE CursorStart = VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x3F;
587 BYTE CursorEnd = VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] & 0x1F;
588 DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
589 BYTE TextSize = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
590 WORD Location = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG],
591 VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG]);
592
593 if (CursorStart < CursorEnd)
594 {
595 /* Visible cursor */
596 CursorInfo.bVisible = TRUE;
597 CursorInfo.dwSize = (100 * (CursorEnd - CursorStart)) / TextSize;
598 }
599 else
600 {
601 /* No cursor */
602 CursorInfo.bVisible = FALSE;
603 CursorInfo.dwSize = 0;
604 }
605
606 /* Add the cursor skew to the location */
607 Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 3;
608
609 /* Find the coordinates of the new position */
610 Position.X = (WORD)(Location % ScanlineSize);
611 Position.Y = (WORD)(Location / ScanlineSize);
612
613 /* Update the physical cursor */
614 SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
615 SetConsoleCursorPosition(TextConsoleBuffer, Position);
616 }
617
618 /* PUBLIC FUNCTIONS ***********************************************************/
619
620 DWORD VgaGetVideoBaseAddress(VOID)
621 {
622 return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
623 }
624
625 DWORD VgaGetVideoLimitAddress(VOID)
626 {
627 return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
628 }
629
630 COORD VgaGetDisplayResolution(VOID)
631 {
632 COORD Resolution;
633 BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
634
635 /* The low 8 bits are in the display registers */
636 Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
637 Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG];
638
639 /* Set the top bits from the overflow register */
640 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8)
641 {
642 Resolution.Y |= 1 << 8;
643 }
644 if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9)
645 {
646 Resolution.Y |= 1 << 9;
647 }
648
649 /* Increase the values by 1 */
650 Resolution.X++;
651 Resolution.Y++;
652
653 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
654 {
655 /* Multiply the horizontal resolution by the 9/8 dot mode */
656 Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
657 ? 8 : 9;
658
659 /* The horizontal resolution is halved in 8-bit mode */
660 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
661 }
662
663 /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
664 Resolution.Y /= MaximumScanLine;
665
666 /* Return the resolution */
667 return Resolution;
668 }
669
670 VOID VgaRefreshDisplay(VOID)
671 {
672 COORD Resolution = VgaGetDisplayResolution();
673
674 DPRINT("VgaRefreshDisplay\n");
675
676 if (ModeChanged)
677 {
678 /* Change the display mode */
679 VgaUpdateMode();
680
681 /* Reset the mode change flag */
682 ModeChanged = FALSE;
683 }
684
685 if (CursorMoved)
686 {
687 /* Change the text cursor location */
688 VgaUpdateTextCursor();
689
690 /* Reset the cursor move flag */
691 CursorMoved = FALSE;
692 }
693
694 /* Update the contents of the framebuffer */
695 VgaUpdateFramebuffer();
696
697 /* Set the vertical retrace flag */
698 InVerticalRetrace = TRUE;
699
700 /* Ignore if there's nothing to update */
701 if (!NeedsUpdate) return;
702
703 DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
704 UpdateRectangle.Left,
705 UpdateRectangle.Top,
706 UpdateRectangle.Right,
707 UpdateRectangle.Bottom);
708
709 /* Check if this is text mode or graphics mode */
710 if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
711 {
712 /* Graphics mode */
713
714 /* Redraw the screen */
715 InvalidateConsoleDIBits(GraphicsConsoleBuffer, &UpdateRectangle);
716 }
717 else
718 {
719 /* Text mode */
720 COORD Origin = { UpdateRectangle.Left, UpdateRectangle.Top };
721
722 /* Write the data to the console */
723 WriteConsoleOutputA(TextConsoleBuffer,
724 (PCHAR_INFO)ConsoleFramebuffer,
725 Resolution,
726 Origin,
727 &UpdateRectangle);
728
729 }
730
731 /* Clear the update flag */
732 NeedsUpdate = FALSE;
733 }
734
735 VOID VgaHorizontalRetrace(VOID)
736 {
737 /* Set the flag */
738 InHorizontalRetrace = TRUE;
739 }
740
741 VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
742 {
743 DWORD i;
744 DWORD VideoAddress;
745
746 DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n",
747 Address,
748 Size);
749
750 /* Ignore if video RAM access is disabled */
751 if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
752
753 /* Loop through each byte */
754 for (i = 0; i < Size; i++)
755 {
756 VideoAddress = VgaTranslateReadAddress(Address + i);
757
758 /* Load the latch registers */
759 VgaLatchRegisters[0] = VgaMemory[LOWORD(VideoAddress)];
760 VgaLatchRegisters[1] = VgaMemory[VGA_BANK_SIZE + LOWORD(VideoAddress)];
761 VgaLatchRegisters[2] = VgaMemory[(2 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
762 VgaLatchRegisters[3] = VgaMemory[(3 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
763
764 /* Copy the value to the buffer */
765 Buffer[i] = VgaMemory[VideoAddress];
766 }
767 }
768
769 VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
770 {
771 DWORD i, j;
772 DWORD VideoAddress;
773
774 DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n",
775 Address,
776 Size);
777
778 /* Ignore if video RAM access is disabled */
779 if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
780
781 /* Also ignore if write access to all planes is disabled */
782 if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return;
783
784 /* Loop through each byte */
785 for (i = 0; i < Size; i++)
786 {
787 VideoAddress = VgaTranslateWriteAddress(Address + i);
788
789 for (j = 0; j < VGA_NUM_BANKS; j++)
790 {
791 /* Make sure the page is writeable */
792 if (!(VgaSeqRegisters[VGA_SEQ_MASK_REG] & (1 << j))) continue;
793
794 /* Check if this is chain-4 mode */
795 if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
796 {
797 if (((Address + i) & 3) != j)
798 {
799 /* This plane will not be accessed */
800 continue;
801 }
802 }
803
804 /* Check if this is odd-even mode */
805 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
806 {
807 if (((Address + i) & 1) != (j & 1))
808 {
809 /* This plane will not be accessed */
810 continue;
811 }
812 }
813
814 /* Copy the value to the VGA memory */
815 VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(Buffer[i], j);
816 }
817 }
818 }
819
820 BYTE VgaReadPort(WORD Port)
821 {
822 DPRINT("VgaReadPort: Port 0x%04X\n", Port);
823
824 switch (Port)
825 {
826 case VGA_AC_INDEX:
827 {
828 return VgaAcIndex;
829 }
830
831 case VGA_AC_READ:
832 {
833 return VgaAcRegisters[VgaAcIndex];
834 }
835
836 case VGA_SEQ_INDEX:
837 {
838 return VgaSeqIndex;
839 }
840
841 case VGA_SEQ_DATA:
842 {
843 return VgaSeqRegisters[VgaSeqIndex];
844 }
845
846 case VGA_DAC_READ_INDEX:
847 {
848 /* This returns the read/write state */
849 return VgaDacReadWrite ? 0 : 3;
850 }
851
852 case VGA_DAC_WRITE_INDEX:
853 {
854 return VgaDacIndex;
855 }
856
857 case VGA_DAC_DATA:
858 {
859 /* Ignore reads in write mode */
860 if (!VgaDacReadWrite)
861 {
862 BYTE Data = VgaDacRegisters[VgaDacIndex++];
863 VgaDacIndex %= VGA_PALETTE_SIZE;
864 return Data;
865 }
866
867 break;
868 }
869
870 case VGA_MISC_READ:
871 {
872 return VgaMiscRegister;
873 }
874
875 case VGA_CRTC_INDEX:
876 {
877 return VgaCrtcIndex;
878 }
879
880 case VGA_CRTC_DATA:
881 {
882 return VgaCrtcRegisters[VgaCrtcIndex];
883 }
884
885 case VGA_GC_INDEX:
886 {
887 return VgaGcIndex;
888 }
889
890 case VGA_GC_DATA:
891 {
892 return VgaGcRegisters[VgaGcIndex];
893 }
894
895 case VGA_STAT_MONO:
896 case VGA_STAT_COLOR:
897 {
898 BYTE Result = 0;
899
900 /* Reset the AC latch */
901 VgaAcLatch = FALSE;
902
903 /* Set a flag if there is a vertical or horizontal retrace */
904 if (InVerticalRetrace || InHorizontalRetrace) Result |= VGA_STAT_DD;
905
906 /* Set an additional flag if there was a vertical retrace */
907 if (InVerticalRetrace) Result |= VGA_STAT_VRETRACE;
908
909 /* Clear the flags */
910 InHorizontalRetrace = InVerticalRetrace = FALSE;
911
912 return Result;
913 }
914 }
915
916 return 0;
917 }
918
919 VOID VgaWritePort(WORD Port, BYTE Data)
920 {
921 DPRINT("VgaWritePort: Port 0x%04X, Data 0x%02X\n", Port, Data);
922
923 switch (Port)
924 {
925 case VGA_AC_INDEX:
926 {
927 if (!VgaAcLatch)
928 {
929 /* Change the index */
930 if (Data < VGA_AC_MAX_REG) VgaAcIndex = Data;
931 }
932 else
933 {
934 /* Write the data */
935 VgaWriteAc(Data);
936 }
937
938 /* Toggle the latch */
939 VgaAcLatch = !VgaAcLatch;
940
941 break;
942 }
943
944 case VGA_SEQ_INDEX:
945 {
946 /* Set the sequencer index register */
947 if (Data < VGA_SEQ_MAX_REG) VgaSeqIndex = Data;
948
949 break;
950 }
951
952 case VGA_SEQ_DATA:
953 {
954 /* Call the sequencer function */
955 VgaWriteSequencer(Data);
956
957 break;
958 }
959
960 case VGA_DAC_READ_INDEX:
961 {
962 VgaDacReadWrite = FALSE;
963 VgaDacIndex = Data % VGA_PALETTE_SIZE;
964
965 break;
966 }
967
968 case VGA_DAC_WRITE_INDEX:
969 {
970 VgaDacReadWrite = TRUE;
971 VgaDacIndex = Data % VGA_PALETTE_SIZE;
972
973 break;
974 }
975
976 case VGA_DAC_DATA:
977 {
978 /* Ignore writes in read mode */
979 if (VgaDacReadWrite) VgaWriteDac(Data & 0x3F);
980
981 break;
982 }
983
984 case VGA_MISC_WRITE:
985 {
986 VgaMiscRegister = Data;
987
988 break;
989 }
990
991 case VGA_CRTC_INDEX:
992 {
993 /* Set the CRTC index register */
994 if (Data < VGA_CRTC_MAX_REG) VgaCrtcIndex = Data;
995
996 break;
997 }
998
999 case VGA_CRTC_DATA:
1000 {
1001 /* Call the CRTC function */
1002 VgaWriteCrtc(Data);
1003
1004 break;
1005 }
1006
1007 case VGA_GC_INDEX:
1008 {
1009 /* Set the GC index register */
1010 if (Data < VGA_GC_MAX_REG) VgaGcIndex = Data;
1011 break;
1012 }
1013
1014 case VGA_GC_DATA:
1015 {
1016 /* Call the GC function */
1017 VgaWriteGc(Data);
1018
1019 break;
1020 }
1021 }
1022 }
1023
1024 VOID VgaClearMemory(VOID)
1025 {
1026 ZeroMemory(VgaMemory, sizeof(VgaMemory));
1027 }
1028
1029 VOID VgaInitialize(HANDLE TextHandle)
1030 {
1031 INT i, j;
1032 COORD Resolution;
1033 INT AddressSize;
1034 DWORD ScanlineSize;
1035 COORD Origin = { 0, 0 };
1036 SMALL_RECT ScreenRect;
1037 PCHAR_INFO CharBuffer;
1038 DWORD Address = 0;
1039 DWORD CurrentAddr;
1040
1041 /* Set the global handle */
1042 TextConsoleBuffer = TextHandle;
1043
1044 /* Clear the VGA memory */
1045 ZeroMemory(VgaMemory, VGA_NUM_BANKS * VGA_BANK_SIZE);
1046
1047 /* Set the default video mode */
1048 BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
1049 VgaUpdateMode();
1050 ModeChanged = FALSE;
1051
1052 /* Get the data */
1053 Resolution = VgaGetDisplayResolution();
1054 CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
1055 AddressSize = VgaGetAddressSize();
1056 ScreenRect.Left = ScreenRect.Top = 0;
1057 ScreenRect.Right = Resolution.X;
1058 ScreenRect.Bottom = Resolution.Y;
1059 ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
1060
1061 /* Read the data from the console into the framebuffer */
1062 ReadConsoleOutputA(TextConsoleBuffer,
1063 CharBuffer,
1064 Resolution,
1065 Origin,
1066 &ScreenRect);
1067
1068 /* Loop through the scanlines */
1069 for (i = 0; i < Resolution.Y; i++)
1070 {
1071 /* Loop through the characters */
1072 for (j = 0; j < Resolution.X; j++)
1073 {
1074 CurrentAddr = LOWORD((Address + j) * AddressSize);
1075
1076 /* Store the character in plane 0 */
1077 VgaMemory[CurrentAddr] = CharBuffer[i * Resolution.X + j].Char.AsciiChar;
1078
1079 /* Store the attribute in plane 1 */
1080 VgaMemory[CurrentAddr + VGA_BANK_SIZE] = (BYTE)CharBuffer[i * Resolution.X + j].Attributes;
1081 }
1082
1083 /* Move to the next scanline */
1084 Address += ScanlineSize;
1085 }
1086 }
1087
1088 /* EOF */