/***************************************************************************
*       NAME:  STARTUPC.C $Revision: 1.35 $
**      COPYRIGHT:
**      "Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************/
#include <io.h>
#include <stdlib.h>
#include <dos.h>
#include <fcntl.h>
#include <dir.h>
#include <string.h>
#include "findsbos.h"
#include "sbosdata.h"
#include "sbosdefs.h"
#include "shared.h"
#include "resfunc.h"
#include "stuff.h"
#include "synth.h"
#include "mpu401.h"
#include "error.h"
#include "hardware.h"
#include "iwpatch.h"

extern PFV start_release_callback;
extern PFV done_release_callback;

extern void default_callback(void);
extern unsigned char *begin_extra_data;
extern int extra_data_size;
extern int save_lmcfi;
extern int new_lmcfi;
extern SYN_VOICE *voice_data;
extern SHARED shared;
extern unsigned int seg_loader_parm;
extern unsigned int alloc_seg;
extern unsigned int psp_seg_addr;
extern int num_paras;
extern char cmd_line_argv0[];
extern char sbos512[];
extern char sbos1024[];
extern unsigned int mpu401_port;
extern int rom_flag;
extern char verstring2;
extern char default_banner;
extern char fff_string[];
extern char sbos_string[];
extern char iwave_string[];

extern char crlf[];
extern char mpustr[];
extern char sbos_ini_hdr[];
extern char setup_ini_hdr[];
extern char library512[];
extern char library1024[];
extern char lib_path[];
extern char ini_path[];
extern char csn[];
extern char pnprdp[];
extern int pnp_csn;
extern int pnp_read;
extern char Mpu401Emulation[];
extern char memcfg[];
extern char seveneee[];
extern char SbosVector[];
extern char quietst[];
extern char quiet;
extern char chainnmi[];
extern char on[];
void PeekString(unsigned int,unsigned long,void far *,unsigned int,int);

extern unsigned char * _END_BASE_LABEL;
extern unsigned char * _END_MPU_LABEL;

extern unsigned int lower_base_port;
extern unsigned int upper_base_port;
extern unsigned int reg_mixer;
extern unsigned int reg_sb2x6;
extern unsigned int reg_irq_status;
extern unsigned int reg_dram_io;
extern unsigned int reg_adlib_status;
extern unsigned int reg_update_sb2xA;
extern unsigned int reg_adlib_data;
extern unsigned int reg_sb2xC;
extern unsigned int reg_2xC_write;
extern unsigned int reg_sb2xE;
extern unsigned int reg_status_read;
extern unsigned int reg_2xFcontrol;
extern unsigned int reg_2xBindexed;
extern unsigned int reg_voice_sel;
extern unsigned int reg_index;
extern unsigned int reg_data_high;
extern unsigned int reg_data_low;
extern unsigned int reg_388_read;
extern unsigned int reg_389;
extern void (interrupt *old_int7x)();

/****************************************************************************

FUNCTION DEFINITION:
print_string -

*/
void print_string(char * string)
{
	asm mov ax,0900h
	asm mov dx, string
	asm int 21h
}

/****************************************************************************

FUNCTION DEFINITION:
print_crlf -

*/
void print_crlf(void)
{
	asm mov ax,0900h
	asm lea dx,crlf
	asm int 21h
}

/****************************************************************************

FUNCTION DEFINITION:
write_dram - Writes a byte value to DRAM

DESCRIPTION:
  This function is present only for INTERWAVE compiled versions of SBOS
  This function needs to be resident for the MAX version and is defined
  in resfunc.c for that reason.

SEE ALSO:
  read_dram

*/
void write_dram( unsigned long address, unsigned char value )
{
	outp(reg_index, 0x43); /** set DRAM low **/
	outpw (reg_data_low, (unsigned int)(address));
	outp(reg_index,0x44); /** set DRAM high **/
	outp(reg_data_high,LOW_B(MS_WORD(address)));
	outp(reg_dram_io,value);
}

/****************************************************************************

FUNCTION DEFINITION:
read_dram - Reads a byte from DRAM

DESCRIPTION:
  This function is present only for INTERWAVE compiled versions of SBOS
  This function needs to be resident for the MAX version and is defined
  in resfunc.c for that reason.

SEE ALSO:
  write_dram

RETURNS:
  uchar - DRAM byte value

*/
unsigned char read_dram( unsigned long address )
{
	unsigned char retval;

	outp(reg_index, 0x43); /** set DRAM low **/
	outpw (reg_data_low, (unsigned int)(address));
	outp(reg_index,0x44); /** set DRAM high **/
	outp(reg_data_high,LOW_B(MS_WORD(address)));
	retval = inportb(reg_dram_io);
	return retval;
}

int
match_chunk(char *str1,char *str2,int length)
{
int i;

for (i=0;i<length;i++)
	if (*str1++ != *str2++)
		return(FALSE);
return(TRUE);
}

/***************************************************************************

FUNCTION DEFINITION:
setup_driver - Opens library file and gets pointers into DRAM

DESCRIPTION:
    If compiled for the INTERWAVE:
        Looks in the environment for the iw.ini path and opens the iw.ini file
        Searches for the path and filename for the appropriate sbs file and
        tries to open it.  If it cant find the file(s)...
    If compiled for the MAX:
        Looks in the directory the driver executable exists in and tries again 
        to open the file(s).  If the open fails on the 1024k lib, it tries 
        to open the 512k sbs file if working with 1M RAM or more.  If that 
        fails, prints a message and exits with a bad status.
    Otherwise, reads the sbs header and gets pointers to DRAM.  Also gets 
    structure sizes and determines resident size of driver.

RETURNS: int -  0 if pass
               -1 if fail

*/
int setup_driver(void)
{
    unsigned int pos;             /** position after resident code to keep **/
    unsigned int i;
    int wave_data;                /** size of wave data **/
    int handle=-1;                /** file handle for sbs files **/
    SBOS_LIB_HEADER lib_header;
    char char_mpubase[5];
    unsigned int mpu_base;
    char nullchar = 0;
    char *ptrtonull = &nullchar;
    char *libnameptr = NULL;
	long rom_place;
	ROM_HDR rom_hdr;
	CHUNK_HDR fff_chunk;
	CHUNK_HDR sbos_chunk;

    char sbs_cmd_line[127];
    char *ptr;

    /** 
     ** Open the iw.ini file and get the path of the sbs files
     **/
    if(!(shared.status & STAT_ROM))
        {
        rom_flag = FALSE;
    
        /**
         ** Try to open the libs - dram_size must be 2 or 4
         **/
        if(shared.sbosdata->dram_size == 2)
            libnameptr=library512;
        else if(shared.sbosdata->dram_size == 4)
            libnameptr=library1024;

        iwu_get_profile_string(sbos_ini_hdr,libnameptr, ptrtonull,
            lib_path, 127,ini_path);

        handle = file_open(lib_path);
    
        if(handle < 0)
            {
            print_error(ERROR_BAD_LIB);
            return(-1);
            }
    
        /** 
         ** Read the library header to get the dram size information
         **/
        if(file_read(handle,&lib_header,sizeof(SBOS_LIB_HEADER)) != 
            sizeof(SBOS_LIB_HEADER))
            {
            print_error(ERROR_BAD_LIB);
            return(-1);
            }
    
        file_close(handle);
    
        }
    else        // Read parms out of ROM instead ....
        {
#ifdef FAKE_ROM
        rom_flag = FALSE;    // fake out code .... (in reload_data also ...)
#else
        rom_flag = TRUE;
#endif
        // I know this looks useless now, but when the temp code below
        // is done, this is necessary .....

        // Save the current RAM/ROM config
        outp(reg_index,IW_LMCFI);
        save_lmcfi = inpw(reg_data_low);
        // Now change it so ROMS are 4 meg on 4 meg boundaries
        new_lmcfi = ((save_lmcfi & 0xFF1F) | 0x0080);
        outpw(reg_data_low,new_lmcfi);

	    /* Set local memory for auto increment (so PeekStrings will work)*/
    	outp( reg_index, IW_LMCI);
	    outp( reg_data_high,IWL_LMCI_AI);

        lib_header.num_waves = 512;	// init to max ...

//#ifdef NEVER
//--------------------------------------------------------------------
		for (i=0;i<4;i++)
			{
			// See if this rom slot has the SBOS chunk in it.
			rom_place = (long)i*1024L*1024L*4L;
			// Read this ROM Header.
			PeekString(reg_dram_io, rom_place, &rom_hdr, sizeof(rom_hdr),rom_flag);
			// See if its a valid INTRWAVE header ...
			if (match_chunk(rom_hdr.iwave,iwave_string,8))
				{
				rom_place += sizeof(rom_hdr);
				// Save the base location for this ROM chip ....
				if (rom_hdr.series_number == 0)
					{
					// OK. I go the FIRST rom with series #0 ...
					// Now shuffle thru FFF file stuff to find sbos chunk
					while(TRUE)
						{
						PeekString(reg_dram_io, rom_place, &fff_chunk, sizeof(fff_chunk),rom_flag);
						if (match_chunk(fff_chunk.tag,fff_string,4))
							{
							PeekString(reg_dram_io, rom_place+sizeof(fff_chunk), &sbos_chunk, sizeof(fff_chunk),rom_flag);
							if (match_chunk(sbos_chunk.tag,sbos_string,4))
								{
								rom_place +=  2*sizeof(fff_chunk);
								// Found it. Now fixup some stuff ....
								// First fill in library header.
								// Peek into ROM and pick out sboslib struct ....
                        		PeekString(reg_dram_io, rom_place, &lib_header, sizeof(lib_header),rom_flag);
								}
							else    // skip past this FFFF to the NEXT one
								rom_place += (sizeof(fff_chunk)+fff_chunk.length);
							}
						else    // No more FFFF's .....
							break;
						}
					}
				}
			}
//#endif
//--------------------------------------------------------------------
        // Now restore the old config
        outp(reg_index,IW_LMCFI);
        outpw(reg_data_low,save_lmcfi);
        }

	/** 
	 ** Test the MPU-401 baseport for validity
	 **/
	if(shared.sbosdata->mpu401_port == 0)
		shared.status &= ~STAT_MPU401_ENABLED;

    /**
     ** If I'm in MPU-401 mode, use the end of that code for the end of the
     ** code to remain resident.  Otherwise, use the end of the SB emulation
     ** code.
     **/
    if(shared.status & STAT_MPU401_ENABLED)
        {
//      if(print_message(MESSAGE_MPU_BANNER) == 0) print_crlf();
        pos = FP_OFF(&_END_MPU_LABEL);
        start_release_callback = mpu_start_release_callback;
        done_release_callback = mpu_done_release_callback;
        }
    else
        {
        pos = FP_OFF(&_END_BASE_LABEL);
        start_release_callback = default_callback;
        done_release_callback = default_callback;
        }

    /**
     ** Get the size of dram and tack on space for wave data
     **/
    wave_data = lib_header.num_waves;

    /**
     ** Save dram positions in sbosdata struct
     **/
    shared.sbosdata->dram_begin         = lib_header.dram_info.dram_begin;
    shared.sbosdata->dram_stopped_voice=lib_header.dram_info.dram_stopped_voice;
    shared.sbosdata->dram_temp_buff     = lib_header.dram_info.dram_temp_buff;
    shared.sbosdata->dram_perc_map      = lib_header.dram_info.dram_perc_map;
    shared.sbosdata->dram_wave_info     = lib_header.dram_info.dram_wave_info;
    shared.sbosdata->dram_mpu_info      = lib_header.dram_info.dram_mpu_info;

    shared.wave_data = (WAVE_DATA *)pos;
    pos += wave_data;      /** add in resident wave data **/

    // Make sure this is AFTER wave data and info......
    begin_extra_data = (unsigned char *)pos;
    extra_data_size = pos;

    /** 
     ** OK. Now add in the amount needed per voice and init the voice pointer
     ** data structure. This way we can have a variable # of voices without
     ** taking up RAM we don't need.
     **/
    voice_data = (SYN_VOICE *)pos;// this points to the BEGINNING of indiv.
                                   // voice's data.

    // Now bump past by appropriate amount
    if(shared.status & STAT_MPU401_ENABLED)    // IW will use 32 voices for MPU401
        pos += (sizeof(SYN_VOICE)*32);
    else
        pos += (sizeof(SYN_VOICE)*18);
    
    extra_data_size = pos - extra_data_size;

    /**
     ** POS is now the position of the end of all resident code and data
     **/
    pos += 15;             /** add in 15 for avoiding truncation problems **/
    pos = (pos >> 4);      /** divide by 16 to get paragraphs **/
    num_paras = (int)pos;  /** save it **/

    return(0);
}

/****************************************************************************

FUNCTION DEFINITION:
iw_test_iw_ini - Tests for the existence of the iw.ini file.

DESCRIPTION:
  Tries to perform a file open for the file specified in ini_path.  If the open
  fails, a message is printed and bad status is returned.

EXPECTS:
  ini_path to hold the full pathname to the iw.ini file

RETURNS
  0 - iw.ini file present
 -1 - iw.ini file not present

*/
int iw_test_iw_ini(void)
{
    int fp;

    fp = file_open(ini_path);
    if(fp <= 0)
        {
        print_error(ERROR_BAD_IWINI);
        return (-1);
        }
    file_close(fp);
    return(0);
}

/****************************************************************************

FUNCTION DEFINITION:
iw_get_quiet - gets the quiet option from the iwsbos section in the iw.ini file

EXPECTS:
  ini_path is the full path name to the iw.ini file

MODIFIES:
  quiet - global

*/
void iw_get_quiet(void)
{
    int bank;
    char char_option[5];
    char nullchar = 0;
    char *ptrtonull = &nullchar;

    iwu_get_profile_string(sbos_ini_hdr,quietst,ptrtonull,char_option, 5,
        ini_path);

    if((char_option[0] == 'O') || (char_option[0] == 'o'))
    if((char_option[1] == 'N') || (char_option[1] == 'n'))
        quiet = 1;
}

/****************************************************************************

FUNCTION DEFINITION:
iw_get_nmi_opt - 

EXPECTS:
  ini_path is the full path name to the iw.ini file

*/
void iw_get_nmi_opt(void)
{
    int bank;
    char char_option[5];
    char nullchar = 0;
    char *ptrtonull = &nullchar;

    iwu_get_profile_string(sbos_ini_hdr,chainnmi,ptrtonull,char_option, 5,
        ini_path);

    if((char_option[0] == 'O') || (char_option[0] == 'o'))
    if((char_option[1] == 'N') || (char_option[1] == 'n'))
        shared.status |= STAT_CHAIN_NMI;
}

/****************************************************************************

FUNCTION DEFINITION:
iw_get_memcfg -

DESCRIPTION:

EXPECTS:

MODIFIES:

SEE ALSO:

RETURNS:

*/
int iw_get_memcfg(void)
{
    char char_option[6];
    int bank;
    char nullchar = 0;
    char *ptrtonull = &nullchar;

    iwu_get_profile_string(sbos_ini_hdr,memcfg,ptrtonull,char_option, 6,
        ini_path);

    shared.status |= STAT_ROM; /** ROM by default **/

    if((char_option[0] == 'R') || (char_option[0] == 'r'))
    if((char_option[1] == 'A') || (char_option[1] == 'a'))
    if((char_option[2] == 'M') || (char_option[2] == 'm'))
        shared.status &= ~STAT_ROM;

    return(0);
}

/****************************************************************************

FUNCTION DEFINITION:
iw_get_mpu_opt -

DESCRIPTION:

EXPECTS:

MODIFIES:

SEE ALSO:

RETURNS:

*/
void iw_get_mpu_opt(void)
{
    char char_option[5];

	/** Enabled by default - shut off if something is wrong ... **/
    shared.status |= STAT_MPU401_ENABLED;

    iwu_get_profile_string(sbos_ini_hdr,Mpu401Emulation,on,char_option,
        5,ini_path);

    if((char_option[0] == 'O') || (char_option[0] == 'o'))
    if((char_option[1] == 'F') || (char_option[1] == 'f'))
    if((char_option[2] == 'F') || (char_option[2] == 'f'))
        shared.status &= ~STAT_MPU401_ENABLED;
}

/****************************************************************************

FUNCTION DEFINITION:
iw_get_comm_vect -

DESCRIPTION:

EXPECTS:

MODIFIES:

SEE ALSO:

RETURNS:

*/
int iw_get_comm_vect(void)
{
    int vector;
    char char_vector[5];

    iwu_get_profile_string(sbos_ini_hdr,SbosVector,seveneee,char_vector,
        5,ini_path);
    vector = iwu_strtol(char_vector,NULL,16);
    if((vector < MIN_SBOS) || (vector > MAX_SBOS))
        {
        print_error(ERROR_BAD_VECTOR);
        return(-1);
        }

    shared.sbosdata->comm_vect = vector;
    return(0);
}

/***************************************************************************

 FUNCTION_DEFINITION:
 test_base_port - Tests the base port returned from the PNP ports

 DESCRIPTION:
   If the base port is 0xffff or 0, a message is printed out saying that the 
   card can't be found and a bad status is returned.

 RETURNS: 
   int - 0 pass
        -1 fail

*/
int test_base_port(void)
{
    if((lower_base_port == 0xffff) || (lower_base_port == 0))
		{
		print_error(ERROR_CANT_FIND_CARD);
		return(-1);
		}
    return(0);
}

/***************************************************************************

 FUNCTION_DEFINITION:
 test_dram_size - Determines if dram_size is valid

 DESCRIPTION:
   If dram_size is 0 - there is no card at the selected baseport - error
   If dram_size is 1 - there is 256k DRAM - minimun needed is 512k - error
   If dram_size is 2 - no error
   If dram_size is 3 - there is no 768K library - make dram_size = 2 - no error
   If dram_size is 4 - no error
   If dram_size >  4 - make it 4 - no error

 EXPECTS:
   dram_size - Valid result from size_dram()
               Number represents the number of 256k banks of DRAM present

 MODIFIES:
   dram_size - may be set from 3 to 2

 RETURNS: int - -1 if DRAM size will prohibit SBOS from running
                0 if no error

*/
int test_dram_size(void)
{
    if(shared.status & STAT_ROM)
        return(0);

    if(shared.sbosdata->dram_size == 0)
        {
		if(shared.status & STAT_WINDOWS_RUNNING)
			print_error(ERROR_DEVICE_IN_USE);
		else
			print_error(ERROR_LOW_MEM);
        return(-1);
        }

    if(shared.sbosdata->dram_size == 1)
        {
        print_error(ERROR_LOW_MEM);
        return(-1);
        }

    if(shared.sbosdata->dram_size == 3)
        shared.sbosdata->dram_size = 2;

    if(shared.sbosdata->dram_size > 4)
        shared.sbosdata->dram_size = 4;

    return(0);
}

/***************************************************************************

FUNCTION DEFINITION:
setup_pnp - Opens sbs file and gets pnp information

DESCRIPTION:
    If compiled for the INTERWAVE:
        Opens the iw.ini file and gets the csn number and read port address

EXPECTS:
    ini_path to be a full pathname to the iw.ini file on disk

MODIFIES:

RETURNS: 0 if OK
        -1 if can't find pnprdp or csn in [setup 0] in iw.ini file

*/
int setup_pnp(void)
{
    char nullchar = 0;
    char *ptrtonull = &nullchar;
    char local_csn[10];
    char local_rdp[10];
    int ret1,ret2;

	if(!(shared.status & STAT_WINDOWS_RUNNING))
		{
	    ret1 = iwu_get_profile_string(setup_ini_hdr,csn,  ptrtonull,local_csn,10,
    	    ini_path);
	    ret2 = iwu_get_profile_string(setup_ini_hdr,pnprdp,ptrtonull,local_rdp,10,
    	    ini_path);
    
 	   if(ret1 == 0 || ret2 == 0)
    	    {
        	print_error(ERROR_BAD_SETUP);
	        return(-1);
    	    }

	    pnp_csn  = iwu_strtol(local_csn,NULL,16);
	    pnp_read = iwu_strtol(local_rdp,NULL,16);
		}

    return(0);
}

/***************************************************************************

FUNCTION DEFINITION:
hook7x - hooks the communication vector specified in comm_vect

DESCRIPTION:
    Stores the current value of the SBOS communication vector given in shared
    memory and sets the new vector to the address in the driver.

*/
void hook7x(void)
{
    old_int7x = getvect(shared.sbosdata->comm_vect);
    setvect(shared.sbosdata->comm_vect,shared.new_vectors->new_int7x);
}

/***************************************************************************

FUNCTION DEFINITION:
setup_registers - Determines and stores the addresses of the INTERWAVE registers
    using the baseport as a base.

DESCRIPTION:
    Uses the value previously set in baseport to calculate the register
    addresses of some of the other registers in the INTERWAVE.

*/
void setup_registers(void)
{
/** Setup shared address register locations **/

    reg_mixer           = lower_base_port + 0x00;
    reg_sb2x6           = lower_base_port + 0x06; /** WRITE **/
    reg_irq_status      = lower_base_port + 0x06; /** READ  **/
    reg_adlib_status    = lower_base_port + 0x08;
    reg_update_sb2xA    = lower_base_port + 0x08; /** WRITE **/
    reg_388_read        = lower_base_port + 0x0A; /** READ **/
    reg_adlib_data      = lower_base_port + 0x09;
    reg_sb2xC           = lower_base_port + 0x0C;
    reg_2xC_write       = lower_base_port + 0x0D; /** WRITE **/
    reg_sb2xE           = lower_base_port + 0x0E;
    reg_status_read     = lower_base_port + 0x0F; // READ
    reg_2xFcontrol      = lower_base_port + 0x0F; // WRITE
    reg_2xBindexed      = lower_base_port + 0x0B;
    reg_voice_sel       = upper_base_port + 0x02;
    reg_index           = upper_base_port + 0x03;
    reg_data_high       = upper_base_port + 0x05;
    reg_dram_io         = upper_base_port + 0x07;
    reg_data_low        = upper_base_port + 0x04;
    reg_389             = 0x389;
}

/***************************************************************************

FUNCTION DEFINITION:
size_dram - Get the size of DRAM and store the number in shared data

DESCRIPTION:
    Pulls a master reset on the card so we can read dram.  This is done in 
    the case where there has been no reset done prior to this.
    Writes values to specific locations in DRAM to determine how many 256k banks
    of DRAM are present and stores the result.

*/
void size_dram(void)
{
    unsigned char val1, val2, val3, val4;
    unsigned char sav_0;
    long i = 0;
    long address;

    asm    pushf    
    asm cli
    outp( reg_index, 0x4C ); 
    outp( reg_data_high, 0x07 );
    asm popf
    
    sav_0 = read_dram(0L);
    write_dram(0L,0x00);
    while(i < 4) /** 4 possible 256k banks of DRAM **/
        {
        address = i*256L*1024L; /** beginning of 256K block **/
        val1 = read_dram(address);
        val2 = read_dram(address+1);
        write_dram(address,0x55);
        write_dram(address+1,0xAA);
        val3 = read_dram(address);
        val4 = read_dram(0L);
        write_dram(address,val1);
        write_dram(address+1,val2);
        if(val3 != 0x55)
            break;
        if(i != 0)
            if(val4 == 0x55)
                break;
        i++;
        }
    write_dram(0L,sav_0);

    shared.sbosdata->dram_size = i;
}

/***************************************************************************

FUNCTION DEFINITION:
do_fixup - Modify all the incorrect segments in the system so they point to 
    the new copy of the driver

DESCRIPTION:
    Updates all segment references in the driver so they reflect the relocation 
    to upper memory.  Also change any vectors set in the vector table.

*/
void do_fixup(void)
{
    unsigned int new_seg = alloc_seg + 0x10; /** a_r is pointer to PSP **/
    unsigned int far * ptr;
    unsigned char far * ptrnew;
    unsigned char far * ptrold;
    int i;
    SHARED far * shared_far;
    NEW_VECTORS far * new_vectors;
    SBOSDATA far * sbosdata;

    /** Get far pointer to shared and the rest **/
    shared_far = (SHARED far *)MK_FP(new_seg,FP_OFF(&shared));
    new_vectors = (NEW_VECTORS far *)MK_FP(new_seg,
            FP_OFF(shared_far->new_vectors));
    sbosdata =    (SBOSDATA far *)MK_FP(new_seg,
            FP_OFF(shared_far->sbosdata));

    /** Change the addresses in shared.new_vectors **/
    new_vectors->new_int02 = 
                         MK_FP(new_seg,FP_OFF(shared.new_vectors->new_int02));
    new_vectors->new_int08 = 
                         MK_FP(new_seg,FP_OFF(shared.new_vectors->new_int08));
    new_vectors->new_int15 =
                         MK_FP(new_seg,FP_OFF(shared.new_vectors->new_int15));
    new_vectors->new_int21 = 
                         MK_FP(new_seg,FP_OFF(shared.new_vectors->new_int21));
    new_vectors->new_win21 = 
                         MK_FP(new_seg,FP_OFF(shared.new_vectors->new_win21));
    new_vectors->new_int2f = 
                         MK_FP(new_seg,FP_OFF(shared.new_vectors->new_int2f));
    new_vectors->new_int7x = 
                         MK_FP(new_seg,FP_OFF(shared.new_vectors->new_int7x));
    new_vectors->new_intsynth = 
                        MK_FP(new_seg,FP_OFF(shared.new_vectors->new_intsynth));

    /** Change the communication vector to point to the right spot **/
    setvect(sbosdata->comm_vect, (new_vectors->new_int7x));

    /** Update miscellaneous driver data not in shared data **/
    /** This one is a pointer to the command parameter block for the
        spawning loader.  Defined in ints.asm **/
    ptr = (unsigned int far *)MK_FP(new_seg, &seg_loader_parm);
    *ptr = new_seg;

    /** Write the driver name into the MCB (memory control block) so DOS
     ** programs (like MEM and MFT and MSD) can print it out...
     ** The MCB is 16 bytes before the allocated memory block
     ** The book says that byte offset 7-15 are unused but apparently
     ** programs have been using this area to store an identification string
     ** This works on DOS 5.0+. I don't know about before that...
     **/
    ptrnew = (unsigned char far *)MK_FP(alloc_seg - 0x01, 0x08);
    ptrold = (unsigned char far *)MK_FP(psp_seg_addr - 0x01, 0x08);
    for(i = 0; i< 8; i++)
        *(ptrnew++) = *(ptrold++);
}

/****************************************************************************

FUNCTION DEFINITION:
print_version - prints a message displaying the driver name, emulation state,
  and version number

DESCRIPTION:

EXPECTS:
  shared - pointer to the start of shared data

*/
void print_version(void)
{
	int ret;

	if(quiet) return;

	ret = print_message(MESSAGE_VERSION);
	if(ret == 0)
		{
		print_string(&verstring2);
		print_crlf();
		}
	else
		{
		print_string(&default_banner);
		print_crlf();
		}
}
