;****************************************************************************
;				Z S D O S
;   A CP/M 2.2 compatible replacement Basic Disk Operating System (BDOS)
;
;     Copyright (C) 1986,7,8 by:
;
;          Harold F. Bower        and       Cameron W. Cotrill
;
;         7914 Redglobe Ct.                2160 N.W. 159th Place
;         Severn, MD 21144-1048            Beaverton, OR  97006
;         USA.                             USA.
;
;       HalBower@worldnet.att.net         ccotrill@symantec.com
;
;   This program is free software; you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation; either version 2 of the License, or
;   (at your option) any later version.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;   General Public License (file LICENSE.TXT) for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program; if not, write to the Free Software
;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;---------------------------------------------------------------------------
; ZSDOS is a CP/M 2.2 compatable BDOS replacement that contains numerous
; enhancements.  It is based on P2DOS 2.1 by HAJ Ten Brugge and revisions
; to P2DOS made by Harold F. Bower, Benjamin Ho, and Cameron W. Cotrill.
; Several good ideas from both CP/M Plus(tm) and ZRDOS(tm) have been added.
; The authors wish to thank Bridger Mitchell of Plu*Perfect Systems for
; suggesting we put our heads together, for reviewing the efforts, and for
; suggesting better methods for coding some sections.  Thanks also to Joe
; Wright of Alpha Systems for his review and suggestions, as well as
; squeezing a few more bytes for us.

; Support for Plu*Perfect'a BackGrounder ii(tm) and ZDS DateStamper(tm) is
; included, as well as support for ZCPR/BGii WHEEL and PATH.
; ZSDOS is compatable with NZCOM by Joe Wright of Alpha Systems.

; ZSDOS is designed for Z80 compatible processors ONLY!!!
; ZSDOS is coded to run in Z280 protected mode and may be ROMmed.

; LEGAL DEPARTMENT:   P2DOS was written by H.A.J. Ten Brugge,  ZSDOS
; modifications were by Cameron W. Cotrill and Harold F. Bower.
; ZDDOS modifications were done by Carson Wilson, Cameron W. Cotrill
; and Harold F. Bower.

; No author assumes responsibility or liability in the use of this
; program or any of its support utilities.

; P2DOS is Copyright (C) 1985 by H.A.J. Ten Brugge  - All Rights Reserved
;	H.A.J. Ten Brugge
;	F. Zernikestraat 207
;	7553 EC Hengelo
;	Netherlands
; Permission to use P2DOS code in ZSDOS granted to Harold F. Bower and
;   Cameron W. Cotrill in letter 28 March 1988

;   Code sections marked (bm) are revisions suggested by Bridger Mitchell.
;   Code sections marked (bh) are from SUPRBDOS mods to P2DOS by Benjamin Ho.
;   Code sections marked (crw) are revisions to support internal datestamper
;      and are Copyright (C) 1988 by Carson Wilson.

; NOTES: Backgrounder ii and DateStamper are trademarks of Plu*Perfect
;  Systems.  CP/M is a trademark of Digital Research, Incorporated.
;  ZRDOS is a trademark of Echelon, Incorporated.
	PAGE
;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; Version 1.2a, 11/04/89
; Assemble with : SLR Z80ASMP or ZMAC
; Revisions:
; 11/04/89	Moved home call to rddir so bios hostbuf always
;		updated before dir read.
; 07/18/89	Fixed tderr routine in ZDDOS so return codes not
; CWC		altered from tderr unless called from 102 or 103.
; 06/20/89	Fixed bug in F10 ^R that output 256 spaces if ^R
; CWC		entered with tab counter =0.
;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

	MACLIB	ZSDOS.LIB	; Get initialization code

RAMLOW	EQU	0000H		; Start address memory

	CSEG
ZSDOS	EQU	$		; Start address ZSDOS

	  IF  	ZRL
	COMMON	/_BIOS_/
BIOS:
	CSEG
	  ELSE
BIOS	EQU	ZSDOS+0E00H
	  ENDIF

BOOT	EQU	BIOS+0000H	; Cold Boot
WBOOT	EQU	BIOS+0003H	; Warm Boot
CONST	EQU	BIOS+0006H	; Console Status
CONIN	EQU	BIOS+0009H	; Console Input
CONOUT	EQU	BIOS+000CH	; Console Output
LIST	EQU	BIOS+000FH	; List Output
PUNCH	EQU	BIOS+0012H	; Punch Output
READER	EQU	BIOS+0015H	; Reader Input
HOME	EQU	BIOS+0018H	; Home Disk
SELDSK	EQU	BIOS+001BH	; Select Disk
SETTRK	EQU	BIOS+001EH	; Select Track
SETSEC	EQU	BIOS+0021H	; Select Sector
SETDMA	EQU	BIOS+0024H	; Set DMA Address
READ	EQU	BIOS+0027H	; Read 128 Bytes
WRITE	EQU	BIOS+002AH	; Write 128 Bytes
LISTST	EQU	BIOS+002DH	; List Status
SECTRN	EQU	BIOS+0030H	; Sector Translation

; Internal Definitions
	  IF  ZSDOS11
VERMAJ	EQU	1		; Major version number
VERMIN	EQU	1		; Minor version number
	  ELSE
VERMAJ	EQU	1
VERMIN	EQU	2
	  ENDIF		;Zs
VERS	EQU	VERMAJ*10H+VERMIN

CONTC	EQU	03H		; Key to generate warm boot
CONTH	EQU	08H		; Backspace
TAB	EQU	09H		; Tab
LF	EQU	0AH		; Line feed
CR	EQU	0DH		; Carriage return
CONTP	EQU	10H		; Set/reset print flag
CONTR	EQU	12H		; Retype line
CONTS	EQU	13H		; Stop console output
CONTX	EQU	18H		; Delete line (backspaces)
CONTU	EQU	15H		; Same as Control-X
RUBOUT	EQU	7FH		; Delete last char

MAXEXT	EQU	1FH		; Maximum extent number
MAXMOD	EQU	3FH		; Maximum data module number

TDCKSM	EQU	91H		; CHECKSUM OF !!!TIME&.DAT

; Attribute Bit Definitions

PUBATT	EQU	2		; Public attribute offset
PSFATT	EQU	7		; Public/system file (internal only)
WHLATT	EQU	8		; Wheel protect attribute offset
ROATT	EQU	9		; Read only attribute offset
SYSATT	EQU	10		; System attribute offset
ARCATT	EQU	11		; Archive attribute offset

; FCB POSITION EQUATES

FCBEXT	EQU	12		; Extent number
FCBUSR	EQU	13		; User valid at offset 13 if set (internal)
FCBMOD	EQU	14		; Data module number - D7 used as unmod flag
FCBREC	EQU	15		; Record number
NXTREC	EQU	32		; Next record number
	PAGE
;**************************************************************
;*	 Z S D O S	P r o g r a m	  S t a r t	      *
;**************************************************************

; WARNING!!  Do NOT change labels or sequences of ZSDOS through ZSDOS+25H
;   ID string added for easy identification in running system (hfb)

	  IF  ZS
	DEFB	'ZSDOS '	; Used in CP/M for serial number. these bytes
	  ELSE			; are patched by INSTALOS to contain the serial
	DEFB	'ZDDOS '	; Number of the running system so MOVCPM can
	  ENDIF			; still be used without problems.

; ZSDOS Entry Point

START:	JP	ENTRY		; Jump to start of program code

; CP/M 2.2 Compatable Error Vector Table

STBDSC:	DEFW	ERROR		; Bad sector message
STSEL:	DEFW	ERROR		; Select error
STRO:	DEFW	ERROR		; Drive read only
SFILRO:	DEFW	ERROR		; File read only

; External Path Name

PATH:	DEFW	PATHAD		; Path address for file open, 0 if no path

; Wheel Byte Pointer

WHEEL:	DEFW	WHLADR		; Address of wheel byte, 0 if none

; User configuration byte

FLAGS:	DEFB	FLGBITS		; Flag byte set in zsdos.lib

; Dispatch table for time/date stamp routines

; ZSDOS uses all vectors in this table as indicated.  ZDDOS uses all but
; STUPDV, GETSTV, and PUTSTV.  STCRV is used to store the address of the
; stamp for ZDDOS, thus allowing ZSCONFIG to enable and disable stamping
; of Last Access and Modify.

GSTIME:	DEFW	DOTDER		; Address of get/set time/date routine (hfb)
	  IF	ZS
STLAV:	DEFW	DOTDER		; Address of stamp last access routine
STCRV:	DEFW	DOTDER		; Address of stamp create routine
STUPDV:	DEFW	DOTDER		; Address of stamp modify routine
	  ELSE
STLAV:	DEFW	STIME		; Address of stamp last access routine
STCRV:	DEFW	STIME		; Address of stamp create routine
STUPDV:	DEFW	STIME		; Address of stamp modify routine
	  ENDIF
GETSTV:	DEFW	DOTDER		; Address of get stamp routine
PUTSTV:	DEFW	DOTDER		; Address of set stamp routine
	DEFW	DOTDER		; Dummy vector to disable with ZSCONFIG
UNLOAD:	DEFW	0		; Pointer to remove Time Stamp routine

	PAGE
;********************************************************
;*	 Z S D O S	L o w	 R A M	 D a t a	*
;********************************************************

; RAM has been moved down here to an area that is compatable with ZRDOS per
; suggestion by Hal Bower.  The actual addresses used are NOT compatable with
; ZRDOS.

; Due to ZSDOS's smaller RAM area, any program that saves RAM in accordance
; with ZRDOS's specifications for re-entry into BDOS should work under ZSDOS
; without problems.  Some code will be saved also, as well as the Flag Byte,
; but this should be no problem for IOP'S.

; The Write Protect, Login, and Hard Disk Login Vectors are kept at the top of
; ZSDOS, as they must reflect the current status of the Disk System and hence
; should NOT be saved with other system variables Under ANY Circumstance!

	  IF  ROM
	DSEG
	  ENDIF
BGLORAM:
;--------------------------------------------------------------------
; The following locations MUST remain in EXACTLY this order

TABCNT:	DEFB	0		; Tab counter
TABCX1:	DEFB	0		; Temporary Tab counter (used by RDBUF)
;--------------------------------------------------------------------

FCONTP:	DEFB	0		; List enable flag (Control-P) - used by BGii
LASTCH:	DEFB	0		; Last character - used by BGii

;--------------------------------------------------------------------
; The following locations MUST remain in EXACTLY this order

USER:	DEFB	0		; User number - used by BGii
DEFDRV:	DEFB	0		; Default drive number - used by BGii and DS
DRIVE:	DEFB	0		; Drive number
;--------------------------------------------------------------------

FCB0:	DEFB	0		; FCB byte 0

BGHIRAM:
DMA:	DEFW	0080H		; DMA address

TRANS:	DEFW	0		; Translation vector
TEMP0:	DEFW	0		; Number of files on drive


DIRBUF:	DEFW	0		; Directory buffer pointer - used by bgii
IXP:	DEFW	0		; Disk parameter block
CSV:	DEFW	0		; Check sum pointer
ALV:	DEFW	0		; Allocation vector pointer

;--------------------------------------------------------------------
; The following locations MUST remain in EXACTLY this order
; Copy of DPB for Current Drive

DPBOF	EQU	$-ZSDOS		; Value needed by ZSDOS

MAXSEC:	DEFW	0		; Number of sectors/track
NBLOCK:	DEFB	0		; Block shift
NMASK:	DEFB	0		; Mask number of blocks
NEXTND:	DEFB	0		; Extent mask
MAXLEN:	DEFW	0		; Maximum block number-1
NFILES:	DEFW	0		; Maximum number of files-1
NDIR0:	DEFB	0		; First two entries ALV buffer
	DEFB	0		; ..(NDIR1)
NCHECK:	DEFW	0		; Number of checksum entries
NFTRK:	DEFW	0		; First track number
;--------------------------------------------------------------------
FUNCT:	DEFB	0		; Function number
PEXIT:	DEFW	0		; Exit code
;--------------------------------------------------------------------
; The following locations MUST remain in EXACTLY this order

FLDRV:	DEFB	0		; Drive select used flag
RDWR:	DEFB	0		; Read/write flag
SEARQU:	DEFB	0		; Search question mark used
SEARPU:	DEFB	0		; Search public file
;--------------------------------------------------------------------
RECDIR:	DEFW	0		; Record directory (checksum)
FILCNT:	DEFW	0		; File counter
SECPNT:	DEFB	0		; Sector pointer
SUBFLG:	DEFB	0		; Submit flag (reset disk command)

DCOPY:	DEFW	0		; Copy address FCB
SEAREX:	DEFB	0		; Exit code search
SEARNB:	DEFB	0		; Search number of bytes
ERMODE:	DEFB	0		; BDOS error mode

ARWORD:	DEFW	0		; De argument on entry - used for BGii
DEVAL:	DEFW	0		; Return value for DE reg
SPSAVE:	DEFW	0		; Stack pointer location
	  IF  ZS
	DEFB	'ZSDOS 1.1 Copyri'
	  ELSE
	DEFB	'ZDDOS 1.1 Copyri'
	  ENDIF
	DEFB	'ght (c) 1987,88 '
	DEFB	' C.W.Cotrill & H'
	DEFB	'.F.Bow'
IXSAVE:	DEFB	'er'		; User's IX register
ZSDOSS:				; ZSDOS stack

BGRAMTOP EQU	ZSDOSS
	PAGE
	CSEG
;**********************************************************************
;*		 Z S D O S   e n t r y	 p o i n t		      *
;**********************************************************************

ENTRY:	XOR	A		; Clear A
	LD	B,A		; For later 16 bit adds
	LD	L,A
	LD	H,A		; Set HL to zero
	LD	(PEXIT),HL	; Clear exit code
	LD	(FLDRV),HL	; Reset drive select and R/W flags
	LD	(SPSAVE),SP	; Save stack pointer
	LD	SP,ZSDOSS	; Get internal stack pointer
	PUSH	IX		; Save index register on our stack
	PUSH	DE		; Save parameter register
	POP	IX		; Get it back in IX
	LD	(ARWORD),IX	; Save in memory for BGii
	  IF  NOT PICKEY
	LD	(DEVAL),IX	; ..and for non-file access returns
	  ENDIF
	LD	HL,DOSEXIT	; Get exit address ZSDOS
	PUSH	HL		; Save it on stack to return from ZSDOS
	LD	A,C		; Get function code - B reg = 0
	LD	(FUNCT),A	; Save it for later use
	CP	12		; Is it a non-disk function?
	JR	C,ENTRY0	; ..jump if so
	CP	MAXCMD		; Cmnd < Maximum Command Number (48)?
	JR	C,ENTRY1	; ..jump if disk function

; Extended function scanner for added functions

	CP	98		; Is it less than Cmd98?
	RET	C		; ..return if so
	CP	103+1		; Is it greater than Cmd103?
	RET	NC		; ..quit if so
	SUB	98-MAXCMD	; Rework so 98-->49..103-->54
	LD	C,A		; Save reworked function #
			; ..fall thru to entry0..

; If Non-disk Function (ie Function # less than 12), push the address of
;  the SAVEA routine on the Stack (save A reg as return code).	Saves
;  code in Console Routines, as simple RET can be used in most cases.

ENTRY0:	LD	HL,SAVEA
	PUSH	HL		; Vector return thru A reg save
ENTRY1:	LD	HL,CTABLE	; Load table
	ADD	HL,BC		; Add
	ADD	HL,BC		; Add twice to get word value
	LD	A,(HL)		; Get LSB
	INC	HL		; Pointer to MSB
	LD	H,(HL)		; Get MSB
	LD	L,A		; Save LSB in L

; Copy byte argument into A and C to simplify Function calls.  This allows
;  direct BIOS jumps for several functions with resulting code savings.

	LD	C,E		; Place arg in C for BIOS
	LD	A,E		; And in A for others
	JP	(HL)		; Jump to routine

	PAGE
;******************************************************
;*	 C O M M A N D	    T A B L E		      *
;******************************************************
CTABLE:
	  IF  ROM
	DEFW	RAMINI		; Set up RAM
	  ELSE
	DEFW	ERROR5		; Warm boot (BIOS) with ERMODE clear
	  ENDIF
	DEFW	CMND01		; Console input
	DEFW	WRCON		; Console output
	DEFW	READER		; Reader input (BIOS)
	DEFW	PUNCH		; Punch output (BIOS)
	DEFW	LIST		; List output (BIOS)
	DEFW	CMND06		; Direct console I/O
	DEFW	CMND07		; Get I/O byte
	DEFW	CMND08		; Set I/O byte
	DEFW	CMND09		; Print string
	DEFW	CMND10		; Read console buffer
	DEFW	CMND11		; Get console status
	DEFW	CMND12		; Return version number
	DEFW	CMND13		; Reset disk system
	DEFW	CMND14		; Select disk
	DEFW	CMND15		; Open file
	DEFW	CMND16		; Close file
	DEFW	CMND17		; Search for first
	DEFW	CMND18		; Search for next
	DEFW	CMND19		; Delete file
	DEFW	CMND20		; Read sequential
	DEFW	CMND21		; Write sequential
	DEFW	CMND22		; Make file
	DEFW	CMND23		; Rename file
	DEFW	CMND24		; Return login vector
	DEFW	CMND25		; Return current disk
	DEFW	CMND26		; Set DMA address
	DEFW	CMND27		; Get address allocation vector
	DEFW	CMND28		; Write protect disk
	DEFW	CMND29		; Get R/O vector
	DEFW	CMND30		; Set file attributes
	DEFW	CMND31		; Get address disk parameter header (DPH)
	DEFW	CMND32		; Get/set user code
	DEFW	CMND33		; Read random
	DEFW	CMND34		; Write random
	DEFW	CMND35		; Compute file size
	DEFW	CMND36		; Set random record
	DEFW	CMND37		; Reset multiple drive
	DEFW	DUMMY		; Function 38 (unused)
	DEFW	CMND39		; Return fixed disk login vector
	DEFW	CMND40		; Write random with zero fill
	DEFW	DUMMY		; Function 41 (unused)
	DEFW	DUMMY		; Function 42 (unused)
	DEFW	DUMMY		; Function 43 (unused)
	DEFW	DUMMY		; Function 44 (unused)
	DEFW	CMND45		; Set Error Mode
	DEFW	DUMMY		; Function 46 (unused)
	DEFW	CMND47		; Return DMA
	DEFW	CMND48		; Return DOS version

MAXCMD	EQU	($-CTABLE)/2	; Jww

	DEFW	CMD98		; Get Time	; 49
	DEFW	CMD99		; Set Time	; 50
	DEFW	CMD100		; Get Flags	; 51
	DEFW	CMD101		; Set Flags	; 52
	DEFW	CMD102		; Get Stamp	; 53
	DEFW	CMD103		; Put Stamp	; 54

	PAGE
;******************************************************
;*	 N o n - D i s k     F u n c t i o n s	      *
;******************************************************

	  IF  ROM

; Initialize RAM in Data Segment (ROM Systems Only)

RAMINI:	LD	B,SPSAVE-BGLORAM ; Size of low RAM data segment (less stack)
	XOR	A
	LD	HL,BGLORAM	; Start of RAM
RAMIN1:	LD	(HL),A		; Clear first byte
	INC	HL
	DJNZ	RAMIN1		; And everything else
	  IF  ZS		; Need Internal path if ZSDOS
	LD	HL,IPATH	; Point to start of Internal Path
	LD	B,HDLOG+2-IPATH-1 ; and fill high mem less first byte of path
	LD	(HL),01		; Set path to Drive = A
	INC	HL		; ..point to user and Null (Sets user=0)
	  ELSE			; No Path if ZDDOS
	LD	HL,TDFVCT
	LD	B,HDLOG+2-TDFVCT ; Now high RAM
	  ENDIF		;Zs
RAMIN2:	LD	(HL),A
	INC	HL
	DJNZ	RAMIN2
	LD	HL,RAMLOW+80H	; Default DMA buffer
	LD	(DMA),HL	; And save it
	RST	0		; Now BIOS warm boot
	  ENDIF		; Rom

;.....
; I/O Routines

; ZSDOS Console Input.	Read character from Console and Echo
;  If Char=CR,LF,TAB,CONTH or >=Space

CMND01:	CALL	GETCH		; Get character (and test it  jww)
	RET	C		; Less than space, exit
PUTCH:	PUSH	HL		; Save regs for other calls
	CALL	WRCON		; Echo character
	POP	HL
	RET

; Direct Console Input/Output
;  Call with Char in C and E - Enhanced to CP/M-3 Spec
;  Checks ZSDOS typeahead for reliable console I/O under all conditions
;  as per a suggestion by Bridger Mitchell.

CMND06:	INC	E		; Test if get char if avail
	JR	Z,DCIO1		; Yes do input
	INC	E		; Test for 0FEH
	JR	Z,DCIO2		; Yes, get status
	INC	E		; Test for 0FDH
	JR	Z,GETCH		; Yes, wait for input char
	JP	CONOUT		; Else print char

DCIO2:	LD	A,(LASTCH)	; Check for buffered char
	OR	A
	LD	A,0001B		; ..preset ready
	CALL	Z,CONST		; Get console status
	AND	A		; Test it
	RET			; And return it to caller

DCIO1:	CALL	DCIO2		; Get console status
	RET	Z		; Exit if no character present
				; Else fall thru
; Get Character from Console

GETCH:	LD	HL,LASTCH	; Check ZSDOS type ahead for char
	LD	A,(HL)
	LD	(HL),0		; Reset last character
	OR	A		; ..set flags
	CALL	Z,CONIN		; Get character (and test it  jww)

; Test Character
;  Exit Carry=0: CR,LF,TAB,CONTH or >= Space
;	Carry=1: All other Characters

	CP	CR		; Is it a carriage return?
	RET	Z		; ..return if so
	CP	LF		; Is it a line feed?
	RET	Z		; ..return if so
	CP	TAB		; Is it a tab?
	RET	Z		; ..return if so
	CP	CONTH		; Is it a backspace?
	RET	Z		; ..return if so
	CP	' '		; Test >=space
	RET			; ..and return to caller

; Set I/O Status Byte

CMND08:	LD	(RAMLOW+0003H),A ; And save it in RAM and fall through

; Get I/O Status Byte

CMND07:	LD	A,(RAMLOW+0003H) ; Get I/O byte from RAM
	RET

; Buffered Console Read

CMND10:	LD	A,(TABCNT)
	LD	(TABCX1),A	; Save start tab position
	INC	DE
	XOR	A
	LD	(DE),A		; Set char count to zero
	INC	DE		; Point to actual buffer start

RDBUF1:	PUSH	DE		; Save buffer pointer
	CALL	GETCH		; Get next byte from user
	POP	DE
	LD	HL,RDBUF1
	PUSH	HL		; Return address to stack
	LD	HL,(ARWORD)
	LD	C,(HL)		; Put buffer length in C
	INC	HL		; And point to current length

	CP	CR
	  IF  CTLREN
	JR	Z,JZRBX		; Exit if CR
	  ELSE
	JR	Z,RDBUFX
	  ENDIF		;Ctlren

	CP	LF
	  IF  CTLREN
JZRBX:	JP	Z,RDBUFX	; ..or LF
	  ELSE
	JR	Z,RDBUFX
	  ENDIF		;Ctlren
			;..Not CR or LF, so fall thru to next test

; Delete Character from Buffer
;  RUB, Backspace, CR, LF are NEVER in the Buffer

RDBUF2:	CP	RUBOUT		; Delete char?
	JR	Z,DOBACK	; ..jump if so
	CP	CONTH		; Control-H also deletes
	JR	NZ,RDBUF3	; Skip to next test if no delete

DOBACK:	LD	A,(HL)
	AND	A		; Test if attempting del from empty line
	RET	Z		; ..Exit if so
DOBAK0:	DEC	DE		; Back up to last character
	DEC	(HL)		; Erase from buffer
	PUSH	DE		; Save buffer pointer
	LD	B,(HL)		; Get new char count
	INC	HL		; Point to first char
	EX	DE,HL
	LD	HL,TABCNT
	LD	C,(HL)		; Save current Tab count
	INC	HL
	LD	A,(HL)		; Get starting Tab position
	DEC	HL
	LD	(HL),A		; Init the counter
	INC	B		; Insure non-zero
	JR	DOBAK2		; Jump to done test

DOBAK1:	LD	A,(DE)		; Get char from buffer
	CALL	WRCON2		; Counts chars
	INC	DE
DOBAK2:	DJNZ	DOBAK1		; Continue count until done
	LD	A,C		; Get prior tab count
	SUB	(HL)		; Get diff between new and old
	LD	B,A		; Set up as count
	LD	(HL),C		; Restore prior count
	POP	DE		; Restore buffer pointer

; Delete B Characters from Console

	PUSH	DE		; Save pointer
DOBAK5:	LD	C,CONTH
	PUSH	BC		; Save counter from destruction
	CALL	CONOUT
	LD	C,' '
	CALL	CONOUT		; Output backspace,space to CON: only
	LD	A,CONTH
	CALL	WRCON		; Now backspace CON:, counter, and printer
	POP	BC		; Restore counter
	DJNZ	DOBAK5		; Loop until all done
	POP	DE		; Restore pointer
	RET

; Erase Buffer

RDBUF3:	CP	CONTU		; Test erase line
	JR	Z,ERALIN	; Do it if so
	CP	CONTX
	JR	NZ,RDBUF4	; Skip to next test if no erase line

ERALIN:	XOR	A
	OR	(HL)		; Line empty?
	RET	Z		; Exit if so
	PUSH	HL
	CALL	DOBAK0		; Else delete another (skip empty check)
	POP	HL
	JR	ERALIN

RDBUF4:			; If CTL-R=True, do following code, else bypass
	  IF  CTLREN
	CP	CONTR		; If ^R, type clean buffer version on console
	JR	NZ,RDBUF5
	PUSH	HL		; Save pointer to buffer length
	CALL	CROUT		; Do CR/LF
	LD	HL,TABCNT
	LD	(HL),0		; Init Tab count
	INC	HL
	LD	B,(HL)		; And get Tab offset count
	LD	A,' '
	inc	b		; [1.1] insure nz value
	jr	rety1a		; [1.1] so case of lh side of screen ok
RETYP1:	CALL	WRCON		; Space off start of line
rety1a:	DJNZ	RETYP1
	POP	HL		; Point to buffer length
	LD	B,(HL)		; Get how many chars to print
	INC	HL		; Restore buffer pointer
	EX	DE,HL		; Put buffer pointer in DE
	INC	B		; Comp for first DJNZ
	JR	RETYP3		; Skip to done test
RETYP2:	LD	A,(DE)		; Get char from buffer
	CALL	WRCTL		; Output it
	INC	DE		; Bump pointer
RETYP3:	DJNZ	RETYP2		; Loop until done
	RET
	  ENDIF		; Ctlren

; Toggle Line Printer Echo

RDBUF5:	CP	CONTP		; Toggle printer?
	JR	NZ,RDBUF6	; Next test if not
	LD	HL,FCONTP
	LD	A,(HL)		; Get printer echo flag
	CPL			; Toggle it
	LD	(HL),A		; Put back
	RET

; Check if Control-C is First char in BUFF and Exit if so

RDBUF6:	LD	(DE),A		; Put character in buffer
	PUSH	HL
	CALL	WRCTL		; Echo the character
	POP	HL
	INC	(HL)		; Increment the character count

	LD	A,(HL)		; Get current length
	CP	C		; Test against buffer size
	JR	Z,RDBUFX
	DEC	A		; Set Z flag for first character
	LD	A,(DE)		; Get the character back
	INC	DE		; ..and bump the pointer
	RET	NZ		; Return if not the first character
	CP	CONTC		; Possible user abort?
	RET	NZ		; ..return if not
	JP	ERROR5		; Else jump to error reset exit

; Done with Read Console Buffer Function

RDBUFX:	POP	HL		; Clear RDBUF1 return address
	LD	A,CR
	JR	WRCON		; ..and echo a CR

; Print Control Character as '^X'

WRCTL:	CP	' '		; Test if control char
	JR	NC,WRCON	; Not, send it out
	CP	TAB		; Test if Tab
	JR	Z,WRCON0	; It is, so expand with spaces
	PUSH	AF		; Save char
	LD	A,'^'		; Output a karet
	CALL	WRCON1		; No need for Tab test here
	POP	AF
	ADD	A,40H		; Convert to printable
				; And fall thru to WRCON

; Output char with List Echo, Tab Expansion (Function 2)

WRCON:	CP	TAB		; Is it a Tab?
	JR	NZ,WRCON1	; ..jump if not
WRCON0:	LD	A,' '		; Expand Tab with spaces
	CALL	WRCON1		; Write space
	LD	A,(TABCNT)	; Get Tab count
	AND	7		; Test if done
	JR	NZ,WRCON0	; No then repeat
	LD	A,TAB		; Return Tab
	RET			; Return to caller

WRCON1:	PUSH	BC
	PUSH	DE		; Save pointers
	LD	C,A
	PUSH	BC		; Save character

BGPTCH0	EQU	$+1		;<-- BGii patches this address

	CALL	CMND11		; Test status and CONTS/CONTC
	POP	BC		; Get character back
	PUSH	BC		; Save it again
	CALL	CONOUT		; Output it
	POP	BC		; Get character back
	PUSH	BC		; Save it again
	LD	A,(FCONTP)	; Get printer echo flag
	OR	A		; Test it
	CALL	NZ,LIST		; Non zero => output char to printer
	POP	BC		; Restore character
	LD	A,C		; Fall through to count routine
	POP	DE
	POP	BC		; Restore pointers

; Count Characters in line as shown by f10

	LD	HL,TABCNT	; Get pointer to Tab counter
WRCON2:	INC	(HL)		; Increment Tab counter
	CP	RUBOUT		; Test if character = Rubout
	JR	Z,WRCON3	; Treat like Backspace
	CP	' '
	RET	NC		; Ok if not Control
	CP	TAB		; Only DOBACK ever gets Tabs through here
	JR	Z,WRCON4	; Handle differently if Tab
	CP	CONTH
	JR	Z,WRCON3	; Or Backspace
	INC	(HL)		; Must have been echoed as two chars
	CP	LF
	JR	Z,WRCON3	; ..unless it's LF
	CP	CR		; ..or CR
	RET	NZ
	LD	(HL),2		; Reset Tab count
WRCON3:	DEC	(HL)		; Decrement Tab counter
	DEC	(HL)
	RET			; And exit

WRCON4:	LD	A,7		; Bumped by one already
	ADD	A,(HL)		; Tabs are every 8 spaces
	AND	0F8H		; ...mod 8
	LD	(HL),A		; Save updated Tab count
	RET			; ..and continue

; Get Console Status - BGII uses this routine

BGCONST:
CMND11:	CALL	DCIO2		; Get character present status
	RET	Z		; ..exit if none
	CALL	GETCH		; Get next console char
	CP	CONTS		; Is it stop char?
	JR	NZ,GCONS2	; ..jump if Not
	CALL	CONIN		; Get next character
	CP	CONTC		; Does the user want to exit (^C)?
	JR	NZ,CMND11	; ..check for another character if not
	JP	ERROR5		; Else jump to warm boot & clear ERMODE

GCONS2:	LD	(LASTCH),A	; Save character
	LD	A,1		; Character present code
	RET			; Return to caller

; Echo CR,LF

CROUT:	LD	DE,MCRLF	; Fall through to output routine

; Output Message

CMND09:	LD	A,(DE)		; Get byte from buffer
	CP	'$'		; Test last byte
	RET	Z		; Yes, then return to caller
	INC	DE		; Point to next byte
	CALL	WRCON		; Output character
	JR	CMND09		; And test again

	PAGE
;**********************************************
;*	 E r r o r	R o u t i n e s       *
;**********************************************

PRDEC:	LD	BC,100
	CALL	NUM
	LD	C,10
	CALL	NUM
	LD	BC,101H

; Display Number

NUM:	LD	D,-1		; Load number -1
NUM1:	INC	D		; Increment number
	SUB	C		; Divide by C
	JR	NC,NUM1		; Not finished then loop
	ADD	A,C		; Restore last value
	PUSH	AF		; Save it
	LD	A,D		; Test if "0"
	OR	B		; And if leading zero
	JR	Z,NUM2		; Yes, then exit
	LD	B,A		; Set no leading zero
	LD	A,D		; Get number
	ADD	A,'0'		; Make ASCII
	CALL	PUTCH		; Echo number preserving BC
NUM2:	POP	AF		; Restore number
	RET			; And exit

; Error Messages

MDSKCH:	DEFB	'Changed$'

MBADSC:	DEFB	'Bad Sector$'

MSEL:	DEFB	'No Drive$'

MFILRO:	DEFB	'File '

MRO:	DEFB	'W/P$'
	 IF ZS
MBERR:	DEFB	'ZSDOS'
	 ELSE
MBERR:	DEFB	'ZDDOS'
	 ENDIF
	DEFB	' error on $'
	 
MBFUNC:	DEFB	CR,LF,'Call'
MDRIVE:	DEFB	': $'

MFILE:	DEFB	'  File: $'

MCRLF:	DEFB	CR,LF,'$'

; New ZSDOS error handler - enter w/ error code in B and message pointer
; in DE

ERROR:	LD	A,(ERMODE)
	LD	C,A		; Save error mode
	RRCA			; Test supress print
	JR	C,ERROR3	; Suppressed, so skip dsp

; Print ZSDOS Error on X: Explanation

	PUSH	BC
	PUSH	DE		; Save params
	CALL	CROUT		; Output CR/LF
	LD	DE,MBERR
	CALL	CMND09		; Output ZSDOS error on
	LD	A,(DEFDRV)	; Get current default drive
	ADD	A,'A'		; Convert to ascii
	CALL	WRCON		; Output it to console
	LD	DE,MDRIVE	; Point to drive tag
	CALL	CMND09		; Put it also
	POP	DE		; Restore error message pointer
	CALL	CMND09		; Send message

; Now print CALL: XXX [FILE: XXXXXXXX.XXX]

	LD	DE,MBFUNC
	CALL	CMND09		; Display 'call: '
	LD	A,(FUNCT)	; Get function number
	CALL	PRDEC		; Output it
	LD	A,(FLDRV)
	AND	A		; Was FCB used?
	JR	Z,ERROR2	; ..Skip file name display if not
	POP	BC
	PUSH	BC		; Get error type
	PUSH	IX		; Save FCB pointer
	LD	A,(FUNCT)	; ARE WE ERASING A FILE?
	CP	19		; IF SO, GET NAME FROM DIRBUF AS
	JR	NZ,ERROR0	; AMBIG NAME MAY HAVE BEEN USED
	CALL	CALDIR		; Get DIR buffer pointer
	EX	(SP),HL		; To show what we really gagged on
ERROR0:	LD	DE,MFILE
	CALL	CMND09		; Output 'file: '
	POP	HL		; Point to FCB
	LD	B,11		; Output this many chars
ERROR1:	INC	HL
	LD	A,3
	CP	B		; Time to send '.'?
	LD	A,'.'		; Get ready for it
	CALL	Z,PUTCH		; Send it if time
	LD	A,(HL)		; Get char
	AND	7FH		; Mask attributes
	CALL	PUTCH		; Output it
	DJNZ	ERROR1
ERROR2:	CALL	CROUT		; Send CR,LF
	POP	BC		; Get error mode back
ERROR3:	LD	A,4
	SUB	B		; Test if select error
	JR	NZ,ERROR4	; Skip if not
	ld	hl,drive	; point to old default
	ld	a,(hl)		; get it
	dec	hl		; point to bad drive
	cp	(hl)		; same?
	jr	z,error4	; if so, skip relog
	PUSH	BC
	CALL	SELDK		; Get BIOS back in step
	POP	BC
ERROR4:	BIT	1,C		; Test if return error mode
	JR	NZ,ERROR7	; Go if return error
	LD	A,1
	SUB	B		; Test if fatal error
	JR	NC,ERROR6	; If not a fatal error
ERROR5:	XOR	A
	LD	(ERMODE),A	; Set DOS error mode to default CP/M
	RST	0		; ..and leave

ERROR6:	CALL	DCIO1		; Get console char if present
	AND	A		; Test if any
	JR	NZ,ERROR6	; Keep getting them until typeahead eaten
	CALL	GETCH		; Now get operator's response
	CP	CONTC		; Test if abort
	RET	NZ		; If operator said ignore error
	JR	ERROR5		; Else boot

ERROR7:	LD	A,B		; Get error
	LD	H,A		; Save code in H reg for return
	AND	A		; Test if disk changed warning
	RET	Z		; Continue relog if so
	LD	L,0FFH		; Set extended error code
	LD	(PEXIT),HL	; Save as return code
			; ..and fall thru to DOS exit
	PAGE
;******************************************************
;*	 D O S	   E x i t    R o u t i n e	      *
;******************************************************

DOSEXIT: LD	A,(FLDRV)	; Test drive select used flag
	OR	A
	JR	Z,DOSEXT0	; No then exit
	LD	A,(FCB0)	; Get FCB byte 0
	LD	(IX+0),A	; Save it
	LD	A,(DRIVE)	; Get old drive number
	CALL	SELDK		; Select disk
	  IF  PICKEY
	LD	DE,(DEVAL)	; And DE reg for datestamper
	  ENDIF

; If the error handler was invoked, the stack is in an undefined
;  condition at this point.  We therefore have to restore the user's
;  IX register independent of stack position.  Thanks to Joe Wright's
;  eagle eye for catching this one!

DOSEXT0: LD	SP,(SPSAVE)	; Restore user stack
	LD	IX,(IXSAVE)	; Restore IX (stack is don't care)
	LD	HL,(PEXIT)	; Get exit code
	  IF  NOT PICKEY
	LD	DE,(DEVAL)	; And DE reg for DateStamper
	  ENDIF
	LD	A,L		; Copy function code
	LD	B,H
	RET			; And return to caller
	PAGE
;******************************************************
;*	 D i s k     F u n c t i o n s		      *
;******************************************************

; Reset Disk System

CMND13:	LD	HL,RAMLOW+0080H	; Set up DMA address
	LD	(DMA),HL	; And save it
	CALL	STDMA		; Do BIOS call
	XOR	A		; Set default drive = 'A'
	LD	(DEFDRV),A	; Save it
	LD	DE,0FFFFH	; Reset all drives

; Reset Multiple Login Drive - DE = Reset mask
; Fixed Disk Login vector is also altered by this call

CMND37:	CALL	UNLOG		; Clear selected drives in DE from login
	LD	A,(FLAGS)
	BIT	2,A		; Test hard R/O enabled
	JR	NZ,UNWPT1	; If enabled
	LD	HL,DSKWP	; Get drive W/P vector
	CALL	ANDDEM		; Reset W/P stat only of requested drvs
UNWPT1:	LD	A,(FUNCT)
	CP	13		; Skip hard disk login change?
	LD	HL,HDLOG
	CALL	NZ,ANDDEM	; Clear HD Login Vector if Fcn 37
RELOG1:
	  IF  ZS
	LD	HL,(HDLOG)
	CALL	HLORDE		; Don't clear fixed disks from T/D
	EX	DE,HL		; Place modified logout in DE
	LD	HL,TDFVCT
	CALL	ANDDEM		; Clear T/D vector as needed
	  ENDIF

	LD	A,(DEFDRV)	; Get default drive
	PUSH	AF
RELOG2:
	  IF  RESDSK		; (bh)
	CALL	SETDSK		; Allow BIOS to detect density change (bh)
	  ELSE
	DEFB	0,0,0		; Make 3 NOP's to keep constant code (hfb)
	  ENDIF			; (bh)
	POP	AF
	CALL	SELDK		; Select default drive

; ZSDOS watches for any $*.* in any user on any drive during re-log,
; make, and delete.  In this manner, SUBFLG will always be valid -
; even under fast relog and NZCOM!  Thanks to Joe Wright for suggesting
; the need for this, and suggesting ways to do it.

SUBEXT:	LD	A,(SUBFLG)	; Get submit flag
	JR	SAVEA		; Exit

; Check for possible existance of submit file by checking first
; byte of dir entry or FCB for '$'.  Pointer to dir or FCB passed
; to routine in HL.

CKSUB:	INC	HL		; Point to file name
	LD	A,(HL)		; Get first char filename
	DEC	HL
	SUB	'$'		; Test if '$'
	RET	NZ		; Not then exit
	DEC	A		; Load a with 0FFH
	LD	(SUBFLG),A	; Save it in subflg
	RET

; Unlog Drive mask in DE

UNLOG:	LD	A,E		; Get LSB
	CPL			; Complement it
	LD	E,A
	LD	A,D		; Get MSB
	CPL			; Complement it
	LD	D,A		; DE = not reset
	LD	HL,LOGIN	; Get addr of login vector
ANDDEM:	LD	A,E		; Clear login bits of reset drives
	AND	(HL)		; ..a byte at a time
	LD	(HL),A		; Put to memory
	INC	HL
	LD	A,D
	AND	(HL)
	LD	(HL),A
	RET

; Search for File

CMND17:	CALL	SELDRV		; Select drive from FCB
	LD	A,(IX+0)
	SUB	'?'		; Test if '?'
	JR	Z,CMD17B	; If so all entries match
	LD	A,(IX+FCBMOD)	; Get system byte
	CP	'?'		; Test if '?'
	JR	Z,CMD17A	; Yes, jump
	LD	(IX+FCBMOD),0	; Load system byte with Zero
CMD17A:	LD	A,15		; Test first 15 items in FCB
CMD17B:	CALL	SEARCH		; Do search
CMD17C:	LD	HL,(DIRBUF)	; Copy directory buffer
	LD	BC,128		; Directory=128 bytes
MV2DMA:	LD	DE,(DMA)	; To DMA address
	LDIR
	RET			; Exit

; Search for Next Occurence of File

CMND18:	LD	IX,(DCOPY)	; Get last FCB used by search
	LD	(ARWORD),IX	; Save FCB pointer for BGii
	CALL	SELDRV		; Select drive from FCB
	CALL	SEARCN		; Search next file match
	JR	CMD17C		; And copy directory to DMA address

; Delete File

CMND19:	CALL	SELDRV		; Select drive from FCB
	CALL	DELETE		; Delete file
CMD19A:	LD	A,(SEAREX)	; Get exit byte 00=file found, 0FFH=Not
	JR	SAVEA		; And exit

; Rename File

CMND23:	CALL	SELDRV		; Select drive from FCB
	CALL	RENAM		; Rename file
	JR	CMD19A		; And exit

; Return Current Drive

CMND25:	LD	A,(DEFDRV)	; Get current drive
SAVEA:	LD	(PEXIT),A	; Return character
DUMMY:	RET			; ..and exit ZSDOS

; Set flags

CMD101:	LD	(FLAGS),A	; Set ZSDOS flags
				; ..and fall thru
; Get flags

CMD100:	LD	A,(FLAGS)	; Get ZSDOS flags
	JR	SAVEA		; ..and exit

; Change Status

CMND30:	CALL	SELDRV		; Select drive from FCB
	CALL	CSTAT		; Change status
	JR	CMD19A		; And exit

; Return CP/M Version Number

ZDPCH1:
CMND12:	LD	HL,22H		; Set CP/M compatable version number
	  IF  NOT ZS		; (crw)
	CP	'D'		; IS Caller testing for DS?
	JR	NZ,SAVHL	; ..exit if Not
	LD	A,(UNLOAD+1)	; See if Clock was installed by testing
				; ..MSB of Remove vector
	AND	A		; ..if it's zero, then No Clock
	JR	Z,SAVHL		; ..and No DateStamper
	LD	H,E		; Otherwise, return DS Active Flag
	LD	DE,CMD98A	; Have a clock, so get Clock Address
	  ENDIF
	  IF  NOT PICKEY
	LD	(DEVAL),DE	; In case DS gave us a clock addr
	  ENDIF
	JR	SAVHL		; For speed

; Following commands return status in like manner and are consolidated here
; in selected order with least-accessed commands taking longest to traverse
; string, and frequently accessed/time critical exitting quickest.

; The code in this section is a bit obscure, as it depends on burying
; instructions within other instructions.  6502 users have long used the
; 'BIT' trick to skip instructions - this inspired me to see if similar
; things could be done with the Z80.  Indeed they can, as this demonstrates.
; When the Z80 jumps in at a label, it executes the LD HL instruction.	The
; DEFB 0DDH turns the LD HL instructions that follow into LD IX.  In effect,
; this turns the DEFB 0DDH into a one byte relative jump to SAVHL.  As IX
; is never used by these calls, its loss is of no consequence.
; A similar trick is used in SEAR15, resulting in a useless LD HL but
; saving a byte.

; New Universal Return Version FUNCTION 48

CMND48:
	  IF  ZS
	LD	HL,'S' SHL 8 + VERS ;"S" indicates ZSDOS - ZRDOS returns 0
	  ELSE
	LD	HL,'D' SHL 8 + VERS ;"D" indicates ZDDOS - ZRDOS returns 0
	  ENDIF
	DEFB	0DDH		; Trash IX and fall through

; Return Disk W/P Vector

CMND29:	LD	HL,(DSKWP)	; Get disk W/P vector
	DEFB	0DDH		; Trash IX and fall through

; Return Fixed Disk Login Vector

CMND39:	LD	HL,(HDLOG)	; Return fixed disk login vector
	DEFB	0DDH		; Trash IX and fall through

; Return ALV Vector

CMND27:	LD	HL,(ALV)	; Get allocation vector
	DEFB	0DDH		; Trash IX and fall through

; Return Login Vector

CMND24:	LD	HL,(LOGIN)	; Get login vector
	DEFB	0DDH		; Trash IX and fall through

; Return Drive Table

CMND31:	LD	HL,(IXP)	; Get drive table
	DEFB	0DDH		; Trash IX and fall through

; Return Current DMA

CMND47:	LD	HL,(DMA)	; Return current DMA addr
SAVHL:	LD	(PEXIT),HL	; Save it
	RET			; And exit

; Set BDOS Error Mode

CMND45:	LD	(ERMODE),A	; Save error mode
	RET			; And exit

; Set/Get User Code

CMND32:	LD	HL,USER		; Point to user byte location
	INC	A		; Test if 0FFH
	LD	A,(HL)		; Get old user code
	JR	Z,SAVEA		; If 0FFH then exit
	LD	A,E		; Get new user code
	AND	01FH		; Mask it
	LD	(HL),A		; Save it
	RET			; And exit

; Compute File Size Command

CMND35:	CALL	SELDR1		; Select drive from FCB
	CALL	FILSZ		; Compute file size
	JR	CMD19A		; And exit

; Set Random Record Count

CMND36:	LD	HL,32		; Set pointer to next record
	CALL	CALRRC		; Calculate random record count
LDRRC:	LD	(IX+33),D	; And save random record count
	LD	(IX+34),C
	LD	(IX+35),B
	RET			; And exit

; Select Disk From FCB

BGSELDRV:
SELDRV:	LD	A,(ERMODE)	; Are we in modified user mode?
	AND	A
	JR	NZ,SELDR1	; Jump if so, else..
	LD	HL,(ARWORD)	;
	LD	BC,FCBUSR	; Point to user number
	ADD	HL,BC		;
	LD	(HL),A		; Clear user flag
SELDR1:	LD	A,0FFH		; Set disk select done flag
	LD	(FLDRV),A
	LD	A,(DEFDRV)	; Get current drive
	LD	E,A		; Save it in register E
	LD	HL,(ARWORD)
	LD	A,(HL)		; Get drive from FCB
	LD	(FCB0),A	; Save it
	CP	'?'		; Test if '?'
	JR	Z,CMND14	; Yes, then select drive from register E
	PUSH	IX		; Save BGii's IX register
				; IX won't be altered on cmnd14
	LD	IX,(ARWORD)	; Get FCB pointer
;1.1a Changed to allow proper access to Drive P:
;1.2a	AND	0FH		; Mask drive
	AND	1FH		;1.2a Mask Drive
	PUSH	HL
	JR	Z,SELDR0	; Select drive from register E
	LD	E,(HL)		; Get drive from FCB
	DEC	E		; Decrement drive number so A=0
SELDR0:	CALL	CMND14		; - do select of drive
	POP	HL		; Restore FCB pointer

; Resolve User for FCB - FCBPTR in IX, Returns User in A

	LD	A,(IX+FCBUSR)	; ..get potential user in case
	BIT	7,A		; Is this a valid user?
	JR	NZ,RESUS1	; Skip if there is
	LD	A,(USER)	; Get user number
	JR	RESUS1		; ..and bypass push IX

; Set User in FCB to Value passed in A

RESUSR:	PUSH	IX		; Preserve IX
RESUS1:	LD	IX,(ARWORD)
	AND	1FH		; User number in A
	LD	(IX+0),A	; Save in FCB 0 byte
	OR	80H		; Set valid DOS user flag
	LD	(IX+FCBUSR),A	; ..and in FCB 13 byte
	POP	IX		; Restore caller's IX
	RET

; Select Disk Error Exit - The stack is off by one level here, but
;  this is a one way trip anyway.

SELDK3:	LD	HL,(STSEL)	; Load error message address
	LD	B,4		; Select error
	LD	DE,MSEL		; Load select error message
	JP	(HL)		; And display error

; Select Disk from E register

CMND14:	LD	A,(DEFDRV)	; Get current drive
	LD	(DRIVE),A	; Save it in memory
	LD	A,E		; Copy drive number

; Select Disk
;  Call w/ A = Drive Number (0..15 = A..P)

SELDK:	LD	HL,(LOGIN)	; Get login vector
	AND	0FH		; Mask drive number
	LD	B,A		; Save counter
	CALL	NZ,SHRHLB	; ..and rotate into position
SELDK0:	EX	DE,HL		; Put drive bit mask in DE
	LD	HL,DEFDRV	; Get pointer last drive
	BIT	0,E		; Test if drive logged in
	JR	Z,SELDK2	; No, login drive
	CP	(HL)		; Test same drive
	RET	Z		; Yes then exit

; NOTE: A long standing DOS bug concerns the SELECT function.  If a
;  function 14 call is made and the drive doesn't exist, the default
;  will still point to the bad drive unless we fix it in the error
;  routine.  It is for this reason that drive is saved above.  We must
;  allow default to assume the illegal drive value long enough for the
;  error handler to print it, then re-select the old default.

SELDK2:	LD	(HL),A		; Save new current drive
	PUSH	DE		; Save drive logged in flag
	LD	C,A		; Copy drive number
	CALL	SELDSK		; Do BIOS select
	LD	A,H		; Test if error
	OR	L
	JR	Z,SELDK3	; Yes, illegal drive number
	LD	DE,TRANS	; Point to local translation store
	LD	BC,2		; ..and move 2-byte ptr in
	LDIR
	LD	(TEMP0),HL	; Save address temp0
	LD	C,6		; Advance to dirbuf part of DPH
	ADD	HL,BC		; As TEMP1 and TEMP2 unused in P?DOS
	LD	DE,DIRBUF	; Load DIRBUF pointer
	LD	C,8		; Copy 8 bytes
	LDIR
	LD	HL,(IXP)	; Get drive parameter address
	LD	C,15		; Copy 15 bytes
	LDIR
	POP	DE		; Get drive logged in flag
	BIT	0,E		; Test it
	RET	NZ		; Drive logged in so return
	CALL	GETCDM
	EX	DE,HL		; Drive mask in DE
	LD	HL,(LOGIN)	; Get login vector
	CALL	HLORDE		; Set drive bit in login vector
	LD	(LOGIN),HL	; Save login vector
	LD	A,(FLAGS)	; Get flags
	BIT	3,A		; Fast relog enabled?
	JR	Z,INITDR	; Skip if disabled

; The following code checks the WACD size to determine if the drive
;  being selected is a fixed disk.  If the WACD size is 0, the disk
;  is Non-Removable.  However, several BIOSes support remapping of
;  logical drives.  This complicates matters because BDOS must catch
;  the swap and clear the Hard Disk Allocation Vector and allow the
;  allocation bitmaps to be rebuilt.  Thus, every disk that is being
;  selected for the first time traverses this code.  If a disk was
;  logged as a fixed disk and all of the sudden has a WACD buffer,
;  the Fixed Disk Login Vector is cleared.  Thus, for Bug-free
;  operation of Fast Fixed Disk Logging, if drives are swapped
;  NEVER SWAP TWO FIXED DRIVES!

	LD	HL,(NCHECK)	; Is this a fixed drive?
	LD	A,H
	OR	L
	LD	C,A		; Save fixed disk flag (Z=true)
	LD	HL,(HDLOG)
	LD	A,E		; See if logged as fixed disk
	AND	L
	LD	L,A
	LD	A,D
	AND	H		; MSB
	OR	L		; Z flag set if HL and DE = 0
	LD	A,0FFH		; Don't alter flags
	JR	Z,SELDK4	; If not logged as fixed disk
	INC	A		; Else flag as logged
SELDK4:	LD	B,A		; Save logged as fixed disk flag (Z=true)
	OR	C		; Test if still fixed disk
	RET	Z		; Skip re-map if logged and not swapped
	XOR	A
	LD	H,A
	LD	L,A		; Null vector
	OR	B		; Was it logged as a fixed disk?
	JR	Z,SELDK5	; Invalidate HDLOG vector - drive no longer
				; Fixed disk
	LD	A,C
	OR	A		; Wasn't fixed disk before - is it now?
	JR	NZ,INITDR	; Skip vector update if it isn't
	LD	HL,(HDLOG)
	CALL	HLORDE		; Else add this drive to fixed disk vector
SELDK5:	LD	(HDLOG),HL	; Update fixed disk vector
			;..fall thru to INITDR

; Init Drive
;  Clear ALV Bit Buffer after Drive reset

INITDR:	LD	HL,(MAXLEN)	; Get length ALV buffer-1 (bits)
	CALL	SHRHL3		; Divide by 8 to get bytes
	LD	B,H
	LD	C,L		; Counter to BC (will be count+1 cleared)
	LD	HL,(ALV)	; Get pointer ALV buffer
	PUSH	HL
	LD	D,H
	LD	E,L
	INC	DE		; ALV buffer +1 in DE
	XOR	A
	LD	(HL),A		; Clear first 8 bits
	LDIR			; And remainder of buffer
	POP	HL		; Get ALV pointer
	LD	DE,(NDIR0)	; Get first two bytes ALV buffer
	LD	(HL),E		; Save LSB
	INC	HL		; Increment pointer
	LD	(HL),D		; Save MSB
	LD	HL,(TEMP0)	; Clear number of files on this drive
	LD	(HL),A		; Clear LSB (A still has 0)
	INC	HL		; Increment pointer
	LD	(HL),A		; Clear MSB

ZDPCH2	EQU	$		;<-- Intercept first scan (ZDS Patch)
	CALL	SETFCT		; Set file count
INITD2:	LD	A,0FFH		; Update directory checksum
	CALL	RDDIR		; Read FCB's from directory
	CALL	TSTFCT		; Test last FCB
	JP	Z,SUBEXT	; Return subflg for strict CP/M compat (hfb)
	CALL	CALDIR		; Calculate entry point FCB
	LD	A,(HL)		; Get first byte FCB
	CP	0E5H		; Test empty directory entry
	JR	Z,INITD2	; Yes then get next FCB
	CP	021H		; Test time stamp
	JR	Z,INITD2	; Yes then get next FCB

ZDPCH3	EQU	$		;<-- Test for T&D if first time (ZDS Patch)
	CALL	CKSUB		; Test for submit file
	LD	C,1		; Set bit in ALV buffer
	CALL	FILLBB		; Set bits from FCB in ALV buffer
	CALL	TSTLF		; Test for last file
	CALL	NC,SETLF0	; ..and update the last file count if so
	JR	INITD2		; And get next FCB

; Return Mask for Current Drive in HL

GETCDM:	LD	HL,0		; No drives to Or

; Set Drive bit in HL

SDRVB:	EX	DE,HL		; Copy HL=>DE
	LD	HL,1		; Get mask drive "A"
	LD	A,(DEFDRV)	; Get current drive
	OR	A		; Test if drive "A"
	JR	Z,HLORDE	; Yes then done
SDRVB0:	ADD	HL,HL		; Get next mask
	DEC	A		; Decrement drive counter
	JR	NZ,SDRVB0	; And test if done
HLORDE:	LD	A,D		; HL=HL or DE
	OR	H
	LD	H,A
	LD	A,E
	OR	L
	LD	L,A
	RET			; Exit

SHRHL3:	LD	B,3		; Used in a few places

; Shift HL right logical B bits

SHRHLB:	SRL	H
	RR	L		; Shift HL right one bit (divide by 2)
	DJNZ	SHRHLB
	RET

; Calculate Sector/Track Directory

STDIR:	LD	HL,(FILCNT)	; Get FCB counter directory
	  IF  UNROLL
	SRL	H
	RR	L
	SRL	H		; (net cost: 3)
	RR	L		; Divide by 4 (inline for speed)
	  ELSE
	LD	B,2
	CALL	SHRHLB		; Divide by 4
	  ENDIF
	LD	(RECDIR),HL	; Save value (used by checksum)
STDIR2:	EX	DE,HL		; Copy it to DE
STDIR1:	LD	HL,0		; Clear HL

; Calculate Sector/Track
;  Entry: HL,DE=Sector Number (128 byte sector)
;  Result Set Track  =HL,DE  /	MAXSEC
;	  Set Sector =HL,DE MOD MAXSEC

CALST:	LD	BC,(MAXSEC)	; Get sectors/track
	LD	A,17		; Set up loop counter
CALST0:	OR	A
	SBC	HL,BC		; HL > BC?
	CCF
	JR	C,CALST1	; Yes then jump
	ADD	HL,BC		; No then restore HL
	OR	A		; Clear Carry
CALST1:	RL	E		; Shift result in DE
	RL	D
	DEC	A		; Test last bit done
	JR	Z,CALST2	; Yes then exit
	ADC	HL,HL		; Shift next bit in HL
	JR	CALST0		; Continue

CALST2:	PUSH	HL		; Save sector number
	LD	HL,(NFTRK)	; Get first track
	ADD	HL,DE		; Add track number
	LD	B,H		; Copy it to BC
	LD	C,L
	CALL	SETTRK		; CBIOS call Set Track
	POP	BC		; Restore sector number
	LD	DE,(TRANS)	; Get translation table address
	CALL	SECTRN		; CBIOS call sector translation
	LD	B,H		; Copy result to BC
	LD	C,L
	JP	SETSEC		; BIOS call Set Sector

; Get Disk Map Block Number from FCB   (Squeezed by Joe Wright)
;  Exit HL=Address FCB
;	DE=DM
;	BC=Offset in DM
;	Zero Flag Set (Z) if DM=0, Else reset (NZ)

GETDM:	LD	L,(IX+NXTREC)	; Get record number in L
	RL	L		; Shift it left once
	LD	A,(NEXTND)	; Get EXM
	AND	(IX+FCBEXT)	; And the extent number
	LD	H,A		; To H
	LD	A,(NBLOCK)	; Get BSH
	LD	B,A		; To B
	INC	B		; +1
	CALL	SHRHLB		; Shift HL right B times
	LD	D,B		; Zero to D
	LD	A,L		; Result to A

GETDM4:	LD	HL,(ARWORD)
	LD	C,16		; Add offset 16 to point to DM
	ADD	HL,BC
	LD	C,A		; Add entry FCB
	ADD	HL,BC
	LD	A,(MAXLEN+1)	; Test 8 bits/16 bits FCB entry
	OR	A
	LD	E,(HL)		; Get 8 bit value
	JR	Z,GETDMX	; ..and exit if 8-bit entries

	ADD	HL,BC		; Add twice (16 bit values)
	LD	E,(HL)		; Get LSB
	INC	HL		; Increment pointer
	LD	D,(HL)		; Get MSB
	DEC	HL		; Decrement pointer
GETDMX:	LD	A,D		; Check for zero DM value
	OR	E
	RET			; And exit

; Calculate Sector Number
;  Entry: DE=Block Number from FCB

CALSEC:	LD	HL,0		; Clear MSB sector number
	LD	A,(NBLOCK)	; Get loop counter
	LD	B,A		; Save it in B
	EX	DE,HL
CALSC0:	ADD	HL,HL		; Shift L,D,E
	RL	E
	DJNZ	CALSC0		; B times
	EX	DE,HL
	LD	A,(NMASK)	; Get sector mask
	AND	(IX+NXTREC)	; And with next record
	OR	E		; Set up LSB sector number
	LD	E,A
	RET			; And exit

; Check for File Read-Only status, then fall thru to CALDIR

CKRODI:	CALL	CHKFRO		; Abort if the file is R/O
				; ..fall thru..

; Calculate DIRBUF Entry Point

CALDIR:	LD	A,(SECPNT)	; Get sector pointer
CALDIR1:			; New label for DS (crw)
	LD	HL,(DIRBUF)	; Get start address dirbuf
CALDI0:	ADD	A,L		; Add L=L+A
	LD	L,A
	RET	NC		; No carry exit
	INC	H		; Increment H
	RET			; And exit

; Init File Count

SETFCT:	LD	HL,-1		; Set up file count
	LD	(FILCNT),HL	; Save it
	RET			; And exit

; Set Write Protect Disk Command  (relocated & compressed hfb)

CMND28:				; Set read only disk
	LD	HL,(DSKWP)	; Get disk W/P vector
	CALL	SDRVB		; Include drive bit
	LD	(DSKWP),HL	; Save disk W/P vector
	LD	DE,(NFILES)	; Get max number of files-1 (bumped below)
	LD	HL,(TEMP0)	; Get pointer to disk parameter block
	INC	HL		; Correct pointer..
				; Setlf0 relocated in-line here (hfb)
SETLF0:	INC	DE		; Increment last file
	LD	(HL),D		; Save it in TEMP0
	DEC	HL
	LD	(HL),E
	RET			; And exit

; Search using first 15 bytes of FCB, test if found

SRCT15:	CALL	SEAR15		; Search on 15-bytes..(consolidated-hfb)
				; ..fall thru to test presence..
; Test File Count

TSTFCT:	LD	HL,(FILCNT)	; Test file count=0FFFFH
	LD	A,H		; Get MSB
	AND	L		; And LSB
	INC	A		; Test if result=0FFH
	RET			; And exit

; Test Last File

TSTLF:	LD	HL,(TEMP0)	; Get pointer to last file
	LD	DE,(FILCNT)	; Get file counter
	LD	A,E		; Subtract DE-(HL)
	SUB	(HL)
	INC	HL
	LD	A,D
	SBC	A,(HL)
	RET			; Exit

; Get Next FCB from Drive
; Entry A=0 Check Checksum, A=0FFH Update Checksum

RDDIR:	LD	C,A		; Save checksum flag
	LD	HL,(FILCNT)	; Get file counter
	INC	HL		; Increment it
	LD	(FILCNT),HL	; And save it
	LD	DE,(NFILES)	; Get maximum number of files
	LD	A,E		; Is this the last file?
	SUB	L
	LD	A,D
	SBC	A,H
	JR	C,SETFCT	; ..set file count to 0FFFFH if so
	LD	A,L		; Get file count LSB
	RRCA			; *32 (bm/hfb-to save a byte)
	RRCA
	RRCA
	AND	060H		; Mask it
	LD	(SECPNT),A	; Save it for later use
	RET	NZ		; Return if not first FCB sector
	PUSH	BC		; Save checksum flag
	  IF  NOT ZSDOS11	;  (* This was NOT in released package *)
	LD	A,H		; [1.2]
	OR	L		; [1.2] First dir entry?
	   IF  ZS
	JR	NZ,RdDir0	; [1.2] If not
	LD	HL,(NCHECK)	; [1.2] Is this a fixed disk?
	LD	A,H		; [1.2]
	OR	L		; [1.2]
	CALL	NZ,HOME		; [1.2] Home if media could change
	   ELSE		;~Zs
	CALL	Z,HOME		; [1.2] Home if first dir entry
	   ENDIF	;Zs
	  ENDIF		;~Zsdos11
RdDir0:	CALL	STDIR		; Calculate sector/track directory
	  IF  NOT ZS
	CALL	READDR		; Read into DIR buffer

; Check if !!!TIME&.DAT on disk, save temp. result in TDCHEK <crw>

	LD	HL,(FILCNT)
	LD	A,H
	OR	L		; First file? (filcnt = 0)
	JR	NZ,RDDIR2	; ..jump if not
	LD	HL,(DIRBUF)	; Else look for !!!TIME&.DAT
	LD	B,11		; Test 11 bytes
RDDIR1:	INC	HL
	LD	C,(HL)		; Get Next Char
	RES	7,C		; Clear Attricute Bit
	ADD	A,C		; Add to Checksum
	DJNZ	RDDIR1		; Back for more...
	SUB	TDCKSM		; See it it's !!!TIME&.DAT
	LD	(TDCHEK),A	; Save result (0 = !!!TIME&.DAT found)
RDDIR2:			; <crw>
	  ELSE
				; READDR subroutine moved in-line here
	CALL	DMADIR		; Set up DMA directory
	CALL	READR		; Read a record
	CALL	STDMA		; ..and set up user's DMA
	  ENDIF
	POP	BC		; Restore checksum flag

; Update/Check Checksum Directory
; Entry C=0 Check Checksum, C=0FFH update Checksum

CHKDIR:	LD	HL,(NCHECK)	; Get number of checked records
	LD	DE,(RECDIR)	; Get current record
	XOR	A		; Clear carry (bm)
	SBC	HL,DE		; Test current record
	RET	Z		; Exit if zero
	RET	C		; Exit if greater than ncheck
	LD	HL,(DIRBUF)	; Get dirbuf
	CALL	CKS127		; ..and checksum first 127 bytes..
	ADD	A,(HL)		; ...then 128th byte (hfb)
	LD	HL,(CSV)	; Get pointer checksum directory
	ADD	HL,DE		; Add current record
	INC	C		; Test checksum flag
	JR	NZ,CHKDR1	; 0FFH=> update checksum
	LD	(HL),A		; Update checksum
	RET			; And exit

CHKDR1:	CP	(HL)		; Test checksum
	RET	Z		; Exit if ok

; Checksum differs, So Disk has changed.  Relog it and continue

	LD	A,(FLAGS)
	BIT	4,A		; Inform user?
	LD	B,0		; Disk change error code
	LD	DE,MDSKCH	; Disk changed message
	CALL	NZ,ERROR	; Inform user

; Relog Current Drive after media change detected

	CALL	GETCDM		; Get current drive mask in HL
	EX	DE,HL		; Xfer mask to DE
	CALL	UNLOG		; Reset login vector for logged drive
	CALL	RELOG1		; Do the meat of relogging
				; Caveat emptor: this call is recursive...
	CALL	SETFCT		; Re-initialize search file count
	XOR	A		; We only get here by checking.. (bm)
	JR	RDDIR		; And all checking is done from rddir

; Read Sector from Drive

READR:	CALL	READ		; CBIOS call read sector
	JR	WRITE0

; Write Sector on Drive

WRITER:	CALL	WRITE		; CBIOS call write sector
WRITE0:	OR	A		; Test exit code
	RET	Z		; Exit if ok
	LD	B,1		; Disk I/O error code
	LD	DE,MBADSC	; Load bad sector message
	LD	HL,(STBDSC)	; Load bad sector vector
	JP	(HL)		; ZSDOS error on D: Bad Sector

; Close File Command (relocated hfb)

BGPTCH2	EQU	$+1		;<-- BGii patch point

CMND16:	CALL	SELDR1		; Select drive from FCB

; Close File

CLOSE:	BIT	7,(IX+FCBMOD)	; Test FCB/file modified
	RET	NZ		; Not then no close required
	CALL	CHKRO		; Test disk W/P
	CALL	SRCT15		; Search file and test present
	RET	Z		; No then exit with error
	CALL	CKRODI		; Check file W/P, get directory entry
	LD	BC,16		; Offset to DM block
	ADD	HL,BC		; Add offset
	EX	DE,HL		; Save DIR PTR in DE
	LD	HL,(ARWORD)	; Get FCB ptr
	ADD	HL,BC		; Add offset
	EX	DE,HL
	LD	B,C		; Xfer counter

; Copy FCB (DE) to DIR (HL) if and only if DIR=0 or DIR=FCB

CLOSE0:	INC	(HL)
	DEC	(HL)		; Test DIR for 0
	LD	A,(DE)		; Get byte from FCB
	JR	Z,CLOSE1	; OK to Copy if 0
	CP	(HL)		; Test if same as DIR
	JP	NZ,RETCFF	; ..if Not, abort Close and return error
CLOSE1:	LD	(HL),A		; Else save in DIR
	INC	DE
	INC	HL
	DJNZ	CLOSE0		; Bump pointers and loop until done
	LD	DE,-20		; Add -20 to get Extent Number from DIR
	ADD	HL,DE		; HL contains pointer to extent number
	LD	A,(IX+FCBEXT)	; Get extent number FCB
	CP	(HL)		; Compare with extent number directory
	JR	C,CLOSE3	; FCB < directory then jump
	LD	(HL),A		; Save extent number in directory
	INC	HL		; Get pointer to next record
	INC	HL
	INC	HL
	LD	A,(IX+FCBREC)	; Get next record FCB
	LD	(HL),A		; Save next record in directory
CLOSE3:	CALL	CLOSE6		; Clear Archive Bit and Write FCB
	CALL	GETDME		; Get Data Module and Extent
	  IF  NOT ZSDOS11	;  (* NOT in Release version *)
	PUSH	BC		;[1.2] Save prior module and Extent
	JR	Z,CLOSE4	; ..jump to Stamp if they are both 0
	  ELSE		;Zsdos11   (* This was Release version *)
	JR	Z,CLOSE4	; ..jump to Stamp if they are both 0
	PUSH	BC		; Save prior module and Extent
	  ENDIF		;~Zsdos11
	LD	BC,0
	CALL	SETDME		; Set FCB Data Module and Extent to 0
	CALL	SRCT15		; Find proper DIR Entry
	  IF  NOT ZSDOS11
	JR	Z,JSETDME	; ..Exit if Extent 0 Not Found
CLOSE4:
	  ELSE		;Zsdos11
	POP	BC
	JR	Z,JSETDME	; ..Exit if Extent 0 Not Found
CLOSE4:	PUSH	BC
	  ENDIF		;~Zsdos11
	CALL	CLOSE6		; Clear Archive Bit and Write FCB
	LD	HL,(STUPDV)	; Get the update routine address
	  IF  ZS
	CALL	STAMPT		; ..and stamp it
	  ELSE		;If not Zs (crw)
	LD	C,10		; Set Last Modify
	CALL	JPHL		; ..and Stamp if Enabled
	  ENDIF		;ZS
	  IF  NOT  ZSDOS11
JSETDME:
	POP	BC		; Get Original Module and Extent Back
	  ELSE		;Zsdos11
	POP	BC		; Get Original Module and Extent Back
JSETDME:
	  ENDIF		;~Zsdos11
	JP	SETDME		; Restore to FCB and Exit

CLOSE6:	CALL	CALDIR		; Get directory entry
	LD	BC,11		; Point to archive byte
	ADD	HL,BC
	RES	7,(HL)		; Reset archive bit
	RES	7,(IX+ARCATT)	; Reset bit in FCB
	  IF  ZSDOS11
	JR	WRFCB		; Write FCB to Disk

	   IF  NOT ZS
READDR:	CALL	DMADIR		; Set up DMA directory
	CALL	READR		; Read a record
	JR	STDMA		; ..and set up user's DMA
	   ENDIF	;NOT Zs
	  ENDIF		;Zsdos11

WRFCB:	CALL	CALDIR		; Point to dir entry to write
	LD	A,FCBUSR	; Offset to user byte in FCB
	CALL	CALDI0		; ..do the add here
	LD	(HL),0		; Prevent writing it to disk
	CALL	STDIR		; Calculate sector/track directory
	LD	C,0FFH		; Update checksum directory
	CALL	CHKDIR
WRITD1:	CALL	DMADIR		; Set up dma directory (label for DS - crw)
	LD	C,1		; Write directory flag
	CALL	WRITER		; Write record
	JR	STDMA		; Set up DMA user

	  IF  NOT ZSDOS11
	   IF  NOT ZS
READDR:	CALL	DMADIR		; Set up DMA directory
	CALL	READR		; Read a record
	JR	STDMA		; ..and set up user's DMA
	   ENDIF	;NOT Zs
	  ENDIF		;~Zsdos11

; Set DMA Address Command

CMND26:	LD	(DMA),DE	; Save DMA address

; Set DMA Address

STDMA:	LD	BC,(DMA)	; Get DMA address
	JR	DMADR0		; And do BIOS call

; Set DMA Address Directory

DMADIR:	LD	BC,(DIRBUF)	; Get DMA address directory
DMADR0:	JP	SETDMA		; Cbios call set DMA

; Get Bit from ALV Buffer
;  Entry DE=Block Number
;  Exit  A =Bit in LSB
;	 B =Bit Number in A
;	 HL=Pointer in ALV Buffer

GETBIT:	LD	A,E		; Get bit number
	AND	7		; Mask it
	INC	A		; Add 1
	LD	C,A		; Save it
	  IF  UNROLL
	SRL	D		; Get byte number
	RR	E		; DE=DE/8
	SRL	D
	RR	E
	SRL	D
	RR	E		; ..inline for speed (net cost: 4)
	LD	B,A		; Re-save bit number for next shift
	LD	HL,(ALV)	; Get start address ALV buffer
	  ELSE
	EX	DE,HL
	CALL	SHRHL3		; Divide by 8
	LD	B,A		; Re-save bit number for next shift
	LD	DE,(ALV)	; Get start address ALV buffer
	  ENDIF		;Unroll
	ADD	HL,DE		; Add byte number
	LD	A,(HL)		; Get 8 bits
GETBT0:	RLCA			; Get correct bit
	DJNZ	GETBT0
	LD	B,C		; Restore bit number
	RET			; And return to caller

; Set/Reset bit in ALV Buffer
;  Entry DE=Block Number
;	 C =0 Reset Bit, C=1 Set Bit

SETBIT:	PUSH	BC		; Save set/reset bit
	CALL	GETBIT		; Get bit
	AND	0FEH		; Mask it
	POP	DE		; Get set/reset bit
	OR	E		; Set/reset bit
SETBT0:	RRCA			; Rotate bit in correct position
	DJNZ	SETBT0
	LD	(HL),A		; Save 8 bits
	RET			; And return to caller

; Delete File

DELETE:	CALL	COMCOD		; Call common code w/VDEL on stack

; Delete Routine Core (relocated to save space) (hfb)

VDEL:	CALL	CKRODI		; Check file W/P, get directory entry
	LD	(HL),0E5H	; Remove file
	INC	HL
	LD	A,(HL)		; Get first char
	SUB	'$'		; See if submit file
	JR	NZ,VDEL1	; If not
	LD	(SUBFLG),A	; Clear subflg if $*.* erased
VDEL1:	INC	HL
	RES	7,(HL)		; Insure erased files are not public
	LD	C,0		; Remove bits ALV buffer
				; ..fall thru and return to caller..

; Fill bit buffer from FCB in DIRBUF
;  Entry C=0 Reset Bit, C=1 Set Bit

FILLBB:	CALL	CALDIR		; Get directory entry
	LD	DE,16		; Get offset DM block
	ADD	HL,DE		; Add offset
	LD	B,E		; Get block counter
FILLB0:	LD	E,(HL)		; Get LSB block number
	INC	HL		; Increment pointer
	LD	D,0		; Reset MSB block number
	LD	A,(MAXLEN+1)	; Test >256 blocks present
	OR	A
	JR	Z,FILLB1	; No then jump
	DEC	B		; Decrement block counter
	LD	D,(HL)		; Get correct MSB
	INC	HL		; Increment pointer
FILLB1:	LD	A,D		; Test block number
	OR	E
	JR	Z,FILLB2	; Zero then get next block
	PUSH	HL		; Save pointer
	PUSH	BC		; Save counter and set/reset bit
	LD	HL,(MAXLEN)	; Get maximum length ALV buffer
	OR	A		; Reset carry
	SBC	HL,DE		; Test DE<=maxlen ALV buffer
	CALL	NC,SETBIT	; Yes then insert bit
	POP	BC		; Get counter and set/reset bit
	POP	HL		; Get pointer
FILLB2:	DJNZ	FILLB0		; Repeat for all DM entries
	RET			; And return to caller

; Check File W/P Bit - SEARCH called first

CHKFRO:	CALL	CALDIR		; Get directory entry
	LD	DE,WHLATT	; Offset to R/O bit
	ADD	HL,DE		; Add offset
	LD	DE,(WHEEL)	; Get wheel byte address from header
	LD	A,(DE)		; ..and retrieve the actual byte
	AND	A		; ..and check the Wheel byte
	JR	NZ,CHKFR4	; We have wheel, so allow writes anyway
	BIT	7,(HL)		; Else check Wheel attribute
	JR	NZ,CHKFR2	; Yes then error
CHKFR4:	INC	HL		; Check W/P bit (hfb)
	BIT	7,(HL)		; Test file W/P
	JR	NZ,CHKFR2	; If W/P
CHKFR3:	BIT	7,(IX+PSFATT)	; Was file accessed as Public or Path?
	RET	Z		; If normal access
	LD	A,(FLAGS)	; Else test for writes allowed
	AND	0010B
	RET	NZ		; Go ahead, writes are allowed
CHKFR2:	LD	HL,(SFILRO)	; Get pointer to file W/P message
	LD	B,3		; File W/P error code
	LD	DE,MFILRO	; Load file W/P message
	JP	(HL)		; Display message


; Check Drive Write Protect

BGCKDRO:
CHKRO:	CALL	CHKRO1		; Is the disk W/P?
	RET	NZ		; ..return if disk R/W
	LD	B,2		; Else set disk W/P error code
	LD	DE,MRO		; Load drive W/P message
	LD	HL,(STRO)	; Get pointer to drive W/P message
	JP	(HL)		; Display message

CHKRO1:	LD	HL,(DSKWP)	; Get the W/P drive vector
	CALL	SDRVB		; Set the bit for this drive
	SBC	HL,DE		; See if extra bit added (Cy is clear)
	RET

; Search using first 12 bytes of FCB (hfb)

SEAR12:	LD	A,12
	DEFB	21H		; Trash HL and fall through

; Search using first 15 bytes of FCB

SEAR15:	LD	A,15

; Search for File Name
;  Entry: A = Number of bytes for which to search

SEARCH:	LD	(SEARNB),A	; Save number of bytes
	LD	A,0FFH		; Set exit code to 0FFH (not found)
	LD	(SEAREX),A
	LD	(DCOPY),IX	; Copy FCB pointer to RAM (search next)
	CALL	SETFCT		; Initiate file counter

; Force directory read with a Call HOME (bh)   (Only if Floppys-hfb)
	  IF  ZSDOS11		; (* Logic moved to RDDIR if NOT Zsdos11 *)
	LD	HL,(NCHECK)	; Is this a fixed media?
	LD	A,H
	OR	L
	CALL	NZ,HOME		; Invoke CBIOS Home routine if removeable
	  ENDIF		;~Zsdos11

; Search Next File Name

SEARCN:	XOR	A		; Check checksum directory
	LD	H,A
	LD	L,A
	LD	(SEARQU),HL	; Clear question mark & public detected flags
	RES	7,(IX+PSFATT)	; Reset public/system file flag
	CALL	RDDIR		; Get FCB from directory
	CALL	TSTFCT		; Test if past last entry
	JR	Z,JSEAR8	; Yes then jump (note carry always clear)
	LD	DE,(DCOPY)	; Get FCB pointer
	LD	A,(DE)		; Get first byte
	CP	0E5H		; Test if searching empty directory
	JR	Z,SEARC1	; Yes then jump
	PUSH	DE		; Save FCB pointer
	CALL	TSTLF		; Test last file on this drive
	POP	DE		; Restore FCB pointer
JSEAR8:	JR	NC,SEARC8	; Yes then jump
SEARC1:	CALL	CALDIR		; Get entry in directory
	LD	A,(HL)		; Get first byte directory entry
	CP	21H		; Test time stamp
	JR	Z,SEARCN	; Yes then get next directory entry
	LD	C,0		; Clear counter
	LD	A,(SEARNB)	; Get number of bytes to search for
	LD	B,A		; Save it in counter
SEARC2:	LD	A,B		; Test if counter is zero
	OR	A
	JR	Z,SEARC9	; Yes then jump
	LD	A,(DE)		; Get byte from FCB
	XOR	'?'		; Test if question mark
	AND	7FH		; Mask it
	JR	Z,SEARC6	; Yes then jump
	LD	A,C		; Get FCB counter
	OR	A		; Test first byte
	JR	NZ,SEARC3	; No then jump
	LD	A,(FLAGS)	; Get flag byte
	RRA			; Test public file enable
	JR	NC,SEARC3	; ..jump if not
	INC	HL		; Get pointer to Public Bit
	INC	HL
	BIT	7,(HL)		; Test Public Bit directory
	DEC	HL		; Restore pointer
	DEC	HL
	JR	Z,SEARC3	; No public file then jump
	LD	A,(DE)		; Get first byte FCB
	CP	0E5H		; Test if searching empty directory
	JR	Z,SEARC3	; Yes then jump

; The following 3 lines of code represent a deviation from the description of
; PUBLIC Files as given in DDJ Article by Bridger Mitchell and Derek McKay of
; Plu*Perfect Systems.	The PUBLIC Specification states that Public Files will
; NOT be found by any wildcard reference except when a "?" is in the FCB+0
; byte.  The code here relaxes that requirement as follows:  If we are in the
; same user area as the public file, then don't report the file as PUBLIC, but
; find it.  This has a nasty side effect - it allows erasing of PUBLIC files
; if we are in the same area.  However, these files also show up on the direc-
; tory (they wouldn't otherwise), so at least we should know we're blasting
; them.

	XOR	(HL)		; Test FCB = Directory Entry
	AND	7FH		; Mask it (setting Zero Flag)
	JR	Z,SEARC5	; Jump if user is same
	LD	A,0FFH
	LD	(SEARPU),A	; Set Public file found
	  IF  UPATH
	CALL	SETPSF		; Set Public/System file flag
	  ELSE
	SET	7,(IX+PSFATT)	; Set Public/System file flag
	  ENDIF
	JR	SEARC5		; Jump found

SEARC3:	LD	A,C		; Get FCB counter
	CP	13		; Is it User Code?
	JR	Z,SEARC5	; ..jump if so..don't test
	CP	12		; Is it an Extent Number?
	LD	A,(DE)		; ..Get byte from FCB
	JR	Z,SEARC7	; ..Jump if Extent Number
	XOR	(HL)		; Is FCB byte = Directory Entry byte?
	AND	07FH		; ..Mask it
SEARC4:	JR	NZ,SEARCN	; ..jump if not same and get next entry
SEARC5:	INC	DE		; Increment FCB pointer
	INC	HL		; Increment Directory Entry pointer
	INC	C		; Increment counter
	DEC	B		; Decrement counter
	JR	SEARC2		; Test next byte

SEARC6:	DEC	A		; Set question mark found flag
	LD	(SEARQU),A
	JR	SEARC5		; Jump found

SEARC7:
	XOR	(HL)		; Test extent
	CALL	SEARC7A		; Mask Extent
	JR	SEARC4		; ..and test Result


SEARC7A: PUSH	BC
	LD	B,A		; Save Extent
	LD	A,(NEXTND)	; Get extent mask
	CPL			; Complement it
	AND	MAXEXT		; Mask it
	AND	B		; Mask extent
	POP	BC		; Restore counters
	RET

SEARC8:	CALL	SETFCT		; Error set file counter
	JP	RETCFF		; Set return code to FF and exit

SEARC9:	LD	HL,(SEARQU)	; Get question mark and public found flags
	LD	A,H
	AND	L
	JR	NZ,SEARC4	; Yes then search for next entry
	CALL	TSTLF		; Test for last file
	CALL	NC,SETLF0	; And update if so
	LD	HL,(RECDIR)	; Set DE return to directory record
	LD	(DEVAL),HL	; .. for DateStamper simulation
	LD	A,(FILCNT)	; Get file counter
	AND	3		; Mask it
	LD	(PEXIT),A	; And set exit code
	XOR	A		; Clear exit code search
	LD	(SEAREX),A
	RET			; And return to caller

; The following code is common to DELETE, RENAME, and CSTAT.
; It is coded in a manner that is compatable with the Z280
; in protected Mode.

COMCOD:	CALL	CHKRO		; Check disk W/P
	CALL	SEAR12		; Search file
COMCO1:	CALL	TSTFCT		; Test if file found
	POP	HL		; Routine addr to HL (in case not found)
	RET	Z		; Not then exit
	PUSH	HL		; ..found, so routine back to stack
	PUSH	HL		; Twice, as RET pops first push
	LD	HL,COMCO2
	EX	(SP),HL		; COMCO2 to stack, routine addr to HL
	JP	(HL)		; ..branch to routine

COMCO2:	CALL	WRFCB		; Write directory buffer on disk
	CALL	SEARCN		; Search next entry
	JR	COMCO1		; And test it


; Rename File - Note Wildcard Support

RENAM:	CALL	COMCOD		; Go to common code w/VRENAM on stack

VRENAM:	CALL	CHKFRO		; Check file W/P
	LD	HL,(ARWORD)
	LD	DE,16		; Offset to new name
	ADD	HL,DE		; Add offset
	EX	DE,HL		; Copy HL=>DE
	CALL	CALDIR		; Get directory entry
	INC	HL
	INC	HL
	RES	7,(HL)		; Make any renamed file private
	DEC	HL
	DEC	HL
	LD	B,11		; Set up loop counter
RENAM1:	INC	HL		; Increment directory pointer
	INC	DE		; Increment FCB pointer
	LD	A,(DE)		; Get character from FCB
	AND	7FH		; Mask it
	CP	'?'		; Test if question mark
	JR	NZ,RENAM2	; no, then change character on disk
	LD	A,(HL)		; Else get what's there as there is no change
RENAM2:	RLA			; Clear MSB
	RL	(HL)		; Get MSB from directory
	RRA			; And move to FCB
	LD	(HL),A		; Save in directory
	DJNZ	RENAM1		; Loop until done
	RET

; Change Status Bits for File

CSTAT:	CALL	COMCOD		; Go to common code w/VCSTAT on stack

VCSTAT:	PUSH	IX
	POP	DE		; FCB pointer in DE
	CALL	CALDIR		; Get directory entry
	LD	B,11		; Set up loop counter
CSTAT1:	INC	HL		; Increment directory pointer
	INC	DE		; Increment FCB pointer
	LD	A,4		; Are we pointing to Wheel Attribute?
	CP	B
	JR	NZ,CSTAT2	; ..jump if not
	PUSH	HL
	LD	HL,(WHEEL)	; Else do we have Wheel privileges?
	LD	A,(HL)
	POP	HL
	AND	A		; ..set flags to show
	JR	NZ,CSTAT2	; Jump if we have Wheel
	BIT	7,(HL)		; Is file Wheel protected?
	JP	NZ,CHKFR2	; ..jump if so
CSTAT2:	LD	A,(DE)		; Get status bit from FCB
	RL	(HL)		; Remove MSB of directory
	RLA			; Get msb from FCB
	RR	(HL)		; And move into directory char
	DJNZ	CSTAT1		; Loop until done
	RET

; Compute File Size

FILSZ:	LD	BC,0		; Reset file size length
	LD	D,C
	CALL	LDRRC		; Save it in FCB+33,34,35
	CALL	SEAR12		; Search file (hfb)
FILSZ0:	CALL	TSTFCT		; Test if file found
	RET	Z		; Not then exit
	CALL	CALDIR		; Get directory entry
	EX	DE,HL		; Copy to DE
	LD	HL,15		; Offset to next record
	CALL	CALRRC		; Calculate random record count
	LD	A,D		; Test LSB < (ix+33)
	SUB	(IX+33)
	LD	A,C		; Test ISB < (ix+34)
	SBC	A,(IX+34)
	LD	A,B		; Test MSB < (ix+35)
	SBC	A,(IX+35)
	CALL	NC,LDRRC	; Write new maximum
	CALL	SEARCN		; Search next file
	JR	FILSZ0		; And test it

; Find File
	  IF  UPATH
FINDF:	CALL	SRCT15		; Search file
	RET	NZ		; Yes then exit
	LD	A,(FLAGS)
	BIT	5,A		; Test if Path enabled
	RET	Z		; Exit if not
	LD	HL,(PATH)	; Get Path address
	LD	A,H		; Test if zero (no path)
	OR	L
	RET	Z		; Yes then exit
FINDF0:	LD	A,(HL)		; Get first entry path name
	INC	HL		; Increment pointer
	OR	A		; Test if last entry
	JP	Z,SEARC8	; Yes then error exit
	AND	7FH		; Mask drive number
	CP	'$'		; Test if current drive
	JR	NZ,FINDF1	; No then jump
	LD	A,(DRIVE)	; Get current drive
	INC	A		; Increment drive number
FINDF1:	DEC	A		; Decrement drive number
	PUSH	HL		; Save path pointer
	CALL	SELDK		; Select drive
	POP	HL		; Restore path pointer
	LD	A,(HL)		; Get user number
	INC	HL		; Advance pointer
	AND	7FH		; Mask user number
	CP	'$'		; Test if current user
	JR	NZ,FINDF2	; No then jump
	LD	A,(USER)	; Get current user
FINDF2:	AND	1FH		; Mask user number
	PUSH	HL		; Save path pointer
	CALL	RESUSR		; Add new user number in FCB+0 and FCB+13
	CALL	SRCT15		; Search file and test if present
	POP	HL		; Restore path pointer
	JR	Z,FINDF0	; No then test next path entry
	PUSH	HL		; Save path pointer
	CALL	CALDIR		; Get directory entry
	LD	DE,10		; Add offset system bit
	ADD	HL,DE
	BIT	7,(HL)		; Test system file
	LD	A,(FLAGS)	; Test for relaxed path definition
	RLA			; ..by rotating bit..
	RLA			; ..into carry flag
	POP	HL		; Restore path pointer
	JR	C,FINDF3	; If carry, system attrib not required
	JR	Z,FINDF0	; No system file then test next path entry
FINDF3:	LD	A,(DEFDRV)	; Get current drive
	INC	A		; Increment drive number
	LD	(FCB0),A	; Save it in exit FCB0
SETPSF:	SET	7,(IX+PSFATT)	; set Public/System file flag
	RET			; And return to caller
	  ENDIF		;Upath

; Open File Command

CMND15:	CALL	SELDRV		; Select drive from FCB
	LD	(IX+FCBMOD),0	; Clear data module number

; Open File
	  IF  UPATH
	CALL	FINDF		; Find file (use path name)
	CALL	TSTFCT		; Test file found
	  ELSE
	CALL	SRCT15		; Find file W/O path
	  ENDIF		;Upath
	RET	Z		; No then exit
OPENF0:	LD	A,(IX+PSFATT)	; Get Public/System file bit
	PUSH	AF		; Save it
	LD	A,(IX+FCBEXT)	; Get extent number from FCB
	PUSH	AF		; Save it
	CALL	CALDIR		; Get directory entry
	LD	A,(HL)		; Find real user number file is in
	OR	80H		; Set user valid flag
	PUSH	IX		; Save FCB entry
	POP	DE		; Get in in DE
	LD	BC,32		; Number of bytes to move
	LDIR			; Move directory to FCB
	LD	(IX+FCBUSR),A	; And put user byte back
	CALL	SETB14		; Set FCB/File Not Modified
	LD	B,(IX+FCBEXT)	; Get extent number
	LD	C,(IX+FCBREC)	; Get next record number
	POP	AF		; Get old extent number
	LD	(IX+FCBEXT),A	; Save it
	CP	B		; Compare old and new extent number
	JR	Z,OPENF1	; Same then jump
	LD	C,0		; Set next record count to 0
	RR	C		; Record count to Max (80H) if need new extent
OPENF1:	LD	(IX+FCBREC),C	; Save next record count
	POP	AF		; Get Public/System file bit
	RL	(IX+PSFATT)	; Remove MSB from IX+8
	RLA			; Set new MSB in carry
	RR	(IX+PSFATT)	; Save Carry in IX+8
	  IF  ZS
	LD	HL,(STLAV)	; Get address of last accessed routine
	JP	STAMPT
	  ELSE
	LD	C,5		; Set access stamp
	LD	HL,(STLAV)	; Get address of last accessed routine
JPHL:	JP	(HL)		; ..and Jump to it (or DOTDER)
	  ENDIF		;Zs

; Make File Command

CMND22:	CALL	SELDRV		; Select drive from FCB
	LD	(IX+FCBMOD),0	; Clear data module number

; Make File

MAKES:	CALL	CHKRO		; Check drive W/P
	LD	HL,(ARWORD)
	LD	A,(HL)		; Get first byte FCB
	PUSH	AF		; Save it
	LD	(HL),0E5H	; Set first byte to empty file
	LD	A,1		; Search for 1 byte
	CALL	SEARCH		; Search empty file
	POP	AF		; Get first byte FCB
	LD	(IX+0),A	; Restore it
	CALL	TSTFCT		; Test empty file found
	RET	Z		; No then return error
	LD	HL,(ARWORD)	; Get FCB pointer
	CALL	CKSUB		; Check if this is a submit file
	LD	DE,15		; Prepare offset
	ADD	HL,DE		; Add it
	LD	B,17		; Set loop counter
	XOR	A
MAKE0:	LD	(HL),A		; Clear FCB+15 up to FCB+31
	INC	HL		; Increment pointer
	DJNZ	MAKE0		; And clear all bytes
	RES	7,(IX+PSFATT)	; Reset Public/System file bit
	RES	7,(IX+ARCATT)	; Reset archive bit if present
	CALL	CALDIR		; Get directory entry
	PUSH	IX		; Save FCB entry
	POP	DE		; Get it in DE
	EX	DE,HL		; Exchange FCB and directory entry
	LD	BC,32		; Number of bytes to move
	LDIR			; Move bytes
	CALL	WRFCB		; Write FCB on disk
	CALL	SETB14		; Set file not modified
	  IF  ZS
	LD	HL,(STCRV)	; Get address of Stamp Create routine
	JP	STAMPT		; ..and stamp it
	  ELSE
	LD	C,0		; Set Create Stamp
	JP	STIME		; And exit
	  ENDIF		;Zs

; Open Next Extent

OPENEX:	BIT	7,(IX+FCBMOD)	; Test if FCB/File Modified (write)
	JR	NZ,OPENX2	; Not then jump
	CALL	CLOSE		; Close current FCB
	LD	A,(PEXIT)	; Get exit code
	INC	A		; Test if error
	RET	Z		; Yes then exit
OPENX2:	CALL	CALNEX		; Calculate next extent (LABEL MOVED)
	JR	C,OPENX3	; Error then jump

OPENX0:	CALL	SRCT15		; Search for 15-char match & test presence
	JR	NZ,OPENX5	; Yes then jump
	LD	A,(RDWR)	; Test Read/Write flag
	OR	A		; Test if read
	JR	Z,OPENX3	; Yes then error
	CALL	MAKES		; Make new extent if write
	CALL	TSTFCT		; Test if succesfull
	JR	NZ,OPENX6	; Yes then exit
OPENX3:	CALL	SETB14		; Set FCB/File Not Modified
RETCFF:	LD	A,0FFH		; (hfb/cwc) set exit code
OPENX4:	JP	SAVEA		; And return to caller

OPENX5:	CALL	OPENF0		; Open file
OPENX6:	XOR	A		; And clear exit code
	JR	OPENX4		; Use same routine

;==OPENX2:	CALL	CALNEX		; Calculate next extent
;==	JR	C,OPENX3	; Error then jump
;==	JR	OPENX0		; Open next extent, FCB contains DU:

; Calculate Next Extent
;  Exit: Carry=1 => Overflow Detected

CALNEX:	CALL	GETDME		; Get extent number, data module number
	BIT	6,B		; Test error bit random record
	SCF			; Set error flag
	RET	NZ		; ..Error exit if Non-zero
	INC	C		; Increment extent number
	LD	A,C		; Get extent number
	AND	MAXEXT		; Mask it for max extent
	LD	C,A		; Save it in C
;==	JR	NZ,SETDME	; If new data module not required
	JR	NZ,CALNE1	;== IF NEW DATA MODULE NOT REQUIRED
	INC	B		; Set next data module
	LD	A,B		; Get it in A
	AND	MAXMOD		; Mask it for max module
	LD	B,A		; Save it in B
	SCF			; Set error flag
	RET	Z		; And return if file overflow
CALNE1:	LD	(IX+NXTREC),0	;== ZERO NEXT RECORD COUNT
SETDME:	LD	(IX+FCBEXT),C	; Save Extent number
	LD	(IX+FCBMOD),B	; Save Data Module number
	  IF  ZS
	AND	A		; Clear flag here if ZS
	RET
	  ENDIF			; ..else fall thru on ZD to do same thing

GETDME:	LD	C,(IX+FCBEXT)	; Get Extent number
	LD	B,(IX+FCBMOD)	; Get Data Module number
	LD	A,C
	CALL	SEARC7A		; Mask Extent
	RES	7,B		; Clear Unmodified Flag
	OR	B		; Test for Module and Extent = 0
	RET			; ..and return to caller

; Read Random Record Command

CMND33:	CALL	SELDR1		; Select drive from FCB

; Read Random Sector

	XOR	A		; Set read/write flag
	CALL	LDFCB		; Load random record in FCB
	JR	Z,READS		; No error then read sector
	RET			; Return error

; Read Sequential

CMND20:	CALL	SELDR1		; Select drive from FCB

; Read Sector

READS:	XOR	A		; Set Read/Write flag
	LD	(RDWR),A	; Save it
	LD	A,(IX+NXTREC)	; Get record counter
	CP	80H		; Test if last record this extent
;=	JR	NC,READS1	; Yes then open next extent
	JR	Z,READS1	;= Yes then open next extent
	CP	(IX+FCBREC)	; Test if greater then current record
	JR	C,READS2	; No then get record
READS0:	LD	A,1		; Set end of file flag
	JR	OPENX4		; And exit

READS1:	CALL	OPNXCK		; Open next extent
READS2:	CALL	GETDM		; Get block number from DM in FCB
	JR	Z,READS0	; Jump if block number=0 to end file
	CALL	CALSEC		; Calculate Sector Number (128 bytes)
	CALL	CALST		; Calculate Sector/Track number
	CALL	READR		; Read data
	JP	WRITS7		; Increment elsewhere if necessary

; Consolidated Routine to Open Extent and check status

OPNXCK:	CALL	OPENEX		; Open next extent
	LD	A,(PEXIT)	; Get exit code
	OR	A
	RET	Z		;== IF OPEN OK
	POP	HL		;== ELSE POP RETURN ADDRESS TO ABORT R/W
	JR	READS0		;== AND SET ERROR CODE TO EOF
;==	JR	NZ,READS0	; Yes then end of file
;==	LD	(IX+NXTREC),A	; Clear record counter (jww)
;==	RET

; Write Random Record Command (with and without Zero Fill)

CMND40:				; (hfb/cwc)
CMND34:	CALL	SELDR1		; Select drive from FCB

; Write Random Sector and Write Random with Zero Fill

	LD	A,0FFH		; Set Read/Write flag
	CALL	LDFCB		; Load FCB from random record
	JR	Z,WRITES	; No error then write record
	RET			; Return error

; Write Sequential

CMND21:	CALL	SELDR1		; Select drive from FCB

; Write Sector.  Permitted to PUBlic files and those found along Path

WRITES:	LD	A,0FFH		; Set read/write flag
	LD	(RDWR),A	; And save it

BGPTCH1	EQU	$+1		;<-- Patched location for BGii

	CALL	CHKRO		; Check disk W/P
	BIT	7,(IX+ROATT)	; Test if file W/P
	JR	NZ,WRITSA	; Yes then file W/P message
	CALL	CHKFR3		; Test W/P if path or Public used
	LD	HL,(WHEEL)	; Get address of Wheel byte
	LD	A,(HL)		; Do we have it?
	AND	A
	JR	NZ,WRITSB	; Yes - allow write
	BIT	7,(IX+WHLATT)	; Else test if Wheel Prot file
WRITSA:	JP	NZ,CHKFR2	; Yes then file W/P message
WRITSB:	BIT	7,(IX+NXTREC)	; End of this extent?
	CALL	NZ,OPNXCK	; Open next extent and check status (hfb)
	CALL	GETDM		; Get block number from FCB
	JP	NZ,WRITS5	; Jump to write sector if Block Number <> 0
	PUSH	HL		; Save pointer to Block Number
	LD	A,C		; Test first Block Number in extent
	OR	A
	JR	Z,WRITS1	; Yes then jump
	DEC	A		; Decrement pointer to Block Number
	CALL	GETDM4		; Get previous Block Number

; Get Free Block from ALV Buffer
;  Entry DE=Old Block Number
;  Exit  DE=New Block Number (0 if No Free Block)
;   HL counts Up,DE counts Down
				; GETFRE routine relocated here inline
WRITS1:	LD	H,D		; Copy old block to HL
	LD	L,E
GETFR0:	LD	A,D		; Test down counter is zero
	OR	E
	JR	Z,GETFR1	; Yes then jump
	DEC	DE		; Decrememt down counter
	PUSH	HL		; Save up/down counter
	PUSH	DE
	CALL	GETBIT		; Get bit from ALV buffer
	RRA			; Test if zero
	JR	NC,GETFR3	; Yes then found empty block
	POP	DE		; Get up/down counter
	POP	HL
GETFR1:	LD	BC,(MAXLEN)	; Get maximum ALV length-1 in BC
	LD	A,L		; Is HL >= length ALV-1?
	SUB	C		; ..do while preserving HL
	LD	A,H
	SBC	A,B
	JR	NC,GETFR2	; End buffer then jump
	INC	HL		; Increment up counter
	PUSH	DE		; Save down/up counter
	PUSH	HL
	EX	DE,HL		; Save up counter in DE
	CALL	GETBIT		; Get bit from ALV buffer
	RRA			; Test if zero
	JR	NC,GETFR3	; Yes then found empty block
	POP	HL		; Get down/up counter
	POP	DE
	JR	GETFR0		; And test next block

GETFR2:	LD	A,D		; Test if last block tested
	OR	E
	JR	NZ,GETFR0	; No then test next block
	JR	WRITSG		; Continue with DE=0

GETFR3:	SCF			; Set block number used
	RLA			; Save bit
	CALL	SETBT0		; Put bit in ALV buffer
	POP	DE		; Get correct counter
	POP	HL		; Restore stack pointer
				; ..continue with (DE=block number)

WRITSG:	POP	HL		; Get pointer to Block Number
	LD	A,D		; Test if blocknumber = 0
	OR	E
	JR	Z,WRITS8	; Yes then disk full error
	RES	7,(IX+FCBMOD)	; Reset FCB/File Modified
	LD	(HL),E		; Save blocknumber
	LD	A,(MAXLEN+1)	; Get number of blocks
	OR	A		; Is it < 256?
	JR	Z,WRITS2	; ..Jump if so
	INC	HL		; Increment to MSB Block Number
	LD	(HL),D		; ..and save MSB block number
WRITS2:	LD	C,2		; Set write new block flag
	LD	A,(NMASK)	; Get sector mask
	AND	(IX+NXTREC)	; Mask with record counter
	JR	Z,WRITSX	; Zero then Ok (at start new record)
	LD	C,0		; Else clear new block flag
WRITSX:	LD	A,(FUNCT)	; Get function number
	SUB	40		; Test if Write RR with zero fill
	JR	NZ,WRITS6	; No then jump
	PUSH	DE		; Save blocknumber
	LD	HL,(DIRBUF)	; Use directory buffer for zero fill
	LD	B,128		; 128 bytes to clear
WRITS3:	LD	(HL),A		; Clear directory buffer
	INC	HL		; Increment pointer
	DJNZ	WRITS3		; Clear all bytes
	CALL	CALSEC		; Calculate sector number (128 bytes)
	LD	A,(NMASK)	; Get sector mask
	LD	B,A		; Copy it
	INC	B		; Increment it to get number of writes
	CPL			; Complement sector mask
	AND	E		; Mask sector number
	LD	E,A		; And save it
	LD	C,2		; Set write new block flag
WRITS4:	PUSH	HL		; Save registers
	PUSH	DE
	PUSH	BC
	CALL	CALST		; Calculate sector/track
	CALL	DMADIR		; Set DMA directory buffer
	POP	BC		; Get write new block flag
	PUSH	BC		; Save it again
	CALL	WRITER		; Write record on disk
	POP	BC		; Restore registers
	POP	DE
	POP	HL
	LD	C,0		; Clear write new block flag
	INC	E		; Increment sector number
	DJNZ	WRITS4		; Write all blocks
	CALL	STDMA		; Set user DMA address
	POP	DE		; Get Block Number
WRITS5:	LD	C,0		; Clear write new block flag
WRITS6:	RES	7,(IX+FCBMOD)	; Reset FCB/File Modified flag
	PUSH	BC		; Save it
	CALL	CALSEC		; Calculate sector number (128 bytes)
	CALL	CALST		; Calculate Sector/Track
	POP	BC		; Get write new block flag
	CALL	WRITER		; Write record on disk
	LD	A,(IX+NXTREC)	; Get record counter
	CP	(IX+FCBREC)	; Compare with next record
	JR	C,WRITS7	; If less then jump
	INC	A		; Increment record count
	LD	(IX+FCBREC),A	; Save it on next record position
	RES	7,(IX+FCBMOD)	; Reset FCB/File Modified flag
WRITS7:	LD	A,(FUNCT)	; Get function number
	CP	20		; (hfb)
	RET	C		; Return if < 20 (hfb)
	CP	21+1		; (hfb)
	RET	NC		; Return if > 21 (hfb)
	INC	(IX+NXTREC)	; Increment record count
	RET			; And return to caller

WRITS8:	LD	A,2		; Set disk full error
	JP	SAVEA		; And return to caller 


; Load FCB for Random Read/Write
;  Exit : Zero Flag = 1 No Error
;		      0 Error Occured

LDFCB:	LD	(RDWR),A	; Save Read/Write flag
	LD	A,(IX+33)	; Get first byte random record
	LD	D,A		; Save it in D
	RES	7,D		; Reset MSB to get next record
	RLA			; Shift MSB in carry
	LD	A,(IX+34)	; Load next byte random record
	RLA			; Shift Carry
	PUSH	AF		; Save it
	AND	MAXEXT		; Mask next extent
	LD	C,A		; Save it in C
	POP	AF		; Get byte
	RLA			; Shift 4 times
	RLA
	RLA
	RLA
	AND	0FH		; Mask it
	LD	B,A		; Save data module number
	LD	A,(IX+35)	; Get next byte random record
	LD	E,6		; Set random record to large flag
	CP	4		; Test random record to large
	JR	NC,LDFCB8	; Yes then error
	RLCA			; Shift 4 times
	RLCA
	RLCA
	RLCA
	ADD	A,B		; Add byte
	LD	B,A		; Save data module number in B
	LD	(IX+NXTREC),D	; Set next record count
	LD	D,(IX+FCBMOD)	; Get data module number
	BIT	6,D		; Test error random record
	JR	NZ,LDFCB0	; Yes then jump
	LD	A,C		; Get new extent number
	CP	(IX+FCBEXT)	; Compare with FCB
	JR	NZ,LDFCB0	; Not equal then open next extent
	LD	A,B		; Get new data module number
	XOR	(IX+FCBMOD)	; Compare with data module number
	AND	MAXMOD		; Mask it
	JR	Z,LDFCB6	; Equal then return
LDFCB0:	BIT	7,D		; Test FCB modified (write)
	JR	NZ,LDFCB1	; No then jump
	PUSH	DE		; Save registers
	PUSH	BC
	CALL	CLOSE		; Close extent
	POP	BC		; Restore registers
	POP	DE
	LD	E,3		; Set close error
	LD	A,(PEXIT)	; Get exit code
	INC	A
	JR	Z,LDFCB7	; Error then exit
LDFCB1:	CALL	SETDME		; Save Data Module and Extent
	CALL	SEAR15		; Search next FCB
	LD	A,(PEXIT)	; Get error code
	INC	A
	JR	NZ,LDFCB5	; No error then exit
	LD	A,(RDWR)	; Get read/write flag
	LD	E,4		; Set read empty record
	INC	A
	JR	NZ,LDFCB7	; Read then error
	CALL	MAKES		; Make new FCB
	LD	E,5		; Set make error
	LD	A,(PEXIT)	; Get error code
	INC	A
	JR	Z,LDFCB7	; Error then exit
	JR	LDFCB6		; No error exit (zero set)

LDFCB5:	CALL	OPENF0		; Open file
LDFCB6:	JP	OPENX6		; Set zero flag and clear error code 

LDFCB7:	LD	(IX+FCBMOD),0C0H ; Set random record error
LDFCB8:	LD	A,E		; Get error code
	LD	(PEXIT),A	; And save it
	OR	A		; Clear zero flag
SETB14:	SET	7,(IX+FCBMOD)	; (hfb) get FCB/File Not Modified
	RET			; And return to caller

; Calculate Random Record
;  Entry HL=Offset in FCB
;	 DE=FCB Pointer
;  Exit  D=LSB Random Record
;	 C=ISB Random Record
;	 B=MSB Random Record

CALRRC:	ADD	HL,DE		; Pointer to FCB+15 or FCB+32
	LD	A,(HL)		; Get record number
	LD	HL,12		; Offset to extent number
	ADD	HL,DE		; Get pointer to extent byte
	LD	D,A		; Save record number
	LD	A,(HL)		; Get extent byte
	AND	MAXEXT		; Mask it 000eeeee
	RL	D		; Shift MSB in Carry Cy=R, d=rrrrrrr0
	ADC	A,0		; Add Carry 00xeeeex
	RRA			; Shift 1 time (16 bits) 000xeeee
	RR	D		; D=xrrrrrrr
	LD	C,A		; Save ISB
	INC	HL		; Increment to data module number
	INC	HL
	LD	A,(HL)		; Get data module number 00mmmmmm
	RRCA			; Divide module by 16
	RRCA
	RRCA
	RRCA
	PUSH	AF		; Save it mmmm00mm
	AND	03H		; Mask for maximum module
	LD	B,A		; Save it 000000mm
	POP	AF		; Get LSB
	AND	0F0H		; Mask it mmmm0000
	ADD	A,C		; Add with ISB mmmxeeee
	LD	C,A		; Save ISB
	RET	NC		; No carry then return
	INC	B		; Increment MSB 000000mm
	RET			; And return to caller
				; 000000mm mmmxeeee xrrrrrrr
	PAGE
	  IF  ZS
;************************************************************************
;*	  U n i v e r s a l   T i m e / D a t e   S u p p o r t 	*
;************************************************************************

; In order to provide time/date support for as many systems as possible,
; a set of universal routines are used.  These routines do not do the
; actual stamping, but provide all the data required to method specific
; programs to perform the needed services.  To use the DOS services, the
; external handler needs to tie itself into the Time/Date vector table
; in the ZSDOS configuration area.  The Get Stamp, Put Stamp, Stamp Last
; Access, Stamp Create, and Stamp Modify routines receive the following
; parameters in the Z80 registers:
;	A  = Offset to DIR entry [0,20H,40H,60H]
;	BC = Address of ZSDOS WRFCB routine
;	DE = Pointer to Directory Buffer
;	HL = DMA address
;	IX = Pointer to FCB passed to DOS
; The directory buffer contains the dir entry for the FCB passed to DOS,
; A contains the offset.  The disk has been tested for R/O on all calls
; except get stamp and is R/W.	If a CP/M+ style stamping is used, a simple
; call to the address passed in BC is used to update the disk after adding
; the time as required.  This call is ALWAYS required.	The routines may
; use AF,BC,DE, and HL without restoring them.	Four levels of stack are
; available on the DOS stack for use by the functions.	All routines must
; exit with a RET instruction, and A=1 if successful, A=0FFH if error.

; Get/put Timestamps

CMD102:
CMD103:	CALL	SELDRV		; Select DU: from FCB
	CALL	SRCT15		; Find the FCB
	JR	Z,DOTDER	; If not found
	LD	HL,(GETSTV)	; Get time stamp function address
	LD	A,(FUNCT)
	CP	102		; Get stamp?
	JR	Z,DOTDR3	; Yes
	LD	HL,(PUTSTV)	; Get address of set stamp routine
				; ..fall thru to common code..
; Enter here for Stamp Last Access, Stamp Create, Stamp Modify

STAMPT:	PUSH	HL
	CALL	CHKRO1		; Test for disk W/P but avoid error trap
	POP	HL
	JR	Z,DOTDER	; No stamp if disk is W/P

DOTDR3:	CALL	GETDME		; Get Data Module and Extent Number
	JR	NZ,DOTDER	; ..Quit if Not Extent 0 of Module 0
	LD	A,(SECPNT)	; Offset to FCB in dirbuf
	LD	DE,(DIRBUF)	; Dir buffer pointer
	LD	BC,WRFCB	; Address of WRFCB routine
	PUSH	HL		; Save function vector
	LD	HL,(DMA)	; Put DMA in HL
	RET			; Then vector to routine

; Time and Date Routines.  Like the date stamping routines, the user must
; supply the actual driver routines for time and date.	These routines are
; attached to ZSDOS via the vector table in the configuration area.  The
; routines are passed the address to Get/Put the Time and Date in the DE
; and IX registers.  The routines may use AF,BC, and D without restor-
; ing them.  Four levels of stack are available on the DOS stack for use
; by the the functions.  All routines must exit with a RET instruction,
; and A=1 if successful, A=0FFH if error.
; In order to better provide for internal DateStamper, the clock routines
; must save the value at DE+5 when called, and return this value to the
; DOS in the E register.  In addition, the HL register must be returned
; as the called DE value +5.
; The Time/Date string consists of 6 packed BCD digits arrayed as:
;	Byte	00 01 02 03 04 05
;		YY MM DD HH MM SS

; Set Time/Date from user-supplied buffer string

CMD99:	LD	C,1		; Set parameter to set time/date
	DEFB	21H		; ..and fall thru to GSTD

; Get Time/Date to string whose address is supplied by the user

CMD98:	LD	C,0		; Set parameter to get time/date
GSTD:	LD	HL,(GSTIME)	; Get time/date get/set routine address
	PUSH	HL		; ..to stack for pseudo "Jump"
DOTDER:	OR	0FFH		; Save 1 T state while setting flags
	RET			; Vector to service routine
	  ENDIF		;Zs
	PAGE
	  IF  NOT ZS
;---------------------------------------------------------------------
;	   Z D D O S    T i m e    R o u t i n e s    <crw>
;---------------------------------------------------------------------
; STIME - Set file's time and date in !!!TIME&.DAT file
;
; Entry: SECPNT and RECDIR set by search for file.
;	 BC = 10 - set Modify date/time
;	 BC =  5 - set Last Access date/time
;	 BC =  0 - set Create date/time, zero modify & access
;
; Exit : Zero Flag Set (Z) if Time Set in !!!TIME&.DAT file
;	 Zero Flag Reset (NZ) if error or "No Stamp" attribute Set
;
; Note : Only the first extent's stamp is valid.

STIME:	LD	A,(TDCHEK)	; See if !!!TIME&.DAT
RETNZ:	OR	A		; ..file on disk, clear Carry
	RET	NZ		; No.  (NZ) flags error

	BIT	7,(IX+3)	; Datestamper (tm) "no stamp" bit
	RET	NZ		; Don't stamp this file

	PUSH	BC
	CALL	GETDME		; See if this is Extent 0 of Module 0
	POP	BC
	RET	NZ		; Quit Now if it isn't

	LD	B,A		; Zero B
	LD	A,(FUNCT)	; Get Current Function
	CP	102		; Is it Get Stamp?
	JR	Z,STIME0	; ..jump to skip R/O test if so
	CALL	CHKRO1		; Else test for Disk R/O w/o Error Exit
	JP	Z,DOTDER	; ..and Quit if Error

STIME0:	PUSH	BC		; Save 0, 5, or 10

; 1. Get disk sector number of file's T&D record, save offset in T&D
;	sector for later.

				; Carry cleared from above
	LD	A,(SECPNT)	; 0-relative dir. sector offset
				; ..of file FCB (0, 32, 64, or 96)
	RRA			; Divide by 2 for !!!TIME&.DAT offset
				; ..a = 0, 16, 32, or 48
	LD	HL,(RECDIR)	; 0-relative directory sector of FCB
	SRL	H		; Divide by 2 to get 0-relative
				; ..sector of T&D file in HL
	RR	L		; Odd directory sector sets Carry
	JR	NC,STIME1
	ADD	A,64		; Point to 2nd half of record
STIME1:	PUSH	AF		; Save pointer for T&D record
	PUSH	HL		; Save !!!TIME&.DAT file sector
	LD	HL,(NDIR0)	; Get DIR Alloc Bitmap
	LD	A,(NMASK)	; Get Block Mask
	INC	A		; +1 = Number of Records/Block
	LD	E,A		; Save Records/Block in E
	LD	D,B		; Extent, B is 0 from above
	LD	B,16		; Iterate 16 times
STIME2:	ADD	HL,HL		; Shift next DIR Alloc bit out to Carry
	JR	NC,STIME2A	; No Add if No Alloc Bit
	EX	(SP),HL		; Alloc to Stack, Records into HL
	ADD	HL,DE		; Add another Alloc worth of records
	EX	(SP),HL		; Records back to stack, Alloc to HL
STIME2A: DJNZ	STIME2		; Loop until all 16 bits done 
	POP	HL		; Restore !!!TIME&.DATE Record Number

; 2. Read T&D sector from disk.

	CALL	STDIR2		; Set track and sector of T&D file
	CALL	READDR		; Read it to DIRBUF

; 3. Check T&D sector.

	CALL	STIME6		; Check checksum
	CP	(HL)
	JR	NZ,TDERR	; Report error

; 4. Get stamp (GetStp) or set stamp in dirbuf, using offset (tdpnt)

	POP	AF		; Get record pointer
	CALL	CALDIR1		; Get HL = pointer to stamp in DIRBUF
	POP	BC		; C = 0, 5, or 10 (offset)
	LD	A,(FUNCT)
	CP	102		; Get stamp?
	RET	Z		; Yes, just point with HL
	CP	103		; Set stamp from DMA?
	JR	Z,STIME4

	ADD	HL,BC		; Add offset (0, 5, 10)
	PUSH	BC		; Save 0, 5, 10
	CALL	CMD98A		; Load 6 bytes from clock to HL
	POP	BC		; Restore entry parm.
	INC	A		; 0FFH --> 0 on clock error
	JR	Z,DOTDER	; ..jump to set NZ status on error
	LD	(HL),E		; Restore HL+5 (restore only if clock)
	LD	A,C		; Test entry parameter
	OR	A		; Set create stamp?
	JR	NZ,STIME5	; No, write to disk as is

	LD	B,10		; Yes,
STIME3:	LD	(HL),A		; Zero the
	INC	HL		; Access and
	DJNZ	STIME3		; Modify dates
	JR	STIME5		; And write to disk

STIME4:	LD	DE,(DMA)	; Setstp get time from DMA
	EX	DE,HL		; HL points to DMA
				; DE points to stamp in DIRBUF
	LD	BC,15		; Copy 15 bytes to DIRBUF
	LDIR
			;..fall	thru

; 5. Reset T&D checksum and write stamped sector to disk.

STIME5:	CALL	STIME6		; Get checksum in A, (HL) = chk byte
	LD	(HL),A		; Update checksum
	CALL	WRITD1		; Write DIRBUF to !!!TIME&.DAT file
	XOR	A		; Set to (Z), no errors
	RET			; Stime done.

; Don't crash pgm on T&D Err, just return with err

TDERR:	POP	HL
	POP	HL		; Clean up the stack
;=	JR	GSEXIT		; ..and return error
	jr	dotder		; ..and return error

; ------------------------------------------------------------------
; CMND102 - Return file's 15 byte stamp at DMA
;  Entry: DE --> FCB of file (wildcards allowed)
;  Exit : (DMA) holds 10 byte file stamp
;	  A = 1 if Ok, Else A = 0FFH if File or Datestamp not found
;		DMA contents undefined if error.
;
; CMND103 - Set file's 10 byte stamp from DMA
;  Entry: DE --> FCB of file (wildcards allowed)
;	  (DMA) holds 10 byte file stamp
;  Exit : A = 1 if File DateStamp updated (Only first extent is valid)
;	  A = 0FFH if File/Datestamp not found or "No stamp" attribute set

CMD102:
CMD103:	CALL	SELDRV		; Select drive from FCB
	CALL	SRCT15		; Search file, test found
	JR	Z,DOTDER	; ..jump error exit if File Not Found
	LD	A,(FUNCT)	; Get or set?
	CP	103		; Set?
	JR	Z,SETSTP	; ..jump if yes

	RES	7,(IX+3)	; Get. clear "no stamp"
	CALL	STIME		; Point to start of stamp
	JR	NZ,GSEXIT	; ..Exit w/Error if No Stamping allowed
	LD	BC,15		; Load 15 bytes
	CALL	MV2DMA		; Move data to DMA address 
	JR	GSEXIT

SETSTP:	CALL	STIME		; Set from DMA

GSEXIT:	JP	Z,READS0	; Jump to set "1" success status if Ok..
	JP	RETCFF		; ..else set 0FFH Error status

; --------
;  Label for DS version <crw>

CMD98A: EX	DE,HL		; Prepare for STIME call <crw>

; Get Time/Date to string whose address is supplied by the user

CMD98:	LD	C,0		; Set parameter to get time/date
	DEFB	21H		; ..set for fall thru to GSTD

; Set Time/Date from User-supplied Buffer string

CMD99:	LD	C,1		; Set parameter to Set Time/Date

; Clock interface.  Clock module must be ZDS DateStamper compatible
;  Modified to ZDS DateStamper parameter passing

GSTD:	LD	HL,(GSTIME)	; Get time/date get/set routine address
	PUSH	HL
DOTDER:	OR	0FFH		; set error return
	RET			; Vector to service routine

;.....
; Subroutine to Check/Update the !!!TIME&.DAT checksum

;  Entry: DIRBUF points to T&D record
;  Exit :  A holds checksum
;	  HL points to checksum byte in record

STIME6:	XOR	A		; Clear A
	LD	HL,(DIRBUF)
			;..fall thru to do CheckSum..
;****************************************************************
;* NOTE: This routine must fall thru to the CKS127 routine just *
;*  after the ENDIF, so this sequence must not be altered       *
;****************************************************************

;------------------ End time routines <crw> ------------------------
	  ENDIF

; Calculate checksum of 127 bytes addressed by HL.  Return with HL
; pointing to the 128th byte.

CKS127:	LD	B,127		; Test 1st 127 bytes
CKSLP:	ADD	A,(HL)		; Sum all bytes to A
	INC	HL
	DJNZ	CKSLP
	RET

	PAGE
;**************************************************************
;*	 Z S D O S     H i g h	   R A M     D a t a	      *
;**************************************************************

; High RAM area.  These locations are not stored by an IOP or
; BackGrounder.

CODEND:
	  IF  ROM
	    IF  $-ZSDOS GT 0E00H
	*** ZSDOS TOO BIG !!!!!	***
	    ENDIF		;$-zsdos
	DSEG
	  ELSE
	    IF  ZS
	      IF  $-ZSDOS GT 0DF1H
	*** ZSDOS TOO BIG !!!!! ***
	      ENDIF		;$-zsdos
	ORG	ZSDOS+0DF1H		; Set here for Internal Path
	    ELSE
	      IF  $-ZSDOS GT 0DF9H
	*** ZDDOS TOO BIG !!!!! ***
	      ENDIF		;$-zsdos
	ORG	ZSDOS+0DF9H
	    ENDIF		;Zs
	  ENDIF			;Rom
HIRAM:
	  IF  ZS
IPATH:	DEFB	1,0		; Internal Path = Drive A, User 0
	DEFW	00,00		; ..two more blank entries
	DEFB	0		; ...and ending Null
TDFVCT:	DEFW	00		; Time and date file vector
	  ELSE
TDCHEK:	DEFB	0		; used by ZDDOS for T&D present flag
	  ENDIF		;Zs
LOGIN:	DEFW	00		; Login vector
DSKWP:	DEFW	00		; Disk write protect vector
HDLOG:	DEFW	00		; Fixed disk login vector

	  IF  ROM
FREEMEM	EQU	BIOS-CODEND
	  ELSE
FREEMEM	EQU	HIRAM-CODEND
	  ENDIF		;Rom

; Variables for use with BGii

BGLOWL	EQU	BGHIRAM-BGLORAM	   ; Size of Low RAM save
BGHIL	EQU	BGRAMTOP-BGHIRAM   ; Size of Hi RAM save

	END			; End program
