mirror of https://github.com/wwarthen/RomWBW.git
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.
4116 lines
121 KiB
4116 lines
121 KiB
TITLE "ZSDOS 2"
|
|
;************************************************************************
|
|
;* Z S D O S v2 *
|
|
;* A banked CP/M 2.2 compatible replacement BDOS *
|
|
;* by Harold F. Bower and Cameron W. Cotrill *
|
|
;*----------------------------------------------------------------------*
|
|
;* Disassembly: jxl May 2025 *
|
|
;* public release 1.0 May 2025 *
|
|
;* see remarks at the end *
|
|
;*----------------------------------------------------------------------*
|
|
;* Assemble with SLR Z80ASM *
|
|
;* *
|
|
;* A>Z80ASM ZSDOS2/6 *
|
|
;************************************************************************
|
|
|
|
|
|
NAME ('DOS')
|
|
|
|
|
|
VER EQU 20H
|
|
REV MACRO
|
|
DEFB '27g'
|
|
ENDM
|
|
|
|
DATE MACRO
|
|
DEFB '1993'
|
|
ENDM
|
|
|
|
|
|
;::::: DEFINITIONS
|
|
|
|
|
|
RAMLOW EQU 0000H ; Start address memory
|
|
|
|
; Bios entry points
|
|
|
|
COMMON /_BIOS_/
|
|
BIOS EQU $
|
|
|
|
CSEG
|
|
|
|
; 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
|
|
MOVE EQU BIOS+004BH ; (Interbank) move
|
|
SELMEM EQU BIOS+0051H ; Select memory bank
|
|
XMOVE EQU BIOS+0057H ; Set memory banks for MOVE
|
|
RETMEM EQU BIOS+0075H ; Return current memory bank
|
|
|
|
OTPABK EQU BIOS+082H ; offset TPA bank (from B/P Bios base)
|
|
OSYSBK EQU BIOS+083H ; offset SYS bank
|
|
OZ3ENV EQU BIOS+098H ; offset Z3ENV
|
|
|
|
|
|
; Control chars
|
|
|
|
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
|
|
|
|
|
|
; Disk related
|
|
|
|
MAXEXT EQU 1FH ; Maximum extent number
|
|
MAXMOD EQU 3FH ; Maximum data module number
|
|
TDCKSM EQU 91H ; Checksum of !!!TIME&.DAT
|
|
|
|
|
|
; Attribute bits
|
|
|
|
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 positions
|
|
|
|
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
|
|
|
|
|
|
; Internal
|
|
|
|
; ZSDOS 1 = 6D / ZSDOS 2 = ED
|
|
FLGBITS EQU 11101101B ; PUBlic On, P/P Write Off, R/O On,
|
|
; Fast Relog On, Disk Chg Warning Off,
|
|
; Path On, No System Path On
|
|
; (Reserved) On
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
|
|
DEFB 'ZSDOS',VER/16+'0'
|
|
|
|
; ZSDOS entry point
|
|
JP ENTRY ; jump to start of program code
|
|
|
|
|
|
;::::: DATA / CONFIGURATION AREA
|
|
|
|
; CP/M 2.2 compatible 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
|
|
PATH: DEFW PATHAD ; Path address for file open, 0 if no Path
|
|
|
|
; Wheel Byte pointer
|
|
WHEEL: DEFW 0 ; Address of Wheel byte, 0 if none
|
|
|
|
; User configuration byte(s)
|
|
FLAGS: DEFB FLGBITS ; Flag byte (see above)
|
|
STFLAG: DEFB 0110B ; Stamp enable/disable
|
|
; bit2= Modify, bit1= Create, bit0= Access
|
|
|
|
; Dispatch table for time/date stamp routines
|
|
GSTIME: DEFW DOTDER ; Address of get/set time/date routine
|
|
DEFW DOTDER
|
|
UNLOAD: DEFW 0 ; Pointer to remove time stamp routine
|
|
IPATH: DEFW PATHAD ; copy to restore Internal Path (see ZSCFG2)
|
|
|
|
;-----
|
|
|
|
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
|
|
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
|
|
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
|
|
|
|
;-----
|
|
|
|
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 code
|
|
PEXIT: DEFW 0 ; exit code
|
|
|
|
;-----
|
|
|
|
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
|
|
|
|
;-----
|
|
|
|
FINITD: DEFB 0 ; INITDR flag preventing code re-execution
|
|
TPABNK: DEFB 0 ; TPA bank number
|
|
|
|
|
|
DEFB 'ZSDOS ',VER/16+'0','.',VER MOD 16 + '0'
|
|
DEFB ' Copyright (c) '
|
|
DATE
|
|
DEFB ' by C.W.Cotrill & H.F.Bow'
|
|
IXSAVE: DEFB 'er' ; User's IX register
|
|
|
|
ZSDOSS: DEFW 0 ; ZSDOS Stack
|
|
DEFB 0,0,0,0 ; ..also used as buffer to read/set clock
|
|
|
|
;-----
|
|
|
|
; Time/Date
|
|
RWCODE: DEFB 0 ; 0= read stamp, 1= write stamp
|
|
STOFF: DEFB 0 ; offset in stamp
|
|
STATUS: DEFB 0FFH ; status of first routine for exit checs
|
|
|
|
; Other
|
|
FCBADR: DEFW 0 ; address of (target) FCB
|
|
FCBBUP: DEFB 0 ; number of FCB bytes to copy/update
|
|
ZSFCB: DEFS 36 ; buffer for internal FCB (36 bytes)
|
|
ZSSTMP: DEFS 15 ; buffer for Time Stamp (15 bytes)
|
|
|
|
|
|
|
|
;::::::::::::::::::::::::::::::::::::::::
|
|
;::::: ZSDOS Entry Point
|
|
|
|
ENTRY: CALL RETMEM ; B/P Bios fn #39 (A= current memory bank)
|
|
LD (TPABNK),A ; save TPA bank number
|
|
XOR A ; clear A
|
|
LD B,A ; for later 16-bit addr's
|
|
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),DE ; save in memory for BGii
|
|
LD HL,DOSEXIT ; get exit address ZSDOS
|
|
PUSH HL ; save it on stack to return from ZSDOS
|
|
LD A,C ; get function code (reg B= 0)
|
|
LD (FUNCT),A ; save it for later use
|
|
CP 12 ; is it a non-disk function ?
|
|
JR C,ENTRY0 ; ..if so, jump
|
|
CP MAXCMD ; fcn < maximum command number (49) ?
|
|
JR C,ENTRY1 ; ..if so, jump
|
|
|
|
; extended function scanner for added functions
|
|
CP 98 ; is it less than cmnd 98 ?
|
|
RET C ; ..if so, return (fcn not known/valid)
|
|
CP 152 ; is it cmnd 152 ?
|
|
JP Z,CMD152 ; ..if so, execute it
|
|
CP 103+1 ; is it greater than cmnd 103 ?
|
|
RET NC ; ..if so, return (fcn not known/valid)
|
|
SUB 98-MAXCMD ; rework 98..103 --> 50..55
|
|
LD C,A ; save reworked function number
|
|
; ..and fall through to ENTRY0
|
|
|
|
; for Non-disk functions (ie. fcn # less than 12), push address of
|
|
; SAVEA routine on Stack (save A reg as return code). Saves code in
|
|
; Console Routines, as a simple RET can be used in most cases.
|
|
ENTRY0: LD HL,SAVEA
|
|
PUSH HL ; vector return through 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
|
|
; allows direct Bios jumps for several fcn's with code savings
|
|
LD C,E ; place arg in C for BIOS
|
|
LD A,E ; and in A for others
|
|
JP (HL) ; jump to routine
|
|
|
|
|
|
;::::::::::::::::::::::::::::::::::::::::
|
|
;::::: Command Table
|
|
|
|
CTABLE: DEFW ERROR5 ; Warm boot (Bios) with ERMODE clear
|
|
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 CMND46 ; Return Disk Free Space
|
|
DEFW CMND47 ; Return DMA
|
|
DEFW CMND48 ; Return DOS version
|
|
DEFW CMND49 ; Return Environment Descriptor Address
|
|
MAXCMD EQU ($-CTABLE)/2
|
|
DEFW CMD98 ; Get Time
|
|
DEFW CMD99 ; Set Time
|
|
DEFW CMD100 ; Get Flags
|
|
DEFW CMD101 ; Set Flags
|
|
DEFW CMD102 ; Get Stamp
|
|
DEFW CMD103 ; Put Stamp
|
|
|
|
|
|
;::::::::::::::::::::::::::::::::::::::::
|
|
;::::: I/O Routines
|
|
|
|
;--------------------------------------------------
|
|
; fn #1 CONIN
|
|
; enter: none, exit: A= char
|
|
;--------------------------------------------------
|
|
; read char fron Console
|
|
; and echo if char = CR, LF, TAB, CONTH or >=Space
|
|
CMND01: CALL GETCH ; get character (and test it)
|
|
RET C ; ..if less than Space, exit
|
|
PUTCH: PUSH HL ; save regs for other calls
|
|
CALL WRCON ; echo character
|
|
POP HL
|
|
RET
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 6 Direct Console I/O
|
|
; enter: E= 0FFH (In), exit: A= Input Character
|
|
; 0FEH (In) Console Status
|
|
; 0FDH (In) Input Character
|
|
; 00H..0FCH (Out) 00H
|
|
;--------------------------------------------------
|
|
; 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 is available
|
|
JR Z,DCIO1 ; ..if yes, do input
|
|
INC E ; test for 0xFE
|
|
JR Z,DCIO2 ; ..if so, get status
|
|
INC E ; test for 0xFD
|
|
JR Z,GETCH ; ..if so, wait for input char
|
|
JP CONOUT ; else, print char (Bios Console Output)
|
|
DCIO2: LD A,(LASTCH) ; check for buffered char
|
|
OR A
|
|
LD A,00000001B ; preset ready
|
|
CALL Z,CONST ; call Bios Console Status
|
|
AND A ; test it
|
|
RET ; and return to caller
|
|
DCIO1: CALL DCIO2 ; get console status
|
|
RET Z ; ..if no character present, exit
|
|
; else, fall through
|
|
|
|
; get char 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 ; call Bios Console Input
|
|
; get char and fall through to test it
|
|
|
|
; test char
|
|
; out: Carry= 0 for CR, LF, TAB, CONTH, or >=Space
|
|
; Carry= 1 for all other characters
|
|
CP CR ; is it a Carriage Return ?
|
|
RET Z ; ..if so, return
|
|
CP LF ; Line Feed ?
|
|
RET Z ; ..return
|
|
CP TAB ; Tab ?
|
|
RET Z ; ..return
|
|
CP CONTH ; Backspace ?
|
|
RET Z ; ..return
|
|
CP ' ' ; test >= Space
|
|
RET ; ..and return to caller
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 8 Set I/O Byte
|
|
; enter: E= I/O Byte, exit: A= 00H
|
|
;--------------------------------------------------
|
|
CMND08: LD (RAMLOW+0003H),A ; save it in RAM and fall through
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 7 Get I/O Byte
|
|
; enter: None, exit: A= I/O Byte (0003H)
|
|
;--------------------------------------------------
|
|
CMND07: LD A,(RAMLOW+0003H) ; get I/O byte from RAM
|
|
RET
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 10 Read Console Buffer
|
|
; enter: DE= address Buffer, exit: A= 00H
|
|
;--------------------------------------------------
|
|
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
|
|
JR Z,JZRBX ; ..if CR, then exit
|
|
CP LF
|
|
JZRBX: JP Z,RDBUFX ; ..or if LF, then exit
|
|
|
|
; delete char from buffer
|
|
; (Rubout, Backspace, CR, LF are _never_ in the buffer)
|
|
RDBUF2: CP RUBOUT ; delete char ?
|
|
JR Z,DOBACK ; ..if so, jump
|
|
CP CONTH ; Control-H also deletes
|
|
JR NZ,RDBUF3 ; ..if no delete, skip to next test
|
|
DOBACK: LD A,(HL)
|
|
AND A ; test if attempting to del from empty line
|
|
RET Z ; ..if so, then exit
|
|
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 pointer
|
|
|
|
; delete B chars from Console
|
|
PUSH DE ; save pointer
|
|
DOBAK5: LD C,CONTH
|
|
PUSH BC ; save counter from destruction
|
|
CALL CONOUT ; call Bios Console Output
|
|
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 ; ..if so, do it
|
|
CP CONTX
|
|
JR NZ,RDBUF4 ; skip to next test if no erase line
|
|
ERALIN: XOR A
|
|
OR (HL) ; line empty ?
|
|
RET Z ; ..if so, exit
|
|
PUSH HL
|
|
CALL DOBAK0 ; else, delete another (skip empty check)
|
|
POP HL
|
|
JR ERALIN ; if Ctrl-R=True, do following code
|
|
; else bypass
|
|
|
|
RDBUF4: CP CONTR ; if Ctrl-R type clean buffer version on CON:
|
|
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 ; insure nz value
|
|
JR RETY1A ; 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
|
|
|
|
; toggle line printer echo
|
|
RDBUF5: CP CONTP ; toggle printer ?
|
|
JR NZ,RDBUF6 ; ..if not, next test
|
|
LD HL,FCONTP
|
|
LD A,(HL) ; get printer echo flag
|
|
CPL ; toggle it
|
|
LD (HL),A ; put back
|
|
RET
|
|
|
|
; check if Ctrl-C is first char in BUFF, 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 ; ..if not the first character, return
|
|
CP CONTC ; possible user abort ?
|
|
RET NZ ; ..if not, return
|
|
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 char as '^X'
|
|
WRCTL: CP ' ' ; test if Control char
|
|
JR NC,WRCON ; ..if not, send it out
|
|
CP TAB ; test if Tab
|
|
JR Z,WRCON0 ; ..if it is, then expand with spaces
|
|
PUSH AF ; save char
|
|
LD A,'^' ; output a caret
|
|
CALL WRCON1 ; no need for Tab test here
|
|
POP AF
|
|
ADD A,40H ; convert to printable
|
|
; ..and fall through to WRCON
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn #2 CONOUT
|
|
; enter: E= char, exit: none (A= Bios reg A)
|
|
;--------------------------------------------------
|
|
; output char with list echo, Tab expansion
|
|
WRCON: CP TAB ; is it a Tab ?
|
|
JR NZ,WRCON1 ; ..if not, jump
|
|
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 ; ..if not, then repeat
|
|
LD A,TAB ; return Tab
|
|
RET ; return to caller
|
|
WRCON1: PUSH BC ; save pointers
|
|
PUSH DE
|
|
LD C,A ; save character
|
|
PUSH BC
|
|
|
|
BGPTCH0 EQU $+1 ; <--- BGii patches this addr
|
|
|
|
CALL CMND11 ; test status and CONTS/CONTC
|
|
POP BC ; get character back
|
|
PUSH BC ; save it again
|
|
CALL CONOUT ; call Bios Console Output
|
|
POP BC ; get character back
|
|
PUSH BC ; save it again
|
|
LD A,(FCONTP) ; get printer echo flag
|
|
OR A ; test it
|
|
CALL NZ,LIST ; ..if non-zero, output char to printer
|
|
POP BC ; restore character
|
|
LD A,C ; fall through to count routine
|
|
POP DE ; restore pointers
|
|
POP BC
|
|
|
|
; 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 ; ..if so, treat like Backspace
|
|
CP ' '
|
|
RET NC ; ok if not Control char
|
|
CP TAB ; only DOBACK ever gets Tabs through here
|
|
JR Z,WRCON4 ; ..if Tab, handle differently
|
|
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 counter
|
|
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
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 11 Get Console Status
|
|
; enter: None, exit: A= 00H No character
|
|
; 01H Char. present
|
|
;--------------------------------------------------
|
|
; BGii uses this routine
|
|
BGCONST:
|
|
CMND11: CALL DCIO2 ; get character present status
|
|
RET Z ; ..if none, then exit
|
|
CALL GETCH ; get next console char
|
|
CP CONTS ; is it stop char ?
|
|
JR NZ,GCONS2 ; ..if not, jump
|
|
CALL CONIN ; call Bios Console Input to get next char
|
|
CP CONTC ; does the user want to exit (Ctrl-C) ?
|
|
JR NZ,CMND11 ; ..if not, check for another character
|
|
JP ERROR5 ; else, jump to warm boot and 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
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 9 Print String
|
|
; enter: DE= address String, exit: None (A= '$')
|
|
;--------------------------------------------------
|
|
CMND09: LD A,(DE) ; get byte from buffer
|
|
CP '$' ; test, last byte ?
|
|
RET Z ; ..if yes, then return to caller
|
|
INC DE ; else, point to next byte
|
|
CALL WRCON ; ..and output character
|
|
JR CMND09 ; loop, test again
|
|
|
|
|
|
;::::::::::::::::::::::::::::::::::::::::
|
|
;::::: Error Routines
|
|
|
|
PRDEC: LD BC,100
|
|
CALL NUM
|
|
LD C,10
|
|
CALL NUM
|
|
LD BC,0101H
|
|
|
|
; display number
|
|
NUM: LD D,-1 ; load number -1
|
|
NUM1: INC D ; increment number
|
|
SUB C ; divide by C
|
|
JR NC,NUM1 ; ..if 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 ; ..if 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$'
|
|
MBERR: DEFB 'ZSDOS Error on $'
|
|
MBFUNC: DEFB CR,LF,'Call'
|
|
MDRIVE: DEFB ': $'
|
|
MFILE: DEFB ' File: $'
|
|
MCRLF: DEFB CR,LF,'$'
|
|
|
|
|
|
; new ZDSOS error handler
|
|
; in: B= error code
|
|
; DE= message pointer
|
|
ERROR: LD A,(ERMODE)
|
|
LD C,A ; save error mode
|
|
RRCA ; test suppress print
|
|
JR C,ERROR3 ; ..if suppressed, then skip to display
|
|
|
|
; 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 to Console
|
|
LD DE,MDRIVE ; point to Drive tag
|
|
CALL CMND09 ; ouput 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 ; ..if not, skip file name display
|
|
POP BC ; get error type
|
|
PUSH BC
|
|
PUSH IX ; save FCB pointer
|
|
LD A,(FUNCT) ; are we erasing a file ?
|
|
CP 19 ; ..if so, get name from DIRBUF
|
|
JR NZ,ERROR0 ; ..ambiguous name may have been used
|
|
CALL CALDIR ; get Dir buffer pointer
|
|
EX (SP),HL ; to show that we are 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,46 ; get ready for it
|
|
CALL Z,PUTCH ; ..and 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 ; ..if not, skip
|
|
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 SELSYB ; switch System bank
|
|
CALL SELDK ; get Bios back in step
|
|
POP BC
|
|
ERROR4: BIT 1,C ; test if return error mode
|
|
JR NZ,ERROR7 ; ..if return error, go
|
|
LD A,1
|
|
SUB B ; test if fatal error
|
|
JR NC,ERROR6 ; ..if not a fatal error, jump
|
|
; else, fall through
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 0 Boot
|
|
; enter: None, exit: None
|
|
;--------------------------------------------------
|
|
ERROR5: XOR A ; clear A
|
|
LD (ERMODE),A ; set DOS error mode to default CP/M
|
|
LD A,(OTPABK) ; store TPABNK = 0 (at B/P Bios base +0x82)
|
|
CALL SELMEM ; ..and also set it, BIOS fn #27 SELMEM (A= bank #)
|
|
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 ; ..if so, continue relog
|
|
LD L,0FFH ; set extended error code
|
|
LD (PEXIT),HL ; save as return code
|
|
; ..and fall through to DOS exit
|
|
|
|
|
|
;::::::::::::::::::::::::::::::::::::::::
|
|
;::::: DOS Exit Routine
|
|
|
|
DOSEXIT: LD A,(FLDRV) ; test Drive select used flag
|
|
OR A
|
|
JR Z,DOSEXT0 ; ..if no, then exit
|
|
LD A,(FCB0) ; get FCB byte 0
|
|
LD (IX+0),A ; save it
|
|
CALL SELTPB ; select TPA bank
|
|
LD HL,ZSFCB ; buffer internal FCB
|
|
LD DE,(FCBADR)
|
|
LD A,(FCBBUP) ; get number of bytes to copy
|
|
OR A ; test if empty
|
|
JR Z,DOSEXT1 ; ..if zero, skip
|
|
LD C,A ; else, set byte counter (16-bit)
|
|
LD B,0
|
|
LDIR ; and copy
|
|
DOSEXT1: LD A,(DRIVE) ; get old Drive number
|
|
CALL SELSYB ; switch System bank
|
|
CALL SELDK ; select disk
|
|
|
|
|
|
; Stack is in an undefined condition at this point, if error handler
|
|
; was invoked. Independent of Stack position, the user's IX register
|
|
; has to be restored. (Thanks to Joe Wright for catching this one!)
|
|
DOSEXT0: CALL SELTPB ; select TPA bank
|
|
LD SP,(SPSAVE) ; restore user stack
|
|
LD IX,(IXSAVE) ; restore IX
|
|
LD HL,(PEXIT) ; get exit code
|
|
LD DE,(DEVAL) ; and DE reg for DateStamper
|
|
LD A,L ; copy function code
|
|
LD B,H
|
|
RET ; and return to caller
|
|
|
|
|
|
;::::::::::::::::::::::::::::::::::::::::
|
|
;::::: Disk Routines
|
|
|
|
;--------------------------------------------------
|
|
; fn # 13 Reset Disk System
|
|
; enter: None, exit: A= 00H No $*.* on A
|
|
; FFH $*.* on A
|
|
;--------------------------------------------------
|
|
CMND13: CALL SELSYB ; switch System bank
|
|
JP CMD13B ; and continue in Banked code
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;--------------------------------------------------
|
|
; Reset Disk System
|
|
; (continue CMND13)
|
|
;--------------------------------------------------
|
|
CMD13B: LD HL,RAMLOW+80H ; set up default 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
|
|
; ..and fall through to CMD37B
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;--------------------------------------------------
|
|
; fn # 37 Reset Mult Drive
|
|
; enter: DE= Bit Mask, exit: A= 00H
|
|
;--------------------------------------------------
|
|
; fixed disk login vector is also altered by this call
|
|
CMND37: CALL SELSYB ; switch System bank
|
|
JP CMD37B ; and continue in Banked code
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;--------------------------------------------------
|
|
; Reset Multiple Login Drive
|
|
; (continue CMND37)
|
|
;--------------------------------------------------
|
|
; fixed disk login vector is also altered by this call
|
|
CMD37B: 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, jump
|
|
LD HL,DSKWP ; get Drive W/P vector
|
|
CALL ANDDEM ; reset W/P stat only of requested Drives
|
|
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: 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
|
|
LD A,(DEFDRV) ; get default drive
|
|
CALL SELDK
|
|
|
|
|
|
; check for possible existance of submit file
|
|
|
|
; ZSDOS watches for any $*.* file 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
|
|
JP SAVEA ; exit
|
|
|
|
|
|
; check first byte of Dir entry or FCB for '$'
|
|
; in: HL= pointer to Dir or FCB
|
|
CKSUB: INC HL ; point to file name
|
|
LD A,(HL) ; get first char file name
|
|
DEC HL
|
|
SUB '$' ; test if '$'
|
|
RET NZ ; ..if not, then exit
|
|
DEC A ; load with 0xFF
|
|
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 address 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
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;--------------------------------------------------
|
|
; fn # 17 Search for First Entry of File
|
|
; enter: DE= Address of FCB, exit: A= Directory Code
|
|
;--------------------------------------------------
|
|
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 ; ..if 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: CALL SELTPB ; select TPA bank
|
|
LD HL,(DIRBUF) ; copy Directory buffer
|
|
LD BC,128 ; Directory = 128 bytes
|
|
LD DE,(DMA) ; to DMA address
|
|
LDIR
|
|
RET ; exit
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 18 Search for Next Occurrence of File
|
|
; enter: DE= Address of FCB, exit: A= Directory Code
|
|
;--------------------------------------------------
|
|
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
|
|
XOR A ; clear A (zero)
|
|
LD (FCBBUP),A ; ..save as num of bytes to update in FCB
|
|
JR CMD17C ; and copy Directory to DMA address
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 19 Delete File
|
|
; enter: DE= Address of FCB, exit: A= Error Code
|
|
;--------------------------------------------------
|
|
CMND19: CALL SELDRV ; select drive from FCB
|
|
; (also switches System bank)
|
|
CALL DELETE ; ..and continue in Banked code
|
|
|
|
CMND19A: LD A,(SEAREX) ; get exit byte 00= file found, 0FFH= not
|
|
JR SAVEA ; and exit
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 23 Rename File
|
|
; enter: DE= Address of FCB, exit: A= Error Code
|
|
;--------------------------------------------------
|
|
CMND23: CALL SELDRV ; select drive from FCB
|
|
CALL RENAM ; rename file
|
|
JR CMND19A ; and exit
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 25 Get Current Disk
|
|
; enter: None, exit: A= Current Disk
|
|
;--------------------------------------------------
|
|
CMND25: LD A,(DEFDRV) ; get current drive
|
|
SAVEA: LD (PEXIT),A ; return character
|
|
DUMMY: RET ; ..and exit ZSDOS
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 101 Set Flags
|
|
; enter: DE=Flags, exit: None
|
|
;--------------------------------------------------
|
|
CMD101: LD (FLAGS),A ; set ZSDOS flags
|
|
; ..and fall through
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 100 Get flags
|
|
; enter: None, exit: HL= Flags
|
|
;--------------------------------------------------
|
|
CMD100: LD A,(FLAGS) ; get ZSDOS flags
|
|
JR SAVEA ; ..and exit
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 30 Set File Attributes
|
|
; enter: DE= Address of FCB, exit: A= Error Code
|
|
;--------------------------------------------------
|
|
CMND30: CALL SELDRV ; select drive from FCB
|
|
CALL CSTAT ; change status bits of file
|
|
JR CMND19A ; and exit
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 12 Get Version Number
|
|
; enter: None, exit: A= Version Number (22H, CP/M compatible)
|
|
;--------------------------------------------------
|
|
ZDPCH1:
|
|
CMND12: LD HL,22H ; set CP/M compatible version number
|
|
LD A,E
|
|
CP 'D' ; is caller testing for DateStamper ?
|
|
JR NZ,CMD12A ; ..if not, jump exit
|
|
LD H,E ; otherwise return DS Active flag
|
|
LD DE,CMD98A ; have a clock, so get clock address
|
|
CMD12A: LD (DEVAL),DE ; in case DS gave us a clock address
|
|
JR SAVHL ; for speed
|
|
|
|
|
|
; The following code section may seem a bit obscure. 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.
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 48 Get DOS and Version
|
|
; enter: None, exit: H= DOS type: 'S'=ZSDOS, 'D'=ZDDOS
|
|
; L= BCD Version Number
|
|
;--------------------------------------------------
|
|
CMND48: LD HL,'S' SHL 8 + VER ; 'S' indicates ZSDOS, ZRDOS returns 0
|
|
DEFB 0DDH ; trash IX and fall through
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 29 Get R/O Vector
|
|
; enter: None, exit: HL= R/O Vector
|
|
;--------------------------------------------------
|
|
CMND29: LD HL,(DSKWP) ; get disk W/P vector
|
|
DEFB 0DDH ; trash IX and fall through
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 39 Get Fixed Disk Vector
|
|
; enter: None, exit: HL= Fixed Disk Vector
|
|
;--------------------------------------------------
|
|
CMND39: LD HL,(HDLOG) ; return fixed disk login vector
|
|
DEFB 0DDH ; trash IX and fall through
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 27 Get Alloc. Address (ALV)
|
|
; enter: None, exit: HL= Address of Allocation Vector
|
|
;--------------------------------------------------
|
|
CMND27: LD HL,(ALV) ; get allocation vector
|
|
DEFB 0DDH ; trash IX and fall through
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 49 Return ENV Address
|
|
; enter: None, exit: HL= Address Env. Descriptor
|
|
;--------------------------------------------------
|
|
CMND49: LD HL,(OZ3ENV) ; get Z3ENV address (Bios base +0x98)
|
|
DEFB 0DDH ; trash IX and fall through
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 24 Get Login Vector
|
|
; enter: None, exit: HL= Login Vector
|
|
;--------------------------------------------------
|
|
CMND24: LD HL,(LOGIN) ; get login vector
|
|
DEFB 0DDH ; trash IX and fall through
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 31 Get DPB Address
|
|
; enter: None, exit: HL= Address of DPB
|
|
;--------------------------------------------------
|
|
CMND31: LD HL,(IXP) ; get drive table
|
|
DEFB 0DDH ; trash IX and fall through
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 47 Get DMA address
|
|
; enter: None, exit : HL= Current DMA Address
|
|
;--------------------------------------------------
|
|
CMND47: LD HL,(DMA) ; get current DMA address
|
|
LD A,H ; test if valid (<> zero)
|
|
OR L
|
|
SAVHL: LD (PEXIT),HL ; save it
|
|
RET ; and exit
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 45 Set error mode
|
|
; enter: E= FFH (Get), exit: A=0 0H
|
|
; FEH (Get Err/Disp)
|
|
; 01H (Set ZSDOS)
|
|
; 00H (Set CP/M)
|
|
;--------------------------------------------------
|
|
; ##### implementation does not comply with documentation
|
|
CMND45: LD (ERMODE),A
|
|
RET
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 32 Set/Get User Code
|
|
; enter: E= FFH (Get), exit: A= User Number
|
|
; User Number (Set) 00H
|
|
;--------------------------------------------------
|
|
CMND32: LD HL,USER ; point to User byte location
|
|
INC A ; test if 0xFF
|
|
LD A,(HL) ; get old user code
|
|
JR Z,SAVEA ; ..if 0xFF, then exit
|
|
LD A,E ; get new User code
|
|
AND 1FH ; mask it
|
|
LD (HL),A ; save it
|
|
RET ; and exit
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 35 Compute File Size
|
|
; enter: DE= Address of FCB, exit: A= Error Code
|
|
;--------------------------------------------------
|
|
CMND35: CALL SELDR1 ; select drive from FCB
|
|
LD A,36 ; 36 bytes
|
|
LD (FCBBUP),A ; ..to update in FCB
|
|
CALL FILSZ ; compute file size
|
|
JR CMND19A ; and exit
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 36 Set Random Record
|
|
; enter: DE= Address of FCB, exit: A= 00H
|
|
;--------------------------------------------------
|
|
CMND36: LD HL,32 ; set pointer to next record
|
|
CALL CALRRC ; calculate random record count
|
|
LDRRC: LD (IX+33),D ; and save it
|
|
LD (IX+34),C
|
|
LD (IX+35),B
|
|
RET ; ..exit
|
|
|
|
|
|
; ##### CHECK: unreferenced code
|
|
PUSH IX
|
|
CALL SELDRV
|
|
JP NOTUSE2
|
|
; #####
|
|
|
|
|
|
;-----
|
|
; Select Disk From FCB
|
|
BGSELDRV:
|
|
SELDRV: LD A,(ERMODE) ; are we in modified User mode ?
|
|
AND A
|
|
JR NZ,SELDR1 ; ..if so, jump
|
|
LD HL,(ARWORD)
|
|
LD BC,FCBUSR ; else, 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 HL,(ARWORD) ; get argument of fcn call (DE= FCB address)
|
|
LD (FCBADR),HL ; ..and save it
|
|
LD DE,ZSFCB ; set to internal FCB
|
|
PUSH DE
|
|
POP IX ; ..copy to IX
|
|
LD (ARWORD),DE ; ..and save
|
|
LD BC,36 ; byte count
|
|
LDIR ; ..and copy
|
|
LD A,16 ; 16 bytes
|
|
LD (FCBBUP),A ; save # bytes to update in FCB
|
|
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 ; ..if yes, then select drive from reg E
|
|
PUSH IX ; save BGii's IX register
|
|
|
|
; IX won't be altered on cmnd14
|
|
LD IX,(ARWORD) ; get FCB pointer
|
|
AND 1FH ; 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
|
|
; in: IX= FCB ptr
|
|
; out: A= User
|
|
LD A,(IX+FCBUSR) ; ..get potential User in case
|
|
BIT 7,A ; is this a valid User ?
|
|
JR NZ,RESUS1 ; ..if there is, then skip
|
|
LD A,(USER) ; get User number
|
|
JR RESUS1 ; ..and bypass push IX
|
|
|
|
; set User in FCB to value passed in A
|
|
RESUSR: PUSH 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
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
; select disk error exit
|
|
; Stack is off by one level here, but there is a one way trip around
|
|
SELDK3: LD HL,(STSEL) ; load error message address
|
|
LD B,4 ; select error
|
|
LD DE,MSEL ; load select error message
|
|
JP JUMPHL ; Select Disk from E register
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;--------------------------------------------------
|
|
; fn # 14 Select Disk
|
|
; enter: E= Disk Number, exit: A= 00H No $*.* File
|
|
; FFH $*.* File
|
|
;--------------------------------------------------
|
|
CMND14: LD A,(DEFDRV) ; get current drive
|
|
LD (DRIVE),A ; save it in memory
|
|
CALL SELSYB ; switch System bank
|
|
JP CMD14B ; ..and continue in Banked code
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;--------------------------------------------------
|
|
; Select Disk
|
|
; (continue CMND14)
|
|
;--------------------------------------------------
|
|
CMD14B: LD A,E ; copy Drive number
|
|
|
|
; call w/ A= drive no. (0..15 = A..P)
|
|
SELDK: LD HL,(LOGIN) ; get login vector
|
|
AND 0FH ; mask Drive number
|
|
LD B,A ; save counter
|
|
CALL NZ,SHRHLB
|
|
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 ; ..if not, log it in
|
|
CP (HL) ; test same Drive ?
|
|
RET Z ; ..if yes, then exit
|
|
|
|
; NOTE:
|
|
; A long standing DOS bug concerns the SELECT function. If fcn 14 is
|
|
; called 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 ot, 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
|
|
LD B,A
|
|
CALL DRVOK ; test if Drive is valid (Z3ENV Drive vector)
|
|
JR NC,SELDK3 ; ..if not, exit
|
|
CALL SELDSK ; do Bios call Select Disk
|
|
LD A,H ; test if error
|
|
OR L
|
|
JR Z,SELDK3 ; ..if yes, illegal Drive number
|
|
LD DE,TRANS ; point to local translation storage
|
|
LD BC,2 ; ..and move 2-byte ptr in
|
|
LDIR
|
|
LD (TEMP0),HL ; save address in 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 ; if disabled, skip
|
|
|
|
|
|
; The following code checks the WACD size to determine if the drive
|
|
; being selected is a fixed disk. If WACD is 0, the disk is non-
|
|
; removable. However, BIOS may support remapping of logical drives.
|
|
; Therefore BDOS must catch the swap and clear the Hard Disk ALV and
|
|
; allow the allocation bitmaps to be rebuilt. Hence, 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.
|
|
; 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, skip over
|
|
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 fixed disk ?
|
|
JR Z,SELDK5 ; invalidate HDLOG vector
|
|
; (drive no longer considered fixed disk)
|
|
LD A,C
|
|
OR A ; wasn't fixed disk before - is it now ?
|
|
JR NZ,INITDR ; ..if it isn't, skip vector update
|
|
LD HL,(HDLOG)
|
|
CALL HLORDE ; else, add this drive to fixed disk vector
|
|
SELDK5: LD (HDLOG),HL ; update fixed disk vector
|
|
; ..and fall through 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
|
|
LD A,0FFH ; set initial value for flag
|
|
LD (FINITD),A
|
|
|
|
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
|
|
CALL CALDIR ; calculate entry point FCB
|
|
LD A,(HL) ; get first byte FCB
|
|
CP 0E5H ; test empty Directory entry
|
|
JR Z,INITD2 ; ..if yes, then get next FCB
|
|
AND 7FH ; mask
|
|
CP 21H ; test time stamp
|
|
JR Z,INITD2 ; ..if yes, then get next FCB
|
|
|
|
|
|
; check if FCB belongs to !!!TIME&.DAT file
|
|
LD A,(FINITD) ; get first loop flag
|
|
OR A ; test if zero
|
|
JR Z,INITD5 ; ..if yes, then jump
|
|
; to prevent re-execution of following code
|
|
INC A ; ..else, increment
|
|
LD (FINITD),A ; and save flag again
|
|
LD BC,00C00H ; set counter B=12, and clear C
|
|
PUSH HL
|
|
INITD3: LD A,(HL) ; get byte from FCB
|
|
AND 7FH ; mask high bit
|
|
ADD A,C ; add in previous result
|
|
LD C,A ; ..and save it in C
|
|
INC HL ; move ptr forward
|
|
DJNZ INITD3 ; loop till done
|
|
CP TDCKSM ; test if matches checksum of !!!TIME&.DAT
|
|
JR NZ,INITD4 ; ..if not, skip over
|
|
LD HL,(TDFVCT) ; else, get T/D vector
|
|
CALL SDRVB ; set drive bit in HL
|
|
LD (TDFVCT),HL ; and save T/D vector again
|
|
INITD4: POP HL
|
|
|
|
ZDPCH3 EQU $ ; <--- Test for T&D if first time (ZDS Patch)
|
|
|
|
INITD5: 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 ; get next FCB
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;-----
|
|
; 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 ; ..if yes, then done
|
|
SDRVB0: ADD HL,HL ; get next mask
|
|
DEC A ; decrement drive number
|
|
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 ; ZSDOS v1 comment: "used in a few places"
|
|
; ##### used exactly once - make INITDR inline
|
|
|
|
;-----
|
|
; shift HL right logical B bits
|
|
SHRHLB: SRL H
|
|
RR L ; Shift HL right one bit (divide by 2)
|
|
DJNZ SHRHLB
|
|
RET
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;-----
|
|
; calculate Sector/Track directory
|
|
STDIR: LD HL,(FILCNT) ; get FCB counter Directory
|
|
SRL H ; divide by 4
|
|
RR L
|
|
SRL H
|
|
RR L
|
|
LD (RECDIR),HL ; save value (used by checksum)
|
|
STDIR2: EX DE,HL ; move to DE
|
|
STDIR1: LD HL,0 ; clear HL
|
|
|
|
|
|
; calculate Sector/Track
|
|
; Track= HL,DE / MAXSEC, Sector= HL,DE MOD MAXSEC
|
|
; in: HL, DE= sector number (128 byte sector)
|
|
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 ; ..if yes, then jump
|
|
ADD HL,BC ; else, restore HL
|
|
OR A ; clear Carry
|
|
CALST1: RL E ; shift result in DE
|
|
RL D
|
|
DEC A ; test last bit done
|
|
JR Z,CALST2 ; ..if yes, then exit
|
|
ADC HL,HL ; else, 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 ; Bios call Set Track
|
|
POP BC ; restore sector number
|
|
LD DE,(TRANS) ; get translation table address
|
|
CALL SECTRN ; Bios call Sector Translation
|
|
LD B,H ; copy result in BC
|
|
LD C,L
|
|
JP SETSEC ; Bios call Set Sector
|
|
|
|
|
|
; get Disk map block number from FCB
|
|
; out: HL= FCB address
|
|
; DE= DM
|
|
; BC= offset in DM
|
|
; Zero Flag set (Z) if DM= 0, else reset (NZ)
|
|
; (squeezed by Joe Wright)
|
|
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 in 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 ; ..if 8-bit entries, exit
|
|
|
|
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
|
|
; in: 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
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;-----
|
|
; check file Read-Only status, then fall through to CALDIR
|
|
CKRODI: CALL CHKFRO ; abort if the file is R/O
|
|
; ..fall through
|
|
|
|
|
|
;-----
|
|
; calculate DIRBUF entry point
|
|
CALDIR: LD A,(SECPNT) ; get sector pointer
|
|
CALDIR1: LD HL,(DIRBUF) ; get start address dirbuf
|
|
CALDI0: ADD A,L ; add L=L+A
|
|
LD L,A
|
|
RET NC ; ..if no Carry, then 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
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 28 Write Protect Disk
|
|
; enter: None, exit: A= 00H
|
|
;--------------------------------------------------
|
|
CMND28: 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 inline here
|
|
SETLF0: INC DE ; increment last file
|
|
LD (HL),D ; save it in TEMP0
|
|
DEC HL
|
|
LD (HL),E
|
|
RET ; and exit
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;-----
|
|
; search using first 15 bytes of FCB, test if found
|
|
SRCT15: CALL SEAR15 ; search on 15-bytes..
|
|
; ..fall through to test presence
|
|
|
|
|
|
;-----
|
|
; test file count
|
|
TSTFCT: LD HL,(FILCNT) ; test file count= 0xFFFF
|
|
LD A,H ; get MSB
|
|
AND L ; And LSB
|
|
INC A ; test if result= 0xFF
|
|
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 ; and exit
|
|
|
|
|
|
;-----
|
|
; get next FCB from Drive
|
|
; in: 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
|
|
JP C,SETFCT ; ..if so, set file count to 0xFFFF
|
|
LD A,L ; get file count LSB
|
|
RRCA ; *32
|
|
RRCA
|
|
RRCA
|
|
AND 60H ; mask it
|
|
LD (SECPNT),A ; save it for later use
|
|
RET NZ ; ..if not first FCB sector, return
|
|
PUSH BC ; save checksum flag
|
|
CALL STDIR ; calculate sector/track Directory
|
|
RDDIR2: CALL DMADIR ; set up DMA Directory
|
|
CALL READR ; read a record
|
|
CALL STDMA ; ..and set up user's DMA
|
|
POP BC ; check/update checksum Directory
|
|
|
|
; 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
|
|
SBC HL,DE ; test current record
|
|
RET Z ; ..if zero, exit
|
|
RET C ; ..if greater than NCHECK, exit
|
|
CALL CKS127 ; checksum first 127 bytes
|
|
ADD A,(HL) ; ..then 128th byte
|
|
LD HL,(CSV) ; get pointer checksum directory
|
|
ADD HL,DE ; add current record
|
|
INC C ; test checksum flag
|
|
JR NZ,CHKDR1 ; 0xFF -> update checksum
|
|
LD (HL),A ; update checksum
|
|
RET ; and exit
|
|
|
|
CHKDR1: CP (HL) ; test checksum
|
|
RET Z ; ..if ok, exit
|
|
|
|
; checksum differs, so Disk has changed - relog 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 ; transfer 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
|
|
JR RDDIR ; ..and all checking is done in RDDIR
|
|
|
|
|
|
;-----
|
|
; read sector from Drive
|
|
READR: CALL READ ; Bios call Read Sector
|
|
JR WRITE0
|
|
|
|
|
|
;-----
|
|
; write sector on Drive
|
|
WRITER: CALL WRITE ; Bios call Write Sector
|
|
WRITE0: OR A ; test exit code
|
|
RET Z ; ..if ok, exit
|
|
LD B,1 ; disk I/O error code
|
|
LD DE,MBADSC ; load bad sector message
|
|
LD HL,(STBDSC) ; load bad sector vector
|
|
JP JUMPHL ; output error
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
; ##### CHECK: unreferenced code
|
|
PUSH IX
|
|
CALL CMND16
|
|
; #####
|
|
|
|
|
|
; JP target of unreferenced code between CMND36 and SELDRV
|
|
NOTUSE2: LD HL,(FCBADR)
|
|
LD (ARWORD),HL
|
|
; JP target of unreferenced code between CMND34 and CMND21
|
|
NOTUSE3: POP IX
|
|
JP SELTPB ; select TPA bank
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 16 Close File
|
|
; enter: DE= Address of FCB, exit: A= Directory Code
|
|
;--------------------------------------------------
|
|
|
|
BGPTCH2 EQU $+1 ; <--- BGii patch point
|
|
|
|
CMND16: CALL SELDR1 ; select Drive from FCB
|
|
; (also switches System bank)
|
|
JP CLOSE ; ..and continue in Banked code
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;--------------------------------------------------
|
|
; Close File
|
|
; (continue CMND16)
|
|
;--------------------------------------------------
|
|
CLOSE: BIT 7,(IX+FCBMOD) ; test FCB/file modified
|
|
RET NZ ; ..if not, no close required
|
|
CALL CHKRO ; test disk W/P
|
|
CALL SRCT15 ; search file and test present
|
|
RET Z ; ..if not, 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 ; transfer 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 ; ..if 0, ok to copy
|
|
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= pointer to extent number
|
|
LD A,(IX+FCBEXT) ; get extent number FCB
|
|
CP (HL) ; compare with extent number Directory
|
|
JR C,CLOSE3 ; ..if 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
|
|
JR Z,CLOSE4
|
|
PUSH BC ; save prior module and extent
|
|
LD BC,0
|
|
CALL SETDME ; set FCB data module and extent to 0
|
|
CALL SRCT15 ; find proper DIR entry
|
|
POP BC
|
|
JR Z,JSETDME ; ..if extent 0 not found, exit
|
|
CLOSE4: PUSH BC
|
|
CALL CLOSE6 ; clear Archive bit and write FCB
|
|
LD B,4 ; mask for STFLAG (bit2 = Modify stamp)
|
|
LD HL,STUP ; address of stamp update routine
|
|
CALL STPMSK ; mask stamp type and update if enabled
|
|
POP BC ; get original module and extent bak
|
|
JSETDME: 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
|
|
|
|
; write FCB to disk
|
|
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 setor/track Directory
|
|
LD C,0FFH ; update checksum Directory
|
|
CALL CHKDIR
|
|
WRITD1: CALL DMADIR ; set up DMA Directory (label for DS)
|
|
LD C,1 ; write Directory flag
|
|
CALL WRITER ; write record
|
|
JP STDMA ; set up DMA user
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;--------------------------------------------------
|
|
; fn # 26 Set DMA Address
|
|
; enter: DE= DMA Address, exit: A= 00H
|
|
;--------------------------------------------------
|
|
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 ; Bios call Set DMA
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;-----
|
|
; get bit from ALV buffer
|
|
; in: DE= block number
|
|
; out: 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 ; +1
|
|
LD C,A ; save it
|
|
SRL D ; get byte number
|
|
RR E ; divide DE by 8
|
|
SRL D
|
|
RR E
|
|
SRL D
|
|
RR E
|
|
LD HL,(ALV) ; get start address ALV buffer
|
|
LD B,A ; save bit number for next shift
|
|
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
|
|
|
|
|
|
;--------------------------------------------------
|
|
; Delete File
|
|
; (continue CMND19)
|
|
;--------------------------------------------------
|
|
DELETE: CALL COMCOD ; call common code w/ VDEL on stack
|
|
|
|
; delete core routine
|
|
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, skip over
|
|
LD (SUBFLG),A ; clear subflag if $*.* erased
|
|
VDEL1: INC HL
|
|
RES 7,(HL) ; ensure erased files are not public
|
|
LD C,0 ; remove bits ALV buffer
|
|
; ..fall through and return to caller
|
|
|
|
|
|
; fill bit buffer from FCB in DIRBUF
|
|
; in: 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 counter
|
|
LD A,(MAXLEN+1) ; test >256 blocks present
|
|
OR A
|
|
JR Z,FILLB1 ; ..if not, jump
|
|
DEC B ; decrement block counter
|
|
LD D,(HL) ; get correct MSB
|
|
INC HL ; increment block counter
|
|
FILLB1: LD A,D ; test block number
|
|
OR E
|
|
JR Z,FILLB2 ; ..if 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
|
|
JR C,FILLB3 ; ..if yes, skip insert bit
|
|
|
|
; set/reset bit in ALV buffer
|
|
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
|
|
FILLB3: 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
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;-----
|
|
; 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
|
|
LD A,(DE) ; retrieve the actual byte
|
|
AND A ; ..and check it
|
|
JR NZ,CHKFR4 ; we have Wheel, so allow writes
|
|
BIT 7,(HL) ; else, check Wheel attribute
|
|
JR NZ,CHKFR2 ; ..if yes, then jump error
|
|
CHKFR4: INC HL ; check W/P bit
|
|
BIT 7,(HL) ; test file W/P
|
|
JR NZ,CHKFR2 ; ..if W/P, then jump
|
|
CHKFR3: BIT 7,(IX+PSFATT) ; was file accessed as Pubic or Path ?
|
|
RET Z ; ..if normal access, return
|
|
LD A,(FLAGS) ; else, test for writes allowed
|
|
AND 0010B
|
|
RET NZ ; ..if writes allowed, go ahead
|
|
CHKFR2: LD HL,(SFILRO) ; ptr file W/P message (CP/M 2.2 vector table)
|
|
LD B,3 ; file W/P error code
|
|
LD DE,MFILRO ; load file W/P message
|
|
JP JUMPHL ; display message
|
|
|
|
|
|
;-----
|
|
; check Drive Write Protect
|
|
BGCKDRO:
|
|
CHKRO: CALL CHKRO1 ; is the disk W/P ?
|
|
RET NZ ; ..if disk is R/W, then return
|
|
LD B,2 ; else, set disk W/P error code
|
|
LD DE,MRO ; load drive W/P message
|
|
LD HL,(STRO) ; ptr Drive W/P message (CP/M 2.2 vector table)
|
|
JP JUMPHL ; display message
|
|
|
|
CHKRO1: LD HL,(DSKWP) ; get W/P drive vector
|
|
CALL SDRVB ; set the bit for this drive
|
|
SBC HL,DE ; see if extra bit added (Carry is clear)
|
|
RET
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;-----
|
|
; search using first 12 bytes of FCB
|
|
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
|
|
; in: A= number of bytes for which to search
|
|
SEARCH: LD (SEARNB),A ; save number of bytes
|
|
LD A,0FFH ; set exit code to 0xFF (not found)
|
|
LD (SEAREX),A
|
|
LD (DCOPY),IX ; copy FCB pointer to RAM (search next)
|
|
CALL SETFCT ; initiate file counter
|
|
|
|
; force directory read with call home (Floppy only)
|
|
LD HL,(NCHECK) ; is this a fixed media ?
|
|
LD A,H
|
|
OR L
|
|
CALL NZ,HOME ; if removable, invoke Bios call Home routine
|
|
|
|
; search next File Name
|
|
SEARCN: XOR A ; checksum Directory
|
|
LD H,A
|
|
LD L,A
|
|
LD (SEARQU),HL ; clear flags, question mark and PUBlic found
|
|
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 ; ..if yes, 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 ; ..if 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 ; ..if yes, then jump
|
|
|
|
SEARC1: CALL CALDIR ; get entry in Directory
|
|
LD A,(HL) ; get first byte Directory entry
|
|
AND 7FH ; mask high bit
|
|
CP 21H ; test time stamp
|
|
JR Z,SEARCN ; ..if 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 character is zero
|
|
OR A
|
|
JR Z,SEARC9 ; ..if yes, then jump
|
|
LD A,(DE) ; get byte from FCB
|
|
XOR '?' ; test if question mark
|
|
AND 7FH ; mask it
|
|
JR Z,SEARC6 ; ..if yes, then jump
|
|
LD A,C ; get FCB counter
|
|
OR A ; test first byte
|
|
JR NZ,SEARC3 ; ..if not, then jump
|
|
LD A,(FLAGS) ; get flag byte
|
|
RRA ; test PUBlic file enable
|
|
JR NC,SEARC3 ; ..if not, jump
|
|
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 ; ..if no PUBlic file, then jump
|
|
LD A,(DE) ; get first byte FCB
|
|
CP 0E5H ; test if searching empty Directory
|
|
JR Z,SEARC3 ; ..if yes, then jump
|
|
|
|
|
|
; The following 3 lines of code represent a deviation from description
|
|
; of PUBlic files as given in DDJ article by Bridger Mitchell and Derek
|
|
; McKay of Plu*Perfect Systems. The 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 as follows:
|
|
; If we are in the same User area as the PUBlic file, then don't report
|
|
; file as PUBlic, but find it. This has a nasty side effect - PUBlic
|
|
; files can be erased if we are in the same area. However, these files
|
|
; also show up on the directory (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 ; ..if user is same, jump
|
|
|
|
LD A,0FFH
|
|
LD (SEARPU),A ; set PUBlic file found
|
|
SET 7,(IX+PSFATT) ; set PUBlic/SYStem file flag
|
|
JR SEARC5 ; jump found
|
|
|
|
SEARC3: LD A,C ; get FCB counter
|
|
CP 13 ; is it User code ?
|
|
JR Z,SEARC5 ; ..if so, jump (don't test)
|
|
CP 12 ; is it an extent number ?
|
|
LD A,(DE) ; get byte from FCB
|
|
JR Z,SEARC7 ; ..if extent number, then jump
|
|
XOR (HL) ; is FCB byte = Directory Entry byte ?
|
|
AND 7FH ; mask it
|
|
SEARC4: JR NZ,SEARCN ; ..if not the same, jump 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 0xFF and exit
|
|
|
|
SEARC9: LD HL,(SEARQU) ; get question mark and PUBlic found flags
|
|
LD A,H
|
|
AND L
|
|
JR NZ,SEARC4 ; ..if 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
|
|
|
|
|
|
;-----
|
|
; common code of DELETE, RENAME, and CSTAT
|
|
; (coded in a manner that is compatible with 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 ; ..if not, then exit
|
|
PUSH HL ; else, 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
|
|
|
|
; rename core routine
|
|
VRENAM: CALL CHKFRO ; check file W/P
|
|
LD HL,(ARWORD) ; get FCB address
|
|
LD DE,16 ; offset to new name
|
|
ADD HL,DE ; add offset
|
|
EX DE,HL ; swap regs
|
|
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 ; ..if not, then change character on disk
|
|
LD A,(HL) ; else, no change, so get what's there
|
|
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 ; got to common code w/ VCSTAT on stack
|
|
|
|
; change core routine
|
|
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 ; ..if not, jump
|
|
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 ; ..if we have Wheel, jump
|
|
BIT 7,(HL) ; is file Wheel protected ?
|
|
JP NZ,CHKFR2 ; ..if so, jump
|
|
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 till 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
|
|
FILSZ0: CALL TSTFCT ; test if file found
|
|
RET Z ; ..if not, 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
|
|
; (called by CMND15)
|
|
;--------------------------------------------------
|
|
FINDF: CALL SRCT15 ; search file
|
|
RET NZ ; ..if found, then exit
|
|
LD A,(FLAGS)
|
|
BIT 5,A ; test if Path enabled
|
|
RET Z ; ..if not, exit
|
|
LD HL,(PATH) ; get Path address
|
|
LD A,H ; test if zero (no Path)
|
|
OR L
|
|
RET Z ; ..if so, exit
|
|
|
|
FINDF0: CALL DUPATH ; test if current DU: is in Path
|
|
JP Z,SEARC8 ; ..if not, then jump
|
|
LD A,B ; Drive in A
|
|
PUSH BC ; save DU
|
|
PUSH HL
|
|
CALL SELDK
|
|
; ##### PUSH HL first, then BC
|
|
; saves POP HL + next PUSH HL (2 bytes)
|
|
POP HL
|
|
POP BC
|
|
LD A,C ; User in A
|
|
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 ; ..if not present, 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 attribute not required
|
|
JR Z,FINDF0 ; else, SYStem file, so test next Path entry
|
|
FINDF3: LD A,(DEFDRV) ; get current Drive
|
|
INC A ; increment Drive number
|
|
LD (FCB0),A ; save it in exit FCB0
|
|
SET 7,(IX+PSFATT) ; set PUBlic/SYStem file flag
|
|
RET ; and return to caller
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;--------------------------------------------------
|
|
; fn # 15 Open File
|
|
; enter: DE= address of FCB, exit: A= Directory Code
|
|
;--------------------------------------------------
|
|
CMND15: CALL SELDRV ; select Drive from FCB
|
|
; (also switches System bank)
|
|
LD A,32 ; 32 bytes
|
|
LD (FCBBUP),A ; ..to update in FCB
|
|
LD (IX+FCBMOD),0 ; clear data module number
|
|
CALL FINDF ; find file using Path (..in Banked code)
|
|
CALL TSTFCT ; test file found (..in Banked code)
|
|
RET Z ; ..if not, 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 DE
|
|
LD BC,32 ; number of bytes to move
|
|
LDIR ; move Directory to FCB
|
|
LD (IX+FCBUSR),A ; and put User byte back
|
|
SET 7,(IX+FCBMOD) ; 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 ; ..if same, then jump
|
|
LD C,0 ; set next record count to 0
|
|
RR C ; record count to max (0x80) 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+7
|
|
RLA ; set new MSB in Carry
|
|
RR (IX+PSFATT) ; save Carry in IX+7
|
|
LD B,1 ; mask for STFLAG (bit0 = Access stamp)
|
|
LD HL,STLA ; address of last accessed routine
|
|
JP STPMSK ; ..and continue in Banked code
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 22 Make File
|
|
; enter: DE= Address of FCB, exit: A= Directory Code
|
|
;--------------------------------------------------
|
|
CMND22: CALL SELDRV ; select Drive from FCB
|
|
LD (IX+FCBMOD),0 ; clear data module number
|
|
LD A,32 ; 32 bytes
|
|
LD (FCBBUP),A ; save # bytes to update in FCB
|
|
MAKES: CALL CHKRO ; check drive W/P
|
|
CALL SELSYB ; switch System bank
|
|
JP MAKESB ; ..and continue in Banked code
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;--------------------------------------------------
|
|
; Make File
|
|
; (continue CMND22 / MAKES)
|
|
;--------------------------------------------------
|
|
MAKESB: LD HL,(ARWORD)
|
|
LD A,(HL) ; get first byte of 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 of FCB
|
|
LD (IX+0),A ; restore it
|
|
CALL TSTFCT ; test empty file found
|
|
RET Z ; ..if not, 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 counter
|
|
CALL CLRMEM ; clear FCB+15 up to FCB+31
|
|
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 copy
|
|
LDIR
|
|
CALL WRFCB ; write FCB on disk
|
|
SET 7,(IX+FCBMOD) ; set FCB/File not modified
|
|
LD B,2 ; mask for STFLAG (bit1 = Create stamp)
|
|
LD HL,STCR ; address stamp create routine
|
|
JP STPMSK ; mask type and stamp file if enabled
|
|
|
|
|
|
;-----
|
|
; open next extent
|
|
OPENEX: BIT 7,(IX+FCBMOD) ; test if FCB/File modified (write)
|
|
JR NZ,OPENX2 ; ..if not, jump
|
|
CALL CLOSE ; close current FCB
|
|
LD A,(PEXIT) ; get exit code
|
|
INC A ; test if error
|
|
RET Z ; ..if so, exit
|
|
OPENX2: CALL CALNEX ; calculate next extent
|
|
JR C,OPENX3 ; ..if error, jump
|
|
|
|
OPENX0: CALL SRCT15 ; search for 15-char match and test presence
|
|
JR NZ,OPENX5 ; ..if yes, jump
|
|
LD A,(RDWR) ; test Read/Write flag
|
|
OR A ; test if read
|
|
JR Z,OPENX3 ; ..if yes, then error
|
|
CALL MAKES ; make new extent if write
|
|
CALL TSTFCT ; test if successful
|
|
JR NZ,OPENX6 ; ..if yes, then exit
|
|
OPENX3: SET 7,(IX+FCBMOD) ; set FCB/File not modified
|
|
RETCFF: LD A,0FFH ; set exit code
|
|
OPENX4: JP SAVEA ; and return to caller
|
|
|
|
OPENX5: CALL OPENF0 ; open file
|
|
OPENX6: XOR A ; clear exit code
|
|
JR OPENX4 ; use same code to exit routine
|
|
|
|
|
|
;-----
|
|
; calculate next extent
|
|
; out: Carry set (C) if 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 ; ..if non-zero, error exit
|
|
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,CALNE1 ; ..if new data module not required, jump
|
|
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 ; ..if file overflow, return
|
|
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
|
|
AND A ; clear flag
|
|
RET
|
|
|
|
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
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;--------------------------------------------------
|
|
; fn # 33 Read Random Record
|
|
; enter: DE= Address of FCB, exit: A= Read/Write Code
|
|
;--------------------------------------------------
|
|
CMND33: CALL SELDR1 ; select Drive from FCB
|
|
LD A,36
|
|
LD (FCBBUP),A ; save # bytes to update in FCB
|
|
|
|
; read random sector
|
|
XOR A ; set Read/Write flag
|
|
CALL LDFCB ; load FCB random record (..in Banked code)
|
|
JR Z,READS ; ..if no error, then read sector
|
|
RET
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 20 Read Sequential
|
|
; enter: DE= Address of FCB, exit: A= Read/Write Code
|
|
;--------------------------------------------------
|
|
CMND20: CALL SELDR1 ; select Drive from FCB
|
|
; (also switches System bank)
|
|
LD A,33 ; 33 bytes
|
|
LD (FCBBUP),A ; save # bytes to update in FCB
|
|
|
|
; read sector
|
|
READS: JP READSB ; ..continue in Banked code
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;--------------------------------------------------
|
|
; Read Sector
|
|
; (continue CMND20 / READS)
|
|
;--------------------------------------------------
|
|
READSB: XOR A ; set Read/Write flag
|
|
LD (RDWR),A ; save it
|
|
LD A,(IX+NXTREC) ; get record counter
|
|
CP 80H ; test if last record in this extent
|
|
JR Z,READSB1 ; ..if yes, then open next extent
|
|
CP (IX+FCBREC) ; test if greater than current record
|
|
JR C,READSB2 ; ..if not, then get record
|
|
READSB0: LD A,1 ; set end of file flag
|
|
JP SAVEA ; and exit
|
|
|
|
READSB1: CALL OPNXCK ; open next extent
|
|
READSB2: CALL GETDM ; get block number from DM in FCB
|
|
JR Z,READSB0 ; ..if block number= 0, jump to end of 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 ; return if open OK
|
|
POP HL ; else, pop return address to abort R/W
|
|
JR READSB0 ; ..and set error code to EOF
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;--------------------------------------------------
|
|
; fn # 34 Write Random
|
|
; fn # 40 Write random with zero fill
|
|
; enter: DE= Address of FCB, exit: A= Read/Write Code
|
|
;--------------------------------------------------
|
|
CMND34:
|
|
CMND40: CALL SELDR1 ; select Drive from FCB
|
|
LD A,36 ; 36 bytes
|
|
LD (FCBBUP),A ; save # bytes to update in FCB
|
|
|
|
; write random sector and write random with zero fill
|
|
LD A,0FFH ; set Read/Write flag
|
|
CALL LDFCB ; load FCB random record (..in Banked code)
|
|
JR Z,WRITES ; ..if no error, then write record
|
|
RET ; else, return error
|
|
|
|
|
|
; ##### CHECK: unreferenced code
|
|
CALL CHKRO ; check drive W/P status
|
|
PUSH IX
|
|
LD IX,ZSFCB ; buffer internal FCB
|
|
CALL NOTUSE1 ; write sector
|
|
JP NOTUSE3
|
|
RET
|
|
; #####
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 21 Write Sequential
|
|
; enter: DE= Address of FCB, exit: A= Read/Write Code
|
|
;--------------------------------------------------
|
|
CMND21: CALL SELDR1 ; select Drive from FCB
|
|
LD A,33 ; 33 bytes
|
|
LD (FCBBUP),A ; save # bytes to update in FCB
|
|
|
|
; write sector - permitted to PUBlic file and those found along Path
|
|
WRITES: LD A,0FFH ; set Read/Write flag
|
|
LD (RDWR),A ; save it
|
|
|
|
BGPTCH1 EQU $+1 ; <--- patched location for BGii
|
|
|
|
CALL CHKRO ; check disk W/P
|
|
NOTUSE1: CALL SELSYB ; switch System bank
|
|
JP WRITESB ; ..and continue in Banked code
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;--------------------------------------------------
|
|
; Write Sector
|
|
; (continue CMND21 / WRITES)
|
|
;--------------------------------------------------
|
|
WRITESB: BIT 7,(IX+ROATT) ; test if file W/P
|
|
JR NZ,WRITSA ; ..if 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 Wheel privileges ?
|
|
AND A
|
|
JR NZ,WRITSB ; ..if yes, allow write
|
|
BIT 7,(IX+WHLATT) ; else, test if file is Wheel protected
|
|
WRITSA: JP NZ,CHKFR2 ; ..if so, then file W/P message
|
|
WRITSB: BIT 7,(IX+NXTREC) ; end of this extent ?
|
|
CALL NZ,OPNXCK ; open next extent and check status
|
|
CALL GETDM ; get block number from FCB
|
|
JP NZ,WRITS5 ; ..if not zero, then jump to write sector
|
|
PUSH HL ; save pointer to block number
|
|
LD A,C ; test first block number in extent
|
|
OR A
|
|
JR Z,WRITS1 ; ..if yes, then jump
|
|
DEC A ; decrement pointer to block number
|
|
CALL GETDM4 ; get previous block number
|
|
|
|
|
|
; get free block from ALV buffer
|
|
; in: DE= old block number
|
|
; out: DE= new block number, 0 if no free block
|
|
; (HL counts up, DE counts down)
|
|
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 ; ..if so, jump
|
|
DEC DE ; decrement down counter
|
|
PUSH HL ; save up/down counter
|
|
PUSH DE
|
|
CALL GETBIT ; get bit from ALV buffer
|
|
RRA ; test if zero
|
|
JR NC,GETFR3 ; ..if 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 ; ..if end of buffer, then jump
|
|
INC HL ; increment up counter
|
|
PUSH DE ; save down/up counter
|
|
PUSH HL
|
|
EX DE,HL ; up counter in DE
|
|
CALL GETBIT ; get bit from ALV buffer
|
|
RRA ; test if zero
|
|
JR NC,GETFR3 ; ..if 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 testet
|
|
OR E
|
|
JR NZ,GETFR0 ; ..if not, then test next block
|
|
JR WRITSG ; continue with DE= 0
|
|
|
|
GETFR3: SCF ; set block number used
|
|
RLA ; save bit
|
|
|
|
; SETBT0 code now inline
|
|
GETFR4: RRCA ; rotate bit in correct position
|
|
DJNZ GETFR4
|
|
LD (HL),A ; save 8 bits
|
|
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 block number = 0
|
|
|
|
; WRITS8 code now inline
|
|
OR E
|
|
LD A,2 ; set disk full error
|
|
JP Z,SAVEA ; and return to caller
|
|
RES 7,(IX+FCBMOD) ; reset FCB/File modified
|
|
LD (HL),E ; save block number
|
|
LD A,(MAXLEN+1) ; get number of blocks
|
|
OR A ; is it < 256 ?
|
|
JR Z,WRITS2 ; ..if so, jump
|
|
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 number
|
|
JR Z,WRITSX ; ..if 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 Random Record with zero fill
|
|
JR NZ,WRITS6 ; ..if not, then jump
|
|
PUSH DE ; save block number
|
|
LD HL,(DIRBUF) ; use Directory buffer for zero fill
|
|
LD B,128 ; 128 bytes to clear
|
|
CALL CLRMEM ; do clear
|
|
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 sectors/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 sectors/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
|
|
RET C ; return if < 20
|
|
CP 21+1
|
|
RET NC ; return if > 21
|
|
INC (IX+NXTREC) ; increment record count
|
|
RET ; and return to caller
|
|
|
|
|
|
;--------------------------------------------------
|
|
; Load FCB for random read/write
|
|
; (called by CMND33 / CMND34 / CMND40)
|
|
;--------------------------------------------------
|
|
; out: Zero Flag set (Z) = no error, reset (NZ) if error
|
|
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 00FH ; mask it
|
|
LD B,A ; save data module number
|
|
LD A,(IX+35)
|
|
LD E,6 ; set random record too large flag
|
|
CP 4 ; test random record too large
|
|
JR NC,LDFCB8 ; ..if 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 ; ..if yes, then jump
|
|
LD A,C ; get new extent number
|
|
CP (IX+FCBEXT) ; compare with FCB
|
|
JR NZ,LDFCB0 ; ..if 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 ; ..if equal, then return
|
|
LDFCB0: BIT 7,D ; test FCB modified (write)
|
|
JR NZ,LDFCB1 ; ..if 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 ; ..if 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 ; if no error, then exit
|
|
LD A,(RDWR) ; get Read/Write flag
|
|
LD E,4 ; set read empty record
|
|
INC A
|
|
JR NZ,LDFCB7 ; ..if read error, then jump
|
|
CALL MAKES ; make new FCB
|
|
LD E,5 ; set make error
|
|
LD A,(PEXIT) ; get error code
|
|
INC A
|
|
JR Z,LDFCB7 ; ..if error, then exit
|
|
JR LDFCB6 ; else, exit w/ zero set (no error)
|
|
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) ; set FCB/File not modified
|
|
RET ; and return to caller
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;-----
|
|
; calculate random record
|
|
; in: HL= offset in FCB
|
|
; DE= FCB pointer
|
|
; out: D= LSB random record
|
|
; C= ISB
|
|
; B= MSB
|
|
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 (00xeeeeex)
|
|
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 00mmmmmmm
|
|
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
|
|
|
|
|
|
;-----
|
|
; clear/fill memory
|
|
CLRM4: LD B,4 ; clear memory area (used by MAKES and WRITSX)
|
|
|
|
CLRMEM: XOR A ; clear A
|
|
|
|
; fill memory with byte in A
|
|
; in: A= byte
|
|
; B= counter (# bytes)
|
|
; HL= destination
|
|
FILLM: LD (HL),A ; store byte
|
|
INC HL ; increment pointer
|
|
DJNZ FILLM ; and clear all bytes
|
|
RET
|
|
|
|
|
|
;-----
|
|
; check/update !!!TIME&.DAT checksum
|
|
; calc checksum of 127 bytes, return with HL pointing to 128th byte
|
|
; in: DIRBUF= pointer to T&D record
|
|
; out: A= checksum
|
|
; HL= points to checksum byte in record
|
|
CKS127: LD HL,(DIRBUF) ; directory buffer pointer
|
|
XOR A ; clear A
|
|
LD B,127 ; test first 127 bytes
|
|
CKSLP: ADD A,(HL) ; sum all bytes to A
|
|
INC HL
|
|
DJNZ CKSLP
|
|
RET
|
|
|
|
|
|
;-----
|
|
; get word (16-bit value) indirectly
|
|
; in: HL= base addr
|
|
; A= offset
|
|
; out: HL= 16-bit value at offset addr
|
|
; Z-Flag set if HL= 0
|
|
GWHLA: CALL CALDI0 ; add A to HL
|
|
LD A,(HL) ; get low byte at memory location
|
|
INC HL
|
|
LD H,(HL) ; get high byte
|
|
LD L,A
|
|
OR H ; test if zero (set return status)
|
|
RET
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;-----
|
|
; test if Drive is valid
|
|
; in: B= drive number
|
|
; out: Carry flag set (C) = ok, NC= not ok
|
|
DRVOK: LD HL,(OZ3ENV) ; B/P Bios base +0x98 (addr Z3 ENV decriptor)
|
|
LD A,H ; test if valid (<> zero)
|
|
OR L
|
|
JR Z,DRVOK1 ; ..if not, jump exit (assume drive is valid)
|
|
LD A,8 ; else, offset to Env type (ENV base +8)
|
|
CALL CALDI0 ; move ptr fwd
|
|
BIT 7,(HL) ; is it extended (=> 0x80) ?
|
|
JR Z,DRVOK1 ; ..if not, jump
|
|
LD A,44 ; move ptr fwd to DRVEC
|
|
CALL GWHLA ; ..and get vector of valid drives in HL
|
|
LD A,16 ; set up loop counter
|
|
SUB B ; adjust to drive in scope
|
|
DRVOK0: DEC A ; counter -1
|
|
ADD HL,HL ; "shift" MSB into Carry
|
|
JR NZ,DRVOK0 ; loop
|
|
RET ; ..and return with status in Carry
|
|
DRVOK1: SCF ; set return status (Carry set = Ok)
|
|
RET
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;-----
|
|
; test if current DU: is in Path
|
|
; in: HL= path address
|
|
; out: B= current drive, C= current user
|
|
; A= 0xFF, Z-Flag clear (NZ) if match or '$$'
|
|
; Z-Flag set if no match
|
|
DUPATH: LD BC,(USER) ; get default drive (B) and user number (C)
|
|
LD A,(HL) ; get byte of path entry
|
|
OR A ; test for end
|
|
RET Z ; ..if yes, then return with Z-Flag set
|
|
CP '$' ; test if current drive
|
|
JR Z,DUPAT0 ; ..if so, then jump
|
|
DEC A ; decrement drive number of path entry
|
|
LD B,A ; ..and save in B
|
|
DUPAT0: INC HL ; advance pointer
|
|
LD A,(HL) ; get user number
|
|
CP '$' ; test if current user
|
|
JR Z,DUPAT1 ; ..if so, then jump
|
|
LD C,A ; else, save in C
|
|
DUPAT1: INC HL ; advance pointer
|
|
OR 0FFH ; set exit code to 0xFF (match)
|
|
RET ; and return to caller
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 46 Return Free Space
|
|
; enter: E= Disk Number, exit: A= Error Code
|
|
; Space in DMA+0..DMA+3
|
|
;--------------------------------------------------
|
|
CMND46: CALL CMND14 ; select disk from E register
|
|
CALL SELSYB ; switch System bank
|
|
CALL CMD46B ; ..and continue in Banked code
|
|
JP DOSEXT1
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
;--------------------------------------------------
|
|
; Return Free Space
|
|
; (continue CMND46)
|
|
;--------------------------------------------------
|
|
CMD46B: LD HL,DFCALC
|
|
CALL CLRM4 ; clear 4 bytes
|
|
LD DE,(MAXLEN) ; get maximum blocks-1
|
|
INC DE ; +1
|
|
LD HL,(ALV) ; get ptr to allocation vector
|
|
|
|
; process one byte of ALV - outer loop
|
|
DFREE1: LD C,(HL) ; get bit pattern of allocation byte
|
|
PUSH HL ; save pointer
|
|
LD B,8 ; process 8 bits
|
|
DFREE2: RL C ; shift MSB to Carry
|
|
CCF ; invert
|
|
JR NC,DFREE4 ; ..if MSB was 1, then jump
|
|
|
|
; count zeros - inner loop
|
|
PUSH BC
|
|
LD B,4
|
|
LD HL,DFCALC
|
|
DFREE3: LD A,(HL)
|
|
ADC A,0 ; add Carry
|
|
LD (HL),A ; ..and save back
|
|
INC HL ; move ptr
|
|
DJNZ DFREE3 ; loop till done
|
|
|
|
POP BC
|
|
DFREE4: DEC DE ; decrease number of blocks
|
|
LD A,E ; test if zero
|
|
OR D
|
|
JR Z,DFREE5 ; ..if zero, exit inner loop
|
|
DJNZ DFREE2
|
|
POP HL ; restore ALV ptr
|
|
INC HL ; move to next byte
|
|
JR DFREE1 ; ..and loop (outer)
|
|
|
|
; allocation is measured in blocks, convert to kB
|
|
DFREE5: POP HL ; clear stack
|
|
LD A,(NBLOCK) ; get block shift factor
|
|
SUB 3 ; (BSH= 3 -> 1k, 4 -> 2k, ...8 -> 32k)
|
|
JR Z,DFREEX ; if BSH-3 = 0, then we're done
|
|
|
|
; multiply 32-bit (4 bytes)
|
|
DFREE6: LD HL,DFCALC ; set ptr to data storage
|
|
LD B,4 ; bytes to work
|
|
DFREE7: RL (HL) ; shift MSB into Carry
|
|
INC HL ; move ptr
|
|
DJNZ DFREE7 ; ..and loop
|
|
DEC A
|
|
JR NZ,DFREE6 ; if not zero, loop for next block size
|
|
; ..else, fall through and transfer data
|
|
|
|
|
|
; transfer to DMA using interbank move
|
|
; (destination could be in a disabled memory bank)
|
|
DFREEX: LD A,(TPABNK) ; get TPA bank #
|
|
LD B,A ; ..in B
|
|
LD A,(OSYSBK) ; get SYS bank # (Bios base +0x83 SYSBNK)
|
|
LD C,A ; ..in C
|
|
CALL XMOVE ; call Bios fn #29 XMOVE, set banks for MOVE
|
|
LD HL,DFCALC ; source address
|
|
LD DE,(DMA) ; destination address
|
|
LD BC,4 ; bytes to move
|
|
JP MOVE ; ..and do it (Bios fn #25)
|
|
|
|
|
|
|
|
;_______________________// Banked Data Segment //
|
|
COMMON /B2RAM/
|
|
|
|
; Disk free calculation data area (4 bytes)
|
|
DFCALC: DEFS 4
|
|
; DEFB 0,0,0,0
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;--------------------------------------------------
|
|
; fn # 152 Parse FileSpec
|
|
; enter: DE= FCB Address, exit: A= Number of '?'s in Fn.Ft
|
|
; String in DMA Buffer DE= Address of delimiter char
|
|
; FCB+15= 0 if Parse Ok, 0FFH if Error(s)
|
|
;--------------------------------------------------
|
|
CMD152: PUSH DE ; save ptr to FCB
|
|
XOR A
|
|
LD (DE),A ; clear FCB Drive field
|
|
INC DE ; move ptr fwd
|
|
LD A,' ' ; prepare to init FCB name and type fields
|
|
LD B,8+3
|
|
CM152A: LD (DE),A
|
|
INC DE
|
|
DJNZ CM152A
|
|
|
|
; destination FCB initialized
|
|
POP DE ; restore ptr to FCB
|
|
LD HL,(DMA) ; get DMA address
|
|
LD BC,(USER) ; get User number
|
|
LD A,C ; ..in A
|
|
LD (STATUS),A ; and save as status
|
|
CALL SCANF8 ; place first token (8-bytes) in name field
|
|
CP ':' ; is the delimiter a Colon ?
|
|
LD A,0 ; prepare status flag for ok DU/DIR
|
|
JP NZ,SCAN1 ; ..if only a file name, then jump
|
|
|
|
INC HL ; point to char after Colon
|
|
PUSH HL
|
|
PUSH DE
|
|
CALL CMND49 ; get address of Z3 Environment
|
|
; ##### CHECK: should jump further down ? (repeats CALL CMND49)
|
|
JR Z,DUSCAN ; ..if none found, jump
|
|
LD A,21 ; offset to Z3NDIR (addr Named Dir Buffer)
|
|
CALL GWHLA ; get vector at offset memory location
|
|
JR Z,DUSCAN ; ..if address not valid, jump to DU scan
|
|
INC DE
|
|
|
|
; scan Named Dir entries
|
|
DIRS1: CALL DUPATH ; test if current DU: in Path
|
|
JR Z,DUSCAN ; ..if not, then jump
|
|
PUSH BC ; save DU
|
|
PUSH HL ; save ptr to file name
|
|
PUSH DE ; save ptr to NDR entry
|
|
LD B,8 ; max 8 chars
|
|
DIRS2: LD A,(DE) ; get char
|
|
CP (HL) ; compare to NDR entry
|
|
JR NZ,DIRS3 ; ..if no match, exit loop
|
|
INC HL ; else, point to next char
|
|
INC DE
|
|
DJNZ DIRS2 ; loop
|
|
|
|
DIRS3: POP DE ; restore regs
|
|
POP HL
|
|
POP BC
|
|
JR Z,CHKVVC ; ..if ok, then jump
|
|
LD BC,16 ; 8 bytes name + 8 bytes password
|
|
ADD HL,BC ; advance ptr
|
|
JR DIRS1
|
|
|
|
; scan Drive/User
|
|
DUSCAN: CALL CMND49 ; get address of Z3 Environment
|
|
JR Z,DUS0 ; ..if none found, jump
|
|
|
|
LD A,46 ; offset to DU Flag
|
|
CALL CALDI0 ; move ptr fwd
|
|
LD A,(HL) ; get byte at memory location
|
|
OR A
|
|
JR Z,DUSERR ; ..if zero, then jump
|
|
; (1= ok to accept DU:, 0= not ok)
|
|
|
|
; no Z3 Envorionment, assume DU: form and scan/extract Drive/User
|
|
DUS0: LD L,C
|
|
EX DE,HL
|
|
CALL DELIM
|
|
EX DE,HL
|
|
CP ' '+1 ; legal char ?
|
|
JR C,DUSOK ; ..if only delimiter, jump w/ current DU
|
|
SUB 41H ; convert possible drive spec to number
|
|
JR C,DUS1 ; ..if less than 'A', must be digit
|
|
|
|
; set Drive number (0= A)
|
|
LD B,A ; save drive in B
|
|
INC DE ; point to next input char
|
|
LD A,(DE) ; get char
|
|
CP ' '+1 ; end of string ?
|
|
JR C,DUCHEK ; ..if so, jump to check limits
|
|
|
|
; set User number
|
|
DUS1: LD HL,2*256 ; get up to 2 digits
|
|
DUS1A: LD A,(DE)
|
|
CP ' '+1 ; is it a delimiter ?
|
|
JR C,DUS2 ; ..if so, jump to end
|
|
SUB '0' ; digit ?
|
|
JR C,DUSERR ; ..if not, jump
|
|
CP 10 ; number in range (0..9) ?
|
|
JR NC,DUSERR ; ..if not, jump
|
|
LD C,A ; save
|
|
LD A,L ; ..and multiply old digit
|
|
ADD A,A ; *2
|
|
ADD A,A ; *4
|
|
ADD A,L ; *5
|
|
ADD A,A ; *10
|
|
ADD A,C
|
|
LD L,A
|
|
INC DE ; advance to next byte
|
|
DEC H ; count down
|
|
JR NZ,DUS1A ; ..loop if more to go
|
|
LD A,(DE)
|
|
DUS2: CP ' ' ; is it the proper delimiter ?
|
|
JR NZ,DUSERR ; ..if not, jump error exit
|
|
LD C,L ; save good User in C
|
|
|
|
|
|
; BC now has parsed DU:, check legality
|
|
DUCHEK: LD HL,(USER) ; get currently logged DU
|
|
OR A
|
|
SBC HL,BC ; same as that parsed ?
|
|
JR Z,DUSOK ; ..if so, take good exit
|
|
|
|
CHKVVC: CALL SELSYB ; switch System bank
|
|
CALL DRVOK ; check if drive is valid
|
|
CALL SELTPB ; switch back to TPA bank
|
|
JR C,DUSOK
|
|
|
|
DUSERR: LD BC,(USER) ; get current DU
|
|
DEFB 0F6H ; fall through with OR A,0AFH
|
|
|
|
DUSOK: XOR A ; set return status OK
|
|
POP DE
|
|
POP HL
|
|
PUSH AF
|
|
LD A,B ; get drive #
|
|
INC A ; change to A= 1 base
|
|
LD (DE),A ; store in FCB+0
|
|
LD A,C ; get User #
|
|
LD (STATUS),A ; store in mem
|
|
CALL SCANF8 ; scan token (8-byte)
|
|
POP AF ; restore A cleared
|
|
; ..and fall through
|
|
|
|
|
|
; skip to file type field
|
|
; in: HL= ptr to next char
|
|
; DE= ptr to DN field of FCB
|
|
SCAN1: PUSH AF ; save error flag for exit
|
|
EX DE,HL
|
|
LD BC,8 ; point to before file type field of FCB
|
|
ADD HL,BC
|
|
EX DE,HL ; Extract file type field
|
|
LD B,3
|
|
LD A,(HL) ; get the delimiter char
|
|
CP '.' ; is it '.' ?
|
|
JR NZ,SCAN2 ; ..if no type, then jump
|
|
INC HL ; else, point to char after '.'
|
|
CALL SCANF ; ..and get file type
|
|
SCAN2: LD (DEVAL),HL
|
|
INC DE ; move ptr from last char of name to T1
|
|
INC DE ; ..to T2
|
|
INC DE ; ..to T3
|
|
INC DE ; ..to EXM
|
|
XOR A ; zero
|
|
LD (DE),A ; store here at FCB+12 (EXM)
|
|
INC DE ; move to S1
|
|
LD A,(STATUS) ; get User number
|
|
LD (DE),A ; ..and store it here at FCB+13 (S1)
|
|
INC DE ; move ptr to S2
|
|
XOR A ; zero
|
|
LD (DE),A ; store here at FCB+14 (S2)
|
|
INC DE ; move ptr to FCB+15
|
|
POP AF ; restore error flag
|
|
LD (DE),A ; ..and save it in FCB+15
|
|
RET
|
|
|
|
|
|
;-----
|
|
; scan up to 8 chars
|
|
SCANF8: XOR A ; get a 0
|
|
LD (PEXIT),A ; ..and clear question mark counter
|
|
LD B,8 ; scan for up to 8 chars
|
|
; ..fall through to main scan routine
|
|
|
|
; scan token with interpreting/expanding '*' and '?' wild cards
|
|
; in: B= max number of bytes
|
|
; HL= ptr to token
|
|
; DE= destination addr (FCB file name field)
|
|
; out: HL= ptr to terminating delimiter
|
|
SCANF: PUSH DE ; preserve FCB address
|
|
SCANF0: INC DE ; ptr to next byte in FCB
|
|
CALL DELIM ; is it a delimiter ?
|
|
JR Z,SCANF2 ; ..if so, jump
|
|
INC HL ; else, point to next char
|
|
CP '*' ; is (DE) a wild card ?
|
|
JR NZ,SCANF1 ; ..if not, continue
|
|
DEC HL ; else, back up to same char
|
|
LD A,'?' ; ..and expand with '?'
|
|
|
|
SCANF1: LD (DE),A
|
|
CP '?' ; is it wild ?
|
|
JR NZ,SCNOQ ; ..if not, jump
|
|
PUSH HL ; else, save HL
|
|
LD HL,PEXIT ; point to count storage
|
|
INC (HL) ; ..and increment
|
|
POP HL ; restore HL
|
|
SCNOQ: DJNZ SCANF0 ; ..loop till done
|
|
INC DE ; then advance to next FCB char
|
|
|
|
; flush to next delimiter
|
|
SCANF3: CALL DELIM
|
|
JR Z,SCANFX ; ..if delimiter found, then jump
|
|
INC HL ; point to next char
|
|
JR SCANF3 ; and loop
|
|
|
|
; preserve present char, and fill remaining chars in field w/ spaces
|
|
SCANF2: PUSH AF ; save char
|
|
SCANFA: LD A,' ' ; store this char
|
|
LD (DE),A ; ..in FCB field
|
|
INC DE ; point to next
|
|
DJNZ SCANFA ; loop till done
|
|
POP AF ; restore char
|
|
SCANFX: POP DE ; restore FCB address
|
|
RET ; ..and exit
|
|
|
|
|
|
; capitalize char, then fall through to check for delimiter
|
|
DELIM: LD A,(HL) ; get char
|
|
CP 'a' ; less than small letter A ?
|
|
JR C,SDELM
|
|
CP 'z'+1 ; between small A and small Z ?
|
|
JR NC,SDELM
|
|
AND 5FH ; remove bit 5 to capitalize
|
|
|
|
; check for delimiter
|
|
; in: HL= ptr to char
|
|
; out: A= addressed char
|
|
; Zero Flag set (Z) if delimiter, end-of-line or cmd separator
|
|
; Zero Flag cleard (NZ) if char is not a delimiter
|
|
SDELM: CP '_' ; is it an Underscore ?
|
|
RET Z
|
|
CP '.' ; a Period ?
|
|
RET Z
|
|
CP ',' ; a Comma ?
|
|
RET Z
|
|
CP '>' ; greater than a Greater Sign ?
|
|
RET NC ; ..if so, return
|
|
CP ':' ; is it any of : ; < = > ?
|
|
JR NC,SDEL0 ; ..if so, jump to set status
|
|
CP ' ' ; is it less than a Space ?
|
|
RET NC ; ..if so, return (Space has zero set)
|
|
XOR A ; else, must be less than Space,
|
|
RET ; so return with zero in A
|
|
SDEL0: CP A ; set Z-Flag retaining char
|
|
RET
|
|
|
|
|
|
;::::::::::::::::::::::::::::::::::::::::
|
|
;::::: Time and Date Routines
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 98 Get time
|
|
; enter: DE= Address to Put Time, exit: A= Time/Date Code
|
|
;--------------------------------------------------
|
|
CMD98A: EX DE,HL ; entry point for cmnd12
|
|
|
|
CMD98: LD C,0 ; set parameter to get time/date
|
|
DEFB 21H ; ..and fall through to GSTD (trashing HL)
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 99 Set time
|
|
; enter: DE= Address of Time, exit: A= Time/Date Code
|
|
;--------------------------------------------------
|
|
CMD99: LD C,1 ; set parameter to set 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
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 103 Set file stamp
|
|
; enter: DE= Address of FCB, exit: A= Time/Date Code
|
|
; Stamp in DMA Buffer
|
|
;--------------------------------------------------
|
|
; wildcards allowed in FCB
|
|
; ##### ZSDOS-GP.Z80 (v1) refers to "10 byte stamp from DMA" ??
|
|
CMD103: LD HL,(DMA) ; address DMA buffer
|
|
LD DE,ZSSTMP ; time stamp buffer
|
|
CALL STPCP0 ; ..and copy
|
|
|
|
|
|
;--------------------------------------------------
|
|
; fn # 102 Get file stamp
|
|
; enter: DE= Address of FCB, exit: A= Time/Date Code
|
|
; Stamp in DMA Buffer
|
|
;--------------------------------------------------
|
|
; wildcards allowed in FCB
|
|
CMD102: CALL SELDRV ; select Drive in FCB
|
|
; (also switches System bank)
|
|
JP CMD103B ; copy stamp (..in Banked code)
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
; ZSDOS2 supports DateStamper and P2DOS (CP/M+) file stamping.
|
|
; Internally, the Universal T&D format is used, and converted
|
|
; as needed when reading from / writing to disk. Similarly,
|
|
; all stamps are converted to/from the Universal stamp format
|
|
; (15 bytes packed BCD), and stuffed into ZSSTMP buffer.
|
|
|
|
;-----
|
|
; Universal Stamp format
|
|
; Bytes 0..4 Create 5..9 Access 10..14 Modify
|
|
; | YY MM DD HH MM | YY MM DD HH MM | YY MM DD HH MM |
|
|
;
|
|
; YY MM DD HH MM SS - Universal T&D format
|
|
; (all BCD, prefix 19 assumed for years 79-99, else 20)
|
|
|
|
;-----
|
|
; P2DOS (CP/M+) Stamp format
|
|
; Bytes 0..3 Create 4..7 Modify
|
|
; | nn nn HH MM | nn nn HH MM |
|
|
;
|
|
; nnnn HH MM SS - P2DOS (CP/M+) format
|
|
; (nnnn = binary number of das since 1 Jan 1978)
|
|
; (HH MM SS = time in BCD)
|
|
|
|
|
|
;--------------------------------------------------
|
|
; Get/Set file stamp
|
|
; (continue from CMD102/CMD103)
|
|
;--------------------------------------------------
|
|
CMD102B:
|
|
CMD103B: CALL SRCT15 ; find the FCB
|
|
JP Z,DOTDER ; ..if not found, exit w/ error
|
|
LD HL,GETSTR ; address of get time stamp routine
|
|
LD A,(FUNCT) ; check function
|
|
CP 102 ; get stamp ?
|
|
JR Z,DOTDR3 ; ..if yes, jump
|
|
LD HL,PUTSTR ; address of put (set) time stamp routine
|
|
JR STAMPT ; mask for enabled stamp type(s) in B
|
|
STPMSK: LD A,(STFLAG) ; get time stamp flags
|
|
AND B ; mask
|
|
RET Z ; ..if not selected, then return
|
|
|
|
; enter here for stamp Create/Access/Modify
|
|
STAMPT: PUSH HL
|
|
CALL CHKRO1 ; test for disk W/P but avoid error trap
|
|
POP HL
|
|
JP Z,DOTDER ; no stamp if disk is W/P
|
|
DOTDR3: CALL GETDME ; get data module and extent number
|
|
JP NZ,DOTDER ; ..if not extent 0 of module 0, then quit
|
|
JP (HL) ; else, continue in GETSTR/PUTSTR routine
|
|
|
|
|
|
;-----
|
|
; Stamp Create
|
|
STCR: CALL P2CR ; call P2D Create routine
|
|
LD B,0 ; offset into stamp in B
|
|
JR STT ; Stamp Update
|
|
|
|
;-----
|
|
; Stamp Modify
|
|
STUP: CALL P2UP ; call P2D Update routine
|
|
LD B,10 ; offset into stamp in B
|
|
JR STT ; Stamp Access
|
|
|
|
;-----
|
|
; Stamp Access
|
|
STLA: LD B,5 ; offset into stamp in B
|
|
LD A,0FFH ; set correct value to validate status
|
|
|
|
; update stamp
|
|
STT: LD (STATUS),A ; save first routine status
|
|
LD C,2 ; show as stamp
|
|
LD A,(SECPNT) ; get sector pointer
|
|
ADD A,3 ; point to no date attribute
|
|
LD L,A
|
|
LD H,0
|
|
LD DE,(DIRBUF) ; get Directory buffer pointer
|
|
ADD HL,DE ; point to Dir entry
|
|
BIT 7,(HL)
|
|
LD A,(STATUS) ; get prev routine status for possible ret
|
|
RET NZ ; ..if no date attribute, return (don't update)
|
|
JR STPSV0 ; else, jump to service routine
|
|
|
|
|
|
;-----
|
|
; get/put Stamp routines
|
|
GETSTR: CALL GSTAMP ; get file stamp in Universal format
|
|
CP 1 ; test if OK
|
|
JR Z,GETST0 ; ##### not needed if following code is removed
|
|
|
|
; ##### CHECK unreferenced code
|
|
LD BC,0 ; flag as read
|
|
CALL STPSVC
|
|
; #####
|
|
|
|
GETST0: JP STPCPY
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
STPCPY: CALL SELTPB ; select TPA bank
|
|
LD HL,ZSSTMP ; time stamp buffer
|
|
LD DE,(DMA) ; address DMA buffer
|
|
STPCP0: LD BC,15 ; copy 15 bytes
|
|
LDIR
|
|
RET
|
|
|
|
|
|
|
|
;_______________________// Banked //
|
|
COMMON /BANK2/
|
|
|
|
RET ; ##### unreferenced, will not be executed
|
|
|
|
; get/put Stamp routines contd.
|
|
PUTSTR: CALL PSTAMP ; put file stamp in Universal format
|
|
LD BC,1 ; flag as write
|
|
; ..and fall through
|
|
|
|
|
|
;-----
|
|
; Stamp service routine, combined get/put
|
|
; in: A= index to Dir entry (00H, 20H, 40H, 60H)
|
|
; B= offset in stamp (0, 5, 10)
|
|
; C= function (0= read, 1= write, 2= update)
|
|
STPSVC: LD (STATUS),A ; save first routine status
|
|
STPSV0: LD (RWCODE),BC ; save function code and sector offset
|
|
LD BC,(TDFVCT) ; get T/D vector
|
|
CALL GETCDM ; get mask for current Drive in HL
|
|
LD A,H
|
|
AND B
|
|
LD H,A
|
|
LD A,L
|
|
AND C
|
|
OR H ; test if Drive has T/D vector
|
|
JR Z,STPSVE ; ..if it doesn't, jump error
|
|
|
|
CALL DMADIR ; set DMA address to Directory buffer
|
|
LD BC,(RECDIR) ; get current Directory record
|
|
XOR A
|
|
SRL B ; calculate buffer offset
|
|
RR C ; divide record by two, lsb to carry
|
|
RRA ; place in msb of A
|
|
LD HL,SECPNT ; get sector pointer
|
|
ADD A,(HL) ; get offset to stamp in buffer
|
|
RRA ; divide by 2 (8 stamps/rec)
|
|
PUSH AF ; save index for later
|
|
PUSH BC ; ..and save relative record number
|
|
|
|
; now calculate record number needed
|
|
LD HL,(NDIR0) ; first entry ALV buffer
|
|
LD DE,(NMASK) ; mask number of blocks
|
|
INC E ; now is number of records per block
|
|
XOR A ; clear A (cheap zero)
|
|
LD D,A ; ..to D for 16-bit math
|
|
LD B,16 ; check all bits
|
|
STPSV1: ADD HL,HL ; shift MSB into Carry
|
|
JR NC,STPSV2 ; ..if bit was zero, then jump
|
|
EX (SP),HL ; else, get relative record
|
|
ADD HL,DE ; add records for this directory block
|
|
EX (SP),HL ; back to top-of-stack
|
|
STPSV2: DJNZ STPSV1 ; loop until done
|
|
POP DE ; has actual record number now
|
|
|
|
CALL STDIR1 ; set track/sector and read
|
|
CALL READ
|
|
POP DE ; get index (was in A before)
|
|
AND A ; test if read ok
|
|
JR NZ,STPSVE ; ..if error, jump
|
|
CALL CKS127 ; checksum first 127 bytes
|
|
CP (HL) ; test against DS's checksum
|
|
JR NZ,STPSVE ; ..if error, jump exit
|
|
LD BC,15 ; size of Universal Stamp
|
|
LD E,D ; index in E
|
|
LD D,B ; D= 0
|
|
LD HL,(DIRBUF)
|
|
ADD HL,DE ; now pointing to correct stamp
|
|
LD DE,ZSSTMP ; time stamp buffer
|
|
LD A,(RWCODE) ; get Read/Write code
|
|
AND A ; is it Get stamp ?
|
|
JR NZ,PUTSTP ; ..if not, we're supposed to write
|
|
LDIR ; copy bytes
|
|
JR STPSVX ; ..and exit
|
|
|
|
|
|
;-----
|
|
; copy Stamp from User DMA to local buffer
|
|
PUTSTP: SUB 2 ; is this a simple put
|
|
JR Z,UPSTMP ; ..if not, read the clock
|
|
EX DE,HL ; swap pointers
|
|
PUTS1: LDIR ; move new stamp into place
|
|
CALL CKS127 ; checksum first 127 bytes of local buffer
|
|
LD (HL),A ; ..and save checksum in last byte of record
|
|
INC C ; flag as non-deferred (0 from ldir -> 01)
|
|
CALL WRITE ; write sector to disk
|
|
AND A ; test if error
|
|
JR NZ,STPSVE
|
|
STPSVX: LD A,1 ; indicate all is well (OL status)
|
|
DEFB 01H ; ..fall through the LD A,FF with LD BC,FF3E
|
|
|
|
; return error status
|
|
STPSVE: LD A,0FFH ; set status
|
|
PUSH AF ; save return code
|
|
CALL STDMA ; set DMA
|
|
POP AF ; restore return code
|
|
CP 1 ; is it OK ?
|
|
RET Z ; ..if so, return
|
|
LD A,(STATUS) ; else, get first routine status
|
|
RET ; ..and return
|
|
|
|
|
|
;-----
|
|
; read Clock, place in proper Stamp field
|
|
UPSTMP: LD D,A ; reg A= 0
|
|
LD A,(STOFF) ; get offset in stamp
|
|
LD E,A ; make word length in DE
|
|
ADD HL,DE ; pointer to target address
|
|
PUSH HL ; save it
|
|
OR A ; is it Create time ?
|
|
LD B,15 ; prepare to clear 15 bytes
|
|
CALL Z,CLRMEM ; ..if Create, then zero entire stamp entry
|
|
LD DE,ZSDOSS ; point to Time and Date buffer
|
|
PUSH DE
|
|
CALL CMD98 ; call fn 98 to read clock
|
|
POP HL ; get buffer start
|
|
POP DE ; get stamp target address
|
|
DEC A ; was the clock read OK ?
|
|
JR NZ,STPSVE ; ..if not, jump error exit
|
|
LD BC,5 ; else, move 5 bytes
|
|
JR PUTS1 ; ..and finish up
|
|
|
|
|
|
;-----
|
|
; update Create/Modify field in T&D buffer
|
|
P2CR: LD E,0 ; set to Create field in stamp
|
|
DEFB 21H ; trash HL and fall through
|
|
|
|
P2UP: LD E,4 ; set to Modify field in stamp
|
|
CALL SETREC ; calculate offset in stamp
|
|
LD C,E ; move offset to C (B= 0)
|
|
ADD HL,BC ; destination now in HL
|
|
LD DE,ZSDOSS ; set address to read time
|
|
PUSH HL ; save destination address
|
|
PUSH DE ; ..and source address
|
|
CALL CMD98 ; call fn 98 to get time
|
|
POP DE ; restore addresses
|
|
POP HL
|
|
DEC A ; was the clock read OK ?
|
|
JR NZ,NOTIM0 ; ..if not, jump error exit
|
|
|
|
; ##### move label/target PSTMP1 up by one instruction
|
|
; this CALL U2PTIM could be saved (3 bytes)
|
|
CALL U2PTIM ; else, convert DE= U-time -> HL= P-time
|
|
JR PSTMP1 ; jump to write FCB
|
|
|
|
|
|
;-----
|
|
; Put File Stamp in Universal format
|
|
; convert Create and Modify time fields from Universal T&D format
|
|
; to P2DOS (CP/M+) form and insert in Directory buffer, call WRFCB
|
|
; routine to write on exit
|
|
; in: A= index to Dir entry (00H, 20H, 40H)
|
|
; out: A= time/date code
|
|
PSTAMP: CALL SETREC ; calculate the Stamp area address for file
|
|
LD DE,ZSSTMP ; time stamp buffer
|
|
CALL U2PTIM ; convert Create (DE= U-time -> HL= P-time)
|
|
JR NZ,NOTIM0 ; ..if invalid date, jump error exit
|
|
INC DE ; bypass Last Access field of input
|
|
INC DE
|
|
INC DE
|
|
INC DE
|
|
INC DE
|
|
CALL U2PTIM ; convert Modify field
|
|
PSTMP1: JR NZ,NOTIM0 ; ..if invalid date, jump error exit
|
|
CALL WRFCB ; write stamp
|
|
OKRET: LD A,1 ; set status ok and return
|
|
RET
|
|
|
|
NOSTD: POP AF ; remove return address from Stack
|
|
NOTIM0: OR 0FFH ; set error flags
|
|
RET ; back to caller
|
|
|
|
|
|
;-----
|
|
; Get File Stamp in Universal format
|
|
; read Create and Modify stamps and convert to Universal T&D format,
|
|
; null the Access time field
|
|
; in: A= index to Dir entry (00H, 20H, 40H)
|
|
; out: A= time/date code
|
|
GSTAMP: CALL SETREC ; calculate source Stamp address in HL
|
|
LD DE,ZSSTMP ; time stamp buffer
|
|
CALL P2UTIM ; convert Create (HL= P-time -> DE= U-time)
|
|
LD B,5 ; zero Last Access field for this type
|
|
XOR A ; clear A
|
|
GSLOOP: LD (DE),A ; ..and poke a zero
|
|
INC DE
|
|
DJNZ GSLOOP ; ..in each location
|
|
CALL P2UTIM ; convert Modify field
|
|
JR OKRET ; ..jump to return OK status
|
|
|
|
|
|
;-----
|
|
; convert Universal T&D to P2DOS (CP/M+) form
|
|
; in: DE= ptr to Universal T&D string
|
|
; HL= ptr to destination buffer for P2DOS (CP/M+) T&D
|
|
; out: A= 0, Zero Flag set (Z), if conversion OK
|
|
; A= 0FFH, Zero Flag clear (NZ), error (dest nulled)
|
|
; DE= ptr to Seconds byte in Universal T&D (not moved)
|
|
; HL= ptr to Seconds byte in P2DOS (CP/M+) T&D (not filled)
|
|
U2PTIM: PUSH HL ; save destination address
|
|
LD A,(DE) ; get BCD year
|
|
LD B,A ; ..to B
|
|
INC DE ; advance to month
|
|
LD A,(DE) ; get BCD month
|
|
OR B ; is it invalid (YY = MM = 0) ?
|
|
JR Z,NODATE ; ..if invalid stamp, jump to error exit
|
|
LD A,B ; get BCD year again from B
|
|
CALL BCDBIN ; convert year to binary
|
|
CP 78 ; is it 20th century ?
|
|
JR NC,YR19 ; ..if so, jump
|
|
ADD A,100 ; else, make it 21st century
|
|
YR19: LD BC,1900 ; set base century
|
|
ADD A,C ; add current year to base
|
|
LD C,A
|
|
LD A,0
|
|
ADC A,B
|
|
LD B,A
|
|
LD A,(DE) ; get BCD month
|
|
INC DE
|
|
CALL BCDBIN ; ..convert to binary
|
|
LD H,A
|
|
LD A,(DE) ; get day
|
|
INC DE ; point to U-hours
|
|
PUSH DE ; ..and save address on stack
|
|
CALL BCDBIN ; convert day to Binary
|
|
LD L,A ; day to L (Binary)
|
|
|
|
; check validity of day, month year (CHKDAT from DATE.ASM)
|
|
; L= binary day, H= binary month, BC= binary year (1978..2077)
|
|
LD A,H ; month
|
|
DEC A ; convert to 0-11 range
|
|
CP 12 ; is it a valid month ?
|
|
JR NC,BADDAT ; ..if invalid, then jump error
|
|
PUSH HL ; save day/month
|
|
LD E,A ; month-1 in E
|
|
LD D,0
|
|
LD HL,DMTABLE ; set lookup table for months
|
|
ADD HL,DE
|
|
LD D,(HL) ; get days in this month
|
|
POP HL ; restore day/month
|
|
CP 2-1 ; is this February ?
|
|
CALL Z,LEAPYR ; ..if so, check for leap year
|
|
JR NZ,CHKDT0 ; else, jump
|
|
INC D ; make 29 days
|
|
CHKDT0: LD A,L ; check for day within range
|
|
DEC A ; have day > 0, check for <= max. days
|
|
CP D
|
|
JR NC,BADDAT ; ..anything else is error
|
|
|
|
; calculate 16-bit binary date since 1978 in days, works til 2157
|
|
PUSH HL ; save month (H) and day (L)
|
|
LD H,0 ; null out month, leaving just days
|
|
EX DE,HL ; ..move to DE
|
|
LD L,C ; year in HL
|
|
LD H,B
|
|
LD BC,1978 ; start with base year in BC
|
|
DAYS0: OR A
|
|
SBC HL,BC ; is this the starting year ?
|
|
ADD HL,BC
|
|
JR Z,DAYS1 ; ..if so, jump
|
|
PUSH HL
|
|
LD HL,365 ; add days in non-leap year
|
|
ADD HL,DE ; ..to total days count in DE
|
|
EX DE,HL ; ..and put new days total in DE
|
|
POP HL
|
|
CALL LEAPYR ; is this a leap year ?
|
|
INC BC ; advance to next year
|
|
JR NZ,DAYS0 ; ..if not leap year, then loop
|
|
INC DE ; else, add a day
|
|
JR DAYS0 ; ..then loop
|
|
|
|
|
|
; error routines, destination P2DOS field nulled
|
|
NODATE: INC DE ; advance source ptr for same routine
|
|
INC DE
|
|
DEFB 3EH ; ..fall through to 2nd POP with LD A,0D1H
|
|
BADDAT: POP DE ; restore Universal T&D string (-> hours)
|
|
POP HL ; restore destination addr for P2DOS date
|
|
BADDA1: CALL CLRM4 ; fill with 4 nulls
|
|
INC DE ; ..advance ptr to exit position
|
|
INC DE
|
|
DEC A ; set error flags (A= 0FFH, Zero clear (NZ))
|
|
RET
|
|
|
|
|
|
; DE= total days (year + day), BC= binary year, (Stack)= month and day
|
|
; first day 0001H : Su 01 Jan 1978
|
|
; last day 8EADH : Fr 31 Dec 2077
|
|
; real last day FFFFH : Su 05 Jun 2157
|
|
DAYS1: POP HL ; restore month and day
|
|
EX DE,HL ; date to HL, month/day to DE
|
|
PUSH HL ; ..and save binary date
|
|
LD HL,DMTABLE ; address days-of-month table
|
|
LD E,1
|
|
DAYS2: LD A,D ; check for matching month
|
|
CP E
|
|
JR Z,DAYS4 ; ..exit when match
|
|
LD A,(HL) ; get days in this month
|
|
EX (SP),HL ; put table on stack, binary date to HL
|
|
ADD A,L ; add month's days to cum binary date
|
|
LD L,A
|
|
LD A,0
|
|
ADC A,H
|
|
LD H,A
|
|
LD A,E ; check this month
|
|
CP 2 ; is it February ?
|
|
CALL Z,LEAPYR ; ..if so, check if leap year
|
|
JR NZ,DAYS3A ; jump if not Feb and/or leap year
|
|
INC HL ; else, bump cum binary date by 29 Feb
|
|
DAYS3A: EX (SP),HL ; put cum binary date to stack
|
|
; ..and days-of-month table to HL
|
|
INC HL ; point to next month
|
|
INC E ; bump index counter
|
|
JR DAYS2 ; ..and loop
|
|
|
|
DAYS4: POP BC ; exit here, put cum binary date to BC
|
|
POP DE ; restore Universal T&D string (-> Hrs)
|
|
POP HL ; ..and destination address
|
|
LD (HL),C ; put binary date in string
|
|
INC HL
|
|
LD (HL),B
|
|
MOVHM: INC HL
|
|
EX DE,HL ; swap src/dest pointers
|
|
LDI ; move BCD hours
|
|
LDI ; ..and BCD minutes
|
|
EX DE,HL ; restore pointers into correct regs for exit
|
|
XOR A ; set OK flags
|
|
RET ; and return
|
|
|
|
|
|
;-----
|
|
; calculate leap year correction
|
|
; in: BC= year (binary, leap years = xxxxxx00B)
|
|
; out: Zero Flag set (Z) = correction necessary, reset (NZ) = no corr
|
|
LEAPYR: BIT 0,C ; get lower part of date
|
|
RET NZ ; ..if not leap year, return
|
|
BIT 1,C ; test other bit
|
|
RET ; ..and return
|
|
|
|
|
|
;-----
|
|
; convert BCD to Binary
|
|
; in: A= BCD digit
|
|
; out: A= binary number
|
|
BCDBIN: OR A ; test if zero
|
|
RET Z ; ..if so, return (it's the same)
|
|
PUSH BC ; save register
|
|
LD B,0 ; set counter
|
|
BCDBI0: INC B ; bump counter
|
|
SUB 1 ; count down BCD
|
|
DAA
|
|
JR NZ,BCDBI0 ; ..till all gone
|
|
LD A,B
|
|
POP BC
|
|
RET
|
|
|
|
|
|
;----
|
|
; convert Binary to BCD
|
|
; in: A= byte to be converted
|
|
; out: A= two packed BCD digits
|
|
; Convert byte in A register to two packed BCD digits
|
|
BINBCD: PUSH BC ; save register
|
|
LD B,0FFH ; preset counter
|
|
BINBCL: INC B ; bump counter output
|
|
SUB 10
|
|
JR NC,BINBCL ; loop bumping counter till no more 10's
|
|
ADD A,10 ; ..correct for underflow
|
|
LD C,A ; save low nybble here for a while
|
|
LD A,B ; ..and bring high nybble in A
|
|
ADD A,A ; move it into position
|
|
ADD A,A
|
|
ADD A,A
|
|
ADD A,A
|
|
ADD A,C ; add in low nybble
|
|
POP BC ; restore regs
|
|
RET
|
|
|
|
|
|
;-----
|
|
; convert P2DOS (CP/M+) form to Universal T&D string
|
|
; in: HL= ptr to P2DOS (CP/M+) T&D
|
|
; DE= ptr to destination buffer for Universal T&D
|
|
; out: A= 0, Zero Flag set (Z), if conversion OK
|
|
; A= 0FFH, Zero Flag clear (NZ), error (dest nulled)
|
|
; HL= ptr to Seconds byte in P2DOS (CP/M+) T&D (not moved)
|
|
; DE= ptr to Seconds byte in Universal T&D (not filled)
|
|
P2UTIM: PUSH DE ; save destination addr
|
|
LD E,(HL) ; get binary date to DE
|
|
INC HL
|
|
LD D,(HL)
|
|
INC HL
|
|
EX DE,HL ; put binary day/date in HL, P2D ptr in DE
|
|
LD A,H ; check for valid entry
|
|
OR L ; is date present ?
|
|
JR NZ,P2UTI0 ; ..if not null, then jump
|
|
POP HL ; get destination addr back
|
|
LD B,5
|
|
CALL BADDA1 ; ..and null the U-time field
|
|
EX DE,HL ; put pointers in correct regs
|
|
RET ; ..and return to caller
|
|
|
|
P2UTI0: PUSH DE ; save P2D pointer (-> Min)
|
|
LD BC,1978 ; beginning year
|
|
DMJ0: LD DE,365 ; set days in normal year
|
|
CALL LEAPYR ; check for leap year
|
|
JR NZ,DMJ1 ; ..if not leap year, then jump
|
|
INC DE ; else, add one day
|
|
DMJ1: OR A ; when # of days left..
|
|
SBC HL,DE ; ..is less than days in year..
|
|
JR C,DMJ2 ; ..year is in HL, so exit
|
|
JR Z,DMJ2 ; ..or if last day of year
|
|
INC BC ; bump starting year
|
|
JR DMJ0 ; ..and back for another try
|
|
|
|
; BC= binary year, HL= remaining days
|
|
DMJ2: ADD HL,DE ; compensate for underflow
|
|
LD A,1 ; start with month 1 (Jan)
|
|
LD D,0 ; prepare for 16-bit math
|
|
PUSH HL ; save days remaining
|
|
LD HL,DMTABLE ; address days-of-month table
|
|
DMJ3: LD E,(HL) ; get days in current month to E
|
|
CP 2 ; is it February ?
|
|
CALL Z,LEAPYR ; ..if so, check for leap year
|
|
JR NZ,DMJ4 ; ..if not leap year, then jump
|
|
INC E ; else, make 29 days
|
|
DMJ4: EX (SP),HL ; swap ptr (HL) with days remaining (Stack)
|
|
OR A
|
|
SBC HL,DE ; subtract days in month from remaining days
|
|
JR C,DMJ5 ; ..if we've gone too far, then exit
|
|
JR Z,DMJ5 ; ..or just far enough (last day of month)
|
|
EX (SP),HL ; swap back
|
|
INC HL ; point to next month in table
|
|
INC A ; bump month counter
|
|
JR DMJ3 ; ..and try again
|
|
|
|
; arrive here with binary year on Stack, relative month in A (Jan = 1),
|
|
; days in that month in E, and binary year in BC
|
|
DMJ5: ADD HL,DE ; compensate for underflow
|
|
EX (SP),HL ; ..and put back on Stack
|
|
POP HL ; restore day in L
|
|
CALL BINBCD ; convert month (in A) to BCD
|
|
LD H,B ; ..moving year to HL
|
|
LD B,A
|
|
LD A,L ; convert day
|
|
LD L,C
|
|
CALL BINBCD ; ..to BCD
|
|
LD C,A
|
|
LD DE,100 ; subtract centuries, one by one..
|
|
DMJ7A: OR A
|
|
SBC HL,DE
|
|
JR NC,DMJ7A ; ..until we go too far
|
|
ADD HL,DE ; then correct for underflow
|
|
LD A,L ; get year (tens and ones)
|
|
CALL BINBCD ; ..to BCD
|
|
POP DE ; restore P2D pointer (-> Min)
|
|
POP HL ; get Universal T&D string address
|
|
LD (HL),A ; store years
|
|
INC HL
|
|
LD (HL),B ; ..months
|
|
INC HL
|
|
LD (HL),C ; ..days
|
|
CALL MOVHM ; store hours and minutes, and set flags
|
|
EX DE,HL ; put U-time exit address in DE
|
|
RET ; ..and finish up elsewhere
|
|
|
|
|
|
;------
|
|
; calculate offset within T&D entry, if one exists
|
|
; out: HL= ptr to first byte of Create field
|
|
SETREC: LD HL,(DIRBUF)
|
|
LD BC,60H ; offset to Time and Date fields
|
|
ADD HL,BC
|
|
LD A,(HL) ; get byte
|
|
SUB 21H ; is TimeStamping present
|
|
JP NZ,NOSTD ; ..if not, quit here
|
|
|
|
LD A,(SECPNT) ; get sector pointer
|
|
RRCA ; shift 2 times
|
|
RRCA
|
|
LD C,A ; save temporarily
|
|
RRCA ; shift 2 more times
|
|
RRCA
|
|
ADD A,C ; ..and add in again
|
|
LD C,A ; set for offset (C= 0, 10, 20)
|
|
ADD HL,BC ; add offset
|
|
INC HL ; ..and bump to Create Time start
|
|
XOR A ; set return status OK
|
|
RET
|
|
|
|
; day-of-month table
|
|
DMTABLE: DEFB 31,28,31,30,31,30,31,31,30,31,30,31
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG
|
|
|
|
;-----
|
|
; JP (HL) with bank context switching
|
|
JUMPHL: CALL RETMEM ; get current mem bank (BIOS fn #39 RETMEM)
|
|
LD (CURBNK),A ; save it
|
|
CALL SELTPB ; select TPA bank
|
|
CALL JPHL ; do the actual JP (HL)
|
|
LD A,(CURBNK) ; get bank # back
|
|
JP SELMEM ; select mem bank again (BIOS fn #27 SELMEM)
|
|
; ..and let return from there
|
|
JPHL: JP (HL)
|
|
|
|
CURBNK: DEFS 1
|
|
|
|
|
|
|
|
;_______________________// Non-Banked //
|
|
CSEG ; technically not needed
|
|
; but necessary to assemble identical REL file
|
|
|
|
;::::::::::::::::::::::::::::::::::::::::
|
|
;::::: Memory Bank Switching Routines
|
|
|
|
; bring SYSBNK into context (Banked mode)
|
|
SELSYB: PUSH AF
|
|
LD A,(OSYSBK) ; B/P Bios base +0x83 (offset SYSBNK)
|
|
JR SELBNK ; switch bank
|
|
|
|
; bring TPABNK into context (Non-banked mode)
|
|
SELTPB: PUSH AF
|
|
LD A,(TPABNK) ; get TPA bank number
|
|
; ..and fall through
|
|
|
|
; select memory bank
|
|
SELBNK: CALL SELMEM ; BIOS fn #27 SELMEM (A= bank #)
|
|
POP AF
|
|
RET
|
|
|
|
|
|
;::::: DATA AREA (in CSEG)
|
|
|
|
PATHAD: DEFB 1,0 ; Internal path - Drive A, User 0
|
|
DEFB 0,0 ; blank entry
|
|
DEFB 0,0 ; blank entry
|
|
DEFB 0 ; ..and ending null
|
|
|
|
;-----
|
|
|
|
TDFVCT: DEFW 0 ; time and date vector
|
|
|
|
;-----
|
|
|
|
LOGIN: DEFW 0 ; Login vector
|
|
DSKWP: DEFW 0 ; Disk write protect vector
|
|
HDLOG: DEFW 0 ; Fixed disk login vector
|
|
|
|
|
|
END
|
|
|
|
|
|
;************************************************************************
|
|
; Remarks jxl:
|
|
; When assembled as M-REL, this source file produces an _exact_ copy
|
|
; of ZS227G.ZRL included in available B/P Bios package(s).
|
|
; Compared to v1 (ZSDOS-GP.Z80) many code portions are unaltered, or
|
|
; show only slight modifications. Major changes result from banking and
|
|
; addition of universal T&D stamp routines, supporting DateStamper
|
|
; as well as P2DOS (CP/M+) stamps.
|
|
; Besides basic functionality (e.g. error handling), all I/O routines
|
|
; are located in non-banked memory. Many Disk routines begin in non-banked
|
|
; memory with just a small footprint, and are continued in banked memory.
|
|
; This is also the case for T&D stamp routines. With the exception of
|
|
; 4 bytes stored in /B2RAM/, all data bytes are located in non-banked
|
|
; CSEG code segment.
|
|
;
|
|
; A comment on function 152 Parse FileSpec: ZCPR 4.1 (Z41.ZRL) relies
|
|
; on that function. Thus, it cannot be removed. Even though most probably
|
|
; no other application might use it. Currently, the entire function is
|
|
; located in non-banked memory. A considerable amount of space could be
|
|
; reclaimed by moving this function (or parts of it) to banked memory.
|
|
;
|
|
; Possible optimisations noticed during disassembly are marked with
|
|
; "#####" in the comment. Speaking of which - non-banked and banked parts
|
|
; need to be sector-aligned, ie. BPBUILD fills the remainder of the last
|
|
; 128-byte block with zeros. In its current state, the non-banked part
|
|
; leaves 71 bytes unused. Or, in other words, a block could be saved
|
|
; if 57 bytes could be eliminated and/or moved to banked code section.
|
|
;************************************************************************
|
|
|