; C02 Module fileio Assembly Language Eoutines for run6502 emulator
; This is the reference implementation of the fileo module

; The run6502 program has been customized with an emulated file command
; routine at the address set with the -F command line option.
; The file routine is JSRed with the command in A, and any parameters 
; in Y and/or X. After command execution. Y and/or X will contain any 
; return values, with Y is usually the error code.
; 
; The Command Routine FSCMD is defines in file include/run6502.a02


        SUBROUTINE FILEIO

DRIVES  EQU 26          ;Number of Disk Drives
DRIVE   BYTE  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"  ;Disk Drive Letters

DISKS   EQU 1           ;Number of Disks in Drive
DISK    BYTE  0

;File Open Modes
MREAD   EQU $00         ;Open for Read
MWRITE  EQU $40         ;Open for Write
MAPPND  EQU $80         ;Open for Append
MRECRD  EQU $C0         ;Record Oriented File

;File Open Types
TTEXT   EQU $00         ;Open as Texy File  
TBNRY   EQU $20         ;Open as Binary File

; File Load Modes
MRELCT  EQU $00         ;Relocate (Load at Specified Address)
MABSLT  EQU $80         ;Absolute (Load at Address in File Header)

;ferror(chan, &msg) - Check for Error
;Args: A = Channel Number
;      Y,X = String Address
;Returns: A = Last Error ($FF - Invalid Channel)
;         Y = Error (0=None)
FERROR: JSR FSADDR    ;Set Buffer Address
        TAY           ;Set Channel Number
        LDA #'Y'      ;Set Command to FERROR
        BNE .FEXECX   ;Execute and Return Result in A

;fflush(chan) - Flush Write Buffer to File
;Args: A = Channel 
;Returns: A = Status (0=Success, $FF = Failure)
;         Y = Error (0=None)
FFLUSH: TAY             ;Set Channel Number
        LDA #'F'        ;Set Command to FLUSH
        BNE .FEXECX     ;Execute and Return Result in A

;fopen(mode, &name) - Open File
;Setup: Call FSRECS with Record Size if Mode is MRECRD
;Args: A = File Mode | Drive ID 
;      Y,X = Pointer to File Name
;Returns: A = File Channel (0=File Not Opened)
;         Y = Error (0=None)
FOPEN:  JSR FSNAME      ;Set Filename
.FOPEN  TAX             ;Pass File Mode to X
        JSR FSDRVN      ;Convert Drive Letter to Drive Number
        BCS .FOPERR     ;If Error, Return Carry Set
        TAY             ;Pass Drive Number in Y
				CLC							;Set Mode to FOPEN
        LDA #'O'        ;Set Command to OPEN
.FEXECX JSR FSCMD       ;Execute Command
        TXA             ;Return Channel in A
.FOPERR RTS

;fclose(chan) - Close File
;Args: A = Channel Number
;Returns: A = Error (0=None)
FCLOSE: TAY             ;Set Channel
        CLC             ;Set Mode to FCLOSE
        LDA #'C'        ;Set Command to CLOSE
.FEXECY JSR FSCMD       ;Execute Command
        TYA             ;Return Errno
        RTS

;feof(chan) - Check for End of File
;Args: A = Channel Number
;Returns: A = $00 - Not End of File
;             $FF - End of File
;             Otherwise, Error Code
FEOF:   TAY             ;Set Channel
        LDA #'E'        ;Set Command to EOF
        BNE .FEXECY     ;Execute and Return Errno


;fgetc(chan) - Read Character from File
;Args: A = Channel Number
;Returns: A = Character
;         Y = Error Code ($00 - Success)
FGETC:  TAY             ;Set Channel
        LDA #'G'        ;Set Command to GET
        BNE .FEXECX     ;Execute and Return Result

;fgets(chan, &s) - Read String from File
;Args: A = Channel Number
;      Y,X = Address of String
;Returns: A = Number of Bytes Read
;         Y = Error Code ($00 - Success)
FGETS:  JSR FSADDR      ;Set String Address
        TAY             ;Set Channel Number
        LDA #'H'        ;Set Command to GETS
        BNE .FEXECX     ;Execute and Return Result in A

;fgetr(chan, recno) - Read Record from File
;Setup: Call FSADDR with buffer address
;Args: A = Channel Number
;      Y,X = Record Number
;Returns: A = Error Code
;         Y,X = Next Record Number
FGETR:  JSR FSINDX      ;Set Record Number
        TAY             ;Set Channel Number
        SEC             ;Set Mode to RECORD
        LDA #'R'        ;Set Command to READ
        BNE .FSCMD      ;Execute Command and Return

;fgetw(chan) - Read Word from File
;Args: A = Channel Number
;Returns: A = Error Code ($00 - Success)
;         Y,X = Word
FGETW:  TAY             ;Set Channel
        CLC             ;Set Mode to Get
        LDA #'J'        ;Set Command to GET
        BNE .FSCMD      ;Execute Command and Return

;fputc(chan, char) - Write Character to File
;Args: A = Character to Write
;      Y = Channel Number
;Returns: A = Error Code ($00 - Success)
FPUTC:  TAX             ;Set Character to Write
        LDA #'P'        ;Set Command to PUT
        BNE .FEXECX     ;Execute and Return Result in A
       
;fputln(chan, &s) - Write String to File with Newline
;Args: A = Channel Number
;      Y,X = Address of String
;Returns: A = Number of Characters Written
;         Y = Error Code ($00 - Success)
FPUTLN: SEC             ;Append Newline
        BCS .FPUTSA     ;Write String to File

;fputr(chan, recno) - Write Record from File
;Setup: Call FSADDR with buffer address
;Args: A = Channel Number
;      Y,X = Record Number
;Returns: A = Error Code
;         Y,X = Next Record Number
FPUTR:  JSR FSINDX      ;Set Record Number
        TAY             ;Set Channel Number
        SEC             ;Set Mode to RECORD
        LDA #'W'        ;Set Command to READ
        BNE .FSCMD      ;Execute Command and Return
        
;fputs(chan, &s) - Write String to File
;Args: A = Channel Number
;      Y,X = Address of String
;Returns: A = Number of Characters Written
;         Y = Error Code ($00 - Success)
FPUTS:  CLC             ;Do Not Append Newline
.FPUTSA JSR FSADDR      ;Set File Buffer Address
        TAY             ;Set Channel
        LDA #'Q'        ;Set Command to PUTS
        BNE .FEXECX     ;Execute and Return Result in A

;fputw(chan, word) - Write Word to File
;Args: A = Channel Number
;      Y,X = Word
;Returns: A = Error Code ($00 - Success)
FPUTW:  JSR FSADDR      ;Set File Buffer Address
        TAY             ;Set Channel
        SEC             ;Set Mode to PUT
        LDA #'J'        ;Set Command to WORD
        BNE .FEXECY     ;Execute and Return Result in A

;fread(count, chan) - Read Bytes from File
;Setup: Call FSADDR with array address
;Args: A = Number of Bytes to Read
;      Y = Channel Number
;Returns: A = Number of Bytes Read
;         Y = Error Code ($00 - Success)
FREAD:  TAX             ;Set Number of Bytes to Write
        CLC             ;Set Mode to BYTES
        LDA #'R'        ;Set Command to READ
        BNE .FEXECX     ;Execute and Return Result in A

;rewind(chan) - Move to Beginning of File
;Args: A = Channel Number
;Returns: A = Error Code ($00 - Success)
REWIND: LDX #0          ;Set Position to $0000
        LDY #0          ;and Fall Through to FSEEK

;fseek(chan, pos) - Move to Position in File
;Args: A = Channel Number
;      Y,X = Position
;Returns: A = Error Code ($00 = Success)
FSEEK:  JSR FSINDX    ;Set File Index to Position`
        TAY           ;Set Channel to A
        CLC           ;Set Mode to SEEK
        LDA #'Z'      ;Set Command to SEEK
        BNE .FEXECY   ;Execute and Return Errno
        
;ftell(chan, pos) - Get Position in File
;Args: A = Channel Number
;Returns: A = Error Code ($00 = Success)
;         Y,X = Position in File ($FFFF = Failure)
FTELL:  JSR FSADDR    ;Set File Address to Position`
        TAY           ;Set Channel to A
        SEC           ;Set Mode to TELL
        LDA #'Z'      ;Set Command to SEEK
        BNE .FSCMD      ;Execute Command and Return

;fwrite(count cban) - Write Bytes to File
;Setup: Call FSADDR with array address
;Args: A = Number of Bytes to Write
;      Y = Channel Number
;Returns: A = Number of Bytes Written
;         Y = Error Code ($00 - Success)
FWRITE: TAX           ;Set Number of Bytes to Write
        LDA #'W'      ;Set Command to WRITE
        BNE .FEXECX   ;Execute and Return Result in A

;fload(name) - Load File into Memory
;Setup: Call FSNAME with filename address
;       Call FSADDR with start address
;Args: A = Option + DriveID
;      Y,X = End Address
;Returns: A = Error Code ($00 - Success)
;         X,Y = Load Address
FLOAD:  CMP #MABSLT   ;Check Load Mode
        BNE .ERR22    ;If Invalid, Return Error
        LDA #'L'      ;Set Command to LOAD
        BNE .FSCMD      ;Execute Command and Return
.ERR22  LDA #22
        RTS

;fsave(name) - Save File from Memory
;Setup: Call FSNAME with filename address
;       Call FSADDR with start address
;Args: A = Option + DriveID
;      Y,X = End Address
;Returns: A = Error Code ($00 - Success)
FSAVE: ORA $0        ;If Not Drive 0
       BNE .ERR22    ;Return Error
       LDA #'S'      ;Set Command to SAVE
       BNE .FSCMD    ;Execute Command and Return

;File System Commands

;Convert Drive Letter Into Drive Number
;Drive letters A-Z in DOS map to drive numbers 1-26
;A drice specifier of NULL - SPACE or @ maps to drive 0, which
;means the current drive in the run6502 File Command Processor.
;Args: A = Drive Letter
;Returns: A = Drive Number
;         Carry Clear = Valid Drive Number
;         Carry Set = Invalid Drive Number
FSDRVN: SEC
        SBC #' '        ;If Drive Specifier 
        BCS .FSDRVL     ;is NULL through SPACE
        LDA #0          ;  Return Drive Number 0
        RTS             ;  and Carry Clear
.FSDRVL AND #$1F        ;Convert A-Z, a-z to 1-26
        CMP #DRIVES+2   ;If Drive Number is 
        BCC .RETURN     ;greater than 26
        LDA #$FF        ;  Return Operation Failed
        LDY #1          ;  and Error - Invalid Argument
.RETURN RTS

;fsinit() - Initialize File System
FSINIT: LDA #'I'        ;Set Command to INITFS
.FSCMD  JMP FSCMD       ;Execute Command and Return

;fsrecs(size) - Set File Record Size
;Args: A = Size
FSRECS: TAX             ;Set Y.X to 0,A
        LDY #0          ;and Fall The to FSINDX

;fsindx(&array) - Set File Record Number
;Args: Y,X = Record Number
FSINDX: PHP             ;Save Status Register
        PHA             ;Save Accumulator
        SEC             ;Set Mode to FILEINDX
        BCS .FSADDR     ;Execute ADDRESS and Restore Accumulator

;fsaddr(&array) - Set File Buffer Address
;Args: Y,X = Address of String or Array
FSADDR: PHP             ;Save Status Register
        PHA             ;Save Accumulator
        CLC             ;Set mode to FILEADDR
.FSADDR LDA #'A'        ;Set Command to ADDRESS
        JSR FSCMD       ;Execute command
        PLA             ;Restore Accumulator
        PLP             ;Restore Status Register
        RTS

;fsbuff(&name) - Set File Buffer
;Args: Y,X = Address of String
FSBUFF: PHA             ;Save Accumulator
        PHP             ;Save Status Register
        SEC             ;Set Mode to FILEBUFF
        BCS .SETNAM     ;Execute Command 'N' and Return

;fsname(&name) - Set File Name
;Args: Y,X = Address of Filename
FSNAME: PHA             ;Save Accumulator
        PHP             ;Save Status Register
        CLC             ;Set Mode to FILENAME
.SETNAM LDA #'N'        ;Set command to name
.FSCMDA JSR FSCMD       ;Execute command
        PLP             ;Restore Status Register
        PLA             ;Restore Accumulator
        RTS
        
; Command   Description           Arguments                 Returns
; FSADDR    Set Buffer Address    A='A',YX=Addr             YX=Addr
; FCLOSE    Close File            A='C',Y=Chan,CC           Y=Err
; CLOSEDIR  Close Directory       A='C',Y=Chan,CS           Y=Err
; READDIR   Read Dir Entry        A='D',Y=Chan (1)          Y=Err
; FEOF      Check for EOF         A="E',Y=Chan              Y=Err ($FF=EOF)
; FFLUSH    Flush Output          A='F',Y=Chan              Y=Err
; FGETC     Get Character         A='G',Y=Chan              Y=Err, X=Char
; FGETS     Get String            A='H',Y=Chan (1)          Y=Err
; FSINIT    Init File System      A='I',
; REMOVE    Remove File           A='K',YX=Name             Err
; FLOAD     Load File             A='L' (3)                 A=Err, YX=End
; RENAME    Rename File           A='M',YX=New (2)          Y=Err
; FSNAME    Set Filename          A='N',YX=Name,CC        
; FOPEN     Open File             A='O',Y=Mode|Drive,CC (2) Y=Err, X=Chan
; OPENDIR   Open Directory        A='O',Y=Drive,CS (2)      Y=Err, X=Chan
; FPUTC     Put Character         A='P',Y=Chan,X=Char       Y=Err, X=Char
; FPUTS     Put String            A='Q',Y=Chan  (1)         Y=Err
; FREAD     Read Bytes            A='R',Y=Chan,X=Count (1)  Y=Err, X=Count   
; FSAVE     Save File             A='S',Y=Chan,YX=End  (3)  Y=Err
; GETCWD    Get Current Dir       A='U',Y=Drive (1)         Y=Err, X=Len
; CHDIR     Change Directory      A='U',Y=Drive (2)         Y=Err
; GETDRV    Get Current Drive     A='V',CC                  Y=Err, X=Result
; CHDRV     Set Current Drive     A='V',Y=Drive,CS          Y=Err, X=Result
; FWRITE    Write Bytes           A='W',Y=Chan, X=Count (1) Y=Err, X=Count
; MKDIR     Create Directory      A='X',Y=Drive,CC (2)      Y=Err, X=Result
; RMDIR     Remove Directory      A='X',Y=Drive,CS (2)      Y=Err, X=Result
; FERROR    Get Last Error        A='Y',Y=Chan (1)          Y=Err


; (1) Requires call to FSADDR
; (2) Requires call to FSNAME (and FSINDX for FOPEN w/MRECRD)
; (3) Requires calls to FSADDR and FSNAME
