Files
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

4190 lines
69 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

.z80
subttl Copyright Information
title Personal CP/M BDOS, Version 1.0, April 1984
;*****************************************************************
;*****************************************************************
;** **
;** P E R S O N A L C P / M **
;** **
;** B a s i c D i s k O p e r a t i n g S y s t e m **
;** **
;** I n t e r f a c e M o d u l e **
;** **
;*****************************************************************
;*****************************************************************
;
; Copyright (c) 1984
; Digital Research
; Box 579, Pacific Grove
; California
subttl Equates
on equ 0ffffh
off equ 00000h
data_low equ on ;code segment will be at lower address
;than data segment
standard equ on ;data not in separate segment
org 0000H
BASE equ $
; bios value defined at end of module
SSIZE equ 32 ;32 level stack
; low memory locations
reboot equ 0000h ;reboot system
;
; equates for non graphic characters
;
ctlc equ 03h ;control c
ctle equ 05h ;physical eol
ctlh equ 08h ;backspace
ctlp equ 10h ;prnt toggle
ctlr equ 12h ;repeat line
ctls equ 13h ;stop/start screen
ctlu equ 15h ;line delete
ctlx equ 18h ;=ctl-u
ctlz equ 1Ah ;end of file
rubout equ 7Fh ;char delete
tab equ 09h ;tab char
cr equ 0Dh ;carriage return
lf equ 0Ah ;line feed
ctl equ 5Eh ;up arrow
subttl PUBLICs and EXTRNs
name ('BDOS')
cseg
;used
; by
public BDOS$CD
public BDOS$DT
public ?bdos
public ?bdosc
public ?bdosw
;defined in
extrn ?flush ;bios
extrn ?discd ;bios
extrn ?mov ;bios
extrn ?auxis ;bios
extrn ?auxos ;bios
extrn ?dscrf ;bios
extrn ?bytbc ;bios
extrn ?bytba ;bios
subttl BDOS Front End
BDOS$CD:
defb '654321'
; enter here from the user's program with function number in c,
; and information address in d,e
if not data_low
?bdos:
endif
jp bdose ;past parameter block
defw SUB$FLAG
defw OLOG
front_size equ $-bdos$cd ;size of block to load to front of
;data segment
bdose: ;arrive here from user programs
ld (info),de ;info = DE
ld A,C ;FX=BDOS FUNCTION NUMBER
ld (FX),a
ld a,(ACTDSK) ;SELDSK=ACTDSK
ld (SELDSK),a
ld hl,0 ;return value defaults to 0000
ld (aret),hl
ld (RESEL),hl ; RESEL,RELOG = FALSE
;save user's stack pointer, set to local stack
ld (entsp),sp ;entsp = SP
ld sp,lstack ;local stack setup
ld hl,goback ;return here after all functions
push hl ;jmp goback equivalent to ret
ld A,C
cp nfuncs
jr nc,HIGH$FX
ld C,E ;possible output character to C
ld hl,functab
DISPATCH:
ld E,A ;DE=func, HL=.ciotab
ld D,0
add hl,de ;DE=functab(func)
add hl,de
ld E,(hl)
inc hl
ld D,(hl)
ld hl,(info) ;info in DE for later xchg
ex de,hl ;dispatched
jp (hl)
subttl Imbedded copyright message
defb 'COPYRIGHT (C) 1984, '
defb 'DIGITAL RESEARCH '
defb '042384'
subttl
HIGH$FX:
cp 45
jp z,FUNC45
cp 48
jp z,FUNC48
cp 124
jp z,func124
cp 125
jp z,func125
ld hl,XFUNCTAB
sub 109 ; RETURN IF FX < 109
ret c
cp XNFUNCS ; RETURN IF FX >= 109 + XNFUNCS
ret nc
jr DISPATCH
subttl Cold & Warm Start Initialization
?bdosc: ;COLD START INITIALIZATION ROUTINE
call FUNC13
ld C,CS$INIT$SIZE
jr WS$INIT0
?bdosw: ;WARM START INITIALIZATION ROUTINE
ld C,WS$INIT$SIZE
WS$INIT0:
xor A
ld hl,COLUMN
WS$INIT1:
ld (hl),A
inc hl
dec C
jr nz,WS$INIT1
if data_low
ld bc,front_size ;move an image of the front of the BDOS code
ld de,bdos$dt ;segment to the front of the data segment for
ld hl,bdos$cd ;systems with data lower in RAM
ldir
endif
ld A,'$'
ld (OUT$DELIM),a
call SCAN$DRIVE
jp NOSELECT1
subttl Dispatch Table for Functions
functab:
defw wbootf,func1,func2,func3
defw punchf,listf,func6,func7
defw func8,func9,func10,func11
diskf equ ($-functab)/2 ;disk funcs
defw func12,func13,func14,func15
defw func16,func17,func18,func19
defw func20,func21,func22,func23
defw func24,func25,func26,func27
defw func28,func29,func30,func31
defw func32,func33,func34,func35
defw func36,func37,func38,func39
defw func40
nfuncs equ ($-functab)/2
XFUNCTAB:
defw FUNC109,FUNC110,FUNC111,FUNC112,FUNC113
XNFUNCS equ ($-XFUNCTAB)/2
subttl Error Subroutine
ERROR: ; C = ERROR #, A = FF => RETURN & DISPLAY MODE
push af
push bc
call CPMERR
pop bc
pop af
inc A
ret z
dec C
call z,RESET$DRIVE
call CONINF
jp WBOOTF
subttl Console Handlers
conin:
;read console character to A
ld hl,kbchar
ld A,(hl)
ld (hl),0
or A
ret nz
;no previous keyboard character ready
jp coninf ;get character externally
;ret
conech: ;read character with echo
call conin
call echoc
jr c,CONECH1
;character must be echoed before return
push af
ld C,A
call tabout
pop af
ret ;with character in A
CONECH1:
cp CTLS
ret nz
call CONBS
jr CONECH
echoc:
;echo character if graphic
;cr, lf, tab, or backspace
cp cr
ret z ;carriage return?
cp lf
ret z ;line feed?
cp tab
ret z ;tab?
cp ctlh
ret z ;backspace?
cp ' '
ret ;carry set if not graphic
CONBRKX:
ld a,(KBCHAR)
or A
jr nz,CONB1
CONBRKX1:
call CONSTF
and 1
ret
conbrk: ;check for character ready
ld a,(KBCHAR)
or A
jr nz,CONB1
CONBRK1:
call CONBRKX1
ret z ; RETURN IF CHARACTER NOT READY
;character ready, read it
call coninf ;to A
cp ctls
jr nz,conb0 ;check stop screen function
CONBS:
;found ctls, read next character
call coninf ;to A
cp ctlc
jp z,reboot ;ctlc implies re-boot
;not a reboot, act as if nothing has happened
xor A
ret ;with zero in accumulator
conb0:
;character in accum, save it
ld (kbchar),a
conb1:
;return with true set in accumulator
ld A,-1
ret
conout:
;compute character position/write console char from C
;compcol = true if computing column position
ld a,(compcol)
or A
jr nz,compout
;write the character, then compute the column
;write console character from C
push bc
call CONBRK1 ;check for screen stop function
pop bc
push bc ;recall/save character
call conoutf ;externally, to console
;may be copying to the list device
ld a,(listcp)
or A
call nz,listf ;to printer, if so
pop bc ;recall the character
compout:
ld A,C ;recall the character
;and compute column position
ld hl,column ;A = char, HL = .column
cp rubout
ret z ;no column change if nulls
inc (hl) ;column = column + 1
cp ' '
ret nc ;return if graphic
;not graphic, reset column position
dec (hl) ;column = column - 1
ld A,(hl)
or A
ret z ;return if at zero
;not at zero, may be backspace or end line
ld A,C ;character back to A
cp ctlh
jr nz,notbacksp
;backspace character
dec (hl) ;column = column - 1
ret
notbacksp:
;not a backspace character, eol?
cp lf
ret nz ;return if not
;end of line, column = 0
ld (hl),0 ;column = 0
ret
ctlout:
;send C character with possible preceding up-arrow
ld A,C
call echoc ;cy if not graphic (or special case)
jr nc,tabout ;skip if graphic, tab, cr, lf, or ctlh
;send preceding up arrow
push af
ld C,ctl
call conout ;up arrow
pop af
or 40h ;becomes graphic letter
ld C,A ;ready to print
;(drop through to tabout)
tabout:
;expand tabs to console
ld a,(FX)
dec A
jr z,TABOUT1
ld a,(CONMODE)
and 10H
jp nz,CONOUTF
TABOUT1:
ld A,C
cp tab
jr nz,conout ;direct to conout if not
;tab encountered, move to next tab position
tab0:
ld C,' '
call conout ;another blank
ld a,(column)
and 111b ;column mod 8 = 0 ?
jr nz,tab0 ;back for another if not
ret
backup:
;back-up one screen position
call pctlh
ld C,' '
call conoutf
;(drop through to pctlh)
pctlh:
;send ctlh to console without affecting column count
ld C,ctlh
jp conoutf
;ret
crlfp:
;print #, cr, lf for ctlx, ctlu, ctlr functions
;then move to strtcol (starting column)
ld C,'#'
call conout
call crlf
;column = 0, move to position strtcol
crlfp0:
ld a,(column)
ld hl,strtcol
cp (hl)
ret nc ;stop when column reaches strtcol
ld C,' '
call conout ;print blank
jr crlfp0
crlf:
;carriage return line feed sequence
ld C,cr
call conout
ld C,lf
jp conout
;ret
print:
;print message until M(BC) = '$'
ld hl,OUT$DELIM
ld a,(bc)
cp (hl)
ret z ;stop on $
;more to print
inc bc
push bc
ld C,A ;char to C
call tabout ;another character printed
pop bc
jr print
read: ;read to info address (max length, current length, buffer)
ld A,1
ld (FX),a
ld a,(column)
ld (strtcol),a ;save start for ctl-x, ctl-h
ld hl,(info)
ld C,(hl)
inc hl
push hl
xor A
ld B,A
ld (SAVE$POS),a
;B = current buffer length,
;C = maximum buffer length,
;HL= next to fill - 1
readnx:
;read next character, BC, HL active
push bc
push hl ;blen, cmax, HL saved
readn0:
call conin ;next char in A
pop hl
pop bc ;reactivate counters
cp cr
jp z,readen ;end of line?
cp lf
jp z,readen ;also end of line
cp ctlh
jr nz,noth ;backspace?
;do we have any characters to back over?
ld a,(STRTCOL)
ld D,A
ld a,(COLUMN)
cp D
jr z,readnx
ld (COMPCOL),a ;COL>0
;characters remain in buffer, can we backup one
ld a,b ;check character count SCC 22 Apr 84
or a ; SCC 22 Apr 84
jr z,linelen ;already 0, don't decr SCC 22 Apr 84
dec B ;remove one character
;compcol > 0 marks repeat as length compute
jr linelen ;uses same code as repeat
noth:
;not a backspace
cp rubout
jr nz,notrub ;rubout char?
;rubout encountered, rubout if possible
ld A,B
or A
jr z,readnx ;skip if len=0
;buffer has characters, resend last char
ld A,(hl)
dec B
dec hl ;A = last char
;blen=blen-1, next to fill - 1 decremented
jp rdech1 ;act like this is an echo
notrub:
;not a rubout character, check end line
cp ctle
jr nz,note ;physical end line?
;yes, save active counters and force eol
push bc
ld A,B
ld (SAVE$POS),a
push hl
call crlf
xor A
ld (strtcol),a ;start position = 00
jr readn0 ;for another character
note:
;not end of line, list toggle?
cp ctlp
jr nz,notp ;skip if not ctlp
;list toggle - change parity
push hl ;save next to fill - 1
ld hl,listcp ;HL=.listcp flag
ld A,1
sub (hl) ;True-listcp
ld (hl),A ;listcp = not listcp
pop hl
jr readnx ;for another char
notp:
;not a ctlp, line delete?
cp ctlx
jr nz,notx
pop hl ;discard start position
;loop while column > strtcol
backx:
ld a,(strtcol)
ld hl,column
cp (hl)
jr nc,read ;start again
dec (hl) ;column = column - 1
call backup ;one position
jr backx
notx:
;not control-X, control-U?
cp ctlu
jr nz,notu ;skip if not
;delete line (ctlu)
call crlfp ;physical eol
pop hl ;discard starting position
jp read ;to start all over
notu:
;not line delete, repeat line?
cp ctlr
jr nz,notr
xor A
ld (SAVE$POS),a
linelen:
;repeat line, or compute line len (ctlh)
;if compcol > 0
push bc
call crlfp ;save line length
pop bc
pop hl
push hl
push bc
;bcur, cmax active, beginning buff at HL
rep0:
ld A,B
or A
jr z,rep1 ;count len to 00
inc hl
ld C,(hl) ;next to print
dec B
pop de
push de
ld A,D
sub B
ld D,A
push bc
push hl ;count length down
ld a,(save$pos)
cp D
call c,CTLOUT ;character echoed
pop hl
pop bc ;recall remaining count
jr rep0 ;for the next character
rep1:
;end of repeat, recall lengths
;original BC still remains pushed
push hl ;save next to fill
ld a,(compcol)
or A ;>0 if computing length
jp z,readn0 ;for another char if so
;column position computed for ctlh
ld hl,column
sub (hl) ;diff > 0
ld (compcol),a ;count down below
;move back compcol-column spaces
backsp:
;move back one more space
call backup ;one space
ld hl,compcol
dec (hl)
jr nz,backsp
jp readn0 ;for next character
notr:
;not a ctlr, place into buffer
rdecho:
inc hl
ld (hl),A ;character filled to mem
inc B ;blen = blen + 1
rdech1:
;look for a random control character
push bc
push hl ;active values saved
ld C,A ;ready to print
call ctlout ;may be up-arrow C
pop hl
pop bc
ld A,(hl) ;recall char
cp ctlc ;set flags for reboot test
ld A,B ;move length to A
jr nz,notc ;skip if not a control c
cp 1 ;control C, must be length 1
jp z,reboot ;reboot if blen = 1
;length not one, so skip reboot
notc:
;not reboot, are we at end of buffer?
cp C
jp c,readnx ;go for another if not
readen:
;end of read operation, store blen
pop hl
ld (hl),B ;M(current len) = B
ld C,cr
jp conout ;return carriage
;ret
subttl Character I/O Functions
func1:
;return console character with echo
call conech
jr sta$ret
func2 equ tabout
;write console character with tab expansion
func3:
;return reader character
call readerf
jr sta$ret
;func4: equated to punchf
;write punch character
;func5: equated to listf
;write list character
;write to list device
func6:
;direct console i/o - read if 0FFh
ld A,C
inc A
jr z,dirinp ;0FFh => 00h, means input mode
inc A
jp nz,CONOUTF ;DIRECT OUTPUT FUNCTION
;0FEh => STATUS
call CONBRKX
ret z
jp LRET$EQ$FF
dirinp:
call CONBRKX ;status check
ret z ;skip, return 00 if not ready
;character is ready, get it
call CONIN ;to A
jr sta$ret
FUNC7: ;READER STATUS
call ?auxis
jr STA$RET
FUNC8: ;PUNCH STATUS
call ?auxos
jr STA$RET
func9:
;write line until $ encountered
ex de,hl ;was lhld info
ld C,L
ld B,H ;BC=string address
jp print ;out to console
func10 equ read
;read a buffered console line
func11:
;check console status
call conbrk
;(drop through to sta$ret)
sta$ret:
;store the A register to aret
ld (aret),a
func$ret:
ret ;jmp goback (pop stack for non cp/m functions)
setlret1:
;set lret = 1
ld A,1
jr sta$ret
subttl CP/M-Plus Function
FUNC109: ;GET/SET CONSOLE MODE
; CONMODE BITS = 7 6 5 4 3 2 1 0
; DEFINED BITS = 4
;
; BIT 4 = 0: Normal BDOS operation
; 1: Supress BDOS expansion of tabs, ^P and ^S handling on
; console output
ld hl,CONMODE
TEST$SET:
ld A,D
and E
inc A
ld A,(hl)
jr z,STA$RET
ld (hl),E
ret
FUNC110: ;GET/SET OUTPUT DELIMITER
ld hl,OUT$DELIM
jr TEST$SET
FUNC111: ;PRINT BLOCK TO CONSOLE
FUNC112: ;PRINT BLOCK TO LIST
;
ex de,hl
ld E,(hl)
inc hl
ld D,(hl)
inc hl
ld C,(hl)
inc hl
ld B,(hl)
ex de,hl
;HL = ADDR OF STRING
;BC = LENGTH OF STRING
BLK$OUT:
ld A,B
or C
ret z
push bc
push hl
ld C,(hl)
call BLK$OUT1
pop hl
inc hl
pop bc
dec bc
jr BLK$OUT
BLK$OUT1:
ld a,(FX)
rra
jp c,TABOUT
jp LISTF
subttl New Personal CP/M Functions
FUNC113: ;PERFORM SCREEN FUNCTION
call ?dscrf
jp sthl$ret
func124: ;Byte BLT copy
call ?bytbc
jr sta$ret
func125: ;Byte BLT alter
call ?bytba
jr sta$ret
;
; end of Basic I/O System
subttl BDOS Disk functions
;*****************************************************************
;*****************************************************************
;** **
;** B a s i c D i s k O p e r a t i n g S y s t e m **
;** **
;*****************************************************************
;*****************************************************************
dvers equ 28h ;Personal CP/M 1.0
;
; module addresses
;
;;; literal constants
true equ 0ffh ;constant true
false equ 000h ;constant false
enddir equ 0ffffh ;end of directory
byte equ 1 ;number of bytes for "byte" type
word equ 2 ;number of bytes for "word" type
;
; fixed addresses in low memory
;
tfcb equ 005ch ;default fcb location
tbuff equ 0080h ;default buffer location
subttl
;
; error message handlers
;
rod$error:
;report read/only disk error
ld C,2
jr GOERR
rof$error:
;report read/only file error
ld C,3
jr GOERR
sel$error:
call RESET$DRIVE
;report select error
ld C,4
goerr:
ld H,C
ld L,0FFH
ld (ARET),hl
GOERR1:
ld a,(ERRMODE)
inc A
call nz,ERROR
ld A,0FFH
ld (CURDSK),a
ld a,(FX)
cp 27
jp z,GOBACK0
cp 31
jp z,GOBACK0
jp GOBACK
subttl Local Subroutines for Bios Interface
MOVE:
ld B,0 ;move number of bytes in C
;
MOVEX:
call ?mov
ret
subttl Select Disk
selectdisk:
;select the disk drive given by register D, and fill
;the base addresses curtrka - alloca, then fill
;the values of the disk parameter block
ld C,D ;current disk# to c
ld hl,LSN$NS
ld B,0
add hl,bc
ld (LSN$ADD),hl
;lsb of e = 0 if not yet logged - in
call seldskf ;HL filled by call
;HL = 0000 if error, otherwise disk headers
ld A,H
or L
ret z ;return with 0000 in HL and z flag
;disk header block address in hl
ld E,(hl)
inc hl
ld D,(hl)
inc hl ;DE=.tran
ld (cdrmaxa),hl
inc hl
inc hl ;.cdrmax
ld (curtrka),hl
inc hl
inc hl ;HL=.currec
ld (curreca),hl
inc hl
inc hl ;HL=.buffa
;DE still contains .tran
ex de,hl
ld (tranv),hl ;.tran vector
ld hl,buffa ;DE= source for move, HL=dest
ld C,addlist
call move ;addlist filled
;now fill the disk parameter block
ld de,(dpbaddr) ;DE is source
ld hl,sectpt ;HL is destination
ld C,dpblist
call move ;data filled
;now set single/double map mode
ld hl,(maxall) ;largest allocation number
ld A,H ;00 indicates < 255
ld hl,single
ld (hl),true ;assume a=00
or A
jr z,retselect
;high order of maxall not zero, use double dm
ld (hl),false
retselect:
scf
ret ;select disk function ok
subttl HOME - move to track 0, sector 0
home:
;move to home position, then offset to start of dir
call homef ;move to track 00, sector 00 reference
;lxi h,offset ;mov c,m ;inx h ;mov b,m ;call settrkf ;
;first directory position selected
xor A ;constant zero to accumulator
ld hl,(curtrka)
ld (hl),A
inc hl
ld (hl),A ;curtrk=0000
ld hl,(curreca)
ld (hl),A
inc hl
ld (hl),A ;currec=0000
;curtrk, currec both set to 0000
ret
subttl RDBUFF & WRBUFF - read & write disk buffers
rdbuff:
;read buffer and check condition
ld a,1
call readf ;current drive, track, sector, dma
jr diocomp ;check for i/o errors
wrbuff:
;write buffer and check condition
;write type (wrtype) is in register C
;wrtype = 0 => normal write operation
;wrtype = 1 => directory write operation
;wrtype = 2 => start of new block
call writef ;current drive, track, sector, dma
diocomp: ;check for disk errors
or A
ret z
ld C,A
jp GOERR
subttl SEEK$DIR - seek the record containing the current dir entry
seek$dir:
ld hl,(dcnt) ;directory counter to HL
ld C,dskshf
call hlrotr ;value to HL
ld (arecord),hl ;ready for seek
;jmp seek
;ret
subttl SEEK - seek the track given by actual record
seek:
;seek the track given by arecord (actual record)
;load the registers from memory
ld hl,arecord
ld c,(hl) ;arecord
inc hl
ld b,(hl)
ld hl,(curreca)
ld e,(hl) ;currec
inc hl
ld d,(hl)
ld hl,(curtrka)
ld A,(hl) ;curtrk
inc hl
ld h,(hl)
ld l,A
;loop while arecord < currec
seek0:
ld A,c
sub e
ld A,b
sbc a,d
jr nc,seek1 ;skip if arecord >= currec
;currec = currec - sectpt
push hl
ld hl,(sectpt)
ld A,e
sub L
ld e,A
ld A,d
sbc a,H
ld d,A
pop hl
;curtrk = curtrk - 1
dec hl
jr seek0 ;for another try
seek1:
;look while arecord >= (t:=currec + sectpt)
push hl
ld hl,(sectpt)
add hl,de ;HL = currec+sectpt
jr c,seek2 ;can be > FFFFH
ld A,c
sub l
ld A,b
sbc a,h
jr c,seek2 ;skip if t > arecord
;currec = t
ex de,hl
;curtrk = curtrk + 1
pop hl
inc hl
jr seek1 ;for another try
seek2: pop hl
;arrive here with updated values in each register
push bc
push de
push hl ;to stack for later
;stack contains (lowest) BC=arecord, DE=currec, HL=curtrk
ex de,hl
ld hl,(offset)
add hl,de ;HL = curtrk+offset
ld B,H
ld C,L
call settrkf ;track set up
;note that BC - curtrk is difference to move in bios
pop de ;recall curtrk
ld hl,(curtrka)
ld (hl),E
inc hl
ld (hl),D ;curtrk updated
;now compute sector as arecord-currec
pop de ;recall currec
ld hl,(curreca)
ld (hl),e
inc hl
ld (hl),d
pop bc ;BC=arecord, DE=currec
ld A,c
sub e
ld c,A
ld A,b
sbc a,d
ld b,A
ld hl,(tranv)
ex de,hl ;BC=sector#, DE=.tran
call sectran ;HL = tran(sector)
ld C,L
ld B,H ;BC = tran(sector)
jp setsecf ;sector selected
;ret
subttl FCB constants
;; file control block (fcb) constants
empty equ 0E5h ;empty directory entry
lstrec equ 127 ;last record# in extent
recsiz equ 128 ;record size
fcblen equ 32 ;file control block size
dirrec equ recsiz/fcblen ;directory elts / record
dskshf equ 2 ;log2(dirrec)
dskmsk equ dirrec-1
fcbshf equ 5 ;log2(fcblen)
extnum equ 12 ;extent number field
maxext equ 31 ;largest extent number
ubytes equ 13 ;unfilled bytes field
modnum equ 14 ;data module number
maxmod equ 15 ;largest module number
fwfmsk equ 80h ;file write flag is high order modnum
namlen equ 15 ;name length
reccnt equ 15 ;record count field
dskmap equ 16 ;disk map field
lstfcb equ fcblen-1
nxtrec equ fcblen
ranrec equ nxtrec+1 ;random record field (2 bytes)
;
; reserved file indicators
;
rofile equ 9 ;high order of first type char
invis equ 10 ;invisible file in dir command
; equ 11 ;reserved
subttl Utility functions for file access
dm$position:
;compute disk map position for vrecord to HL
ld hl,blkshf
ld C,(hl) ;shift count to C
ld a,(vrecord) ;current virtual record to A
dmpos0:
or A
rra
dec C
jr nz,dmpos0
;A = shr(vrecord,blkshf) = vrecord/2**(sect/block)
ld B,A ;save it for later addition
ld A,8
sub (hl) ;8-blkshf to accumulator
ld C,A ;extent shift count in register c
ld a,(extval) ;extent value ani extmsk
dmpos1:
;blkshf = 3,4,5,6,7, C=5,4,3,2,1
;shift is 4,3,2,1,0
dec C
jr z,dmpos2
or A
rla
jr dmpos1
dmpos2:
;arrive here with A = shl(ext and extmsk,7-blkshf)
add a,B ;add the previous shr(vrecord,blkshf) value
;A is one of the following values, depending upon alloc
;bks blkshf
;1k 3 v/8 + extval * 16
;2k 4 v/16+ extval * 8
;4k 5 v/32+ extval * 4
;8k 6 v/64+ extval * 2
;16k 7 v/128+extval * 1
ret ;with dm$position in A
subttl GETDM - return disk map value from position given by BC
getdm:
ld hl,(info) ;base address of file control block
ld de,dskmap
add hl,de ;HL =.diskmap
add hl,bc ;index by a single byte value
ld a,(single) ;single byte/map entry?
or A
jr z,getdmd ;get disk map single byte
ld L,(hl)
ld H,0
ret ;with HL=00bb
getdmd:
add hl,bc ;HL=.fcb(dm+i*2)
;double precision value returned
ld E,(hl)
inc hl
ld D,(hl)
ex de,hl
ret
subttl INDEX - compute disk block number from current FCB
index:
call dm$position ;0...15 in register A
ld C,A
ld B,0
call getdm ;value to HL
ld (arecord),hl
ld A,L
or H
ret
subttl ATRAN - compute actual record address, assuming index called
atran:
ld a,(blkshf) ;shift count to reg A
ld hl,(arecord)
atran0:
add hl,hl
dec A
jr nz,atran0 ;shl(arecord,blkshf)
ld (arecord1),hl ;save shifted block #
ld a,(blkmsk)
ld C,A ;mask value to C
ld a,(vrecord)
and C ;masked value in A
or L
ld L,A ;to HL
ld (arecord),hl ;arecord=HL or (vrecord and blkmsk)
ret
subttl GETEXTA - get current extent field address
getexta:
ld hl,(info)
ld de,extnum
add hl,de ;HL=.fcb(extnum)
ret
subttl GETFCBA - compute RECCNT and NXTREC addresses for GET/SETFCB
getfcba:
ld hl,(info)
ld de,reccnt
add hl,de
ex de,hl ;DE=.fcb(reccnt)
ld hl,nxtrec-reccnt
add hl,de ;HL=.fcb(nxtrec)
ret
subttl GETFCB - set variables from currently addressed FCB
getfcb:
call getfcba ;addresses in DE, HL
ld A,(hl)
ld (vrecord),a ;vrecord=fcb(nxtrec)
ex de,hl
ld A,(hl)
ld (rcount),a ;rcount=fcb(reccnt)
call getexta ;HL=.fcb(extnum)
ld a,(extmsk) ;extent mask to a
and (hl) ;fcb(extnum) and extmsk
ld (extval),a
ret
subttl SETFCB - place values back into current FCB
setfcb:
call getfcba ;addresses to DE, HL
ld a,(VRECORD)
ld (hl),A
ld a,(FX)
cp 22
jr nc,setfcb_1
inc (hl)
setfcb_1:
ex de,hl
ld a,(rcount)
ld (hl),A ;fcb(reccnt)=rcount
ret
subttl HLROTR - HL rotated right by amount C
hlrotr:
inc C ;in case zero
hlrotr0:
dec C
ret z ;return when zero
srl h ;SCC - operation performed was actually a
rr l ;'shift right logical' of HL
jr hlrotr0
subttl HLROTL - HL rotated left by amount C
hlrotl:
inc C ;may be zero
hlrotl0:
dec C
ret z ;return if zero
add hl,hl
jr hlrotl0
subttl
SCAN$DRIVE:
ld hl,(DLOG)
SD$0:
ld A,16
SD$1:
dec A
add hl,hl
jr nc,SD$4
push af
push hl
ld E,A
ld a,(SCAN$FLAG)
inc A
jr z,SD$2
call TMPSELECT
or 1
call COPY$ALV
call SET$DIR$BLKS
jr SD$3
SD$2:
ld C,E
call ?discd
SD$3:
pop hl
pop af
SD$4:
or A
jr nz,SD$1
ret
SET$DLOG:
ld de,DLOG
set$cdisk:
ld a,(CURDSK)
SET$CDISK1:
ld C,A ;ready parameter for shift
ld hl,1 ;number to shift
call hlrotl ;HL = mask to integrate
ld a,(de)
or L
ld (de),a
inc de
ld a,(de)
or H
ld (de),a
ret
nowrite:
;return true if dir checksum difference occurred
ld hl,(rodsk)
TEST$VECTOR:
ld a,(curdsk)
ld C,A
call hlrotr
ld A,L
and 1b
ret ;non zero if nowrite
TST$LOG$FXS:
ld hl,LOG$FXS
TST$LOG0:
ld a,(FX)
ld B,A
TST$LOG1:
ld A,(hl)
cp B
ret z
inc hl
or A
jr nz,TST$LOG1
inc A
ret
TST$RELOG:
ld hl,RELOG
ld A,(hl)
or A
ret z
ld (hl),0
call CURSELECT
ld hl,0
ld (DCNT),hl
xor A
ld (DPTR),a
ret
CHK$EXIT$FXS:
ld hl,GOBACK
push hl
ld hl,RW$FXS
call TST$LOG0
jr z,CHK$MEDIA2
ld hl,SC$FXS
call TST$LOG0
jp z,LRET$EQ$FF
pop hl
ret
SET$LSN:
ld hl,(LSN$ADD)
ld C,(hl)
call GETEXTA
inc hl
ld (hl),C
ret
SET$RLOG:
ld hl,(OLOG)
call TEST$VECTOR
ret z
ld de,RLOG
jr SET$CDISK
CHECK$FCB:
call GETEXTA
inc hl
ld A,(hl)
ld hl,(LSN$ADD)
cp (hl)
call nz,CHK$MEDIA1
call GETMODNUM
and 40H
ret z
ld hl,(INFO)
ld (hl),0
ret
CHK$MEDIA1:
ld hl,(RLOG)
call TEST$VECTOR
ret z
pop hl
pop hl
CHK$MEDIA2:
ld A,10
jp STA$RET
set$ro:
;set current disk to read only
ld de,RODSK
ld a,(SELDSK)
call SET$CDISK1
;high water mark in directory goes to max
ld hl,(dirmax)
inc hl
ex de,hl ;DE = directory max
ld hl,(cdrmaxa) ;HL = .cdrmax
ld (hl),E
inc hl
ld (hl),D ;cdrmax = dirmax
ret
check$rodir:
;check current directory element for read/only status
call getdptra ;address of element
check$rofile:
;check current buff(dptr) or fcb(0) for r/o status
ld de,rofile
add hl,de ;offset to ro bit
ld A,(hl)
rla
ret nc ;return if not set
jp rof$error
check$write:
;check for write protected disk
call nowrite
ret z ;ok to write if not rodsk
jp rod$error ;read only disk error
getdptra:
;compute the address of a directory element at
;positon dptr in the buffer
ld hl,(buffa)
ld a,(dptr)
addh:
;HL = HL + A
add a,L
ld L,A
ret nc
;overflow to H
inc H
ret
getmodnum:
;compute the address of the module number
;bring module number to accumulator
;(high order bit is fwf (file write flag)
ld hl,(info)
ld de,modnum
add hl,de ;HL=.fcb(modnum)
ld A,(hl)
ret ;A=fcb(modnum)
clrmodnum:
;clear the module number field for user open/make
call getmodnum
ld (hl),0 ;fcb(modnum)=0
ret
setfwf:
call getmodnum ;HL=.fcb(modnum), A=fcb(modnum)
;set fwf (file write flag) to "1"
or fwfmsk
ld (hl),A ;fcb(modnum)=fcb(modnum) or 80h
;also returns non zero in accumulator
ret
compcdr:
;return cy if cdrmax > dcnt
ld de,(dcnt) ;DE = directory counter
ld hl,(cdrmaxa) ;HL=.cdrmax
ld A,E
sub (hl) ;low(dcnt) - low(cdrmax)
inc hl ;HL = .cdrmax+1
ld A,D
sbc a,(hl) ;hig(dcnt) - hig(cdrmax)
;condition dcnt - cdrmax produces cy if cdrmax>dcnt
ret
setcdr:
;if not (cdrmax > dcnt) then cdrmax = dcnt+1
call compcdr
ret c ;return if cdrmax > dcnt
;otherwise, HL = .cdrmax+1, DE = dcnt
inc de
ld (hl),D
dec hl
ld (hl),E
ret
subdh:
;compute HL = DE - HL
ld A,E
sub L
ld L,A
ld A,D
sbc a,H
ld H,A
ret
newchecksum:
ld C,0FEH ;drop through to compute new checksum
checksum:
;compute current checksum record and update the
;directory element if C=true, or check for = if not
;ARECORD < chksiz?
ld de,(ARECORD)
ld hl,(chksiz)
call subdh ;DE-HL
ret nc ;skip checksum if past checksum vector size
;ARECORD < chksiz, so continue
push bc ;save init flag
;COMPUTE CHECKSUM FOR CURRENT DIRECTORY BUFFER
ld C,RECSIZ ;SIZE OF DIRECTORY BUFFER
ld hl,(BUFFA) ;CURRENT DIRECTORY BUFFER
xor A ;CLEAR CHECKSUM VALUE
COMPUTECS0:
add a,(hl)
inc hl
dec C ;CS=CS+BUFF(RECSIZ-C)
jr nz,COMPUTECS0
ld de,(checka) ;address of check sum vector
ld hl,(ARECORD)
add hl,de ;HL = .check(ARECORD)
pop bc ;recall true=0ffh or false=00 to C
inc C ;0ffh produces zero flag
jr z,initial$cs
inc C
jr z,UPDATE$CS
;not initializing, compare
cp (hl) ;compute$cs=check(ARECORD)?
ret z ;no message if ok
call NOWRITE
ret nz
ld A,0FFH
ld (RELOG),a
call set$rlog
RESET$DRIVE:
call set$dlog
jp RESET37X
initial$cs:
cp (hl)
ld (hl),A
ret z
ld hl,(LSN$ADD)
ld A,1
or (hl)
UPDATE$CS:
;initializing the checksum
ld (hl),A
ret
wrdir:
;write the current directory entry, set checksum
call newchecksum ;initialize entry
call setdir ;directory dma
ld C,1 ;indicates a write directory operation
call wrbuff ;write the buffer
jr setdata ;to data dma address
;ret
rd$dir:
;read a directory entry into the directory buffer
call seek$dir
call setdir ;directory dma
call rdbuff ;directory record loaded
; jmp setdata to data dma address
;ret
setdata:
;set data dma address
ld hl,dmaad
jr setdma ;to complete the call
setdir:
;set directory dma address
ld hl,buffa ;jmp setdma to complete call
setdma:
;HL=.dma address to set (i.e., buffa or dmaad)
ld C,(hl)
inc hl
ld B,(hl) ;parameter ready
jp setdmaf
dir$to$user:
;copy the directory entry to the user buffer
;after call to search or searchn by user code
ld de,(buffa) ;source is directory buffer
ld hl,(dmaad) ;destination is user dma address
ld C,recsiz ;copy entire record
call MOVE
ld hl,LRET
ld A,(hl)
inc A
ret z
ld a,(DCNT)
and DSKMSK
ld (hl),A
ret
end$of$dir:
;return zero flag if at end of directory, non zero
;if not at end (end of dir if dcnt = 0ffffh)
ld hl,(DCNT)
ld A,L
and H
inc A
ret
set$end$dir:
;set dcnt to the end of the directory
ld hl,enddir
ld (dcnt),hl
ret
read$dir:
;read next directory entry, with C=true if initializing
ld de,(dirmax) ;in preparation for subtract
ld hl,(dcnt)
inc hl
ld (dcnt),hl ;dcnt=dcnt+1
;continue while dirmax >= dcnt (dirmax-dcnt no cy)
call subdh ;DE-HL
jr nc,read$dir0
;yes, set dcnt to end of directory
jr set$end$dir
;ret
read$dir0:
;not at end of directory, seek next element
;initialization flag is in C
ld a,(dcnt)
and dskmsk ;low(dcnt) and dskmsk
ld B,fcbshf ;to multiply by fcb size
read$dir1:
add a,A
dec B
jr nz,read$dir1
;A = (low(dcnt) and dskmsk) shl fcbshf
ld (dptr),a ;ready for next dir operation
or A
ret nz ;return if not a new record
push bc ;save initialization flag C
call rd$dir ;read the directory record
pop bc ;recall initialization flag
call CHECKSUM
ld a,(RELOG)
or A
ret z
call CHK$EXIT$FXS
call TST$RELOG
jr RD$DIR
;ret
getallocbit:
;given allocation vector position BC, return with byte
;containing BC shifted so that the least significant
;bit is in the low order accumulator position. HL is
;the address of the byte for possible replacement in
;memory upon return, and D contains the number of shifts
;required to place the returned value back into position
ld A,C
and 111b
inc A
ld E,A
ld D,A
;d and e both contain the number of bit positions to shift
ld H,B
ld L,C
ld C,3
call HLROTR
ld B,H
ld C,L
ld hl,(alloca) ;base address of allocation vector
add hl,bc
ld A,(hl) ;byte to A, hl = .alloc(BC shr 3)
;now move the bit to the low order position of A
rotl: rlca
dec E
jr nz,rotl
ret
set$alloc$bit:
;BC is the bit position of ALLOC to set or reset. The
;value of the bit is in register E.
push de
call getallocbit ;shifted val A, count in D
and 11111110b ;mask low bit to zero (may be set)
pop bc
or C ;low bit of C is masked into A
;jmp rotr ;to rotate back into proper position
;ret
rotr:
;byte value from ALLOC is in register A, with shift count
;in register C (to place bit back into position), and
;target ALLOC position in registers HL, rotate and replace
rrca
dec D
jr nz,rotr ;back into position
ld (hl),A ;back to ALLOC
ret
COPY$ALV:
;IF Z FLAG SET, COPY 1ST ALV TO 2ND ALV
;OTHERWISE, COPY 2ND ALV TO 1ST ALV
push af
call GET$NALBS
ld B,H
ld C,L
ld hl,(ALLOCA)
ld D,H
ld E,L
add hl,bc
pop af
jp z,MOVEX
ex de,hl
jp MOVEX
SCANDM$AB:
push bc
call SCANDM$A
pop bc
;JMP SCANDM$B
SCANDM$B:
;SET/RESET 2ND ALV
push bc
call GET$NALBS
ex de,hl
ld hl,(ALLOCA)
pop bc
push hl
add hl,de
ld (ALLOCA),hl
call SCANDM$A
pop hl
ld (ALLOCA),hl
ret
SCANDM$A:
;SET/RESET 1ST ALLOCATION VECTOR
;scan the disk map addressed by dptr for non-zero
;entries, the allocation vector entry corresponding
;to a non-zero entry is set to the value of C (0,1)
call getdptra ;HL = buffa + dptr
;HL addresses the beginning of the directory entry
ld de,dskmap
add hl,de ;hl now addresses the disk map
push bc ;save the 0/1 bit to set
ld C,fcblen-dskmap+1 ;size of single byte disk map + 1
scandm0:
;loop once for each disk map entry
pop de ;recall bit parity
dec C
ret z ;all done scanning?
;no, get next entry for scan
push de ;replace bit parity
ld a,(single)
or A
jr z,scandm1
;single byte scan operation
push bc ;save counter
push hl ;save map address
ld C,(hl)
ld B,0 ;BC=block#
jr scandm2
scandm1:
;double byte scan operation
dec C ;count for double byte
push bc ;save counter
ld C,(hl)
inc hl
ld B,(hl) ;BC=block#
push hl ;save map address
scandm2:
;arrive here with BC=block#, E=0/1
ld A,C
or B ;skip if = 0000
jr z,scanm3
ld hl,(maxall) ;check invalid index
ld A,L
sub C
ld A,H
sbc a,B ;maxall - block#
call nc,set$alloc$bit
;bit set to 0/1
scanm3:
pop hl
inc hl ;to next bit position
pop bc ;recall counter
jr scandm0 ;for another item
GET$NALBS: ;GET # OF ALLOCATION VECTOR BYTES
ld hl,(MAXALL)
ld C,3
call HLROTR
inc hl
ret
SET$DIR$BLKS:
ld de,(DIRBLK)
ld hl,(ALLOCA)
ld A,(hl)
or E
ld (hl),A
inc hl
ld A,(hl)
or D
ld (hl),A
ret
initialize:
;initialize the current disk
;lret = false ;set to true if $ file exists
;compute the length of the allocation vector
;number of bytes in alloc vector is (maxall/8)+1
call GET$NALBS
ld B,H
ld C,L ;count down BC til zero
ld hl,(alloca) ;base of allocation vector
;fill the allocation vector with zeros
initial0:
ld (hl),0
inc hl ;alloc(i)=0
dec bc ;count length down
ld A,B
or C
jr nz,initial0
;set the reserved space for the directory
call SET$DIR$BLKS
;allocation vector initialized, home disk
call home
;cdrmax = 3 (scans at least one directory record)
ld hl,(cdrmaxa)
ld (hl),3
inc hl
ld (hl),0
;cdrmax = 0000
call set$end$dir ;dcnt = enddir
;read directory entries and check for allocated storage
initial2:
ld C,true
call read$dir
call end$of$dir
jp z,COPY$ALV ;return if end of directory
;not end of directory, valid entry?
call getdptra ;HL = buffa + dptr
ld A,0F0H
and (hl)
jr nz,INITIAL2
;now scan the disk map for allocated blocks
ld C,1 ;set to allocated
call SCANDM$A
call setcdr ;set cdrmax to dcnt
jr initial2 ;for another entry
copy$dirloc:
;copy directory location to lret following
;delete, rename, ... ops
ld a,(dirloc)
jp sta$ret
;ret
compext:
;compare extent# in A with that in C, return nonzero
;if they do not match
push bc ;save C's original value
push af
ld a,(extmsk)
cpl
ld B,A
;B has negated form of extent mask
ld A,C
and B
ld C,A ;low bits removed from C
pop af
and B ;low bits removed from A
sub C
and maxext ;set flags
pop bc ;restore original values
ret
SEARCH$EXTNUM:
ld C,EXTNUM
jr SEARCH
SEARCH$NAMLEN:
ld C,NAMLEN
search:
;search for directory element of length C at info
ld A,0ffh
ld (dirloc),a ;changed if actually found
ld hl,searchl
ld (hl),C ;searchl = C
ld hl,(info)
ld (searcha),hl ;searcha = info
call set$end$dir ;dcnt = enddir
call home ;to start at the beginning
;(drop through to searchn)
searchn:
;search for the next directory element, assuming
;a previous call on search which sets searcha and
;searchl
ld C,false
call read$dir ;read next dir element
call end$of$dir
jr z,search$fin ;skip to end if so
;not end of directory, scan for match
ld hl,(searcha)
ex de,hl ;DE=beginning of user fcb
ld a,(de) ;first character
cp empty ;keep scanning if empty
jr z,searchnext
;not empty, may be end of logical directory
push de ;save search address
call compcdr ;past logical end?
pop de ;recall address
jr nc,search$fin ;artificial stop
searchnext:
xor A
ld (USER0$SEARCH),a
call getdptra ;HL = buffa+dptr
ld a,(searchl)
ld C,A ;length of search to c
ld B,0 ;b counts up, c counts down
searchloop:
ld A,C
or A
jr z,endsearch
ld a,(de)
cp '?'
jr z,searchok ;? matches all
;scan next character if not ubytes
ld A,B
cp ubytes
jr z,searchok
;not the ubytes field, extent field?
cp extnum ;may be extent field
ld a,(de) ;fcb character
jr z,searchext ;skip to search extent
jr c,skipsys ;skip test for sys flag SCC 23 Apr 84
sub (hl) ; SCC 23 Apr 84
and 3Fh ; SCC 23 Apr 84
jr z,searchok ; SCC 23 Apr 84
ld a,(de) ;get FCB char again SCC 23 Apr 84
skipsys: ; SCC 23 Apr 84
sub (hl)
and 7Fh ;mask-out flags/extent modulus
jr z,SEARCHOK
ld A,(hl)
or b
jr nz,SEARCHN
ld a,(FX)
cp 15
jr nz,SEARCHN
ld a,(USRCODE)
or A
jr z,SEARCHN
ld A,0FFH
ld (USER0$SEARCH),a
jr SEARCHOK
searchext:
;A has fcb character
;attempt an extent # match
push bc ;save counters
ld C,(hl) ;directory character to c
call compext ;compare user/dir char
pop bc ;recall counters
jr nz,searchn ;skip if no match
searchok:
;current character matches
inc de
inc hl
inc B
dec C
jr searchloop
endsearch:
xor A
ld (DIRLOC),a
ld (LRET),a
ld hl,USER0$SEARCH
inc (hl)
ret nz
ld hl,(DCNT)
ld (SDCNT),hl
jp SEARCHN
search$fin:
;end of directory, or empty name
call set$end$dir ;may be artifical end
LRET$EQ$FF:
ld A,255
ld B,A
inc B
jp sta$ret
delete:
;delete the currently addressed file
call RESELECT
call check$write ;write protected?
call SEARCH$EXTNUM ;search through file type
ret z
DELETE00:
jr z,DELETE1
call CHECK$RODIR
ld hl,(INFO)
call CHK$WILD
jr nz,DELETE11
call SEARCHN
jr DELETE00
DELETE1:
call SEARCH$EXTNUM
DELETE10:
jp z,COPY$DIRLOC
DELETE11:
call GETDPTRA
ld (hl),EMPTY
ld C,0
call SCANDM$AB
call SET$DIR$BLKS
call DELETE$SUB
call WRDIR
call SEARCHN
jr DELETE10
CHK$WILD:
ld C,11
CHK$WILD1:
inc hl
ld A,3FH
sub (hl)
and 7FH
ret z
dec C
jr nz,CHK$WILD1
or A
ret
get$block:
;given allocation vector position BC, find the zero bit
;closest to this position by searching left and right.
;if found, set the bit to one and return the bit position
;in hl. if not found (i.e., we pass 0 on the left, or
;maxall on the right), return 0000 in hl
ld D,B
ld E,C ;copy of starting position to de
lefttst:
ld A,C
or B
jr z,righttst ;skip if left=0000
;left not at position zero, bit zero?
dec bc
push de
push bc ;left,right pushed
call getallocbit
rra
jr nc,retblock ;return block number if zero
;bit is one, so try the right
pop bc
pop de ;left, right restored
righttst:
ld hl,(maxall) ;value of maximum allocation#
ld A,E
sub L
ld A,D
sbc a,H ;right=maxall?
jr nc,retblock0 ;return block 0000 if so
inc de
push bc
push de ;left, right pushed
ld B,D
ld C,E ;ready right for call
call getallocbit
rra
jr nc,retblock ;return block number if zero
pop de
pop bc ;restore left and right pointers
jr lefttst ;for another attempt
retblock:
rla
inc A ;bit back into position and set to 1
;d contains the number of shifts required to reposition
call rotr ;move bit back to position and store
pop hl
pop de ;HL returned value, DE discarded
ret
retblock0:
;cannot find an available bit, return 0000
ld A,C
or B
jr nz,lefttst ;also at beginning
ld hl,0000h
ret
copy$fcb:
;copy the entire file control block
ld C,0
ld E,fcblen ;start at 0, to fcblen-1
;jmp copy$dir
copy$dir:
;copy fcb information starting at C for E bytes
;into the currently addressed directory entry
push de ;save length for later
ld B,0 ;double index to BC
ld hl,(info) ;HL = source for data
add hl,bc
push hl
call TEST$SUB
ld C,0FFH
call z,SET$SUB$FLAG
pop de ;DE=.fcb(C), source for copy
call getdptra ;HL=.buff(dptr), destination
pop bc ;DE=source, HL=dest, C=length
call move ;data moved
seek$copy:
;enter from close to seek and copy current element
call seek$dir ;to the directory element
jp wrdir ;write the directory element
;ret
TEST$SUB:
inc hl
ld de,SUB$FCB
ld C,11
TEST$SUB1:
ld a,(de)
cp (hl)
ret nz
inc de
inc hl
dec C
jr nz,TEST$SUB1
xor A
ret
SUB$FCB: defb '$$$ SUB'
DELETE$SUB:
call GETDPTRA
call TEST$SUB
ld C,0
ret nz
;JMP SET$SUB$FLAG
SET$SUB$FLAG:
ld a,(CURDSK)
or A
ret nz
ld hl,SUB$FLAG
ld (hl),C
ret
rename:
;rename the file described by the first half of
;the currently addressed file control block. the
;new name is contained in the last half of the
;currently addressed file conrol block. the file
;name and type are changed, but the reel number
;is ignored. the user number is identical
call check$write ;may be write protected
;search up to the extent field
call RESELECT
call SEARCH$EXTNUM
;copy position 0
ld hl,(info)
ld A,(hl) ;HL=.fcb(0), A=fcb(0)
ld de,dskmap
add hl,de ;HL=.fcb(dskmap)
ld (hl),A ;fcb(dskmap)=fcb(0)
;assume the same disk drive for new named file
rename0:
j<> z,COPY$DIRLO<4C> ;sto<74> a<> en<65> o<> dir
call DELETE$SUB
;not end of directory, rename next element
call check$rodir ;may be read-only file
ld C,dskmap
ld E,extnum
call copy$dir
;element renamed, move to next
call searchn
jr rename0
indicators:
;set file indicators for current fcb
call RESELECT
call SEARCH$EXTNUM ;through file type
indic0:
jp z,COPY$DIRLOC ;stop at end of dir
;not end of directory, continue to change
ld C,0
ld E,extnum ;copy name
call copy$dir
call searchn
jr indic0
open:
;search for the directory entry, copy to fcb
call SEARCH$NAMLEN
OPEN1:
ret z ;return with lret=255 if end
;not end of directory, copy fcb information
open$copy:
;(referenced below to copy fcb info)
call getexta
ld A,(hl)
push af
push hl ;save extent#
call getdptra
ex de,hl ;DE = .buff(dptr)
ld hl,(info) ;HL=.fcb(0)
ld C,nxtrec ;length of move operation
push de ;save .buff(dptr)
call move ;from .buff(dptr) to .fcb(0)
;note that entire fcb is copied, including indicators
call setfwf ;sets file write flag
pop de
ld hl,extnum
add hl,de ;HL=.buff(dptr+extnum)
ld C,(hl) ;C = directory extent number
ld hl,reccnt
add hl,de ;HL=.buff(dptr+reccnt)
ld B,(hl) ;B holds directory record count
pop hl
pop af
ld (hl),A ;restore extent number
;HL = .user extent#, B = dir rec cnt, C = dir extent#
;if user ext < dir ext then user := 128 records
;if user ext = dir ext then user := dir records
;if user ext > dir ext then user := 0 records
ld A,C
cp (hl)
ld A,B ;ready dir reccnt
jr z,open$rcnt ;if same, user gets dir reccnt
ld A,0
jr c,open$rcnt ;user is larger
ld A,128 ;directory is larger
open$rcnt: ;A has record count to fill
ld hl,(info)
ld de,reccnt
add hl,de
ld (hl),A
ret
mergezero:
;HL = .fcb1(i), DE = .fcb2(i),
;if fcb1(i) = 0 then fcb1(i) := fcb2(i)
ld A,(hl)
inc hl
or (hl)
dec hl
ret nz ;return if = 0000
ld a,(de)
ld (hl),A
inc de
inc hl ;low byte copied
ld a,(de)
ld (hl),A
dec de
dec hl ;back to input form
ret
close:
;locate the directory element and re-write it
xor A
ld (lret),a
ld (dcnt),a
ld (dcnt+1),a
call nowrite
ret nz ;skip close if r/o disk
;check file write flag - 0 indicates written
call getmodnum ;fcb(modnum) in A
and fwfmsk
ret nz ;return if bit remains set
call SEARCH$NAMLEN
ret z ;return if not found
;merge the disk map at info with that at buff(dptr)
ld bc,dskmap
call getdptra
add hl,bc
ex de,hl ;DE is .buff(dptr+16)
ld hl,(info)
add hl,bc ;DE=.buff(dptr+16), HL=.fcb(16)
ld C,fcblen-dskmap ;length of single byte dm
merge0:
ld a,(single)
or A
jr z,merged ;skip to double
;this is a single byte map
;if fcb(i) = 0 then fcb(i) = buff(i)
;if buff(i) = 0 then buff(i) = fcb(i)
;if fcb(i) <> buff(i) then error
ld A,(hl)
or A
ld a,(de)
jr nz,fcbnzero
;fcb(i) = 0
ld (hl),A ;fcb(i) = buff(i)
fcbnzero:
or A
jr nz,buffnzero
;buff(i) = 0
ld A,(hl)
ld (de),a ;buff(i)=fcb(i)
buffnzero:
cp (hl)
jr nz,mergerr ;fcb(i) = buff(i)?
jr dmset ;if merge ok
merged:
;this is a double byte merge operation
call mergezero ;buff = fcb if buff 0000
ex de,hl
call mergezero
ex de,hl ;fcb = buff if fcb 0000
;they should be identical at this point
ld a,(de)
cp (hl)
jr nz,mergerr ;low same?
inc de
inc hl ;to high byte
ld a,(de)
cp (hl)
jr nz,mergerr ;high same?
;merge operation ok for this pair
dec C ;extra count for double byte
dmset:
inc de
inc hl ;to next byte position
dec C
jr nz,merge0 ;for more
;end of disk map merge, check record count
;DE = .buff(dptr)+32, HL = .fcb(32)
ld bc,-(fcblen-extnum)
add hl,bc
ex de,hl
add hl,bc
;DE = .fcb(extnum), HL = .buff(dptr+extnum)
ld a,(de) ;current user extent number
;if fcb(ext) >= buff(fcb) then
;buff(ext) := fcb(ext), buff(rec) := fcb(rec)
cp (hl)
jr c,endmerge
;fcb extent number >= dir extent number
ld (hl),A ;buff(ext) = fcb(ext)
;update directory record count field
ld bc,reccnt-extnum
add hl,bc
ex de,hl
add hl,bc
;DE=.buff(reccnt), HL=.fcb(reccnt)
ld A,(hl)
ld (de),a ;buff(reccnt)=fcb(reccnt)
endmerge:
ld A,true
ld (fcb$copied),a ;mark as copied
ld C,1
call SCANDM$B
call SETFWF
jp seek$copy ;ok to "wrdir" here - 1.4 compat
;ret
mergerr:
;elements did not merge correctly
ld hl,lret
dec (hl) ;=255 non zero flag set
ret
make:
;create a new file by creating a directory entry
;then opening the file
call check$write ;may be write protected
ld hl,(info)
push hl ;save fcb address, look for e5
ld hl,efcb
ld (info),hl ;info = .empty
ld C,1
call search ;length 1 match on empty entry
pop hl ;recall info address
ld (info),hl ;in case we return here
ret z ;return with error condition 255 if not found
ex de,hl ;DE = info address
;clear the remainder of the fcb
ld hl,namlen
add hl,de ;HL=.fcb(namlen)
ld C,fcblen-namlen ;number of bytes to fill
xor A ;clear accumulator to 00 for fill
make0:
ld (hl),A
inc hl
dec C
jr nz,make0
ld hl,ubytes
add hl,de ;HL = .fcb(ubytes)
ld (hl),A ;fcb(ubytes) = 0
call setcdr ;may have extended the directory
;now copy entry to the directory
call copy$fcb
;and set the file write flag to "1"
jp setfwf
;ret
open$reel:
;close the current extent, and open the next one
;if possible. RMF is true if in read mode
xor A
ld (fcb$copied),a ;set true if actually copied
call close ;close current extent
;lret remains at enddir if we cannot open the next ext
ld a,(LRET)
inc A
ret z ;return if end
;increment extent number
ld hl,(info)
ld bc,extnum
add hl,bc ;HL=.fcb(extnum)
ld A,(hl)
inc A
and maxext
ld (hl),A ;fcb(extnum)=++1
jr z,open$mod ;move to next module if zero
;may be in the same extent group
ld B,A
ld a,(extmsk)
and B
;if result is zero, then not in the same group
ld hl,fcb$copied ;true if the fcb was copied to directory
and (hl) ;produces a 00 in accumulator if not written
jr z,open$reel0 ;go to next physical extent
;result is non zero, so we must be in same logical ext
jr open$reel1 ;to copy fcb information
open$mod:
;extent number overflow, go to next module
ld bc,modnum-extnum
add hl,bc ;HL=.fcb(modnum)
inc (hl) ;fcb(modnum)=++1
;module number incremented, check for overflow
ld A,(hl)
and maxmod ;mask high order bits
jr z,open$r$err ;cannot overflow to zero
;otherwise, ok to continue with new module
open$reel0:
call SEARCH$NAMLEN ;next extent found?
jr nz,OPEN$REEL1
;end of file encountered
ld a,(rmf)
inc A ;0ffh becomes 00 if read
jr z,open$r$err ;sets lret = 1
;try to extend the current file
call make
;cannot be end of directory
call end$of$dir
jr z,open$r$err ;with lret = 1
jr open$reel2
open$reel1:
;not end of file, open
call open$copy
open$reel2:
call getfcb ;set parameters
xor A
jp sta$ret ;ret with lret = 0
open$r$err:
;cannot move to next extent of this file
call setlret1 ;lret = 1
jp setfwf ;ensure that it will not be closed
;ret
seqdiskread:
call RESELECTX
diskread: ;(may enter from seqdiskread)
ld A,true
ld (rmf),a ;read mode flag = true (open$reel)
;read the next record from the current fcb
call getfcb ;sets parameters for the read
ld a,(vrecord)
ld hl,rcount
cp (hl) ;vrecord-rcount
;skip if rcount > vrecord
jr c,recordok
;not enough records in the extent
;record count must be 128 to continue
cp 128 ;vrecord = 128?
jr nz,diskeof ;skip if vrecord<>128
call open$reel ;go to next extent if so
xor A
ld (vrecord),a ;vrecord=00
;now check for open ok
ld a,(lret)
or A
jr nz,diskeof ;stop at eof
recordok:
;arrive with fcb addressing a record to read
call index
;error 2 if reading unwritten data
;(returns 1 to be compatible with 1.4)
jr z,diskeof
;record has been allocated, read it
call atran ;arecord now a disk address
call seek ;to proper track,sector
call rdbuff ;to dma address
jp setfcb ;replace parameter
;ret
diskeof:
jp setlret1 ;lret = 1
;ret
seqdiskwrite:
call RESELECTX
diskwrite: ;(may enter here from seqdiskwrite above)
ld A,false
ld (rmf),a ;read mode flag
;write record to currently selected file
call check$write ;in case write protected
ld hl,(info) ;HL = .fcb(0)
call check$rofile ;may be a read-only file
call GETMODNUM
and 40H
jp nz,ROF$ERROR
call getfcb ;to set local parameters
ld a,(vrecord)
cp lstrec+1 ;vrecord-128
;skip if vrecord > lstrec
;vrecord = 128, cannot open next extent
jp nc,setlret1 ;lret=1
diskwr0:
;can write the next record, so continue
call index
ld C,0 ;marked as normal write operation for wrbuff
jr nz,diskwr1
;not allocated
;the argument to getblock is the starting
;position for the disk search, and should be
;the last allocated block for this file, or
;the value 0 if no space has been allocated
call dm$position
ld (dminx),a ;save for later
ld bc,0000h ;may use block zero
or A
jr z,nopblock ;skip if no previous block
;previous block exists at A
ld C,A
dec bc ;previous block # in BC
call getdm ;previous block # to HL
ld B,H
ld C,L ;BC=prev block#
nopblock:
;BC = 0000, or previous block #
call get$block ;block # to HL
;arrive here with block# or zero
ld A,L
or H
jr nz,blockok
;cannot find a block to allocate
ld A,2
jp sta$ret ;lret=2
blockok:
;allocated block number is in HL
ld (arecord),hl
ex de,hl ;block number to DE
ld hl,(info)
ld bc,dskmap
add hl,bc ;HL=.fcb(dskmap)
ld a,(single)
or A ;set flags for single byte dm
ld a,(dminx) ;recall dm index
jr z,allocwd ;skip if allocating word
;allocating a byte value
call addh
ld (hl),E ;single byte alloc
jr diskwru ;to continue
allocwd:
;allocate a word value
ld C,A
ld B,0 ;double(dminx)
add hl,bc
add hl,bc ;HL=.fcb(dminx*2)
ld (hl),E
inc hl
ld (hl),D ;double wd
diskwru:
;disk write to previously unallocated block
ld C,2 ;marked as unallocated write
diskwr1:
;continue the write operation if no allocation error
;C = 0 if normal write, 2 if to prev unalloc block
ld a,(lret)
or A
ret nz ;stop if non zero returned value
push bc ;save write flag
call atran ;arecord set
ld a,(FX)
cp 40
jr nz,diskwr11
pop bc
push bc
ld A,C
dec A
dec A
jr nz,diskwr11 ;old allocation
push hl ;arecord in hl ret from atran
ld hl,(buffa)
ld D,A ;zero buffa & fill
fill0:
ld (hl),A
inc hl
inc D
jp p,fill0
call setdir
ld hl,(arecord1)
ld C,2
fill1:
ld (arecord),hl
push bc
call seek
pop bc
call wrbuff ;write fill record
ld hl,(arecord)
;restore last record
ld C,0 ;change allocate flag
ld a,(blkmsk)
ld B,A
and L
cp B
inc hl
jr nz,fill1 ;cont until cluster is zeroed
pop hl
ld (arecord),hl
call setdata
call SEEK
jr DISKWR12
diskwr11:
call seek ;to proper file position
pop bc
push bc ;restore/save write flag (C=2 if new block)
ld a,(ARECORD)
ld hl,BLKMSK
and (hl)
jr z,DISKWR13
DISKWR12:
ld C,0
DISKWR13:
call wrbuff ;written to disk
pop bc ;C = 2 if a new block was allocated, 0 if not
;increment record count if rcount<=vrecord
ld a,(vrecord)
ld hl,rcount
cp (hl) ;vrecord-rcount
jr c,diskwr2
;rcount <= vrecord
ld (hl),A
inc (hl) ;rcount = vrecord+1
ld C,2 ;mark as record count incremented
diskwr2:
;A has vrecord, C=2 if new block or new record#
dec C
dec C
jr nz,noupdate
push af ;save vrecord value
call getmodnum ;HL=.fcb(modnum), A=fcb(modnum)
;reset the file write flag to mark as written fcb
and (not fwfmsk) and 0ffh ;bit reset
ld (hl),A ;fcb(modnum) = fcb(modnum) and 7fh
pop af ;restore vrecord
noupdate:
;check for end of extent, if found attempt to open
;next extent in preparation for next write
cp lstrec ;vrecord=lstrec?
jr nz,diskwr3 ;skip if not
;may be random access write, if so we are done
;change next
ld a,(FX)
cp 22
jr nc,DISKWR3 ;skip next extent open op
;update current fcb before going to next extent
call setfcb
call open$reel ;rmf=false
;vrecord remains at lstrec causing eof if
;no more directory space is available
ld hl,lret
ld A,(hl)
or A
jr nz,nospace
;space available, set vrecord=255
dec A
ld (vrecord),a ;goes to 00 next time
nospace:
ld (hl),0 ;lret = 00 for returned value
diskwr3:
jp setfcb ;replace parameters
;ret
rseek:
;random access seek operation, C=0ffh if read mode
;fcb is assumed to address an active file control block
;(MODNUM HAS BEEN SET TO 1010$0000b IF PREVIOUS BAD SEEK)
push bc ;save r/w flag
ld de,(info) ;DE will hold base of fcb
ld hl,ranrec
add hl,de ;HL=.fcb(ranrec)
ld A,(hl)
and 7fh
push af ;record number
ld A,(hl)
rla ;cy=lsb of extent#
inc hl
ld A,(hl)
rla
and 11111b ;A=ext#
ld C,A ;C holds extent number, record stacked
ld A,(hl)
rra
rra
rra
rra
and 1111b ;mod#
ld B,A ;B holds module#, C holds ext#
pop af ;recall sought record #
;check to insure that high byte of ran rec = 00
inc hl
ld L,(hl) ;l=high byte (must be 00)
inc L
dec L
ld L,6 ;zero flag, l=6
;produce error 6, seek past physical eod
jr nz,seekerr
;otherwise, high byte = 0, A = sought record
ld hl,nxtrec
add hl,de ;HL = .fcb(nxtrec)
ld (hl),A ;sought rec# stored away
;arrive here with B=mod#, C=ext#, DE=.fcb, rec stored
;the r/w flag is still stacked. compare fcb values
ld hl,extnum
add hl,de
ld A,C ;A=seek ext#
sub (hl)
jr nz,ranclose ;tests for = extents
;extents match, check mod#
ld hl,modnum
add hl,de
ld A,B ;B=seek mod#
;could be overflow at eof, producing module#
;of 90H or 10H, so compare all but fwf
sub (hl)
and 3FH
jr z,seekok ;same?
ranclose:
push bc
push de ;save seek mod#,ext#, .fcb
call close ;current extent closed
pop de
pop bc ;recall parameters and fill
ld L,3 ;cannot close error #3
ld a,(lret)
inc A
jr z,badseek
ld hl,extnum
add hl,de
ld (hl),C ;fcb(extnum)=ext#
ld hl,modnum
add hl,de
ld (hl),B ;fcb(modnum)=mod#
call open ;is the file present?
ld a,(lret)
inc A
jr nz,seekok ;open successful?
;cannot open the file, read mode?
pop bc ;r/w flag to c (=0ffh if read)
push bc ;everyone expects this item stacked
ld L,4 ;seek to unwritten extent #4
inc C ;becomes 00 if read operation
jr z,badseek ;skip to error if read operation
;write operation, make new extent
call make
ld L,5 ;cannot create new extent #5
ld a,(lret)
inc A
jr z,badseek ;no dir space
;file make operation successful
seekok:
pop bc ;discard r/w flag
xor A
jp sta$ret ;with zero set
badseek:
;fcb no longer contains a valid fcb, mark
;with 1010$00000B IN MODNUM FIELD so that it
;appears as overflow with file write flag set
push hl ;save error flag
call getmodnum ;HL = .modnum
ld (hl),10100000B
pop hl ;and drop through
seekerr:
pop bc ;discard r/w flag
ld A,L
ld (lret),a ;lret=#, nonzero
;setfwf returns non-zero accumulator for err
jp setfwf ;flag set, so subsequent close ok
;ret
randiskread:
;random disk read operation
call RESELECTX
ld C,true ;marked as read operation
call rseek
call z,diskread ;if seek successful
ret
randiskwrite:
;random disk write operation
call RESELECTX
ld C,false ;marked as write operation
call rseek
call z,diskwrite ;if seek successful
ret
compute$rr:
;compute random record position for getfilesize/setrandom
ex de,hl
add hl,de
;DE=.buf(dptr) or .fcb(0), HL = .f(nxtrec/reccnt)
ld C,(hl)
ld B,0 ;BC = 0000 0000 ?rrr rrrr
ld hl,extnum
add hl,de
ld A,(hl)
rrca
and 80h ;A=e000 0000
add a,C
ld C,A
ld A,0
adc a,B
ld B,A
;BC = 0000 000? errrr rrrr
ld A,(hl)
rrca
and 0fh
add a,B
ld B,A
;BC = 000? eeee errrr rrrr
ld hl,modnum
add hl,de
ld A,(hl) ;A=XXX? mmmm
add a,A
add a,A
add a,A
add a,A ;cy=? A=mmmm 0000
push af
add a,B
ld B,A
;cy=?, BC = mmmm eeee errr rrrr
push af ;possible second carry
pop hl ;cy = lsb of L
ld A,L ;cy = lsb of A
pop hl ;cy = lsb of L
or L ;cy/cy = lsb of A
and 1 ;A = 0000 000? possible carry-out
ret
getfilesize:
;compute logical file size for current fcb
;zero the receiving ranrec field
call RESELECT
ld hl,(info)
ld de,ranrec
add hl,de
push hl ;save position
ld (hl),D
inc hl
ld (hl),D
inc hl
ld (hl),D ;=00 00 00
call SEARCH$EXTNUM
getsize:
jr z,SETSIZE
;current fcb addressed by dptr
call getdptra
ld de,reccnt ;ready for compute size
call compute$rr
;A=0000 000? BC = mmmm eeee errr rrrr
;compare with memory, larger?
pop hl
push hl ;recall, replace .fcb(ranrec)
ld E,A ;save cy
ld A,C
sub (hl)
inc hl ;ls byte
ld A,B
sbc a,(hl)
inc hl ;middle byte
ld A,E
sbc a,(hl) ;carry if .fcb(ranrec) > directory
jr c,getnextsize ;for another try
;fcb is less or equal, fill from directory
ld (hl),E
dec hl
ld (hl),B
dec hl
ld (hl),C
getnextsize:
call searchn
jr getsize
setsize:
pop hl ;discard .fcb(ranrec)
ret
setrandom:
;set random record from the current file control block
ld hl,(info)
ld de,nxtrec ;ready params for computesize
call compute$rr ;DE=info, A=cy, BC=mmmm eeee errr rrrr
ld hl,ranrec
add hl,de ;HL = .fcb(ranrec)
ld (hl),C
inc hl
ld (hl),B
inc hl
ld (hl),A ;to ranrec
ret
TMPSELECT:
ld hl,SELDSK
ld (hl),E
CURSELECT:
ld a,(SELDSK)
ld hl,CURDSK
cp (hl)
jr nz,SELECT
cp 0FFH
ret nz
select:
;select disk info for subsequent input or output ops
ld (hl),A
ld D,A
ld hl,(DLOG)
call TEST$VECTOR
ld E,A
push de
call selectdisk
pop hl ;recall dlog vector
jp nc,SEL$ERROR ;returns with CARRY SET if select ok
;is the disk logged in?
dec L
ret z ;return if bit is set
;disk not logged in, set bit and initialize
call INITIALIZE
ld hl,(LSN$ADD)
ld A,(hl)
and 1
push af
add a,(hl)
ld (hl),A
pop af
call nz,SET$RLOG
jp SET$DLOG
;ret
RESELECTX:
ld hl,CHECK$FCB
push hl
reselect:
;check current fcb to see if reselection necessary
ld A,true
ld (resel),a ;mark possible reselect
ld hl,(info)
ld A,(hl) ;drive select code
ld (FCBDSK),a
and 11111b ;non zero is auto drive select
dec A ;drive code normalized to 0..30, or 255
jp m,noselect ; SCC 22 Apr 84
ld (SELDSK),a
noselect:
call CURSELECT
;set user code
ld a,(usrcode) ;0...15
ld hl,(info)
ld (hl),A
NOSELECT0:
call TST$LOG$FXS
ret nz
call FUNC48
NOSELECT1:
ld C,0FFH
jp ?discd
subttl Individual Function Handlers
func12:
;return version number
ld A,dvers
jp sta$ret ;lret = dvers (high = 00)
;ret ;jmp goback
page
func13:
;reset disk system - initialize to disk 0
ld hl,0
ld (rodsk),hl
ld (dlog),hl
xor A
ld (ACTDSK),a
dec A
ld (CURDSK),a
;note that usrcode remains unchanged
ld hl,tbuff
ld (dmaad),hl ;dmaad = tbuff
call SETDATA ;to data dma address
jr NOSELECT1
;ret ;jmp goback
page
FUNC14:
call TMPSELECT
ld a,(SELDSK)
ld (ACTDSK),a
ret
page
func15:
;open file
call clrmodnum ;clear the module number
call reselect
call OPEN
call OPENX
ld a,(DIRLOC)
inc A
ret z
ld hl,(SDCNT)
ld A,L
and 0FCH
ld L,A
dec hl
ld (DCNT),hl
ld hl,(INFO)
ld (hl),0
call SEARCHN
call OPEN1
call OPENX
ret
OPENX:
call END$OF$DIR
ret z
pop hl
ld a,(USRCODE)
ld hl,(INFO)
cp (hl)
jr z,OPENX1
ld de,10 ;test file attribute t2'
add hl,de ;for sys | dir status
ld A,(hl)
and 80H
jr nz,openx2 ;system, allow open SCC 22 Apr 84
inc hl ;bump ptr to ext field SCC 22 Apr 84
inc hl ; SCC 22 Apr 84
ld (hl),a ;zero remainder of FCB SCC 22 Apr 84
ld d,h ; SCC 22 Apr 84
ld e,l ; SCC 22 Apr 84
inc de ; SCC 22 Apr 84
ld bc,19 ; SCC 22 Apr 84
ldir ; SCC 22 Apr 84
jp lret$eq$FF ;flag open failure SCC 22 Apr 84
openx2: ; SCC 22 Apr 84
ld de,4
add hl,de
ld A,(hl)
or 40H
ld (hl),A
OPENX1:
ld de,OLOG
call SET$CDISK
jp SET$LSN
;ret ;jmp goback
page
func16:
;close file
call reselect
call CLOSE
jp SET$LSN
;ret ;jmp goback
page
func17:
;search for first occurence of a file
ex de,hl
xor A
CSEARCH:
push af
ld A,(hl)
cp '?'
jr nz,CSEARCH1
call CURSELECT
call NOSELECT0
ld C,0
jr CSEARCH2
CSEARCH1:
call GETEXTA
ld A,(hl)
cp '?'
call nz,CLRMODNUM
call RESELECT
ld C,NAMLEN
CSEARCH2:
pop af
ld hl,DIR$TO$USER
push hl
jp z,SEARCH
jp SEARCHN
page
func18:
;search for next occurence of a file
ld hl,(SEARCHA)
ld (INFO),hl
or 1
jr CSEARCH
page
FUNC19 equ DELETE
;delete a file
FUNC20 equ SEQDISKREAD
;read a file
FUNC21 equ SEQDISKWRITE
;write a file
page
func22:
;make a file
call clrmodnum
call reselect
call MAKE
jr OPENX1
;ret ;jmp goback
FUNC23 equ RENAME
;rename a file
func24:
;return the login vector
ld hl,(dlog)
jr sthl$ret
;ret ;jmp goback
func25:
;return selected disk number
ld a,(SELDSK)
jp sta$ret
;ret ;jmp goback
func26:
;set the subsequent dma address to info
ex de,hl ;was lhld info
ld (dmaad),hl ;dmaad = info
jp setdata ;to data dma address
;ret ;jmp goback
func27:
;return the login vector address
call CURSELECT
ld hl,(alloca)
jr sthl$ret
;ret ;jmp goback
func28 equ set$ro
;write protect current disk
;ret ;jmp goback
func29:
;return r/o bit vector
ld hl,(rodsk)
jr sthl$ret
;ret ;jmp goback
FUNC30 equ INDICATORS
;set file indicators
func31:
;return address of disk parameter block
call CURSELECT
ld hl,(dpbaddr)
sthl$ret:
ld (aret),hl
ret ;jmp goback
func32:
;GET/SET USER CODE
ld hl,USRCODE
;DOES REG E = FFH?
ld A,E
inc A
ld A,(hl)
jp z,STA$RET ; YES - RETURN USER
;SET USER NUMBER
ld A,E
and 0FH
ld (hl),A
ret
FUNC33 equ RANDISKREAD
;random disk read operation
FUNC34 equ RANDISKWRITE
;random disk write operation
FUNC35 equ GETFILESIZE
;return file size (0-65536)
func36 equ setrandom
;set random record
;ret ;jmp goback
func37:
;drive reset
ex de,hl
RESET37X:
push hl
ld A,L
cpl
ld E,A
ld A,H
cpl
ld hl,(dlog)
and H
ld D,A
ld A,L
and E
ld E,A
ld hl,(rodsk)
ex de,hl
ld (dlog),hl
ld A,L
and E
ld L,A
ld A,H
and D
ld H,A
ld (rodsk),hl
ld A,0FFH
ld (CURDSK),a
ld (SCAN$FLAG),a
pop hl
jp SD$0
func38 equ func$ret
func39 equ func$ret
FUNC40 equ RANDISKWRITE
FUNC45:
;SET BDOS ERROR MODE
ld A,E
ld (ERRMODE),a
ret
FUNC48:
;FLUSH BUFFERS
call ?flush
jp DIOCOMP
subttl BDOS call termination
GOBACK0:
ld hl,0FFFFH
ld (ARET),hl
goback:
;arrive here at end of processing to return to user
ld a,(resel)
or A
jr z,retmon
;reselection may have taken place
ld hl,(INFO)
ld a,(FCBDSK)
ld (hl),A
;return from the disk monitor
retmon:
ld sp,(entsp) ;user stack restored
ld hl,(aret)
ld A,L
ld B,H ;BA = HL = aret
ret
subttl Initialized Data Areas
efcb:
defb empty ;0e5=available dir entry
LOG$FXS:
defb 15,16,17,19,22,23,30,35,0
RW$FXS:
defb 20,21,33,34,40,0
SC$FXS:
defb 16,18
subttl CPMERR - console error message routine
;*****************************************************************
;*****************************************************************
;** **
;** S I M P L E C P / M **
;** **
;** S t a n d a r d E r r o r R o u t i n e **
;** **
;*****************************************************************
;*****************************************************************
cpmerr: ; c = error #
ld B,0
dec C
ld hl,errtbl
add hl,bc
add hl,bc
ld E,(hl)
inc hl
ld D,(hl)
;stack message address, advance to new line
push de
call crlf
;print error prefix
ld bc,dskmsg
call print
;identify drive
ld a,(seldsk)
add a,'A'
ld C,A
call conout
;print colon and space
ld bc,colon
call print
;print error message tail
pop bc
jp print
errtbl: defw permsg,rodmsg,rofmsg,selmsg
dskmsg: defb 'CP/M Error On $'
colon: defb ': $'
permsg: defb 'Disk I/O$'
selmsg: defb 'Invalid Drive$'
rofmsg: defb 'Read/Only File$'
rodmsg: defb 'Read/Only Disk$'
;------------------------------------------------------------------------------
subttl Data Segment
if standard
dseg
endif
public DLOG
public RODSK
public FX
public ERRMODE
public LISTCP
public KBCHAR
public SUB$FLAG
public INFO
public ARET
public ARECORD
public SELDSK
public CONMODE
public out$delim
BDOS$DT equ $
if data_low
?bdos equ $+6
defs front_size
endif
dlog: defs WORD ;logged-in disks
rodsk: defs WORD ;read only disk vector
dmaad: defs WORD ;initial dma address
;
; curtrka - alloca are set upon disk select
; (data must be adjacent, do not insert variables)
; (address of translate vector, not used)
;
cdrmaxa: defs word ;pointer to cur dir max value
curtrka: defs word ;current track address
curreca: defs word ;current record address
buffa: defs word ;pointer to directory dma address
dpbaddr: defs word ;current disk parameter block address
checka: defs word ;current checksum vector address
alloca: defs word ;current allocation vector address
addlist equ $-buffa ;address list size
;
; sectpt - offset obtained from disk parm block at dpbaddr
; (data must be adjacent, do not insert variables)
;
sectpt: defs word ;sectors per track
blkshf: defs byte ;block shift factor
blkmsk: defs byte ;block mask
extmsk: defs byte ;extent mask
maxall: defs word ;maximum allocation number
dirmax: defs word ;largest directory number
dirblk: defs word ;reserved allocation bits for directory
chksiz: defs word ;size of checksum vector
offset: defs word ;offset tracks at beginning
dpblist equ $-sectpt ;size of area
;
; local variables
;
tranv: defs word ;address of translate vector
fcb$copied: defs byte ;set true if copy$fcb called
rmf: defs byte ;read mode flag for open$reel
dirloc: defs byte ;directory flag in rename, etc.
dminx: defs byte ;local for diskwrite
searchl: defs byte ;search length
searcha: defs word ;search address
single: defs byte ;set true if single byte allocation map
fcbdsk: defs byte ;disk named in fcb
rcount: defs byte ;record count in current fcb
extval: defs byte ;extent number and extmsk
vrecord: defs BYTE ;current virtual record
arecord: defs word ;current actual record
arecord1: defs word ;current actual block# * blkmsk
;
; local variables for directory access
;
dptr: defs byte ;directory pointer 0,1,2,3
dcnt: defs word ;directory counter 0,1,...,dirmax
entsp: defs word ;entry stack pointer
defs ssize*2 ;stack size
lstack:
info: defs word ;information address
aret: defs word ;address value to return
lret equ aret ;low(aret)
; RESEL & RELOG ARE INITIALIZED AS A PAIR AT BDOS ENTRY
resel: defs byte ;reselection flag
RELOG: defs BYTE ;RELOG DRIVE SWITCH
save$pos: defs BYTE ;SAVE BEGINNING FUNCTION 10 BUFF POS
LSN$ADD: defs WORD ;LOGIN SEQ # ADDRESS
LSN$NS: defs 16 ;LOGIN SEQUENCE #S (1 PER DRIVE)
SDCNT: defs WORD ;SAVE USER 0 DCNT FOR OPEN FX
USER0$SEARCH: defs WORD ;USER 0 SEARCH FLAG FOR OPEN FX
CURDSK: defs BYTE ;CURRENT DISK NUMBER
ACTDSK: defs BYTE ;ACTUAL SELECTED DISK NUMBER
SELDSK: defs BYTE ;CURRENTLY SELECTED DISK NUMBER
OUT$DELIM: defs BYTE ;FUNCTION 9 (PRINT) DELIMITER
; The following two variables are set to zero by the
; CCP prior to passing control to a loaded transient program.
OLOG: defs WORD ;FILE OPEN DRIVE VECTOR
RLOG: defs WORD ;MEDIA CHANGE DRIVE VECTOR
subttl Reinitialized data
; The following variables are initialized to zero by the BDOS
; warm start initialization routine (WS$INIT) and the
; cold start initialization routine (CS$INIT)
column: defs byte ;column position
usrcode: defs byte ;current user number
kbchar: defs byte ;initial key char = 00
compcol: defs byte ;true if computing column position
strtcol: defs byte ;starting column position after read
FX: defs BYTE ;CURRENT BDOS FUNCTION NUMBER
ERRMODE: defs BYTE ;BDOS ERROR MODE (NORMAL,RETURN,RET & DISPLAY)
SCAN$FLAG: defs BYTE ;SCAN$DRIVE FLAG (FF=DRIVE RESET,0=WS$INIT)
CONMODE: defs BYTE ;CONSOLE MODE (BIT 2 = RAW OUTPUT, BIT 4 = ESD)
WS$INIT$SIZE equ $-COLUMN
;
; The following variables are initialized to zero by the BDOS
; cold start initialization routine (CS$INIT)
;
listcp: defs byte ;listing toggle
FX10FLG: defs BYTE ;CCP FUNCTION 10 (^C) FLAG
SUB$FLAG: defs BYTE ;SUBMIT FLAG ($$$.SUB FILE ON A)
SUBWORK: defs 20 ;CCP SUBMIT WORK AREA
CS$INIT$SIZE equ $-COLUMN
cseg ;this code makes the code segment begin on a
; page boundry
LAST:
defb 0
org (((LAST-BASE)+255) AND 0FF00H) - 1
defb 0
subttl BIOS access constants
BIOS equ $
bootf equ bios+3*0 ;cold boot function
wbootf equ bios+3*1 ;warm boot function
constf equ bios+3*2 ;console status function
coninf equ bios+3*3 ;console input function
conoutf equ bios+3*4 ;console output function
listf equ bios+3*5 ;list output function
punchf equ bios+3*6 ;punch output function
readerf equ bios+3*7 ;reader input function
homef equ bios+3*8 ;disk home function
seldskf equ bios+3*9 ;select disk function
settrkf equ bios+3*10 ;set track function
setsecf equ bios+3*11 ;set sector function
setdmaf equ bios+3*12 ;set dma function
readf equ bios+3*13 ;read disk function
writef equ bios+3*14 ;write disk function
liststf equ bios+3*15 ;list status function
sectran equ bios+3*16 ;sector translate
end