; FSFILE.ASM ; ZX Spectrum tape/microdrive filing routines - J.G.Harston ; --------------------------------------------------------- ; FSLOAD - Load data, allows "F:FILENAME" filenames ; FSSAVE - Save data, allows "F:FILENAME" filenames ; FSDRIVE - Select drive/device ; FSCAT - Catalog files ; FSINIT - Initialise default drive/device ; Size: 554 bytes ; ; All routines are entered with IX=>some workspace, HL=>filename ; (terminated with carriage return), and return C if Completed ; successfully, and return NC if Not Completed. ; ; Using 1:FILENAME syntax for microdrive access also works using ; the Disciple/PlusD as they allow the use of the Interface 1 syntax. ; ; NOTE: The microdrive access manipulates the Basic workspace, ; so the code cannot be called from code that wants to return ; to Basic with RET. If the code wants to return to Basic after ; it has to drop to the command line with RST 8:DEFB &FF or ; manually contruct a GOTO to a Basic program line. ; ; Microdrive access can only only load/save binary (type 3) files, ; with exec address always set to &FFFF. ; Tape access can load/save any file type as specified on entry. ; FSLOAD - Load data ; ------------------ ; On entry, IX=>header workspace, at least 34 bytes ; HL=>filename, terminated with ; A = type ; DE= start or 0 to use file's address ; On exit, CS= load successful ; NC= load failed - BREAK pressed or Bad filename ; FSLOAD CALL FSPARSE ; Parse filename, build header RET NC ; Bad filename CP "T" JP NZ,MDLOAD ; Load from microdrive FSLD01 PUSH IX LD DE,17 ; DE=length ADD IX,DE ; IX=>loaded header XOR A ; Header block CALL FSLD10 ; Load a header block FSLD02 POP IX JR C,FSLD03 ; Header loaded CALL &1F54 ; Test BREAK key JR C,FSLD01 ; Loop for another header RET ; BREAK pressed FSLD03 PUSH IX LD A,(IX+1) CP " " JR Z,FSLD08 ; load "" - match next LD B,11 ; Match type byte and ten characters LD A,(IX+0) JR FSLD04 ; Check type byte first FSLDLP LD A,(IX+0) CP "*" JR Z,FSLD08 ; Match all CP "#" JR Z,FSLD06 ; Match one character CP (IX+17) JR Z,FSLD06 ; Match one character XOR &20 ; Force to upper case FSLD04 XOR (IX+17) ; EQ=match, CC always JR Z,FSLD06 ; Matched POP IX JR FSLD01 ; Loop for another FSLD06 INC IX DJNZ FSLDLP ; Loop for 11 bytes FSLD08 POP IX ; IX=>header to match LD H,(IX+14) LD L,(IX+13) ; Start LD A,H OR L JR NZ,FSLD09 ; Use specified address LD H,(IX+17+14) ; Use file's address LD L,(IX+17+13) FSLD09 LD D,(IX+17+12) ; Length LD E,(IX+17+11) PUSH HL POP IX ; IX=start address LD A,&FF ; Data block FSLD10 SCF ; Load ; Drop through... ; ; Call LDBYTES catching error ; --------------------------- ERRSP EQU 23613 LDBYTES LD HL,(ERRSP) ; Save error handler PUSH HL CALL LDBYT2 JR SVDONE LDBYT2 LD (ERRSP),SP JP &0556 ; FSSAVE - Save data ; ------------------ ; On entry, IX= header workspace, at least 17 bytes ; HL=>filename, terminated with ; A = type ; BC= length ; DE= start ; (IX+15)=exec ; On exit, CS= save successful ; NC= save failed - BREAK pressed or Bad filename ; FSSAVE CALL FSPARSE ; Parse filename, build header RET NC ; Bad filename RET Z ; Null filename CP "T" JP NZ,MDSAVE ; Save to microdrive ; ; Call SVBYTES catching error ; --------------------------- SVBYTES LD HL,(ERRSP) ; Save error handler PUSH HL CALL SVBYT2 SVDONE POP HL LD (ERRSP),HL RET SVBYT2 LD (ERRSP),SP PUSH DE ; Stack data start address JP &0984 ; Save header and data ; FSCAT - Catalog files ; --------------------- ; On entry, IX=>header workspace, at least 17 bytes ; HL=>filename, terminated with ; On exit, Tape catalog terminated by pressing a key ; FSCAT CALL FSGETDR ; Parse drive specifier CP "T" JP NZ,MDCAT ; Catalog microdrive FSCAT1 LD C,3 ; Three columns FSCATLP PUSH IX PUSH BC LD DE,17 ; DE=length XOR A ; Header block SCF ; Load CALL LDBYTES ; Load a header block POP BC POP IX BIT 5,(IY+1) JR NZ,FSCATX ; Exit if key pressed JR NC,FSCATLP ; Get another header PUSH IX LD (IY+82),255 ; Prevent Scroll? LD B,10 FSCAT4 INC IX LD A,(IX+0) ; Get character RST 16 ; Print it DJNZ FSCAT4 POP IX DEC C JR Z,FSCAT1 LD A,32 RST 16 JR FSCATLP FSCATX LD A,8 ; Print tidy newline RST 16 LD A,13 RST 16 RET ; FSDRIVE - Select drive ; ---------------------- ; On entry, IX=>header workspace, at least 17 bytes ; HL=>drive pathname ; On exit, CS= drive set ok ; NC= bad drive character ; FSINIT LD A,"T" ; Default to 'T' JR FSSETDR FSDRIVE CALL FSGETDR ; Parse drive RET NC ; Bad filename FSSETDR LD (FSDRV),A ; Set drive SCF ; CS=Ok RET ; FSGETDR - Parse drive specifier ; ------------------------------- ; On entry, IX=>header workspace ; HL=>filename ; Allows D D: D:xxx ; On exit, As for FSPARSE ; FSGETDR CALL FSSKIP ; Skip spaces, A=first char CP "!" ; Check for no params LD A,(HL) ; Get second char DEC HL ; Step back to start JR C,FSPARSE ; - get current drive CP ":" JR Z,FSPARSE ; D: - parse drive LD A,(HL) ; Get first char back CP "@" RET C ; <'@', CS=Ok AND &DF ; Force to upper case SCF ; CS=Ok RET ; FSPARSE - Parse filename, build header ; -------------------------------------- ; On entry, IX=>header workspace ; HL=>filename ; DE= start ; BC= length ; A = type ; On exit, IX=>header ; A = drive/device ; EQ= null filename ; NC= bad filename ; DE,BC preserved ; HL corrupted ; FSPARSE LD (IX+0),A ; File type PUSH BC PUSH IX FSPAR1 CALL FSSKIP ; Skip spaces LD C,A ; C=drive LD A,(HL) INC HL CP ":" JR Z,FSPAR2 ; Use this drive LD BC,(FSDRV) ; C=current drive DEC HL ; Step back to start DEC HL FSPAR2 BIT 6,C JR Z,FSPAR3 RES 5,C ; Ensure upper case FSPAR3 LD B,10 ; 10 characters FSPARLP LD A,(HL) CP "!" JR C,FSPAR4 ; End of filename INC HL INC IX LD (IX+0),A DJNZ FSPARLP LD A,(HL) CP "!" ; CS+NE=ok, NC=bad JR FSPAROK FSPAR4 INC IX LD (IX+0),32 ; Pad with spaces DJNZ FSPAR4 LD A,(IX-9) CP " " ; EQ=null filename, NC=ok CCF ; EQ=null filename, CS=ok FSPAROK LD A,C ; A=drive POP IX POP BC LD (IX+14),D ; Start LD (IX+13),E LD (IX+12),B ; Length LD (IX+11),C RET FSSKIP LD A,(HL) INC HL CP 32 JR Z,FSSKIP ; Skip spaces RET ; A=char, HL=>next ; Microdrive routines ; =================== ; MDCAT ; ----- ; On entry, A =drive '1'-'8' ; IX=>Parsed header MDCAT PUSH AF ; Save drive number LD B,&CF ; B='CAT' LD HL,MDHDR3 ; HL=>"0"... AND A ; NC=no filename CALL MDSETUP ; Set up Int#1 command LD HL,-5 ADD HL,DE ; Point to drive number POP AF LD (HL),A ; Store drive number INC HL ; HL=>end of line JR MDCOMMAND ; MDLOAD ; ------ ; On entry, A =drive '1'-'8' ; IX=>Parsed header MDLOAD LD B,&EF ; A= 'LOAD' CALL MDINIT ; Set up Int#1 command LD HL,-10 ADD HL,DE ; HL=>load address LD A,(HL) INC HL OR (HL) ; EQ=use file's address INC HL JR NZ,MDCOMMAND LD DE,-7 ; HL=>terminate address ADD HL,DE JR MDCOMMAND ; MDSAVE ; ------ ; On entry, A =drive '1'-'8', Cy=C ; IX=>Parsed header MDSAVE LD B,&F8 ; A= 'SAVE' CALL MDINIT ; Set up Int#1 command EX DE,HL ; HL=>end of command ; fall through to... ; ; Execute a Interface 1 command at ELINE ; -------------------------------------- ; ELINE=>command, HL+1=><80> to be put in MDCOMMAND INC HL CALL &16B3 ; Set end of line and WORKSP LD HL,(ERRSP) ; Save error handler PUSH HL CALL MDWAKE ; Set ERRSP, wake up Interface 1 SET 2,(IY+124) ; Ensure ERRSP used for errors SET 7,(IY+1) ; Set 'Not syntax checking' LD (IY+10),1 ; Set ZXBasic statement 1 LD (IY+0),&FF ; No error CALL MDCALL ; Execute command LD A,(23610) ; Get ERRNR INC A ; EQ=ok, NE=error EX AF,AF' ; Can't PUSH through restore CALL MDRESTORE ; Clear workspace EX AF,AF' MDCMD2 POP HL LD (ERRSP),HL ; Restore ERRSP CP 1 ; C=ok, NC=error EI RET MDWAKE LD (ERRSP),SP ; Set up error handler RST &08 ; Wake up Interface 1 DEFB &31 RET MDCALL JP &1B8A ; Call LINERUN to execute code MDRESTORE RES 1,(IY+124) ; Prevent autoload trap LD DE,MDRETRN ; Disciple/PlusD return LD HL,&0046 ; Interface 1 part of error handler LD (&5CED),HL ; Reclaim channels, turn off drives, RST &08 ; reset ERR_NR DEFB &32 ; Call via vector MDRETRN RET ; Interface 1 returns via ERRSP ; Initialise microdrive call ; -------------------------- ; Enter here for filename ; B=command, A=drive, HL=>command MDINIT SCF ; CS=filename needed LD HL,MDHDR1 ; HL=> *"m";"... MDSETUP LD C,7 ; prepare to copy *"m";VAL" MDSET1 EX DE,HL LD HL,(23641) ; HL=>workspace LD (HL),B ; Insert command EX DE,HL INC DE LD B,0 ; Copy *"m";VAL" or "m";VAL" LDIR ; or VAL"0";"" RET NC ; No filename LD (DE),A ; Drive character INC DE INC HL LD C,3 ; Copy ";" LDIR PUSH IX LD B,10 MDSETLP INC IX LD A,(IX) CP "!" JR C,MDSETAD LD (DE),A INC DE DJNZ MDSETLP MDSETAD POP IX AND A ; Carry=0 MDSET2 LD BC,6 ; Copy "CODE0<0e><00><00> LDIR CALL MDADDR CALL MDADDR CCF JR C,MDSET2 ; Do twice RET MDADDR LD A,(IX+13) JR NC,MDADD2 LD A,(IX+9) MDADD2 LD (DE),A INC IX INC DE RET ; Microdrive command data ; ----------------------- MDHDR1 DEFB &2A,&22,&6D,&22,&3B ; *"m"; MDHDR3 DEFB &B0,&22,&30,&22,&3B,&22 ; VAL"1";" DEFB &22,&AF,&30,&0E,&00,&00 ; "CODE0<0e><00><00> DEFB &00,&2C,&30,&0E,&00,&00 ; <00>,0<0e><00><00> FSDRV DEFB "T" ; Current drive