                               ;
                               ; BDOS	CP/M REV. 2.2
                               ;
EQU           MSIZE,56         ;Memory size
                               ;DOSLOC	EQU  (MSIZE-20)*1024+3C00H	;Where BDOS resides
EQU           DOSLOC,MSIZE-20*1024+$3500
                               ;Where TT BDOS resides
                               ;
EQU           BOOTV,$0000      ;Vector to BIOS warm boot routine
EQU           IOBYTE,$0003     ;System I/O device assignment
EQU           TBUFF,$0080      ;Default DMA buffer location
                               ;
EQU           FALSE,0          ;
EQU           TRUE,1           ;
                               ;
EQU           CR,$0D           ;
EQU           LF,$0A           ;
EQU           TAB,$09          ;Tab
EQU           BACKSP,$08       ;Backspace
EQU           CTRLC,$03        ;Control-C
EQU           CTRLE,$05        ;Control-E
EQU           CTRLP,$10        ;Control-P
EQU           CTRLR,$12        ;Control-R
EQU           CTRLS,$13        ;Control-S
EQU           CTRLU,$15        ;Control-U
EQU           CTRLX,$18        ;Control-X
EQU           DELDAT,$0E5      ;Deleted data byte
                               ;
ORG           DOSLOC           ;
SERIAL:
DB                          0,0,0
                               ;Room for serial number
DB                          0,0,0
                               ;
                               ;************************************************************
                               ;bdos entry point. data enters as follows:
                               ;		C  = command
                               ;		DE = address or 16 bit data word
                               ;		E  = 8 bit data word
                               ;
                               ;      return from bdos data is as follows:
                               ;		A  = status or value
                               ;		HL = 16 bit value or address
                               ;
ENTRY:
       JP      BDOS            ;Entry to disk monitor
                               ;
                               ;******************************************************************************
                               ; BDOS error message table - used by other CP/M utilities perhaps
                               ;
PERERR:
DW                          PERSUB
                               ;Permanent error subroutine
SELERR:
DW                          SELSUB
                               ;Select error subroutine
ROERR:
DW                          ROSUB
                               ;Read-only subroutine
RONERR:
DW                          RONSUB
                               ;Read-only error subroutine
                               ;
                               ;******************************************************************************
                               ;bdos entry saves input data as follows:
                               ;    DE -> FCB		0  -> OLDDSK
                               ;    E  -> IDATA	0  -> OUT1
                               ;    0  -> ODATA	sets return to BEXIT on stack
                               ;    SP -> OLDSP
                               ;    
                               ;    saves input data and jumps to command routine
                               ;
BDOS:
       EX      DE,HL           ;swap DE and HL - entry FCB is now in HL
       LD      (FCB),HL        ;save data in FCB for now
       EX      DE,HL           ;put registers back
       LD      A,E             ;move data byte into A
       LD      (IDATA),A       ;save it 
       LD      HL,0            ;zero out HL
       LD      (ODATA),HL      ;save this for the return
       ADD     HL,SP           ;add in the user stack pointer
       LD      (OLDSP),HL      ;save this for the return also
       LD      SP,USRCOD       ;point to BDOS stack
       XOR     A               ;zero out a
       LD      (OLDDSK),A      ;save this as the old disk
       LD      (OUT1),A        ;and at OUT1 for disk relog on exit
       LD      HL,BEXIT        ;get the return point address
       PUSH    HL              ;and stuff it on the stack for exiting
       LD      A,C             ;move the command into A
       CP      41              ;Max cmds +1 - is it too big?
       RET     NC              ;return if so
       LD      C,E             ;get input data from E into C
       LD      HL,DISTBL       ;point to the dispatch table
       LD      E,A             ;move the command from A into E
       LD      D,0             ;zero out D
       ADD     HL,DE           ;add in the dispatch table
       ADD     HL,DE           ;once more - over two bytes
       LD      E,(HL)          ;pull the jump address
       INC     HL              ;  into
       LD      D,(HL)          ;    DE
       LD      HL,(FCB)        ;get the info word into HL
       EX      DE,HL           ;swap DE and HL
       JP      (HL)            ;jump to the command routine
                               ;
                               ;******************************************************************************
                               ; command dispatch table
                               ;
DISTBL:
DW                          WBOOTF
                               ; 0: System reset
DW                          REDCON
                               ; 1: Console input
DW                          WRTCON
                               ; 2: Console output
DW                          REDRDR
                               ; 3: Reader input
DW                          PUNF
                               ; 4: Punch output
DW                          LISTF
                               ; 5: List output
DW                          DIRTIO
                               ; 6: Direct console I/O
DW                          GETIOB
                               ; 7: Get I/O Byte
DW                          PUTIOB
                               ; 8: Set I/O Byte
DW                          PRNBUF
                               ; 9: Print string
DW                          REDBUF
                               ;10: Read console buffer
DW                          GCSTAT
                               ;11: Get console status
DW                          GETVER
                               ;12: Return version number
DW                          RESET
                               ;13: Reset disk system
DW                          LOGIN
                               ;14: Select disk
DW                          OPEN
                               ;15: Open file
DW                          CLOSE
                               ;16: Close file
DW                          SEAR1
                               ;17: Search for first
DW                          SEARN
                               ;18: Search for next
DW                          DELETE
                               ;19: Delete file
DW                          READ
                               ;20: Read sequential
DW                          WRITE
                               ;21: Write sequential
DW                          CREATE
                               ;22: Make file
DW                          RENAME
                               ;23: Rename file
DW                          GLOGIN
                               ;24: Return login vector
DW                          GETDRV
                               ;25: Return current disk
DW                          DMASET
                               ;26: Set DMA address
DW                          GALLOC
                               ;27: Get addr (alloc)
DW                          MAKRO
                               ;28: Write protect disk
DW                          GROVEC
                               ;29: Get R/O vector
DW                          SETATT
                               ;30: Set file attributes
DW                          GETPAR
                               ;31: Get addr (disk parms)
DW                          MODUSR
                               ;32: Set/Get user code
DW                          REDRND
                               ;33: Read random
DW                          WRTRND
                               ;34: Write random
DW                          FILSIZ
                               ;35: Compute file size
DW                          SETRND
                               ;36: Set random record
DW                          RESDRV
                               ;37: Reset drive
DW                          JR ;38: Undefined - go back
DW                          JR ;39: Undefined - go back
DW                          ZERRND
                               ;40: Fill random file w/ zeros
                               ;
                               ;******************************************************************************
                               ; Print the permanent error message
                               ;
PERSUB:
       LD      HL,PERMSG       ;point to bad sector message
       CALL    DOSERR          ;print out with disk and return
       CP      CTRLC           ;return with a char - is it ctrlc?
       JP      Z,BOOTV         ;if so go reboot
       RET                     ;otherwise just go back
                               ;
                               ;******************************************************************************
                               ; Print the select error message then die
                               ;
SELSUB:
       LD      HL,SELMSG       ;'Select' msg
       JP      BOMB            ;say too bad then abort
                               ;
                               ;******************************************************************************
                               ; Print read only error message then die
ROSUB:
       LD      HL,ROMSG        ;Send R/O msg
       JP      BOMB            ; say too bad then abort
                               ;
                               ;******************************************************************************
                               ; Print file read-only error message, die
                               ;
RONSUB:
       LD      HL,ROEMSG       ;'File R/O' msg
                               ;
                               ;******************************************************************************
                               ; Print out the error message then reboot
                               ;
BOMB:
       CALL    DOSERR          ;Print BDOS err on [dsk #]
       JP      BOOTV           ;Reboot system
                               ;
                               ;******************************************************************************
                               ; Error messages
                               ;
DOSMSG:
DB                          'Bdos Err On '
DSKCH:
DB                          ' : $'
PERMSG:
DB                          'Bad Sector$'
SELMSG:
DB                          'Select$'
ROEMSG:
DB                          'File '
ROMSG:
DB                          'R/O$'
                               ;
                               ;******************************************************************************
                               ; Print BDOS error msg with disk #
                               ;
DOSERR:
       PUSH    HL              ;save the message pointer
       CALL    CRLF            ;Turn up a new line
       LD      A,(CURDSK)      ;get the current disk
       ADD     A,'A'           ;add in the ascii bias
       LD      (DSKCH),A       ;Form drive ID char
       LD      BC,DOSMSG       ;Print the
       CALL    PRINT           ; 'Bdos Err on' msg
       POP     BC              ;restore the message pointer
       CALL    PRINT           ;Print specific error message
                               ;
                               ;******************************************************************************
                               ; Read next console character
                               ;
CONIN:
       LD      HL,CHRRDY       ;Point to the char ready flag
       LD      A,(HL)          ;move the ready byte into A
       LD      (HL),0          ;and zero out the flag
       OR      A               ;was anyone home
       RET     NZ              ;return if not
       JP      CONIF           ;otherwise go get the thing
                               ;
                               ;******************************************************************************
                               ; Read console character, echo print if it is ok 
                               ;
CIECHO:
       CALL    CONIN           ;get a character
       CALL    GRAFIC          ;check the control characters
       RET     C               ;return if the char is not printable
       PUSH    AF              ;otherwise save it
       LD      C,A             ;put a copy into C
       CALL    WRTCON          ;and print it out
       POP     AF              ;restore it
       RET                     ;and return to sender
                               ;
                               ;******************************************************************************
                               ;see if A is a good control char, ret with CY set if not
                               ;
GRAFIC:
       CP      CR              ;test for carriage return
       RET     Z               ;return if it was
       CP      LF              ;how about line feed
       RET     Z               ;that is ok
       CP      TAB             ;as is tab
       RET     Z               ;so back with it
       CP      BACKSP          ;Backspace in 2.0
       RET     Z               ;is known
       CP      ' '             ;test all other control characters
       RET                     ;and go back with carry set for no echo
                               ;
                               ;******************************************************************************
                               ; Check for console break and abort
                               ;
CONBRK:
       LD      A,(CHRRDY)      ;get the character ready byte
       OR      A               ;and check for waiting char
       JP      NZ,CB1          ;non zero says something is there
       CALL    CONSF           ;check the console if none there
       AND     $01             ;and in the status bit
       RET     Z               ;return if it came up zero
       CALL    CONIF           ;otherwise go get the waiting char
       CP      CTRLS           ;was it a control-s
       JP      NZ,CB0          ;if not return with ready flag set
       CALL    CONIF           ;if it was wait for the next char
       CP      CTRLC           ;is this one control c?
       JP      Z,BOOTV         ;if so we reboot
       XOR     A               ;or else zero a
       RET                     ;and simply return
                               ;
CB0:
       LD      (CHRRDY),A      ;Save input char
CB1:
       LD      A,TRUE          ;and say true to the caller
       RET                     ;and go back
                               ;
                               ;******************************************************************************
                               ; Output char in C to console, list also if LSTCPY = true
                               ;
CONOUT:
       LD      A,(CTEMP1)      ;get the first temp column counter
       OR      A               ;is it zero?
       JP      NZ,CONOU1       ;if nonzero jump over
       PUSH    BC              ;otherwise save input character
       CALL    CONBRK          ;Check for abort
       POP     BC              ;recover the char
       PUSH    BC              ;save it again
       CALL    CONOF           ;and print the character on the console
       POP     BC              ;recover once more
       PUSH    BC              ;would you believe save it again
       LD      A,(LSTCPY)      ;get the list flag byte
       OR      A               ;to set flags
       CALL    NZ,LISTF        ; and to list if LSTCPY=true
       POP     BC              ;restore char one last time
CONOU1:
       LD      A,C             ;put the byte into A
       LD      HL,COLUMN       ;point to the buffer column counter
       CP      $7F             ;Rubout ?
       RET     Z               ;return if so
       INC     (HL)            ;Increment column
       CP      ' '             ;is the character a space?
       RET     NC              ;return if less - must be control
       DEC     (HL)            ;Decrement column
       LD      A,(HL)          ;and get the character there
       OR      A               ;check for a null
       RET     Z               ;return if it is
       LD      A,C             ;move the char into C
       CP      BACKSP          ;was it a backspace?
       JP      NZ,CONOU2       ;if not jump over
       DEC     (HL)            ;Decrement column
       RET                     ;and return
                               ;
CONOU2:
       CP      LF              ;was it a linefeed?
       RET     NZ              ;return if not again
       LD      (HL),0          ;Set column to 0
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ; Print char in C at console, convert Ctrl chars to ^[char]
                               ;
CTLOUT:
       LD      A,C             ;move the char into A
       CALL    GRAFIC          ;see if it is printable
       JP      NC,WRTCON       ;jump past cause it is ok
       PUSH    AF              ;otherwise save ctrl char on the stack
       LD      C,$5E           ;get the "^" char
       CALL    CONOUT          ;and print it
       POP     AF              ;get the char back
       OR      $40             ;add in the bias to make it print
       LD      C,A             ;put it into C for the next
                               ;
                               ;******************************************************************************
                               ; BDOS function 2: write to the system console char in C
                               ;
WRTCON:
       LD      A,C             ;which puts it into A
       CP      TAB             ;check for tab
       JP      NZ,CONOUT       ;if not jump and print
TABOUT:
       LD      C,' '           ;Space gets printed to expand tabs
       CALL    CONOUT          ; to
       LD      A,(COLUMN)      ;  next
       AND     $07             ;   tab
       JP      NZ,TABOUT       ;    stop
       RET                     ;Return
                               ;
DELAST:
       CALL    BACKUP          ;back up one character
       LD      C,' '           ;then get a blank
       CALL    CONOF           ;and print it out
BACKUP:
       LD      C,BACKSP        ;get a backspace
       JP      CONOF           ;and print it as well to move into spot
                               ;
                               ;******************************************************************************
                               ; Print pound sign, CRLF, and fix columns
                               ;
LBCRLF:
       LD      C,'#'           ;get a pound sign
       CALL    CONOUT          ;and print it
       CALL    CRLF            ;Turn up a new line
LB1:
       LD      A,(COLUMN)      ;get the column counter
       LD      HL,CTEMP2       ;point to the temp counter
       CP      (HL)            ;are they equal?
       RET     NC              ;not there yet
       LD      C,' '           ;so get a space
       CALL    CONOUT          ;and print it out
       JP      LB1             ;loop til the counts are the same
                               ;
                               ;******************************************************************************
                               ; Print CR/LF at console
                               ;
CRLF:
       LD      C,CR            ; get a cr
       CALL    CONOUT          ;and print it out
       LD      C,LF            ;followed by a lf
       JP      CONOUT          ;which goes out too
                               ;
                               ;******************************************************************************
                               ; Print string at (BC) until '$' with TAB expansion
                               ;
PRINT:
       LD      A,(BC)          ;get the byte at (BC)
       CP      '$'             ;is it the end mark?
       RET     Z               ;if so we are done
       INC     BC              ;otherwise bump the pointer
       PUSH    BC              ;and save it in the stack
       LD      C,A             ;get the character into C
       CALL    WRTCON          ;write it out expanding tabs
       POP     BC              ;recover the pointer
       JP      PRINT           ;and loop til we hit the stop
                               ;
                               ;******************************************************************************
                               ; BDOS function 10: Read console buffer at 
                               ;     enter with BC -> console buffer address
                               ;
REDBUF:
       LD      A,(COLUMN)      ;get the column counter into A
       LD      (CTEMP2),A      ;save it at CTEMP2
       LD      HL,(FCB)        ;get the information into HL
       LD      C,(HL)          ;get the buffer count byte 
       INC     HL              ;and bump the pointer to the next spot
       PUSH    HL              ;save it for later
       LD      B,0             ;zero B
RB0:
       PUSH    BC              ;save BC for later
       PUSH    HL              ;and HL as well
RB1:
       CALL    CONIN           ;go get a char from the console
       AND     $7F             ;strip the parity bit
       POP     HL              ;recover HL
       POP     BC              ;and BC
       CP      CR              ;is this character a CR?
       JP      Z,RBEXIT        ;if so jump over
       CP      LF              ;how about a LF
       JP      Z,RBEXIT        ;jump with that as well
       CP      BACKSP          ;do we have a backspace?
       JP      NZ,CHKRUB       ;if not over we go
       LD      A,B             ;put B into A
       OR      A               ;is it still zero?
       JP      Z,RB0           ;if so go get another character
       DEC     B               ;if not decrement the count in B
       LD      A,(COLUMN)      ;get the column counter
       LD      (CTEMP1),A      ;save it at CTEMP1 for later
       JP      RB65            ;jump over
                               ;
                               ; Check for Rubout (remove & echo last char.)
                               ;
CHKRUB:
       CP      $7F             ;do we have a rubout?
       JP      NZ,CHKEOL       ;if not jump over
       LD      A,B             ;if so move b into A
       OR      A               ;set the flags
       JP      Z,RB0           ;if b was zero go get another
       LD      A,(HL)          ;get the char at (ODATA)
       DEC     B               ;decrement the count in B
       DEC     HL              ;point back to FCB
       JP      RB10            ;jump to echo the char
                               ;
                               ; Check for Control-E (physical end-of-line)
                               ;
CHKEOL:
       CP      CTRLE           ; is it end of line?
       JP      NZ,CKPTOG       ;jump over if not
       PUSH    BC              ;if so save BC
       PUSH    HL              ;and FCB address
       CALL    CRLF            ;Turn up a new line
       XOR     A               ;zero A
       LD      (CTEMP2),A      ;set it into CTEMP2
       JP      RB1             ;and go get more
                               ;
                               ; Check for Control-P
                               ;
CKPTOG:
       CP      CTRLP           ;is it the print toggle
       JP      NZ,CKBOL        ;if not jump past
       PUSH    HL              ;if so save FCB address
       LD      HL,LSTCPY       ;get the pointer to print toggle byte
       LD      A,$01           ;put a 1 into A
       SUB     (HL)            ;subtract it from the toggle
       LD      (HL),A          ;and put it back
       POP     HL              ;recover FCB
       JP      RB0             ;and go get more
                               ;
                               ; Check for Control-X (bac+space to beg. current line)
                               ;
CKBOL:
       CP      CTRLX           ;do we back up?
       JP      NZ,CKREML       ;if not on to the next choice
       POP     HL              ;if so restore the stack pointer
BLOOP:
       LD      A,(CTEMP2)      ;get the byte at CTEMP2
       LD      HL,COLUMN       ;and point to the column counter
       CP      (HL)            ;are they the same?
       JP      NC,REDBUF       ;if so go try again for input
       DEC     (HL)            ;if not decrement the CTEMP2 count
       CALL    DELAST          ;delete the character there
       JP      BLOOP           ;and loop until we are done
                               ;
                               ; check for control-U
                               ;
CKREML:
       CP      CTRLU           ;do we remove the line after newline?
       JP      NZ,CKRETL       ;if not try again
       CALL    LBCRLF          ;if so print a "#" and CR
       POP     HL              ;restore the stack
       JP      REDBUF          ;and try for input again
                               ;
                               ; Check for Control-R (retype current line after new line)
                               ;
CKRETL:
       CP      CTRLR           ;want to retype?
       JP      NZ,ECHOCC       ;if not onward for next
RB65:
       PUSH    BC              ;if so save the count in B
       CALL    LBCRLF          ;print a "#" and CRLF
       POP     BC              ;recover ODATA
       POP     HL              ;and FCB
       PUSH    HL              ;saving FCB again
       PUSH    BC              ;and ODATA
RB7:
       LD      A,B             ;move the count into 
       OR      A               ;see if it is zero
       JP      Z,FIXCOL        ;if so jump over
       INC     HL              ;if not point to ODATA
       LD      C,(HL)          ;pull the byte there into C
       DEC     B               ;decrement the count
       PUSH    BC              ;save it on the stack
       PUSH    HL              ;and the ODATA pointer
       CALL    CTLOUT          ;print it out expanding control chars
       POP     HL              ;recover the ODATA pointer
       POP     BC              ;and the count in B
       JP      RB7             ;loop until the line is out
                               ;
                               ;fix up the column counters
                               ;
FIXCOL:
       PUSH    HL              ;save the pointer
       LD      A,(CTEMP1)      ;get CTEMP1
       OR      A               ;set the flags
       JP      Z,RB1           ;if zero go get the next character
       LD      HL,COLUMN       ;point to the column counter
       SUB     (HL)            ;subtract it from the value of CTEMP1
       LD      (CTEMP1),A      ;and save this back in CTEMP1
FXLOOP:
       CALL    DELAST          ;delete the last character
       LD      HL,CTEMP1       ;point to CTEMP1
       DEC     (HL)            ;decrement it by one
       JP      NZ,FXLOOP       ;loop to delete all of them
       JP      RB1             ;go get the next character
                               ;
                               ;echo the control character
                               ;
ECHOCC:
       INC     HL              ;must be some other control character
       LD      (HL),A          ;put the character into ODATA+1
       INC     B               ;bump the count by one
RB10:
       PUSH    BC              ;save it on the stack
       PUSH    HL              ;and the pointer as well
       LD      C,A             ;put the character into C
       CALL    CTLOUT          ;print it out with grafic control chars
       POP     HL              ;recover the pointer
       POP     BC              ;and the count
       LD      A,(HL)          ;put the byte at (HL) into A
       CP      CTRLC           ;is it an abort?
       LD      A,B             ;put the count into A
       JP      NZ,RB11         ;if no abort jump over
       CP      $01             ;is the count 1?
       JP      Z,BOOTV         ;if so boot
RB11:
       CP      C               ;if not does it equal C
       JP      C,RB0           ;if less go get another char
RBEXIT:
       POP     HL              ;recover the pointer
       LD      (HL),B          ;put the count in b there
       LD      C,CR            ;get a CR
       JP      CONOUT          ;and print it out
                               ;
                               ;******************************************************************************
                               ;BDOS function 1: Read console - return with byte in A
                               ;
REDCON:
       CALL    CIECHO          ;get a char echo if printable
       JP      GOBAK           ;and go back with it
                               ;
                               ;******************************************************************************
                               ; BDOS function 3: Read reader - return with byte in A
                               ;
REDRDR:
       CALL    READF           ;get byte from reader
       JP      GOBAK           ;and return with it
                               ;
                               ;******************************************************************************
                               ; BDOS function 6: Direct I/O 
                               ;	on entry, C=FF for input, C=char for output
                               ;	(Book says E reg vice C)
                               ;	 appears can enter with FE or FF --- ????
                               ;
                               ;	return with char or status in A
                               ;
DIRTIO:
       LD      A,C             ;Get request
       INC     A               ;Test for FF=input request
       JP      Z,INREQ         ;Skip down if input request
       INC     A               ;if FF adding one will set zero flag
       JP      Z,CONSF         ;if it was go get console status
       JP      CONOF           ;otherwise go send it out
                               ;
INREQ:
       CALL    CONSF           ;get console status
       OR      A               ;set flags
       JP      Z,REXIT         ;return if none - restore first
       CALL    CONIF           ;if someone is there go get it
       JP      GOBAK           ;and return with it
                               ;
                               ;******************************************************************************
                               ; BDOS function 7: get IO byte into A
                               ;
GETIOB:
       LD      A,(IOBYTE)      ;get the iobyte
       JP      GOBAK           ;and go back with it
                               ;
                               ;******************************************************************************
                               ; BDOS function 8: set IO byte from C into place
                               ;
PUTIOB:
       LD      HL,IOBYTE       ;point to the iobyte
       LD      (HL),C          ;put the new value in from C
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ; BDOS function 9: Print console buffer until '$'
                               ;	entry string address in DE
                               ;
PRNBUF:
       EX      DE,HL           ;swap DE and HL - HL points to buffer
       LD      C,L             ;and get a copy
       LD      B,H             ;  of HL into BC
       JP      PRINT           ;go to the print routine
                               ;
                               ;******************************************************************************
                               ; BDOS function 11: check console status - return in A
                               ;
GCSTAT:
       CALL    CONBRK          ;Check for abort
GOBAK:
       LD      (ODATA),A       ;save result in return info byte
JR:
       RET                     ;and go back to caller
                               ;
JR1:
       LD      A,$01           ;get a 1 for return
       JP      GOBAK           ;set it in to rinfo and return
                               ;
                               ;******************************************************************************
                               ; console input buffer data storage area
                               ;
CTEMP1:
DB                          0
                               ;temp used for console input buffer
CTEMP2:
DB                          0
                               ;temp count holder for buffer
COLUMN:
DB                          0
                               ;column pointer for buffer
LSTCPY:
DB                          0
                               ;list copy toggle byte
CHRRDY:
DB                          0
                               ;char waiting flag
OLDSP:
DB                          0,0
                               ;Caller's Stack ptr
DB                          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
DB                          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
DB                          0,0,0,0,0,0,0,0
                               ;48 bytes (room for stack)
USRCOD:
DB                          0
                               ;user and disk code lives here
CURDSK:
DB                          0
                               ;current disk holder
FCB:
DW                          0  ;Storage for caller's parms
ODATA:
DW                          0  ;return information storage
                               ;
                               ;******************************************************************************
                               ; Goes to Select error
                               ;
PTSERR:
       LD      HL,SELERR       ;HL=Select error sub addr
                               ;
                               ;******************************************************************************
                               ;routine branches to (HL)
                               ;
HLGO:
       LD      E,(HL)          ;get (HL) into E
       INC     HL              ;bump pointer
       LD      D,(HL)          ;and pull in (HL+1) into D
       EX      DE,HL           ;swap DE and HL
       JP      (HL)            ;swap PC and HL and off we go
                               ;
                               ;******************************************************************************
                               ; routine to move C bytes DE to HL
                               ;
MOVE:
       INC     C               ;add 1 to move count to start loop
MOVLOP:
       DEC     C               ;decrement the loop count
       RET     Z               ;return if we are done
       LD      A,(DE)          ;otherwise get the byte at (DE)
       LD      (HL),A          ;and put it at (HL)
       INC     DE              ;bump the source pointer
       INC     HL              ;and the destination pointer
       JP      MOVLOP          ;and loop til you are done
                               ;
                               ;******************************************************************************
                               ;routine to set up disk identification data for access
                               ;
DISKID:
       LD      A,(CURDSK)      ;get the current logged disk
       LD      C,A             ;and put it into C
       CALL    SELF            ;Select disk - HL comes back with DISP parm
       LD      A,H             ;move H into A for the zero test
       OR      L               ;or in L - zero says select error
       RET     Z               ;so return to LOGDSK with error
       LD      E,(HL)          ;otherwise move
       INC     HL              ;    translation address
       LD      D,(HL)          ;       into DE
       INC     HL              ;bump once more
       LD      (SCRT0),HL      ;and save this address at scratch 0
       INC     HL              ;move the pointer
       INC     HL              ;   two doors down
       LD      (SCRT1),HL      ;and save this address at scratch 1
       INC     HL              ;move over
       INC     HL              ;  two more
       LD      (SCRT2),HL      ;saving the result at scratch 2
       INC     HL              ;two more
       INC     HL              ;  for good measure
       EX      DE,HL           ;swap DE and HL - DE has DIRBUF pointer
       LD      (TRANS),HL      ;save the original disp parm header
       LD      HL,DTEMP        ;point to the temp DMA area
       LD      C,8             ;Bytes to move
       CALL    MOVE            ;Move C bytes DIRBUF to DTEMP (dont know why)
       LD      HL,(DPB)        ;get the disk parm block address
       EX      DE,HL           ;and put it into DE
       LD      HL,DPBLK        ;now set HL to point to storage area
       LD      C,15            ;Bytes to move
       CALL    MOVE            ;Move disk data into storage area
       LD      HL,(DSIZE)      ;get the size of this disk
       LD      A,H             ;high byte zero says 1024 byte blocks
       LD      HL,BSIZE        ;point to block size byte
       LD      (HL),$0FF       ;put in an FF
       OR      A               ;set flags
       JP      Z,JRFF          ;Return FF if blocks are 1024 bytes
       LD      (HL),0          ;otherwise set in a ZERO
JRFF:
       LD      A,$0FF          ;and get an FF into A
       OR      A               ;set flags
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine to home the selected disk and reset data words
                               ;
HOMDSK:
       CALL    HOMF            ;home the selected drive
       XOR     A               ;get a zero
       LD      HL,(SCRT1)      ;point to the current track storage
       LD      (HL),A          ;put in the zero
       INC     HL              ;point to the low half
       LD      (HL),A          ;and zero this as well
       LD      HL,(SCRT2)      ;point to starting sector count for this track
       LD      (HL),A          ;zero it
       INC     HL              ;bump pointer
       LD      (HL),A          ;both halves of this word
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine reads indicated sector and checks status
                               ;
RDSEC:
       CALL    DRDF            ;Read sector
       JP      PERCHK          ;jump to check error and return
                               ;
                               ;******************************************************************************
                               ;routine to write indicated sector and check result
                               ;
WRSEC:
       CALL    DWRF            ;Write sector
                               ;
PERCHK:
       OR      A               ;set flags
       RET     Z               ;return if all is well
       LD      HL,PERERR       ;HL=Permanent err sub addr
       JP      HLGO            ;Go to routine
                               ;
                               ;******************************************************************************
                               ;routine to get the next directory sector location for reading
                               ;
GETDS:
       LD      HL,(COUNT)      ;get the directory count
       LD      C,$02           ;ready to slide it over 2 bits right
       CALL    SPINHL          ;spin hl into position - sector number
       LD      (GROUP),HL      ;save a copy at group
       LD      (LDIRPNT),HL    ;and another at directory pointer
GETD1:
       LD      HL,GROUP        ;point to directory sector number
       LD      C,(HL)          ;pull it
       INC     HL              ;  into
       LD      B,(HL)          ;    BC
       LD      HL,(SCRT2)      ;pull current sector address
       LD      E,(HL)          ;pull (scratch2) 16 bits wide
       INC     HL              ;  into
       LD      D,(HL)          ;    DE
       LD      HL,(SCRT1)      ;pull current track address
       LD      A,(HL)          ;pull (scratch1) into A
       INC     HL              ;  into
       LD      H,(HL)          ;    HL
       LD      L,A             ;      16 bits at (scratch1) into HL
                               ;
                               ;next two blocks set the track and starting sector number for the track
                               ;  so the directory disk access will be right
                               ;
GETD2:
       LD      A,C             ;subtract
       SUB     E               ;  track start sector
       LD      A,B             ;   from
       SBC     A,D             ;     desired sector number
       JP      NC,GETD3        ;if too far jump
       PUSH    HL              ;save current track on stack
       LD      HL,(DPBLK)      ;get sectors per track out
       LD      A,E             ;subtract
       SUB     L               ;  value
       LD      E,A             ;    from
       LD      A,D             ;      track start sector
       SBC     A,H             ;        and leave
       LD      D,A             ;           result in DE
       POP     HL              ;restore track count
       DEC     HL              ;decrement count
       JP      GETD2           ;and loop until start is less than desired
                               ;
                               ; BC has the sector we want, DE the track start sector number, HL the track
                               ; 
GETD3:
       PUSH    HL              ;save the track count
       LD      HL,(DPBLK)      ;get the sectors per track
       ADD     HL,DE           ;compute the next track start sector number
       JP      C,GETD4         ;if this exceeds the desired number jump
       LD      A,C             ;otherwise 
       SUB     L               ;   subtract
       LD      A,B             ;      new track start number
       SBC     A,H             ;        from the desired sector
       JP      C,GETD4         ;if ok then jump over
       EX      DE,HL           ;otherwise swap DE and HL
       POP     HL              ;recover track number
       INC     HL              ;bump it by 1
       JP      GETD3           ;and loop until done
                               ;
                               ;point the disk to the right spot for access now that the computing is done
                               ;
GETD4:
       POP     HL              ;recover track number
       PUSH    BC              ;save desired sector number
       PUSH    DE              ;and the track starting sector number
       PUSH    HL              ;and the track number again
       EX      DE,HL           ;swap DE and HL
       LD      HL,(TOFS)       ;get track offset into HL
       ADD     HL,DE           ;add in the desired track number
       LD      B,H             ;and move it
       LD      C,L             ;   into BC
       CALL    TRKF            ;Select track
       POP     DE              ;recover entry track into DE
       LD      HL,(SCRT1)      ;get pointer to current track storage 
       LD      (HL),E          ;and put
       INC     HL              ;  the new track
       LD      (HL),D          ;    back into scratch
       POP     DE              ;recover current sector number
       LD      HL,(SCRT2)      ;get storage address
       LD      (HL),E          ;and save
       INC     HL              ;  the new current sector number
       LD      (HL),D          ;   there
       POP     BC              ;recover new track sector start number 
       LD      A,C             ;subtract
       SUB     E               ;  track sector start number
       LD      C,A             ;    from
       LD      A,B             ;      the desired number
       SBC     A,D             ;        and
       LD      B,A             ;          leave result in BC
       LD      HL,(TRANS)      ;point to translation routine
       EX      DE,HL           ;put address into DE
       CALL    SECTRN          ;go do the translation
       LD      C,L             ;put the result
       LD      B,H             ;   into BC
       JP      SECF            ;finish setting up the disk pointers 
                               ;
                               ;******************************************************************************
                               ;routine gets extent sector count from storage and figures block number
                               ;
COMBLK:
       LD      HL,BLSHFT       ;point to block shift factor
       LD      C,(HL)          ;pull it into C
       LD      A,(ESCNT2)      ; pull in the extent low byte
COMBL1:
       OR      A               ;set flags - clear carry
       RRA                     ;rotate right through carry
       DEC     C               ;decrement count
       JP      NZ,COMBL1       ;loop until done
       LD      B,A             ;then save the byte into B
       LD      A,$08           ;get bit 3 set
       SUB     (HL)            ;subtract block shift from this value 
       LD      C,A             ;and save it into C
       LD      A,(ESCNT1)      ;get the extent high byte
COMBL2:
       DEC     C               ;decrement count again
       JP      Z,COMBL3        ;if done jump
       OR      A               ;otherwise set flags - clear carry
       RLA                     ;rotate left through carry
       JP      COMBL2          ;loop until done
                               ;
COMBL3:
       ADD     A,B             ;add in the offset 
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine points into the FCB block and returns with group pointer. 
                               ;If BSIZE=0 blocks are>1024 and 16 bits come back
                               ;
GETGRP:
       LD      HL,(FCB)        ;get the FCB address
       LD      DE,16           ;and a 16
       ADD     HL,DE           ;add in the offset
       ADD     HL,BC           ;add in BC as well
       LD      A,(BSIZE)       ;get the block size byte
       OR      A               ;set flags
       JP      Z,HLPBC         ;if zero add HL and BC
       LD      L,(HL)          ;pull in this byte
       LD      H,0             ;set upper byte 0
       RET                     ;return with HL set
                               ;
                               ; routine sets HL = (HL+BC)
                               ;
HLPBC:
       ADD     HL,BC           ;add HL and BC
       LD      E,(HL)          ;move this address byte into E
       INC     HL              ;point to next
       LD      D,(HL)          ;pull this one into D
       EX      DE,HL           ;swap DE and HL
       RET                     ;and go back
                               ;
                               ;******************************************************************************
                               ;routine takes sector count for directory area and gets the group pointer out
                               ;
SECGRP:
       CALL    COMBLK          ;get the block number
       LD      C,A             ;and put it into C
       LD      B,0             ;zero out B
       CALL    GETGRP          ;now go get the right group
       LD      (GROUP),HL      ;save it at group storage
       RET                     ;and go back
                               ;
                               ;******************************************************************************
                               ; routine checks group and sets flags if zero - no group 0 should be there
                               ;
TSTGRP:
       LD      HL,(GROUP)      ;get the group pointer out
       LD      A,L             ;move low into A
       OR      H               ;or with H to set flags
       RET                     ;and go back
                               ;
                               ;******************************************************************************
                               ; routine appears to figure actual sector number from group pointer
                               ;
COMSEC:
       LD      A,(BLSHFT)      ;get the block shift factor
       LD      HL,(GROUP)      ;and the group pointer into HL
COMS1:
       ADD     HL,HL           ;slide it right
       DEC     A               ;and decrement count
       JP      NZ,COMS1        ;looping until it has shifted right amount
       LD      (DIRPNT),HL     ;then save it in the directory pointer
       LD      A,(BLKMSK)      ;now get the block mask out
       LD      C,A             ;save it in C
       LD      A,(ESCNT2)      ;get the low extent counter
       AND     C               ;and it with the block mask
       OR      L               ;or in the directory pointer low byte
       LD      L,A             ;slide this back into L
       LD      (GROUP),HL      ;and save this address at group
       RET                     ;then back to sender
                               ;
                               ;******************************************************************************
                               ;routine to point to information + 12 - the extent byte of the FCB
                               ;
GETEX:
       LD      HL,(FCB)        ;get the FCB pointer
       LD      DE,12           ;and get a 12
       ADD     HL,DE           ;add in the offset
       RET                     ;return with HL set to extent address
                               ;
                               ;******************************************************************************
                               ;routine sets HL to current record (CR) and DE to record count (RC)
                               ;
SETPT:
       LD      HL,(FCB)        ;get the FCB address
       LD      DE,15           ;get an offset of 15
       ADD     HL,DE           ;HL now points to RC address
       EX      DE,HL           ;swap new value into DE
       LD      HL,17           ;now get an offset of 17
       ADD     HL,DE           ;add this in so HL points to CR
       RET                     ;return with HL and DE set
                               ;
                               ;******************************************************************************
                               ;routine appears to get the extent byte from FCB and set up extent pointers
                               ;
FIXEXT:
       CALL    SETPT           ;set record pointers
       LD      A,(HL)          ;get the current record for sequential I/O
       LD      (ESCNT2),A      ;and save it
       EX      DE,HL           ;swap DE and HL
       LD      A,(HL)          ;pull in the record count for this extent
       LD      (RECCNT),A      ;and save it
       CALL    GETEX           ;point to extent byte in FCB
       LD      A,(EXTMSK)      ;get the extent mask
       AND     (HL)            ;and the extent byte extent mask
       LD      (ESCNT1),A      ;saving the result
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine appears to set up record counters
                               ;
FIXREC:
       CALL    SETPT           ;set the record pointers
       LD      A,(DCODE)       ;get the byte
       CP      $02             ;is it a 2?
       JP      NZ,FIXRC1       ;if not jump past
       XOR     A               ;zero out A
FIXRC1:
       LD      C,A             ;move A into C
       LD      A,(ESCNT2)      ;get the byte
       ADD     A,C             ;add it to C
       LD      (HL),A          ;put the result away at CR in FCB
       EX      DE,HL           ;swap DE and HL 
       LD      A,(RECCNT)      ;get the byte ( record count)
       LD      (HL),A          ;place it at RC in FCB
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ; spin HL by C bits right
                               ;
SPINHL:
       INC     C               ;set up for the loop decrement of C
SPH1:
       DEC     C               ;decrement loop count
       RET     Z               ;return if all is well
       LD      A,H             ;otherwise get H into A
       OR      A               ;clear carry
       RRA                     ;rotate right through carry
       LD      H,A             ;put the new byte back
       LD      A,L             ;move L into A
       RRA                     ;rotate it as well
       LD      L,A             ;and put it back
       JP      SPH1            ;loop until the count quits
                               ;
                               ;******************************************************************************
                               ;routine to compute a check sum across a buffer of 128 bytes
                               ;
SUM128:
       LD      C,128           ;we will sum 128 bytes in the temp buffer
       LD      HL,(DTEMP)      ;get the address of the buffer from DTEMP
       XOR     A               ;zero A
SUM1:
       ADD     A,(HL)          ;add in the byte at (HL)
       INC     HL              ;bump pointer to next
       DEC     C               ;decrement count
       JP      NZ,SUM1         ;loop if we are not finished
       RET                     ;otherwise go back with sum in A
                               ;
                               ;******************************************************************************
                               ;routine to slide HL left C bits 
                               ;
SPINBIT:
       INC     C               ;bump for the decrement
SPB1:
       DEC     C               ;decrement the loop count
       RET     Z               ;return when done
       ADD     HL,HL           ;slide HL left one bit
       JP      SPB1            ;spin until count zeroes
                               ;
                               ;******************************************************************************
                               ;set the disk bit in the vector - BC has the current vector
                               ;
SETDBT:
       PUSH    BC              ;save the BC pair
       LD      A,(CURDSK)      ;get the current disk
       LD      C,A             ;put it into C
       LD      HL,1            ;get a 1 into HL
       CALL    SPINBIT         ;spin HL into right spot to set RO
       POP     BC              ;recover BC
       LD      A,C             ;put C into A
       OR      L               ;or in the RO flag low
       LD      L,A             ;put it back
       LD      A,B             ;pull in the high byte
       OR      H               ;or in the RO flag high
       LD      H,A             ;put it back
       RET                     ;and return with new RO vector in HL
                               ;
                               ;******************************************************************************
                               ;routine to check for drive READ-ONLY state
                               ;
ISRO:
       LD      HL,(ROVEC)      ;point to the RO vector
       LD      A,(CURDSK)      ;get the current disk
       LD      C,A             ;and put it into C
       CALL    SPINHL          ;spin the RO vector into position
       LD      A,L             ;and pull L into A
       AND     $01             ;mask off all but bit 0
       RET                     ;and return with flags zero for no,1 for yes
                               ;
                               ;******************************************************************************
                               ; BDOS function 28: makes drive read only
                               ;	
MAKRO:
       LD      HL,ROVEC        ;point to the RO vector
       LD      C,(HL)          ;pull in the low half
       INC     HL              ;point to high byte
       LD      B,(HL)          ;pull this one into B - got it all
       CALL    SETDBT          ;go set this drive RO
       LD      (ROVEC),HL      ;and save the new RO vector back for later
       LD      HL,(DIRMAX)     ;get the directory max value
       INC     HL              ;point to RO + 1
       EX      DE,HL           ;swap this address into DE
       LD      HL,(SCRT0)      ;point to scratch0
       LD      (HL),E          ;put RO + 1 
       INC     HL              ;   into
       LD      (HL),D          ;     scratch0
       RET                     ;and go back
                               ;
                               ;******************************************************************************
                               ;routine to check for file read only
                               ;
CHKFRO:
       CALL    PNTDIR          ;set up HL to DMA + offset
CHKFR1:
       LD      DE,9            ;get 9 - point to file ro bit in t1
       ADD     HL,DE           ;add it to the pointer
       LD      A,(HL)          ;pull in this byte
       RLA                     ;rotate it left through carry
       RET     NC              ;if bit 7 was not set return
       LD      HL,RONERR       ;HL=Read-only error sub addr
       JP      HLGO            ;Go to routine
                               ;
                               ;******************************************************************************
                               ;see if the selected drive is read only - say we cant write if so
                               ;
ROCHK:
       CALL    ISRO            ;is the current drive read only?
       RET     Z               ;return if not - we can write
       LD      HL,ROERR        ;otherwise point to error routine
       JP      HLGO            ;Go to routine
                               ;
                               ;******************************************************************************
                               ;routine to point to directory entry stored in buffer
                               ;
PNTDIR:
       LD      HL,(DTEMP)      ;get the temp buffer address out
       LD      A,(DIROFF)      ;get the offset from storage
PNTD1:
       ADD     A,L             ;add it to A
       LD      L,A             ;  and put it into L
       RET     NC              ;    if no overflow go back
       INC     H               ;otherwise add in the overflow
       RET                     ;and then return
                               ;
                               ;******************************************************************************
                               ;routine gets the S2 byte at (FCB + 14)
                               ;
GETS2:
       LD      HL,(FCB)        ;get the input FCB address
       LD      DE,14           ;point 14 downstream
       ADD     HL,DE           ;set up this address
       LD      A,(HL)          ;pull this byte into A
       RET                     ;and go back
                               ;
                               ;******************************************************************************
                               ;routine sets the S2 byte at (FCB + 14) to zero
                               ;
ZS2:
       CALL    GETS2           ;get the address of S2
       LD      (HL),0          ;and set in a zero
       RET                     ;and go back
                               ;
                               ;******************************************************************************
                               ;routine sets bit 8 of S2
                               ;
SETS28:
       CALL    GETS2           ;point to S2
       OR      $80             ;or in the eigth bit
       LD      (HL),A          ;and put this value back
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine subtracts counter from word at scratch0
                               ;
SUBCS0:
       LD      HL,(COUNT)      ;get the counter
       EX      DE,HL           ;swap it into DE
       LD      HL,(SCRT0)      ;then get scratch0 data word
       LD      A,E             ;and subtract
       SUB     (HL)            ;  data at scratch 0
       INC     HL              ;    from
       LD      A,D             ;      the 
       SBC     A,(HL)          ;        counter
       RET                     ;and go back with result set in flags?
                               ;
                               ;******************************************************************************
                               ;routine checks count against scratch0 and decrements count if ok
                               ;
CHKINC:
       CALL    SUBCS0          ;compare counter and (scratch0)
       RET     C               ;if (scratch0)>counter return
       INC     DE              ;otherwise bump DE
       LD      (HL),D          ;and place it
       DEC     HL              ;  into
       LD      (HL),E          ;    (scratch0)
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine to compute HL = DE - HL
                               ;
SUBHD:
       LD      A,E             ;pull E into A
       SUB     L               ;subtract from L
       LD      L,A             ;and put it back
       LD      A,D             ;then move in the D value 
       SBC     A,H             ;and subtract with borrow
       LD      H,A             ;put back into H
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine to check directory area with check sum to see if it changed
                               ;
CHKSUM:
       LD      C,$0FF          ;get the default return code
CHKCNG:
       LD      HL,(LDIRPNT)    ;point to the directory area
       EX      DE,HL           ;put the address into DE
       LD      HL,(CKSIZE)     ;get the check size
       CALL    SUBHD           ;compute HL = DE - HL 
       RET     NC              ;return if they were the same
       PUSH    BC              ;otherwise save the BC regs
       CALL    SUM128          ;and compute the new check sum
       LD      HL,(CHANGE)     ;point to the change storage area
       EX      DE,HL           ;swap DE and HL
       LD      HL,(LDIRPNT)    ;point to the directory area
       ADD     HL,DE           ;add in the change word
       POP     BC              ;recover BC
       INC     C               ;bump count by one
       JP      Z,CHK1          ;if zero save A at (HL) and return
       CP      (HL)            ;if not check A and (HL) 
       RET     Z               ;return if they are the same
       CALL    SUBCS0          ;otherwise check counter and (scratch0)
       RET     NC              ;return if they are ok
       CALL    MAKRO           ;make the disk read only if not
       RET                     ;and return
                               ;
CHK1:
       LD      (HL),A          ;put A into (HL)
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine to write directory sector out
                               ;
WRTDS:
       CALL    CHKSUM          ;check the directory check sum
       CALL    SETEMP          ;if ok set DMA address up
       LD      C,$01           ;get the code into C
       CALL    WRSEC           ;write the sector
       JP      SETDMA          ;reset the DMA address and return
                               ;
                               ;******************************************************************************
                               ;routine to read the next sector from the disk
                               ;
GETNXT:
       CALL    SETEMP          ;set the DMA address to (DTEMP)
       CALL    RDSEC           ;and read the next sector
                               ;
                               ;******************************************************************************
                               ;tell BIOS the correct DMA address
                               ;
SETDMA:
       LD      HL,DMAADR       ;point to the DMA address word
       JP      SET2            ;and tell BIOS about it
                               ;
                               ;******************************************************************************
                               ; set the dma address to DTEMP
                               ;
SETEMP:
       LD      HL,DTEMP        ;get the DMA address from DTEMP
                               ;
SET2:
       LD      C,(HL)          ;move the low byte into C
       INC     HL              ;point to the next
       LD      B,(HL)          ;and pull high into B
       JP      DMAF            ;Set DMA buffer addr & return
                               ;
                               ;******************************************************************************
                               ;routine to move the data stored at (DTEMP) to (DMAADR)
                               ;
MOVSEC:
       LD      HL,(DTEMP)      ;point to the  DMA buffer
       EX      DE,HL           ;put it into DE
       LD      HL,(DMAADR)     ;get the DMA address for the load
       LD      C,128           ;Bytes to move
       JP      MOVE            ;Move C bytes DE to HL
                               ;
                               ;******************************************************************************
                               ;routine checks count - returns 1 if it is zero
                               ;and the first byte at count if it is nonzero
                               ;
CHKCNT:
       LD      HL,COUNT        ;get count
       LD      A,(HL)          ;check to
       INC     HL              ;   see
       CP      (HL)            ;     if it is zero
       RET     NZ              ; return if it is not   
       INC     A               ;bump up to 1 if it is
       RET                     ;and then return
                               ;
                               ;******************************************************************************
                               ;routine sets counter to -1 for starting loop
                               ;
SETCNT:
       LD      HL,65536-1           ;start off with a 16 bit -1
       LD      (COUNT),HL      ;and save it at count
       RET                     ;return to sender
                               ;
                               ;******************************************************************************
                               ;routine moves to next directory entry - COUNT says the last one looked at
                               ;
NXTDIR:
       LD      HL,(DIRMAX)     ;get the number of directory entries
       EX      DE,HL           ;save it in DE
       LD      HL,(COUNT)      ;now get the directory counter
       INC     HL              ;add one to it
       LD      (COUNT),HL      ;and save it back for later
       CALL    SUBHD           ;looking too far?
       JP      NC,GOTDIR       ;if no carry still entries left
       JP      SETCNT          ;otherwise return with a 16 bit -1
                               ;
                               ;******************************************************************************
                               ;routine to get the next directory sector off of the disk
                               ;
GOTDIR:
       LD      A,(COUNT)       ;get the directory count low byte
       AND     $03             ;strip off all but low two (4 entries per sec)
       LD      B,5             ;set to spin low 3 over 5
SPINAB:
       ADD     A,A             ;move over 1 bit
       DEC     B               ;decrement count
       JP      NZ,SPINAB       ;loop until done
       LD      (DIROFF),A      ;save this for later
       OR      A               ;set flags
       RET     NZ              ;return if not zero
       PUSH    BC              ;otherwise save BC
       CALL    GETDS           ;looks like we set for getting the next dir
       CALL    GETNXT          ;and read the next sector
       POP     BC              ;recover the BC pair
       JP      CHKCNG          ;see if the disk changed and return
                               ;
                               ;******************************************************************************
                               ;routine gets an allocation bit set into position
                               ; and sets pointer to allocation byte for marking blocks
                               ; BC has the group pointer - Exit with BC -> allocation byte
                               ;				       D  -> bit for this group
                               ;				       A  -> allocation byte spun into place
                               ;
GETBIT:
       LD      A,C             ;save entry C into A
       AND     $07             ;strip off all but low 3
       INC     A               ;add one to the result - bit number for group
       LD      E,A             ;and put this into E
       LD      D,A             ;and another into D for exit
       LD      A,C             ;get another copy of C
       RRCA                    ;spin
       RRCA                    ;  it
       RRCA                    ;    right 3
       AND     $1F             ;strip off high 3 leaving high 5 in place
       LD      C,A             ;put this into C
       LD      A,B             ;now get entry B
       ADD     A,A             ;and spin
       ADD     A,A             ;  it left
       ADD     A,A             ;    four
       ADD     A,A             ;     bits
       ADD     A,A             ;
       OR      C               ;or in the word at C
       LD      C,A             ;and put it back into C
       LD      A,B             ;get another copy of B
       RRCA                    ;slide
       RRCA                    ;  over 3
       RRCA                    ;    right
       AND     $1F             ;strip off all but low 3
       LD      B,A             ;place this into B, BC is set
       LD      HL,(ALLOCA)     ;now get the allocation pointer
       ADD     HL,BC           ;add in the offset
       LD      A,(HL)          ;and pull in this byte of it
GBLOOP:
       RLCA                    ;rotate it left through carry
       DEC     E               ;decrement the rotate count
       JP      NZ,GBLOOP       ;loop until the bit is in place
       RET                     ;and then return with it
                               ;
                               ;******************************************************************************
                               ;routine to set allocation bit - BC has the group pointer
                               ;enter with E set with current allocation byte
                               ;
SETBIT:
       PUSH    DE              ;save DE
       CALL    GETBIT          ;get allocation bit into position
       AND     $0FE            ;clear bit zero
       POP     BC              ;restore BC - C has bit to set 
       OR      C               ;or in the allocation bit
PUTBAK:
       RRCA                    ;spin right
       DEC     D               ;decrement count
       JP      NZ,PUTBAK       ;loop til it is back in place
       LD      (HL),A          ;and then put it back in
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine to update allocation map for current directory
                               ;
FIXALL:
       CALL    PNTDIR          ;set pointer to DMA + offset
       LD      DE,16           ;get offset of 16
       ADD     HL,DE           ;add this in - point to group bytes
       PUSH    BC              ;save BC with set bit in C
       LD      C,17            ;set up to spin 16 times
FIXAL1:
       POP     DE              ;get data off stack
       DEC     C               ;decrement counter
       RET     Z               ;return if it is zero
       PUSH    DE              ;otherwise save the data again
       LD      A,(BSIZE)       ;get the block size
       OR      A               ;set the flags
       JP      Z,FIXAL2        ;if zero jump - blocks are > 1024
       PUSH    BC              ;otherwise save the current count 
       PUSH    HL              ;and the address data
       LD      C,(HL)          ;pull in the word at (HL)
       LD      B,0             ;zero out B - BC now has group pointer
       JP      FIXAL3          ;and jump over
                               ;
FIXAL2:
       DEC     C               ;these groups are two bytes wide so dec again
       PUSH    BC              ;save the new count on the stack
       LD      C,(HL)          ;pull in the high group byte
       INC     HL              ;  point to low
       LD      B,(HL)          ;     and pull it in so BC has both
       PUSH    HL              ;save this address
FIXAL3:
       LD      A,C             ;put the new data into A
       OR      B               ;compare with B
       JP      Z,FIXAL4        ;if it is zero block is not allocated
       LD      HL,(DSIZE)      ;get the number of disk blocks
       LD      A,L             ;subtract
       SUB     C               ;   HL
       LD      A,H             ;     from
       SBC     A,B             ;       BC
       CALL    NC,SETBIT       ;if not too big, go set the bit
FIXAL4:
       POP     HL              ;recover the group pointer
       INC     HL              ;bump it by one
       POP     BC              ;recover count
       JP      FIXAL1          ;and spin until done
                               ;
                               ;******************************************************************************
                               ;routine to get the allocation vector
                               ;
GETAL:
       LD      HL,(DSIZE)      ;get the max number of blocks
       LD      C,$03           ;set to 
       CALL    SPINHL          ;  divide by eight 
       INC     HL              ;add 1 for the map size
       LD      B,H             ;and put map size 
       LD      C,L             ;  into BC - number of bytes in allocation map
       LD      HL,(ALLOCA)     ;point to allocation storage area
CLRALL:
       LD      (HL),0          ;put in a zero
       INC     HL              ;bump the pointer
       DEC     BC              ;one less group to zero out
       LD      A,B             ;put the new count into A
       OR      C               ;check for done
       JP      NZ,CLRALL       ;if not loop until so
       LD      HL,(AL01)       ;if done get directory allocation bits
       EX      DE,HL           ;swap DE and AL01
       LD      HL,(ALLOCA)     ;point to allocation stogare
       LD      (HL),E          ;put directory allocation bits
       INC     HL              ;  into that 
       LD      (HL),D          ;     storage area cause they are busy
       CALL    HOMDSK          ;home the selected disk
       LD      HL,(SCRT0)      ;get the scratch 0 address
       LD      (HL),$03        ;save a 3 at (scratch0)
       INC     HL              ;point to (scratch0+1)
       LD      (HL),0          ;and zero this out - it has 16 bit 3 now
       CALL    SETCNT          ;set a counter to -1
GETA1:
       LD      C,$0FF          ;set the exit code in
       CALL    NXTDIR          ;set the next directory pointers
       CALL    CHKCNT          ;was there one?
       RET     Z               ;return if not
       CALL    PNTDIR          ;point to the right spot
       LD      A,DELDAT        ;get the file deleted mark
       CP      (HL)            ;is this what we see?
       JP      Z,GETA1         ;keep looking for a nonempty block
       LD      A,(USRCOD)      ;get the user byte
       CP      (HL)            ;test this against the first byte
       JP      NZ,GETA2        ;if we don't match mark this block
       INC     HL              ;bump pointer to file name entry
       LD      A,(HL)          ;pull the byte in
       SUB     '$'             ;is this a submit file name $$$.SUB 
       JP      NZ,GETA2        ;if not take this block also
       DEC     A               ;otherwise set A to FF
       LD      (ODATA),A       ;and save it at ODATA for CCP to use
GETA2:
       LD      C,$01           ;set the bit into C
       CALL    FIXALL          ;fix this allocation bit
       CALL    CHKINC          ;test for too far - bump count if not
       JP      GETA1           ;spin until done with the scan
                               ;
                               ;******************************************************************************
                               ;routine loads return code and goes back to sender
                               ;
RETCOD:
       LD      A,(SCODE1)      ;
       JP      GOBAK           ;
                               ;
                               ;******************************************************************************
                               ;routine appears to check extent byte
                               ;
CHKEXT:
       PUSH    BC              ;get the entry BC safe
       PUSH    AF              ;and the accumulator
       LD      A,(EXTMSK)      ;get the extent mask
       CPL                     ;flip it over
       LD      B,A             ;and save it into B
       LD      A,C             ;move C into A
       AND     B               ;and it with the flipped extent mask
       LD      C,A             ;save this into C
       POP     AF              ;get the entry A back
       AND     B               ;and with flip extent mask
       SUB     C               ;subtract C
       AND     $1F             ;mask off high bit
       POP     BC              ;recover the entry BC
       RET                     ;and go back confused
                               ;
                               ;******************************************************************************
                               ;search for a specified file - set flags to say how it went
                               ;used by all file routines to set up file information
                               ;
SEARCH:
       LD      A,$0FF          ;set return code up
       LD      (SCODE1),A      ;and save it for later
       LD      HL,SCODE        ;point to search code area
       LD      (HL),C          ;save entry code away
       LD      HL,(FCB)        ;get the entry FCB address
       LD      (SEARA),HL      ;save a copy 
       CALL    SETCNT          ;set the search counter up
       CALL    HOMDSK          ;home the selected disk
SERCHN:
       LD      C,0             ;
       CALL    NXTDIR          ;locate next directory entry
       CALL    CHKCNT          ;check to see if one was found
       JP      Z,NOTFND        ;jump over if not
       LD      HL,(SEARA)      ;get the FCB address out
       EX      DE,HL           ;into DE
       LD      A,(DE)          ;get the byte at (DE)
       CP      DELDAT          ;is it a deleted file? 
       JP      Z,SERCH1        ;if so jump over
       PUSH    DE              ;save this address for later
       CALL    SUBCS0          ;check count and increment
       POP     DE              ;recover the address
       JP      NC,NOTFND       ;if no carry we didn't find the file
SERCH1:
       CALL    PNTDIR          ;point to the indicated directory entry
       LD      A,(SCODE)       ;get the search code out
       LD      C,A             ;stuff it into C
       LD      B,0             ;zero out the high byte
SERCH2:
       LD      A,C             ;move it to A 
       OR      A               ;set the flags - check for zero     
       JP      Z,SEREXT        ;if so jump over
       LD      A,(DE)          ;otherwise get the FCB file name char
       CP      '?'             ;is it a wild card name?
       JP      Z,MATCH         ;if so jump over
       LD      A,B             ;check B
       CP      $0D             ;is it past extent byte?
       JP      Z,MATCH         ;if so this is a good file to go back with
       CP      $0C             ;how about at the extent byte?
       LD      A,(DE)          ;get the byte again
       JP      Z,SERCH3        ;if so jump over
       SUB     (HL)            ;otherwise subtract the two bytes
       AND     $7F             ;strip off the special bits
       JP      NZ,SERCHN       ;if not zero this doesnt match so try again
       JP      MATCH           ;if it matches press on
                               ;
SERCH3:
       PUSH    BC              ;save BC from harm
       LD      C,(HL)          ;pull the byte into C
       CALL    CHKEXT          ;check the extent byte
       POP     BC              ;recover BC
       JP      NZ,SERCHN       ;if not zero search again
MATCH:
       INC     DE              ;otherwise bump FCB pointer
       INC     HL              ;and pointer into memory
       INC     B               ;increment the FCB count
       DEC     C               ;decrement the C counter
       JP      SERCH2          ;and keep going with the search
                               ;
SEREXT:
       LD      A,(COUNT)       ;get the count
       AND     $03             ;strip off the high 5 bits
       LD      (ODATA),A       ;save this at ODATA for return
       LD      HL,SCODE1       ;get the return code address
       LD      A,(HL)          ;pull it into A
       RLA                     ;slide it left one bit through carry
       RET     NC              ;if bit 7 not set return
       XOR     A               ;otherwise zero out A
       LD      (HL),A          ;put it back at SCODE1
       RET                     ;then go back
                               ;
NOTFND:
       CALL    SETCNT          ;set the counter for error code
       LD      A,$0FF          ;and set the return byte into A
       JP      GOBAK           ;then return to sender
                               ;
                               ;******************************************************************************
                               ;routine to delete specified file
                               ;
DELETF:
       CALL    ROCHK           ;see if the selected drive is read only
       LD      C,$0C           ;look for name and extent
       CALL    SEARCH          ;go find the file
DELOOP:
       CALL    CHKCNT          ;was it there
       RET     Z               ;return if not - all extents are marked
       CALL    CHKFRO          ;see if file was read only
       CALL    PNTDIR          ;point to directory entry
       LD      (HL),DELDAT     ;put in the file deleted marker
       LD      C,0             ;set C for the fix bit routine
       CALL    FIXALL          ;fix the allocation vector
       CALL    WRTDS           ;write out the directory sector
       CALL    SERCHN          ;search for the next extent
       JP      DELOOP          ;and loop until done
                               ;
                               ;******************************************************************************
                               ;routine to check for block availability
                               ;
BLKAVL:
       LD      D,B             ;move BC
       LD      E,C             ;  into DE
BLKA1:
       LD      A,C             ;save a copy of C into A
       OR      B               ;  check if BC is zero
       JP      Z,BLKA2         ;if so jump over and look again
       DEC     BC              ;decrement the counter
       PUSH    DE              ;save on the stack
       PUSH    BC              ;save BC as well
       CALL    GETBIT          ;check the allocation bit
       RRA                     ;slide it right through carry
       JP      NC,TAKBLK       ;if no carry it is free
       POP     BC              ;restore
       POP     DE              ;  stack
BLKA2:
       LD      HL,(DSIZE)      ;get the disk size block count
       LD      A,E             ;subtract
       SUB     L               ;   HL
       LD      A,D             ;    from
       SBC     A,H             ;        DE
       JP      NC,BLKA3        ;if no carry all is well - jump over
       INC     DE              ;otherwise bump DE
       PUSH    BC              ;save on stack
       PUSH    DE              ;    for next look
       LD      B,D             ;put DE
       LD      C,E             ;   into BC
       CALL    GETBIT          ;get this allocation bit out
       RRA                     ;slide right to test
       JP      NC,TAKBLK       ;if no carry take the block
       POP     DE              ;recover the
       POP     BC              ;  stack
       JP      BLKA1           ;and keep looking
                               ;
TAKBLK:
       RLA                     ;restore the allocation bit
       INC     A               ;add one to set bit
       CALL    PUTBAK          ;write it back into the vector
       POP     HL              ;recover
       POP     DE              ;  the stack
       RET                     ;and return to the caller
                               ;
BLKA3:
       LD      A,C             ;is BC
       OR      B               ;  zero?
       JP      NZ,BLKA1        ;if not jump back and continue
       LD      HL,0            ;otherwise get a 16 bit zero
       RET                     ;and return with it
                               ;
                               ;******************************************************************************
                               ;routine to move the directory entry
                               ;
MOVDIR:
       LD      C,0             ;set C to zero for the move
       LD      E,32            ;get the directory size
                               ;
                               ;******************************************************************************
                               ;routine to set the directory onto the disk
                               ;
SETDIR:
       PUSH    DE              ;save the count on the stack
       LD      B,0             ;zero the high byte
       LD      HL,(FCB)        ;get the FCB address
       ADD     HL,BC           ;add in the offset for the move
       EX      DE,HL           ;save it in DE
       CALL    PNTDIR          ;point to the directory area
       POP     BC              ;recover the count
       CALL    MOVE            ;Move C bytes DE to HL
SETD1:
       CALL    GETDS           ;go pull in the rest of the directory 
       JP      WRTDS           ;and write the current sector out
                               ;
                               ;******************************************************************************
                               ;routine to rename the selected file
                               ;
RENAMF:
       CALL    ROCHK           ;see if the selected disk is read only
       LD      C,$0C           ;no- check name and extent
       CALL    SEARCH          ;by calling the search routine
       LD      HL,(FCB)        ;get the information FCB address 
       LD      A,(HL)          ;and pull the drivecode
       LD      DE,16           ;we will move down 16 bytes
       ADD     HL,DE           ;point to the new file name in FCB
       LD      (HL),A          ;put in the new drive code
RENF1:
       CALL    CHKCNT          ;check the loop counter
       RET     Z               ;return if all extents are renamed
       CALL    CHKFRO          ;see if the filw is read only
       LD      C,$10           ;set to move 16 bytes of the file name
       LD      E,$0C           ;get the search code ready
       CALL    SETDIR          ;set in the file name
       CALL    SERCHN          ;search for the next extent
       JP      RENF1           ;loop until no more extents
                               ;
                               ;******************************************************************************
                               ;routine sets up pointers to indicated file
                               ;
SETFN:
       LD      C,$0C           ;look at name and extent
       CALL    SEARCH          ;search for the file
SETF1:
       CALL    CHKCNT          ;see if there was an entry left
       RET     Z               ;return when all extents have been found
       LD      C,0             ;
       LD      E,$0C           ;
       CALL    SETDIR          ;
       CALL    SERCHN          ;look for the next extent
       JP      SETF1           ;and loop till all found
                               ;
                               ;******************************************************************************
                               ;routine to open selected file
                               ;
OPENF:
       LD      C,$0F           ;look at FCB through S1 and S2 bytes
       CALL    SEARCH          ;search for the file
       CALL    CHKCNT          ;was it found
       RET     Z               ;return if not
OPEN1:
       CALL    GETEX           ;get the extent byte
       LD      A,(HL)          ;pull it into A
       PUSH    AF              ;save it for later
       PUSH    HL              ;save the address as well
       CALL    PNTDIR          ;point to the directory area
       EX      DE,HL           ;and put the address into DE
       LD      HL,(FCB)        ;get the FCB address
       LD      C,32            ;Bytes to move
       PUSH    DE              ;save the directory address
       CALL    MOVE            ;Move C bytes DE to HL
       CALL    SETS28          ;set bit 7 of S2
       POP     DE              ;recover the directory address
       LD      HL,12           ;set to point downstream
       ADD     HL,DE           ;now pointing to extent byte of directory
       LD      C,(HL)          ;pull this byte in
       LD      HL,15           ;ready to move down again
       ADD     HL,DE           ;now point to 
       LD      B,(HL)          ;put the extent byte back here
       POP     HL              ;recover the FCB address
       POP     AF              ;and the entry extent byte
       LD      (HL),A          ;put the byte back
       LD      A,C             ;move the directory extent byte into A
       CP      (HL)            ;are they the same
       LD      A,B             ;
       JP      Z,OPEN2         ;if so jump
       LD      A,0             ;otherwise get a zero
       JP      C,OPEN2         ;if it was too big jump over
       LD      A,$80           ;now get bit 7 set
OPEN2:
       LD      HL,(FCB)        ;point to the FCB again
       LD      DE,15           ;ready to move it
       ADD     HL,DE           ;now point to record count
       LD      (HL),A          ;pull this out
       RET                     ;and go back with it
                               ;
                               ;******************************************************************************
                               ;routine to check if (HL) is zero - if so move (DE) to (HL)
                               ; 
CHKCPY:
       LD      A,(HL)          ;test (HL)
       INC     HL              ;  for
       OR      (HL)            ;    zero
       DEC     HL              ;point back
       RET     NZ              ;return if not zero
       LD      A,(DE)          ;otherwise get (DE)
       LD      (HL),A          ;put it at (HL)
       INC     DE              ;bump
       INC     HL              ;bothof them
       LD      A,(DE)          ;get (DE)
       LD      (HL),A          ;and put it at (DE)
       DEC     DE              ;restore both
       DEC     HL              ;  pointers
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine to close selected file
                               ;
CLOSEF:
       XOR     A               ;cheap zero
       LD      (ODATA),A       ;put it at return info byte
       LD      (COUNT),A       ;and zero out the count
       LD      (COUNT+1),A     ;   word
       CALL    ISRO            ;is the disk read only
       RET     NZ              ;return if so - can't close it 
       CALL    GETS2           ;get the S2 byte from the FCB
       AND     $80             ;strip off the low eight bits 
       RET     NZ              ;return if not zero
       LD      C,$0F           ;otherwise set to look through S2 in FCB
       CALL    SEARCH          ;search for the file
       CALL    CHKCNT          ;was it there
       RET     Z               ;zero says done
       LD      BC,16           ;get the pointer for group bytes in dir
       CALL    PNTDIR          ;point to the directory
       ADD     HL,BC           ;add in the offset - pointing right
       EX      DE,HL           ;swap DE and HL
       LD      HL,(FCB)        ;point to the FCB
       ADD     HL,BC           ;add in the offset again
       LD      C,$10           ;get set for 16 bytes to move
CLOSF1:
       LD      A,(BSIZE)       ;get the block size byte out
       OR      A               ;is it zero
       JP      Z,CLOSF4        ;if so blocks > 1024 so jump past
                               ;
                               ;small blocks
                               ;
       LD      A,(HL)          ;pull the byte out
       OR      A               ;test for zero
       LD      A,(DE)          ;get the (DE) out
       JP      NZ,CLOSF2       ;if not zero jump over
       LD      (HL),A          ;otherwise put A into place
CLOSF2:
       OR      A               ;test the byte again
       JP      NZ,CLOSF3       ;if not zero jump past
       LD      A,(HL)          ;otherwise pull in the byte
       LD      (DE),A          ;stuff it back at (DE)
CLOSF3:
       CP      (HL)            ;are they the same
       JP      NZ,CLOSF7       ;if not jump
       JP      CLOSF5          ;otherwise skip over 
                               ;
                               ;big blocks
                               ;
CLOSF4:
       CALL    CHKCPY          ;check and increment
       EX      DE,HL           ;exchange again
       CALL    CHKCPY          ;check again for next byte
       EX      DE,HL           ;exchange back - ready to continue
       LD      A,(DE)          ;get the new byte from (DE)
       CP      (HL)            ;does it match (HL)
       JP      NZ,CLOSF7       ;if not over we go
       INC     DE              ;bump pointer
       INC     HL              ;   data
       LD      A,(DE)          ;get next byte
       CP      (HL)            ;compare it as well
       JP      NZ,CLOSF7       ;again if no match jump
       DEC     C               ;decrement the count
CLOSF5:
       INC     DE              ;increment the
       INC     HL              ;  pointers
       DEC     C               ;decrement the count
       JP      NZ,CLOSF1       ;if not done spinn around again
       LD      BC,65536-20          ;point back 
       ADD     HL,BC           ;  20 bytes
       EX      DE,HL           ;swap pointers
       ADD     HL,BC           ;and back this one up as well
       LD      A,(DE)          ;pull in the byte at (DE)
       CP      (HL)            ;test against (HL)
       JP      C,CLOSF6        ;if (HL)>(DE) jump
       LD      (HL),A          ;if not put A into (HL)
       LD      BC,3            ;get a 3
       ADD     HL,BC           ;point down 3
       EX      DE,HL           ;swap pointers again
       ADD     HL,BC           ;down three also 
       LD      A,(HL)          ;get the byte at (HL)
       LD      (DE),A          ;stuff it in at (DE)
CLOSF6:
       LD      A,$0FF          ;get the flag data
       LD      (RWFLG1),A      ;set flag to say reading
       JP      SETD1           ;write out the directory block and return
                               ;
CLOSF7:
       LD      HL,ODATA        ;point to output data byte
       DEC     (HL)            ;decrement it 1
       RET                     ;and go back
                               ;
                               ;******************************************************************************
                               ;make a new directory entry for a new file
                               ;
MAKE:
       CALL    ROCHK           ; is the disk read only
       LD      HL,(FCB)        ;nope - so get the FCB address
       PUSH    HL              ;and save it on the stack
       LD      HL,TINFO        ;point to the TINFO area
       LD      (FCB),HL        ;save this as the FCB address
       LD      C,$01           ;set to look at only the ET byte in the FCB
       CALL    SEARCH          ;search for the new file name 
       CALL    CHKCNT          ;was it there
       POP     HL              ;get the old FCB back
       LD      (FCB),HL        ;restore it right
       RET     Z               ;return if it was there
       EX      DE,HL           ;save FCB in DE
       LD      HL,15           ;get offset to record count
       ADD     HL,DE           ;and set the pointer there
       LD      C,17            ;get set to spin through 16 bytes
       XOR     A               ;get a zero ready
MAK1:
       LD      (HL),A          ;and move zero
       INC     HL              ;  into
       DEC     C               ;    all
       JP      NZ,MAK1         ;     the group bytes
       LD      HL,13           ;get the offset to the S1 byte
       ADD     HL,DE           ;and point to it
       LD      (HL),A          ;zero this as well
       CALL    CHKINC          ;see if we have more to do
       CALL    MOVDIR          ;copy the directory in place
       JP      SETS28          ;set bit 7 of S2 and return
                               ;
                               ;******************************************************************************
                               ;routine sets up for the next extent access for either read or write
                               ;
NEXTEX:
       XOR     A               ;get a zero
       LD      (RWFLG1),A      ;say we are writing
       CALL    CLOSEF          ;close the file
       CALL    CHKCNT          ;did it exist
       RET     Z               ;return if not
       LD      HL,(FCB)        ;get the FCB address
       LD      BC,12           ;set to point to extent byte
       ADD     HL,BC           ;now it does
       LD      A,(HL)          ;pull the thing in
       INC     A               ;bump by one
       AND     $1F             ;strip off bit 7
       LD      (HL),A          ;and put it back
       JP      Z,NEXTE1        ;if it was zero jump over
       LD      B,A             ;otherwise save it in B
       LD      A,(EXTMSK)      ;get the extent mask
       AND     B               ;and use it
       LD      HL,RWFLG1       ;and point to the flag
       AND     (HL)            ;and in this byte as well
       JP      Z,NEXTE2        ;if zero jump past
       JP      NEXTE3          ;otherwise off to
                               ;
NEXTE1:
       LD      BC,2            ;ready to bump 2 more
       ADD     HL,BC           ;add it in
       INC     (HL)            ;increment this byte
       LD      A,(HL)          ;then pull it in
       AND     $0F             ;strip off the high 4 bits
       JP      Z,NEXTE5        ;if zero jump
NEXTE2:
       LD      C,$0F           ;look through S2 byte in FCB
       CALL    SEARCH          ;look for the indicated file
       CALL    CHKCNT          ;see if it was there
       JP      NZ,NEXTE3       ;if so open the next extent
       LD      A,(RWFLG2)      ;get the read/write flag
       INC     A               ;add one
       JP      Z,NEXTE5        ;if it was FF then return
       CALL    MAKE            ;make a new file directory entry
       CALL    CHKCNT          ;was it ok
       JP      Z,NEXTE5        ;if not set up exit routine
       JP      NEXTE4          ;otherwise set up extent pointers and return
                               ;
NEXTE3:
       CALL    OPEN1           ;go open the next extent
NEXTE4:
       CALL    FIXEXT          ;set the pointers up
       XOR     A               ;zero A for the return
       JP      GOBAK           ;and go back
                               ;
NEXTE5:
       CALL    JR1             ;set to return with problems
       JP      SETS28          ;set bit 7 of S2 to say so
                               ;
                               ;******************************************************************************
                               ;routine to read the disk
                               ;
DSKRED:
       LD      A,$01           ;get a !
       LD      (DCODE),A       ;save it for the disk code byte
RRERR:
       LD      A,$0FF          ;get a read flag
       LD      (RWFLG2),A      ;set it in for the control
       CALL    FIXEXT          ;go fix the extent
       LD      A,(ESCNT2)      ;get the extent low byte
       LD      HL,RECCNT       ;point to the record count
       CP      (HL)            ;are they the same?
       JP      C,DSKR1         ;if record count was bigger, jump over
       CP      $80             ;was the count 80 hex?
       JP      NZ,DERR         ;if not jump out with an error
       CALL    NEXTEX          ;go get the next extent
       XOR     A               ;set a=0
       LD      (ESCNT2),A      ;save this as the new extent low byte
       LD      A,(ODATA)       ;get the output data
       OR      A               ;set the flags
       JP      NZ,DERR         ;if it was not zero exit with error
DSKR1:
       CALL    SECGRP          ;otherwise group from the sector count
       CALL    TSTGRP          ;see if it is ok
       JP      Z,DERR          ;if not go back
       CALL    COMSEC          ;if co compute the right sector
       CALL    GETD1           ;get the directory set right
       CALL    RDSEC           ;read the sector
       JP      FIXREC          ;fix up the record and return
                               ;
DERR:
       JP      JR1             ;set the error exit and return
                               ;
                               ;******************************************************************************
                               ;routine to write the disk
                               ;
DSKWRT:
       LD      A,$01           ;set the disk
       LD      (DCODE),A       ;   code up
RWERR:
       LD      A,0             ;set the read write flag for write
       LD      (RWFLG2),A      ;and save it for later
       CALL    ROCHK           ;see if the drive is protected
       LD      HL,(FCB)        ;get the FCB address
       CALL    CHKFR1          ;see if the file is read only
       CALL    FIXEXT          ;fix up the extent
       LD      A,(ESCNT2)      ;and get the low extent byte
       CP      $80             ;is it greater that 80h
       JP      NC,JR1          ;if so exit with errors
       CALL    SECGRP          ;compute the right group
       CALL    TSTGRP          ;see if it is ok
       LD      C,0             ;get a zero
       JP      NZ,DSKW05       ;if ok jump over
       CALL    COMBLK          ;compute the block
       LD      (EXTBLK),A      ;save it at extent block area 
       LD      BC,0            ;get a 16 bit 0
       OR      A               ;set flags
       JP      Z,DSKW01        ;if zero jump over
       LD      C,A             ;
       DEC     BC              ;
       CALL    GETGRP          ;get the group counter out
       LD      B,H             ;save it
       LD      C,L             ;  in BC
DSKW01:
       CALL    BLKAVL          ;see if a block is available
       LD      A,L             ;check for 
       OR      H               ;  zero returned
       JP      NZ,DSKW02       ;if not zero jump over
       LD      A,$02           ;set the disk full message
       JP      GOBAK           ;and return
                               ;
DSKW02:
       LD      (GROUP),HL      ;save the group counter
       EX      DE,HL           ;put it inti DE as well
       LD      HL,(FCB)        ;get the FCB address
       LD      BC,16           ;set the offset to the record count
       ADD     HL,BC           ;and point to it
       LD      A,(BSIZE)       ;get the block size
       OR      A               ;is it zero
       LD      A,(EXTBLK)      ;get the extent block out
       JP      Z,DSKW03        ;if blocks are greater than 1024 jump
       CALL    PNTD1           ;point to the directory entry
       LD      (HL),E          ;save in the group low byte
       JP      DSKW04          ;and jump over
                               ;
DSKW03:
       LD      C,A             ;save the extblk byte into C
       LD      B,0             ;zero out B
       ADD     HL,BC           ;slide the offset 
       ADD     HL,BC           ;   right 2 bits
       LD      (HL),E          ;put the group low byte into place
       INC     HL              ;point to the high byte
       LD      (HL),D          ;and put this in too
DSKW04:
       LD      C,$02           ;set the exit code
DSKW05:
       LD      A,(ODATA)       ;and the exit data
       OR      A               ;see if it is zero
       RET     NZ              ;return if not
       PUSH    BC              ;otherwise save the extent block pointer
       CALL    COMSEC          ;go compute the sector
       LD      A,(DCODE)       ;get the disk code out
       DEC     A               ;count it
       DEC     A               ;  down 2
       JP      NZ,DSKW08       ;if not zero jump over
       POP     BC              ;recover the block offset
       PUSH    BC              ;save it again
       LD      A,C             ;put low into A
       DEC     A               ;decrement it by 1
       DEC     A               ;   and 1 more
       JP      NZ,DSKW08       ;if not zero then jump
       PUSH    HL              ;save the address of the directory group
       LD      HL,(DTEMP)      ;get the temp address
       LD      D,A             ;save the group low byte into D
DSKW06:
       LD      (HL),A          ;and put it into memory
       INC     HL              ;point to the high
       INC     D               ;add one to D
       JP      P,DSKW06        ;if positive jump past
       CALL    SETEMP          ;set the temp buffer
       LD      HL,(DIRPNT)     ;get the directory pointer
       LD      C,$02           ;put a 2 into C
DSKW07:
       LD      (GROUP),HL      ;save the group pointer
       PUSH    BC              ;push the counter onto the stack
       CALL    GETD1           ;get the next directory
       POP     BC              ;recover the group pointer
       CALL    WRSEC           ;write the sector
       LD      HL,(GROUP)      ;get the group data
       LD      C,0             ;
       LD      A,(BLKMSK)      ;get the block mask
       LD      B,A             ;put it into B
       AND     L               ;and in L
       CP      B               ;compare with B
       INC     HL              ;and bump to next
       JP      NZ,DSKW07       ;if not the same jump over
       POP     HL              ;recover HL
       LD      (GROUP),HL      ;and save it at GROUP
       CALL    SETDMA          ;set the dma address
DSKW08:
       CALL    GETD1           ;and get the next directory
       POP     BC              ;recover BC
       PUSH    BC              ;and save it again
       CALL    WRSEC           ;write the sector
       POP     BC              ;restore BC again
       LD      A,(ESCNT2)      ;get the extent count low byte 
       LD      HL,RECCNT       ;point to the record count
       CP      (HL)            ;are they the same
       JP      C,DSKW09        ;if RECCNT>ESCNT2 jump
       LD      (HL),A          ;otherwise save a new RECCNT
       INC     (HL)            ;bump pointer by one
       LD      C,$02           ;get a 2 to force fall through below
DSKW09:
       DEC     C               ;decrement
       DEC     C               ;  by 2
       JP      NZ,DSKW10       ;if not zero jump over
       PUSH    AF              ;save the flags
       CALL    GETS2           ;point to S2 byte
       AND     $7F             ;strip off bit 7
       LD      (HL),A          ;and put it back this way
       POP     AF              ;recover the flags
DSKW10:
       CP      $7F             ;test the byte in A
       JP      NZ,DSKW12       ;if not 7F jump over
       LD      A,(DCODE)       ;get the disk code out
       CP      $01             ;is it 1
       JP      NZ,DSKW12       ;if not jump
       CALL    FIXREC          ;if so fix the record bytes
       CALL    NEXTEX          ;and get the next extent ready
       LD      HL,ODATA        ;point to the output data byte
       LD      A,(HL)          ;pull it in
       OR      A               ;check for zero
       JP      NZ,DSKW11       ;if not jump
       DEC     A               ;decrement it by one if it was zero
       LD      (ESCNT2),A      ;and save this at ESCNT2
DSKW11:
       LD      (HL),0          ;zero out ODATA
DSKW12:
       JP      FIXREC          ;fix the records up and return
                               ;
                               ;******************************************************************************
                               ;random file access routine - C has the code for access type
                               ;     FF for read, 0 for write
                               ; 
RANFIL:
       XOR     A               ;zero the DCODE 
       LD      (DCODE),A       ;   byte
RANF1:
       PUSH    BC              ;save the entry code in C
       LD      HL,(FCB)        ;point to the FCB address
       EX      DE,HL           ;and save it in DE for later
       LD      HL,33           ;get the offset to R0
       ADD     HL,DE           ;pointing to R0 byte
       LD      A,(HL)          ;pull it in
       AND     $7F             ;and strip off the sector count for this ext
       PUSH    AF              ;save it on the stack
       LD      A,(HL)          ;get R0 again
       RLA                     ;slide it left
       INC     HL              ;point to R1
       LD      A,(HL)          ;pull this in
       RLA                     ;rotate this left also pulling in bit 7 of R0 
       AND     $1F             ;strip off the low 5 bits for extent count
       LD      C,A             ;save this in C
       LD      A,(HL)          ;get R1 again
       RRA                     ;rotate
       RRA                     ;   it
       RRA                     ;     right
       RRA                     ;       4 bits
       AND     $0F             ;and mask off the block counter
       LD      B,A             ;put it into B
       POP     AF              ;A has record, B has block, C has extent 
                               ;   (0-127)      (0-15)       (0-31)
       INC     HL              ;point to the overflow byte
       LD      L,(HL)          ;pull it into L
       INC     L               ;add one
       DEC     L               ;then sub 1 to set flag 
       LD      L,$06           ;get seek past end of disk error
       JP      NZ,RANF5        ;if not zero exit with error
       LD      HL,32           ;offset to current record
       ADD     HL,DE           ;point to it
       LD      (HL),A          ;put the current record byte into place
       LD      HL,12           ;now get the 
       ADD     HL,DE           ;   extent pointer
       LD      A,C             ;pull the new one into A
       SUB     (HL)            ;compare with the one we are looking at
       JP      NZ,RANF2        ;if not right, jump over
       LD      HL,14           ;otherwise check the
       ADD     HL,DE           ;   S2 byte
       LD      A,B             ;put the new block into A
       SUB     (HL)            ;and compare with what is there 
       AND     $7F             ;strip off bit 7 for the check
       JP      Z,RANF3         ;if all is well jump back
RANF2:
       PUSH    BC              ;otherwise save the counters
       PUSH    DE              ;and the FCB address
       CALL    CLOSEF          ;and close the current extent
       POP     DE              ;recover the FCB address
       POP     BC              ;and the counters
       LD      L,$03           ;set the close error message
       LD      A,(ODATA)       ;check the result
       INC     A               ;  should be >0 if ok
       JP      Z,RANF4         ;if not we had problems so go back
       LD      HL,12           ;point to
       ADD     HL,DE           ;  the extent byte
       LD      (HL),C          ;put in the new value
       LD      HL,14           ;now point to 
       ADD     HL,DE           ;  the S2 byte
       LD      (HL),B          ;put the new block in place
       CALL    OPENF           ;go open the new extent
       LD      A,(ODATA)       ;get the result
       INC     A               ;and check if ok
       JP      NZ,RANF3        ;if non zero the open was ok so go back
       POP     BC              ;recover the entry code
       PUSH    BC              ;then resave it
       LD      L,$04           ;set the seek to unwritten data error
       INC     C               ;bump the entry code 1
       JP      Z,RANF4         ;if it is zero we exit with the error
       CALL    MAKE            ;otherwise we are writing so open new extent
       LD      L,$05           ;get the directory overflow error
       LD      A,(ODATA)       ;get the result
       INC     A               ;add one to check
       JP      Z,RANF4         ;if errors exit 
RANF3:
       POP     BC              ;otherwise restore the entry code
       XOR     A               ;set zero into A
       JP      GOBAK           ;and return with FCB set right
                               ;
RANF4:
       PUSH    HL              ;save the current error byte in L
       CALL    GETS2           ;point to the S2 byte
       LD      (HL),$0C0       ;set in a C0 hex
       POP     HL              ;recover the error byte
RANF5:
       POP     BC              ;recover the entry code
       LD      A,L             ;set the error byte into A
       LD      (ODATA),A       ;save it for the exit
       JP      SETS28          ;set bit 7 of S2 for later
                               ;
                               ;******************************************************************************
                               ;routine to read random record
                               ;
RDREC:
       LD      C,$0FF          ;set the code for reading
       CALL    RANFIL          ;call the random routine to fix FCB up
       CALL    Z,RRERR         ;If ok then read the record	
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine to write random record
                               ;
WRREC:
       LD      C,0             ;set the code for writing
       CALL    RANFIL          ;go fix the FCB up
       CALL    Z,RWERR         ;if ok then write the sector
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine computes record count for random access
                               ;
COMREC:
       EX      DE,HL           ;put the FCB into DE - offset into HL
       ADD     HL,DE           ;set the pointer to the record byte
       LD      C,(HL)          ;pull the record count into C
       LD      B,0             ;zero out B
       LD      HL,12           ;now set offset
       ADD     HL,DE           ;  to point to the extent byte
       LD      A,(HL)          ;pull it into A
       RRCA                    ;slide right through carry - carry has bit0
       AND     $80             ;strip all but the bit in 7
       ADD     A,C             ;add in the current record count
       LD      C,A             ;and save this in C
       LD      A,0             ;zero A leaving carry set
       ADC     A,B             ;add in the carry and zero in B
       LD      B,A             ;save this in B 
       LD      A,(HL)          ;get the extent byte again
       RRCA                    ;slide right through carry
       AND     $0F             ;mask all but low four
       ADD     A,B             ;add in the data in B
       LD      B,A             ;and save back into B 
       LD      HL,14           ;set the pointer
       ADD     HL,DE           ;  to the S2 byte
       LD      A,(HL)          ;pull it into A
       ADD     A,A             ;and slide
       ADD     A,A             ;   it
       ADD     A,A             ;    left
       ADD     A,A             ;     four bits
       PUSH    AF              ;save the result on the stack
       ADD     A,B             ;add in B, if s2>=16 and B=17 carry is set
       LD      B,A             ;save the result into B
       PUSH    AF              ;save the flags and A
       POP     HL              ;pull flags into L
       LD      A,L             ;put then into A
       POP     HL              ;recover the second flag set into L
       OR      L               ;or the two together
       AND     $01             ;and strip off all but carry
       RET                     ;then return
                               ;
                               ;******************************************************************************
                               ;routine gets the record count from the last extent of a random file
                               ;
GETSIZ:
       LD      C,$0C           ;look at file name ant extent
       CALL    SEARCH          ;go locate the file
       LD      HL,(FCB)        ;point to the FCB
       LD      DE,33           ;get the offset to the
       ADD     HL,DE           ;   R0 byte
       PUSH    HL              ;save the address on the stack
       LD      (HL),D          ;put the 0 into position
       INC     HL              ;point to R1
       LD      (HL),D          ;0 goes there as well
       INC     HL              ;point to R2 - overflow byte
       LD      (HL),D          ;once more with zero
GSLOOP:
       CALL    CHKCNT          ;check the counter
       JP      Z,GSEXIT        ;if zero we exit now
       CALL    PNTDIR          ;otherwise point to the directory
       LD      DE,15           ;get the offset to extent byte 
       CALL    COMREC          ;check extent overflow
       POP     HL              ;recover the R0 address
       PUSH    HL              ;save it again
       LD      E,A             ;save the directory code in E
       LD      A,C             ;and put the R0 byte into A 
       SUB     (HL)            ;subtract the byte from the FCB
       INC     HL              ;point to the R1 byte 
       LD      A,B             ;put B in A
       SBC     A,(HL)          ;subtract with borrow from here as well
       INC     HL              ;once more with the overflow byte
       LD      A,E             ;E into A 
       SBC     A,(HL)          ;subtract the overflow
       JP      C,GETSZ1        ;if carry jump over - file is too big
       LD      (HL),E          ;put R2 into place
       DEC     HL              ;point back
       LD      (HL),B          ;set R1 in as well
       DEC     HL              ;back again
       LD      (HL),C          ;and set R0 into place
GETSZ1:
       CALL    SERCHN          ;go look for next extent
       JP      GSLOOP          ;loop until we hit the end
                               ;
GSEXIT:
       POP     HL              ;recover the address of random file size
       RET                     ;and return with it
                               ;
                               ;******************************************************************************
                               ; BDOS function 36 - set random record with data set by RANFIL
                               ;
SETRND:
       LD      HL,(FCB)        ;get the FCB address
       LD      DE,32           ;and the offset to the record count
       CALL    COMREC          ;get the file pointers into A b and c
       LD      HL,33           ;offset to the R0 byte
       ADD     HL,DE           ;point to it
       LD      (HL),C          ;put the byte in C into R0
       INC     HL              ;point to R1
       LD      (HL),B          ;put B in there
       INC     HL              ;point to overflow byte
       LD      (HL),A          ;stuff A there
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;routine to log the selected disk
                               ;
LOGDSK:
       LD      HL,(DLOG)       ;point to the login vector
       LD      A,(CURDSK)      ;get the current disk byte
       LD      C,A             ;and put it into C
       CALL    SPINHL          ;spin the selected disk bit into place
       PUSH    HL              ;save this vector on the stack
       EX      DE,HL           ;careful study says this is useless
       CALL    DISKID          ;go get the disk information set
       POP     HL              ;recover the vector
       CALL    Z,PTSERR        ;if trouble say so and quit
       LD      A,L             ;otherwise put the low byte into A 
       RRA                     ;slide it right bit 0 into carry
       RET     C               ;return if bit was set - we already got it
       LD      HL,(DLOG)       ;otherwise point to the login vector
       LD      C,L             ;put the vector
       LD      B,H             ;   into BC
       CALL    SETDBT          ;set the disk bit using setro
       LD      (DLOG),HL       ;save the vector into place again
       JP      GETAL           ;get the new allocation vector
                               ;
                               ;******************************************************************************
                               ; BDOS function 14: select disk
                               ;	enter with selected disk in IDATA
                               ;
LOGIN:
       LD      A,(IDATA)       ;get the disk to log
       LD      HL,CURDSK       ;point to the current disk storage
       CP      (HL)            ;are they the same
       RET     Z               ;return if so
       LD      (HL),A          ;otherwise make this current
       JP      LOGDSK          ;and log it in then return
                               ;
                               ;******************************************************************************
                               ;routine to select disk indicated in FCB drive code
                               ;
RSELCT:
       LD      A,$0FF          ;get an FF
       LD      (OUT1),A        ;save it to force disk reset on exit
       LD      HL,(FCB)        ;point to the FCB
       LD      A,(HL)          ;pull in the new drive code
       AND     $1F             ;strip off the user bits
       DEC     A               ;decrement to make it right
       LD      (IDATA),A       ;save it for the login routines
       CP      $1E             ;is it too big
       JP      NC,XIT          ;if so go back
       LD      A,(CURDSK)      ;get the current disk out
       LD      (OUTDSK),A      ;save it for the return
       LD      A,(HL)          ;pull in the new code again
       LD      (OLDDSK),A      ;save the value as the olddisk
       AND     $0E0            ;strip off the drive bits
       LD      (HL),A          ;put the user bits back
       CALL    LOGIN           ;go log the new disk
XIT:
       LD      A,(USRCOD)      ;get the user code out
       LD      HL,(FCB)        ;point back to the FCB
       OR      (HL)            ;set in the new user code bits
       LD      (HL),A          ;and put the result back
       RET                     ;then return
                               ;
                               ;******************************************************************************
                               ;BDOS function 12: routine returns CP/M version in A
                               ;
GETVER:
       LD      A,$22           ;say this is CP/M version 2.2
       JP      GOBAK           ;and return
                               ;
                               ;******************************************************************************
                               ; BDOS function 13: routine to reset the system disk
                               ;
RESET:
       LD      HL,0            ;get ready to reset the vectors
       LD      (ROVEC),HL      ;set both read only bits
       LD      (DLOG),HL       ;   and login bits to off
       XOR     A               ;zero A
       LD      (CURDSK),A      ;  and make it the current disk
       LD      HL,TBUFF        ;Set DMA addr to 80H
       LD      (DMAADR),HL     ;save this as the new DMA value
       CALL    SETDMA          ;make it so for real
       JP      LOGDSK          ;go log in drive A and return
                               ;
                               ;******************************************************************************
                               ; BDOS function 15: open file
                               ;	enter with FCB address in HL and INFO 
                               ;	exit with directory code in A
                               ; 
OPEN:
       CALL    ZS2             ;set S2 to zero for the new file
       CALL    RSELCT          ;select the right disk
       JP      OPENF           ;then open the file in the FCB
                               ;
                               ;******************************************************************************
                               ; BDOS function 16: close the selected file
                               ;	enter with FCB address
                               ;	exit with directory code in A
                               ;
CLOSE:
       CALL    RSELCT          ;select the right drive
       JP      CLOSEF          ;and finish off the close operation
                               ;
                               ;******************************************************************************
                               ; BDOS function 17: search for first file entry
                               ;	enter with FCB address
                               ;	exit with directory code in A	
                               ;
SEAR1:
       LD      C,0             ;set the search code into C
       EX      DE,HL           ;swap FCB into DE
       LD      A,(HL)          ;pull in the file name byte
       CP      '?'             ;is it a wild card
       JP      Z,SRF2          ;if so jump over
       CALL    GETEX           ;otherwise get the extent
       LD      A,(HL)          ;pull in the byte here
       CP      '?'             ;is it a wild card
       CALL    NZ,ZS2          ;if not set S2 to 0
       CALL    RSELCT          ;select the right drive
       LD      C,$0F           ;look at name thru S2 byte
SRF2:
       CALL    SEARCH          ;search for the file
       JP      MOVSEC          ;jump to move the sector and return
                               ;
                               ;******************************************************************************
                               ;BDOS function 18: search for next directory entry
                               ;	enter with FCB set as before
                               ;	exit with directory code in A 255 says not found
                               ;
SEARN:
       LD      HL,(SEARA)      ;get the last search FCB out
       LD      (FCB),HL        ;save this as the FCB
       CALL    RSELCT          ;select the right drive
       CALL    SERCHN          ;then search for the nex extent
       JP      MOVSEC          ;move the sector and return
                               ;
                               ;******************************************************************************
                               ; BDOS function 19: delete file
                               ;	enter with FCB address set
                               ;	exit with directory code in A
                               ;
DELETE:
       CALL    RSELCT          ;select the right drive
       CALL    DELETF          ;delete the indicated file
       JP      RETCOD          ;set the return code and go back
                               ;
                               ;******************************************************************************
                               ; BDOS function 20: read sequential
                               ;	enter with FCB address
                               ;	exit with directory code in A
                               ;
READ:
       CALL    RSELCT          ;select the indicated drive
       JP      DSKRED          ;and go read the disk
                               ;
                               ;******************************************************************************
                               ; BDOS function 21: write sequential
                               ;	enter with FCB address
                               ;	exit with directory code in A
                               ;
WRITE:
       CALL    RSELCT          ;select the drive
       JP      DSKWRT          ;and go write the disk
                               ;
                               ;******************************************************************************
                               ; BDOS function 22: make new file entry
                               ;	enter with FCB
                               ;	exit with directory code in A
                               ;
CREATE:
       CALL    ZS2             ;zero out S2
       CALL    RSELCT          ;select the drive
       JP      MAKE            ;and make a new directory entry
                               ;
                               ;******************************************************************************
                               ; BDOS function 23: rename file
                               ;	enter with FCB address
                               ;	exit with directory code in A
                               ;
RENAME:
       CALL    RSELCT          ;select the drive
       CALL    RENAMF          ;and go rename the file
       JP      RETCOD          ;set the return code and exit
                               ;
                               ;******************************************************************************
                               ; BDOS function 24: return login vector
                               ;	exit with login vector in HL
                               ;
GLOGIN:
       LD      HL,(DLOG)       ;get the login vector out
       JP      RETHL           ;return with it
                               ;
                               ;******************************************************************************
                               ; BDOS function 25: return current disk
                               ;	exit with current disk in A
                               ;
GETDRV:
       LD      A,(CURDSK)      ;get the current disk out
       JP      GOBAK           ;and go back with it
                               ;
                               ;******************************************************************************
                               ; BDOS function 26: set DMA address
                               ;	enter with DMA address set
                               ;
DMASET:
       EX      DE,HL           ;save the new address into HL
       LD      (DMAADR),HL     ;save it into place
       JP      SETDMA          ;then make it true
                               ;
                               ;******************************************************************************
                               ; BDOS function 27: get allocation vector address
                               ;	return address in HL
                               ;
GALLOC:
       LD      HL,(ALLOCA)     ;get the allocation vector out
       JP      RETHL           ;and go back with it
                               ;
                               ;******************************************************************************
                               ; BDOS function 29: get read/only vector
                               ;	return vector in HL
                               ;
GROVEC:
       LD      HL,(ROVEC)      ;get the read only vector out
       JP      RETHL           ;and go back with it
                               ;
                               ;******************************************************************************
                               ; BDOS function 30: set file attributes
                               ;	enter with FCB address with new attributes set in F1..F7,T1..T3
                               ;	exit with directory code in A
                               ;
SETATT:
       CALL    RSELCT          ;select the indicate drive
       CALL    SETFN           ;set in the new file name with bits set
       JP      RETCOD          ;set the return code and exit
                               ;
                               ;******************************************************************************
                               ; BDOS function 31: get address of disk parameter table
                               ;	return address in HL
                               ;
GETPAR:
       LD      HL,(DPB)        ;get the disk parameter table address
RETHL:
       LD      (ODATA),HL      ;save it at return data word
       RET                     ;and go back with it
                               ;
                               ;******************************************************************************
                               ; BDOS function 32: get or set user code
                               ;	enter with LINFO set to FF for get and to value for set
                               ;
MODUSR:
       LD      A,(IDATA)       ;get the input argument
       CP      $0FF            ;do we do a set
       JP      NZ,SETCODE      ;or a get
       LD      A,(USRCOD)      ;must be get so get
       JP      GOBAK           ;and go back with it
                               ;
SETCODE:
       AND     $1F             ;otherwise strip off the high bits
       LD      (USRCOD),A      ;and save the new value
       RET                     ;then return
                               ;
                               ;******************************************************************************
                               ; BDOS function 33: read random record
                               ;	enter with FCB address
                               ;	exit with return code in A
                               ;
REDRND:
       CALL    RSELCT          ;select the right drive
       JP      RDREC           ;and go read the sector
                               ;
                               ;******************************************************************************
                               ; BDOS function 34: write random record
                               ;	enter with FCB address
                               ;	exit with return code in A
                               ;
WRTRND:
       CALL    RSELCT          ;go select the drive
       JP      WRREC           ;and write the sector
                               ;
                               ;******************************************************************************
                               ; BDOS function 35: compute file size
                               ;	enter with FCB address
                               ;	exit with random record set in FCB
                               ;
FILSIZ:
       CALL    RSELCT          ;select the right drive
       JP      GETSIZ          ;and compute the file size
                               ;
                               ;******************************************************************************
                               ; BDOS function 37: Routine to reset drive
                               ;	enter with drive vector for reset
                               ;	return zero in A
                               ;
RESDRV:
       LD      HL,(FCB)        ;has the drive vector for reset
       LD      A,L             ;move low into A
       CPL                     ;and flip it
       LD      E,A             ;put the result back into E
       LD      A,H             ;get the high vector byte
       CPL                     ;  flip itr alse
       LD      HL,(DLOG)       ;get the login vector
       AND     H               ;and in the new bits in high
       LD      D,A             ;save the result into D
       LD      A,L             ;now on the the low bits
       AND     E               ;and in the new bits
       LD      E,A             ;and save the result into E
       LD      HL,(ROVEC)      ;point to the ro vector
       EX      DE,HL           ;swap DE and HL
       LD      (DLOG),HL       ;put the new login vector into place
       LD      A,L             ;get the read only low bits
       AND     E               ;and in the new bits
       LD      L,A             ;put then back into L
       LD      A,H             ;now on the the high bits
       AND     D               ;set the new ones in
       LD      H,A             ;and put the result back
       LD      (ROVEC),HL      ;then save the new value into place
       RET                     ;and return when done
                               ;
                               ;******************************************************************************
                               ; bdos exit routine
                               ;
BEXIT:
       LD      A,(OUT1)        ;get the out flag for drive reset
       OR      A               ;set the flags
       JP      Z,REXIT         ;if zero we may exit as is
       LD      HL,(FCB)        ;otherwise get the FCB address
       LD      (HL),0          ;put a zero into drive select byte
       LD      A,(OLDDSK)      ;get the old disk out
       OR      A               ;was it zero
       JP      Z,REXIT         ;if so exit
       LD      (HL),A          ;otherwise set in the old disk byte
       LD      A,(OUTDSK)      ;get the disk to relog
       LD      (IDATA),A       ;save it for the login
       CALL    LOGIN           ;go log in the old disk
REXIT:
       LD      HL,(OLDSP)      ;get the old stack pointer out
       LD      SP,HL           ;put it back in place
       LD      HL,(ODATA)      ;get the output data into place
       LD      A,L             ;put L into A
       LD      B,H             ;and H into B
       RET                     ;then return from BDOS happy
                               ;
                               ;******************************************************************************
                               ; BDOS function 40: Routine to write random file with zero fill
                               ;	enter with FCB address
                               ;	exit with return code in A
                               ;
ZERRND:
       CALL    RSELCT          ;select the desired drive
       LD      A,2             ;get the code for zero fill
       LD      (DCODE),A       ;set it into place
       LD      C,0             ;zero c as well
       CALL    RANF1           ;then off the finish the job set pointers
       CALL    Z,RWERR         ;then write the sector if all is well
       RET                     ;and return
                               ;
                               ;******************************************************************************
                               ;bdos data area follows
                               ;
TINFO:
DB                          $0E5
                               ;
ROVEC:
DW                          0  ;read only vector address storage
DLOG:
DW                          0  ;disk login areax
DMAADR:
DW                          $0080
                               ;DMA address defaults to 80H
SCRT0:
DW                          0  ;
SCRT1:
DW                          0  ;holds current track
SCRT2:
DW                          0  ;holds starting sector number of current track
DTEMP:
DW                          0  ;temp DMA address storage
DPB:
DW                          0  ;disk parameter block address
CHANGE:
DW                          0  ;scratch storage for disk change check
ALLOCA:
DW                          0  ;allocation vector area
DPBLK:
DW                          0  ;disk parm block - number of sectors/trk
BLSHFT:
DB                          0
                               ;block shift factor
BLKMSK:
DB                          0
                               ;block mask
EXTMSK:
DB                          0
                               ;null mask
DSIZE:
DW                          0  ;disk size
DIRMAX:
DW                          0  ;directory max
AL01:
DW                          0  ;Allocation storage
CKSIZE:
DW                          0  ;number of entries to check
TOFS:
DW                          0  ;track offset
TRANS:
DW                          0  ;translation routine address holder
RWFLG1:
DB                          0
                               ;flag indicates read or write
RWFLG2:
DB                          0
                               ;flag also used to indicate read or write
SCODE1:
DB                          0
DCODE:
DB                          0
                               ;disk code byte
IDATA:
DB                          0
                               ;input data storage area
EXTBLK:
DB                          0
SCODE:
DB                          0
SEARA:
DB                          0
DB                          0
DB                          0
DB                          0
BSIZE:
DB                          0
                               ;block size byte
OUT1:
DB                          0
                               ;used to force relog of disk on exit
OUTDSK:
DB                          0
                               ;BDOS exit disk
OLDDSK:
DB                          0
                               ;disk entered with
RECCNT:
DB                          0
                               ;record count holder
ESCNT1:
DB                          0
                               ;extent recort count high byte
ESCNT2:
DB                          0
                               ;extent record count low byte
DB                          0
GROUP:
DB                          0
                               ;group number storage
DB                          0
DIRPNT:
DB                          0
                               ;directory pointer area
DB                          0
DIROFF:
DB                          0
                               ;offset into the directory area
COUNT:
DB                          0
                               ;holds 16 bit counter for looping
DB                          0
LDIRPNT:
DB                          0
DB                          0,0,0,0
DB                          0,0,0,0
DB                          0,0,0,0
DB                          0,0,0,0
DB                          0,0,0
                               ;
                               ; BIOS Entry Vector Definitions
                               ;
                               ; (These are actually in CBIOS)
                               ;
BLOCK         3                ;(BOOT) Cold boot entry
WBOOTF:
BLOCK         3                ;Warm boot entry
CONSF:
BLOCK         3                ;Console status
CONIF:
BLOCK         3                ;Console char in
CONOF:
BLOCK         3                ;Console char out
LISTF:
BLOCK         3                ;List char out
PUNF:
BLOCK         3                ;Punch char out
READF:
BLOCK         3                ;Reader char in
HOMF:
BLOCK         3                ;Home disk
SELF:
BLOCK         3                ;Select disk
TRKF:
BLOCK         3                ;Set disk track addr
SECF:
BLOCK         3                ;Set disk sector addr
DMAF:
BLOCK         3                ;Set DMA buffer addr
DRDF:
BLOCK         3                ;Read sector
DWRF:
BLOCK         3                ;Write sector
BLOCK         3                ;
SECTRN:
BLOCK         3                ;Sector translation routine
                               ;
.END