[comp.sources.amiga] v89i033: simcpm - cpm simulator v2.3, Part02/03

page@swan.ulowell.edu (Bob Page) (03/08/89)

Submitted-by: jlydiatt@jlami.wimsey.bc.ca (Jeff Lydiatt)
Posting-number: Volume 89, Issue 33
Archive-name: applications/simcpm23.2

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	simcpm1.asm1
# This archive created: Tue Mar  7 22:34:42 1989
cat << \SHAR_EOF > simcpm1.asm1
vermaj	 equ	$02	;Major version number
vermin	 equ	$30	;Minor version number
revyear  equ	$89	;Year last assembled
revmonth equ	$01	;Month last assembled
revday	 equ	$09	;Day last assembled
*************************************************************************
*									*
*									*
*	Z-80 Simulator for MC68000					*
*									*
*	With CP/M 2.2 call support, and optional tracing		*
*									*
*									*
* Amiga version 2.3 (December 1988, Charlie Gibbs):			*
*									*
*	Incorporated modifications by Willi Kusche			*
*	(his version 2.1, June 29, 1988):				*
*		Register definitions split into "ecpmdefs.i"		*
*		Improved macro usage					*
*		Correction of errors in Z-80 flag setting routines	*
*		Incorporated modified trace dump routines		*
*		Allow changing of trace addresses during execution	*
*		Added the following BDOS calls:				*
*			27 - get allocation vector			*
*				(using a fake allocation vector)	*
*			31 - get disk parameter block			*
*				(using a fake disk parameter block)	*
*									*
*	Modifications inspired by Ulf Nordquist's CP/M emulator:	*
*	      - BDOS calls 17 and 18 (search for first file and		*
*		search for next file) now put file size information	*
*		into the FCB.						*
*	      - BDOS call 27 (get allocation vector) now builds a fake	*
*		allocation vector based on actual volume information.	*
*									*
*	The file handle table now also contains the first 12 bytes	*
*	of the FCB to which the handle corresponds.  The FCB is		*
*	no longer modified with a sequence number.			*
*									*
*	BDOS call 20 (sequential read) now returns 01h in the		*
*	accumulator when end of file is reached, rather than 0FFh	*
*	as is suggested by CP/M manuals.  Some utilities (such as	*
*	ASM.COM) assume 0FFh indicates an I/O error.			*
*									*
*	A new INCLUDE file, options.i, has been added.  This file	*
*	contains EQUate statements which allow you to set options	*
*	that cannot be easily set at execution time.  Current options	*
*	allow inclusion of Heath/Zenith 19 escape code translation,	*
*	and support of 68010 and higher processors by replacing all	*
*	MOVE SR instructions by MOVE CCR.				*
*									*
*									*
* Amiga version 2.2 (June 1988, Charlie Gibbs): 			*
*									*
*	BDOS call 23 (rename) was not working properly. 		*
*									*
*	BIOS and BDOS call messages (for tracing or errors)		*
*	now indicate that the call number is in hexadecimal.		*
*									*
*	The following BDOS calls have now been implemented:		*
*		35 - get file end address				*
*		36 - get direct address 				*
*									*
*									*
* Amiga version 2.1 (May 1988, Charlie Gibbs):				*
*									*
*	If a reference is made to any drive other than A:, SimCPM	*
*	will insert the string CPMx: ahead of any file name it uses.	*
*	For instance, if a program tries to open MYFILE.DAT on drive	*
*	B:, SimCPM will try to open CPMB:MYFILE.DAT.  You can ASSIGN	*
*	these simulated drives anywhere you want.  Drive A: will	*
*	always go to the current directory, unless the user number	*
*	is not zero.  If you specify any user number other than zero,	*
*	it will be included, e.g. if the above example were opened	*
*	under user 1, SimCPM will look for CPMB01:MYFILE.DAT.		*
*	If the file were on drive A under user 1, SimCPM will look	*
*	for CPMA01:MYFILE.DAT.						*
*									*
*	If the file handle table overflowed, the file which caused	*
*	the overflow has already been opened.  It was not being 	*
*	closed again.  This file is now closed before the emulated	*
*	program is aborted.						*
*									*
*	If an emulated program failed to close all the files it opened, *
*	the emulator would close these files.  However, it did not	*
*	clear the open flag for any such files.  If another program	*
*	was then run, when it terminated the emulator would try to	*
*	close these same files again.  This bug has been corrected.	*
*									*
*	Z-80 instructions not supported by version 2.0 have been	*
*	implemented as follows: 					*
*		LD I,A moves to a dummy interrupt register.		*
*		LD A,I moves from a dummy interrupt register.		*
*		LD R,A is ignored.					*
*		LD A,R moves a random value from the clock into 	*
*			the low-order 7 bits of the accumulator.	*
*		IM1 and IM2 are ignored.				*
*									*
*	The -z command-line switch has been implemented.		*
*	It sets instruction tracing, just like the -t switch,		*
*	but causes Z-80 instruction mnemonics to be used.		*
*	If this switch is not set, 8080 mnemonics are used.		*
*									*
*	BDOS call 32 (get or set user code) is now fully implemented.	*
*									*
*	The following BDOS calls have now been implemented:		*
*		13 - reset all drives					*
*		24 - get active drive map				*
*		28 - protect drive					*
*		29 - get read-only drive map				*
*									*
*	The USER and SAVE built-in commands have been implemented.	*
*	These are the only build-in commands provided, since commands	*
*	such as DIR or TYPE can be handled through a CLI window.	*
*									*
*									*
* Amiga version 2.0 (January 1988, Charlie Gibbs):			*
*									*
*	Added all Z-80 instructions except the following:		*
*		LD A,I							*
*		LD I,A							*
*		LD A,R							*
*		LD R,A							*
*		IM1							*
*		IM2							*
*									*
*	Added -t flag for tracing without needing			*
*		a separate version to maintain speed.			*
*									*
*	Added the following BDOS calls: 				*
*		17 - search for first file				*
*		18 - search for next file				*
*		24 - get active drive map				*
*			(assumes only drive A: is active)		*
*		28 - protect drive (ignored)				*
*		29 - get read-only map					*
*			(assumes no drives are protected)		*
*		32 - get or set user code				*
*			(always gets user code zero, any attempt	*
*			to set to a user code other than zero		*
*			results in a fatal error)			*
*									*
*	Added serial port support (simulated 8251 on ports 14 and 15)	*
*									*
*									*
*	Converted to AmigaDOS September 1987 by Charlie Gibbs		*
*	(after painstakingly typing it all in from Dr. Dobbs		*
*	Journal, January through March 1986).  Improvements		*
*	described by Jim Cathey in his letter in the June 1986		*
*	DDJ have been included.  Repetitive code is generated		*
*	by macros whenever it would save my fingers.			*
*									*
*									*
*	Version 1.2 1/21/85 JEC 					*
*		Fixed Extent bug in OPEN logic. 			*
*		Sped up code, sample MAC from 2:13 to 1:40		*
*		Now runs at a 1.4 MHz equivalent based on MAC sample.	*
*									*
*	Version 1.1 8/29/84 JEC 					*
*		Fixed BIOS call #6 bug. 				*
*									*
*	Version 1.0 05/25/84 by Jim Cathey				*
*									*
*	This program has been written for speed whenever possible,	*
*	as such tends to be large because of the separate subroutine	*
*	for each and every opcode of the target processor.		*
*									*
*	On an 8MHz 68000 (Compupro) system the simulation speed is	*
*	a little better than a 1MHz Z-80 when running MAC.  The time	*
*	for a sample assembly was 2:13 for the simulation vs. 0:35	*
*	on a 4MHz Z-80, both systems used identical hard disk systems.	*
*									*
*	It is not a complete simulation, as some flag handling		*
*	isn't quite right, but it is enough to run the program		*
*	I wrote for it (DDT, LU, MAC, and Morrow's FORMATW).		*
*									*
*************************************************************************
	code
	page
*************************************************************************
*									*
*	This file contains the startup routines, the simulator core,	*
*	tracing code, and the CP/M 2.2 simulation.			*
*									*
*************************************************************************

	xref	optabl,flags,mloop,mloopt,tracesad,traceead,traceflg,start2
	xdef	illegl,service,dump,inp,outp,movear

	xref	_AbsExecBase
	xref	_CreatePort
	xref	_DeletePort
	xdef	_SysBase
	xdef	_DOSBase

	include	"options.i"
	include	"ecpmdefs.i"

*
* ASCII character values
*
bel	equ	$07		;Bell (or beep or whatever)
bs	equ	$08		;Backspace
ht	equ	$09		;Horizontal tab
lf	equ	$0A		;Line feed
ff	equ	$0C		;Form feed
cr	equ	$0D		;Carriage return
so	equ	$0E		;Shift out
si	equ	$0F		;Shift in
esc	equ	$1B		;Escape
	page
*--------------------------------
*
* Some commonly used macros
*
*--------------------------------

sys	macro			;Call a system routine.
	ifnd	_LVO\1
	xref	_LVO\1
	endc
	jsr	_LVO\1(a6)
	endm

*----------------------------
* Target system Mnemonics
*----------------------------
tHLT	EQU    $76
tJMP	EQU    $C3
tRET	EQU    $C9

*----------------------
* Equates
*----------------------

MODE_OLDFILE equ 1005
MODE_NEWFILE equ 1006
ACCESS_READ  equ -2
ACCESS_WRITE equ -1
STARTBAUD equ	1200		;Starting baud rate
NUMBITS equ	8		;Number of data bits
CTLCHAR equ	$11130501	;XON, XOFF, ENQ, ACK
SERFLAGS equ	$A0		;No XON/XOFF, shared
PARITYON equ	1		;Parity enabled
PARITYODD equ	2		;Odd parity
* I/O command codes
CMD_INVALID equ 0
CMD_RESET   equ 1
CMD_READ    equ 2
CMD_WRITE   equ 3
CMD_UPDATE  equ 4
CMD_CLEAR   equ 5
CMD_STOP    equ 6
CMD_START   equ 7
CMD_FLUSH   equ 8
CMD_NONSTD  equ 9
SDCMD_QUERY equ CMD_NONSTD
SDCMD_BREAK equ CMD_NONSTD+1
SDCMD_SETPARAMS equ CMD_NONSTD+2 ;Set serial port parameters
	page
*************************************************************************
*									*
*	Initialization							*
*									*
*************************************************************************

start:	move.l	sp,savesp	;Save the stack pointer.
	move.l	_AbsExecBase,a6
	move.l	a6,_SysBase	;Working copy of _AbsExecBase
	move.b	#1,testdol	;"pstring" should test for leading $.
	clr.w	esclen		;No partial escape sequence is saved.
	clr.b	insflag 	;We're not in insert mode.
	clr.b	traceit 	;Clear trace request flag.
	clr.b	z80flag 	;Clear Z-80 flag.
	clr.b	btrcflg 	;Turn off BIOS/BDOS call tracing.
	clr.b	quitflg 	;Clear the quit flag.
	clr.b	bufflag 	;Disable output buffering.
	clr.b	listopn 	;The list device is closed.
	clr.b	frstset 	;First call to "setbaud"
	clr.b	target+4	;Set default drive and user to A0:.
	move.w	#1,acmap	;Set active drive map to A: only.
	clr.w	romap		;No drives are set read-only.
	move.l	#STARTBAUD,baud ;Initial serial port parameters
	move.b	#NUMBITS,bits
	move.l	#strbuf,strptr	;Initialize output buffer pointer.

	lea	handles,a1
	move.w	#(handlen-handles)/4-1,d1
1$	clr.l	(a1)+		;Clear file handles.
	dbra	d1,1$
	clr.l	rawhand 	;Clear RAW: handle too.

*
* Copy the command line to "cmdline", stripping out leading switches if any.
*
	lea	cmdline,a1
	subq	#1,d0
* Skip over leading blanks, if any.
leadblk cmpi.b	#' ',(a0)+	;Leading blank?
	bne.s	setbuf		;No.
	dbra	d0,leadblk	;Skip over leading blank.
*  If the -b switch is given, activate output buffering.
setbuf	subq.l	#1,a0		;Back onto the first non-blank.
	cmpi.b	#'-',(a0)	;Possible buffer switch?
	bne.s	savecmd 	;No - start saving the command line.
	cmpi.b	#'B',1(a0)	;Activate output buffering?
	beq.s	1$		;Yes.
	cmpi.b	#'b',1(a0)
	bne.s	settrc		;No.
1$	move.b	#1,bufflag	;Set buffered-output flag.
	bra.s	skipsw
* If the -t switch is given, set "traceit".
settrc	cmpi.b	#'T',1(a0)
	beq.s	1$
	cmpi.b	#'t',1(a0)
	bne.s	setz80
1$	move.b	#1,traceit	;Set trace flag.
* If the -z switch is given, set both "z80flag" and "traceit".
setz80	cmpi.b	#'Z',1(a0)
	beq.s	1$
	cmpi.b	#'z',1(a0)
	bne.s	skipsw
1$	move.b	#1,z80flag	;Set Z-80 flag.
	move.b	#1,traceit	;Set trace flag also.
* Skip over the switch and check for more.
skipsw	cmpi.b	#' ',(a0)+	;End of switch?
	beq.s	1$		;Yes.
	dbra	d0,skipsw
	addq.l	#1,a1		;Adjust A1.
	bra.s	gotcmd		;There is no command line left.
1$	subq.l	#1,a0		;Back onto the first blank.
	bra.s	leadblk 	;Look for the next non-blank.
* Save the command line (except for leading switches).
savecmd move.b	(a0)+,(a1)+	;Save the command line, if any.
	dbra	d0,savecmd
gotcmd	move.b	#0,-1(a1)	;Replace the newline with a null.
	move.b	cmdline,cmdflag ;Save command-line flag.

*
* Open libraries and set up a RAW: window.
*
	move.b	#1,quitflg	;Quit immediately if failure below.
	move.l	_SysBase,a6	;Find library
	lea	dosname,a1	;'dos.library'
	moveq	#0,d0		;Any version
	sys	OpenLibrary	;Open dos.library.
	move.l	d0,a6		;Point to doslib for next operation.
	move.l	d0,_DOSBase	;Save it for future reference.
	sys	Input		;Get file handle for keyboard.
	move.l	d0,stdin	;Save it here.
	beq	quitprg 	;Couldn't get keyboard handle.
	sys	Output		;Get file handle for screen.
	move.l	d0,stdout	;Save it here.
	beq	quitprg 	;Couldn't get screen handle.
	move.l	#loadmsg,d1
	bsr	pstring 	;Display where we loaded.
	move.l	#start,d1
	bsr	plong
	bsr	pspace
	move.b	#'/',d1
	bsr	pchar
	bsr	pspace
	move.l	#start2,d1
	bsr	plong
	bsr	pcrlf
	move.l	#rawspec,d1
	move.l	#MODE_NEWFILE,d2
	sys	Open		;Open a RAW: window.
	move.l	d0,rawhand	;Save the file handle here.
	bne	opened		;We succeeded.
	move.l	#rawerr,d1
	bsr	pstring 	;Display error message...
	sys	IoErr
	move.l	d0,d1
	bsr	plong		; and error code.
	bsr	pcrlf
	bra	quitprg

dosname dc.b	'dos.library',0
loadmsg dc.b	'SimCPM version '
	dc.b	vermaj&15+'0','.',vermin/16+'0',vermin&15+'0'
	dc.b	' has loaded at $'
rawspec dc.b	'RAW:0/0/640/200/CP/M Emulator'
	dc.b	' by Jim Cathey and Charlie Gibbs - version '
	dc.b	vermaj&15+'0','.'
	dc.b	vermin/16+'0',vermin&15+'0',' ('
	dc.b	revyear/16+'0',revyear&15+'0','/'
	dc.b	revmonth/16+'0',revmonth&15+'0','/'
	dc.b	revday/16+'0',revday&15+'0',')',0
rawerr	dc.b	'Unable to open RAW: - code $'
setwin	dc.b	$9B,'0x',$9B,'8y',$9B,'24t',$9B,'80u',$9B,'H',$9B,'J$'

opened	move.b	cmdflag,quitflg ;If we have a command, execute it and quit.
	move.l	#setwin,d1
	bsr	pstring 	;Set the window to 24 by 80.

*
* Come back here to load another program.
*
nextprg lea	target,targbase ;Start of target memory
	clr.b	insflag 	;Reset insert mode.
	move.l	#simsg,d1
	bsr	pstring 	;In case last program sent SHIFT OUT
	clr.b	dumpcnt 	;Reset dump pause counter.
	clr.l	rpport		;serial.device message port pointers
	clr.l	wpport
	bsr	entrads 	;Enter trace delimiting addresses.
	bsr	lodfdos 	;Load up the fake FDOS in target memory.
	bsr	lodregs 	;Load the remaining simulation registers.
	bsr	loadcom 	;Load the .COM program.
	jmp	(return)	;Execute the simulation.

simsg	dc.b	si,'$'		;Resets MSB of each output character
	page
*************************************************************************
*									*
*	Illegal instructions and dumping				*
*									*
*************************************************************************

illegl	move.l	#illgmsg,d1	;Illegal opcode, say what & where,
	bsr	pstring
	lea	-1(pseudopc),a0
	move.b	(a0),d1
	bsr	pbyte
	cmpi.b	#$CB,(a0)	;Display sub-op-code for prefixes CB...
	beq.s	1$
	cmpi.b	#$DD,(a0)	;DD...
	beq.s	1$
	cmpi.b	#$ED,(a0)	;ED...
	beq.s	1$
	cmpi.b	#$FD,(a0)	;and FD.
	bne.s	2$
1$	move.b	1(a0),d1
	bsr	pbyte
2$	move.l	#ilgmsg2,d1
	bsr	pstring
	suba.l	targbase,a0
	move.l	a0,d1
	bsr	pword
	move.l	#ilgmsg3,d1
	bsr	pstring
	move.l	#dumpmsg,d1
	bsr	pstring
	clr.b	dumpcnt
	bsr	dump		; and spill guts.
	bra	quitprg 	;Quit simulation.

illgmsg dc.b	cr,lf,'Illegal instruction $'
ilgmsg2 dc.b	' at $'
ilgmsg3 dc.b	'.$'
dumpmsg dc.b	cr,lf,'Register contents:$'

*
* Dump all registers and decode the current instruction.
*
dump	movem.l	d0-d3/a1,-(sp)
	bsr	pcrlf
	move.b	regf,d0
	bsr	dspflag
	move.b	#'A',(a0)+
	move.b	#'=',(a0)+
	move.b	rega,d1		;Accumulator
	bsr	ubyte
	move.b	#' ',(a0)+
	move.b	#'B',(a0)+
	move.b	#'=',(a0)+
	move.w	regb(regs),d1	;BC
	bsr	uword
	move.b	#' ',(a0)+
	move.b	#'D',(a0)+
	move.b	#'=',(a0)+
	move.w	regd(regs),d1	;DE
	bsr	uword
	move.b	#' ',(a0)+
	move.b	#'H',(a0)+
	move.b	#'=',(a0)+
	move.w	regh(regs),d1	;HL
	bsr	uword
	move.b	#' ',(a0)+
	move.b	#'S',(a0)+
	move.b	#'=',(a0)+
	move.l	pseudosp,d1	;SP
	sub.l	targbase,d1
	bsr	uword
	move.b	#' ',(a0)+
	move.b	#'P',(a0)+
	move.b	#'=',(a0)+
	move.l	pseudopc,d1	;PC
	sub.l	targbase,d1
	bsr	uword
	move.b	#' ',(a0)+
	move.l	a1,-(sp)
	move.l	pseudosp,a1
	moveq	#3,d2
	moveq	#'0',d3
tosloop	move.b	#'S',(a0)+	;Display the top 4 stack entries.
	move.b	d3,(a0)+
	addq.b	#1,d3
	move.b	#'=',(a0)+
	move.b	1(a1),d1 
	ror.w	#8,d1
	move.b	0(a1),d1
	bsr	uword
	move.b	#' ',(a0)+
	addq.l	#2,a1
	dbra	d2,tosloop
	move.b	#'$',(a0)+
	move.l	(sp)+,a1
	move.l	#workbuf,d1
	bsr	pstring 	;Displaying as a single string is much faster.
	bsr	pcrlf
	move.b	regf2(regs),d0	;Alternate flags
	bsr	dspflag
	move.b	#'A',(a0)+
	move.b	#'''',(a0)+
	move.b	rega2(regs),d1	;Alternate accumulator
	bsr	ubyte
	move.b	#' ',(a0)+
	move.b	#'B',(a0)+
	move.b	#'''',(a0)+
	move.w	regb2(regs),d1	;Alternate BC
	bsr	uword
	move.b	#' ',(a0)+
	move.b	#'D',(a0)+
	move.b	#'''',(a0)+
	move.w	regd2(regs),d1	;Alternate DE
	bsr	uword
	move.b	#' ',(a0)+
	move.b	#'H',(a0)+
	move.b	#'''',(a0)+
	move.w	regh2(regs),d1	;Alternate HL
	bsr	uword
	move.b	#' ',(a0)+
	move.b	#'X',(a0)+
	move.b	#'=',(a0)+
	move.w	regix(regs),d1	;IX
	bsr	uword
	move.b	#' ',(a0)+
	move.b	#'Y',(a0)+
	move.b	#'=',(a0)+
	move.w	regiy(regs),d1	;IY
	bsr	uword
	move.b	#' ',(a0)+
	move.b	#' ',(a0)+
	move.b	(pseudopc),d1	;Current opcode byte
	cmpi.b	#$CB,d1 	;Prefix?
	beq.s	oppfx		;Yes.
	cmpi.b	#$DD,d1
	beq.s	oppfx
	cmpi.b	#$ED,d1
	beq.s	oppfx
	cmpi.b	#$FD,d1
	beq.s	oppfx
	move.b	#' ',(a0)+
	bsr	ubyte		;Unprefixed opcode
	move.b	#' ',(a0)+
	bra.s	opx
oppfx	bsr	ubyte		;Prefix
	move.b	1(pseudopc),d1
	bsr	ubyte		;Prefixed opcode
opx	move.b	#' ',(a0)+
*
* Decode the instruction.
*
	moveq	#0,d0
	move.b	(pseudopc),d0	;Opcode
	clr.b	prefix		;Assume it's not a prefix.
	cmpi.b	#$DD,d0 	;Is it?
	beq.s	dopfx		;Yes.
	cmpi.b	#$FD,d0
	bne.s	saveop		;No.
dopfx	move.b	d0,prefix	;It's a prefix.
	move.b	1(pseudopc),d0	;The opcode is really here.
saveop	move.b	d0,opcode	;Save the opcode.
* Look up the opcode.
	cmpi.b	#$40,d0 	;Is opcode in range 00-3F?
	bcc.s	lookupC 	;No.
* The opcode is in the range 00-3F.
	lea	mnop008,a1	;Assume we're using 8080 mnemonics.
	tst.b	z80flag 	;Should we use Z-80 mnemonics?
	beq.s	lokup0c 	;No
	lea	mnop00z,a1
lokup0c mulu	#12,d0		;Offset into opcode table
	add.l	d0,a1		;A1 points to decoded instruction
	bra	decode		;Decode remainder of instruction.
* The opcode is in the range C0-FF.
lookupC cmpi.b	#$C0,d0 	;Is opcode in range C0-FF?
	bcs.s	lookup8 	;No.
	cmpi.b	#$ED,d0 	;ED-prefix instructions?
	beq	lookupE 	;Yes.
	cmpi.b	#$CB,d0 	;CB-prefix instructions?
	beq	lookupB 	;Yes.
	lea	mnopC08,a1	;Assume we're using 8080 mnemonics.
	tst.b	z80flag 	;Should we use Z-80 mnemonics?
	beq.s	lokupCc 	;No.
	lea	mnopC0z,a1
lokupCc subi.w	#$C0,d0
	mulu	#12,d0		;Offset into opcode table
	add.l	d0,a1		;A1 points to decoded instruction
	bra	decode		;Decode remainder of instruction.
* The opcode is in the range 80-BF.
lookup8 cmpi.b	#$80,d0 	;Is opcode in range 80-BF?
	bcs.s	lookup4 	;No - it's in range 40-7F.
	lea	mnop808,a1	;Assume we're using 8080 mnemonics.
	tst.b	z80flag 	;Are we?
	beq.s	lokup8c 	;Yes.
	lea	mnop80z,a1	;Use the Z-80 mnemonic table.
lokup8c lsr.b	#3,d0		;Divide opcode by 8.
	andi.w	#7,d0		;Instruction type
	mulu	#9,d0		;Offset into opcode table
	add.l	d0,a1		;A1 points to decoded instruction
	bra	decode		;Decode remainder of instruction.
* The opcode is in the range 40-7F.
lookup4 cmpi.b	#$76,d0 	;Is this a HLT (Z-80 HALT) instruction?
	bne.s	lokup4m 	;No.
	lea	mnop768,a1
	tst.b	z80flag 	;Do the Z-80 mnemonic?
	beq	decode		;No.
	lea	mnop76z,a1
	bra	decode
lokup4m lea	mnop408,a1	;Assume 8080 MOV instruction.
	tst.b	z80flag 	;Are we doing Z-80 mnemonics?
	beq	decode		;No.
	lea	mnop40z,a1	;Make it a Z-80 LD instruction.
	bra	decode
* CB-prefix instruction decoding
lookupB move.b	1(pseudopc),d0	;Sub-opcode
	tst.b	prefix		;IX or IY with displacement?
	beq.s	lokupB0 	;No.
	move.b	2(pseudopc),d0	;Skip over the displacement.
lokupB0 move.b	d0,opcode
	cmpi.b	#$40,d0 	;Is opcode in range CB00-CB3F?
	bcc.s	lokupB4 	;No.
	lea	mnopCB08,a1
	lsr.b	#3,d0
	mulu	#8,d0
	add.l	d0,a1
	bra	decode
lokupB4 move.b	d0,d1		;Save the sub-opcode.
	andi.w	#$C0,d0 	;Opcode is in range CB40-CBFF.
	lsr.b	#3,d0		;Displacement into 8-byte entries
	lea	mnopCB48,a1
	add.l	d0,a1
lokupBm move.b	(a1)+,(a0)+	;Move mnemonic to decoded area.
	cmpi.b	#'$',(a1)
	bne.s	lokupBm
	move.b	d1,d0		;Get the sub-opcode again.
	andi.w	#$38,d0 	;Isolate the bit number.
	lsr.b	#3,d0
	addi.b	#'0',d0 	;Convert the bit number to ASCII.
	move.b	d0,(a0)+	;Move bit number to decoded instruction.
	move.b	#',',(a0)+
	move.b	d1,d0
	andi.b	#7,d0		;Isolate the register specification.
	bsr	dspreg		;Set up the register number.
	bra	dispop
* ED-prefix instruction decoding
lookupE move.b	1(pseudopc),d0	;Sub-opcode
	move.b	d0,opcode
	cmpi.b	#$40,d0 	;Is it below ED40?
	bcs	lookupI 	;Yes - it's illegal.
	cmpi.b	#$80,d0 	;Is it in range ED40-ED7F?
	bcc	lokupE8 	;No.
	lea	mnopE48,a1	;Assume we're using 8080 mnemonics.
	tst.b	z80flag 	;Should we use Z-80 mnemonics?
	beq.s	lokupEc 	;No
	lea	mnopE4z,a1
lokupEc andi.w	#$3F,d0
	mulu	#12,d0		;Offset into opcode table
	add.l	d0,a1		;A1 points to decoded instruction
	bra	decode		;Decode remainder of instruction.
lokupE8 cmpi.b	#$A0,d0 	;Is opcode in range ED80-ED9F?
	bcs	lookupI 	;Yes - it's illegal.
	cmpi.b	#$BC,d0 	;It must be in range EDA0-EDBB.
	bcc	lookupI
	lea	mnopEA8,a1
	andi.w	#$1F,d0
	mulu	#5,d0
	add.l	d0,a1
	cmpi.b	#' ',(a1)	;Is opcode blank?
	bne.s	decode		;No - decode the instruction.
* The instruction is illegal.
lookupI lea	mnopilg,a1	;Point to "ILLEGAL" message.
*
* Decode the instruction according to the string pointed to by A1.
*  The decoded instruction is built where A0 points.
*  See the mnemonic tables for an explanation of operand codes.
*
decode:
* Code "r" - register in bits 0-2 of opcode
	cmpi.b	#'r',(a1)
	bne.s	decodeq
	move.b	opcode,d0
	bsr	dspreg
	bra	decodex
* Code "q" - register in bits 3-5 of opcode
decodeq cmpi.b	#'q',(a1)
	bne.s	decodep
	move.b	opcode,d0
	lsr.b	#3,d0
	bsr	dspreg
	bra	decodex
* Code "p" - register pair in bits 4-5 of opcode
decodep cmpi.b	#'p',(a1)
	bne	decodeh
	move.b	opcode,d0
	andi.b	#$30,d0 	;Isolate the register bits.
	bne.s	decodpd 	;They aren't 00.
	move.b	#'B',(a0)+	;00 - register B
	tst.b	z80flag 	;Are we using Z-80 mnemonics?
	beq	decodex 	;No.
	move.b	#'C',(a0)+	;Call it BC for Z-80.
	bra	decodex
decodpd cmpi.b	#$20,d0
	beq.s	decodph
	bhi.s	decodps
	move.b	#'D',(a0)+	;01 - register D (DE for Z-80)
	tst.b	z80flag
	beq	decodex
	move.b	#'E',(a0)+
	bra	decodex
decodph cmpi.b	#$DD,prefix	;10 - check for index prefix.
	beq.s	decodpx 	;IX
	bhi.s	decodpy 	;IY
	move.b	#'H',(a0)+	;Register H (HL for Z-80)
	tst.b	z80flag
	beq	decodex
	move.b	#'L',(a0)+
	bra	decodex
decodpx move.b	#'I',(a0)+	;IX
	move.b	#'X',(a0)+
	bra	decodex
decodpy move.b	#'I',(a0)+	;IY
	move.b	#'Y',(a0)+
	bra	decodex
decodps cmpi.b	#$F0,opcode	;11 - depends on opcode
	bcc.s	decodpp
	move.b	#'S',(a0)+	;11 is SP if opcode is less than F0.
	move.b	#'P',(a0)+
	bra	decodex
decodpp tst.b	z80flag 	;Otherwise, it's PSW (AF for Z-80).
	bne.s	decodpz
	move.b	#'P',(a0)+
	move.b	#'S',(a0)+
	move.b	#'W',(a0)+
	bra.s	decodex
decodpz move.b	#'A',(a0)+
	move.b	#'F',(a0)+
	bra.s	decodex
* Code "h" - HL, IX, or IY depending on prefix
decodeh cmpi.b	#'h',(a1)
	bne.s	decoden
	cmpi.b	#$DD,prefix
	beq.s	decodpx 	;IX
	bhi.s	decodpy 	;IY
	move.b	#'H',(a0)+	;HL
	move.b	#'L',(a0)+
	bra	decodex
* Code "n" - 8-bit value following opcode
decoden cmpi.b	#'n',(a1)
	bne.s	decodea
	move.b	1(pseudopc),d1
	tst.b	prefix		;DD or FD prefix?
	beq.s	1$		;No - we have the value.
	cmpi.b	#$36,opcode	;Is opcode below 36?
	bcs.s	1$		;Yes - it's a move to index register half
	move.b	3(pseudopc),d1	;The value is actually here.
1$	bsr	ubyte		;Convert the value to a hex string.
	bra.s	decodex
* Code "a" - 16-bit value following opcode
decodea cmpi.b	#'a',(a1)
	bne.s	decodem
	move.b	2(pseudopc),d1
	lsl.w	#8,d1
	move.b	1(pseudopc),d1
	cmpi.b	#$ED,(pseudopc) ;Is this an ED-prefix instruction?
	beq.s	1$		;Yes - the value is shifted one byte.
	tst.b	prefix		;DD or FD prefix?
	beq.s	2$		;No - we have the value.
1$	move.b	3(pseudopc),d1	;The value is actually here.
	lsl.w	#8,d1
	move.b	2(pseudopc),d1
2$	bsr	uword		;Convert the value to a hex string.
	bra.s	decodex
* Not a special code - just move the character as is.
decodem move.b	(a1),(a0)+
* Try for another character to move (and possibly decode).
decodex addq.l	#1,a1
	cmpi.b	#'$',(a1)
	bne	decode
*
* Display the decoded instruction.
*
dispop	move.b	#cr,(a0)+
	move.b	#lf,(a0)+
	move.b	#'$',(a0)+
	move.l	#workbuf,d1
	bsr	pstring 	;Displaying as a single string is much faster.
* Pause after every screenful to give the operator time to read it.
	addq.b	#1,dumpcnt	;Count the number of times dumped.
	cmpi.b	#8,dumpcnt	;Is the screen full of dumps?
	bcs	dumpx		;No - exit.
	move.l	#dmpmsg3,d1
	bsr	pstring 	;Ask for operator action.
	bsr	dmpstr		;Make sure the prompt gets out!
	movem.l a0-a1/a6,-(sp)
	move.l	rawhand,d1	;Console input
	move.l	#dumpcnt,d2
	move.l	_DOSBase,a6
	moveq	#1,d3
	sys	Read		;Get the operator's reply.
	movem.l (sp)+,a0-a1/a6
	bsr	pcrlf
	cmpi.b	#'Q',dumpcnt	;Does he want to quit?
	beq	quitprg 	;Yes.
	cmpi.b	#'q',dumpcnt
	beq	quitprg
	cmpi.b	#'S',dumpcnt	;Stop tracing?
	beq.s	2$		;Yes.
	cmpi.b	#'s',dumpcnt
	beq.s	2$
	cmpi.b	#'C',dumpcnt	;Change trace address?
	beq.s	1$		;Yes.
	cmpi.b	#'c',dumpcnt
	bne.s	dmpcont
1$	bsr	gtrange		;Get new trace range.
2$	clr.b	traceflg	;Disable tracing and continue.
dmpcont	clr.b	dumpcnt 	;Reset the dump counter.
dumpx	movem.l (sp)+,d0-d3/a1
	rts

dmpmsg3 dc.b	'Q to quit, S to stop tracing, C to change trace,'
	dc.b	' any other key to continue: $'

*
* Display the register number indicated by the low-order 3 bits of D0.
*  H and L will be displayed as XH and XL if necessary.
*  M ((HL) for Z-80) will be converted to (IX+d) or (IY+d) if necessary.  
*
dspreg	andi.b	#7,d0		;Isolate register number.
	cmpi.b	#6,d0		;Which register is it?
	beq.s	dspregm 	;M - might need a special routine.
	bhi.s	dsprega 	;A - this one is easy.
	addi.b	#'B',d0 	;Registers B, C, D, E, H, and L
	cmpi.b	#'F',d0 	;Is it H or L?
	bcs.s	dspregb 	;No - we're ready.
	bne.s	dspregl 	;It's L.
	moveq	#'H',d0 	;It's H.
	bra.s	dspregp
dspregl moveq	#'L',d0 	;It's L.
dspregp cmpi.b	#$DD,prefix	;Are we manipulating an index register?
	bcs.s	dspregb 	;No.
	bne.s	dspregy 	;Register YH or YL
	move.b	#'X',(a0)+	;Register XH or XL
	bra.s	dspregb
dspregy move.b	#'Y',(a0)+
dspregb move.b	d0,(a0)+
	rts
dsprega move.b	#'A',(a0)+	;It's the accumulator.
	rts
dspregm cmpi.b	#$DD,prefix	;Index register?
	bcc.s	dspregi 	;Yes.
	tst.b	z80flag 	;Are we using 8080 mnemonics?
	bne.s	dspregz 	;No.
	move.b	#'M',(a0)+	;It's M for 8080.
	rts
dspregz move.b	#'(',(a0)+	;It's (HL) for Z-80.
	move.b	#'H',(a0)+
	move.b	#'L',(a0)+
	move.b	#')',(a0)+
	rts
dspregi move.b	#'(',(a0)+	;It's an indexed operand.
	move.b	#'I',(a0)+
	move.b	#'X',(a0)+	;Assume it's IX.
	cmpi.b	#$DD,prefix	;Is it?
	beq.s	dspregd 	;Yes.
	move.b	#'Y',-1(a0)	;It's IY.
dspregd move.b	#'+',(a0)+
	move.b	2(pseudopc),d1	;Displacement
	bsr	ubyte
	move.b	#')',(a0)+
	rts

*
* Display the contents of the flag register in D0.
*
dspflag	lea	workbuf,a0
	move.b	#'-',d1		;Carry (assume not set)
	btst	#0,d0
	beq	1$
	move.b	#'C',d1		;Carry flag is set.
1$	move.b	d1,(a0)+
	move.b	#'-',d1		;Zero
	btst	#6,d0
	beq	2$
	move.b	#'Z',d1
2$	move.b	d1,(a0)+
	move.b	#'-',d1		;Minus
	btst	#7,d0
	beq	3$
	move.b	#'M',d1
3$	move.b	d1,(a0)+
	move.b	#'-',d1		;Even parity
	btst	#2,d0
	beq	4$
	move.b	#'E',d1
4$	move.b	d1,(a0)+
	move.b	#'-',d1		;Intermediate carry
	btst	#4,d0
	beq	5$
	move.b	#'I',d1
5$	move.b	d1,(a0)+
	move.b	#' ',(a0)+
	rts
	page
*************************************************************************
*									*
*	Initialization subroutines					*
*									*
*************************************************************************

*
* Load up the fake FDOS.
*
lodfdos move.l	a6,-(sp)
	lea	fdos,a6
	move.l	targbase,pseudosp
	adda.l	#$10000,pseudosp
	lea	-256(pseudosp),a0
	move.w	#fdoslen,d0
1$	move.b	(a6)+,(a0)+
	dbra	d0,1$
	lea	-256(pseudosp),a0
	move.l	a0,d0
	sub.l	targbase,d0
	move.b	#tJMP,0(targbase)	;Build BIOS and BDOS jumps.
	move.b	#tJMP,5(targbase)
	move.b	d0,6(targbase)
	rol.w	#8,d0
	move.b	d0,7(targbase)
	rol.w	#8,d0
	addq.w	#3,d0
	move.b	d0,1(targbase)
	rol.w	#8,d0
	move.b	d0,2(targbase)
	clr.w	-(pseudosp)	;Set up a return stack to exit simulation.
	move.l	(sp)+,a6
	rts

*
* Set up working registers.
*
lodregs lea	optabl,opptr	;Point base reg. to opcode dispatch table.
	lea	mloop,return
	tst.b	traceit 	;Is tracing required?
	beq.s	1$		;No.
	lea	mloopt,return	;Point return to trace test.
1$	lea	flags,flagptr
	lea	$100(targbase),pseudopc ;Start execution at 0100H.
	moveq	#$E,regcon0e		;Set up quick constants.
	moveq	#$1,regcon01
	moveq	#$F,regcon0f
	move.l	#$FF,regconff
	moveq	#0,rega
	moveq	#0,regf
	clr.b	regi(regs)
	rts
	page
*
* Get start and end addresses for tracing.
*
entrads tst.b	traceit 	;Is tracing required?
	beq	entradx 	;No.
	bsr	gtrange 	;Get trace range.
* Find out whether BIOS/BDOS calls are to be traced.
	move.l	#btrcmsg,d1
	bsr	pstring
	lea	workbuf,a0
	move.b	#10,(a0)
	bsr	getline
	move.b	#1,btrcflg
	cmpi.b	#'Y',workbuf+2
	beq.s	entradx
	cmpi.b	#'y',workbuf+2
	beq.s	entradx
	clr.b	btrcflg
entradx clr.b	traceflg	;Start with tracing turned off.
	rts

gtrange move.l	#tracemsg,d1	;Enter trace address if necessary.
	bsr	pstring
	lea	workbuf,a0
	move.b	#workbufn-workbuf-2,(a0)
	bsr	getline 	;Get the string.
	moveq	#0,d0
	move.b	1(a0),d0	;Number of bytes read
	addq.l	#2,a0		;Skip over length information.
	clr.b	0(a0,d0)	;Insert string terminator.
	bsr	atol		;Get trace start address.
	andi.l	#$FFFF,d1
	add.l	#target,d1
	move.l	d1,tracesad
* Now get the ending address.
	move.l	#tracemg2,d1
	bsr	pstring
	lea	workbuf,a0
	move.b	#workbufn-workbuf-2,(a0)
	bsr	getline
	moveq	#0,d0
	move.b	1(a0),d0
	addq.l	#2,a0
	clr.b	0(a0,d0)
	bsr	atol
	andi.l	#$FFFF,d1
	add.l	#target,d1
	move.l	d1,traceead
	bsr	pcrlf
	rts

tracemsg dc.b	cr,lf,'Start trace at >$'
tracemg2 dc.b	'  End trace at >$'
btrcmsg dc.b	'Trace BIOS/BDOS calls? >$'

*
* Open the file to be loaded, and load it into target space if successful.
*
loadcom movem.l d1-d3/a1-a2/a6,-(sp)	;Save registers.
	lea	cmdline,a0
	tst.b	cmdflag 	;Do we have a command already?
	bne.s	scancmd 	;Yes - process it.
* Display the command prompt.
prompt	lea	target,targbase ;Just in case "targbase" gets clobbered
	move.b	4(targbase),d1	;Get the default drive code.
	andi.b	#$0F,d1
	addi.b	#'A',d1 	;Convert drive code to ASCII letter.
	bsr	pchar		;Display the current drive.
	move.b	4(targbase),d1	;Get user number.
	lsr.b	#4,d1		;Move it to low-order 4 bits.
	beq.s	promptp 	;Don't insert user number if it's zero.
	cmpi.b	#10,d1		;Is user number 10 or greater?
	bcs.s	promptu 	;No.
	move.b	d1,-(sp)
	move.b	#'1',d1
	bsr	pchar		;Display tens digit of user number.
	move.b	(sp)+,d1
	subi.b	#10,d1
promptu addi.b	#'0',d1 	;Convert user number to ASCII.
	bsr	pchar		;Display user number.
promptp move.b	#'>',d1
	bsr	pchar
	lea	cmdline,a0
	move.b	#cmdlinen-cmdline-2,(a0)
	bsr	getline 	;Get a command line.
	moveq	#0,d0
	move.b	1(a0),d0	;Length of command line
	beq.s	prompt		;Zero - try again.
	addq.l	#2,a0		;Skip over length information.
	clr.b	0(a0,d0)	;Insert command line terminator.
	cmpi.b	#3,(a0) 	;Control-C?
	bne.s	scancmd 	;No.
	move.b	#1,quitflg	;Set quit flag.
	bra	quitprg 	;Exit the simulator.
scancmd clr.b	cmdflag 	;Ask for a new command next time.
	clr.b	builtin 	;Clear "built-in command" flag.
* Check for a change of drive number.
	tst.b	2(a0)		;Is the command two characters long?
	bne.s	convcmd 	;No - treat as a normal command.
	cmpi.b	#':',1(a0)	;Drive specifier?
	bne.s	convcmd 	;No.
	bsr	ucase		;Get the drive code.
	cmpi.b	#'A',d0 	;Is it valid?
	bcs.s	opensel 	;No.
	cmpi.b	#'P',d0
	bhi.s	opensel
	subq.b	#1,d0
	andi.b	#$0F,d0 	;New drive code
	andi.b	#$F0,4(targbase)
	or.b	d0,4(targbase)	;Insert into current drive slot.
	bra	prompt
opensel move.l	#1$,d1
	bsr	pstring 	;'BDOS Error on '
	bsr	ucase
	move.b	d0,d1
	bsr	pchar		;Drive letter
	move.l	#2$,d1
	bsr	pstring 	;': Select'
	bra	prompt		;Try again.

1$	dc.b	'BDOS Error on $'
2$	dc.b	': Select',cr,lf,'$'

* Convert the program file name to an AmigaDOS file name in "comname".
convcmd lea	comname,a2
	cmpi.b	#':',1(a0)	;Is there a drive specifier?
	bne.s	1$		;No.
	bsr	ucase		;Get the drive letter.
	cmpi.b	#'A',d0 	;Is it valid?
	bcs.s	opensel 	;No.
	cmpi.b	#'P',d0
	bhi.s	opensel
	addq.l	#1,a0		;Skip over the colon.
	bra.s	2$
1$	move.b	4(targbase),d0	;Current drive and user number
	beq.s	loadnam 	;Drive A:, user 0
	andi.b	#$0F,d0 	;Isolate current drive bits
	addi.b	#'A',d0 	;Convert to drive code.
2$	move.b	4(targbase),d1	;Current drive and user number
	lsr.b	#4,d1		;Isolate the user number.
	bne.s	3$		;Not zero - make a directory name.
	cmpi.b	#'A',d0 	;Are we loading from drive A:?
	beq.s	loadnam 	;Yes - use the current directory.
3$	move.b	#'C',(a2)+	;Convert to AmigaDOS device.
	move.b	#'P',(a2)+
	move.b	#'M',(a2)+
	move.b	d0,(a2)+	;Insert the drive letter.
	tst.b	d1		;User zero?
	beq.s	5$		;Yes - don't bother inserting it.
	move.b	#'0',(a2)+	;Assume user number is less than 10.
	cmpi.b	#10,d1		;Is user number 10 or greater?
	bcs.s	4$		;No.
	move.b	#'1',-1(a2)	;Change the first digit to 1.
	subi.b	#10,d1
4$	addi.b	#'0',d1 	;Convert user number to ASCII.
	move.b	d1,(a2)+	;Insert user number into file spec.
5$	move.b	#':',(a2)+	;Insert a colon.
loadnam move.l	#comnamen,d1	;End of name, allowing for .COM suffix
	subq.l	#6,d1		;Allow for .COM suffix.
	sub.l	a2,d1		;Adjust for directory prefix, if any.
1$	bsr	ucase		;Convert file name to upper case.
	move.b	d0,(a2)+
	cmpi.b	#' ',(a0)	;End of name?
	beq.s	gotname 	;Yes.
	tst.b	(a0)		;End of command string?
	dbeq	d1,1$		;No - keep on going.
gotname move.l	a0,comend	;Save position in command line.
	move.b	#'.',(a2)+	;Mash file name to .COM.
	move.b	#'C',(a2)+
	move.b	#'O',(a2)+
	move.b	#'M',(a2)+
	clr.b	(a2)
	clr.b	cmdline 	;Ask for a new command next time.
* If this is a USER or SAVE command, don't look for a .COM file -
*  we'll process these commands ourselves.
	lea	comname,a0	;Scan for possible device name.
	move.l	a0,a1		;Use A1 as a scan pointer - preserve A0.
1$	tst.b	(a1)		;End of command name?
	beq.s	2$		;Yes - there is no device name.
	cmpi.b	#':',(a1)+	;End of device name?
	bne.s	1$		;No - continue scanning.
	move.l	a1,a0		;Point A0 past the device name.
2$	cmpi.b	#'.',4(a0)	;Is the command name four characters long?
	bne	opencom 	;No.
	cmpi.b	#'U',(a0)	;Check for a USER command.
	bne.s	tstsave
	cmpi.b	#'S',1(a0)
	bne.s	tstsave
	cmpi.b	#'E',2(a0)
	bne.s	tstsave
	cmpi.b	#'R',3(a0)
	bne.s	tstsave
	move.b	#1,builtin	;Indicate we have a USER command.
	bra	loaded
tstsave cmpi.b	#'S',(a0)	;Check for a SAVE command.
	bne.s	opencom
	cmpi.b	#'A',1(a0)
	bne.s	opencom
	cmpi.b	#'V',2(a0)
	bne.s	opencom
	cmpi.b	#'E',3(a0)
	bne.s	opencom
	move.b	#2,builtin	;Indicate we have a SAVE command.
	bra	loaded
* Open the command file and load it if it exists.
opencom move.l	#comname,d1
	move.l	#MODE_OLDFILE,d2
	move.l	_DOSBase,a6
	sys	Open		;Open the file.
	tst.l	d0		;Did the open fail?
	bne.s	comopen 	;No.
	lea	comname,a0
openerr cmpi.b	#'.',(a0)+	;Find end of file name.
	bne.s	openerr
	move.b	#'?',-1(a0)
	move.b	#cr,(a0)+
	move.b	#lf,(a0)+
	move.b	#'$',(a0)
	move.l	#comname,d1
	bsr	pstring 	;Echo "name?"
	bra	prompt		;Try again.

comopen move.l	d0,-(sp)	;Save the file handle.
	move.l	d0,d1
	move.l	pseudopc,d2	;Start loading at $0100 in target.
	move.l	#65536-512,d3	;Maximum number of bytes to load
	move.l	_DOSBase,a6
	sys	Read		;Load the .COM file.
	move.l	(sp)+,d1
	move.l	_DOSBase,a6
	sys	Close		;Close the .COM file.

* The program has now been loaded (unless it's USER or SAVE).
loaded	movem.l (sp)+,d1-d3/a1-a2/a6	;Refresh registers.
	movem.l d1-d3/a1-a2/a6,-(sp)
	lea	$80(targbase),a0	;Set up target's base page.
	move.l	a0,dmaaddr

* Set up FCBs and command line tail.
	lea	$5C(targbase),a0
	lea	$6C(targbase),a2
	clr.b	(a0)+
	clr.b	(a2)+
	moveq	#10,d0
clrfcb	move.b	#' ',(a0)+	;Clear FCBs.
	move.b	#' ',(a2)+
	dbra	d0,clrfcb
	clr.b	$80(targbase)	;Clear the command line tail.

	move.l	comend,a0	;Restore position in command line.
fcb1	tst.b	(a0)		;End of command line?
	beq	loadusr 	;Yes.
	cmpi.b	#' ',(a0)+	;Skip over to first file name
	beq.s	fcb1
	subq.l	#1,a0		;Back onto start of file name.
	move.l	a0,-(sp)	;Save position on command line.
	lea	$81(targbase),a2;A2 loads the command line tail.
gettail move.b	(a0)+,(a2)+	;Copy the command tail.
	bne.s	gettail
	move.l	a2,d0
	lea	$82(targbase),a0;Don't count null terminator!
	sub.l	a0,d0
	move.b	d0,$80(targbase);Length of command line tail
	move.l	(sp)+,a0	;Go back to the first file name.

	lea	$5C(targbase),a2;Address of current FCB
getfcb	move.l	a2,fcbptr	;Save pointer to current FCB.
	cmpi.b	#':',1(a0)	;Is a drive specified?
	bne.s	1$		;No.
	move.b	(a0),(a2)	;Get drive letter.
	subi.b	#'A'-1,(a2)	;Convert to drive code.
	addq.l	#2,a0		;Skip over drive designator.
	tst.b	(a0)		;End of command line?
	beq.s	loadusr 	;Yes - we're done.
	cmpi.b	#' ',(a0)	;End of file name?
	beq.s	getfcbx 	;Yes.
1$	addq.l	#1,a2		;Start of file name in FCB
2$	move.b	(a0)+,(a2)+	;Copy file name to FCB.
3$	tst.b	(a0)		;End of command?
	beq.s	loadusr 	;Yes.
	cmpi.b	#' ',(a0)	;End of file name?
	beq.s	getfcbx 	;Yes.
	cmpi.b	#'.',(a0)	;Start of file name extension?
	bne.s	2$		;No - continue loading file name.
	move.l	fcbptr,a2	;Copy original pointer
	lea	9(a2),a2	;Skip over to extension field.
	addq.l	#1,a0		;Skip over the period.
	bra.s	3$
getfcbx tst.b	(a0)		;End of command line?
	beq.s	loadusr 	;Yes.
	cmpi.b	#' ',(a0)+	;Look for another file name.
	beq.s	getfcbx
	subq.l	#1,a0		;Back onto start of file name.
	move.l	fcbptr,d0
	lea	$5C(targbase),a2
	cmp.l	d0,a2		;Was this the first FCB?
	bne.s	loadusr 	;No - stop after two FCBs.
	lea	16(a2),a2	;Skip over to the next FCB.
	bra.s	getfcb		;Load the next FCB.

* If this is a USER or SAVE command, process it here.
*  These are the only built-in commands that SimCPM supports,
*  since the rest are just as easily done through the CLI.
*  The first operand is a USER number or the number of pages to SAVE.
loadusr tst.b	builtin 	;Is this a built-in command?
	beq	loadcmx 	;No - we're done.
	lea	$5C(targbase),a0 ;Scan the first FCB.
	moveq	#0,d0		;Build the number here.
	tst.b	(a0)+		;Is drive code omitted?
	bne.s	badunum 	;No - we don't have a valid number.
	cmpi.b	#' ',(a0)	;Is the number missing?
	beq.s	badunum 	;Yes - error.
	cmpi.b	#' ',3(a0)	;Is the number too long?
	bne.s	badunum 	;Yes - it's likely too big.
getuser cmpi.b	#'0',(a0)	;Is the current digit valid?
	bcs.s	badunum 	;No.
	cmpi.b	#'9',(a0)
	bhi.s	badunum
	mulu	#10,d0		;Shift previous digits, if any.
	move.b	(a0)+,d1	;Get the current digit.
	andi.w	#$0F,d1 	;Convert current digit to binary.
	add.w	d1,d0		;Accumulate total.
	cmpi.b	#' ',(a0)	;End of number?
	bne.s	getuser 	;No - try for another digit.
	bra.s	chkuser
badunum moveq	#-1,d0		;Invalid number
chkuser cmpi.b	#1,builtin	;Is this a USER command?
	bne.s	chksave 	;No.
	cmpi.w	#15,d0		;Is user number over 15?
	bls.s	setuser 	;No - it's valid.
	move.l	#badumsg,d1
	bsr	pstring 	;Display an error message and try again.
	bra	prompt
setuser andi.b	#$0F,4(targbase);Clear original user number.
	lsl.b	#4,d0		;Move new user bits into position.
	or.b	d0,4(targbase)	;Insert new user number.
	bra	prompt

badumsg dc.b	'Invalid user number',cr,lf,'$'

chksave tst.w	d0		;Attempt to save zero blocks?
	beq.s	badblks 	;Yes - error.
	cmpi.w	#255,d0 	;Is number of blocks to SAVE valid?
	bls.s	savefn		;Yes.
badblks move.l	#1$,d1
	bsr	pstring
	bra	prompt
1$	dc.b	'Number of pages must be from 1 to 255.',cr,lf,'$'
savefn	lea	$6C(targbase),a0;FCB for SAVE file name
	cmpi.b	#' ',1(a0)	;Is file name missing?
	bne.s	opensav 	;No.
	move.l	#1$,d1
	bsr	pstring
	bra	prompt
1$	dc.b	'File name is missing.',cr,lf,'$'
opensav lea	opnname,a1	;Build AmigaDOS file name here.
	move.l	a1,d1		;We'll need it here.
	move.l	d0,-(sp)	;Save number of blocks to save.
	bsr	convfn		;Make a file name.
	move.l	#MODE_NEWFILE,d2
	move.l	_DOSBase,a6
	sys	Open		;Open the file.
	tst.l	d0		;Did the open fail?
	bne.s	saveit		;No.
	move.l	(sp)+,d0	;Clean up the stack.
	move.l	#1$,d1
	bsr	pstring
	bra	prompt
1$	dc.b	'Unable to open file for SAVE.',cr,lf,'$'
saveit	move.l	d0,d1		;Handle for SAVE file
	lea	target+$100,a0	;"targbase" isn't intact right now.
	move.l	a0,d2		;Start of data to save
	move.l	(sp)+,d3	;Number of 256-byte pages to save
	asl.l	#8,d3		;Convert to number of bytes.
	move.l	d1,-(sp)	;Save the file handle.
	sys	Write		;Write the file.
	move.l	(sp)+,d1
	sys	Close		;Close the file.
	bra	prompt		;Successful completion

* We have successfully loaded a .COM file.
loadcmx movem.l (sp)+,d1-d3/a1-a2/a6	;Restore registers.
	rts			;Exit.

*
* Subroutine to get a character and convert it to upper case
*
ucase	move.b	(a0)+,d0
	cmpi.b	#'a',d0
	bcs.s	ucasex
	cmpi.b	#'z',d0
	bhi.s	ucasex
	subi.b	#'a'-'A',d0
ucasex	rts
	page
*************************************************************************
*									*
*	BDOS / BIOS service routines					*
*									*
*************************************************************************
service movem.l a1/a6,-(sp)
	move.b	rega,newrega	;Save Z-80 accumulator (D2)
	move.l	_DOSBase,a6	;Get dos.library pointer
* Decode the byte following the HLT instruction (BIOS call type).
	moveq	#0,d0		;Handle BIOS/BDOS service request
	move.b	(pseudopc)+,d0	; of form HLT DB opcode.
	cmp	#(biostabn-biostab)/4,d0
	blt.s	dobios		;Function number is within range.
badbios move.b	d0,-(sp)	;Flag illegal BIOS call
	move.l	#ilgbios,d1	; and spill guts.
	bsr	pstring
	move.b	(sp)+,d1
	bsr	pbyte
	move.l	#atmsg,d1
	bsr	pstring
	move.b	1(pseudosp),d1	;Address where called (top stack entry)
	ror.w	#8,d1
	move.b	0(pseudosp),d1
	bsr	pword
	bsr	pcrlf
	bsr	dump
	bra	quitprg

ilgbios dc.b	cr,lf,'Illegal BIOS call $'
biosmsg dc.b	'BIOS call $'
atmsg	dc.b	' (hex) at $'

dobios	move.l	d0,-(sp)	;Save BIOS function number.
	beq.s	biostrx 	;Zero - it's a BDOS call.
	tst.b	btrcflg 	;Trace BIOS calls?
	beq.s	biostrx 	;No.
	move.l	#biosmsg,d1
	bsr	pstring
	move.l	(sp),d1
	bsr	pbyte
	move.l	#atmsg,d1
	bsr	pstring
	move.b	1(pseudosp),d1	;Address where called (top stack entry)
	ror.w	#8,d1
	move.b	0(pseudosp),d1
	bsr	pword
	bsr	pcrlf
	move.l	(sp),d0
biostrx asl	#2,d0		;Multiply function number by 4.
	addi.l	#biostab,d0	;Point at address table entry.
	movea.l d0,a0
	movea.l (a0),a0 	;Point to appropriate service routine.
	move.l	(sp)+,d0	;Restore BIOS function number.
	jmp	(a0)		;Jump to the routine.
* If the BIOS code is zero, it's a BDOS call.
*  Decode register C using a similar routine to the BIOS decoding above.
bdosfn	moveq	#0,d0
	move.b	regc(regs),d0	;Get BDOS function number.
	cmp	#(bdostabn-bdostab)/4,d0
	blt.s	dobdos		;Function number is within range.
badbdos move.b	d0,-(sp)
	move.l	#ilgbdos,d1	;Illegal or unsupported BDOS call
	bsr	pstring
	move.b	(sp)+,d1
	bsr	pbyte
	move.l	#atmsg,d1
	bsr	pstring
	move.b	1(pseudosp),d1	;Address where called (top stack entry)
	ror.w	#8,d1
	move.b	0(pseudosp),d1
	bsr	pword
	bsr	pcrlf
	bsr	dump
	bra	quitprg

ilgbdos dc.b	cr,lf,'Illegal BDOS call $'
bdosmsg dc.b	'BDOS call $'

dobdos	move.l	d0,-(sp)	;Save BDOS function number.
	tst.b	btrcflg 	;Trace BDOS calls?
	beq.s	bdostrx 	;No.
	move.l	#bdosmsg,d1
	bsr	pstring
	move.l	(sp),d1
	bsr	pbyte
	move.l	#atmsg,d1
	bsr	pstring
	move.b	1(pseudosp),d1
	ror.w	#8,d1
	move.b	0(pseudosp),d1
	bsr	pword
	bsr	pcrlf
	move.l	(sp),d0
bdostrx cmpi.b	#10,d0		;BDOS function 10 or higher?
	bcs.s	bdosjmp 	;No.
	bsr	dmpstr		;Dump any outstanding console output.
	move.l	(sp),d0 	;Restore BDOS function number.
bdosjmp asl	#2,d0		;Multiply function number by 4.
	addi.l	#bdostab,d0	;Point at address table entry.
	movea.l d0,a0
	movea.l (a0),a0 	;Point to appropriate service routine.
	move.l	(sp)+,d0	;Restore BDOS function number.
	moveq	#0,d1
	move.w	regd(regs),d1	;Get argument.
	jmp	(a0)		;Jump to the routine.
* Return here after performing the BDOS function.
results movem.l (sp)+,a1/a6
	moveq	#0,rega
	move.b	newrega,rega	;Get new accumulator value.
* We have finished processing the BDOS function.
	move.b	rega,d0 	;Set flags.
	and.w	regconff,d0
	move.b	0(flagptr,d0.w),regf
	rts
*
* Individual BDOS service routines
*
bdos00	bra	quitprg 	;Exit program.

bdos01	bsr	dmpstr		;Console input
	move.l	rawhand,d1
	move.l	#newrega,d2
	moveq	#1,d3
	sys	Read
	bra	results

bdos02	move.b	rege(regs),d1	;Console output
	clr.b	testdol 	;Allow dollar signs
	bsr	pchar
	bra	results

bdos03	equ	badbdos 	;Reader input

bdos04	equ	badbdos 	;Punch output

bdos05	pea	rege(regs)	;List output byte
bdos05t tst.b	listopn 	;Is the printer already open?
	bne.s	bdos05w 	;Yes.
	move.l	#prtname,d1
	move.l	#MODE_NEWFILE,d2
	sys	Open		;Open the printer.
	move.l	d0,prthand	;Save the file handle.
	bne.s	1$		;The open was successful.
	move.l	#badprt,d1
	bsr	pstring 	;Indicate an unsuccessful open.
	bsr	dump		;Spill guts...
	bra	quitprg 	; and exit.
1$	move.b	#1,listopn	;Indicate that the list device is open.
bdos05w move.l	prthand,d1
	move.l	(sp)+,d2	;Character to send to the list device
	moveq	#1,d3		;Just send one byte.
	sys	Write		;Send the byte to the list device.
	bra	results
	
prtname dc.b	'PRT:RAW',0
badprt	dc.b	'Unable to open the list device!$'

bdos06	cmpi.b	#$FF,rege(regs) ;Direct console I/O
	bne	bdos02		;Send the byte.
	bsr	dmpstr		;Dump any outstanding output.
	move.l	rawhand,d1
	moveq	#1,d2		;Wait for one microsecond.
	sys	WaitForChar	;Check whether a character is ready.
	tst.l	d0		;Is a character ready?
	bne	bdos01		;Yes - get it.
	clr.b	newrega 	;Indicate that nothing is ready.
	bra	results

bdos07	move.b	3(targbase),newrega	;Get IOBYTE
	bra	results

bdos08	move.b	rege(regs),3(targbase)	;Set IOBYTE
	bra	results

bdos09	add.l	targbase,d1	;Console output string
	bsr	pstring
	bra	results

bdos10	add.l	targbase,d1	;Console input line
	movea.l d1,a0		;The buffer is here.
	bsr	getline 	;Get a line.
	cmpi.b	#3,2(a0)	;Was it a control-C?
	bne	results 	;No - continue processing.
	bra	quitprg 	;Terminate the program.

bdos11	move.l	rawhand,d1	;Console status check
	moveq	#1,d2		;Wait for one microsecond.
	sys	WaitForChar	;Check whether a character is ready.
	move.b	d0,newrega	;Result is compatible with CP/M.
	bra	results

bdos12	clr.b	regh(regs)	;Get system identification
	move.b	#$22,regl(regs) ;Pretend we're CP/M 2.2.
	move.b	#$22,newrega	;Some programs use undocumented return reg.
	clr.b	regb(regs)
	bra	results

bdos13	move.b	4(targbase),d0	;Reset all drives
	andi.b	#$0F,d0 	;Current drive
	moveq	#1,d1
	lsl.w	d0,d1		;Set up drive bit.
	move.w	d1,acmap	;Set active drive map to current drive only.
	clr.w	romap		;Reset read-only map.
	bra	results

bdos14	move.b	rege(regs),d0	;Select drive
	andi.b	#$0F,d0 	;Isolate drive bits.
	andi.b	#$F0,4(targbase)
	or.b	d0,4(targbase)	;Insert new bits.
	moveq	#1,d1
	lsl.w	d0,d1		;Set up drive bit.
	or.w	d0,acmap	;Add new drive to active drive map.
	bra	results

bdos15	move.l	d1,-(sp)	;Open existing file
	bsr	gethand
	tst.l	d1		;Is the file already open?
	beq.s	bdos15g		;No - go ahead.
	clr.l	(a1)		;Clear file handle table entry.
	sys	Close		;Close the file.
bdos15g	move.l	(sp)+,d1
	add.l	#target,d1
	move.l	#MODE_OLDFILE,d2
	movea.l d1,a0		;The FCB is here.
	bsr	mapdrv		;Get drive map bit.
bdos15o move.l	a0,-(sp)
	lea	opnname,a1	;Build AmigaDOS file name here.
	move.l	a1,d1		;We'll need it here.
	bsr	convfn		;Make a file name.
	sys	Open		;Open the file.
	move.l	(sp)+,a1	;The FCB is here.
	lea	handles,a0
	moveq	#(handlen-handles)/16-1,d1
	move.b	#$FF,newrega	;Assume the open failed.
	tst.l	d0		;Did it fail?
	beq	results		;Yes.
	clr.b	newrega 	;Set flag to indicate success.
1$	tst.l	(a0)		;Available handle entry?
	beq.s	2$		;Yes.
	lea	16(a0),a0	;Check the next entry.
	dbra	d1,1$
	move.l	d0,d1		;File handle table overflow!
	sys	Close		;Close the file.
	move.l	#fullmsg,d1
	bsr	pstring 	;Display an error message
	bra	quitprg 	; and forget the whole thing.
2$	move.l	d0,(a0)+	;Save the file handle.
	moveq	#11,d0
3$	move.b	(a1)+,(a0)+	;Move first 12 bytes of FCB to table.
	dbra	d0,3$
	move.w	newdmap,d0
	or.w	d0,acmap	;Add drive to active drive map.
	bra	results

fullmsg dc.b	'Too many files are open!',cr,lf,'$'

bdos16	move.b	#$FF,newrega	;Close file
	bsr	gethand 	;Get the file handle.
	tst.l	d1		;Did we find it?
	beq	results 	;No - return failure code.
	clr.l	(a1)		;Clear the handle table entry.
	sys	Close		;Close the file.
	clr.b	newrega 	;Indicate success.
	bra	results

bdos18	lea	renname,a0	;Search for next file
	bra.s	bdos17i
bdos17	lea	srchnam,a0	;Search for first file
bdos17i	move.b	#$FF,newrega	;Assume we'll fail.
	add.l	targbase,d1
	movea.l d1,a1		;The FCB is here.
	addq.l	#1,a1		;The file name is here.
	moveq	#10,d0
1$	cmp.b	#'*',(a1)	;Is this an asterisk?
	beq.s	2$		;Yes - replace with question marks.
	move.b	(a1)+,(a0)+	;Move the current character.
	dbra	d0,1$
	bra.s	bdos17c
2$	move.b	#'?',(a0)+	;Convert to question marks.
	addq.l	#1,a1
	subq	#1,d0
	bmi.s	bdos17c		;The asterisk was in the extension.
	cmpi.w	#2,d0		;End of file name?
	bne.s	2$		;No - insert another question mark.
	bra.s	1$		;Start the extension field.
bdos17c	lea	target,targbase
	cmpi.b	#18,regc(regs)	;Search for next file?
	bne.s	bdos17k		;No.
	lea	srchnam,a0
	lea	renname,a1
	moveq	#10,d0
1$	cmp.b	(a0)+,(a1)+	;Search for the same name as last time?
	bne	results		;No - exit with failed status.
	dbra	d0,1$
	tst.l	fibsize		;Is the last file completely done?
	beq.s	bdos17k		;Yes - try for another one.
	pea	0		;Housekeeping for the stack
	bra.s	bdos17b		;Create an entry for the next extent.
bdos17k	move.l	#null,d1
	move.l	#ACCESS_READ,d2
	sys	Lock		;Get a file lock.
	tst.l	d0		;Did we fail?
	beq	results 	;Yes - exit.
	move.l	d0,-(sp)	;Save the lock.
	move.l	d0,d1		;Put it here for Examine.
	move.l	#fib,d2
	lea	target,targbase
	move.w	newdmap,d0
	or.w	d0,acmap	;Add drive to active drive map.
	cmpi.b	#18,regc(regs)	;Search for next file?
	beq.s	bdos17n 	;Yes.
	sys	Examine 	;Set up to find the first file.
	tst.l	d0		;Did we succeed?
	beq	bdos17x 	;No.
bdos17n move.l	(sp),d1
	move.l	#fib,d2
	sys	ExNext		;Look for the next file.
	tst.l	d0		;Did we succeed?
	beq	bdos17x 	;No.
	tst.l	fibtype 	;Is this a file entry?
	bpl	bdos17n 	;No - ignore subdirectory entries.
	clr.w	ext17		;Clear extent number.

bdos17b	move.l	dmaaddr,a1	;Build a fake FCB here.
	clr.b	(a1)+		;Clear drive code.
	move.l	a1,-(sp)	;The file name starts here.
	moveq	#10,d0
1$	move.b	#' ',(a1)+	;Clear file name and extension.
	dbra	d0,1$
	moveq	#19,d0
2$	clr.b	(a1)+		;Clear remainder of FCB to zeros.
	dbra	d0,2$
	lea	fibname,a0	;A0 scans AmigaDOS file name.
	move.l	(sp)+,a1	;A1 builds CP/M file name.
bdos17f tst.b	(a0)		;End of file name?
	beq.s	2$		;Yes.
	move.b	(a0)+,d0	;Get the current character.
	cmpi.b	#'a',d0		;Is it lower case?
	bcs.s	1$		;No - leave it alone.
	cmpi.b	#'z'+1,d0
	bcc.s	1$
	subi.b	#'a'-'A',d0	;Convert to upper case.
1$	move.b	d0,(a1)+	;Move the (possibly-converted) character.
	cmp.b	#'.',(a0)	;Start of file name extension?
	bne.s	bdos17f 	;No.
	move.l	dmaaddr,d0
	add.w	#9,d0		;Point to start of extension field.
	cmpa.l	d0,a1		;Is file name too long?
	bhi	bdos17n 	;Yes - not a valid CP/M file name - ignore it.
	move.l	d0,a1
	addq.l	#1,a0		;Skip over period.
	bra.s	bdos17f 	;Get the file name extension.
2$	move.l	dmaaddr,d0
	add.w	#13,d0
	cmpa.l	d0,a1		;Is the file name too long?
	bhi	bdos17n 	;Yes - ignore it.
	move.l	dmaaddr,a0
	addq.l	#1,a0
	lea	srchnam,a1
	moveq	#10,d0
3$	cmpi.b	#'?',(a1)	;Wild card character?
	bne.s	4$		;No - compare it.
	addq.l	#1,a0		;Skip over wild card character.
	addq.l	#1,a1
	dbra	d0,3$
	bra.s	bdos17w		;We found a wild card match.
4$	cmp.b	(a0)+,(a1)+	;Does the file name match the search name?
	bne	bdos17n 	;No - try for another one.
	dbra	d0,3$		;Check the next character.
bdos17w	lea	target,targbase
	moveq	#0,d1
	move.w	regd(regs),d1
	moveq	#0,d0
	move.b	12(targbase,d1.l),d0	;Extent flag
	beq.s	bdos17h		;Extent zero - take it.
	cmpi.b	#'?',d0		;Search for all extents?
	beq.s	bdos17h		;Yes - take this one.
	move.w	d0,ext17	;Save extent number.
	moveq	#14,d1
	lsl.l	d1,d0		;Multiply by 16384 to get byte displacement.
	sub.l	d0,fibsize	;Beyond end of file?  (Adjust byte count!)
	bcs	bdos17n		;Yes - forget this entry.

bdos17h	clr.b	newrega 	;Set success flag.
	move.l	dmaaddr,a0	;The FCB is here.
	move.w	ext17,d0	;Current extent number (counts up from zero)
	move.b	d0,12(a0)	;Insert it into the FCB.
	andi.b	#$1F,12(a0)	;Only the low-order 5 bits go here.
	lsr.l	#5,d0
	move.b	d0,14(a0)	;High-order portion of extent number
	move.l	fibsize,d0	;Number of bytes remaining
	move.b	#$80,15(a0)	;Assume the current extent is full.
	cmpi.l	#16384,d0	;Is it?
	bcc.s	1$		;Yes.
	and.l	#$3FFF,d0	;Number of bytes in the last extent
	add.w	#$7F,d0		;(for rounding)
	lsr.l	#7,d0		;Number of 128-byte records in extent
	move.b	d0,15(a0)	;Store record count in FCB.
1$	lea	target,targbase
	moveq	#0,d1
	move.w	regd(regs),d1
	cmpi.b	#'?',12(targbase,d1.l)	;Search for all extents?
	bne.s	2$			;No - we're done with this file.
	addq.w	#1,ext17	;Bump extent counter.
	move.l	#16384,d0
	sub.l	d0,fibsize	;Decrement remaining byte count
	bhi.s	bdos17x		;There's more to do.
2$	clr.l	fibsize		;Set remaining count to zero.
bdos17x move.l	(sp)+,d1	;Get the lock.
	beq	results		;Ignore dummy entry for multi-extent file.
	sys	UnLock		;Release the lock.
	bra	results

bdos19	add.l	targbase,d1	;Delete file
	movea.l d1,a0		;The FCB is here.
	bsr	mapdrv		;Get drive map bit.
	and.w	romap,d1	;Is this drive read-only?
	bne	roerr		;Yes - abort the deletion.
	lea	opnname,a1	;Build AmigaDOS file name here.
	move.l	a1,d1		;We'll need it here.
	bsr	convfn		;Make a file name.
	sys	DeleteFile	;Delete the file.
	move.w	newdmap,d0
	or.w	d0,acmap	;Add drive to active drive map.
	bra	results

bdos20	clr.b	newrega 	;Sequential read
	bsr	gethand
	tst.l	d1
	beq.s	1$
	move.l	dmaaddr,d2
	move.l	#128,d3
	sys	Read
	tst.l	d0		;Were we successful?
	bgt	results 	;Yes.
1$	move.b	#$1,newrega	;Set end-of-file flag.
	bra	results

bdos21	clr.b	newrega 	;Sequential write
	bsr	gethand
	tst.l	d1
	beq.s	1$
	move.l	d1,-(sp)
	bsr	mapdrv		;Get drive map bit.
	move.w	d1,d0
	move.l	(sp)+,d1
	and.w	romap,d0	;Is this drive read-only?
	bne.s	roerr		;Yes - error
	move.l	dmaaddr,d2
	move.l	#128,d3
	sys	Write
	tst.l	d0		;Were we successful?
	bgt	results 	;Yes.
1$	move.b	#$FF,newrega	;Set failure flag.
	bra	results

bdos22	move.l	d1,-(sp)	;Make new file
	bsr	gethand
	tst.l	d1		;Is the file already open?
	beq.s	bdos22g		;No - go ahead.
	clr.l	(a1)		;Clear file handle table entry.
	sys	Close		;Close the file.
bdos22g	move.l	(sp)+,d1
	add.l	#target,d1
	move.l	#MODE_NEWFILE,d2
	movea.l d1,a0		;The FCB is here.
	bsr	mapdrv		;Get the drive map bit.
	and.w	romap,d1	;Is the drive read-only?
	beq	bdos15o 	;No - continue with BDOS 15 open routine
roerr	move.l	#1$,d1
	bsr	pstring 	;'BDOS Error on '
	bsr	ucase
	move.b	d0,d1
	add.b	#'A',d1
	bsr	pchar		;Drive letter
	move.l	#2$,d1
	bsr	pstring 	;': R/O'
	bra	quitprg 	;Abort the program.

1$	dc.b	'BDOS Error on $'
2$	dc.b	': R/O',cr,lf,'$'

bdos23	add.l	targbase,d1	;Rename file
	movea.l d1,a0
	move.l	a0,-(sp)
	bsr	mapdrv		;Get drive map bit.
	and.w	romap,d1	;Is this drive read-only?
	bne	roerr		;Yes - error
	lea	opnname,a1
	bsr	convfn		;Convert old file name.
	move.l	(sp)+,a0
	lea	16(a0),a0
	lea	renname,a1
	bsr	convfn		;Convert new file name.
	move.l	#opnname,d1
	move.l	#renname,d2
	sys	Rename		;Rename the file.
	move.w	newdmap,d1
	or.w	d1,acmap	;Add drive to active drive map.
	clr.b	newrega 	;Assume we succeeded.
	tst.l	d0		;Did we fail?
	bne	results 	;No.
	move.b	#$FF,newrega
	bra	results

bdos24	move.w	acmap,regh(regs);Get active drive map
	bra	results

bdos25	move.b	4(targbase),newrega	;Get default drive number
	andi.b	#$0F,newrega	;Isolate drive bits.
	bra	results

bdos26	add.l	targbase,d1	;Set file buffer address
	move.l	d1,dmaaddr
	bra	results

bdos27	move.w	#fakealv-fdos+$FF00,regh(regs)	;Get allocation vector
	move.b	regl(regs),newrega	;Make undocumented copy.
	move.b	regh(regs),regb(regs)
	move.l	#null,d1
	move.l	#ACCESS_READ,d2
	sys	Lock		;Get a file lock.
	move.l	d0,d1		;Did we fail?
	beq	results 	;Yes - exit.
	move.l	d0,-(sp)	;Save the lock.
	move.l	#InfoData,d2
	sys	Info		;Get disk information.
	tst.l	d0		;Did we fail?
	beq	bdos27x		;Yes - exit.
	lea	fakealv-fdos+$FF00,a0
	adda.l	targbase,a0	;A0 loads allocation vector.
	move.b	#$F0,(a0)+	;Initial allocation for directory
	clr.b	(a0)+
	move.l	a0,-(sp)
	move.l	id_NumBlocks,d1	;Number of 512-byte blocks on disk
	addq.l	#3,d1
	lsr.l	#2,d1		;Convert to 2048-byte blocks for CP/M.
	move.l	d1,d0
	subq.l	#1,d0
	move.b	d0,fakedpb+5	;Insert high block number in DPB.
	lsr.l	#8,d0
	move.b	d0,fakedpb+6
1$	clr.b	(a0)+		;Clear remainder of allocation vector.
	dbra	d1,1$
	move.l	(sp)+,a0
	move.l	id_NumBlocksUsed,d0	;Number of 512-byte blocks used
	moveq	#11,d1
	lsr.w	#2,d0		;CP/M (2048-byte) blocks used
	beq.s	bdos27x		;Nothing is allocated.
	move.w	d0,d1
	and.w	#7,d1		;Number of bits to set in partial ALV byte
	beq.s	2$ 		;No partial byte
	subq.w	#1,d1		;Number of bits less one
	move.b	#$80,d2		;Here's one bit.
	asr.b	d1,d2		;Make some more.
	move.b	d2,(a0)+	;Store partial allocation vector byte.
2$	lsr.w	#3,d0		;Number of ALV bytes to set all bits in
	beq	bdos27x		;There are none - we're done.
	subq.w	#1,d0
3$	move.b	#$FF,(a0)+	;Set all bits in these ALV bytes.
	dbra	d0,3$
bdos27x	move.l	(sp)+,d1
	sys	UnLock		;Release the file lock.
	bra	results

bdos28	move.b	4(targbase),d0	;Protect drive
	andi.b	#$0F,d0 	;Current drive
	moveq	#1,d1
	lsl.w	d0,d1		;Set up drive bit.
	or.w	d1,romap	;Add it to read-only map.
	bra	results

bdos29	move.w	romap,regh(regs);Get read-only map
	bra	results

bdos30	equ	badbdos 	;Set file attributes

bdos31	move.w	#fakedpb-fdos+$FF00,regh(regs)	;Get disk parameter block
	move.b	regl(regs),newrega	;Make undocumented copy.
	move.b	regh(regs),regb(regs)
	bra	results

bdos32	cmp.b	#$FF,rege(regs) ;Get or set user code
	bne.s	1$		;Set it.
	move.b	4(targbase),d0	;Current drive and user code
	lsr.b	#4,d0		;Set up user code.
	move.b	d0,newrega
	move.b	newrega,regl(regs)
	move.b	regh(regs),regb(regs)
	bra	results
1$	andi.b	#$0F,4(targbase);Clear old user code.
	move.b	rege(regs),d0	;Get new user code.
	lsl.b	#4,d0		;Shift bits into position.
	or.b	d0,4(targbase)	;Insert new user code.
	bra	results

bdos33	pea	_LVORead(a6)	;Direct access read
	clr.b	newrega
	bsr	gethand
	bra.s	bdos34c 	;Use common read/write routine.

bdos34	pea	_LVOWrite(a6)	;Direct access write
	clr.b	newrega
	bsr	gethand
	move.l	d1,-(sp)
	bsr	mapdrv		;Get drive map bit.
	move.w	d1,d0
	move.l	(sp)+,d1
	and.w	romap,d0	;Is this drive read-only?
	bne	roerr		;Yes - error
bdos34c move.l	d1,-(sp)	;Save file handle (common read/write routine)
	moveq	#0,d2
	move.b	35(a0),d2	;Get seek address.
	rol.l	#8,d2
	move.b	34(a0),d2
	rol.l	#8,d2
	move.b	33(a0),d2
	rol.l	#7,d2		;Convert record number to byte displacement.
	move.b	33(a0),32(a0)	;Set up current record number in extent.
	andi.b	#$7F,32(a0)
	moveq	#14,d0
	ror.l	d0,d2
	move.b	d2,12(a0)	;Current extent number
	rol.l	d0,d2
	moveq	#-1,d3
	sys	Seek		;Seek to desired position.
	move.l	(sp)+,d1	;Get the file handle again.
	move.l	(sp)+,a0	;Address of read or write routine
	tst.l	d0		;Were we successful?
	bmi	1$		;No.
	move.l	dmaaddr,d2
	move.l	#128,d3
	jsr	(a0)		;Read or write the desired record.
	tst.l	d0		;Were we successful?
	bgt	results 	;Yes.
1$	move.b	#6,newrega	;Set failure (invalid address) flag.
	bra	results

bdos35	bsr	gethand 	;Get file end address
	move.l	a0,-(sp)	;Pointer to FCB
	move.l	d1,-(sp)
	moveq	#0,d2
	moveq	#1,d3
	sys	Seek		;Jump to end of file.
	move.l	(sp)+,d1
	move.l	d0,d2		;Old position
	moveq	#-1,d3
	sys	Seek		;Go back to the old position.
	move.l	(sp)+,a0	;Restore FCB pointer.
	add.l	#$7F,d0		;Adjust for partial final block, if any.
	lsr.l	#7,d0		;File size in 128-byte records
	move.b	d0,33(a0)	;Insert file size into FCB.
	lsr.l	#8,d0
	move.b	d0,34(a0)
	lsr.l	#8,d0
	move.b	d0,35(a0)
	bra	results

bdos36	bsr	gethand 	;Get direct address
	move.l	a0,-(sp)	;Save pointer to FCB.
	moveq	#0,d2
	moveq	#0,d3
	sys	Seek		;Seek to current position.
	move.l	(sp)+,a0	;Restore FCB pointer.
	lsr.l	#7,d0		;Convert to 128-byte record number.
	beq.s	bdos36m 	;Beginning of file
	subq.l	#1,d0		;Back up to record just read.
bdos36m move.b	d0,33(a0)	;Insert position into FCB.
	lsr.l	#8,d0
	move.b	d0,34(a0)
	lsr.l	#8,d0
	move.b	d0,35(a0)
	bra	results

*
* Individual BIOS service routines
*
bios01	bra	quitprg 	;Warm boot

bios02	equ	bdos11		;Console status check

bios03	equ	bdos01		;Console input byte

bios04	move.b	regc(regs),d1	;Console output byte
	clr.b	testdol 	;Allow dollar signs
	bsr	pchar
	bra	results

bios05	pea	regc(regs)	;List output byte
	bra	bdos05t

bios06	equ	badbios 	;Punch output byte

bios07	equ	badbios 	;Reader input byte

bios08	equ	badbios 	;Home disk

bios09	equ	badbios 	;Select disk

bios10	equ	badbios 	;Set track

bios11	equ	badbios 	;Set sector

bios12	equ	badbios 	;Set DMA address

bios13	equ	badbios 	;Read disk

bios14	equ	badbios 	;Write disk

bios15	move.b	#$FF,newrega	;List status
	bra	results


*
* Set "newdmap" with a bit corresponding to the drive code in the
*  FCB pointed to by A0 (or the default drive if necessary).
*  The contents of "newdmap" will also be in the low-order word of D1.
*  Register D0 will be set to 0 for drive A:, 1 for drive B:, 2 for C:, etc.
*  The contents of register A0 will be preserved.
*
mapdrv	move.b	(a0),d0 	;Drive code
	bne.s	1$		;The drive is specified.
	move.b	4(targbase),d0	;Get the default drive.
	andi.b	#$0F,d0
	addq	#1,d0
1$	subq.b	#1,d0		;Adjust drive code so that 0 is A:, etc.
	moveq	#1,d1
	lsl.w	d0,d1		;Set up drive map bit.
	move.w	d1,newdmap	;Save it.
	rts


*
* Simulation of the Z-80 LD A,R instruction -
*  load a random 7-bit value into the accumulator.
*
movear	move.l	#dtstamp,d1
	movem.l a1/a6,-(sp)
	move.l	_DOSBase,a6
	sys	DateStamp
	movem.l (sp)+,a1/a6
	move.b	dtstamp+11,rega ;Low-order 7 bits of timer ticks
	andi.b	#$7F,rega
	jmp	(return)


*
* End of program, one way or another
*
quitprg move.l	savesp,sp	;Restore stack pointer.
	bsr	dmpstr		;Dump any outstanding console output.
	clr.w	romap		;Clear the read-only map.

* Close the serial.device if it was used.
	tst.l	rpport
	beq.s	closlis 	;Nothing was opened.
	tst.l	wpport
	beq.s	q3		;The output port wasn't opened.

	lea	writreq,a1
	move.l	_SysBase,a6
	sys	CloseDevice	;Close the serial output device.

	move.l	wpport,-(sp)
	jsr	_DeletePort	;Delete the serial output port.
	addq.l	#4,sp
	clr.l	wpport

q3	lea	readreq,a1
	move.l	_SysBase,a6
	sys	CloseDevice	;Close the serial input device.

	move.l	rpport,-(sp)
	jsr	_DeletePort	;Delete the serial input port.
	addq.l	#4,sp
	clr.l	rpport

* If the list device was used, close it.
closlis tst.b	listopn 	;Is the printer open?
	beq.s	closprt 	;No.
	move.l	prthand,d1
	sys	Close		;Close the printer.
closprt clr.b	listopn 	;Reset the "printer-open" flag.

* If any files were left open by the last program, close them.
	lea	handles,a0
	moveq	#(handlen-handles)/16-1,d0
closall move.l	(a0),d1
	beq.s	closnxt 	;This file isn't open.
	movem.l a0/d0,-(sp)
	move.l	_DOSBase,a6
	sys	Close		;Close this file.
	movem.l (sp)+,a0/d0
	clr.l	(a0)		;Clear the file handle.
closnxt	lea	16(a0),a0
	dbra	d0,closall
* Check whether we should quit the simulation.
	tst.b	quitflg 	;Exit the simulator?
	bne.s	exitsim 	;Yes.
	tst.b	cmdflag 	;Was .COM file loaded from command line?
	beq	nextprg 	;No - re-display the command prompt.
* Terminate execution of the simulator.
exitsim move.l	rawhand,d1	;Is RAW: open?
	beq.s	closlib 	;No.
	move.l	_DOSBase,a6
	sys	Close		;Close RAW:
closlib move.l	_SysBase,a6
	move.l	_DOSBase,a1
	sys	CloseLibrary	;Close dos.library.
	moveq	#0,d0		;Return with no error.
	rts			;All done
	page
*************************************************************************
*									*
*	AmigaDOS interface routines					*
*									*
*************************************************************************

*
* Get a line from the console.	CP/M BDOS 10 conventions are used.
*  A0 is assumed to point to the start of the buffer.
*  If the first character encountered is a control-C, this routine
*  exits, leaving just the control-C in the buffer.
*
getline movem.l d2-d3/a0-a1/a6,-(sp)
	bsr	dmpstr		;Flush the screen buffer first.
	move.l	_DOSBase,a6
	lea	2(a0),a1	;The current character loads here.
	clr.b	1(a0)		;Clear character count.
getlinl move.l	rawhand,d1	;Read from RAW:
	move.l	a1,d2		; into current position
	moveq	#1,d3		;  for a length of one byte.
	movem.l d1-d3/a0-a1,-(sp)
	sys	Read		;Get a character.
	movem.l (sp)+,d1-d3/a0-a1
	cmpi.b	#cr,(a1)	;Did we get a carriage return?
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.