;**************************************************************************** ; 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 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: ; 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 ;--------------------------------------------------------------------- ; 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 CMD98A: EX DE,HL ; Prepare for STIME call ; 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 ------------------------ 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