/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    Monitor.c
Abstract:

    This module is the user mode portion of the x86 monitor

Author:

    Dave Hastings (daveh) 16 Mar 1991

Environment:

    User mode only

Revision History:
    William Hsieh   10-10-1992 Added A20 wrapping support
--*/

#define VDD_INTEG 1
#include "monitorp.h"
#include <windows.h>
#include <stdio.h>
#include <malloc.h>

// Tim Nov 92.
void sas_connect_memory(
    IN sys_addr Low,
    IN sys_addr High,
    IN int Type
    );

//BUGBUGBUGBUG Include file

// from base\inc\sas.h
/* memory types for sas */
#define SAS_RAM 0
#define SAS_VIDEO 1
#define SAS_ROM 2
#define SAS_WRAP 3
#define SAS_INACCESSIBLE 4
#define SAS_MAX_TYPE    SAS_INACCESSIBLE

#define SIXTYFOURK 0x10000L
#define ONEMEGA 0x100000L

void rom_init();
void rom_checksum();
void copyROM();

USHORT get_lim_backfill_segment(void);
BOOL   HoldEMMBackfillMemory(ULONG Address, ULONG Size);

#if DBG
extern unsigned short get_emm_page_size(void);
extern unsigned short get_intel_page_size(void);
#endif

/* SYNC THESE DEFINITIONS WITH BASE\EMM.H, or sas_init will assert */
#define EMM_PAGE_SIZE		0x4000
#define INTEL_PAGE_SIZE 	0x1000

typedef struct
{
    ULONG (*b_read) ();
    ULONG (*w_read) ();
    VOID (*str_read) ();
} READ_POINTERS;

// Internal Data
PMEMTYPE MemType = NULL;

// External Data
extern READ_POINTERS read_pointers;

// M variables used by video.lib

host_addr Start_of_M_area;       /* host addr (char *) of start of M */
sys_addr Length_of_M_area;       /* sys addr (long) offset of end of M */

static  HANDLE A20SectionHandle = NULL;
static BOOL A20IsON = FALSE;
static	USHORT	BackFillSegment;



EXPORT
VOID
sas_init(
    IN sys_addr Size
    )

/*++

Routine Description:

    This routine initializes the SAS module, and allocates the linear
    address space for the VDM, and loads the ROM

Arguments:

    Size - Supplies the size of the VDMs linear address space.

Return Value:

    None.

--*/

{
    NTSTATUS Status;
    ULONG ViewSize;
    PVOID BaseAddress;
    OBJECT_ATTRIBUTES   A20ObjAttr;
    LARGE_INTEGER       SectionSize;
    UCHAR SectionAnsiName[80];
    WCHAR SectionUnicodeName[80];
    UNICODE_STRING UnicodeString;
    USHORT Pages;
    ULONG BackFillBase;

#define CONVENTIONAL_MEM_SECTION "\\BaseNamedObjects\\VdmConventionalMemory"

    //
    // Create a name for the Convetional memory section
    //
    sprintf(
        SectionAnsiName,
        "%s%d",
        CONVENTIONAL_MEM_SECTION,
        GetCurrentProcessId()
        );

    if (MultiByteToWideChar(0, 0, SectionAnsiName, -1, SectionUnicodeName,80)
        == 0
    ) {
#if DBG
        DbgBreakPoint();
#endif
    //    host_error(EG_MALLOC_FAILURE,ERR_QUIT,"");
        TerminateVDM();
    }

    RtlInitUnicodeString(&UnicodeString, SectionUnicodeName);

    InitializeObjectAttributes(
        &A20ObjAttr,
        &UnicodeString,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
        );

    SectionSize.HighPart = 0L;
    SectionSize.LowPart = 640 * 1024 + 64 * 1024;

    Status = NtCreateSection(
        &A20SectionHandle,
        SECTION_MAP_WRITE|SECTION_MAP_EXECUTE,
        &A20ObjAttr,
        &SectionSize,
        PAGE_EXECUTE_READWRITE,
        SEC_RESERVE,
        NULL
        );

    if (!NT_SUCCESS(Status)) {
	// bugbug -williamh
	// we should pop up an approiate message before we
	// terminate the vdm.
#if DBG
	    DbgPrint("sas_init: can not create himem section, status = %lx\n",
		     Status);
#endif
        TerminateVDM();
    }
    VdmSize = Size;

    //
    // N.B.  We expect that process creation has reserved the first 16 MB
    //       for us already.  If not, then this won't work worth a darn

    // free the first 640KB virtual address.
    // This is done because it has been resevered before sas_init get called
    BaseAddress = (PVOID)1;
    ViewSize = 640 * 1024 - 1;
    Status = NtFreeVirtualMemory(
        NtCurrentProcess(),
        &BaseAddress,
        &ViewSize,
        MEM_RELEASE
        );


    if (!NT_SUCCESS(Status)) {
#if DBG
	DbgPrint("sas_init: cannot free 1st 640k virtual address, status = %lx\n",
		 Status);
#endif
        TerminateVDM();
    }

    BaseAddress =(PVOID) ONEMEGA;
    ViewSize = SIXTYFOURK;
    Status = NtFreeVirtualMemory(
        NtCurrentProcess(),
        &BaseAddress,
        &ViewSize,
        MEM_RELEASE
        );


    if (!NT_SUCCESS(Status)) {
#if DBG
	DbgPrint("sas_init: can not free himem virtual address, status = %lx\n",
		 Status);
#endif
        TerminateVDM();
    }

    BaseAddress = (PVOID)VDM_BASE_ADDRESS;
    ViewSize = SIXTYFOURK - (ULONG)VDM_BASE_ADDRESS;
    SectionSize.HighPart = SectionSize.LowPart = 0;

    Status = NtMapViewOfSection(
        A20SectionHandle,
        NtCurrentProcess(),
        &BaseAddress,
        0,
        ViewSize,
        &SectionSize,
        &ViewSize,
        ViewUnmap,
        MEM_DOS_LIM,
        PAGE_EXECUTE_READWRITE
        );

    if (!NT_SUCCESS(Status)){
#if DBG
	DbgPrint("sas_init: can not map view of 1st 64K, status = %ls\n",
		 Status);
#endif
        TerminateVDM();
    }
    BaseAddress = (PVOID) ONEMEGA;
    ViewSize = SIXTYFOURK;
    Status = NtMapViewOfSection(A20SectionHandle,
        NtCurrentProcess(),
        &BaseAddress,
        0,
        ViewSize,
        &SectionSize,
        &ViewSize,
        ViewUnmap,
        MEM_DOS_LIM,
        PAGE_EXECUTE_READWRITE
        );

    if (!NT_SUCCESS(Status)){
#if DBG
	DbgPrint("sas_init: can not map view of himem space, status = %lx\n",
		 Status);
#endif
        TerminateVDM();
    }

    // get emm back fill segment address from softpc
    // we cut the backfill memory area into pieces in EMM_PAGE_SIZE unit.
    // this is done so that EMM manager can grab the address space
    // as EMM page frame.
    // note that if EMM is disabled, the backfill segment will be
    // (640 * 1024 / 16).

    BackFillSegment = get_lim_backfill_segment();

    ASSERT(BackFillSegment <= 640 * 1024 / 16);

    //
    // Map the rest of conventional memory
    // only map up to the emm backfill segment.
    BaseAddress = (PVOID) (64 * 1024);
    ViewSize = BackFillSegment * 16 - 64 * 1024;
    SectionSize.LowPart = 64 * 1024;
    SectionSize.HighPart = 0;
    Status = NtMapViewOfSection(A20SectionHandle,
        NtCurrentProcess(),
        &BaseAddress,
        0,
        ViewSize,
        &SectionSize,
        &ViewSize,
        ViewUnmap,
        MEM_DOS_LIM,
        PAGE_EXECUTE_READWRITE
        );
    if (!NT_SUCCESS(Status)){
#if DBG
	DbgPrint("sas_init: can not map view of himem space, status = %lx\n",
		 Status);
#endif
        TerminateVDM();
    }

// if there are any backfill memory, map it to our section initially
    if (BackFillSegment < 640 * 1024 / 16) {

    /* make sure our constants are in sync with emm.h */
#if DBG
	ASSERT(EMM_PAGE_SIZE == get_emm_page_size());
	ASSERT(INTEL_PAGE_SIZE == get_intel_page_size());
#endif
	if (!HoldEMMBackFillMemory(BackFillSegment * 16,
				   (640 * 1024) - BackFillSegment * 16)
				  ) {

#if DBG
	    DbgPrint("sas_init: can not map backfill space, status = %lx\n",
		     Status);
#endif
	    TerminateVDM();
	}
    }

    //
    // Allocate ROM area
    //
    BaseAddress = (PVOID)(640 * 1024);
    ViewSize = 384 * 1024;
    Status = NtAllocateVirtualMemory(
        NtCurrentProcess(),
        &BaseAddress,
        0L,
        &ViewSize,
        MEM_COMMIT,
        PAGE_READWRITE
        );
    if (!NT_SUCCESS(Status)){
#if DBG
	DbgPrint("sas_init: can not map view of himem space, status = %lx\n",
		 Status);
#endif
        TerminateVDM();
    }

    A20IsON = FALSE;

    Start_of_M_area = 0;
    Length_of_M_area = VdmSize;
    sas_connect_memory(0, VdmSize + 2*SIXTYFOURK -1, SAS_RAM);
}

#if VDD_INTEG

EXPORT
VOID
sas_term(
    VOID
    )
/*++

Routine Description:

    Free memory prior to reallocing it

Arguments:

    None.

Return Value:

    None.
--*/
{
    PVOID BaseAddress;
    NTSTATUS Status;
    ULONG Size;

    BaseAddress = (PVOID)VDM_BASE_ADDRESS;
    Size = VdmSize;
    Status = NtFreeVirtualMemory(
        NtCurrentProcess(),
        &BaseAddress,
        &Size,
        MEM_DECOMMIT);

    if (!NT_SUCCESS(Status)) {
        VDprint(VDP_LEVEL_ERROR,
            ("SoftPc: NtDeCommitVirtualMemory failed !!!! Status = %lx\n",
            Status));
        VDbreak(VDB_LEVEL_ERROR);
    }
}


EXPORT
sys_addr
sas_memory_size(
    VOID
    )
/*++

Routine Description:

    This routine returns the size of Intel memory

Arguments:

    none

Return Value:

    size of intel memory

--*/
{
    return(VdmSize);
}


EXPORT
VOID
sas_connect_memory(
    IN sys_addr Low,
    IN sys_addr High,
    IN int Type
    )
/*++

Routine Description:

    This routine sets up a type record for the specified address region.
    If the specified address region was a different type, it is changed to
    the new type.

Arguments:

    Low -- the starting address of the region
    High -- the ending address of the region
    Type -- the type for the region, one of SAS_RAM, SAS_VIDEO, SAS_ROM,
        SAS_WRAP, SAS_INACCESSIBLE

Return Value:

    None.
--*/
{
    //bugbug do we handle new block contained in old block correctly?
    PMEMTYPE Current, Previous, New, Temp;

    if (!MemType) {
        MemType = (PMEMTYPE) ch_malloc(sizeof(MEMTYPE));
        MemType->Previous = NULL;
        MemType->Next = NULL;
        MemType->Start = Low;
        MemType->End = High;
        MemType->Type = (half_word)Type;
        return;
    }

    Current = MemType;
    while (Current && (Low > Current->Start)) {
        Previous = Current;
        Current = Current->Next;
    }

    if ((Current) && (Low == Current->Start) && (High == Current->End)) {
        Current->Type = (half_word)Type;
        return;
    }

    if (!Current) {
        // Block goes at end of list
        New = (PMEMTYPE) ch_malloc(sizeof(MEMTYPE));
        Previous->Next = New;
        New->Previous = Previous;
        New->Start = Low;
        New->End = High;
        New->Type = (half_word)Type;
        New->Next = NULL;
    } else {
        // Block goes in front of Current
        New = (PMEMTYPE) ch_malloc(sizeof(MEMTYPE));
        New->Start = Low;
        New->Type = (half_word)Type;
        New->End = High;
        New->Previous = Current->Previous;
        New->Next = Current;
        Current->Previous = New;
        if (!New->Previous) {
            MemType = New;
        } else {
            New->Previous->Next = New;
        }
    }


    // Block overlaps one or more existing blocks

    if (New->Previous) {
        if (New->Previous->End > New->End) {
            // block contained in exising block
            Temp = (PMEMTYPE) ch_malloc(sizeof(MEMTYPE));
            Temp->Previous = New;
            Temp->Next = New->Next;
            New->Next = Temp;
            if (Temp->Next) {
                Temp->Next->Previous = Temp;
            }
            Temp->End = New->Previous->End;
            New->Previous->End = New->Start - 1;
            Temp->Start = New->End + 1;
            Temp->Type = New->Previous->Type;
            return;
        } else if (New->Previous->End >= New->Start){
            // block overlaps end of exising block
            New->Previous->End = New->Start - 1;
        }
    }

    // remove all blocks entirely contained in new block
    while ((New->Next) && (New->Next->End <= New->End)) {
        Temp = New->Next;
        New->Next = New->Next->Next;
        if (New->Next) {
            New->Next->Previous = New;
        }
        free(Temp);
    }

    // remove portion of next block overlapping new block
    if ((New->Next) && (New->Next->Start <= New->End)) {
        New->Next->Start = New->End + 1;
    }

}


EXPORT
half_word
sas_memory_type(
    IN sys_addr Address
    )
/*++

Routine Description:

    This routine returns the type of memory at a specific address

Arguments:

    Address -- linear address to return type for.

Return Value:

    the type for the region, one of SAS_RAM, SAS_VIDEO, SAS_ROM,
        SAS_WRAP, SAS_INACCESSIBLE
--*/
{
    PMEMTYPE Current;

    if (Address > VdmSize) {
        return SAS_INACCESSIBLE;
    }

    Current = MemType;
    while (Current && !((Address >= Current->Start) &&
        (Address <= Current->End))) {
        Current = Current->Next;
    }
    if (!Current) {
        return SAS_INACCESSIBLE;
    }
    return Current->Type;
}



EXPORT
VOID
sas_enable_20_bit_wrapping(
    VOID
    )
/*++

Routine Description:

    This routine causes memory addresses to wrap at 1MB

Arguments:

    None.

Return Value:

    None.

--*/
{
    NTSTATUS    Status;
    PVOID   BaseAddress;
    ULONG   Size;
    LARGE_INTEGER SectionOffset;
    // if A20 line is off already do nothing
    if (A20IsON == FALSE){
        return;
    }
    BaseAddress = (PVOID)ONEMEGA;
    Size = SIXTYFOURK;
    Status = NtUnmapViewOfSection(NtCurrentProcess(),
                                  BaseAddress
                                  );

    if (!NT_SUCCESS(Status)) {
#if DBG
	DbgPrint("A20OFF: Unable to unmap view of section, status = %lx\n",
                 Status);
#endif
        TerminateVDM();
    }
    SectionOffset.HighPart = SectionOffset.LowPart = 0;
    Status = NtMapViewOfSection(A20SectionHandle,
                                NtCurrentProcess(),
                                &BaseAddress,
                                0,
                                Size,
                                &SectionOffset,
                                &Size,
                                ViewUnmap,
                                MEM_DOS_LIM,
                                PAGE_EXECUTE_READWRITE
                                );

    if (!NT_SUCCESS(Status)) {
#if DBG
	DbgPrint("A20OFF: Unable to map view of section, status = %lx\n",
                 Status);
#endif
        TerminateVDM();
    }
    A20IsON = FALSE;
}

EXPORT
VOID
sas_disable_20_bit_wrapping(
    VOID
    )
/*++

Routine Description:

    This routine causes addressing to NOT wrap at 1MB

Arguments:

    None.

Return Value:

    None.

--*/
{
    NTSTATUS    Status;
    PVOID   BaseAddress;
    ULONG   Size;
    LARGE_INTEGER SectionOffset;

    // if A20 line is on already do nothing
    if (A20IsON == TRUE){
        return;
    }
    BaseAddress = (PVOID)ONEMEGA;
    Size = SIXTYFOURK;

    Status = NtUnmapViewOfSection(NtCurrentProcess(),
                                  BaseAddress
                                  );


    if (!NT_SUCCESS(Status)) {
#if DBG
	DbgPrint("A20ON: Unable to unmap view of section, status = %lx\n",
                 Status);
#endif
	TerminateVDM();
    }
    SectionOffset.HighPart = 0;
    SectionOffset.LowPart = 640 * 1024;
    Status = NtMapViewOfSection(A20SectionHandle,
                                NtCurrentProcess(),
                                &BaseAddress,
                                0,
                                Size,
                                &SectionOffset,
                                &Size,
                                ViewUnmap,
                                MEM_DOS_LIM,
                                PAGE_EXECUTE_READWRITE
                                );

    if (!NT_SUCCESS(Status)) {
#if DBG
	DbgPrint("A20ON: Unable to map view of section, status = %lx\n",
                 Status);
#endif
	TerminateVDM();
    }
    A20IsON = TRUE;
}



EXPORT
half_word
sas_hw_at(
    IN sys_addr Address
    )
/*++

Routine Description:

    This routine returns the byte at the specified address

Arguments:

    Address -- address of byte to return

Return Value:

    value of byte at specified address

--*/
{
    half_word RetVal;

    if (Address > VdmSize) {
        return 0xFE;
    }

    RetVal = *((half_word *)Address);
    return RetVal;
}


EXPORT
word
sas_w_at(
    IN sys_addr Address
    )
/*++

Routine Description:

    This routine returns the word at the specified address

Arguments:

    Address -- address of word to return

Return Value:

    value of word at specified address

--*/
{
    word RetVal;

//    DbgPrint("NtVdm : sas_w_at \n");
    if (Address > VdmSize) {
        return 0xFEFE;
    }

    RetVal = *((word *)Address);
    return RetVal;
}


EXPORT
double_word
sas_dw_at(
    IN sys_addr Address
    )
/*++

Routine Description:

    This routine returns the dword at the specified address

Arguments:

    Address -- address of dword to return

Return Value:

    value of dword at specified address

--*/
{
    double_word RetVal;

    //DbgPrint("NtVdm : sas_dw_at \n");
    RetVal = (double_word)(((ULONG)sas_w_at(Address + 2) << 16) +
        sas_w_at(Address));
    return RetVal;
}


EXPORT
VOID
sas_load(
    IN sys_addr Address,
    IN half_word *Value
    )
/*++

Routine Description:

    This routine stores the byte at the specified address in the supplied
    variable

Arguments:

    Address -- address of byte to return
    Value -- Variable to store the value in

Return Value:

    None.
--*/
{
    //DbgPrint("NtVdm : sas_load \n");
    if (Address > VdmSize) {
        *Value = 0xFE;
        return;
    }

    *Value = *((half_word *)Address);
    return;
}


EXPORT
VOID
sas_loadw(
    IN sys_addr Address,
    IN word *Value
    )
/*++

Routine Description:

    This routine stores the word at the specified address in the supplied
    variable

Arguments:

    Address -- address of word to return
    Value -- Variable to store the value in

Return Value:

    None.
--*/
{
    //DbgPrint("NtVdm : sas_loadw\n");
    if (Address > VdmSize) {
        *Value = 0xFEFE;
        return;
    }

    *Value = *((word *)Address);
    //DbgPrint("NtVdm : sas_loadw word at address %lx is %x (Not video)\n",Address,*Value);
    return;
}



EXPORT
VOID
sas_store(
    IN sys_addr Address,
    IN half_word Value
    )
/*++

Routine Description:

    This routine stores the specified byte at the specified address

Arguments:

    Address -- address of word to return
    Value -- value to store

Return Value:

    None.
--*/
{
    half_word Type;
    //DbgPrint("NtVdm : sas_store\n");
    if (Address <= VdmSize) {
        Type = sas_memory_type(Address);
        switch (Type) {
            case SAS_ROM:
                break;

            default:
                *((half_word *)Address) = Value;
                //DbgPrint("NtVdm : sas_store put byte %x at address %lx\n",Value,Address);
                break;
        }
    }
}



EXPORT
VOID
sas_storew(
    IN sys_addr Address,
    IN word Value
    )
/*++

Routine Description:

    This routine stores the specified word at the specified address

Arguments:

    Address -- address of word to return
    Value -- value to store at the specified address

Return Value:

    None.
--*/
{

    //DbgPrint("NtVdm : sas_storew\n");
    if (Address + 1 <= VdmSize) {
        switch (sas_memory_type(Address)) {

            case SAS_ROM:
                break;

            default:
                *((word *)Address) = Value;
                //DbgPrint("NtVdm : sas_storew put word %x at address %lx\n",Value,Address);
                break;
        }
    }
}



EXPORT
VOID
sas_storedw(
    IN sys_addr Address,
    IN double_word Value
    )
/*++

Routine Description:

    This routine stores the specified dword at the specified address

Arguments:

    Address -- address of word to return
    Value -- value to store at the specified address

Return Value:

    None.
--*/
{
    //_asm int 3;
    sas_storew(Address, (word)(Value & 0xFFFF));
    sas_storew(Address + 2, (word)((Value >> 16) & 0xFFFF));
}


EXPORT
VOID
sas_loads(
    IN sys_addr Source,
    IN host_addr Destination,
    IN sys_addr Length
    )
/*++

Routine Description:

     This routine copies the string from the specified intel address to the
     specified host address

Arguments:

    Source -- Intel address to copy from
    Destination -- host address to copy the string to
    Length -- length of the string to copy

Return Value:

    None.
--*/
{

    //DbgPrint("NtVdm : sas_loads\n");
    RtlCopyMemory((PVOID) Destination, (PVOID) Source, Length);
}



EXPORT
VOID
sas_stores(
    IN sys_addr Destination,
    IN host_addr Source,
    IN sys_addr Length
    )
/*++

Routine Description:

     This routine copies the string from the specified host address to the
     specified intel address

Arguments:

    Destination -- intel address to copy the string to
    Source -- host address to copy from
    Length -- length of the string to copy

Return Value:

    None.
--*/
{

    //DbgPrint("NtVdm : sas_stores\n");
    switch (sas_memory_type(Destination)) {

        case SAS_ROM:
            break;

        default:
            RtlCopyMemory((PVOID) Destination, (PVOID) Source, Length);
            break;
    }
}


EXPORT
VOID
sas_move_bytes_forward(
    IN sys_addr Source,
    IN sys_addr Destination,
    IN sys_addr Length
    )
/*++

Routine Description:

    This routine copies one region of intel memory to another.

Arguments:

    Source -- source intel address
    Destination -- destination intel address
    Length -- length of region to copy (in bytes)

Return Value:

    None.
--*/
{
    //DbgPrint("NtVdm : sas_move_bytes_forward\n");
    switch (sas_memory_type(Destination)) {

        case SAS_ROM:
            break;

        default:
            RtlCopyMemory((PVOID) Destination, (PVOID) Source, Length);
            break;
    }
}



EXPORT
VOID
sas_move_words_forward(
    IN sys_addr Source,
    IN sys_addr Destination,
    IN sys_addr Length
    )
/*++

Routine Description:

    This routine copies one region of intel memory to another.

Arguments:

    Source -- source intel address
    Destination -- destination intel address
    Length -- length of region to copy (in words)

Return Value:

    None.
--*/
{
    //_asm int 3;
    Length <<= 1;
    switch (sas_memory_type(Destination)) {

        case SAS_ROM:
            break;

        default:
            RtlCopyMemory((PVOID) Destination, (PVOID) Source, Length);
            break;
    }
}



EXPORT
VOID
sas_move_bytes_backward(
    IN sys_addr Source,
    IN sys_addr Destination,
    IN sys_addr Length
    )
/*++

Routine Description:

    This routine copies one region of intel memory to another.

Arguments:

    Source -- source intel address
    Destination -- destination intel address
    Length -- length of region to copy (in bytes)

Return Value:

    None.
--*/
{
    //_asm int 3;
    switch (sas_memory_type(Destination)) {

        case SAS_ROM:
            break;

        default:
            RtlCopyMemory((PVOID) (Destination - Length + 1),
                          (PVOID) (Source - Length + 1),
                          Length);
            break;
    }
}



EXPORT
VOID
sas_move_words_backward(
    IN sys_addr Source,
    IN sys_addr Destination,
    IN sys_addr Length
    )
/*++

Routine Description:

    This routine copies one region of intel memory to another.

Arguments:

    Source -- source intel address
    Destination -- destination intel address
    Length -- length of region to copy (in words)

Return Value:

    None.
--*/
{
    //_asm int 3;
    Length <<= 1;
    switch (sas_memory_type(Destination)) {

        case SAS_ROM:
            break;

        default:
            RtlCopyMemory((PVOID) (Destination - Length + 1),
                          (PVOID) (Source - Length + 1),
                          Length);
            break;
    }
}

EXPORT
VOID
sas_fills(
    IN sys_addr Address,
    IN half_word Value,
    IN sys_addr Length
    )
/*++

Routine Description:

    This routine fills a specified region of intel memory with a byte value

Arguments:

    Address -- address to fill at
    Value -- value to fill with
    Length -- length of region to fill

Return Value:

    None.
--*/
{
    half_word Type;

    //DbgPrint("NtVdm : sas_fills\n");
    Type = sas_memory_type(Address);
    switch (Type) {

        case SAS_ROM:
            break;

        default:
            RtlFillMemory((PVOID) Address, Length, Value);
            break;
    }
}

EXPORT
VOID
sas_fillsw(
    IN sys_addr Address,
    IN word Value,
    IN sys_addr Length
    )
/*++

Routine Description:

    This routine fills a specified region of intel memory with a word value

Arguments:

    Address -- address to fill at
    Value -- value to fill with
    Length -- length of region to fill

Return Value:

    None.
--*/
{

    word *p;
    half_word Type;

    //DbgPrint("NtVdm : sas_fillsw\n");
    Type = sas_memory_type(Address);
    switch (Type) {

        case SAS_ROM:
            break;

        default:
            p = (word *)Address;
            while (Length--) {
                *p++ = Value;
            }
            break;
    }
}

host_addr scratch = NULL;

EXPORT
host_addr
sas_scratch_address(
    IN sys_addr Length
    )
/*++

Routine Description:

    This routine supplies a scratch buffer for short term use

Arguments

    Length -- length of buffer needed

Return Value:

    None.

NOTE: Sudeepb 31-Oct-1993 Converted scratch to be allocated dynamically rather
      than as a static array.
--*/
{
    //DbgPrint("NtVdm : sas_scratch_address\n");
    if (Length > 64 * 1024) {
        //DbgPrint("SoftPc: sas_scratch_address requet for buffer larger than 64K\n");
        return NULL;
    }

    if (scratch)
        return scratch;

    if ((scratch = (host_addr) malloc (64 * 1024)) == NULL)
        return NULL;

    return scratch;
}

EXPORT
half_word
sas_hw_at_no_check(
    sys_addr addr
    )
// bugbug comment
{
    //DbgPrint("NtVdm : sas_hw_at_no_check\n");
    //DbgPrint("NtVdm : sas_hw_at_no_check byte at %lx is %x\n",addr,*((half_word *)addr));
    return *((half_word *)addr);
}

EXPORT
word
sas_w_at_no_check(
    sys_addr addr
    )
// bugbug comment
{
    //DbgPrint("NtVdm : sas_w_at_no_check\n");
    //DbgPrint("NtVdm : sas_w_at_no_check word at %lx is %x\n",addr,*((word *)addr));
    return *((word *)addr);
}
EXPORT
double_word
sas_dw_at_no_check(
    sys_addr addr
    )
// bugbug comment
{
    //DbgPrint("NtVdm : sas_dw_at_no_check\n");
    //DbgPrint("NtVdm : sas_dw_at_no_check double word at %lx is %lx\n",addr,*((double_word *)addr));
    return *((double_word *)addr);
}


EXPORT
VOID
sas_store_no_check(
    sys_addr addr,
    half_word val
    )
// bugbug comment
{
    //DbgPrint("NtVdm : sas_store_no_check\n");
    *((half_word *)addr) = val;
    //DbgPrint("NtVdm : sas_store_no_check stored byte %x at %lx\n",val,addr);
}

EXPORT
VOID
sas_storew_no_check(
    sys_addr addr,
    word val
    )
// bugbug comment
{
    //DbgPrint("NtVdm : sas_storew_no_check\n");
    *((word *)addr) = val;
}
EXPORT
double_word
effective_addr(
    IN word Segment,
    IN word Offset
    )
/*++

Routine Description:

    This routine maps effective_addr to Sim32GetVdmPointer

Arguments:

    Segment -- segment of address
    Offset -- offset of address

Return Value:

    Actual Intel address corresponding to the address supplied
--*/
{
    //DbgPrint("NtVdm : effective_addr\n");
    return (ULONG)Sim32GetVDMPointer(((((ULONG)Segment) << 16) | Offset), 1,
        (UCHAR) (getMSW() & MSW_PE ? TRUE : FALSE));
}

typedef enum
{
    RAM,
    VIDEO,
    ROM,
    IN_FRAGMENT,
    NEXT_FRAGMENT
} mem_type;

typedef struct
{
    VOID    (*b_write)();
    VOID    (*w_write)();
    VOID    (*b_fill)();
    VOID    (*w_fill)();
    VOID    (*b_move)();
    VOID    (*w_move)();
} MEM_HANDLERS;
#define TYPE_RANGE ((int)SAS_INACCESSIBLE)
#define write_b_write_ptrs( offset, func )  ( b_write_ptrs[(offset)] = (func) )
#define write_w_write_ptrs( offset, func )  ( w_write_ptrs[(offset)] = (func) )
#define write_b_page_ptrs( offset, func )   ( b_move_ptrs[(offset)] = b_fill_ptrs[(offset)] = (func) )
#define write_w_page_ptrs( offset, func )   ( w_move_ptrs[(offset)] = w_fill_ptrs[(offset)] = (func) )
#define init_b_write_ptrs( offset, func )   ( b_write_ptrs[(offset)] = (func) )
#define init_w_write_ptrs( offset, func )   ( w_write_ptrs[(offset)] = (func) )
#define init_b_page_ptrs( offset, func )    ( b_move_ptrs[(offset)] = b_fill_ptrs[(offset)] = (func) )
#define init_w_page_ptrs( offset, func )    ( w_move_ptrs[(offset)] = w_fill_ptrs[(offset)] = (func) )
#define read_b_write_ptrs( offset )     ( b_write_ptrs[(offset)] )
#define read_w_write_ptrs( offset )     ( w_write_ptrs[(offset)] )
#define read_b_page_ptrs( offset )      ( b_move_ptrs[(offset)] )
#define read_w_page_ptrs( offset )      ( w_move_ptrs[(offset)] )
#define read_b_move_ptrs( offset )      ( b_move_ptrs[(offset)] )
#define read_w_move_ptrs( offset )      ( w_move_ptrs[(offset)] )
#define read_b_fill_ptrs( offset )      ( b_fill_ptrs[(offset)] )
#define read_w_fill_ptrs( offset )      ( w_fill_ptrs[(offset)] )

/*
*   The main gmi data structures are defined here
*/
void (*(b_write_ptrs[TYPE_RANGE]))() ; /* byte write function */
void (*(w_write_ptrs[TYPE_RANGE]))() ; /* word write function */
void (*(b_fill_ptrs[TYPE_RANGE]))() ;  /* byte str fill func */
void (*(w_fill_ptrs[TYPE_RANGE]))() ;  /* word str fill func */
void (*(b_move_ptrs[TYPE_RANGE]))() ;  /* byte str write func */
void (*(w_move_ptrs[TYPE_RANGE]))() ;  /* word str write func */

void    gmi_define_mem(type,handlers)
mem_type type;
MEM_HANDLERS *handlers;
{
    int int_type = (int)(type);
    init_b_write_ptrs(int_type, (void(*)())(handlers->b_write));
    init_w_write_ptrs(int_type, (void(*)())(handlers->w_write));
    b_move_ptrs[int_type] = (void(*)())(handlers->b_move);
    w_move_ptrs[int_type] = (void(*)())(handlers->w_move);
    b_fill_ptrs[int_type] = (void(*)())(handlers->b_fill);
    w_fill_ptrs[int_type] = (void(*)())(handlers->w_fill);
}
#endif
BOOL sas_twenty_bit_wrapping_enabled() {
    return (!A20IsON);
}

VOID sas_part_enable_20_bit_wrapping(){
}
VOID sas_part_disable_20_bit_wrapping(){
}


/*
 * This function maps the given EMM backfill memory to DOS conventional
 * memory. The function is provided to EMM manager to put back
 * unmapped backfill memory(hold its contents while it is not mapped).
 *
 * NOTE: The very first caller will be sas_init.
 *
 * Input: ULONG BaseAddress -- the starting address, must be in INTEL page
 *			       boundary
 *	  ULONG Size	    -- size of the range, must be a multiple of
 *			       EMM_PAGE_SIZE.
 *
 * According to LouP, a view costs about 400 bytes of memory. This is why
 * I make these function strictly to work on EMM_PAGE_SIZE instead of 4KB.
 */


BOOL
HoldEMMBackFillMemory(ULONG BaseAddress, ULONG Size)
{
    ULONG NewBase, Pages, i;
    LARGE_INTEGER   SectionOffset;
    ULONG ViewSize;
    NTSTATUS Status;

    /* this function can only be called if there is backfill at all */
    ASSERT(BackFillSegment < 640 * 1024 / 16);

    // size must be EMM_PAGE_SIZE multiple
    ASSERT((Size % EMM_PAGE_SIZE) == 0);

    // address must be on INTEL page boundary
    ASSERT((BaseAddress & (INTEL_PAGE_SIZE - 1)) == 0);

    for (Pages = Size / EMM_PAGE_SIZE; Pages; Pages--) {
	SectionOffset.LowPart = BaseAddress;
	SectionOffset.HighPart = 0;
	ViewSize = EMM_PAGE_SIZE;
	Status = NtMapViewOfSection(A20SectionHandle,
				    NtCurrentProcess(),
				    (PVOID *)&BaseAddress,
				    0,
				    ViewSize,
				    &SectionOffset,
				    &ViewSize,
				    ViewUnmap,
				    MEM_DOS_LIM,
				    PAGE_EXECUTE_READWRITE
				    );
	if (!NT_SUCCESS(Status))
	    break;
	BaseAddress += EMM_PAGE_SIZE;
    }
    return (NT_SUCCESS(Status));
}
