reactos/subsystems/mvdm/ntvdm/io.c

607 lines
16 KiB
C

/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: subsystems/mvdm/ntvdm/io.c
* PURPOSE: I/O Port Handlers
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
/* INCLUDES *******************************************************************/
#include "ntvdm.h"
#define NDEBUG
#include <debug.h>
#include "emulator.h"
#include "io.h"
/* PRIVATE VARIABLES **********************************************************/
typedef struct _EMULATOR_IO_HANDLERS
{
EMULATOR_INB_PROC InB;
EMULATOR_INW_PROC InW;
EMULATOR_IND_PROC InD;
EMULATOR_INSB_PROC InsB;
EMULATOR_INSW_PROC InsW;
EMULATOR_INSD_PROC InsD;
EMULATOR_OUTB_PROC OutB;
EMULATOR_OUTW_PROC OutW;
EMULATOR_OUTD_PROC OutD;
EMULATOR_OUTSB_PROC OutsB;
EMULATOR_OUTSW_PROC OutsW;
EMULATOR_OUTSD_PROC OutsD;
} EMULATOR_IO_HANDLERS, *PEMULATOR_IO_HANDLERS;
typedef struct _EMULATOR_IOPORT_HANDLERS
{
HANDLE hVdd; // == NULL if unused,
// INVALID_HANDLE_VALUE if handled internally,
// a valid VDD handle if handled externally.
union
{
/* For Windows compatibility only, not used internally... */
VDD_IO_HANDLERS VddIoHandlers;
/* ... we use these members internally */
EMULATOR_IO_HANDLERS IoHandlers;
};
} EMULATOR_IOPORT_HANDLERS, *PEMULATOR_IOPORT_HANDLERS;
/*
* This is the list of registered I/O Port handlers.
*/
EMULATOR_IOPORT_HANDLERS IoPortProc[EMULATOR_MAX_IOPORTS_NUM] = {{NULL}};
/* PUBLIC FUNCTIONS ***********************************************************/
UCHAR
IOReadB(USHORT Port)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.InB)
{
return IoPortProc[Port].IoHandlers.InB(Port);
}
else if (IoPortProc[Port].hVdd != NULL && IoPortProc[Port].hVdd != INVALID_HANDLE_VALUE &&
IoPortProc[Port].VddIoHandlers.inb_handler)
{
UCHAR Data;
ASSERT(Port <= MAXWORD);
IoPortProc[Port].VddIoHandlers.inb_handler(Port, &Data);
return Data;
}
else
{
/* Return an empty port byte value */
DPRINT("Read from unknown port: 0x%X\n", Port);
return 0xFF;
}
}
VOID
IOReadStrB(USHORT Port,
PUCHAR Buffer,
ULONG Count)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.InsB)
{
IoPortProc[Port].IoHandlers.InsB(Port, Buffer, Count);
}
else if (IoPortProc[Port].hVdd != NULL && IoPortProc[Port].hVdd != INVALID_HANDLE_VALUE &&
IoPortProc[Port].VddIoHandlers.insb_handler)
{
ASSERT(Port <= MAXWORD);
ASSERT(Count <= MAXWORD);
IoPortProc[Port].VddIoHandlers.insb_handler(Port, Buffer, (WORD)Count);
}
else
{
while (Count--) *Buffer++ = IOReadB(Port);
}
}
VOID
IOWriteB(USHORT Port,
UCHAR Buffer)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.OutB)
{
IoPortProc[Port].IoHandlers.OutB(Port, Buffer);
}
else if (IoPortProc[Port].hVdd != NULL && IoPortProc[Port].hVdd != INVALID_HANDLE_VALUE &&
IoPortProc[Port].VddIoHandlers.outb_handler)
{
ASSERT(Port <= MAXWORD);
IoPortProc[Port].VddIoHandlers.outb_handler(Port, Buffer);
}
else
{
/* Do nothing */
DPRINT("Write to unknown port: 0x%X\n", Port);
}
}
VOID
IOWriteStrB(USHORT Port,
PUCHAR Buffer,
ULONG Count)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.OutsB)
{
IoPortProc[Port].IoHandlers.OutsB(Port, Buffer, Count);
}
else if (IoPortProc[Port].hVdd != NULL && IoPortProc[Port].hVdd != INVALID_HANDLE_VALUE &&
IoPortProc[Port].VddIoHandlers.outsb_handler)
{
ASSERT(Port <= MAXWORD);
ASSERT(Count <= MAXWORD);
IoPortProc[Port].VddIoHandlers.outsb_handler(Port, Buffer, (WORD)Count);
}
else
{
while (Count--) IOWriteB(Port, *Buffer++);
}
}
USHORT
IOReadW(USHORT Port)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.InW)
{
return IoPortProc[Port].IoHandlers.InW(Port);
}
else if (IoPortProc[Port].hVdd != NULL && IoPortProc[Port].hVdd != INVALID_HANDLE_VALUE &&
IoPortProc[Port].VddIoHandlers.inw_handler)
{
USHORT Data;
ASSERT(Port <= MAXWORD);
IoPortProc[Port].VddIoHandlers.inw_handler(Port, &Data);
return Data;
}
else
{
UCHAR Low, High;
// FIXME: Is it ok on Little endian and Big endian ??
Low = IOReadB(Port);
High = IOReadB(Port + sizeof(UCHAR));
return MAKEWORD(Low, High);
}
}
VOID
IOReadStrW(USHORT Port,
PUSHORT Buffer,
ULONG Count)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.InsW)
{
IoPortProc[Port].IoHandlers.InsW(Port, Buffer, Count);
}
else if (IoPortProc[Port].hVdd != NULL && IoPortProc[Port].hVdd != INVALID_HANDLE_VALUE &&
IoPortProc[Port].VddIoHandlers.insw_handler)
{
ASSERT(Port <= MAXWORD);
ASSERT(Count <= MAXWORD);
IoPortProc[Port].VddIoHandlers.insw_handler(Port, Buffer, (WORD)Count);
}
else
{
while (Count--) *Buffer++ = IOReadW(Port);
}
}
VOID
IOWriteW(USHORT Port,
USHORT Buffer)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.OutW)
{
IoPortProc[Port].IoHandlers.OutW(Port, Buffer);
}
else if (IoPortProc[Port].hVdd != NULL && IoPortProc[Port].hVdd != INVALID_HANDLE_VALUE &&
IoPortProc[Port].VddIoHandlers.outw_handler)
{
ASSERT(Port <= MAXWORD);
IoPortProc[Port].VddIoHandlers.outw_handler(Port, Buffer);
}
else
{
// FIXME: Is it ok on Little endian and Big endian ??
IOWriteB(Port, LOBYTE(Buffer));
IOWriteB(Port + sizeof(UCHAR), HIBYTE(Buffer));
}
}
VOID
IOWriteStrW(USHORT Port,
PUSHORT Buffer,
ULONG Count)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.OutsW)
{
IoPortProc[Port].IoHandlers.OutsW(Port, Buffer, Count);
}
else if (IoPortProc[Port].hVdd != NULL && IoPortProc[Port].hVdd != INVALID_HANDLE_VALUE &&
IoPortProc[Port].VddIoHandlers.outsw_handler)
{
ASSERT(Port <= MAXWORD);
ASSERT(Count <= MAXWORD);
IoPortProc[Port].VddIoHandlers.outsw_handler(Port, Buffer, (WORD)Count);
}
else
{
while (Count--) IOWriteW(Port, *Buffer++);
}
}
ULONG
IOReadD(USHORT Port)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.InD)
{
return IoPortProc[Port].IoHandlers.InD(Port);
}
else
{
USHORT Low, High;
// FIXME: Is it ok on Little endian and Big endian ??
Low = IOReadW(Port);
High = IOReadW(Port + sizeof(USHORT));
return MAKELONG(Low, High);
}
}
VOID
IOReadStrD(USHORT Port,
PULONG Buffer,
ULONG Count)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.InsD)
{
IoPortProc[Port].IoHandlers.InsD(Port, Buffer, Count);
}
else
{
while (Count--) *Buffer++ = IOReadD(Port);
}
}
VOID
IOWriteD(USHORT Port,
ULONG Buffer)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.OutD)
{
IoPortProc[Port].IoHandlers.OutD(Port, Buffer);
}
else
{
// FIXME: Is it ok on Little endian and Big endian ??
IOWriteW(Port, LOWORD(Buffer));
IOWriteW(Port + sizeof(USHORT), HIWORD(Buffer));
}
}
VOID
IOWriteStrD(USHORT Port,
PULONG Buffer,
ULONG Count)
{
if (IoPortProc[Port].hVdd == INVALID_HANDLE_VALUE &&
IoPortProc[Port].IoHandlers.OutsD)
{
IoPortProc[Port].IoHandlers.OutsD(Port, Buffer, Count);
}
else
{
while (Count--) IOWriteD(Port, *Buffer++);
}
}
VOID RegisterIoPort(USHORT Port,
EMULATOR_INB_PROC InHandler,
EMULATOR_OUTB_PROC OutHandler)
{
if (IoPortProc[Port].IoHandlers.InB == NULL)
IoPortProc[Port].IoHandlers.InB = InHandler;
else
DPRINT1("IoPortProc[0x%X].IoHandlers.InB already registered\n", Port);
if (IoPortProc[Port].IoHandlers.OutB == NULL)
IoPortProc[Port].IoHandlers.OutB = OutHandler;
else
DPRINT1("IoPortProc[0x%X].IoHandlers.OutB already registered\n", Port);
/* We hold the I/O port internally */
IoPortProc[Port].hVdd = INVALID_HANDLE_VALUE;
}
VOID UnregisterIoPort(USHORT Port)
{
/*
* Put automagically all the fields to zero:
* the hVdd gets unregistered as well as all the handlers.
*/
// IoPortProc[Port] = {NULL};
RtlZeroMemory(&IoPortProc[Port], sizeof(IoPortProc[Port]));
}
VOID FASTCALL
EmulatorReadIo(PFAST486_STATE State,
USHORT Port,
PVOID Buffer,
ULONG DataCount,
UCHAR DataSize)
{
UNREFERENCED_PARAMETER(State);
if (DataSize == 0 || DataCount == 0) return;
if (DataSize == sizeof(UCHAR))
{
if (DataCount == 1)
*(PUCHAR)Buffer = IOReadB(Port);
else
IOReadStrB(Port, Buffer, DataCount);
}
else if (DataSize == sizeof(USHORT))
{
if (DataCount == 1)
*(PUSHORT)Buffer = IOReadW(Port);
else
IOReadStrW(Port, Buffer, DataCount);
}
else if (DataSize == sizeof(ULONG))
{
if (DataCount == 1)
*(PULONG)Buffer = IOReadD(Port);
else
IOReadStrD(Port, Buffer, DataCount);
}
else
{
PUCHAR Address = (PUCHAR)Buffer;
while (DataCount--)
{
ULONG CurrentPort = Port;
ULONG Count;
UCHAR NewDataSize = DataSize;
/* Read dword */
Count = NewDataSize >> 2; // NewDataSize / sizeof(ULONG);
NewDataSize = NewDataSize & 3; // NewDataSize % sizeof(ULONG);
while (Count--)
{
*(PULONG)Address = IOReadD(CurrentPort);
CurrentPort += sizeof(ULONG);
Address += sizeof(ULONG);
}
/* Read word */
Count = NewDataSize >> 1; // NewDataSize / sizeof(USHORT);
NewDataSize = NewDataSize & 1; // NewDataSize % sizeof(USHORT);
while (Count--)
{
*(PUSHORT)Address = IOReadW(CurrentPort);
CurrentPort += sizeof(USHORT);
Address += sizeof(USHORT);
}
/* Read byte */
Count = NewDataSize; // NewDataSize / sizeof(UCHAR);
// NewDataSize = NewDataSize % sizeof(UCHAR);
while (Count--)
{
*(PUCHAR)Address = IOReadB(CurrentPort);
CurrentPort += sizeof(UCHAR);
Address += sizeof(UCHAR);
}
}
}
}
VOID FASTCALL
EmulatorWriteIo(PFAST486_STATE State,
USHORT Port,
PVOID Buffer,
ULONG DataCount,
UCHAR DataSize)
{
UNREFERENCED_PARAMETER(State);
if (DataSize == 0 || DataCount == 0) return;
if (DataSize == sizeof(UCHAR))
{
if (DataCount == 1)
IOWriteB(Port, *(PUCHAR)Buffer);
else
IOWriteStrB(Port, Buffer, DataCount);
}
else if (DataSize == sizeof(USHORT))
{
if (DataCount == 1)
IOWriteW(Port, *(PUSHORT)Buffer);
else
IOWriteStrW(Port, Buffer, DataCount);
}
else if (DataSize == sizeof(ULONG))
{
if (DataCount == 1)
IOWriteD(Port, *(PULONG)Buffer);
else
IOWriteStrD(Port, Buffer, DataCount);
}
else
{
PUCHAR Address = (PUCHAR)Buffer;
while (DataCount--)
{
ULONG CurrentPort = Port;
ULONG Count;
UCHAR NewDataSize = DataSize;
/* Write dword */
Count = NewDataSize >> 2; // NewDataSize / sizeof(ULONG);
NewDataSize = NewDataSize & 3; // NewDataSize % sizeof(ULONG);
while (Count--)
{
IOWriteD(CurrentPort, *(PULONG)Address);
CurrentPort += sizeof(ULONG);
Address += sizeof(ULONG);
}
/* Write word */
Count = NewDataSize >> 1; // NewDataSize / sizeof(USHORT);
NewDataSize = NewDataSize & 1; // NewDataSize % sizeof(USHORT);
while (Count--)
{
IOWriteW(CurrentPort, *(PUSHORT)Address);
CurrentPort += sizeof(USHORT);
Address += sizeof(USHORT);
}
/* Write byte */
Count = NewDataSize; // NewDataSize / sizeof(UCHAR);
// NewDataSize = NewDataSize % sizeof(UCHAR);
while (Count--)
{
IOWriteB(CurrentPort, *(PUCHAR)Address);
CurrentPort += sizeof(UCHAR);
Address += sizeof(UCHAR);
}
}
}
}
BOOL
WINAPI
VDDInstallIOHook(IN HANDLE hVdd,
IN WORD cPortRange,
IN PVDD_IO_PORTRANGE pPortRange,
IN PVDD_IO_HANDLERS IoHandlers)
{
WORD i;
/* Check validity of the VDD handle */
if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
/* Loop for each range of I/O ports */
while (cPortRange--)
{
/* Register the range of I/O ports */
for (i = pPortRange->First; i <= pPortRange->Last; ++i)
{
/*
* Don't do anything if the I/O port is already
* handled internally or externally.
*/
if (IoPortProc[i].hVdd != NULL)
{
DPRINT1("IoPortProc[0x%X] already registered\n", i);
continue;
}
/* Register wrt. the VDD */
IoPortProc[i].hVdd = hVdd;
/* Disable the internal handlers */
IoPortProc[i].IoHandlers.InB = NULL;
IoPortProc[i].IoHandlers.InW = NULL;
IoPortProc[i].IoHandlers.InD = NULL;
IoPortProc[i].IoHandlers.InsB = NULL;
IoPortProc[i].IoHandlers.InsW = NULL;
IoPortProc[i].IoHandlers.InsD = NULL;
IoPortProc[i].IoHandlers.OutB = NULL;
IoPortProc[i].IoHandlers.OutW = NULL;
IoPortProc[i].IoHandlers.OutD = NULL;
IoPortProc[i].IoHandlers.OutsB = NULL;
IoPortProc[i].IoHandlers.OutsW = NULL;
IoPortProc[i].IoHandlers.OutsD = NULL;
/* Save our handlers */
IoPortProc[i].VddIoHandlers = *IoHandlers;
}
/* Go to the next range */
++pPortRange;
}
return TRUE;
}
VOID
WINAPI
VDDDeInstallIOHook(IN HANDLE hVdd,
IN WORD cPortRange,
IN PVDD_IO_PORTRANGE pPortRange)
{
WORD i;
/* Check validity of the VDD handle */
if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_INVALID_PARAMETER);
return;
}
/* Loop for each range of I/O ports */
while (cPortRange--)
{
/* Unregister the range of I/O ports */
for (i = pPortRange->First; i <= pPortRange->Last; ++i)
{
/*
* Don't do anything if we don't own the I/O port.
*/
if (IoPortProc[i].hVdd != hVdd)
{
DPRINT1("IoPortProc[0x%X] owned by somebody else\n", i);
continue;
}
/*
* Put automagically all the fields to zero:
* the hVdd gets unregistered as well as all the handlers.
*/
// IoPortProc[i] = {NULL};
RtlZeroMemory(&IoPortProc[i], sizeof(IoPortProc[i]));
}
/* Go to the next range */
++pPortRange;
}
}
/* EOF */