Zapple-II/SOFTCARD80.ASM#040000
2019-10-14 17:18:27 -04:00

1 line
20 KiB
Plaintext

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Z80 code running on Softcard
; Implements CP/M style BDOS interface
; Requires the companion SOFTCARD65 6502 code
; Bobbi 2019
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BDOSADDR EQU 05000H ; Keep below 32K for now (Z80asm bug)
STCKTOP EQU 06000H ; Top of Z80 stack
SOFTCARD EQU 0E400H ; Softcard in slot 4 ($C400)
; 6502 zero page, in Z80 address space
CMD EQU 0F006H ; 6502 $06
AREG EQU 0F007H ; 6502 $07
XREG EQU 0F008H ; 6502 $08
YREG EQU 0F009H ; 6502 $09
ADDR EQU 0F0EBH ; 6502 $EB (LSB)
ADDRH EQU 0F0ECH ; 6502 $EC (MSB)
; Addresses of 6502 routines, in 6502 address space
COUT EQU 0FDEDH ; Print char in A
RDKEY EQU 0FD0CH ; Read key, return in A
BELL EQU 0FBE4H ; Sound the bell
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Entry point when Z80 cold starts is 0000H
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ORG 0000H
JP BDOSINIT ; Initialize BDOS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; See obsolescence.wix.com/obsolescence/cpm-internals for info
; on low storage usage ...
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
IOBYTE DEFB 0 ; Maps virtual to real devices
CURDRV DEFB 0 ; Currently selected drive 0=A: etc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; BDOS entry point must be at address 0005H for CP/M compatibility
; Function to invoke is passed in C
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BDOS JP BDOSIMP ; BDOS code is at top of memory
RVEC1 DEFW 0000H ; Restart vector 1
RVEC2 DEFW 0000H ; Restart vector 2
RVEC3 DEFW 0000H ; Restart vector 3
RVEC4 DEFW 0000H ; Restart vector 4
RVEC5 DEFW 0000H ; Restart vector 5
RVEC6 DEFW 0000H ; Restart vector 6
RVEC7 DEFW 0000H ; Restart vector 7
; Space for private BDOS data (implementation dependent) up to 0060H
DMAADDR DEFW 0080H ; DMA address defaults to FILEBUF (0080H)
LOGVEC DEFW 0000H ; Vector of logged in drives
ROVEC DEFW 0000H ; Vector of read-only drives
USERNUM DEFB 00H ; User number
; End of private, implementation dependent space
ORG 0060H ; Standard addr of 32 byte FCB
FCBDRV DEFB 00H ; FCB Drive (0 current, 1 A:, 2 B: etc)
FCBNAM DEFM 'FILENAMEEXT' ; FCB filename and extension
FCBEX DEFB 00H ; FCB extent field
FCBS1 DEFB 00H ; FCB S1 field
FCBS2 DEFB 00H ; FCB S2 field
FCBRC DEFB 00H ; FCB RC field
FCBMAP DEFS 16 ; Map of blocks in file
ORG 0080H ; Standard addr of 128 byte File Buffer
FILEBUF DEFS 128
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The application program proper starts at 0100H
; in order to be compatible with CP/M .COM programs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ORG 0100H
; Print signon message using C_WRITESTR
PROGSTRT LD DE,WELCOME ; Address of string
LD C,09H ; C_WRITESTR call
CALL BDOS ; CP/M BDOS call
; Print the alphabet using C_WRITE
LD B,'A'
L1 LD E,B ; Character to print
LD C,02H ; C_WRITE call
PUSH BC ; Preserve B (and C)
CALL BDOS ; CP/M BDOS call
POP BC ; Restore B (and C)
INC B
LD A,'Z'
CP B
JP Z,S1
JP L1
; Loop until there is a keystroke waiting using C_STAT
S1 LD C,0BH ; C_STAT call
CALL BDOS ; CP/M BDOS call
CP 0 ; Anything?
JR Z,S1 ; If not, loop
; Print a couple of asterisks
LD E,'*' ;
LD C,02H ; C_WRITE call
CALL BDOS ; CP/M BDOS call
LD E,'*' ;
LD C,2 ; C_WRITE call
CALL BDOS ; CP/M BDOS call
; Create a file using ProDOS MLI
; Creates 'A/TESTFILE.TMP'
; Directory 'A' needs to exist already
LD DE,MSG1 ; Address of string
LD C,09H ; C_WRITESTR call
CALL BDOS ; CP/M BDOS call
LD A,1 ; A: drive
LD (FCBDRV),A ;
LD A,'T' ; Filename
LD (FCBNAM),A ;
LD A,'E' ; Filename
LD (FCBNAM+1),A ;
LD A,'S' ; Filename
LD (FCBNAM+2),A ;
LD A,'T' ; Filename
LD (FCBNAM+3),A ;
LD A,'F' ; Filename
LD (FCBNAM+4),A ;
LD A,'I' ; Filename
LD (FCBNAM+5),A ;
LD A,'L' ; Filename
LD (FCBNAM+6),A ;
LD A,'E' ; Filename
LD (FCBNAM+7),A ;
LD A,'T' ; Extension
LD (FCBNAM+8),A ;
LD A,'M' ; Extension
LD (FCBNAM+9),A ;
LD A,'P' ; Extension
LD (FCBNAM+10),A ;
LD DE,0060H ; Default FCB address
LD C,16H ; F_MAKE call
CALL BDOS ; CP/M BDOS call
LD A,(AREG) ; Look at the return code
CP 0 ; Success?
JP Z,SUCC ;
LD DE,FAILMSG ; Fail message
JP PRMSG ;
SUCC LD DE,SUCCMSG ; Success message
PRMSG LD C,09H ; C_WRITESTR call
CALL BDOS ; CP/M BDOS call
; Read keyboard and echo to screen C_READ, C_WRITE
L2 LD C,1 ; C_READ call
CALL BDOS ; CP/M BDOS call
LD E,A ; Prepare to echo keystroke
LD C,2 ; C_WRITE call
CALL BDOS ; CP/M BDOS call
JP L2 ; Forever and ever
WELCOME DEFB 13
DEFM 'Zapple-II Test Stub...'
DEFB 13, '$'
MSG1 DEFB 13
DEFM 'Creating A/TESTFILE.TMP'
DEFB 13, '$'
SUCCMSG DEFM 'Success!'
DEFB 13, '$'
FAILMSG DEFM 'FAIL!'
DEFB 13, '$'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Implementation of CP/M STYLE BDOS
; Function to invoke is passed in C
; C=00H C_TERMCPM System reset
; C=01H C_READ Console read
; C=02H C_WRITE Console write
; C=07H GET_IOB Get IOBYTE
; C=08H SET_IOB Set IOBYTE
; C=09H C_WRITESTR Console write string
; C=0BH C_STAT Console status
; C=0CH S_BDOSVER Return version number
; C=0DH DRV_ALLRESET Reset disks
; C=0EH DRV_SET Select disk
; C=0FH F_OPEN Open file (IN PROGRESS)
; C=10H F_CLOSE Close file (IN PROGRESS)
; C=16H F_MAKE Create file (IN PROGRESS)
; C=17H DRV_LOGVEC Return bitmap of logged-in drives
; C=19H DRV_GET Return current drive
; C=1AH F_DMAOFF Set DMA address
; C=1CH DRV_SETRO Software write-protect current drive
; C=1DH DRV_ROVEC Return bitmap of read-only drives
; C=20H F_USERNUM Get/set user number
; C=25H DRV_RESET Selectively reset disk drives
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ORG BDOSADDR
BDOSINIT LD SP,STCKTOP ; Initialize SP
LD A,0 ;
LD (IOBYTE),A ; Initialize IOBYTE
LD (CURDRV),A ; Drive A:
LD (FRN1),A ; Initialize FRNs to zero
LD (FRN2),A ; ...
LD (FRN3),A ; ...
LD (FRN4),A ; ...
LD HL,0080H ; Initialize DMAADDR to 0080H
LD (DMAADDR),HL ; ...
LD HL,0000H ; Initialize LOGVEC & ROVEC to 0000H
LD (LOGVEC),HL ; ...
LD (ROVEC),HL ; ...
JP PROGSTRT ; Run user program
BDOSIMP LD A,C ; Prepare to check C is in range
CP 41 ; Max syscall# for CP/M 2.2 is 40
JP NC,UNIMP ; If >41 then call UNIMP
LD HL,BDOSVEC ; Start of vector table
SLA C ; Multiply C by 2
LD B,0 ; MSB of BC is zero
ADD HL,BC ; Address of vector in HL
LD C,(HL) ; Read LSB of address to jump to
INC HL ; Read MSB of address to jump to
LD H,(HL) ; ...
LD L,C ; Address needs to be in HL
JP (HL) ; Jump to it!
; Vector table
BDOSVEC DEFW C_TERMCPM ; C=00H
DEFW C_READ ; C=01H
DEFW C_WRITE ; C=02H
DEFW UNIMP ; C=03H (A_READ) AUX
DEFW UNIMP ; C=04H (A_WRITE) AUX
DEFW UNIMP ; C=05H (L_WRITE) PRN
DEFW UNIMP ; C=06H (C_RAWIO)
DEFW GET_IOB ; C=07H
DEFW SET_IOB ; C=08H
DEFW C_WRITESTR ; C=09H
DEFW UNIMP ; C=0AH (C_READSTR)
DEFW C_STAT ; C=0BH
DEFW S_BDOSVER ; C=0CH
DEFW DRV_ALLRST ; C=0DH
DEFW DRV_SET ; C=0EH
DEFW F_OPEN ; C=0FH
DEFW F_CLOSE ; C=10H
DEFW UNIMP ; C=11H (F_SFIRST)
DEFW UNIMP ; C=12H (F_SNEXT)
DEFW UNIMP ; C=13H (F_DELETE)
DEFW UNIMP ; C=14H (F_READ)
DEFW UNIMP ; C=15H (F_WRITE)
DEFW F_MAKE ; C=16H
DEFW UNIMP ; C=17H (F_RENAME)
DEFW DRV_LOGVEC ; C=18H
DEFW DRV_GET ; C=19H
DEFW F_DMAOFF ; C=1AH
DEFW UNIMP ; C=1BH (DRV_ALLOCVEC)
DEFW DRV_SETRO ; C=1CH
DEFW DRV_ROVEC ; C=1DH
DEFW UNIMP ; C=1EH (F_ATTRIB)
DEFW UNIMP ; C=1FH (DRV_DPB)
DEFW F_USERNUM ; C=20H
DEFW UNIMP ; C=21H (F_READRAND)
DEFW UNIMP ; C=22H (F_WRITERAND)
DEFW UNIMP ; C=23H (F_SIZE)
DEFW UNIMP ; C=24H (F_RANDREC)
DEFW DRV_RESET ; C=25H
DEFW UNIMP ; C=26H (*nothing* in CP/M 2.2)
DEFW UNIMP ; C=27H (*nothing* in CP/M 2.2)
DEFW UNIMP ; C=28H (F_WRITEZF)
; Unimplemented BDOS call, just ring the bell
UNIMP LD HL,BELL ; We are going to call BELL
LD (ADDR),HL ; ...
LD A,1 ; CMD=1 means call 6502 sub
LD (CMD),A ; ...
LD (SOFTCARD),A ; Do it!
RET ; Return to calling program
; System reset. Jump to $0000 - doesn't return
C_TERMCPM RST 0 ; Quick jump to zero
; Wait for a character from the console, return it in A and L
; Also echoes the char to the console
C_READ LD HL,RDKEY ; We are going to call RDKEY
LD (ADDR),HL ; ...
LD A,1 ; CMD=1 means call 6502 sub
LD (CMD),A ; ...
LD (SOFTCARD),A ; Do it!
LD A,(AREG) ; Grab the return value
PUSH AF ; Preserve A (and F)
LD HL,COUT ; Echo the character using COUT
LD (ADDR),HL ; ...
LD A,1 ; CMD=1 means call 6502 sub
LD (CMD),A ; ...
LD (SOFTCARD),A ; Do it!
POP AF ; Restore A (and F)
AND 7FH ; Mask high bit
LD L,A ; Copy A to L
RET ; Return to calling program
; Write character in E to the console
; TODO: Handle tabs, ^S and ^Q
C_WRITE LD A,80H ; Set high bit
OR E ; ...
CP 8AH ; Check for linefeed
RET Z ; If LF, don't print it
LD (AREG),A ; Pass char to COUT in 6502 A
LD HL,COUT ; We are going to call COUT
LD (ADDR),HL ; ...
LD A,1 ; CMD=1 means call 6502 sub
LD (CMD),A ; ...
LD (SOFTCARD),A ; Do it!
RET ; Return to calling program
; Get the IOBYTE in A and L
GET_IOB LD A,(IOBYTE) ;
LD L,A ; Copy to L
RET
; Set the IOBYTE
; E contains the IOBYTE value to set
SET_IOB LD A,E ;
LD (IOBYTE),A ;
RET
; Write ASCII string to console. '$' is the terminator
; DE contains the address of the string
C_WRITESTR LD A,(DE) ; Fetch character from string
CP '$' ; Is it '$'?
RET Z ; If so, we are done
PUSH DE ; We are gonna need E
LD E,A ; For C_WRITE
CALL C_WRITE ; Sent char to console
POP DE ; Recover the pointer
INC DE ; Advance pointer
JP C_WRITESTR ; Handle the next char
; Returns 0 in A and L if no chars waiting, non zero otherwise
C_STAT LD A,3 ; CMD=3 means peek at keyboard
LD (CMD),A ; ...
LD (SOFTCARD),A ; Do it
LD A,(AREG) ; Grab the return value
LD L,A ; Copy A to L
RET ; Return to calling program
; Returns system type in B and H, BDOS version in A and L
S_BDOSVER LD B,0 ; System is 8080 CP/M
LD H,B ; Also in H
LD A,22H ; Pretend to v2.2
LD L,A ; Also in A
RET
; Reset disks
; Makes A: drive the default
; TODO: Empty all disk buffers
DRV_ALLRST LD A,0 ; 0 means drive A:
LD (CURDRV),A ; Store in CURDRV
LD BC,FILEBUF ; FILEBUF is at 0080H
LD (DMAADDR),BC ; Reset DMA address
RET
; Select disk
; Disk to select is passed in E (A: is 0, B: is 1 etc.)
; Return 00 for success, 0FFH for error in A and L
DRV_SET LD A,E ; Prepare to compare disk number
CP 16 ; Support 16 'drives' A: - P:
JP NC,DSERR ; If A>15 ... error
LD (CURDRV),A ; Store the requested drive number
LD A,0 ; Return code meaning success
JP DSRET ;
DSERR LD A,0FFH ; Return code for error
DSRET LD L,A ; Return code in L too
RET
; Open file
; DE is the address of the FCB describing the file to open
; Returns error codes in A and L:
; 0 through 3 for success, 0FFH is file not found
F_OPEN CALL MAKEPATH ; Populate PATHLEN and PATH
; Work out which IOBUF to allocate for this file
; And set the pointer in the ProDOS parameter list
LD A,(FRN1) ; See if IOBUF1 is free
CP 0 ; ...
JP Z,FOS1 ; IOBUF1 is free
LD A,(FRN2) ; See if IOBUF2 is free
CP 0 ; ...
JP Z,FOS2 ; IOBUF2 is free
LD A,(FRN3) ; See if IOBUF3 is free
CP 0 ; ...
JP Z,FOS3 ; IOBUF3 is free
LD A,(FRN4) ; See if IOBUF4 is free
CP 0 ; ...
JP Z,FOS4 ; IOBUF4 is free
LD A,0FFH ; Error too many files open
LD L,A ; Copy to L
RET ; Error return
FOS1 LD HL,IOBUF1 ; Address of IOBUF1
LD A,1 ; Buffer 1
JP FOS5 ;
FOS2 LD HL,IOBUF2 ; Address of IOBUF2
LD A,2 ; Buffer 2
JP FOS5 ;
FOS3 LD HL,IOBUF3 ; Address of IOBUF3
LD A,3 ; Buffer 3
JP FOS5 ;
FOS4 LD HL,IOBUF4 ; Address of IOBUF4
LD A,4 ; Buffer 4
FOS5 LD (FOMLII),HL ; Store in parameter list
LD (FOIOB),A ; Record the buffer index for later
;
; TODO NOW THE CODE TO ACTUALLY INVOKE THE MLI CALL ON 6502
;
;
; TODO HANDLE ERROR RETURN CODES FROM MLI
;
LD A,(FOMLIN) ; Get ProDOS file reference number
LD H,D ; Pointer to FCB ...
LD L,E ; ... into HL
LD BC,14 ; Offset to S2 field (reserved field)
ADD HL,BC ; Compute address
LD (HL),A ; Store file refence number is S2 field
LD A,(FOIOB) ; Obtain IOBUF idx (1,2,3,4)
CP 1 ; Is it 1? ...
JP Z,FOS6 ; ... yes
CP 2 ; Is it 2? ...
JP Z,FOS7 ; ... yes
CP 3 ; Is it 3? ...
JP Z,FOS8 ; ... yes
JP FOS9 ; Must be 4
FOS6 LD A,(FOMLIN) ;
LD (FRN1),A ;
JP FOS10 ;
FOS7 LD A,(FOMLIN) ;
LD (FRN2),A ;
JP FOS10 ;
FOS8 LD A,(FOMLIN) ;
LD (FRN3),A ;
JP FOS10 ;
FOS9 LD A,(FOMLIN) ;
LD (FRN4),A ;
JP FOS10 ;
FOS10
;
; TODO HANDLE RETURN CODE ON SUCCESS (0,1,2,3)
;
RET
FOMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
DEFB 3 ; ProDOS PL: Three parameters
FOMLIP DEFW PATHLEN ; ProDOS PL: pointer to path
FOMLII DEFW 0000H ; ProDOS PL: pointer to IO buffer
FOMLIN DEFB 0 ; ProDOS PL: File reference number
FOIOB DEFB 0 ; Local variable to record IOBUF idx
DEFB 60H ; RTS in 6502 code
; Close file
; DE is the address of the FCB describing the file to close
; Returns error codes in BA and HL:
; TODO WRITE THIS!!!!
F_CLOSE
; Steps:
; 1) Get the file reference number from field S2 of FCB & fill in FCMLIN
; 2) Hand off pointer to the JSR instruction to 6502 to run
; 3) On return, check return code and set BA and HL accordingly
RET
FCMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
DEFB 1 ; ProDOS PB: One parameter
FCMLIN DEFB 0 ; ProDOS PB: File reference number
DEFB 60H ; RTS in 6502 code
; Create (and open) file
; DE is the address of the FCB describing the file to create
; Returns error codes in A and L:
; 0 through 3 for success, 0FFH is file could not be created
F_MAKE CALL MAKEPATH ; Populate PATHLEN and PATH
LD HL,FMMLI ; Pass address of 6502 JSR instruction
LD (ADDR),HL ; ...
LD A,2 ; CMD=2 means ProDOS MLI call
LD (CMD),A ; ...
LD (SOFTCARD),A ; Do it!
LD A,(AREG) ; Get return code from the MLI call
CP 0 ; See if there was an error
JP NZ,FMS1 ; Handle error
;;;; CALL F_OPEN ; Open the file using same FCB (DE ptr)
LD A,0 ;;; TEMP DEBUG - SHOULD CALL F_OPEN ABOVE ;;;
RET
FMS1 LD A,0FFH ; 0FFH for error
LD L,A ; Return code in L also
RET
RET
FMMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
DEFB 7 ; ProDOS PL: Seven parameters
FMMLIP DEFW PATHLEN ; ProDOS PL: Pointer to path
FMMLIA DEFB 0C3H ; ProDOS PL: Access (0C3H full access)
FMMLIT DEFB 0 ; ProDOS PL: File type (0 means typeless)
FMMLIAT DEFW 0000H ; ProDOS PL: Aux file type (always 0000H)
FMMLIS DEFB 1 ; ProDOS PL: Storage type (1 for file)
FMMLICD DEFW 0000H ; ProDOS PL: Create date (always 0000H)
FMMLICT DEFW 0000H ; ProDOS PL: Create time (always 0000H)
DEFB 60H ; RTS in 6502 code
; Return bitmap of logged-in drives in HL
DRV_LOGVEC LD HL,(LOGVEC) ;
RET
; Return current drive in A
DRV_GET LD A,(CURDRV) ;
RET
; Set DMA address
; DMA address is passed in DE
F_DMAOFF LD (DMAADDR),DE ;
RET
; Software write-protect current disk
DRV_SETRO LD B,80H ; Set MS-bit in B, will rotate this below
LD A,(CURDRV) ; Current drive (0 A:, 1 B: ...)
INC A ; It is easier if A: is 1, B is 2 etc.
CP 9 ; See if it in LS-byte or MS-byte of ROVEC
JP NC,DSRMSB ; If A>8 then drive bit in in MS-byte
DSRL1 RL B ;
DEC A ;
JP NZ,DSRL1 ;
LD A,(ROVEC) ; Fetch the LS-byte of ROVEC
OR B ; Set the bit using OR
LD (ROVEC),A ; Store LS-byte back to ROVEC
RET ; We're done
DSRMSB LD C,8 ; Subtract 8 from the drive number
SUB C ; A = A-8
DSRL2 RL B ;
DEC A ;
JP NZ,DSRL2 ;
LD A,(ROVEC+1) ; Fetch the MS-byte of ROVEC
OR B ; Set the bit using OR
LD (ROVEC+1),A ; Store MS-byte back to ROVEC
RET
; Return bitmap of read-only drives in HL
DRV_ROVEC LD HL,(ROVEC) ; Bit 0 of L is A:, bit 7 of H is P:
RET
; Get/set user number
; E contains the user number to set, or 0FFH to get current user number
; If E is 0FFH then user number is returned in A
F_USERNUM LD A,E ; See if it is get or set
CP 0FFH ; 0FFH means 'get'
JP Z,FUNS1 ; It is 'get'
LD (USERNUM),A ; Set user number
RET
FUNS1 LD A,(USERNUM) ; Get user number
RET
; Selectively reset disk drives
; DE contains bitmap of drives to reset (bit 0 of E if A:, bit 7 of D is P:)
; Returns A=00H if okay, A=FFH if error
; Resetting means removing the read-only status
DRV_RESET LD A,(ROVEC) ; Do the LSB ...
XOR E ; ... with E
LD (ROVEC),A ;
LD A,(ROVEC+1) ; Then the MSB ...
XOR D ; ... with D
LD (ROVEC+1),A ;
RET
; Populate the PATH buffer (and PATHLEN) by copying from FCB
; DE contains a pointer to the FCB
; Be sure not to trash DE
; TODO FINISH THIS!!!
MAKEPATH LD A,(DE) ; Get drive number from FCB
CP 0 ; See if it is zero (default drive)
JP NZ,MPS1 ; If drive explicit
LD A,(CURDRV) ; If default drive use CURDRV
INC A ; CURDRV is zero based
MPS1 ADD A,'A'-1 ; Convert to drive letter
LD (PATH),A ; Store as first char of path
LD A,'/' ; Second char of path is '/'
LD (PATH+1),A ; ...
LD C,2 ; Use C to count chars in filename
LD H,D ; Copy address of FCB from DE ...
LD L,E ; ... to HL
INC HL ; HL points to filename in FCB
LD IX,PATH+2 ; IX points to next char of path to write
MPL1 LD A,(HL) ; Obtain filename character
CP ' ' ; See if it is a space (? or NULL maybe?)
JP Z,MPS3 ; If so we are done with filename
EX AF,AF' ; We need to re-use A here
LD A,C ; Get character count
CP 10 ; Drive letter, slash and 8 char filename
JP Z,MPS2 ; If so we are done with filename
EX AF,AF' ; Swap back to original A reg
LD (IX+0),A ; Copy to PATH buffer
INC C ; Count the chars
INC HL ; Next byte of filename in FCB
INC IX ; Next byte of PATH buffer
JP MPL1 ; Loop till done
MPS2 EX AF,AF' ; Swap back to original A reg
MPS3 ; EXTENSION
; ...
LD A,C ; Store length of string
LD (PATHLEN),A ; ...
RET
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Additional private scratch space for BDOS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 64 bytes for storing a file path as a Pascal style string
PATHLEN DEFB 0
PATH DEFS 64
; Four 1024 byte ProDOS I/O buffers
; TODO THESE MUST START ON A PAGE BOUNDARY!!!!
IOBUF1 DEFS 1024
IOBUF2 DEFS 1024
IOBUF3 DEFS 1024
IOBUF4 DEFS 1024
; Record file reference numbers for each I/O buffer
; Or 0 if no file is using the buffer
FRN1 DEFB 0
FRN2 DEFB 0
FRN3 DEFB 0
FRN4 DEFB 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;