mirror of
https://github.com/wwarthen/RomWBW.git
synced 2026-02-06 22:43:15 -06:00
Jörg Linder has disassembled and thoroughly commented a great deal of the BPBIOS binaries. This was an incredible amount of work. I have added all of these to the RomWBW build scripts and will ultimately integrate them more completely.
4117 lines
121 KiB
Z80 Assembly
4117 lines
121 KiB
Z80 Assembly
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.
|
|
;************************************************************************
|