You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3174 lines
94 KiB

;WW - Globally changed dquotes to squotes for all strings
;WW - Globally changed NOT, AND, OR in conditional assembly to ~, &, |
;
;****************************************************************************
; 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.
;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; WW - MOVED TO N8VEM WRAPPER
; 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 ~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 00 ; 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 00 ; ..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 ~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 ~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 ~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' << 8 + VERS ;"S" indicates ZSDOS - ZRDOS returns 0
ELSE
LD HL,'D' << 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 ~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 ~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 ~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 ~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 ~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 ~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 ~ZSDOS11
IF ~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 ~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 > 0E00H
*** ZSDOS TOO BIG !!!!! ***
ENDIF ;$-zsdos
DSEG
ELSE
IF ZS
IF $-ZSDOS > 0DF1H
*** ZSDOS TOO BIG !!!!! ***
ENDIF ;$-zsdos
; ORG ZSDOS+0DF1H ; Set here for Internal Path
FILL ((ZSDOS+0DF1H) - $),055H
ELSE
IF $-ZSDOS > 0DF9H
*** ZDDOS TOO BIG !!!!! ***
ENDIF ;$-zsdos
; ORG ZSDOS+0DF9H
FILL ((ZSDOS+0DF9H) - $),055H
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