mirror of
https://github.com/reactos/reactos.git
synced 2024-07-11 15:15:27 +00:00
[NTVDM]
Implement Video Graphics Array (VGA) support. Replace the old BIOS video code with new code that uses the VGA. svn path=/branches/ntvdm/; revision=59648
This commit is contained in:
parent
b49476f20c
commit
455deb3bde
|
@ -8,6 +8,7 @@ list(APPEND SOURCE
|
|||
pic.c
|
||||
timer.c
|
||||
ps2.c
|
||||
vga.c
|
||||
ntvdm.c
|
||||
ntvdm.rc)
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,7 +15,6 @@
|
|||
|
||||
/* DEFINES ********************************************************************/
|
||||
|
||||
#define CONSOLE_VIDEO_MEM_END 0xBFFFF
|
||||
#define ROM_AREA_START 0xE0000
|
||||
#define ROM_AREA_END 0xFFFFF
|
||||
#define BDA_SEGMENT 0x40
|
||||
|
@ -32,18 +31,19 @@
|
|||
#define BIOS_EQUIPMENT_LIST 0x2C // HACK: Disable FPU for now
|
||||
#define BIOS_DEFAULT_VIDEO_MODE 0x03
|
||||
#define BIOS_MAX_PAGES 8
|
||||
#define BIOS_PAGE_SIZE 0x1000
|
||||
#define BIOS_MAX_VIDEO_MODE 0x13
|
||||
#define DEFAULT_ATTRIBUTE 0x07
|
||||
#define GRAPHICS_VIDEO_SEG 0xA000
|
||||
#define TEXT_VIDEO_SEG 0xB800
|
||||
|
||||
typedef struct
|
||||
enum
|
||||
{
|
||||
DWORD Width;
|
||||
DWORD Height;
|
||||
BOOLEAN Text;
|
||||
BYTE Bpp;
|
||||
BOOLEAN Gray;
|
||||
BYTE Pages;
|
||||
WORD Segment;
|
||||
} VIDEO_MODE;
|
||||
SCROLL_DIRECTION_UP,
|
||||
SCROLL_DIRECTION_DOWN,
|
||||
SCROLL_DIRECTION_LEFT,
|
||||
SCROLL_DIRECTION_RIGHT
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
|
@ -91,6 +91,8 @@ typedef struct
|
|||
BYTE ComTimeOut[4];
|
||||
WORD KeybdBufferStart;
|
||||
WORD KeybdBufferEnd;
|
||||
BYTE ScreenRows;
|
||||
WORD CharacterHeight;
|
||||
} BIOS_DATA_AREA, *PBIOS_DATA_AREA;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
@ -99,10 +101,8 @@ typedef struct
|
|||
|
||||
BOOLEAN BiosInitialize(VOID);
|
||||
VOID BiosCleanup(VOID);
|
||||
VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress);
|
||||
VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress);
|
||||
inline DWORD BiosGetVideoMemoryStart(VOID);
|
||||
inline VOID BiosVerticalRefresh(VOID);
|
||||
BYTE BiosGetVideoMode(VOID);
|
||||
BOOLEAN BiosSetVideoMode(BYTE ModeNumber);
|
||||
WORD BiosPeekCharacter(VOID);
|
||||
WORD BiosGetCharacter(VOID);
|
||||
VOID BiosVideoService(LPWORD Stack);
|
||||
|
@ -111,5 +111,12 @@ VOID BiosKeyboardService(LPWORD Stack);
|
|||
VOID BiosTimeService(LPWORD Stack);
|
||||
VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack);
|
||||
VOID BiosSystemTimerInterrupt(LPWORD Stack);
|
||||
BOOLEAN BiosScrollWindow(
|
||||
INT Direction,
|
||||
DWORD Amount,
|
||||
SMALL_RECT Rectangle,
|
||||
BYTE Page,
|
||||
BYTE FillAttribute
|
||||
);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -98,6 +98,15 @@ typedef struct _DOS_INPUT_BUFFER
|
|||
CHAR Buffer[ANYSIZE_ARRAY];
|
||||
} DOS_INPUT_BUFFER, *PDOS_INPUT_BUFFER;
|
||||
|
||||
typedef struct _DOS_DRIVER_HEADER
|
||||
{
|
||||
DWORD NextDriver;
|
||||
WORD Attributes;
|
||||
WORD StrategyEntry;
|
||||
WORD InterruptEntry;
|
||||
CHAR DeviceName[8];
|
||||
} DOS_DRIVER_HEADER, *PDOS_DRIVER_HEADER;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "emulator.h"
|
||||
#include "bios.h"
|
||||
#include "dos.h"
|
||||
#include "vga.h"
|
||||
#include "pic.h"
|
||||
#include "ps2.h"
|
||||
#include "timer.h"
|
||||
|
@ -40,17 +41,19 @@ static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT S
|
|||
/* Make sure the requested address is valid */
|
||||
if ((Address + Size) >= MAX_ADDRESS) return;
|
||||
|
||||
/* Are we reading some of the console video memory? */
|
||||
if (((Address + Size) >= BiosGetVideoMemoryStart())
|
||||
&& (Address < CONSOLE_VIDEO_MEM_END))
|
||||
{
|
||||
/* Call the VDM BIOS to update the video memory */
|
||||
BiosUpdateVideoMemory(max(Address, BiosGetVideoMemoryStart()),
|
||||
min(Address + Size, CONSOLE_VIDEO_MEM_END));
|
||||
}
|
||||
|
||||
/* Read the data from the virtual address space and store it in the buffer */
|
||||
RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
|
||||
|
||||
/* Check if we modified the console video memory */
|
||||
if (((Address + Size) >= VgaGetVideoBaseAddress())
|
||||
&& (Address < VgaGetVideoLimitAddress()))
|
||||
{
|
||||
DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
|
||||
LPBYTE VgaBuffer = &Buffer[VgaAddress - Address];
|
||||
|
||||
/* Read from the VGA memory */
|
||||
VgaReadMemory(VgaAddress, VgaBuffer, Size);
|
||||
}
|
||||
}
|
||||
|
||||
static VOID EmulatorWriteMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
||||
|
@ -68,12 +71,14 @@ static VOID EmulatorWriteMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT
|
|||
RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
|
||||
|
||||
/* Check if we modified the console video memory */
|
||||
if (((Address + Size) >= BiosGetVideoMemoryStart())
|
||||
&& (Address < CONSOLE_VIDEO_MEM_END))
|
||||
if (((Address + Size) >= VgaGetVideoBaseAddress())
|
||||
&& (Address < VgaGetVideoLimitAddress()))
|
||||
{
|
||||
/* Call the VDM BIOS to update the screen */
|
||||
BiosUpdateConsole(max(Address, BiosGetVideoMemoryStart()),
|
||||
min(Address + Size, CONSOLE_VIDEO_MEM_END));
|
||||
DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
|
||||
LPBYTE VgaBuffer = &Buffer[VgaAddress - Address];
|
||||
|
||||
/* Write to the VGA memory */
|
||||
VgaWriteMemory(VgaAddress, VgaBuffer, Size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,6 +120,26 @@ static VOID EmulatorReadIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
|||
break;
|
||||
}
|
||||
|
||||
case VGA_AC_WRITE:
|
||||
case VGA_AC_READ:
|
||||
case VGA_SEQ_INDEX:
|
||||
case VGA_SEQ_DATA:
|
||||
case VGA_DAC_READ_INDEX:
|
||||
case VGA_DAC_WRITE_INDEX:
|
||||
case VGA_DAC_DATA:
|
||||
case VGA_MISC_READ:
|
||||
case VGA_MISC_WRITE:
|
||||
case VGA_CRTC_INDEX:
|
||||
case VGA_CRTC_DATA:
|
||||
case VGA_GC_INDEX:
|
||||
case VGA_GC_DATA:
|
||||
case VGA_STAT_MONO:
|
||||
case VGA_STAT_COLOR:
|
||||
{
|
||||
*Buffer = VgaReadPort(Address);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
DPRINT1("Read from unknown port: 0x%X\n", Address);
|
||||
|
@ -168,6 +193,26 @@ static VOID EmulatorWriteIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size
|
|||
break;
|
||||
}
|
||||
|
||||
case VGA_AC_WRITE:
|
||||
case VGA_AC_READ:
|
||||
case VGA_SEQ_INDEX:
|
||||
case VGA_SEQ_DATA:
|
||||
case VGA_DAC_READ_INDEX:
|
||||
case VGA_DAC_WRITE_INDEX:
|
||||
case VGA_DAC_DATA:
|
||||
case VGA_MISC_READ:
|
||||
case VGA_MISC_WRITE:
|
||||
case VGA_CRTC_INDEX:
|
||||
case VGA_CRTC_DATA:
|
||||
case VGA_GC_INDEX:
|
||||
case VGA_GC_DATA:
|
||||
case VGA_STAT_MONO:
|
||||
case VGA_STAT_COLOR:
|
||||
{
|
||||
VgaWritePort(Address, Byte);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
DPRINT1("Write to unknown port: 0x%X\n", Address);
|
||||
|
@ -478,6 +523,9 @@ VOID EmulatorStep(VOID)
|
|||
/* Skip the opcodes */
|
||||
EmulatorContext.state->reg_ip += 4;
|
||||
|
||||
// HACK: Refresh the display because the called function may wait.
|
||||
VgaRefreshDisplay();
|
||||
|
||||
/* Call the BOP handler */
|
||||
EmulatorBop(Instruction[1]);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "ntvdm.h"
|
||||
#include "emulator.h"
|
||||
#include "bios.h"
|
||||
#include "vga.h"
|
||||
#include "dos.h"
|
||||
#include "timer.h"
|
||||
#include "pic.h"
|
||||
|
@ -137,7 +138,7 @@ INT wmain(INT argc, WCHAR *argv[])
|
|||
DisplayMessage(L"Could not start program: %S", CommandLine);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Set the last timer tick to the current time */
|
||||
QueryPerformanceCounter(&LastTimerTick);
|
||||
|
||||
|
@ -146,18 +147,18 @@ INT wmain(INT argc, WCHAR *argv[])
|
|||
{
|
||||
/* Get the current number of ticks */
|
||||
CurrentTickCount = GetTickCount();
|
||||
|
||||
|
||||
/* Get the current performance counter value */
|
||||
QueryPerformanceCounter(&Counter);
|
||||
|
||||
|
||||
/* Get the number of PIT ticks that have passed */
|
||||
TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
|
||||
* PIT_BASE_FREQUENCY) / Frequency.QuadPart;
|
||||
|
||||
|
||||
/* Update the PIT */
|
||||
for (i = 0; i < TimerTicks; i++) PitDecrementCount();
|
||||
LastTimerTick = Counter;
|
||||
|
||||
|
||||
/* Check for console input events every millisecond */
|
||||
if (CurrentTickCount != LastTickCount)
|
||||
{
|
||||
|
@ -165,20 +166,23 @@ INT wmain(INT argc, WCHAR *argv[])
|
|||
LastTickCount = CurrentTickCount;
|
||||
}
|
||||
|
||||
/* Check for vertical refresh */
|
||||
/* Check for vertical retrace */
|
||||
if ((CurrentTickCount - LastVerticalRefresh) >= 16)
|
||||
{
|
||||
BiosVerticalRefresh();
|
||||
VgaRefreshDisplay();
|
||||
LastVerticalRefresh = CurrentTickCount;
|
||||
}
|
||||
|
||||
|
||||
/* Horizontal retrace occurs as fast as possible */
|
||||
VgaHorizontalRetrace();
|
||||
|
||||
/* Continue CPU emulation */
|
||||
for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++)
|
||||
{
|
||||
EmulatorStep();
|
||||
Cycles++;
|
||||
}
|
||||
|
||||
|
||||
if ((CurrentTickCount - LastCyclePrintout) >= 1000)
|
||||
{
|
||||
DPRINT1("NTVDM: %d Instructions Per Second\n", Cycles);
|
||||
|
@ -187,6 +191,9 @@ INT wmain(INT argc, WCHAR *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
/* Perform another screen refresh */
|
||||
VgaRefreshDisplay();
|
||||
|
||||
Cleanup:
|
||||
BiosCleanup();
|
||||
EmulatorCleanup();
|
||||
|
|
974
subsystems/ntvdm/vga.c
Normal file
974
subsystems/ntvdm/vga.c
Normal file
|
@ -0,0 +1,974 @@
|
|||
/*
|
||||
* COPYRIGHT: GPL - See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Virtual DOS Machine
|
||||
* FILE: vga.c
|
||||
* PURPOSE: VGA hardware emulation
|
||||
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#define NDEBUG
|
||||
|
||||
#include "vga.h"
|
||||
#include "bios.h"
|
||||
|
||||
/* PRIVATE VARIABLES **********************************************************/
|
||||
|
||||
static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
|
||||
static BYTE VgaMiscRegister;
|
||||
static BYTE VgaSeqIndex = VGA_SEQ_RESET_REG;
|
||||
static BYTE VgaSeqRegisters[VGA_SEQ_MAX_REG];
|
||||
static BYTE VgaGcIndex = VGA_GC_RESET_REG;
|
||||
static BYTE VgaGcRegisters[VGA_GC_MAX_REG];
|
||||
static BYTE VgaCrtcIndex = VGA_CRTC_HORZ_TOTAL_REG;
|
||||
static BYTE VgaCrtcRegisters[VGA_CRTC_MAX_REG];
|
||||
static BYTE VgaAcIndex = VGA_AC_PAL_0_REG;
|
||||
static BOOLEAN VgaAcLatch = FALSE;
|
||||
static BYTE VgaAcRegisters[VGA_AC_MAX_REG];
|
||||
static BYTE VgaDacIndex = 0;
|
||||
static BOOLEAN VgaDacReadWrite = FALSE;
|
||||
static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
|
||||
static BOOLEAN InVerticalRetrace = FALSE;
|
||||
static BOOLEAN InHorizontalRetrace = FALSE;
|
||||
static HANDLE TextConsoleBuffer = NULL;
|
||||
static HANDLE GraphicsConsoleBuffer = NULL;
|
||||
static LPVOID ConsoleFramebuffer = NULL;
|
||||
static HANDLE ConsoleMutex = NULL;
|
||||
static BOOLEAN NeedsUpdate = FALSE;
|
||||
static BOOLEAN ModeChanged = TRUE;
|
||||
static BOOLEAN CursorMoved = FALSE;
|
||||
static BOOLEAN TextMode = TRUE;
|
||||
static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
|
||||
|
||||
/* PRIVATE FUNCTIONS **********************************************************/
|
||||
|
||||
static inline INT VgaGetAddressSize(VOID)
|
||||
{
|
||||
if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
|
||||
{
|
||||
/* Double-word addressing */
|
||||
return 4;
|
||||
}
|
||||
|
||||
if (VgaCrtcRegisters[VGA_CRTC_MODE_CONTROL_REG] & VGA_CRTC_MODE_CONTROL_BYTE)
|
||||
{
|
||||
/* Byte addressing */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Word addressing */
|
||||
return 2;
|
||||
}
|
||||
|
||||
static inline DWORD VgaTranslateReadAddress(DWORD Address)
|
||||
{
|
||||
CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
|
||||
DWORD Offset = Address - MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
|
||||
BYTE Plane;
|
||||
|
||||
/* Check for chain-4 and odd-even mode */
|
||||
if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
|
||||
{
|
||||
/* The lowest two bits are the plane number */
|
||||
Plane = Offset & 3;
|
||||
Offset >>= 2;
|
||||
}
|
||||
else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
|
||||
{
|
||||
/* The LSB is the plane number */
|
||||
Plane = Offset & 1;
|
||||
Offset >>= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use the read mode */
|
||||
Plane = VgaGcRegisters[VGA_GC_READ_MAP_SEL_REG] & 0x03;
|
||||
}
|
||||
|
||||
/* Multiply the offset by the address size */
|
||||
Offset *= VgaGetAddressSize();
|
||||
|
||||
return Offset + Plane * VGA_BANK_SIZE;
|
||||
}
|
||||
|
||||
static inline DWORD VgaTranslateWriteAddress(DWORD Address)
|
||||
{
|
||||
CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
|
||||
DWORD Offset = Address - MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
|
||||
|
||||
/* Check for chain-4 and odd-even mode */
|
||||
if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
|
||||
{
|
||||
/* Shift the offset to the right by 2 */
|
||||
Offset >>= 2;
|
||||
}
|
||||
else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
|
||||
{
|
||||
/* Shift the offset to the right by 1 */
|
||||
Offset >>= 1;
|
||||
}
|
||||
|
||||
/* Multiply the offset by the address size */
|
||||
Offset *= VgaGetAddressSize();
|
||||
|
||||
/* Return the offset on plane 0 */
|
||||
return Offset;
|
||||
}
|
||||
|
||||
static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
|
||||
{
|
||||
DPRINT("VgaMarkForUpdate: Row %d, Column %d\n", Row, Column);
|
||||
|
||||
/* Check if this is the first time the rectangle is updated */
|
||||
if (!NeedsUpdate)
|
||||
{
|
||||
UpdateRectangle.Left = UpdateRectangle.Top = (SHORT)0x7FFF;
|
||||
UpdateRectangle.Right = UpdateRectangle.Bottom = (SHORT)0x8000;
|
||||
}
|
||||
|
||||
/* Expand the rectangle to include the point */
|
||||
UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
|
||||
UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
|
||||
UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
|
||||
UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
|
||||
|
||||
/* Set the update request flag */
|
||||
NeedsUpdate = TRUE;
|
||||
}
|
||||
|
||||
static VOID VgaWriteSequencer(BYTE Data)
|
||||
{
|
||||
ASSERT(VgaSeqIndex < VGA_SEQ_MAX_REG);
|
||||
|
||||
/* Save the value */
|
||||
VgaSeqRegisters[VgaSeqIndex] = Data;
|
||||
}
|
||||
|
||||
static VOID VgaWriteGc(BYTE Data)
|
||||
{
|
||||
ASSERT(VgaGcIndex < VGA_GC_MAX_REG);
|
||||
|
||||
/* Save the value */
|
||||
VgaGcRegisters[VgaGcIndex] = Data;
|
||||
|
||||
/* Check the index */
|
||||
switch (VgaGcIndex)
|
||||
{
|
||||
case VGA_GC_MISC_REG:
|
||||
{
|
||||
/* The GC misc register decides if it's text or graphics mode */
|
||||
ModeChanged = TRUE;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static VOID VgaWriteCrtc(BYTE Data)
|
||||
{
|
||||
ASSERT(VgaGcIndex < VGA_CRTC_MAX_REG);
|
||||
|
||||
/* Save the value */
|
||||
VgaCrtcRegisters[VgaCrtcIndex] = Data;
|
||||
|
||||
/* Check the index */
|
||||
switch (VgaCrtcIndex)
|
||||
{
|
||||
case VGA_CRTC_END_HORZ_DISP_REG:
|
||||
case VGA_CRTC_VERT_DISP_END_REG:
|
||||
case VGA_CRTC_OVERFLOW_REG:
|
||||
{
|
||||
/* The video mode has changed */
|
||||
ModeChanged = TRUE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_CRTC_CURSOR_LOC_LOW_REG:
|
||||
case VGA_CRTC_CURSOR_LOC_HIGH_REG:
|
||||
case VGA_CRTC_CURSOR_START_REG:
|
||||
case VGA_CRTC_CURSOR_END_REG:
|
||||
{
|
||||
/* Set the cursor moved flag */
|
||||
CursorMoved = TRUE;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static VOID VgaWriteDac(BYTE Data)
|
||||
{
|
||||
/* Set the value */
|
||||
VgaDacRegisters[VgaDacIndex++] = Data;
|
||||
VgaDacIndex %= VGA_PALETTE_SIZE;
|
||||
|
||||
// TODO: Change the palette!
|
||||
}
|
||||
|
||||
static VOID VgaWriteAc(BYTE Data)
|
||||
{
|
||||
ASSERT(VgaAcIndex < VGA_AC_MAX_REG);
|
||||
|
||||
/* Save the value */
|
||||
VgaAcRegisters[VgaAcIndex] = Data;
|
||||
}
|
||||
|
||||
static VOID VgaEnterGraphicsMode(UINT Width, UINT Height, UINT BitDepth)
|
||||
{
|
||||
INT i;
|
||||
CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
|
||||
BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
|
||||
LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
|
||||
LPWORD PaletteIndex = (LPWORD)(BitmapInfoBuffer + sizeof(BITMAPINFOHEADER));
|
||||
|
||||
/* Fill the bitmap info header */
|
||||
ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
|
||||
BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
BitmapInfo->bmiHeader.biWidth = Width;
|
||||
BitmapInfo->bmiHeader.biHeight = Height;
|
||||
BitmapInfo->bmiHeader.biBitCount = 8;
|
||||
BitmapInfo->bmiHeader.biPlanes = 1;
|
||||
BitmapInfo->bmiHeader.biCompression = BI_RGB;
|
||||
BitmapInfo->bmiHeader.biSizeImage = Width * Height * (BitDepth / 8);
|
||||
|
||||
/* Fill the palette data */
|
||||
for (i = 0; i < BitDepth; i++) PaletteIndex[i] = i;
|
||||
|
||||
/* Fill the console graphics buffer info */
|
||||
GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE;
|
||||
GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
|
||||
GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
|
||||
|
||||
/* Create the buffer */
|
||||
GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
CONSOLE_GRAPHICS_BUFFER,
|
||||
&GraphicsBufferInfo);
|
||||
|
||||
/* Save the framebuffer address and mutex */
|
||||
ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap;
|
||||
ConsoleMutex = GraphicsBufferInfo.hMutex;
|
||||
|
||||
/* Set the active buffer */
|
||||
SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer);
|
||||
}
|
||||
|
||||
static VOID VgaLeaveGraphicsMode()
|
||||
{
|
||||
/* Switch back to the text buffer */
|
||||
SetConsoleActiveScreenBuffer(TextConsoleBuffer);
|
||||
|
||||
/* Cleanup the video data */
|
||||
CloseHandle(ConsoleMutex);
|
||||
CloseHandle(GraphicsConsoleBuffer);
|
||||
GraphicsConsoleBuffer = NULL;
|
||||
}
|
||||
|
||||
static VOID VgaUpdateMode(VOID)
|
||||
{
|
||||
COORD Resolution = VgaGetDisplayResolution();
|
||||
|
||||
if (!TextMode)
|
||||
{
|
||||
/* Switching from graphics mode to text mode */
|
||||
VgaLeaveGraphicsMode();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Free the old framebuffer */
|
||||
HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer);
|
||||
ConsoleFramebuffer = NULL;
|
||||
}
|
||||
|
||||
/* Check if the new mode is alphanumeric */
|
||||
if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA))
|
||||
{
|
||||
/* Resize the console */
|
||||
SetConsoleScreenBufferSize(TextConsoleBuffer, Resolution);
|
||||
|
||||
/* Allocate a framebuffer */
|
||||
ConsoleFramebuffer = HeapAlloc(GetProcessHeap(),
|
||||
HEAP_ZERO_MEMORY,
|
||||
sizeof(CHAR_INFO)
|
||||
* Resolution.X
|
||||
* Resolution.Y);
|
||||
if (ConsoleFramebuffer == NULL)
|
||||
{
|
||||
DisplayMessage(L"An unexpected error occurred!\n");
|
||||
VdmRunning = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the text mode flag */
|
||||
TextMode = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Enter 8-bit graphics mode */
|
||||
VgaEnterGraphicsMode(Resolution.X, Resolution.Y, 8);
|
||||
|
||||
/* Clear the text mode flag */
|
||||
TextMode = FALSE;
|
||||
}
|
||||
|
||||
/* Perform a full update */
|
||||
NeedsUpdate = TRUE;
|
||||
UpdateRectangle.Left = 0;
|
||||
UpdateRectangle.Top = 0;
|
||||
UpdateRectangle.Right = Resolution.X;
|
||||
UpdateRectangle.Bottom = Resolution.Y;
|
||||
}
|
||||
|
||||
static VOID VgaUpdateFramebuffer(VOID)
|
||||
{
|
||||
INT i, j, k;
|
||||
COORD Resolution = VgaGetDisplayResolution();
|
||||
INT AddressSize = VgaGetAddressSize();
|
||||
DWORD Address = (VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG] << 8)
|
||||
+ VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG];
|
||||
DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
|
||||
PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
|
||||
PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
|
||||
|
||||
/* Loop through the scanlines */
|
||||
for (i = 0; i < Resolution.Y; i++)
|
||||
{
|
||||
/* Check if this is text mode or graphics mode */
|
||||
if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
|
||||
{
|
||||
/* Graphics mode */
|
||||
|
||||
/* Loop through the pixels */
|
||||
for (j = 0; j < Resolution.X; j++)
|
||||
{
|
||||
BYTE PixelData = 0;
|
||||
|
||||
/* Check the shifting mode */
|
||||
if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFT256)
|
||||
{
|
||||
/* 4 bits shifted from each plane */
|
||||
|
||||
/* Check if this is 16 or 256 color mode */
|
||||
if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
|
||||
{
|
||||
/* One byte per pixel */
|
||||
PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
|
||||
+ (Address + (j / VGA_NUM_BANKS))
|
||||
* AddressSize];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 4-bits per pixel */
|
||||
|
||||
PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
|
||||
+ (Address + (j / (VGA_NUM_BANKS * 2)))
|
||||
* AddressSize];
|
||||
|
||||
/* Check if we should use the highest 4 bits or lowest 4 */
|
||||
if (((j / VGA_NUM_BANKS) % 2) == 0)
|
||||
{
|
||||
/* Highest 4 */
|
||||
PixelData >>= 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Lowest 4 */
|
||||
PixelData &= 0x0F;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFTREG)
|
||||
{
|
||||
/*
|
||||
* 2 bits shifted from plane 0 and 2 for the first 4 pixels,
|
||||
* then 2 bits shifted from plane 1 and 3 for the next 4
|
||||
*/
|
||||
|
||||
// TODO: NOT IMPLEMENTED!
|
||||
DPRINT1("Interleaved shift mode is not implemented!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 1 bit shifted from each plane */
|
||||
|
||||
/* Check if this is 16 or 256 color mode */
|
||||
if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
|
||||
{
|
||||
/* 8 bits per pixel, 2 on each plane */
|
||||
|
||||
for (k = 0; k < VGA_NUM_BANKS; k++)
|
||||
{
|
||||
/* The data is on plane k, 4 pixels per byte */
|
||||
BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
|
||||
+ (Address + (j / 4)) * AddressSize];
|
||||
|
||||
/* The mask of the first bit in the pair */
|
||||
BYTE BitMask = 1 << (((3 - (j % 4)) * 2) + 1);
|
||||
|
||||
/* Bits 0, 1, 2 and 3 come from the first bit of the pair */
|
||||
if (PlaneData & BitMask) PixelData |= 1 << k;
|
||||
|
||||
/* Bits 4, 5, 6 and 7 come from the second bit of the pair */
|
||||
if (PlaneData & (BitMask >> 1)) PixelData |= 1 << (k + 4);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 4 bits per pixel, 1 on each plane */
|
||||
|
||||
for (k = 0; k < VGA_NUM_BANKS; k++)
|
||||
{
|
||||
BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
|
||||
+ (Address + (j / 8)) * AddressSize];
|
||||
|
||||
/* If the bit on that plane is set, set it */
|
||||
if (PlaneData & (1 << (7 - (j % 8)))) PixelData |= 1 << k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now check if the resulting pixel data has changed */
|
||||
if (GraphicsBuffer[i * Resolution.X + j] != PixelData)
|
||||
{
|
||||
/* Yes, write the new value */
|
||||
GraphicsBuffer[i * Resolution.X + j] = PixelData;
|
||||
|
||||
/* Mark the specified pixel as changed */
|
||||
VgaMarkForUpdate(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Text mode */
|
||||
|
||||
/* Loop through the characters */
|
||||
for (j = 0; j < Resolution.X; j++)
|
||||
{
|
||||
DWORD CurrentAddr = LOWORD((Address + j) * AddressSize);
|
||||
CHAR_INFO CharInfo;
|
||||
|
||||
/* Plane 0 holds the character itself */
|
||||
CharInfo.Char.AsciiChar = VgaMemory[CurrentAddr];
|
||||
|
||||
/* Plane 1 holds the attribute */
|
||||
CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE];
|
||||
|
||||
/* Now check if the resulting character data has changed */
|
||||
if ((CharBuffer[i * Resolution.X + j].Char.AsciiChar != CharInfo.Char.AsciiChar)
|
||||
|| (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes))
|
||||
{
|
||||
/* Yes, write the new value */
|
||||
CharBuffer[i * Resolution.X + j] = CharInfo;
|
||||
|
||||
/* Mark the specified pixel as changed */
|
||||
VgaMarkForUpdate(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Move to the next scanline */
|
||||
Address += ScanlineSize;
|
||||
}
|
||||
}
|
||||
|
||||
static VOID VgaUpdateTextCursor(VOID)
|
||||
{
|
||||
COORD Position;
|
||||
CONSOLE_CURSOR_INFO CursorInfo;
|
||||
BYTE CursorStart = VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x3F;
|
||||
BYTE CursorEnd = VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] & 0x1F;
|
||||
DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
|
||||
WORD Location = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG],
|
||||
VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG]);
|
||||
|
||||
if (CursorStart < CursorEnd)
|
||||
{
|
||||
/* Visible cursor */
|
||||
CursorInfo.bVisible = TRUE;
|
||||
CursorInfo.dwSize = (100 * (CursorEnd - CursorStart)) >> 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No cursor */
|
||||
CursorInfo.bVisible = FALSE;
|
||||
CursorInfo.dwSize = 0;
|
||||
}
|
||||
|
||||
/* Add the cursor skew to the location */
|
||||
Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 3;
|
||||
|
||||
/* Find the coordinates of the new position */
|
||||
Position.X = Location % ScanlineSize;
|
||||
Position.Y = Location / ScanlineSize;
|
||||
|
||||
/* Update the physical cursor */
|
||||
SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
|
||||
SetConsoleCursorPosition(TextConsoleBuffer, Position);
|
||||
}
|
||||
|
||||
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||
|
||||
DWORD VgaGetVideoBaseAddress(VOID)
|
||||
{
|
||||
CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
|
||||
return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
|
||||
}
|
||||
|
||||
DWORD VgaGetVideoLimitAddress(VOID)
|
||||
{
|
||||
CONST DWORD MemoryLimit[] = { 0xA7FFF, 0xA7FFF, 0xB7FFF, 0xBFFFF };
|
||||
return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
|
||||
}
|
||||
|
||||
COORD VgaGetDisplayResolution(VOID)
|
||||
{
|
||||
COORD Resolution;
|
||||
|
||||
/* The low 8 bits are in the display registers */
|
||||
Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
|
||||
Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG];
|
||||
|
||||
/* Set the top bits from the overflow register */
|
||||
if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8)
|
||||
{
|
||||
Resolution.Y |= 1 << 8;
|
||||
}
|
||||
if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9)
|
||||
{
|
||||
Resolution.Y |= 1 << 9;
|
||||
}
|
||||
|
||||
/* Increase the values by 1 */
|
||||
Resolution.X++;
|
||||
Resolution.Y++;
|
||||
|
||||
if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
|
||||
{
|
||||
/* Multiply the horizontal resolution by the 9/8 dot mode */
|
||||
Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
|
||||
? 8 : 9;
|
||||
|
||||
/* The horizontal resolution is halved in 8-bit mode */
|
||||
if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
|
||||
|
||||
/* Divide the vertical resolution by the maximum scan line */
|
||||
Resolution.Y /= ((DWORD)VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Divide the number of scanlines by the font size */
|
||||
Resolution.Y /= 16;
|
||||
}
|
||||
|
||||
/* Return the resolution */
|
||||
return Resolution;
|
||||
}
|
||||
|
||||
VOID VgaRefreshDisplay(VOID)
|
||||
{
|
||||
COORD Resolution = VgaGetDisplayResolution();
|
||||
|
||||
DPRINT("VgaRefreshDisplay\n");
|
||||
|
||||
if (ModeChanged)
|
||||
{
|
||||
/* Change the display mode */
|
||||
VgaUpdateMode();
|
||||
|
||||
/* Reset the mode change flag */
|
||||
ModeChanged = FALSE;
|
||||
}
|
||||
|
||||
if (CursorMoved)
|
||||
{
|
||||
/* Change the text cursor location */
|
||||
VgaUpdateTextCursor();
|
||||
|
||||
/* Reset the cursor move flag */
|
||||
CursorMoved = FALSE;
|
||||
}
|
||||
|
||||
/* Update the contents of the framebuffer */
|
||||
VgaUpdateFramebuffer();
|
||||
|
||||
/* Set the vertical retrace flag */
|
||||
InVerticalRetrace = TRUE;
|
||||
|
||||
/* Ignore if there's nothing to update */
|
||||
if (!NeedsUpdate) return;
|
||||
|
||||
DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
|
||||
UpdateRectangle.Left,
|
||||
UpdateRectangle.Top,
|
||||
UpdateRectangle.Right,
|
||||
UpdateRectangle.Bottom);
|
||||
|
||||
/* Check if this is text mode or graphics mode */
|
||||
if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
|
||||
{
|
||||
/* Graphics mode */
|
||||
|
||||
/* Redraw the screen */
|
||||
InvalidateConsoleDIBits(GraphicsConsoleBuffer, &UpdateRectangle);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Text mode */
|
||||
COORD Origin = { UpdateRectangle.Left, UpdateRectangle.Top };
|
||||
|
||||
/* Write the data to the console */
|
||||
WriteConsoleOutputA(TextConsoleBuffer,
|
||||
(PCHAR_INFO)ConsoleFramebuffer,
|
||||
Resolution,
|
||||
Origin,
|
||||
&UpdateRectangle);
|
||||
|
||||
}
|
||||
|
||||
/* Clear the update flag */
|
||||
NeedsUpdate = FALSE;
|
||||
}
|
||||
|
||||
VOID VgaHorizontalRetrace(VOID)
|
||||
{
|
||||
/* Set the flag */
|
||||
InHorizontalRetrace = TRUE;
|
||||
}
|
||||
|
||||
VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
|
||||
{
|
||||
INT i;
|
||||
|
||||
DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n",
|
||||
Address,
|
||||
Size);
|
||||
|
||||
/* Ignore if video RAM access is disabled */
|
||||
if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
|
||||
|
||||
/* Loop through each byte */
|
||||
for (i = 0; i < Size; i++)
|
||||
{
|
||||
DWORD VideoAddress = VgaTranslateReadAddress(Address + i);
|
||||
|
||||
/* Copy the value to the buffer */
|
||||
Buffer[i] = VgaMemory[VideoAddress];
|
||||
}
|
||||
}
|
||||
|
||||
VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
|
||||
{
|
||||
INT i, j;
|
||||
|
||||
DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n",
|
||||
Address,
|
||||
Size);
|
||||
|
||||
/* Ignore if video RAM access is disabled */
|
||||
if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
|
||||
|
||||
/* Also ignore if write access to all planes is disabled */
|
||||
if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return;
|
||||
|
||||
/* Loop through each byte */
|
||||
for (i = 0; i < Size; i++)
|
||||
{
|
||||
DWORD VideoAddress = VgaTranslateWriteAddress(Address + i);
|
||||
|
||||
for (j = 0; j < VGA_NUM_BANKS; j++)
|
||||
{
|
||||
/* Make sure the page is writeable */
|
||||
if (!(VgaSeqRegisters[VGA_SEQ_MASK_REG] & (1 << j))) continue;
|
||||
|
||||
/* Check if this is chain-4 mode */
|
||||
if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
|
||||
{
|
||||
if (((Address + i) & 3) != j)
|
||||
{
|
||||
/* This plane will not be accessed */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if this is odd-even mode */
|
||||
if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
|
||||
{
|
||||
if (((Address + i) & 1) != (j & 1))
|
||||
{
|
||||
/* This plane will not be accessed */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy the value to the VGA memory */
|
||||
VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = Buffer[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BYTE VgaReadPort(WORD Port)
|
||||
{
|
||||
DPRINT("VgaReadPort: Port 0x%04X\n", Port);
|
||||
|
||||
switch (Port)
|
||||
{
|
||||
case VGA_AC_INDEX:
|
||||
{
|
||||
return VgaAcIndex;
|
||||
}
|
||||
|
||||
case VGA_AC_READ:
|
||||
{
|
||||
return VgaAcRegisters[VgaAcIndex];
|
||||
}
|
||||
|
||||
case VGA_SEQ_INDEX:
|
||||
{
|
||||
return VgaSeqIndex;
|
||||
}
|
||||
|
||||
case VGA_SEQ_DATA:
|
||||
{
|
||||
return VgaSeqRegisters[VgaSeqIndex];
|
||||
}
|
||||
|
||||
case VGA_DAC_READ_INDEX:
|
||||
{
|
||||
/* This returns the read/write state */
|
||||
return VgaDacReadWrite ? 0 : 3;
|
||||
}
|
||||
|
||||
case VGA_DAC_WRITE_INDEX:
|
||||
{
|
||||
return VgaDacIndex;
|
||||
}
|
||||
|
||||
case VGA_DAC_DATA:
|
||||
{
|
||||
/* Ignore reads in write mode */
|
||||
if (!VgaDacReadWrite)
|
||||
{
|
||||
BYTE Data = VgaDacRegisters[VgaDacIndex++];
|
||||
VgaDacIndex %= VGA_PALETTE_SIZE;
|
||||
return Data;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_MISC_READ:
|
||||
{
|
||||
return VgaMiscRegister;
|
||||
}
|
||||
|
||||
case VGA_CRTC_INDEX:
|
||||
{
|
||||
return VgaCrtcIndex;
|
||||
}
|
||||
|
||||
case VGA_CRTC_DATA:
|
||||
{
|
||||
return VgaCrtcRegisters[VgaCrtcIndex];
|
||||
}
|
||||
|
||||
case VGA_GC_INDEX:
|
||||
{
|
||||
return VgaGcIndex;
|
||||
}
|
||||
|
||||
case VGA_GC_DATA:
|
||||
{
|
||||
return VgaGcRegisters[VgaGcIndex];
|
||||
}
|
||||
|
||||
case VGA_STAT_MONO:
|
||||
case VGA_STAT_COLOR:
|
||||
{
|
||||
BYTE Result = 0;
|
||||
|
||||
/* Reset the AC latch */
|
||||
VgaAcLatch = FALSE;
|
||||
|
||||
/* Set a flag if there is a vertical or horizontal retrace */
|
||||
if (InVerticalRetrace || InHorizontalRetrace) Result |= VGA_STAT_DD;
|
||||
|
||||
/* Set an additional flag if there was a vertical retrace */
|
||||
if (InVerticalRetrace) Result |= VGA_STAT_VRETRACE;
|
||||
|
||||
/* Clear the flags */
|
||||
InHorizontalRetrace = InVerticalRetrace = FALSE;
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VOID VgaWritePort(WORD Port, BYTE Data)
|
||||
{
|
||||
DPRINT("VgaWritePort: Port 0x%04X, Data 0x%02X\n", Port, Data);
|
||||
|
||||
switch (Port)
|
||||
{
|
||||
case VGA_AC_INDEX:
|
||||
{
|
||||
if (!VgaAcLatch)
|
||||
{
|
||||
/* Change the index */
|
||||
if (Data < VGA_AC_MAX_REG) VgaAcIndex = Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Write the data */
|
||||
VgaWriteAc(Data);
|
||||
}
|
||||
|
||||
/* Toggle the latch */
|
||||
VgaAcLatch = !VgaAcLatch;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_SEQ_INDEX:
|
||||
{
|
||||
/* Set the sequencer index register */
|
||||
if (Data < VGA_SEQ_MAX_REG) VgaSeqIndex = Data;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_SEQ_DATA:
|
||||
{
|
||||
/* Call the sequencer function */
|
||||
VgaWriteSequencer(Data);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_DAC_READ_INDEX:
|
||||
{
|
||||
VgaDacReadWrite = FALSE;
|
||||
VgaDacIndex = Data % VGA_PALETTE_SIZE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_DAC_WRITE_INDEX:
|
||||
{
|
||||
VgaDacReadWrite = TRUE;
|
||||
VgaDacIndex = Data % VGA_PALETTE_SIZE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_DAC_DATA:
|
||||
{
|
||||
/* Ignore writes in read mode */
|
||||
if (VgaDacReadWrite) VgaWriteDac(Data & 0x3F);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_MISC_WRITE:
|
||||
{
|
||||
VgaMiscRegister = Data;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_CRTC_INDEX:
|
||||
{
|
||||
/* Set the CRTC index register */
|
||||
if (Data < VGA_CRTC_MAX_REG) VgaCrtcIndex = Data;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_CRTC_DATA:
|
||||
{
|
||||
/* Call the CRTC function */
|
||||
VgaWriteCrtc(Data);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_GC_INDEX:
|
||||
{
|
||||
/* Set the GC index register */
|
||||
if (Data < VGA_GC_MAX_REG) VgaGcIndex = Data;
|
||||
break;
|
||||
}
|
||||
|
||||
case VGA_GC_DATA:
|
||||
{
|
||||
/* Call the GC function */
|
||||
VgaWriteGc(Data);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VOID VgaInitialize(HANDLE TextHandle)
|
||||
{
|
||||
INT i, j;
|
||||
COORD Resolution;
|
||||
INT AddressSize;
|
||||
DWORD ScanlineSize;
|
||||
COORD Origin = { 0, 0 };
|
||||
SMALL_RECT ScreenRect;
|
||||
PCHAR_INFO CharBuffer;
|
||||
DWORD Address = 0;
|
||||
|
||||
/* Set the global handle */
|
||||
TextConsoleBuffer = TextHandle;
|
||||
|
||||
/* Set the default video mode */
|
||||
BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
|
||||
VgaUpdateMode();
|
||||
ModeChanged = FALSE;
|
||||
|
||||
/* Get the data */
|
||||
Resolution = VgaGetDisplayResolution();
|
||||
CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
|
||||
AddressSize = VgaGetAddressSize();
|
||||
ScreenRect.Left = ScreenRect.Top = 0;
|
||||
ScreenRect.Right = Resolution.X;
|
||||
ScreenRect.Bottom = Resolution.Y;
|
||||
ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
|
||||
|
||||
/* Read the data from the console into the framebuffer */
|
||||
ReadConsoleOutputA(TextConsoleBuffer,
|
||||
ConsoleFramebuffer,
|
||||
Resolution,
|
||||
Origin,
|
||||
&ScreenRect);
|
||||
|
||||
|
||||
/* Loop through the scanlines */
|
||||
for (i = 0; i < Resolution.Y; i++)
|
||||
{
|
||||
/* Loop through the characters */
|
||||
for (j = 0; j < Resolution.X; j++)
|
||||
{
|
||||
DWORD CurrentAddr = LOWORD((Address + j) * AddressSize);
|
||||
|
||||
/* Store the character in plane 0 */
|
||||
VgaMemory[CurrentAddr] = CharBuffer[i * Resolution.X + j].Char.AsciiChar;
|
||||
|
||||
/* Store the attribute in plane 1 */
|
||||
VgaMemory[CurrentAddr + VGA_BANK_SIZE] = CharBuffer[i * Resolution.X + j].Attributes;
|
||||
}
|
||||
|
||||
/* Move to the next scanline */
|
||||
Address += ScanlineSize;
|
||||
}
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
|
200
subsystems/ntvdm/vga.h
Normal file
200
subsystems/ntvdm/vga.h
Normal file
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* COPYRIGHT: GPL - See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Virtual DOS Machine
|
||||
* FILE: vga.h
|
||||
* PURPOSE: VGA hardware emulation (header file)
|
||||
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
||||
*/
|
||||
|
||||
#ifndef _VGA_H_
|
||||
#define _VGA_H_
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include "ntvdm.h"
|
||||
|
||||
/* DEFINES ********************************************************************/
|
||||
|
||||
/* Register I/O ports */
|
||||
#define VGA_AC_INDEX 0x3C0
|
||||
#define VGA_AC_WRITE 0x3C0
|
||||
#define VGA_AC_READ 0x3C1
|
||||
#define VGA_SEQ_INDEX 0x3C4
|
||||
#define VGA_SEQ_DATA 0x3C5
|
||||
#define VGA_DAC_READ_INDEX 0x3C7
|
||||
#define VGA_DAC_WRITE_INDEX 0x3C8
|
||||
#define VGA_DAC_DATA 0x3C9
|
||||
#define VGA_MISC_READ 0x3CC
|
||||
#define VGA_MISC_WRITE 0x3C2
|
||||
#define VGA_CRTC_INDEX 0x3D4
|
||||
#define VGA_CRTC_DATA 0x3D5
|
||||
#define VGA_GC_INDEX 0x3CE
|
||||
#define VGA_GC_DATA 0x3CF
|
||||
#define VGA_STAT_MONO 0x3BA
|
||||
#define VGA_STAT_COLOR 0x3DA
|
||||
|
||||
#define VGA_NUM_BANKS 4
|
||||
#define VGA_BANK_SIZE 0x10000
|
||||
#define VGA_PALETTE_SIZE 768
|
||||
#define VGA_BITMAP_INFO_SIZE (sizeof(BITMAPINFOHEADER) + 2 * (VGA_PALETTE_SIZE / 3))
|
||||
|
||||
/* Sequencer reset register bits */
|
||||
#define VGA_SEQ_RESET_AR (1 << 0)
|
||||
#define VGA_SEQ_RESET_SR (1 << 1)
|
||||
|
||||
/* Sequencer clock register bits */
|
||||
#define VGA_SEQ_CLOCK_98DM (1 << 0)
|
||||
#define VGA_SEQ_CLOCK_SLR (1 << 2)
|
||||
#define VGA_SEQ_CLOCK_DCR (1 << 3)
|
||||
#define VGA_SEQ_CLOCK_S4 (1 << 4)
|
||||
#define VGA_SEQ_CLOCK_SD (1 << 5)
|
||||
|
||||
/* Sequencer memory register bits */
|
||||
#define VGA_SEQ_MEM_EXT (1 << 1)
|
||||
#define VGA_SEQ_MEM_OE (1 << 2)
|
||||
#define VGA_SEQ_MEM_C4 (1 << 3)
|
||||
|
||||
/* Graphics controller mode register bits */
|
||||
#define VGA_GC_MODE_READ (1 << 3)
|
||||
#define VGA_GC_MODE_OE (1 << 4)
|
||||
#define VGA_GC_MODE_SHIFTREG (1 << 5)
|
||||
#define VGA_GC_MODE_SHIFT256 (1 << 6)
|
||||
|
||||
/* Graphics controller miscellaneous register bits */
|
||||
#define VGA_GC_MISC_NOALPHA (1 << 0)
|
||||
#define VGA_GC_MISC_OE (1 << 1)
|
||||
|
||||
/* CRTC overflow register bits */
|
||||
#define VGA_CRTC_OVERFLOW_VT8 (1 << 0)
|
||||
#define VGA_CRTC_OVERFLOW_VDE8 (1 << 1)
|
||||
#define VGA_CRTC_OVERFLOW_VRS8 (1 << 2)
|
||||
#define VGA_CRTC_OVERFLOW_SVB8 (1 << 3)
|
||||
#define VGA_CRTC_OVERFLOW_LC8 (1 << 4)
|
||||
#define VGA_CRTC_OVERFLOW_VT9 (1 << 5)
|
||||
#define VGA_CRTC_OVERFLOW_VDE9 (1 << 6)
|
||||
#define VGA_CRTC_OVERFLOW_VRS9 (1 << 7)
|
||||
|
||||
/* CRTC underline register bits */
|
||||
#define VGA_CRTC_UNDERLINE_DWORD (1 << 6)
|
||||
|
||||
/* CRTC mode control register bits */
|
||||
#define VGA_CRTC_MODE_CONTROL_WRAP (1 << 5)
|
||||
#define VGA_CRTC_MODE_CONTROL_BYTE (1 << 6)
|
||||
#define VGA_CRTC_MODE_CONTROL_SYNC (1 << 7)
|
||||
|
||||
/* AC control register bits */
|
||||
#define VGA_AC_CONTROL_ATGE (1 << 0)
|
||||
#define VGA_AC_CONTROL_MONO (1 << 1)
|
||||
#define VGA_AC_CONTROL_LGE (1 << 2)
|
||||
#define VGA_AC_CONTROL_BLINK (1 << 3)
|
||||
#define VGA_AC_CONTROL_PPM (1 << 5)
|
||||
#define VGA_AC_CONTROL_8BIT (1 << 6)
|
||||
#define VGA_AC_CONTROL_P54S (1 << 7)
|
||||
|
||||
/* Miscellaneous register bits */
|
||||
#define VGA_MISC_COLOR (1 << 0)
|
||||
#define VGA_MISC_RAM_ENABLED (1 << 1)
|
||||
#define VGA_MISC_OE_INVERT (1 << 5)
|
||||
#define VGA_MISC_HSYNCP (1 << 6)
|
||||
#define VGA_MISC_VSYNCP (1 << 7)
|
||||
|
||||
/* Status register flags */
|
||||
#define VGA_STAT_DD (1 << 0)
|
||||
#define VGA_STAT_VRETRACE (1 << 3)
|
||||
|
||||
enum
|
||||
{
|
||||
VGA_SEQ_RESET_REG,
|
||||
VGA_SEQ_CLOCK_REG,
|
||||
VGA_SEQ_MASK_REG,
|
||||
VGA_SEQ_CHAR_REG,
|
||||
VGA_SEQ_MEM_REG,
|
||||
VGA_SEQ_MAX_REG
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
VGA_GC_RESET_REG,
|
||||
VGA_GC_ENABLE_RESET_REG,
|
||||
VGA_GC_COLOR_COMPARE_REG,
|
||||
VGA_GC_ROTATE_REG,
|
||||
VGA_GC_READ_MAP_SEL_REG,
|
||||
VGA_GC_MODE_REG,
|
||||
VGA_GC_MISC_REG,
|
||||
VGA_GC_COLOR_IGNORE_REG,
|
||||
VGA_GC_BITMASK_REG,
|
||||
VGA_GC_MAX_REG
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
VGA_CRTC_HORZ_TOTAL_REG,
|
||||
VGA_CRTC_END_HORZ_DISP_REG,
|
||||
VGA_CRTC_START_HORZ_BLANKING_REG,
|
||||
VGA_CRTC_END_HORZ_BLANKING_REG,
|
||||
VGA_CRTC_START_HORZ_RETRACE_REG,
|
||||
VGA_CRTC_END_HORZ_RETRACE_REG,
|
||||
VGA_CRTC_VERT_TOTAL_REG,
|
||||
VGA_CRTC_OVERFLOW_REG,
|
||||
VGA_CRTC_PRESET_ROW_SCAN_REG,
|
||||
VGA_CRTC_MAX_SCAN_LINE_REG,
|
||||
VGA_CRTC_CURSOR_START_REG,
|
||||
VGA_CRTC_CURSOR_END_REG,
|
||||
VGA_CRTC_START_ADDR_HIGH_REG,
|
||||
VGA_CRTC_START_ADDR_LOW_REG,
|
||||
VGA_CRTC_CURSOR_LOC_HIGH_REG,
|
||||
VGA_CRTC_CURSOR_LOC_LOW_REG,
|
||||
VGA_CRTC_VERT_RETRACE_START_REG,
|
||||
VGA_CRTC_VERT_RETRACE_END_REG,
|
||||
VGA_CRTC_VERT_DISP_END_REG,
|
||||
VGA_CRTC_OFFSET_REG,
|
||||
VGA_CRTC_UNDERLINE_REG,
|
||||
VGA_CRTC_START_VERT_BLANKING_REG,
|
||||
VGA_CRTC_END_VERT_BLANKING,
|
||||
VGA_CRTC_MODE_CONTROL_REG,
|
||||
VGA_CRTC_LINE_COMPARE_REG,
|
||||
VGA_CRTC_MAX_REG
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
VGA_AC_PAL_0_REG,
|
||||
VGA_AC_PAL_1_REG,
|
||||
VGA_AC_PAL_2_REG,
|
||||
VGA_AC_PAL_3_REG,
|
||||
VGA_AC_PAL_4_REG,
|
||||
VGA_AC_PAL_5_REG,
|
||||
VGA_AC_PAL_6_REG,
|
||||
VGA_AC_PAL_7_REG,
|
||||
VGA_AC_PAL_8_REG,
|
||||
VGA_AC_PAL_9_REG,
|
||||
VGA_AC_PAL_A_REG,
|
||||
VGA_AC_PAL_B_REG,
|
||||
VGA_AC_PAL_C_REG,
|
||||
VGA_AC_PAL_D_REG,
|
||||
VGA_AC_PAL_E_REG,
|
||||
VGA_AC_PAL_F_REG,
|
||||
VGA_AC_CONTROL_REG,
|
||||
VGA_AC_OVERSCAN_REG,
|
||||
VGA_AC_COLOR_PLANE_REG,
|
||||
VGA_AC_HORZ_PANNING_REG,
|
||||
VGA_AC_COLOR_SEL_REG,
|
||||
VGA_AC_MAX_REG
|
||||
};
|
||||
|
||||
/* FUNCTIONS ******************************************************************/
|
||||
|
||||
DWORD VgaGetVideoBaseAddress(VOID);
|
||||
DWORD VgaGetVideoLimitAddress(VOID);
|
||||
COORD VgaGetDisplayResolution(VOID);
|
||||
VOID VgaRefreshDisplay(VOID);
|
||||
VOID VgaHorizontalRetrace(VOID);
|
||||
VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size);
|
||||
VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size);
|
||||
BYTE VgaReadPort(WORD Port);
|
||||
VOID VgaWritePort(WORD Port, BYTE Data);
|
||||
VOID VgaInitialize(HANDLE TextHandle);
|
||||
|
||||
#endif
|
||||
|
||||
/* EOF */
|
Loading…
Reference in a new issue