; 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
; routine at the address set with the -F command line option
; The file routine is JSRed with the command code in A, and any
; parameters in Y and/or X. After command execution. Y and/or X
; will contain any return values. Y is usually the error code.

        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
MWRITE  EQU $80

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

;fflush(chan) - Flush Write Buffer to File
;Args: A = Channel 
;Returns: A = Error (0=None)
FFLUSH: TAY             ;Set Channel Number
        LDA #'F'        ;Set Command to FLUSH
        BNE .FEXECX     ;Execute and Return Result in A
.FEXEC  JSR FSCMD       ;Execute Command
        TYA             ;Return Error Code

;fopen(mode, &name) - Open File
;Args: A = 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
        TAX             ;Pass Mode to X
        JSR FSDRVN      ;Convert Drive Letter to Drive Number
        BCS .RETURN     ;If Error, Return Carry Set
        TAY             ;Pass Drive Number in Y
        LDA #'O'        ;Set Command to OPEN
.FEXECX JSR FSCMD       ;Execute Command
        TXA             ;Return Channel in A
        RTS

;fclose(chan) - Close File
;Args: A = Channel Number
;Returns: A = Error (0=None)
FCLOSE: TAY             ;Set Channel
        LDA #'C'        ;Set Command to CLOSE
        BNE .FEXEC      ;Execute and Return Error in A

;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 .FEXEC      ;Execute and Return Error in A

;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 in A

;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

;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 .FEXEC      ;Execute and Return Error 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

;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

;fread(chan, count) - 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
        LDA #'R'      ;Set Command to READ
        BNE .FEXECX   ;Execute and Return Result in A

;fwrite(chan, count) - 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
        JMP 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

;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

;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

;fsaddr(&array) - Set File Buffer Address
;Emulation: sets internal variable filebuff
;Args: Y,X = Address of String or Array
FSADDR: PHA             ;Save Accumulator
        LDA #'A'        ;Set Command to ADDRESS
        BNE .FSCMDA     ;Execute and Restore Accumulator
        
;fsname(&name) - Set File Name
;Emulation: sets internal variable filename
;Args: Y,X = Address of Filename
FSNAME: PHA             ;Save Accumulator
        LDA #'N'        ;Set command to name
.FSCMDA JSR FSCMD       ;Execute command
        PLA             ;Restore Accumulator
        RTS

;fscmd(cmd, args) - File I/O Command Processor
;Args: A = Command Code
;      Y,X = Arguments(s)
;Returns: A = Command Code (usually)
;         Y = Error Code (usually)
;         X = Result (when applicable)
FSCMD: EQU $FFE6        ;run6502 File I/O Command Routine
        
; Command   Description           Arguments       Requires  Returns
; FSADDR    Set Buffer Address    A='A', YX=Addr            YX=Addr
; CLOSEDIR  Close Directory       A='B', Y=Chan             Y=Err
; FCLOSE    Close File            A='C', Y=Chan             Y=Err
; OPENDIR   Open Directory        A='D', YX=Name            Y=Err, X=Chan
; 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 #           Y=Err
; FSINIT    Init File System      A='I'
; READDIR   Read Dir Entry        A='J', Y=Chan #           Y=Err
; REMOVE    Remove File           A='K', YX=Name            Y=Err
; FLOAD     Load File             A='L'  *                  A=Err, YX=End
; RENAME    Rename File           A='M', YX=New +           Y=Err
; FSNAME    Set Filename          A='N', YX=Name        
; FOPEN     Open File             A='O', Y=Mode +           Y=Err
; FPUTC     Put Character         A='P', Y=Chan, X=Char     Y=Err
; FPUTS     Put String            A='Q', Y=Chan  #          Y=Err
; FREAD     Read Bytes            A='R', Y=Chan, X=Count #  Y=Err, X=Count   
; FSAVE     Save File             A='S', Y=Chan, YX=End  *  Y=Err
; GETCWD    Get Current Dir       A='T', Y=Chan, YX=Addr    Y=Err, X=Len
; CHDIR     Change Directory      A='U', YX=Name            Y=Err
; GCDRIVE   Get/Set Cur Drive     A='V', Y=Drive, C=Mode    Y=Err, X=Result
; FWRITE    Write Bytes           A='W', Y=Chan, X=Count #  Y=Err, X=Count
; MKRMDIR   Create/Remove Dir     A='X', YX=Name, C=Mode    Y=Err, X=Result
; FERROR    Get Last Error        A='Y', Y=Chan #           Y=Err


; # Requires call to FSADDR
; + Requires call to FSNAMR
; * Requires calls to FSADDR and FSNAME 
