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