/*
* sim32.c - Sim32 for Microsoft NT SoftPC.
*
* Ade Brownlow
* Wed Jun 5 91
*
* %W% %G% (c) Insignia Solutions 1991
*
* This module provides the Microsoft sim32 interface with the additional sas
* functionality and some host sas routines. We also provide cpu idling facilities.
*
* This module in effect provides (along with the cpu) what Microsoft term as the IEU -
* see documentation.
*/
#ifdef SIM32
#ifdef CPU_40_STYLE
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#endif /* CPU_40_STYLE */
#include <windows.h>
#include "insignia.h"
#include "host_def.h"
#include <stdlib.h>
#include <stdio.h>
#include "xt.h"
#include "sim32.h"
#include "sas.h"
#include "gmi.h"
#include "ckmalloc.h"
#include CpuH
#ifdef CPU_40_STYLE
#include "nt_mem.h"
#endif /* CPU_40_STYLE */
/********************************************************/
/* IMPORTS & EXPORTS */
/* Sas/gmi Sim32 crossovers */
GLOBAL BOOL Sim32FlushVDMPointer (double_word, word, UTINY *, BOOL);
GLOBAL BOOL Sim32FreeVDMPointer (double_word, word, UTINY *, BOOL);
GLOBAL BOOL Sim32GetVDMMemory (double_word, word, UTINY *, BOOL);
GLOBAL BOOL Sim32SetVDMMemory (double_word, word, UTINY *, BOOL);
GLOBAL sys_addr sim32_effective_addr (double_word, BOOL);
GLOBAL UTINY *sas_alter_size(sys_addr);
GLOBAL UTINY *host_sas_init(sys_addr);
GLOBAL UTINY *host_sas_term(void);
/* Microsoft sas extensions */
GLOBAL IMEMBLOCK *sas_mem_map (void);
GLOBAL void sas_clear_map(void);
IMPORT ULONG Sas_wrap_mask;
#ifndef MONITOR
//
// Pointer to a scratch video buffer. Updated by sim32 routines
// when intel video addr is requested.
IU8 *ScratchVideoBuffer = 0;
#define VIDEO_REGEN_START 0xa0000
#define VIDEO_REGEN_END 0xbffff
#define VID_BUFF_SIZE 0x20000
#define IsVideoMemory(LinAddr) \
((LinAddr) >= VIDEO_REGEN_START && (LinAddr) <= VIDEO_REGEN_END)
IU8 *GetVideoMemory(ULONG iaddr)
{
//
// If there isn't a video scratch buffer, allocate one.
// This will stick around until ntvdm terminates. Could be
// optimized to free the buffer when not in use.
//
if (!ScratchVideoBuffer) {
ScratchVideoBuffer = malloc(VID_BUFF_SIZE);
if (!ScratchVideoBuffer) {
return NULL;
}
}
//
// We could do this more efficiently, by only copying
// minimum area needed, but then we need to keep track of
// what to update on the flush and do ref counting.
// Since video memory access by host code is rare
// (only seen in demWrite\demRead so far) be simple minded.
//
sas_loads (VIDEO_REGEN_START,
ScratchVideoBuffer,
VID_BUFF_SIZE
);
return ScratchVideoBuffer + (iaddr - VIDEO_REGEN_START);
}
BOOL SetVideoMemory(ULONG iaddr)
{
ULONG VideoOffset = iaddr - VIDEO_REGEN_START;
if (!ScratchVideoBuffer) {
return FALSE;
}
sas_stores(iaddr,
ScratchVideoBuffer + VideoOffset,
VID_BUFF_SIZE - VideoOffset
);
return TRUE;
}
#endif
/********************************************************/
/* MACROS */
/* macro to convert the supplied address to intel address */
#define convert_addr(a,b,c,d) \
{ \
if ((a = sim32_effective_addr (b,c)) == (sys_addr)-1)\
{\
return (d);\
}\
}
/********************************************************/
/* The actual sim32 interfaces, most of these routines can be more or less mapped directly
* to existing routines in sas or gmi.
*
* WARNING: This routine returns a pointer into M, and
* WILL NOT work for backward M.
*/
UCHAR *Sim32pGetVDMPointer(ULONG addr, UCHAR pm)
{
sys_addr iaddr;
if (pm && (addr == 0))
return(NULL);
convert_addr (iaddr, addr, pm, NULL);
//STF - need sas_wrap_mask with PE....iaddr &= Sas_wrap_mask;
if (IsVideoMemory(iaddr)) {
return GetVideoMemory(iaddr);
}
return (NtGetPtrToLinAddrByte(iaddr));
}
/*
* See Sim32pGetVDMPointer
*
* This call must be maintaned as is because it is exported for VDD's
* in product 1.0.
*/
UCHAR *ExpSim32GetVDMPointer IFN3(double_word, addr, double_word, size, UCHAR, pm)
{
return Sim32pGetVDMPointer(addr, (UCHAR)pm);
}
GLOBAL BOOL Sim32FlushVDMPointer IFN4(double_word, addr, word, size, UTINY *, buff, BOOL, pm)
{
sys_addr iaddr;
convert_addr (iaddr, addr, pm, 0);
//STF - need sas_wrap_mask with PE....iaddr &= Sas_wrap_mask;
#ifndef MONITOR
if (IsVideoMemory(iaddr) && !SetVideoMemory(iaddr)) {
return FALSE;
}
#endif //MONITOR
sas_overwrite_memory(iaddr, (ULONG)size);
return (TRUE);
}
GLOBAL BOOL Sim32FreeVDMPointer IFN4(double_word, addr, word, size, UTINY *, buff, BOOL, pm)
{
/* we haven't allocated any new memory so always return success */
return (TRUE);
}
GLOBAL BOOL Sim32GetVDMMemory IFN4(double_word, addr, word, size, UTINY *, buff, BOOL, pm)
{
sys_addr iaddr;
convert_addr (iaddr, addr, pm, FALSE);
/* effectivly a sas_loads */
sas_loads (iaddr, buff, (sys_addr)size);
/* always return success */
return (TRUE);
}
GLOBAL BOOL Sim32SetVDMMemory IFN4(double_word, addr, word, size, UTINY *, buff, BOOL, pm)
{
sys_addr iaddr;
convert_addr (iaddr, addr, pm, FALSE);
/* effectivly a sas_stores */
sas_stores (iaddr, buff, (sys_addr)size);
/* always return success */
return (TRUE);
}
/********************************************************/
/* Support routines for sim32 above */
GLOBAL sys_addr sim32_effective_addr IFN2(double_word, addr, BOOL, pm)
{
word seg, off;
double_word descr_addr;
DESCR entry;
seg = (word)(addr>>16);
off = (word)(addr & 0xffff);
if (pm == FALSE)
{
return ((double_word)seg << 4) + off;
}
else
{
if ( selector_outside_table(seg, &descr_addr) == 1 )
{
/*
This should not happen, but is a check the real effective_addr
includes. Return error -1.
*/
#ifndef PROD
printf("NTVDM:sim32:effective addr: Error for addr %#x (seg %#x)\n",addr, seg);
HostDebugBreak();
#endif
return ((sys_addr)-1);
}
else
{
read_descriptor(descr_addr, &entry);
return entry.base + off;
}
}
}
/********************************************************/
/* Microsoft extensions to sas interface */
LOCAL IMEMBLOCK *imap_start=NULL, *imap_end=NULL;
GLOBAL IMEMBLOCK *sas_mem_map ()
{
/* produce a memory map for the whole of intel space */
sys_addr iaddr;
int mem_type;
if (imap_start)
sas_clear_map();
for (iaddr=0; iaddr < Length_of_M_area; iaddr++)
{
mem_type = sas_memory_type (iaddr);
if (!imap_end)
{
/* this is the first record */
check_malloc (imap_start, 1, IMEMBLOCK);
imap_start->Next = NULL;
imap_end = imap_start;
imap_end->Type = mem_type;
imap_end->StartAddress = iaddr;
continue;
}
if (imap_end->Type != mem_type)
{
/* end of a memory section & start of a new one */
imap_end->EndAddress = iaddr-1;
check_malloc (imap_end->Next, 1,IMEMBLOCK);
imap_end = imap_end->Next;
imap_end->Next = NULL;
imap_end->Type =mem_type;
imap_end->StartAddress = iaddr;
}
}
/* terminate last record */
imap_end->EndAddress = iaddr;
return (imap_start);
}
GLOBAL void sas_clear_map()
{
IMEMBLOCK *p, *q;
for (p=imap_start; p; p=q)
{
q=p->Next;
free(p);
}
imap_start=imap_end=NULL;
}
/********************************************************/
/* Microsoft specific sas stuff (ie host sas) */
#define SIXTEENMEG 1024*1024*12
LOCAL UTINY *reserve_for_sas = NULL;
#ifndef CPU_40_STYLE
LOCAL sys_addr current_sas_size =0; /* A local Length_of_M_area */
GLOBAL UTINY *host_sas_init IFN1(sys_addr, size)
{
UTINY *rez;
DWORD M_plus_type_size;
/* allocate 16 MEG of virtual memory */
if (!reserve_for_sas)
{
if (!(reserve_for_sas = (UTINY *)VirtualAlloc ((void *)NULL, SIXTEENMEG,
MEM_RESERVE, PAGE_READWRITE)))
{
#ifndef PROD
printf ("NTVDM:Failed to reserve 16 Meg virtual memory for sas\n");
#endif
exit (0);
}
}
/* now commit to our size */
M_plus_type_size = size + NOWRAP_PROTECTION +
((size + NOWRAP_PROTECTION) >> 12);
rez = (UTINY *)VirtualAlloc ((void *) reserve_for_sas,
M_plus_type_size,
MEM_COMMIT,
PAGE_READWRITE);
if (rez)
Length_of_M_area = current_sas_size = size;
return (rez);
}
GLOBAL UTINY *host_sas_term()
{
if (!reserve_for_sas)
return (NULL);
/* deallocate the reserves */
VirtualFree (reserve_for_sas, SIXTEENMEG, MEM_RELEASE);
/* null out reserve pointer */
reserve_for_sas = NULL;
Length_of_M_area = current_sas_size = 0;
return (NULL);
}
GLOBAL UTINY *sas_alter_size IFN1(sys_addr, new)
{
UTINY *tmp;
if (!reserve_for_sas)
{
#ifndef PROD
printf ("NTVDM:Sas trying to alter size before reserve setup\n");
#endif
return (NULL);
}
/* if we are already at the right size return success */
if (new == current_sas_size)
{
return (reserve_for_sas);
}
if (new > current_sas_size)
{
/* move to end of current commited area */
tmp = reserve_for_sas + current_sas_size;
if (!VirtualAlloc ((void *)tmp, (DWORD)(new - current_sas_size), MEM_COMMIT,
PAGE_READWRITE))
{
printf ("NTVDM:Virtual Allocate for resize from %d to %d FAILED!\n",
current_sas_size, new);
return (NULL);
}
}
else
{
/* move to the place where sas needs to end */
tmp = reserve_for_sas + new;
/* now decommit the unneeded memory */
if (!VirtualFree ((void *)tmp, (DWORD)(current_sas_size - new), MEM_DECOMMIT))
{
printf ("NTVDM:Virtual Allocate for resize from %d to %d FAILED!\n",
current_sas_size, new);
return (NULL);
}
}
Length_of_M_area = current_sas_size = new;
return (reserve_for_sas);
}
#else /* CPU_40_STYLE */
// Intel space allocation and deallocation control function for the A4 CPU
GLOBAL UTINY *host_sas_init IFN1(sys_addr, size)
{
/* Initialise memory management system and allocation bottom 1M+64K */
if(!(Start_of_M_area = InitIntelMemory(size)))
{
/* Initialise function failed, exit */
#ifndef PROD
printf ("NTVDM:Failed to allocate virtual memory for sas\n");
#endif
exit(0);
}
Length_of_M_area = size;
return(Start_of_M_area);
}
GLOBAL UTINY *host_sas_term()
{
/* Has any Intel memory been allocated ? */
if(Start_of_M_area)
{
/* Free allocated intel memory and control structures */
FreeIntelMemory();
reserve_for_sas = NULL; /* null out reserve pointer */
Length_of_M_area = 0;
}
return(NULL);
}
#endif /* CPU_40_STYLE */
#endif /* SIM32 */