#include "insignia.h"
#include "host_def.h"
/*
* SoftPC Revision 3.0
*
* Title	: ROM init functions
*
* Author	: Ade Brownlow	
*
* NB : These functions are used by BOTH the c and assembler cpus.
*		also note that host_read_resource now returns a long.
*
* SCCS ID:	@(#)rom.c	1.53 06/16/95
*
* (C) Copyright Insignia Solutions Ltd, 1994.
*/

#include <stdio.h>
#if defined(NTVDM) && defined(MONITOR)
#include <malloc.h>
#endif

#include TypesH
#include MemoryH

/*
 * SoftPC include files
 */
#include "xt.h"
#include "sas.h"
#include CpuH
#include "error.h"
#include "config.h"
#include "rom.h"
#include "debug.h"
#include "ckmalloc.h"
#include "yoda.h"
#include "gispsvga.h"

#ifdef	CPU_40_STYLE
#ifdef CCPU
#include "ccpusas4.h"
#else	/* ! CCPU */
#include "Cpu_c.h"
#endif	/* ! CCPU */
#endif	/* CPU_40_STYLE */

#ifndef BIOS1ROM_FILENAME
#define	BIOS1ROM_FILENAME	"bios1.rom"
#endif /* BIOS1ROM_FILENAME */

#ifndef BIOS2ROM_FILENAME
#ifdef	CPU_40_STYLE
#define	BIOS2ROM_FILENAME	"bios4.rom"
#else	/* CPU_40_STYLE */
#define	BIOS2ROM_FILENAME	"bios2.rom"
#endif	/* CPU_40_STYLE */
#endif /* BIOS2ROM_FILENAME */

#ifndef EGAROM_FILENAME
#define	EGAROM_FILENAME		"ega.rom"
#endif /* EGAROM_FILENAME */

#ifndef VGAROM_FILENAME
#define	VGAROM_FILENAME		"vga.rom"
#endif /* VGAROM_FILENAME */

#ifndef V7VGAROM_FILENAME
#define	V7VGAROM_FILENAME	"v7vga.rom"
#endif /* V7VGAROM_FILENAME */

#ifdef 	GISP_SVGA
#define	GISP_VGAROM_FILENAME         "hwvga.rom"
#define	GISP_BIOS1ROM_FILENAME       "hwbios1.rom"
#define	GISP_BIOS2ROM_FILENAME       BIOS2ROM_FILENAME
#endif

#ifndef ADAPTOR_ROM_START
#define ADAPTOR_ROM_START	0xc8000
#endif	/* ADAPTOR_ROM_START */

#ifndef ADAPTOR_ROM_END
#define ADAPTOR_ROM_END		0xe0000
#endif	/* ADAPTOR_ROM_END */

#define ADAPTOR_ROM_INCREMENT	0x800

#ifndef EXPANSION_ROM_START
#define EXPANSION_ROM_START	0xe0000
#endif	/* EXPANSION_ROM_START */

#ifndef EXPANSION_ROM_END
#define EXPANSION_ROM_END	0xf0000
#endif	/* EXPANSION_ROM_END */

#define EXPANSION_ROM_INCREMENT	0x10000

#define	ROM_SIGNATURE		0xaa55

/* Current SoftPC verion number */
#define MAJOR_VER	0x03
#define MINOR_VER	0x00

#if defined(macintosh) && defined(A2CPU)
	/* Buffer is temporarily allocted  - no bigger than needed. */
#define ROM_BUFFER_SIZE 1024*25
#else
	/* Using sas_scratch_buffer - will get 64K anyway. */
#define ROM_BUFFER_SIZE 1024*64
#endif

LOCAL LONG read_rom IPT2(char *, name, sys_addr, address);
LOCAL	half_word	do_rom_checksum IPT1(sys_addr, addr);

#ifdef ANSI
extern long host_read_resource (int, char *, host_addr, int ,int);
#else
extern long host_read_resource ();
#endif

/*(
 *=========================== patchCheckSum ================================
 * patchCheckSum
 *
 * Purpose
 *	This function calculates the check-sum for the indicated ROM,
 *	and patches it in at the indicated offset into the ROM.
 *
 *	It also checks that the ROM has the correct signature, and length,
 *	and rounds the size up to a multiple of 512 bytes.
 *
 *	Note this routine should not be called once paging is turned on.
 *
 * Input
 *	start	Physical address of start of ROM
 *	length	length of ROM in bytes
 *	offset	Checksum byte offset from start.
 *
 * Outputs
 *	None.
 *
 * Description
 *	We round the size-up to a multiple of 512 bytes, check the
 *	signature, then patch-in the checksum.
)*/

LOCAL void
patchCheckSum IFN3(PHY_ADDR, start, PHY_ADDR, length, PHY_ADDR, offset)
{
	PHY_ADDR roundedLength;
	IU16 signature;
	IU8 checksum;
	IU8 *buffer;
	PHY_ADDR currByte;
	PHY_ADDR indicatedLength;
	

	roundedLength = (length + 511) & (~511);
	sas_connect_memory(start, start + roundedLength - 1, SAS_RAM);

#ifndef PROD
	if (roundedLength != length) {
		always_trace3("ROM at 0x%.5lx length rounded up from 0x%.8lx to 0x%.8lx", start, length, roundedLength);
	}

	if (roundedLength > (128 * 1024)) {
		always_trace2("ROM at 0x%.5lx has a length of 0x%.8lx which is more than 128K", start, roundedLength);
		force_yoda();
		return;
	}

	if ((roundedLength <= offset) || (roundedLength < 4)) {
		always_trace1("ROM at 0x%.5lx is too short!", start);
		force_yoda();
		return;
	}
#endif

	signature = sas_PR16(start);
	if (signature != 0xaa55) {
		always_trace2("ROM at 0x%.5lx has an invalid signature 0x%.4x (should be aa55)", start, signature);
		sas_PW16(start, 0xaa55);
	}

	indicatedLength = sas_PR8(start + 2) * 512;
	if (indicatedLength != roundedLength) {
		always_trace3("ROM at 0x%.5lx has incorrect length 0x%.8lx (actually 0x%.8lx)", start, indicatedLength, roundedLength);
		sas_PW8(start + 2, roundedLength / 512);
	}


	check_malloc(buffer, roundedLength, IU8);

	sas_loads((LIN_ADDR)start, buffer, roundedLength);

	checksum = 0;
	for (currByte = 0; currByte < roundedLength; currByte++) {
		checksum += buffer[currByte];
	}
	host_free(buffer);

	if (checksum != 0) {
		always_trace2("ROM at 0x%.8lx has incorrect checksum 0x%.2x", 
			start, checksum);
		sas_PW8(start + offset, 
			(IU8)((IS8)sas_PR8(start + offset) - checksum));
	}
	sas_connect_memory(start, start + roundedLength - 1, SAS_ROM);

}


/*(
=============================== read_video_rom ============================
PURPOSE:	Load the appropriate video rom file.
INPUT:		None.
OUTPUT:		None.
===========================================================================
)*/
GLOBAL void read_video_rom IFN0()
{
#ifdef REAL_VGA
	read_rom (VGAROM_FILENAME, EGA_ROM_START);
#else /* REAL_VGA */
	PHY_ADDR romLength = 0;

	switch ((ULONG) config_inquire(C_GFX_ADAPTER, NULL))
	{
#ifndef GISP_SVGA
#ifdef	VGG
	case VGA:
#ifdef V7VGA
		romLength = read_rom (V7VGAROM_FILENAME, EGA_ROM_START);
#else	/* V7VGA */
		romLength = read_rom (VGAROM_FILENAME, EGA_ROM_START);
#endif  /* V7VGA */
		break;
#endif	/* VGG */

#ifdef	EGG
	case EGA:
		romLength = read_rom (EGAROM_FILENAME, EGA_ROM_START);
		break;
#endif	/* EGG */

	default:
		/* No rom required */
		break;

#else			/* GISP_SVGA */

	/* GISP_SVGA - only have the gisp vga roms or, none for CGA boot */
	case VGA:
		romLength = read_rom (GISP_VGAROM_FILENAME, EGA_ROM_START);
	default:
		break;

#endif		/* GISP_SVGA */	

	}

	if (romLength != 0)
	{
		/* There is a problem with emm386 and Windows start up, which
		 * is cured by setting the video bios rom internal length
		 * to 32Kb.
		 * Is seems that the V86 manager (or emm386) incorrectly
		 * maps C6000..C7FFF during initialisation.
		 * We round up the video ROM to 32Kb to avoid this problem,
		 * which reduces the amount of "upper memory" RAM available to 
		 * dos extenders by 12K.
		 */
		if (romLength < (32*1024))
			romLength = (32*1024);
		patchCheckSum(EGA_ROM_START, romLength, 5);
	}
#endif	/* not REAL_VGA */
}

GLOBAL void rom_init IFN0()
{
#if !defined(NTVDM) || ( defined(NTVDM) && !defined(X86GFX) )
	 /*
     * Fill up all of ROM (Intel C0000 upwards) with bad op-codes.
     * This is the Expansion ROM and the BIOS ROM.
     * This will enable the CPU to trap any calls to ROM that are not made at a
     * valid entry point.
     */

#ifdef GISP_SVGA
	mapHostROMs( );
#else		/* GISP_SVGA */
#if	defined(macintosh) && defined(A2CPU)
	/* not macintosh 2.0 cpus - they have sparse M */
#else
	sas_fills( ROM_START, BAD_OP, PC_MEM_SIZE - ROM_START);
#endif		/* macintosh && A2CPU */
#endif		/* GISP_SVGA */

	/*
	 * emm386 needs a hole to put it's page frame in.
	 */
#if defined(SPC386) && !defined(GISP_CPU)
	sas_connect_memory(0xc0000, 0xfffff, SAS_ROM);
#endif

	/* Load the video rom. */
	read_video_rom();
    
	/* load the rom bios */
#ifdef GISP_SVGA
	if ((ULONG) config_inquire(C_GFX_ADAPTER, NULL) == CGA )
	{
		read_rom (BIOS1ROM_FILENAME, BIOS_START);
		read_rom (BIOS2ROM_FILENAME, BIOS2_START);
	}
	else
	{
		read_rom (GISP_BIOS1ROM_FILENAME, BIOS_START);
		read_rom (GISP_BIOS2ROM_FILENAME, BIOS2_START);
	}

#else		/* GISP_SVGA */

	read_rom (BIOS1ROM_FILENAME, BIOS_START);
	read_rom (BIOS2ROM_FILENAME, BIOS2_START);

#endif		/* GISP_SVGA */

#else	/* !NTVDM | (NTVDM & !X86GFX) */

	/*
	 * Now tell the CPU what it's not allowed to write over...
	 *
	 * These used to be done for everyone, but now they're only done for NT
	 * as everyone else should have done it inside read_rom.
	 */
	sas_connect_memory (BIOS_START, 0xFFFFFL, SAS_ROM);
#ifdef EGG
	sas_connect_memory (EGA_ROM_START, EGA_ROM_END-1, SAS_ROM);
#endif
#endif /* !NTVDM | (NTVDM & !X86GFX) */

	host_rom_init();
}

LOCAL LONG read_rom IFN2(char *, name, sys_addr, address)
{
#if !(defined(NTVDM) && defined(MONITOR))
	host_addr tmp;
	long size = 0;

    /* do a rom load - use the sas_io buffer to get it the right way round 	*/
    /* BIOS rom first. 														*/
	/* Mac on 2.0 cpu doesn't want to use sas scratch buffer. 				*/
#if defined(macintosh) && defined(A2CPU)
    tmp = (host_addr)host_malloc(ROM_BUFFER_SIZE);
#else
	tmp = (host_addr)sas_scratch_address(ROM_BUFFER_SIZE);
#endif

    if (!tmp)
    {
	host_error(EG_MALLOC_FAILURE, ERR_CONT | ERR_QUIT, NULL);
	return(0);
    }
    if (size = host_read_resource(ROMS_REZ_ID, name, tmp, ROM_BUFFER_SIZE, TRUE))
    {
	sas_connect_memory( address, address+size, SAS_RAM);
        sas_stores (address, tmp, size);
	sas_connect_memory( address, address+size, SAS_ROM);
    }

#if defined(macintosh) && defined(A2CPU)
	host_free((char *)tmp);
#endif

    return( size );
#else
    return ( 0L );
#endif	/* !(NTVDM && MONITOR) */
}

LOCAL	half_word	do_rom_checksum IFN1(sys_addr, addr)
{
	LONG	sum = 0;
	sys_addr	last_byte_addr;

	last_byte_addr = addr + (sas_hw_at(addr+2)*512);

	for (; addr<last_byte_addr; addr++)
		sum += sas_hw_at(addr);

	return( sum % 0x100 );
}

LOCAL	VOID	do_search_for_roms IFN3(sys_addr, start_addr,
	sys_addr, end_addr, unsigned long, increment)
{
	word	signature;
	half_word	checksum;
	sys_addr	addr;
	word		savedCS;
	word		savedIP;

	for ( addr = start_addr; addr < end_addr; addr += increment )
	{
		if ((signature = sas_w_at(addr)) == ROM_SIGNATURE)
		{
			if ((checksum = do_rom_checksum(addr)) == 0)
			{
			/*
				Now point at address of init code.
			*/
				addr += 3;
			/*
				Fake a CALLF by pushing a return CS:IP.
				This points at a BOP FE in the bios to
				get us back into 'c'
			*/
				push_word( 0xfe00 );
				push_word( 0x95a );
				savedCS = getCS();
				savedIP = getIP();
				setCS((addr & 0xf0000) >> 4);
				setIP((addr & 0xffff));
				host_simulate();
				setCS(savedCS);
				setIP(savedIP);
				assert1(NO, "Additional ROM located and initialised at 0x%x ", addr-3);
			}
			else
			{
				assert2(NO, "Bad additonal ROM located at 0x%x, checksum = 0x%x\n", addr, checksum);
			}
		}
	}
}

GLOBAL void search_for_roms IFN0()
{
#if !defined(NTVDM) || (defined(NTVDM) && !defined(X86GFX))
#ifndef GISP_SVGA
/*
        First search for adaptor ROM modules
*/
    do_search_for_roms(ADAPTOR_ROM_START,
                                ADAPTOR_ROM_END, ADAPTOR_ROM_INCREMENT);

/*
        Now search for expansion ROM modules
*/
    do_search_for_roms(EXPANSION_ROM_START,
                                EXPANSION_ROM_END, EXPANSION_ROM_INCREMENT);
#endif 		/* GISP_SVGA */
#endif	/* !NTVDM | (NTVDM & !X86GFX) */
}


GLOBAL void rom_checksum IFN0()
{
#if !defined(NTVDM) || (defined(NTVDM) && !defined(X86GFX) )
	patchCheckSum(BIOS_START, PC_MEM_SIZE - BIOS_START,
				0xfffff - BIOS_START);
#endif	/* !NTVDM | (NTVDM & !X86GFX) */
}

GLOBAL VOID patch_rom IFN2(sys_addr, addr, half_word, val)
{
#if !defined(NTVDM) || (defined(NTVDM) && !defined(X86GFX) )
	UTINY	old_val;

	/*
	 * 4.0 style CPUs don't export this variable, and if sas hasn't been
	 * inited, then the sas_connect will drop out to yoda.
	 */

#ifdef CPU_40_STYLE

	IU8	*hostPtr;

	/* TMM 14/2/95
	 * -----------
	 * What we are doing here is replacing the sas_connect() method of writing to ROM
	 * with the new approach of poking the values directly in there. See display_string()
	 * below for a more detiled discussion of the why's and wherefores.
	 */

#ifdef macintosh

	/* The Mac config system wants to call this routine before the
	 * CPU exists, so we'd better invent a Mac-specific IBOOL to
	 * make the symptom non-fatal - finding and fixing the cause
	 * is too hard.
	 */
	{
		extern IBOOL SafeToCallSas;

		if (!SafeToCallSas)
			return;	
	}

#endif /* macintosh */
 
	/* The page might not be present (Arrggghhhh!!!!!)
	** so we can't do anything sensible and must give
	** up. We print an error though.
	*/
	hostPtr = getPtrToPhysAddrByte (addr);
	if (hostPtr == 0)
	{
		host_error(EG_OWNUP, ERR_QUIT, NULL);
		return;
	}

	old_val = *hostPtr;

	/* Optimisation - don't upset the world if the value is unchanged.
	 */
	if (old_val == val)
		return;

	*hostPtr = val;

/*
 *	Adjust the checksum value by new - old.
 *	val is now difference between new and old value.
 *	We don't do this for GISP_SVGA because the checksums are already
 *	screwed, and attempting to write to the real host system ROM would
 *	only make things worse!
 */

#ifndef GISP_SVGA
	/* Now get the checksum at the end of the ROM */
	hostPtr = getPtrToPhysAddrByte (0xFFFFFL);
	if (hostPtr == 0)
	{
		host_error(EG_OWNUP, ERR_QUIT, NULL);
		return;
	}
	
	/* Now set the checksum to the difference between the old and new values */
	*hostPtr -= (val - old_val);
	
#endif /* GISP_SVGA */

#else	/* CPU_40_STYLE */

	/*
	 * 4.0 style CPUs don't export this variable, and if sas hasn't been
	 * inited, then the sas_connect will drop out to yoda.
	 */

	if (Length_of_M_area == 0) 
		return;
 
	old_val = sas_hw_at( addr );

	/* Optimisation - don't upset the world if the value is unchanged.
	 */
	if (old_val == val)
		return;

	sas_connect_memory (addr, addr, SAS_RAM);
	sas_store (addr,val);
	sas_connect_memory (addr, addr, SAS_ROM);
/*
 *	Adjust the checksum value by new - old.
 *	val is now difference between new and old value.
 *	We don't do this for GISP_SVGA because the checksums are already
 *	screwed, and attempting to write to the real host system ROM would
 *	only make things worse!
 */

#ifndef GISP_SVGA
	val -= old_val;
	old_val = sas_hw_at( 0xFFFFFL );

	old_val -= val;
	sas_connect_memory (0xFFFFFL, 0xFFFFFL, SAS_RAM);
	sas_store (0xFFFFFL, old_val);
	sas_connect_memory (0xFFFFFL, 0xFFFFFL, SAS_ROM);
#endif /* GISP_SVGA */

#endif	/* CPU_40_STYLE */

#endif	/* !NTVDM | (NTVDM & !X86GFX) */
}

#ifndef GISP_SVGA

/*
 * These routines were used by 2.0 CPUs which performed
 * post-write checks. Since all 3.0 and later CPUs do
 * pre-write checks they're no longer needed.
 */
 
#if !(defined(NTVDM) & defined(MONITOR))
void update_romcopy IFN1(long, addr)
{
	UNUSED( addr );
}
#endif

GLOBAL void copyROM IFN0()
{
}

#endif		/* GISP_SVGA */

/*
 * To enable our drivers to output messages generated from
 * our bops we use a scratch area inside our rom.
 */
#ifndef GISP_SVGA
LOCAL sys_addr  cur_loc = DOS_SCRATCH_PAD;
#else		/* GISP_SVGA */
/* For GISP svga builds, we initialise from gispROMInit() */
sys_addr  cur_loc;
#endif		/* GISP_SVGA */

GLOBAL void display_string IFN1(char *, string_ptr)
{
#if !defined(NTVDM) || (defined(NTVDM) && !defined(X86GFX) )
	/*
	 * Put the message "*string_ptr" in the ROM
	 * scratch area where the drivers know where
	 * to output it from.
	 */

#ifdef CPU_40_STYLE

	IU8	*hostPtr;
	IU16	count;
	IU32	endLinAddr;

	/* In a paging environment, we must be careful as a
	** the ROM area could have been copied and/or mapped
	** as read only. We must alter the memory which is 
	** currently at the linear address of the ROM (whether
	** that is actually our rom or a RAM copy of it). We
	** must force this alteration despite any protection
	** placed on the page by the Intel page tables.
	*/

	/* get a host pointer to the memory behind the required
	** linear address. 
	*/
	hostPtr = getPtrToLinAddrByte(cur_loc);

	/* The page might not be present (Arrggghhhh!!!!!)
	** so we can't do anything sensible and must give
	** up. We print an error though.
	*/
	if (hostPtr == 0)
	{
		host_error(EG_OWNUP, ERR_QUIT, NULL);
		return;
	}

	/* the area to be patched must lie entirely in one intel page for
	** this method to be sure to work. So check it.
	*/
	endLinAddr = (cur_loc + strlen(string_ptr) + 2);
	if (((endLinAddr ^ DOS_SCRATCH_PAD) > 0xfff) || (endLinAddr > DOS_SCRATCH_PAD_END))
	{
#ifndef PROD
		fprintf(trace_file, "*** Warning ***: patch string into ROM too long; tuncating string '%s'", string_ptr);
#endif
		if ((DOS_SCRATCH_PAD_END ^ DOS_SCRATCH_PAD) > 0xfff)
		{
			/* The defined DOS scratch pad crosses a page
			** boundary. must truncate to the page boundary,
			** allowing for the '$' and terminating zero
			*/
			string_ptr[0xffd - (DOS_SCRATCH_PAD & 0xfff)] = '\0';
		}
		else
		{
			/* The string overflows the DOS scratch pad. We
			** must truncate to the scrtach pad boundary,
			** allowing for the '$' and terminating zero
			*/
			string_ptr[cur_loc - DOS_SCRATCH_PAD - 2] = '\0';
		}
	}
	for (count = 0; count < strlen(string_ptr); count++)
	{
		*IncCpuPtrLS8(hostPtr) = string_ptr[count];
	}
	/* Terminate the string */
	*IncCpuPtrLS8(hostPtr) = '$';
	*IncCpuPtrLS8(hostPtr) = '\0';
#else /* CPU_40_STYLE */
	sas_connect_memory(DOS_SCRATCH_PAD, DOS_SCRATCH_PAD_END, SAS_RAM);
	sas_stores(cur_loc, (host_addr)string_ptr, strlen(string_ptr));
	cur_loc += strlen(string_ptr);

	/* Terminate the string */
	sas_store(cur_loc, '$');
	sas_store(cur_loc + 1, '\0');
	sas_disconnect_memory(DOS_SCRATCH_PAD, DOS_SCRATCH_PAD_END);
	cur_loc -= strlen(string_ptr);
#endif /* CPU_40_STYLE */
#endif	/* !NTVDM | !MONITOR */
	cur_loc+=strlen(string_ptr);
}

GLOBAL void clear_string IFN0()
{
        cur_loc = DOS_SCRATCH_PAD;  /* Need to reset this pointer to start of **
                                    ** scratch area to prevent messages being **
                                    ** repeatedly displayed.                  */
	display_string ("");
}

/* Returns the SoftPC version to our device drivers */

GLOBAL void softpc_version IFN0()
{
	setAH(MAJOR_VER);
	setAL(MINOR_VER);
}
