summaryrefslogtreecommitdiffstats
path: root/private/mvdm/softpc.new/base/system/rom.c
blob: 2a7190bceb2450610d5e258c73bc9db409a8fc4e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
#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);
}