;Assembly of Challenger 2.10 ;Greg Cook, 15 October 2024 ;This is a BeebAsm assembly source file. ; ; https://github.com/stardot/beebasm ; ;Most symbols copied from matching code in Acorn DNFS 3.00, ;as landmarks. ; ; https://github.com/stardot/AcornDNFSv300/ ; ;Comments by Greg Cook. Comments beginning with capital letters ;are entry points. [D] marks differences from Opus DDOS 3.46. ;Disc transfer level 1 is eliminated in Challenger; L2 calls L0. ;Define any of the following symbols on the BeebAsm command line ;to select build options. ;For example: ; beebasm -i chal210.asm.txt -D _M1770C -D _MASTER ;Floppy drive controller selection _O2791C =? 0 ;Opus WD 2791 controller _O2793C =? 0 ;Opus WD 2793 controller _O1770C =? 0 ;Opus WD 1770 controller _W1770C =? 0 ;Watford WD 1770 controller _A1770C =? 0 ;Acorn B/B+ WD 1770 controller _M1770C =? 0 ;Acorn Master WD 1770 controller _PG400C =? 0 ;Slogger Pegasus 400 controller _CHALLC =? 0 ;Opus Challenger 3-in-1 unit (default) ;Usability options _MASTER =? 0 ;Add service calls $25, $26, $27 ;Pass unrecognised *commands to the LIBFS via FSC 11 ;Based on the Challenger 2.00 filing system ROM released by ;Slogger Software, with patches by Tom Seddon. ;Differences from Challenger 200B: ;- Fix parsing of file specifications "*" and ".*" ;- Fix *INFO, *EX, *VERIFY, and EOF warning flag behaviour ;- Preserve stack in *FDCSTAT ;- Preserve open files in *FORMAT ;- Ignore *BUILD if Challenger unit not fitted ;- Install empty BASIC program properly after copying ;- *RUN, */ enter executables with Acorn DFS-like registers ;- OSFILE calls 7,9,10,11 (create/stamp/save file) implemented ;- OSFILE $FF (load file) releases NMI and returns A=1 ;- Transfer files >64 KiB between RAM disc and coprocessor ;- OSFILE 4 sets attribute from control block ;- Consider only b7,b6,b3 of A on entry to OSFIND. Raise ; "Not found" error iff b6,b3 are both set and no file ; can be opened. ;- OSARGS A=3, Y>0 (set EXT) implemented ;- Preserve RAM disc on OSWORD $7F call $5F (verify data) ;- Return special registers $12, $1A in OSWORD $7F ;- Add *EX to command table ;The functions of the Chafix module are built into this assembly. ;A ROM assembled from this source allows a simple RISC OS program ;running on an attached SPROW ARM7TDMI coprocessor to read and write ;files using the ANSI C Library and the built-in ARM Tube OS. ; ; http://regregex.bbcmicro.net/#prog.dfsfix ;Floppy disc parameters sdspt = $0A ;10 sectors per track in SD ddspt = $12 ;18 sectors per track in DD badtrk = $50 ;80 cylinders maximum on disc volums = $08 ;number of volume catalogues on track 0 skew = $02 ;track skew during formatting in single density sdgap5 = $10 ;number of bytes in gap 5, SD (excl. sync sequence) sdgap1 = $0B ;number of bytes in gap 1, SD (excl. sync sequence) sdgap3 = $10 ;number of bytes in gap 3, SD (excl. sync sequence) ddgap5 = $28 ;number of bytes in gap 5, DD (excl. sync sequence) ddgap1 = $19 ;number of bytes in gap 1, DD (excl. sync sequence) ddgap3 = $16 ;number of bytes in gap 3, DD (excl. sync sequence) ;Floppy drive controller parameters IF _O2791C sense = $FF ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $00 ;sense of FDC status register compared to WD 2793 hdload = $08 ;sense of Type I command bit 3; $00=spin up $08=load head latch0 = $00 ;latch value to select drive 0, single density latch1 = $01 ;latch XOR mask to select drive 1 (unit 1 side 0) latch2 = $02 ;latch XOR mask to select drive 2 (unit 0 side 1) latch6 = $00 ;latch XOR mask to select drive 6 (unit 2 side 0) latchd = $40 ;latch XOR mask to select double density latchr = $00 ;latch value to reset FDC ELIF _O2793C sense = $00 ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $00 ;sense of FDC status register compared to WD 2793 hdload = $08 ;sense of Type I command bit 3; $00=spin up $08=load head latch0 = $00 ;latch value to select drive 0, single density latch1 = $01 ;latch XOR mask to select drive 1 (unit 1 side 0) latch2 = $02 ;latch XOR mask to select drive 2 (unit 0 side 1) latch6 = $00 ;latch XOR mask to select drive 6 (unit 2 side 0) latchd = $40 ;latch XOR mask to select double density latchr = $00 ;latch value to reset FDC ELIF _O1770C sense = $00 ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $80 ;sense of FDC status register compared to WD 2793 hdload = $00 ;sense of Type I command bit 3; $00=spin up $08=load head latch0 = $00 ;latch value to select drive 0, single density latch1 = $01 ;latch XOR mask to select drive 1 (unit 1 side 0) latch2 = $02 ;latch XOR mask to select drive 2 (unit 0 side 1) latch6 = $00 ;latch XOR mask to select drive 6 (unit 2 side 0) latchd = $40 ;latch XOR mask to select double density latchr = $00 ;latch value to reset FDC ELIF _W1770C sense = $00 ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $80 ;sense of FDC status register compared to WD 2793 hdload = $00 ;sense of Type I command bit 3; $00=spin up $08=load head latch0 = $01 ;latch value to select drive 0, single density latch1 = $04 ;latch XOR mask to select drive 1 (unit 1 side 0) latch2 = $02 ;latch XOR mask to select drive 2 (unit 0 side 1) latch6 = $00 ;latch XOR mask to select drive 6 (unit 2 side 0) latchd = $01 ;latch XOR mask to select double density latchr = $00 ;latch value to reset FDC ELIF _A1770C sense = $00 ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $80 ;sense of FDC status register compared to WD 2793 hdload = $00 ;sense of Type I command bit 3; $00=spin up $08=load head latch0 = $29 ;latch value to select drive 0, single density latch1 = $03 ;latch XOR mask to select drive 1 (unit 1 side 0) latch2 = $04 ;latch XOR mask to select drive 2 (unit 0 side 1) latch6 = $00 ;latch XOR mask to select drive 6 (unit 2 side 0) latchd = $08 ;latch XOR mask to select double density latchr = $00 ;latch value to reset FDC ELIF _M1770C sense = $00 ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $80 ;sense of FDC status register compared to WD 2793 hdload = $00 ;sense of Type I command bit 3; $00=spin up $08=load head latch0 = $25 ;latch value to select drive 0, single density latch1 = $03 ;latch XOR mask to select drive 1 (unit 1 side 0) latch2 = $10 ;latch XOR mask to select drive 2 (unit 0 side 1) latch6 = $09 ;latch XOR mask to select drive 6 (unit 2 side 0) latchd = $20 ;latch XOR mask to select double density latchr = $00 ;latch value to reset FDC ELIF _PG400C sense = $00 ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $80 ;sense of FDC status register compared to WD 2793 hdload = $00 ;sense of Type I command bit 3; $00=spin up $08=load head latch0 = $39 ;latch value to select drive 0, single density latch1 = $03 ;latch XOR mask to select drive 1 (unit 1 side 0) latch2 = $04 ;latch XOR mask to select drive 2 (unit 0 side 1) latch6 = $00 ;latch XOR mask to select drive 6 (unit 2 side 0) latchd = $08 ;latch XOR mask to select double density latchr = $00 ;latch value to reset FDC ELSE sense = $00 ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $80 ;sense of FDC status register compared to WD 2793 hdload = $00 ;sense of Type I command bit 3; $00=spin up $08=load head latch0 = $32 ;latch value to select drive 0, single density latch1 = $06 ;latch XOR mask to select drive 1 (unit 1 side 0) latch2 = $01 ;latch XOR mask to select drive 2 (unit 0 side 1) latch6 = $0A ;latch XOR mask to select drive 6 (unit 2 side 0) latchd = $20 ;latch XOR mask to select double density latchr = $00 ;latch value to reset FDC ENDIF ;Challenger FS parameters cbtsiz = $08 ;max. number of entries in copy buffer table l3tris = $03 ;number of disc operation attempts at level 3 idtris = $05 ;number of Read ID attempts (must be odd) bpmove = $0001 ;number of sectors to extend file by in OSBPUT ;(must be >0; 1 guarantees success unless disc full) srom = $0E ;default *OPT 9 setting; ROM slot to page in ;during disc operations dftbid = $01 ;Tube claimant ID for Disc Filing System ;Expansion memory layout runofs = $0612 ;byte address of RLE tables in JIM space jaux = $0000 ;page address of auxiliary workspace jmain = $0001 ;page address of main workspace jcat = $0002 ;page address of current DFS catalogue jshift = $0002 ;page address of file shift buffer shftsz = $0002 ;no. of pages in file shift buffer jslots = $0004 ;page address of open file sector buffers jbuild = $0009 ;page address of *BUILD line input buffer jdrv4 = $000A ;page address of DFS RAM disc 4 d4size = $03F5 ;no. of pages in DFS RAM disc 4 jdrv5 = $0400 ;page address of DFS RAM disc 5 d5size = $03FF ;no. of pages in DFS RAM disc 5 ;Constants defined by ChADFS fstlba = $0000 ;logical block address to write ADFS free space table fstsiz = $0200 ;byte size of ADFS free space table fstadr = $C000 ;memory address of free space table (in HAZEL) rootlb = $0002 ;logical block address to write ADFS root directory rootsz = $0500 ;byte size of ADFS root directory rootad = $C900 ;memory address of root directory (in HAZEL) ;Variables in zero-page reserved NMI area, $00A0..$00A7 sectog = $00A0 ;temporary number of sectors until end of track rtxszl = $00A0 ;LSB number of bytes to transfer to RAM disc or current track frmrun = $00A0 ;number of bytes in current run while preparing/executing format rtxszh = $00A1 ;MSB number of bytes to transfer to RAM disc or current track szcode = $00A1 ;sector size code while preparing format fsectg = $00A2 ;number of sectors remaining while preparing format txsizl = $00A3 ;LSB total number of bytes to transfer to floppy disc romofs = $00A3 ;temporary pointer to repeating sector block in ROM format table txsizm = $00A4 ;2MSB total number of bytes to transfer to floppy disc runptl = $00A4 ;LSB run length table pointer while preparing format txsizh = $00A5 ;MSB total number of bytes to transfer to floppy disc runpth = $00A5 ;LSB run length table pointer while preparing format nmiusr = $00A6 ;2 bytes; user data address during transfer fsectk = $00A6 ;number of sectors per track while preparing format chrnsz = $00A7 ;size of CHRN table while preparing format ;Variables in utility workspace, $00A8..$00AF linnol = $00A8 ;LSB line number in *BUILD, *LIST or file offset in *DUMP cbcont = $00A8 ;number of entries in copy buffer file table nlflag = $00A8 ;flag for printing newlines in *CAT [D]linno now one byte action = $00A8 ;2 bytes; action address of *command nice = $00A8 ;b6=1 will accept shorter allocation than requested ;while creating file (b6=0 OSFILE, b6=1 OSFIND) ;[BUG] FS variable clobbers command workspace linnoh = $00A9 ;LSB line number in *BUILD, *LIST or file offset in *DUMP swaps = $00A9 ;b7=Copying between different discs in same drive (swapping) movcat = $00A9 ;catalogue offset of file to extend while moving files ;[BUG] FS variable clobbers command workspace lineln = $00AA ;length of input line in *BUILD tindrv = $00AA ;$00=Source disc in drive $80=Destination disc in drive catqua = $00AA ;=0 listing current directory during *CAT >0 listing other dirs tramp = $00AA ;4 bytes; trampoline to read byte of *command and keyword table attrib = $00AA ;file attribute mask in *ACCESS $00=unlocked $80=locked movlen = $00AA ;number of files in catalogue while moving files ;[BUG] FS variable clobbers command workspace inputl = $00AA ;LSB hexadecimal word input by user, or tentative volume size lstmsk = $00AB ;mask to enable line number printing $00=*TYPE $FF=*LIST bldfh = $00AB ;handle of output file in *BUILD catptr = $00AB ;offset of catalogue entry of file being listed in *CAT inputh = $00AB ;MSB hexadecimal word input by user, or tentative volume size dumpct = $00AC ;ASCII column counter in *DUMP bldtbl = $00AC ;5 bytes; OSWORD 0 control block in *BUILD zdirln = $00AC ;zero-page copy of number of files in catalogue during *CAT dmpstk = $00AD ;stack pointer in *DUMP movret = $00AE ;2 bytes; caller's address while producing/consuming file map ;[BUG] FS variable clobbers command workspace strret = $00AE ;2 bytes; pointer to error message string or VDU sequence ;Variables in filing system scratch workspace, $00B0..$00BF ;Variables in filing system persistent workspace, $00C0..$00CF blkptr = $00B0 ;2 bytes; pointer to OSFILE or OSWORD $7F control block evtptr = $00B0 ;2 bytes; pointer to extended vector table read from OSBYTE $A8 headlo = $00B0 ;LSB number of free sectors after file (MSB in X) inscat = $00B0 ;offset of insertion point while creating catalogue entry namptr = $00B0 ;2 bytes; pointer to filename to be compared with open filenames vslbal = $00B0 ;LSB calculated LBA of new random access ;(in _BUGFIX; was offset of buffer from start of file) mapctr = $00B0 ;count of file map entries to test while testing available space gbctr = $00B0 ;counter for copying filename in OSGBPB 8 cnfofs = $00B0 ;offset into drive mapping table during *CONFIG muland = $00B0 ;multiplicand divenl = $00B0 ;LSB dividend asklo = $00B0 ;LSB requested size of volume during *FORMAT, *VOLGEN sklbah = $00B1 ;MSB LBA of largest slack space on volume (big-endian) divenh = $00B1 ;MSB dividend askhi = $00B1 ;LSB requested size of volume during *FORMAT, *VOLGEN o7fcmd = $00B2 ;b5..0=OSWORD $7F command to compare with commands in table sklbal = $00B2 ;MSB LBA of largest slack space on volume namofs = $00B2 ;offset of filename to compare with names of open handles mapsp = $00B2 ;pointer to file map in stack page divorl = $00B2 ;LSB divisor freelo = $00B2 ;LSB number of sectors available for allocation to volumes trktot = $00B2 ;total number of tracks assigned to volumes bootfs = $00B3 ;boot option in Y on entry to service call $03 lokcat = $00B3 ;offset of filename to be updated in catalogue seqvol = $00B3 ;physical volume of file while comparing names of open handles stra = $00B3 ;saved A register on entry to print string/append error message divorh = $00B3 ;MSB divisor freehi = $00B3 ;MSB number of sectors available for allocation to volumes noop = $00B4 ;startup options while initialising private page (unused) bitwrk = $00B4 ;reason code in A on entry to OSFIND, for testing by BIT dcbofs = $00B4 ;channel workspace offset while comparing names of open handles gbadr = $00B4 ;2 bytes; pointer to user's OSGBPB control block fscx = $00B5 ;saved X register on entry to FSC fscno = $00B5 ;$0B=FSC handler is serving FSC 11 dcbsr = $00B5 ;shift register containing channel open flags zgbptr = $00B6 ;4 bytes; temporary copy of pointer from OSGBPB control block frmclo = $00B7 ;command line argument offset in *FORMAT restsp = $00B7 ;stack pointer to restore on command restart abrtsp = $00B8 ;stack pointer to restore on abort or command exit gbusr = $00B8 ;2 bytes; pointer to user memory in OSGBPB hlpcot = $00B8 ;counter of *HELP entries to print dopint = $00B9 ;=0 disc operation is uninterruptible >0 interruptible track = $00BA ;logical track number of disc operation txlbah = $00BA ;MSB starting LBA of disc operation (big-endian) txlbal = $00BB ;LSB starting LBA of disc operation (big-endian) sector = $00BB ;starting sector number of disc operation gentrk = $00BB ;first free track while assigning volumes skewit = $00BB ;running track skew counter while formatting work = $00BC ;18 bytes; rearranged copy of OSFILE control block plus temporaries: lodcat = work +$0000 ;temp cat offset in OSFILE $FF zfrpg = work +$0001 ;address of lowest free/used page in copy buffer wrkcat = work +$0002 ;load/exec/length/start sector in catalogue format lodlo = work +$0002 ;LSB load address in OSFILE page = work +$0002 ;pointer to OSHWM (PAGE) while writing empty BASIC program lodhi = work +$0003 ;3MSB load address in OSFILE tries = work +$0003 ;retry counter at data transfer level 3 exelo = work +$0004 ;LSB exec address in OSFILE ceillo = work +$0004 ;LSB size in sectors of maximum available allocation ftraks = work +$0004 ;number of tracks on disc in *FORMAT, *VERIFY, *VOLGEN exehi = work +$0005 ;3MSB exec address in OSFILE ceilhi = work +$0005 ;MSB size in sectors of maximum available allocation volidx = work +$0005 ;volume index in *VOLGEN lenlo = work +$0006 ;LSB file length in OSFILE strtlo = work +$0006 ;LSB start address in OSFILE 0 icatmp = work +$0006 ;offset while testing for space to create catalogue entry excesl = work +$0006 ;LSB sector excess: requested file allocation minus space available; ;headroom when negative mapvol = work +$0006 ;physical volume of open file while moving open files mapdfl = work +$0006 ;difference between LBAs / move distance while moving open files zfrsiz = work +$0007 ;zero-page copy of size of user memory lenhi = work +$0007 ;2MSB file length in OSFILE strthi = work +$0007 ;3MSB start address in OSFILE 0 excesh = work +$0007 ;MSB sector excess: requested file allocation minus space available; ;headroom when negative movofs = work +$0007 ;channel workspace offset of open file being extended ;(to avoid moving it while shifting following files down) ;=0 while shifting up to ensure open file is included lbahi = work +$0008 ;MSB LBA in OSFILE endlo = work +$0008 ;LSB end address in OSFILE 0 allocl = work +$0008 ;LSB length of file in sectors lbaxlo = work +$0008 ;LSB LBA of end of previous file (little-endian) slackl = work +$0008 ;LSB size of slack space before current file, in sectors prodl = work +$0008 ;LSB product of multiplication lbalo = work +$0009 ;LSB LBA in OSFILE endhi = work +$0009 ;3MSB end address in OSFILE 0 alloch = work +$0009 ;MSB length of file in sectors lbaxhi = work +$0009 ;MSB LBA of end of previous file (little-endian) slackh = work +$0009 ;MSB size of slack space before current file, in sectors prodh = work +$0009 ;MSB product of multiplication lenhl = work +$000A ;MSB length of file while creating catalogue entry todolo = work +$000A ;LSB length in sectors of file being copied mfreel = work +$000A ;LSB total number of free sectors in volume todohi = work +$000B ;MSB length in sectors of file being copied mfreeh = work +$000B ;MSB total number of free sectors in volume wrknam = work +$000B ;current filename mheadl = work +$000C ;LSB sector headroom: total free sectors minus sector excess ;i.e. free sectors remaining after files moved srclo = work +$000C ;LSB LBA of next sector to copy from source mheadh = work +$000D ;MSB sector headroom: total free sectors minus sector excess ;i.e. free sectors remaining after files moved srchi = work +$000D ;MSB LBA of next sector to copy from source dstlo = work +$000E ;LSB LBA of next sector to copy to destination dsthi = work +$000F ;MSB LBA of next sector to copy to destination mvuplo = work +$0010 ;LSB sector excess to be absorbed by shifting previous files up cpycat = work +$0010 ;offset of catalogue entry in *COMPACT mvuphi = work +$0011 ;MSB sector excess to be absorbed by shifting previous files up qualif = $00CE ;current directory fdrive = $00CF ;current drive ;Zero-page system variables worda = $00EF ;reason code in A on entry to OSWORD wordx = $00F0 ;LSB control block address in X on entry to OSWORD wordy = $00F1 ;MSB control block address in Y on entry to OSWORD linptr = $00F2 ;2 bytes; pointer to character array in GSINIT/Svc $25 romid = $00F4 ;slot number of ROM currently paged in escflg = $00FF ;b7=0 normal, b7=1 ESCAPE pressed ;System reserved memory stack = $0100 ;hardwired 6502 stack page errbuf = $0100 ;error trampoline so a language ROM can print the message vtab2 = $0212 ;FILEV, first FS vector claimed by DFS fscvec = $021E ;FSCV vector to control/shut down current FS taddrl = $0406 ;Set up address for Tube intnmi = $0D00 ;NMI service routine priptr = $0DF0 ;table of private pages to each ROM ;Conventional Acorn DFS shared workspace ddirlo = $0E00 ;memory address of catalogue sector 0 under Acorn DFS ddirhi = $0F00 ;catalogue sector 1 presented here when *ENABLE CAT selected ;Expansion hardware fred = $FC00 ;page of addresses for external MMIO devices jpghi = fred +$00FE ;MSB expansion RAM paging register jpglo = fred +$00FF ;LSB expansion RAM paging register ;Expansion memory jim = $FD00 ;when testing for expansion RAM or copying pages base = jim +$0000 ;Challenger workspace starts here auxws = base +$0000 ;auxiliary workspace at $000000..$0000FF config = auxws +$0000 ;*CONFIG mapping, logical drives 0..7 to physical drive (b2..0) adfsmp = config+$0008 ;ChADFS mapping, logical drives 0..7 to physical drive (b2..0) cbtbl = auxws +$0011 ;copy buffer table, 8 entries * 23 bytes cbcat = cbtbl +$0000 ;cf. BE..C5 Load/exec/length/start sector of file in copy buffer cbtxsz = cbcat +$0007 ;size of current transfer cbflag = cbcat +$0008 ;flag byte b7=source read incomplete b6=dest. file part written cbname = cbflag+$0001 ;cf. C7..CE Name and directory of file in copy buffer cblnlo = cbname+$0008 ;LSB length in sectors of file being copied cblnhi = cblnlo+$0001 ;MSB length in sectors of file being copied cbslbl = cblnhi+$0001 ;LSB LBA of next sector to copy from source volume, little-endian cbslbh = cbslbl+$0001 ;MSB LBA of next sector to copy from source volume, little-endian cbdlbh = cbslbh+$0001 ;LSB LBA of next sector to copy to destination volume, big-endian cbdlbl = cbdlbh+$0001 ;MSB LBA of next sector to copy to destination volume, big-endian cbtble = cbdlbl+$0001 ;end of copy buffer table entry voltks = auxws +$00CD ;first track of data area of volumes A..H volszh = auxws +$00D5 ;MSB sizes assigned to volumes A..H in *VOLGEN (big-endian) volszl = auxws +$00D6 ;LSB sizes assigned to volumes A..H in *VOLGEN (big-endian) mainws = base +$0000 ;main workspace at $000100..$0001FF senti1 = mainws+$0000 ;first workspace sentinel $65,$E5=workspace valid ;b7=Challenger is current FS ;$000101..$0001A0 reserved for channel workspace (see below) chrn = mainws+$0061 ;$000161..$0001A9 = table of sector headers during *FORMAT dosram = mainws+$00A1 ;18 bytes; copy of OSFILE/OSGBPB control block ldlow = mainws+$00B3 ;4 bytes; load address passed to OSFILE; Tube tx addr hiwork = ldlow +$0002 ;8 bytes; high words of load/exec/start/end to OSFILE exlow = hiwork+$0000 ;overlaps ldlow; exec address passed to OSFILE strthl = hiwork+$0004 ;2MSB start address in OSFILE 0 strthh = hiwork+$0005 ;MSB start address in OSFILE 0 endhl = hiwork+$0006 ;2MSB end address in OSFILE btemp = mainws+$00BE ;2 bytes; pointer to user's OSGBPB control block renvol = mainws+$00C0 ;source physical volume in *RENAME catofs = mainws+$00C2 ;offset of catalogue entry, 0..$F0, multiple of 8 stasha = mainws+$00C3 ;saved A register on entry to OSBPUT stashx = mainws+$00C4 ;saved X register on entry to OSBPUT, OSBGET stashy = mainws+$00C5 ;saved Y register on entry to OSBPUT, OSBGET defqua = mainws+$00C6 ;default (CSD) directory character defdsk = mainws+$00C7 ;default (CSD) drive (b3..0) and volume (b6..4) libqua = mainws+$00C8 ;library directory character libdsk = mainws+$00C9 ;library drive (b3..0) and volume (b6..4) fdriv = mainws+$00CA ;source volume in *BACKUP, *COPY tdriv = mainws+$00CB ;destination volume in *BACKUP, *COPY tubflg = mainws+$00CC ;b7=Tube data transfer notube = mainws+$00CD ;$00=Tube coprocessor present $FF=Tube absent (inverted MOS flag) dcbmap = mainws+$00CE ;channel open flags dcbbit = mainws+$00CF ;channel open bit mask for current open file dcby = mainws+$00D0 ;channel workspace pointer for current open file seqwb = mainws+$00D2 ;offset of catalogue entry of current open file seqwc = mainws+$00D3 ;not used in _BUGFIX frpage = mainws+$00D5 ;MSB of OSHWM; lowest page number of user memory hipage = mainws+$00D6 ;MSB of HIMEM; 1 + highest page number of user memory frsize = mainws+$00D7 ;number of pages of user memory; = hipage - frpage wildch = mainws+$00D8 ;$23=wildcard characters allowed in filename $FF=no wildcards monflg = mainws+$00D9 ;*OPT 1 monitor 0=verbose $FF=quiet ctemp = mainws+$00DA ;transfer direction 0=writing from memory 1=reading to memory tumflg = mainws+$00DB ;$00=transferring to/from host $FF=transferring to/from Tube catdrv = mainws+$00DC ;drive and volume of catalogue in pages 2..3; b7=catalogue invalid nmiflg = mainws+$00DD ;NMI ownership flag b7=we own NMI prenmi = mainws+$00DE ;previous owner of NMI area enaflg = mainws+$00DF ;*ENABLE counter 1=*ENABLE just called 0=current command enabled ;$FF=current command not enabled qtemp = mainws+$00E0 ;2 bytes; action address in OSGBPB srcopt = mainws+$00E0 ;b7..4=boot option of source volume in *BACKUP linadr = mainws+$00E2 ;2 bytes; offsets of command line start and tail; ;pointer to arguments of *RUN, */ command fcbadr = mainws+$00E4 ;2 bytes; pointer to user's OSFILE control block restrt = mainws+$00E6 ;command restart action address txcall = mainws+$00E9 ;data transfer call number 0=read data 1=write data ;3=write deleted data 4=verify data b7=data address in JIM space spregs = mainws+$00EA ;5 bytes; special registers 0..4 t40flg = spregs+$0000 ;$00=single stepping b6=double stepping b7=automatic stepping sectrk = spregs+$0001 ;number of sectors per track $0A=single density $12=double density voltrk = spregs+$0002 ;first track of current volume (=0 unless set from disc catalogue) denflg = spregs+$0003 ;$00=single density b6=double density b7=automatic density savrom = spregs+$0004 ;*OPT 9 number of paged ROM slot to page in during disc operations head = mainws+$00EF ;3 bytes; physical track number under heads on drive 0/2, 1/3, 6/7 speed = mainws+$00F2 ;track stepping rate in WD 1770 format 0=fast..3=slow fdstat = mainws+$00F3 ;status of last FDC command, reported by *FDCSTAT enacat = mainws+$00F4 ;b7=*ENABLE CAT emulate Acorn DFS's main memory use dscszh = mainws+$00F5 ;MSB size of disc in sectors (big-endian) dscszl = mainws+$00F6 ;LSB size of disc in sectors (big-endian) cblast = mainws+$00F7 ;offset of last entry of copy buffer file table, n*$17 ($00..A1) cbcurs = mainws+$00F8 ;offset of current entry of copy buffer file table packed = mainws+$00F9 ;packed drive parameters of source, destination volume (Y=0 or 2) cpvltk = mainws+$00FA ;first track of source, destination volume (Y=0 or 2) senti2 = mainws+$00FD ;second workspace sentinel $E5=workspace valid ramden = mainws+$00FE ;RAM disc density flag b6=double density chadfs = mainws+$00FF ;b6=ChADFS is current filing system seqmap = base -$001F ;channel workspace at $000101..$0001A0, accessed at offsets $20..$A0 seqcat = seqmap+$0000 ;name of open file (even offsets) ;load/exec/length/start sector in catalogue format (odds) seqll = seqcat+$0009 ;LSB of file length in catalogue entry seqlm = seqcat+$000B ;2MSB of file length in catalogue entry seqrdo = seqcat+$000C ;seventh character of filename; b7=channel read-only seqlh = seqcat+$000D ;top bits exec/length/load/LBA in catalogue entry seqlok = seqcat+$000E ;directory character of filename; b7=file locked seqloc = seqcat+$000F ;LSB of starting LBA in catalogue entry seqpl = seqmap+$0010 ;LSB of sequential pointer (PTR) seqpm = seqmap+$0011 ;2MSB of sequential pointer seqph = seqmap+$0012 ;MSB of sequential pointer seqbuf = seqmap+$0013 ;packed drive parameters b7=auto density b6=double density seqlla = seqmap+$0014 ;LSB of open file's extent (EXT) seqlma = seqmap+$0015 ;2MSB of open file's extent seqlha = seqmap+$0016 ;MSB of open file's extent seqflg = seqmap+$0017 ;channel flags b7=buffer contains PTR b6=buffer changed ;b5=EXT changed b4=EOF warning given ;NB seqmap+$0018 'seqel' always contains zero seqem = seqmap+$0019 ;LSB of number of sectors allocated to file seqeh = seqmap+$001A ;MSB of number of sectors allocated to file seqbit = seqmap+$001B ;channel open bit mask corresponding to open file seqdal = seqmap+$001C ;LSB of starting LBA seqdah = seqmap+$001D ;MSB of starting LBA sequal = seqmap+$001E ;first track of volume of open file seqchk = seqmap+$001F ;volume and drive of open file dirlow = base +$0000 ;catalogue of current volume or disc catalogue at $000200..$0003FF dcver = dirlow+$0000 ;disc catalogue version number, $20=DDOS/Challenger dcszhi = dcver +$0001 ;MSB number of sectors on disc surface dcszlo = dcszhi+$0001 ;LSB number of sectors on disc surface dcspt = dcszlo+$0001 ;number of sectors per track dctrks = dcspt +$0001 ;number of tracks on disc surface dcmyst = dctrks+$0001 ;mystery field, always zero dcvtrk = dirlow+$0008 ;first track of data area of volume, 8 volumes (Y=2*n, n=0..7) dcvmst = dcvtrk+$0001 ;mystery per-volume field, always zero catlow = dirlow+$0008 ;first catalogue entry; filename catdun = catlow+$0000 ;b7=catalogue entry listed modify = catlow+$0007 ;directory letter; b7=file locked dirhig = base +$0000 ;second sector of catalogue cycno = dirhig+$0004 ;BCD cycle number, incremented when updating catalogue dirlen = dirhig+$0005 ;number of files in catalogue * 8; pointer to last entry option = dirhig+$0006 ;when accessing option bits cathig = dirhig+$0008 ;first catalogue entry; attributes slots = base +$0000 ;channel sector buffers at $000400..$0008FF runlen = slots +$0000 ;run lengths in RLE formatting table, lower half of each page rundat = slots +$0080 ;data bytes in RLE formatting table, upper half of each page lnbuff = base +$0000 ;OSWORD $00 line buffer in *BUILD ;System hardware sheila = $FE00 ;page of addresses for internal MMIO devices romsw = sheila+$0030 ;ROMSEL paged ROM selection latch reg3 = sheila+$00E5 ;Tube FIFO 3 ;Floppy drive controller IF _O2791C fdc = sheila+$0080 ;base of floppy drive controller registers latch = sheila+$0084 ;floppy drive interface control latch ELIF _O2793C fdc = sheila+$0080 ;base of floppy drive controller registers latch = sheila+$0084 ;floppy drive interface control latch ELIF _O1770C fdc = sheila+$0080 ;base of floppy drive controller registers latch = sheila+$0084 ;floppy drive interface control latch ELIF _W1770C fdc = sheila+$0084 ;base of floppy drive controller registers latch = sheila+$0080 ;floppy drive interface control latch ELIF _A1770C fdc = sheila+$0084 ;base of floppy drive controller registers latch = sheila+$0080 ;floppy drive interface control latch ELIF _M1770C fdc = sheila+$0028 ;base of floppy drive controller registers latch = sheila+$0024 ;floppy drive interface control latch ELIF _PG400C fdc = fred +$00C4 ;base of floppy drive controller registers latch = fred +$00C0 ;floppy drive interface control latch ELSE fdc = fred +$00F8 ;base of floppy drive controller registers latch = fred +$00FC ;floppy drive interface control latch ENDIF fdccmd = fdc +$0000 ;WD 1770 command register (write only) fdcsta = fdc +$0000 ;WD 1770 status register (read only) fdctrk = fdc +$0001 ;WD 1770 track register fdcsec = fdc +$0002 ;WD 1770 sector register fdcdat = fdc +$0003 ;WD 1770 data register ;Acorn MOS system calls gsinit = $FFC2 ;Set up to read string from character array gsread = $FFC5 ;Read character from string osfind = $FFCE ;Open or close a sequential file osbput = $FFD4 ;Write byte to sequential file osbget = $FFD7 ;Read byte from sequential file osrdch = $FFE0 ;Read character from console osasci = $FFE3 ;Write character to console translating CR to newline oswrch = $FFEE ;Write character to console osword = $FFF1 ;Perform various OS functions according to control block osbyte = $FFF4 ;Perform various OS functions according to registers oscli = $FFF7 ;Interpret command line ORG $8000 .lang ;Language entry BRK EQUW $0000 JMP called ;Service entry EQUB $82 ;rom type: service only EQUB copyr-lang ;copyright offset pointer EQUB $21 ;version No. EQUS "Challenger 3" ;title EQUB $00 ;terminator byte EQUS "2.10" ;version string .copyr EQUB $00 ;terminator byte EQUS "(C)2024 Otus" ;copyright string validated by MOS EQUB $00 ;terminator byte .osfscm ;Issue Filing System Call JMP (fscvec) .called ;ROM service CMP #$01 BNE savpri ;Service call $01 = reserve absolute workspace JSR savita ;save AXY JSR L821F ;probe Challenger unit RAM size TAX BEQ L8086 ;if Challenger unit absent then return JSR pgmain ;else page in main workspace JSR LBA00 ;issue Force Interrupt LDA senti1 ;validate first workspace sentinel AND #$7F ;[D]mask off b7=Challenger is current FS CMP #$65 ;[D]compare remainder with valid value $65/E5 BEQ L8053 ;if equal then validate second sentinel LDA #$65 ;else initialise sentinel=$65, b7=0 no FS STA senti1 JSR LAB7E ;initialise Challenger and ChADFS mappings .L8053 LDA #$E5 ;validate second workspace sentinel CMP senti2 BEQ L806D ;if not equal to valid value $E5 STA senti2 ;then initialise second sentinel LDA #$04 ;[D]set current drive = 4 STA fdrive LDX #$02 ;x=2 select drive 4 volume size = $3F5 JSR LAFF8 ;initialise RAM disc catalogue INC fdrive ;current drive = 5 LDX #$03 ;x=3 select drive 5 volume size = $3FF JSR LAFF8 ;initialise RAM disc catalogue .L806D LDA #$FD ;OSBYTE $FD = read/write type of last reset JSR readby ;call OSBYTE with X=0, Y=$FF TXA ;test type of last reset BEQ L8078 ;if A=0 then soft break so skip JSR L82A4 ;else initialise workspace .L8078 JSR L82C8 ;initialise workspace part 2 BIT enacat ;[D]test b7=*ENABLE CAT BPL L8086 ;if enabled TSX ;[D]then return Y=$17 nine pages of workspace LDA #$17 CMP stack+$03,X ;unless a higher ROM needs more BCC L8086 ;in which case exit STA stack+$03,X ;else return Y=$17 .L8086 RTS .savpri CMP #$02 BNE pmsg ;Service call $02 = reserve private workspace JSR pgmain ;page in main workspace BIT enacat ;[D]test b7=*ENABLE CAT BPL L8095 ;if enabled INY ;then reserve two pages of private workspace INY ;pass updated HWM in Y .L8095 RTS .pmsg CMP #$03 BNE chkcom ;Service call $03 = boot JSR pgmain ;page in main workspace STY bootfs ;save boot flag in scratch space JSR savita ;save AXY LDA #$7A ;call OSBYTE $7A = scan keyboard from $10+ JSR osbyte TXA ;test returned key code BMI aboot0 ;if N=1 no key is pressed, so init and boot CMP #$52 ;[D]else if key pressed is not C BNE L8095 ;then exit LDA #$78 ;else register keypress for two-key rollover JSR osbyte .aboot0 .L81EE ;Initialise Chall. and boot default volume LDA bootfs ;get back boot flag (Y on entry to call $3) PHA ;save on stack SEC JSR LAE5E ;print Challenger banner JSR pcrlf ;print newline JSR L8219 ;get Challenger unit type AND #$03 ;extract b1,b0 of A BEQ L8217 ;if Challenger not installed then exit JSR L82C8 ;else initialise workspace part 2 JSR sinit0 ;initialise Challenger FS PLA ;if boot flag was >0 BNE L820B ;then return A=0 to claim call JMP L82F7 ;else examine and boot default volume .L820B ;Return A=0 ; LDA #$00 ;redundant; returns zero poked by sinit0 RTS ;in savita set by pmsg .chkcom CMP #$04 BNE dohelp ;Service call $04 = unrecognised *command JSR pgmain ;page in main workspace JSR savita ;save AXY TSX STX abrtsp ;save stack pointer to restore on abort TYA ;a=offset of *command from GSINIT pointer LDX #initbl JSR wname0 ;search for command in table BCS unkswz ;if not found then exit LDA senti1 ;[D]else test b7=Challenger is current FS BMI L80D7 ;if b7=1 then execute *command JSR tramp ;else get syntax byte from command table BMI unkswz ;if b7=1 restricted command then return .L80D7 JMP (action) ;else execute *command, Y=cmd line tail ptr .dohelp CMP #$09 BNE inifsy ;Service call $09 = *HELP JSR pgmain ;page in main workspace JSR savita ;save AXY LDA (linptr),Y ;test character at start of *HELP string CMP #$0D ;if not CR then *HELP called with keyword BNE L80F4 ;so scan keyword LDX #hlptab LDA #$03 ;3 entries to print JMP help1 ;print *HELP keywords and pass on the call. .L80F4 ;Scan *HELP keyword JSR setupr ;call GSINIT with C=0 BEQ unkswz ;if no keyword then exit, else: ;Search for *HELP keyword TYA ;a=offset of keyword from GSINIT pointer PHA ;also save on stack LDX #hlptab JSR wname0 ;search for keyword in table BCS L810A ;if keyword found JSR L80D7 ;then call its action address; print help .L810A PLA ;restore string offset TAY .L810C JSR gsread ;call GSREAD BCC L810C ;until end of argument (discarding it) BCS L80F4 ;then scan next *HELP keyword .inifsy CMP #$12 BNE msters CPY #$04 ;Service call $12 = initialise FS BNE unkswz ;if number of FS to initialise = 4 JSR pgmain ;then page in main workspace JSR savita ;save AXY .L820E ;*DISC / *DISK PHA JSR L8236 ;probe JIM page $0001 for RAM BNE L8217 ;if RAM found then Challenger unit installed JSR sinit0 ;so initialise Challenger FS .L8217 PLA .unkswz RTS IF _MASTER .sv25h ;Service call $25 = filing system info LDX #fsblke-L88F0-$01 ;22 bytes to write: .L890C LDA L88F0,X ;get byte of filing system info STA (linptr),Y ;add to MOS table INY ;increment table offset DEX ;decrement count BPL L890C ;loop until 22 bytes written LDX romid ;restore AX, pass updated Y LDA #$25 RTS ENDIF .msters IF _MASTER CMP #$25 BCC unkwrd ;if call no. < $25 then test for 8 BEQ sv25h ;if call no. = $25 then write FS info CMP #$27 BCC sv26h ;if call no. = $26 then *SHUT all files BNE unkswz ;if call no. > $26 then pass it on, else: JSR ischal ;Service call $27 = reset occurred BNE nreset ;if Challenger not initialised then pass call JSR L9753 ;else forget catalogue in JIM pages 2..3 .nreset LDA #$27 ;restore call number RTS ;pass on service call. .sv26h ;Service call $26 = *SHUT command issued JSR ischal ;if Challenger initialised BEQ shut ;then proceed .bail LDA #$26 ;else pass on the call. RTS .ischal ;Test whether Challenger FS active ;on entry X=romid LDA priptr,X ;get type from private page pointer AND #$03 ;mask Challenger unit type 0..2 BEQ bail ;if Challenger not present then return Z=0 JSR pgmain ;else page in main workspace LDA #$E5 ;validate second sentinel CMP senti2 ;if invalid then return Z=0 BNE bail EOR senti1 ;else validate first sentinel (bits 6..0) ASL A ;return Z=1 iff Challenger initialised RTS ;return C=1 if Challenger NOT the current FS .shut LDA dcbmap ;if no files are open AND #$F8 BEQ bail ;then exit LDA #$26 ;else restore call number JSR savita ;save AXY BCC shut1 ;if Challenger FS active then shut all files JSR sinit1 ;initialise Challenger preserving call no. .shut1 JMP close2 ;close a file/all files and return ENDIF .unkwrd CMP #$08 BNE unkswz ;Service call $08 = unrecognised OSWORD JSR pgmain ;page in main workspace JSR savit ;save XY (X will be clobbered on return) LDY wordx ;set $B0..1 = pointer to OSWORD control block STY blkptr+$00 LDY wordy STY blkptr+$01 LDY #$00 STY dopint ;=0 disc operation is uninterruptible LDY worda ;set Y = OSWORD call number (in A on entry) CPY #$7F BNE gtdsks JSR nmicla ;OSWORD A = $7F LDY #$01 ;claim NMI LDA (blkptr),Y ;offset 1 = address LSB STA nmiusr+$00 ;copy to $A6 INY LDA (blkptr),Y ;offset 2 = address 3MSB STA nmiusr+$01 ;copy to $A7 LDY #$00 LDA (blkptr),Y ;offset 0 = drive number BMI L8165 ;if b7=1 then use previous drive PHA ;[D]else save requested drive ROL A ;shift bit 3 = force double density ROL A ;to bit 6 ROL A AND #$40 ;mask bit 6 = hardware double density flag ORA denflg ;or with *DENSITY detected/forced DD flag STA denflg ;update *DENSITY flag PLA ;restore requested drive AND #$07 ;extract drive number 0..7 STA fdrive ;set as current drive .L8165 INY ;offset 1 = address LDX #$02 JSR shftbo ;copy address to $BE,F,$FDB5,6 LDA (blkptr),Y ;[D]y = 5 on exit; offset 5 = no. parameters PHA ;save number of parameters INY ;increment offset LDA (blkptr),Y ;offset 6 = command AND #$3F STA o7fcmd JSR sfour ;shift A right 4 places, extract bit 4: AND #$01 ;a=0 if writing to disc, A=1 if reading JSR chktub ;open Tube data transfer channel LDY #$07 LDA (blkptr),Y ;offset 7 = first parameter (usu. track) INY ;offset 8, Y points to second parameter STA track LDX #$FD ;x = $FD to start at offset 0: .L8186 INX ;add 3 to X INX INX LDA LB8AD+$00,X ;get command byte from table BEQ L81AE ;if the terminator byte then exit CMP o7fcmd ;else compare with OSWORD $7F command BNE L8186 ;if not the same try next entry PHP ;else save interrupt state CLI ;enable interrupts LDA #>(L81A3-$01) ;[D]push return address $81A3 on stack PHA LDA #<(L81A3-$01) PHA LDA LB8AD+$02,X ;fetch action address high byte PHA ;push on stack LDA LB8AD+$01,X ;fetch action address low byte PHA ;push on stack RTS ;jump to action address. .L81A3 ;Finish OSWORD $7F TAX ;[D]hold result in X PLP ;restore interrupt state PLA ;[D]restore number of parameters CLC ;add 7; drive, address, no.parms, command ADC #$07 ;=offset of result in O7F control block TAY ;transfer to Y for use as offset TXA STA (blkptr),Y ;[D]store result in user's OSWORD $7F block PHA ;[D]push dummy byte on stack: .L81AE PLA ;[D]discard byte from stack JSR nmirel ;release NMI JSR L96E6 ;release Tube LDA #$00 ;exit A=0 to claim service call RTS .gtdsks ;OSWORD A <> $7F BCS L81ED ;if A > $7F then exit CPY #$7D ;else if A < $7D BCC L81ED ;then exit JSR setdef ;set current vol/dir = default, set up drive JSR L962F ;load volume catalogue L4 CPY #$7E BEQ getdsz ;OSWORD A = $7D JSR pgcat1 ;page in catalogue sector 1 LDY #$00 ;store in OSWORD control block offset 0 LDA cycno ;get catalogue cycle number STA (blkptr),Y ;store in OSWORD control block offset 0 TYA ;return A = 0, claiming service call. RTS .getdsz ;OSWORD A = $7E get size of volume in bytes JSR pgcat1 ;page in catalogue sector 1 LDA #$00 TAY STA (blkptr),Y ;store 0 at offset 0: multiple of 256 bytes INY ;offset 1 LDA dirhig+$07 ;get LSB volume size from catalogue STA (blkptr),Y ;save as 3MSB volume size INY ;offset 2 LDA dirhig+$06 ;get boot option/top bits volume size AND #$03 ;extract MSB volume size STA (blkptr),Y ;save as 2MSB volume size INY ;offset 3 LDA #$00 ;store 0: volume size less than 16 MiB STA (blkptr),Y ;Y = 3; store LSB volume size .L81ED RTS ;return A = 0, claiming service call. .L8219 ;Get Challenger unit type LDX romid ;get our ROM slot number LDA priptr,X ;get type from private page pointer RTS ;ChADFS ROM call 2 .L821F ;Probe Challenger unit RAM size LDX #$00 ;set X=0, no RAM found JSR L8236 ;probe JIM page $0001 for RAM BNE L822F ;if RAM absent return 0 INX ;else X=1, 256 KiB unit LDA #$04 ;probe JIM page $0401 for RAM JSR L8238 ;will hit empty sockets, not alias to bank 0 BNE L822F ;if RAM absent return 1 INX ;else X=2, 512 KiB unit .L822F TXA LDX romid ;get our ROM slot number STA priptr,X ;store Challenger unit type in private pg ptr RTS .L8236 ;Probe JIM page $0001 for RAM LDA #$00 .L8238 ;Probe JIM page (A*$100)+$01 for RAM STA jpghi ;store MSB JIM paging register LDA #$01 ;page $0001 (main workspace) or $0401 STA jpglo ;store LSB JIM paging register LDA jim+$00 ;read offset 0 of JIM page EOR #$FF ;invert it STA jim+$00 ;write it back LDY #$05 ;wait 13 microseconds .L824A DEY ;allow 1 MHz data bus to discharge BNE L824A CMP jim+$00 ;read offset 0, compare with value written PHP ;save result EOR #$FF ;restore original value STA jim+$00 ;write back in case RAM is there PLP ;return Z=1 if location 0 acts like RAM RTS .sinit0 ;Initialise Challenger FS LDA #$00 TSX STA stack+$08,X ;have A=0 returned on exit .sinit1 LDA #$06 ;FSC $06 = new FS about to change vectors JSR osfscm ;issue Filing System Call LDX #$00 ;x = 0 offset in MOS vector table .init0 LDA vtabb,X ;copy addresses of extended vector handlers STA vtab2,X ;to FILEV,ARGSV,BGETV,BPUTV,GBPBV,FINDV,FSCV INX ;loop until 7 vectors transferred CPX #$0E BNE init0 JSR LADE8 ;call OSBYTE $A8 = get ext. vector table addr STY evtptr+$01 ;set up pointer to vector table STX evtptr+$00 LDX #$00 ;x = 0 offset in Challenger vector table LDY #$1B ;y = $1B offset of FILEV in extended vec tbl .init1 LDA vtabf,X ;get LSB action address from table STA (evtptr),Y ;store in extended vector table INX INY LDA vtabf,X ;get MSB action address from table STA (evtptr),Y ;store in extended vector table INX INY LDA romid ;get our ROM slot number STA (evtptr),Y ;store in extended vector table INY CPX #$0E ;loop until 7 vectors transferred BNE init1 LDA senti1 ;[D]get first workspace sentinel ORA #$80 ;set b7=1 Challenger is current FS STA senti1 ;update first sentinel LDA #$00 STA chadfs ;b6=0 Challenger is current FS LDX #$0F ;service call $0F = vectors claimed JMP doswcl ;call OSBYTE $8F = issue service call ;[D]no wksp/private page validation/init .L82A4 ;Initialise workspace part 1 JSR pgmain ;page in main workspace LDA #$80 ;a=$80 STA denflg ;*OPT 6,0 automatic density STA t40flg ;*OPT 8,255 automatic stepping LDA #srom ;a=$0E STA savrom ;*OPT 9,14 page in ROM slot 14 during disc ops LDA #$00 STA defdsk ;set default volume = "0A" STA libdsk ;set library volume = "0A" STA enacat ;[D]no *ENABLE CAT LDA #$24 STA defqua ;set default directory = "$" STA libqua ;set library directory = "$" RTS .L82C8 ;Initialise workspace part 2 JSR pgmain ;page in main workspace JSR LADE4 ;call OSBYTE $EA = read Tube presence flag TXA EOR #$FF ;invert; 0=tube present $FF=Tube absent STA notube ;save Tube presence flag LDY #$00 ;y=$00 [D]no workspace validation STY dcbmap ;no files are open STY prenmi ;no previous NMI owner STY nmiflg ;NMI resource is not ours STY tubflg ;[D]no Tube data transfer in progress STY chadfs ;b6=0 Challenger is current FS DEY ;y=$FF STY enaflg ;*commands are not *ENABLEd STY monflg ;*OPT 1,0 quiet operation STY catdrv ;no catalogue in JIM pages 2..3 JMP LB8F7 ;set track stepping rate from startup options .L82F7 JSR setdef ;set current volume and directory = default JSR dirldy ;load volume catalogue LDY #$00 LDX #$00 JSR pgcat1 ;page in catalogue sector 1 LDA option ;get boot option/top bits volume size JSR sfour ;shift A right 4 places BEQ L8331 ;if boot option = 0 then exit PHA LDX #run JSR stxylp ;set GSINIT pointer to XY, set Y=0 JSR getnam ;set current file from file spec JSR lookup ;search for file in catalogue PLA ;restore boot option BCS L8332 ;if !BOOT found then boot from it JSR vstrng ;else print "File not found" and return EQUS "File not found" EQUB $0D EQUB $0D NOP .L8331 RTS .L8332 CMP #$02 BCC L8344 ;if boot option = 1 then load !BOOT BEQ L833E ;if boot option = 2 then run !BOOT LDX #exec ;point XY to "E.!BOOT" BNE L8348 ;call OSCLI .L833E LDX #run BNE L8348 ;call OSCLI .L8344 LDX #load .L8348 JMP oscli ;call OSCLI .load EQUS "L.!BOOT" EQUB $0D .exec EQUS "E." .run EQUS "!BOOT" EQUB $0D .type ;*TYPE JSR zerchk ;claim service call and set up argument ptr LDA #$00 ;a = $00 CR does not trigger line no. BEQ type0 .list ;*LIST JSR zerchk ;claim service call and set up argument ptr LDA #$FF ;a = $FF CR triggers line number .type0 STA lstmsk ;store CR mask LDA #$40 ;OSFIND $40 = open a file for reading JSR osfind ;call OSFIND TAY ;test returned file handle BEQ L83A1 ;if file not found then raise error LDA #$0D ;preload CR so *LIST prints line no. 1 BNE type3 ;branch to CR test (always) .type1 JSR osbget ;call OSBGET BCS type2 ;if EOF then finish CMP #$0A ;else if character is LF BEQ type1 ;ignore it and get next one PLP ;else restore result of (A AND mask) - CR BNE type4 ;if no match just print the character PHA ;else save first character of line JSR iprdec ;increment and print BCD word JSR pspace ;print a space PLA ;restore first character .type4 JSR osasci ;call OSASCI BIT escflg ;if ESCAPE pressed BMI L8399 ;then finish .type3 AND lstmsk ;else apply mask to character just prt'd CMP #$0D ;compare masked character with CR PHP ;save result JMP type1 ;and loop to read next character .type2 PLP ;discard result of (A AND mask) - CR .L8399 JSR pcrlf ;print newline .type9 LDA #$00 ;OSFIND $00 = close file JMP osfind ;call OSFIND and exit .L83A1 JMP L8B46 ;raise "File not found" error .dump ;*DUMP JSR zerchk ;claim service call and set up argument ptr LDA #$40 ;OSFIND $40 = open a file for reading JSR osfind ;call OSFIND TAY ;transfer file handle to Y BEQ L83A1 ;if file not found raise error .L83AF BIT escflg ;if ESCAPE pressed BMI type9 ;then close file and exit LDA linnoh ;else get high byte of file offset JSR bytout ;print hex byte LDA linnol ;get low byte of file offset JSR bytout ;print hex byte JSR pspace ;print a space TSX STX dmpstk ;save stack pointer LDX #$08 ;offset = 8 for indexed indirect load [D]not 0 .L83C5 JSR osbget ;call OSBGET BCS L83D4 ;if EOF then finish PHA ;[D]else save byte read for ASCII column JSR bytout ;print hex byte JSR pspace ;print a space DEX ;decrement counter BNE L83C5 ;loop until line complete .L83D4 DEX ;test counter BMI L83E4 ;if EOF on incomplete line PHP ;then save status (N=0, C=1) JSR vstrng ;pad hex column with "** " EQUS "** " LDA #$00 PLP ;restore status PHA ;push NUL to pad ASCII column BPL L83D4 ;loop until line complete (always) .L83E4 PHP ;save C=EOF TSX ;transfer stack pointer to X LDA #$07 STA dumpct ;set counter to 7: .dump6 LDA stack+$09,X ;[D]get byte 9..2 down = byte 1..8 of column CMP #$7F ;if DEL or higher BCS dump7 ;then print a dot CMP #$20 ;else if a printable character BCS dump8 ;then print it .dump7 LDA #$2E ;else print a dot: .dump8 JSR osasci ;call OSASCI DEX ;decrement pointer, work toward top of stack DEC dumpct ;decrement counter BPL dump6 ;loop until line complete JSR pcrlf ;print newline LDA #$08 ;add 8 to file offset CLC ADC linnol STA linnol BCC dump9 ;carry out to high byte INC linnoh .dump9 PLP ;restore carry flag from OSBGET LDX dmpstk ;[D]restore stack pointer to discard column TXS BCC L83AF ;if not at end of file then print next row BCS type9 ;else close file and exit .build ;*BUILD JSR L8219 ;get Challenger unit type AND #$03 ;extract b1,b0 of A BNE build0 ;if Challenger not installed then ignore command RTS .build0 JSR zerchk ;claim service call and set up argument ptr LDA #$80 ;OSFIND $80 = open a file for writing JSR osfind ;call OSFIND STA bldfh ;save file handle JSR LA838 ;[D]set line number = 0 (OSFIND clobbers) .L8422 JSR iprdec ;increment and print BCD word JSR pspace ;print a space LDA #>lnbuff ;y = $FD point to JIM page for OSWORD STA bldtbl+$01 LDX #bldtbl-$01 AND $FF ;y = $FF STY bldtbl+$02 ;maximum line length = 255 STY bldtbl+$04 ;maximum ASCII value = 255 INY STY bldtbl+$00 ;clear low byte of pointer STY bldtbl+$03 ;minimum ASCII value = 0 JSR pgbuil ;page in line buffer TYA ;OSWORD $00 = read line of input JSR osword ;call OSWORD PHP ;save returned flags STY lineln ;save length of line LDY bldfh ;y = file handle for OSBPUT LDX #$00 ;offset = 0 for indexed indirect load .L8447 CPX lineln ;compare offset with line length BEQ L8458 ;if end of line reached then terminate line JSR pgbuil ;else page in line buffer LDA lnbuff,X ;get character of line JSR osbput ;call OSBPUT INX ;increment offset BNE L8447 ;and loop to write rest of line (always) .L8458 PLP ;restore flags from OSWORD BCS L8463 ;if user escaped from input then finish LDA #$0D ;else A = carriage return JSR osbput ;write to file JMP L8422 ;and loop to build next line .L8463 JSR ackesc ;acknowledge ESCAPE condition JSR type9 ;close file: .pcrlf ;Print newline PHA LDA #$0D JSR pchr ;print character in A (OSASCI) PLA .L8470 RTS .chkdsf ;Select source volume JSR savita ;save AXY JSR pgmain ;page in main workspace LDX fdriv ;set X = source volume LDA #$00 ;a=$00 = we want source disc BEQ L8489 ;branch (always) .chkdst ;Select destination volume JSR savita ;save AXY JSR pgmain ;page in main workspace LDX tdriv ;set X = destination volume LDA #$80 ;a=$80 = we want destination disc .L8489 STX fdrive ;set wanted volume as current volume ;[D]no 'set up for current drive' BIT swaps ;if disc swapping not required BPL L8470 ;then exit, else: .L8492 CMP tindrv ;compare wanted disc with disc in drive BEQ L8470 ;if the same then do not prompt STA tindrv ;else wanted disc is going into drive JSR vstrng ;print "Insert " EQUS "Insert " NOP BIT tindrv ;if b7=1 BMI L84B2 ;then print "destination" JSR vstrng ;else print "source" EQUS "source" BCC L84C1 ;and branch (always) .L84B2 JSR vstrng ;print " destination" EQUS "destination" NOP .L84C1 JSR vstrng ;print " disk and hit a key" EQUS " disk and hit a key" NOP JSR L84EF ;wait for keypress JMP pcrlf ;print newline and exit .getyn ;Ask user yes or no JSR L84EF ;wait for keypress AND #$5F ;convert to uppercase CMP #$59 ;is it "Y"? PHP ;save the answer BEQ getyn0 ;if so then print "Y" LDA #$4E ;else print "N" .getyn0 JSR pchr ;print character in A (OSASCI) PLP ;return Z=1 if "Y" or "y" pressed RTS .L84EF ;Wait for keypress JSR clrkey ;call *FX 15,1 = clear input buffer JSR osrdch ;call OSRDCH, wait for input character BCC L84FA ;if ESCAPE was pressed LDX abrtsp ;then abort our routine TXS ;clear our stacked items, return to caller .L84FA RTS .L84FB ;Restore parameters of source drive LDY #$00 ;offset = 0 BEQ L8501 ;branch (always) .L84FF ;Restore parameters of destination drive LDY #$02 ;offset = 2: .L8501 ;Restore parameters of source/dest drive JSR pgmain ;page in main workspace LDA cpvltk,Y ;[D]get first track of selected volume STA voltrk ;set as first track of current volume LDA packed,Y ;get packed drive parameters: .L850D ;Restore packed drive parameters PHA ;save packed drive parameters AND #$C0 ;mask b7,b6 STA denflg ;store *OPT 6 density setting PLA ;restore packed drive parameters LSR A ;shift b1,b0 of A to b7,b6 ROR A ROR A PHA ;save other bits AND #$C0 ;mask b7,b6 STA t40flg ;store *OPT 8 tracks setting PLA ;restore b1,b0 = original b4,b3 AND #$03 ;mask b1,b0: ;Unpack and store sectors per track ;if A=0 on entry then RAM disc BEQ L8558 ;so store 0=sectors per track undefined LSR A ;else if A=1 BEQ unpksd ;then store 10 sectors per track LDA #ddspt EOR sdspt ;else A>1, store 18 sectors per track .unpksd EOR #sdspt .L8558 STA sectrk ;store number of sectors per track RTS .L8523 ;Save parameters of source drive JSR savita ;save AXY LDY #$00 BEQ L852F .L852A ;Save parameters of destination drive JSR savita ;save AXY LDY #$02 .L852F ;Save parameters of source/dest drive JSR pgmain ;page in main workspace LDA voltrk ;get first track of current volume STA cpvltk,Y ;set as first track of selected volume JSR L853F ;pack drive parameters STA packed,Y RTS .L853F ;Pack drive parameters JSR L855C ;pack number of sectors per track ORA t40flg ;apply *OPT 8 tracks setting in b7,b6 ASL A ;shift spt to b4,b3, *OPT 8 to b1,b0 ROL A ROL A ORA denflg ;apply *OPT 6 density setting in b7,b6 RTS ;return packed drive parameters .L855C ;Pack number of sectors per track LDA sectrk ;get current setting BEQ L8568 ;if A=0 then RAM disc, return 0 CMP #ddspt ;else if less than 18 i.e. 10, single dens. LDA #$01 ;then return 1 ADC #$00 ;if 18 or more i.e. double density return 2. .L8568 RTS .cpydsk ;*BACKUP JSR chkena ;ensure *ENABLE active JSR get2dr ;parse and print source and dest. volumes LDA #$00 STA cbcont ;no catalogue entry waiting to be created STA srclo ;set source volume LBA = 0 STA srchi STA dstlo ;set destination volume LBA = 0 STA dsthi JSR L865F ;load source volume catalogue LDA #$00 STA voltrk ;data area starts on track 0 JSR L8523 ;save parameters of source drive JSR L863A ;return volume size in XY/boot option in A STA srcopt ;save source volume boot option STX todolo STY todohi JSR L8659 ;load destination volume catalogue LDA #$00 STA voltrk ;data area starts on track 0 JSR L852A ;save parameters of destination drive LDA packed+$00 ;get density of source drive EOR packed+$02 ;xor with density flag of destination drive ASL A ;extract bit 6 density flag, ignore auto b7 BPL L85CA ;if the same density then skip JSR estrng ;else raise density mismatch error. EQUB $D5 EQUS "Both disks MUST be same density" EQUB $00 .L85CA JSR L863A ;return volume size in XY/boot option in A TXA ;save destination volume size on stack PHA TYA PHA CMP todohi ;compare MSBs dest volume size - source BCC L85DC ;if dest < source then raise error BNE L8600 ;if dest > source then proceed CPX todolo ;else compare LSBs dest - source BCS L8600 ;if dest >= source then proceed .L85DC LDA #$D5 ;else error number = $D5 JSR LA938 ;begin error message, number in A LDA fdriv ;get source drive JSR L8EAD ;print " Drive " plus volume spec in A JSR vstrng ;print " larger than " EQUS " larger than " LDA tdriv ;get destination drive JSR L8EAD ;print " Drive " plus volume spec in A JMP LA8F8 ;terminate error message, raise error .L8600 JSR mvdkda ;copy source drive/file to destination JSR L88CA ;[D]store empty BASIC program at OSHWM (NEW) JSR LB74C ;[D]set Z=1 iff current drive is a RAM disc BNE L860E ;if so PLA ;then discard destination volume size PLA RTS ;and exit .L860E BIT denflg ;else test density flag BVS L8629 ;if double density then update disc catalogue JSR dirldy ;else load volume catalogue L4 PLA ;pop MSB destination volume size AND #$0F ;mask bits 0..3 ORA srcopt ;apply source boot option in bits 4..5 JSR pgcat1 ;page in catalogue sector 1 STA dirhig+$06 ;store in catalogue PLA ;pop LSB destination volume size STA dirhig+$07 ;store in catalogue JMP dirout ;write volume catalogue L4 ;[BUG] Copying a 40 track DD disc to an 80 track DD disc preserves ;the destination disc size at $1001..2 = 1440 sectors but copies ;the source track count at $1004 = 40 tracks. EDOS *CATGEN will be ;unable to assign tracks in the second half of the disc. .L8629 ;Update disc catalogue JSR LACBA ;load disc catalogue L3 JSR pgcat0 ;page in catalogue sector 0 PLA ;pop MSB disc size STA dcszhi ;store in disc catalogue PLA ;pop LSB disc size STA dcszlo ;store in disc catalogue JMP LACBD ;write disc catalogue L3 .L863A ;Return volume size in XY/boot option in A JSR pgcat1 ;page in catalogue sector 1 LDX dirhig+$07 ;get LSB volume size from catalogue LDA dirhig+$06 ;get boot option/top bits volume size PHA AND #$03 ;extract MSB volume size TAY ;put volume size in XY JSR pgmain ;page in main workspace BIT denflg ;test density flag BVC L8655 ;if double density LDX dscszl ;then load disc size from workspace instead LDY dscszh .L8655 PLA ;return disc size in XY AND #$F0 ;return boot option in A bits 5 and 4 RTS .L8659 ;Load destination volume catalogue JSR chkdst ;select destination volume JMP dirldy ;load volume catalogue L4 .L865F ;Load source volume catalogue JSR chkdsf ;select source volume JMP dirldy ;load volume catalogue L4 .cpyfil ;*COPY JSR setwld ;allow wildcard characters in filename JSR get2dr ;parse and print source and dest. volumes JSR chksyn ;call GSINIT with C=0 and require argument JSR getnam ;set current file from file spec JSR chkdsf ;select source volume JSR errlok ;ensure matching file in catalogue JSR L8523 ;save parameters of source drive LDA frpage ;[D]get start of user memory STA zfrpg ;[D]save in zero page LDA #$01 STA cbcont ;one entry in copy buffer file table LSR A ;a=0 STA cblast ;point to start of copy buffer file table: ;Copy file .L868A TYA ;[D]save catalogue offset of found file PHA LDX #$00 .L868E LDA wrknam,X ;save file spec on stack PHA INX CPX #$08 BNE L868E JSR vstrng ;print "Reading " EQUS "Reading " LDX cblast ;get pointer to free end of buffer table JSR prtnam ;print filename from catalogue JSR pcrlf ;print newline .L86AF JSR pgcat1 ;page in catalogue sector 1 LDA cathig,Y ;get matching file's catalogue information JSR pgaux ;page in auxiliary workspace STA cbcat,X ;store information in copy buffer table INX ;$FD11..18,X INY TYA ;loop until 8 bytes copied AND #$07 BNE L86AF .L86C5 JSR pgcat0 ;page in catalogue sector 0 LDA catlow-$08,Y ;get matching file's name and directory JSR pgaux ;page in auxiliary workspace STA cbname-$08,X ;store filename in copy buffer table INX ;$FD1A..21,X INY TYA ;loop until 8 characters copied AND #$07 BNE L86C5 ;then A=0 STA cbflag-$10,X ;clear $FD19,X flag byte LDA cbcat+$04-$10,X ;get LSB length CMP #$01 ;[D]set C=1 iff file includes partial sector LDA cbcat+$05-$10,X ;get 2MSB length ADC #$00 ;round up to get LSB length in sectors STA cblnlo-$10,X ;[D]store LSB length in sectors in table PHP ;save carry flag LDA cbcat+$06-$10,X ;get top bits exec/length/load/start sector JSR isolen ;extract b5,b4 of A PLP ;restore carry flag ADC #$00 ;carry out to get MSB length in sectors STA cblnhi-$10,X ;save length in sectors at $FD22..23,X LDA cbcat+$07-$10,X ;get LSB start LBA STA cbslbl-$10,X ;copy to $FD24,X LDA cbcat+$06-$10,X ;get top bits exec/length/load/start sector AND #$03 ;extract MSB start sector STA cbslbh-$10,X ;store MSB start LBA at $FD25,X: ;Read segment of file .L8704 JSR pgmain ;page in main workspace SEC ;subtract HIMEM - OSHWM LDA hipage SBC zfrpg STA zfrsiz ;= number of pages of user memory LDY cblast ;get pointer to latest buffer table entry JSR pgaux ;page in auxiliary workspace LDA cblnlo,Y ;copy LSB length in sectors STA todolo ;to zero page LDA cblnhi,Y ;MSB length in sectors STA todohi LDA cbslbl,Y ;LSB start LBA STA srclo LDA cbslbh,Y ;MSB start LBA STA srchi JSR L8989 ;set start and size of next transfer LDA zfrpg ;set MSB load address = start of user memory STA lodhi LDA #$00 STA lodlo ;[D]set LSB load address = 0 STA lenlo ;set LSB transfer size = 0 LDA lenhi ;get size of transfer JSR pgaux ;page in auxiliary workspace STA cbtxsz,Y ;overwrite LSB start LBA at $FD18,Y JSR L959C ;set high word of OSFILE load address = $FFFF JSR L970D ;read extended file L5 JSR L899E ;adjust addresses by amount transferred CLC LDA zfrpg ;get start of free copy buffer ADC lenhi ;add size of transfer STA zfrpg ;update start of free copy buffer LDY cblast ;get pointer to latest buffer table entry JSR pgaux ;page in auxiliary workspace LDA todolo ;return LSB length in sectors STA cblnlo,Y ;to copy buffer table LDA todohi ;MSB length in sectors STA cblnhi,Y LDA srclo ;LSB start LBA STA cbslbl,Y LDA srchi ;MSB start LBA STA cbslbh,Y LDA todolo ;test number of sectors to transfer ORA todohi BEQ L8779 ;if no more then read next file/write buffer JSR pgaux ;else page in auxiliary workspace LDA cbflag,Y ;get buffer table entry's flag byte ORA #$80 ;b7=1 file incomplete in buffer STA cbflag,Y ;update flag byte: ;Continue filling copy buffer until full, or write it out .L8779 JSR pgmain ;page in main workspace LDA zfrpg ;has copy buffer been filled up to HIMEM? CMP hipage BEQ L87BA ;if so then write it out BIT cbcont ;else if b7=1 all files read BMI L87BA ;then write out copy buffer LDA cbcont ;else if copy buffer table is full AND #$7F CMP #cbtsiz BEQ L87BA ;then write it out CLC LDA cblast ;else point copy buffer table pointer ADC #cbtble-cbtbl ;to next entry: STA cblast ;Copy next matching file .L8798 LDX #$07 ;8 bytes to restore: .L879A PLA ;restore file spec from stack STA wrknam,X DEX ;loop until 8 bytes restored BPL L879A PLA ;restore catalogue offset of found file STA catofs JSR next ;find next matching file BCC L87AE ;if no more files match then finish INC cbcont ;else increment no. of files in buffer JMP L868A ;and copy next file. ;Flush copy buffer .L87AE LDY cblast ;more than one table entry in use? BNE L87B4 ;if so then write out copy buffer RTS ;else exit .L87B4 LDA cbcont ;set b7=1 all files read ORA #$80 STA cbcont .L87BA JSR pgmain ;page in main workspace JSR chkdst ;select destination volume LDA frpage STA zfrpg ;set start of copy buffer to OSHWM LDA cbcont ;get no. entries in copy buffer AND #$7F ;extract actual number of entries TAX LDY #cbtbl+$0100-cbtble ;y=$E9 going to $00: ;Write file from copy buffer .L87CC TXA PHA ;save number of buffer table entries CLC TYA ADC #cbtble-cbtbl ;point to next buffer table entry STA cbcurs ;set pointer to last entry of buffer table PHA ;and save it TAY JSR pgaux ;page in auxiliary workspace LDA cbflag,Y ;if b6=1 destination file partly copied ASL A BMI L8838 ;write out rest of file ROR A ;else b6=1 don't create entry twice ORA #$40 STA cbflag,Y LDX #$00 .L87EB LDA cbtbl,Y ;read from buffer table entry $FD11..21,Y STA wrkcat,X ;restore file catalogue info $BE..$C5 INY ;and filename and directory $C7..$CE INX CPX #cblnlo-cbtbl BNE L87EB JSR L9753 ;forget catalogue in JIM pages 2..3 JSR lookup ;search for file in catalogue BCC L8801 ;if file found JSR delfil ;then delete catalogue entry .L8801 JSR L852A ;save parameters of destination drive JSR decodl ;expand 18-bit load address to 32-bit JSR decode ;expand 18-bit exec address to 32-bit LDA wrkcat+$06 ;get top bits exec/length/load/start sector JSR isolen ;extract b5,b4 of A STA lenhl ;store MSB length of file JSR genfil ;create catalogue entry JSR vstrng ;print "Writing " EQUS "Writing " NOP JSR prtnam ;print filename from catalogue JSR pcrlf ;print newline LDY cbcurs ;point to last entry of buffer table JSR pgaux ;page in auxiliary workspace LDA wrkcat+$06 ;get top bits exec/length/load/start sector AND #$03 ;extract b1,b0 of A STA cbdlbh,Y ;store MSB destination LBA LDA lbalo ;copy LSB destination LBA STA cbdlbl,Y ;Write segment of file .L8838 LDA cbtxsz,Y ;get no. pages of data in buffer STA lenhi ;set size of transfer CLC LDA cbdlbl,Y ;copy LSB destination LBA STA lbalo ADC lenhi ;add transfer size STA cbdlbl,Y ;update LSB destination LBA of next write LDA cbdlbh,Y ;copy MSB destination LBA STA wrkcat+$06 ADC #$00 ;carry out transfer size STA cbdlbh,Y ;update MSB destination LBA of next write LDA zfrpg ;get start of filled copy buffer STA lodhi ;set MSB of source address LDA #$00 STA lodlo ;clear LSB source address STA lenlo ;clear LSB transfer size JSR L84FF ;restore parameters of destination drive JSR L959C ;set high word of OSFILE load address = $FFFF JSR L9713 ;write extended file L5 CLC LDA zfrpg ;get start of filled copy buffer ADC lenhi ;add size of transfer STA zfrpg ;update start of filled copy buffer PLA ;restore pointer to last table entry TAY PLA ;restore no. entries in buffer table TAX DEX ;remove one BEQ L8876 ;if last entry then check for multi-pass copy JMP L87CC ;else write next file in copy buffer ;Wrote last entry of copy buffer. Copy rest of file or refill buffer .L8876 JSR L84FB ;restore parameters of source drive LDY cbcurs ;point to last entry of buffer table JSR pgaux ;page in auxiliary workspace LDA cbflag,Y ;test flag byte AND #$80 ;if b7=0 file(s) in buffer are complete BEQ L88B0 ;then start refilling copy buffer LDX #$00 ;else start at offset = 0: ;The last entry in the copy buffer table needs another pass ;to fulfil. Move it to the first table slot. .L8888 LDA cbtbl,Y ;copy last entry to first position STA cbtbl,X INY ;increment offsets INX CPX #cbtble-cbtbl ;loop until all 23 bytes copied BNE L8888 LDA #$40 ;b6=1 destination file partly copied STA cbflag ;set buffer table entry's flag byte JSR chkdsf ;select source volume JSR dirldy ;load volume catalogue L4 LDA frpage STA zfrpg ;set start of copy buffer to OSHWM LDA #$01 STA cbcont ;one entry in copy buffer table LSR A ;a=0 STA cblast ;one buffer table entry in use JMP L8704 ;read in the rest of this file. ;Exit if no more files to read; else empty copy buffer and refill it .L88B0 BIT cbcont ;if b7=1 all files read BMI L88C9 ;then exit JSR chkdsf ;else select source volume JSR dirldy ;load volume catalogue L4 LDA frpage STA zfrpg ;set start of copy buffer to OSHWM LDA #$00 STA cblast ;no buffer table entries in use STA cbcont ;no entries in copy buffer table JMP L8798 ;copy next matching file. .L88C9 RTS .L891D ;Shift data JSR savita ;save AXY LDA #shftsz STA frsize ;2 pages of user memory = catalogue sectors LDA #>jshift STA lodhi ;MSB of load address in JIM space = $00 .L8929 JSR L8984 ;set start and size of first transfer LDA #track 0 BEQ L8B88 JSR vstrng ;then print "Track offset = " EQUS " Track offset = " NOP JSR bytout ;print hex byte JSR pcrlf ;print newline .L8B88 JSR pgcat1 ;page in catalogue sector 1 LDY dirlen ;y = offset of last catalogue entry: .L8B8E JSR L9507 ;calculate slack space after file BEQ L8BBF ;if no slack space then only map the file CLC LDA headlo ;else add LSB slack space ADC mfreel ;to LSB total free space STA mfreel TXA ;add MSB slack space ADC mfreeh ;and carry out STA mfreeh ;to MSB total free space JSR vstrng ;print " Free space " EQUS " Free space " NOP JSR LB7E3 ;print start sector (from lbahi/lo) JSR pspace ;print a space TXA ;a = MSB slack space JSR digout ;print hex nibble LDA headlo ;a = LSB slack space JSR LB786 ;print hex byte and newline .L8BBF TYA ;if end of catalogue reached BEQ L8BDD ;then print total free space JSR unstep ;else subtract 8 from Y JSR prtnam ;print filename from catalogue JSR L8CE3 ;print start sector JSR pspace ;print a space JSR L94EB ;calculate number of sectors used by file JSR LB7E3 ;print number of sectors JSR pcrlf ;print newline JSR L94D6 ;calculate LBA of end of file JMP L8B8E ;loop to map next file. .L8BDD ;Print total free space JSR vstrng ;print "Free sectors " EQUB $0D EQUS "Free sectors " LDA mfreeh ;a = MSB total free space JSR digout ;print hex nibble LDA mfreel ;a = LSB total free space JSR LB786 ;print hex byte and newline JMP pgmain ;page in main workspace and exit .ex ;FSC 9 = *EX JSR stxylp ;set GSINIT pointer to XY, set Y=0 .L8BFF JSR setwld ;allow wildcard characters in filename JSR L8A5F ;a=$23; set current filename = "#######" JSR setdef ;set current volume and directory = default JSR LAA3B ;call GSINIT and parse directory spec JSR errlok ;ensure matching file in catalogue BCS L8C25 ;always branch .info ;FSC 10 = *INFO JSR stxylp ;set GSINIT pointer to XY, set Y=0 LDX #infcom ;fall through: JSR L921E ;set up trampoline to read *INFO entry LDY #$00 ;set Y = 0 offset for GSINIT .L8C1C ;*INFO JSR setwld ;allow wildcard characters in filename JSR chksyn ;call GSINIT with C=0 and require argument JSR getlok ;ensure file matching spec in catalogue .L8C25 JSR prtinf ;print *INFO line JSR next ;find next matching file BCS L8C25 ;loop until no more files match. RTS .lookup ;Search for file in catalogue JSR dirld ;ensure current volume catalogue loaded LDY #$F8 ;y=$F8, start beyond first catalogue entry BNE L8C3B ;and jump into search loop (always) .next ;Find next matching file JSR pgmain ;page in main workspace LDY catofs ;set Y = catalogue pointer .L8C3B JSR pgcat1 ;page in catalogue sector 1 JSR step ;add 8 to Y CPY dirlen ;have we reached the end of the catalogue? BCS lookx ;if so return C=0 file not found JSR step ;else add 8 to Y LDX #$07 ;x=7 point to directory character: .L8C4B JSR pgmain ;page in main workspace LDA wrknam,X ;get character of current filename CMP wildch ;compare with wildcard mask BEQ L8C66 ;if ='#' and wildcards allowed accept char JSR caps ;else set C=0 iff character in A is a letter JSR pgcat0 ;page in catalogue sector 0 EOR catlow-$01,Y ;compare with character in catalogue BCS L8C62 ;if character in current filename is letter AND #$DF ;then ignore case .L8C62 AND #$7F ;ignore bit 7, Z=1 if characters equal BNE L8C72 ;if not equal then test next file .L8C66 DEY ;loop to test next (previous) char of name DEX BPL L8C4B ;if no more chars to test then files match JSR pgmain ;page in main workspace STY catofs ;save cat. offset of found file in workspace SEC ;return C=1 file found RTS .L8C72 DEY ;advance catalogue pointer to next file DEX BPL L8C72 BMI L8C3B ;loop until file found or not .delfil ;Delete catalogue entry JSR chkopl ;ensure file not locked or open (mutex) .dellop JSR pgcat0 ;page in catalogue sector 0 LDA catlow+$08,Y ;copy next file's entry over previous entry STA catlow+$00,Y ;shifting entries up one place JSR pgcat1 ;page in catalogue sector 1 LDA cathig+$08,Y ;(copies title/boot/size if catalogue full) STA cathig+$00,Y INY ;loop until current file count reached CPY dirlen ;have we reached the end of the catalogue? BCC dellop TYA ;copy Y to A = pointer to last file; C=1 SBC #$08 ;subtract 8, catalogue contains one file less STA dirlen ;store new file count .lookx CLC .L8C9A JMP pgmain ;page in main workspace and exit. .inform ;Print *INFO line if verbose JSR pgmain ;page in main workspace BIT monflg ;test *OPT 1 setting BMI L8C9A ;if b7=1 then *OPT 1,0 do not print, else: .prtinf ;Print *INFO line JSR savita ;save AXY JSR prtnam ;print filename from catalogue TYA ;save catalogue pointer PHA LDA #dosram STA blkptr+$01 JSR chukbk ;return catalogue information to OSFILE block LDY #$02 ;y = $02 offset of load address in block JSR pspace ;print a space JSR prtin0 ;print load address JSR prtin0 ;print execution address JSR prtin0 ;print file length PLA ;restore catalogue pointer TAY JSR L8CE3 ;print start sector JMP pcrlf ;print newline .prtin0 ;Print 24-bit field at dosram+Y LDX #$03 ;start at MSB, offset = 3: .prtin1 LDA dosram+$02,Y ;get byte at $FDA3,Y JSR bytout ;print hex byte DEY ;increment offset DEX ;decrement counter BNE prtin1 ;loop until 3 bytes printed JSR step7 ;add 7 to Y to point to MSB of next field JMP pspace ;print a space and exit .L8CE3 ;Print start sector JSR pgcat1 ;page in catalogue sector 1 LDA cathig+$06,Y ;get top bits exec/length/load/start sector AND #$03 ;extract MSB start sector JSR digout ;print hex nibble LDA cathig+$07,Y ;get LSB start sector JSR bytout ;print hex byte JMP pgmain ;page in main workspace [D]not print newline .maker ;OSFILE 7 = create file, 11 = create w/ stamp LDA #$00 STA nice ;b6=0 will not accept shorter allocation JSR dirdo ;create file from OSFILE block JSR tryfl0 ;set up pointer to user's OSFILE block LDA #$01 ;return A=$01 from OSFILE: .chukbk ;Return catalogue information to OSFILE block JSR savita ;save AXY TYA ;save catalogue pointer on stack PHA TAX ;and copy to X JSR pgmain ;page in main workspace LDY #$02 ;clear bytes at offsets 2..17 LDA #$00 .L8D04 STA (blkptr),Y INY CPY #$12 BNE L8D04 LDY #$02 ;offset 2 = LSB load address .chukb5 JSR chukb4 ;copy two bytes from catalogue to OSFILE block INY ;skip high bytes of OSFILE field INY CPY #$0E ;loop until 3 fields half-filled: BNE chukb5 ;load address, execution address, file length PLA ;restore catalogue pointer TAX JSR pgcat0 ;page in catalogue sector 0 LDA modify,X ;get directory character BPL chukb3 ;if b7=1 then file is locked LDA #$0A ;so set attributes to LR/RW (old style) LDY #$0E ;no delete, owner read only, public read/write ;note: Acorn DFS returns $08 instead JSR pgmain ;page in main workspace STA (blkptr),Y ;store in OSFILE block .chukb3 JSR pgcat1 ;page in catalogue sector 1 LDA cathig+$06,X ;get top bits exec/length/load/start sector JSR pgmain ;page in main workspace LDY #$04 ;offset 4 = 2MSB load address JSR chukb1 ;expand bits 3,2 to top 16 bits of field LDY #$0C ;offset 12 = 2MSB file length LSR A ;PD43 returned A = ..eelldd LSR A ;shift A right twice to make A = ....eell PHA ;save exec address AND #$03 ;extract bits 1,0 for length (don't expand) STA (blkptr),Y ;store in OSFILE block PLA ;restore exec address in bits 3,2 LDY #$08 ;offset 8 = 2MSB execution address: .chukb1 LSR A ;shift A right 2 places LSR A PHA ;save shifted value for return AND #$03 ;extract bits 3,2 of A on entry CMP #$03 ;if either one is clear BNE L8D51 ;then save both as b1,0 of 2MSB LDA #$FF ;else set MSB and 2MSB = $FF. STA (blkptr),Y INY .L8D51 STA (blkptr),Y PLA ;discard byte on stack RTS .chukb4 ;Copy two bytes from catalogue to OSFILE block JSR chukb6 .chukb6 JSR pgcat1 ;page in catalogue sector 1 LDA cathig,X JSR pgmain ;page in main workspace STA (blkptr),Y INX INY RTS .L8D8A ;*STAT eight volumes JSR L8F0B ;print disc type and volume list LDX #$00 ;for each volume letter A..H: .L8D8F JSR pgaux ;page in auxiliary workspace LDA voltks,X ;test if number of tracks in volume > 0 BEQ L8DA4 ;if = 0 then no such volume, skip TXA ;save volume counter PHA JSR pcrlf ;print newline JSR dirld ;ensure current volume catalogue loaded JSR L9033 ;print volume statistics PLA ;restore volume counter TAX .L8DA4 CLC LDA fdrive ;get current volume ADC #$10 ;increment volume letter STA fdrive ;set as current volume INX ;increment counter CPX #volums ;loop until 8 volumes catalogued BNE L8D8F JMP pgmain .L8D66 ;*STAT JSR setupr ;call GSINIT with C=0 JSR LAA72 ;select specified or default volume TXA ;test bit 0 of X AND #$01 ;if X=3 drive and volume specified BNE L8DB3 ;then stat specified volume, else: ;*STAT eight volumes if double density LDA fdrive ;get current volume AND #$0F ;extract drive number STA fdrive ;set current volume letter to A LDA #$80 ;data transfer call $80 = read data to JIM STA txcall ;set data transfer call number JSR LABB5 ;detect disc format/set sector address BIT denflg ;test density flag BVS L8D8A ;if double density then *STAT eight volumes .L8DB3 ;*STAT specified volume JSR dirld ;ensure current volume catalogue loaded JSR L8F0B ;print disc type and volume list .L9033 ;Print volume statistics LDY #$03 ;y=3 print 2 spaces/ 1 space LDA fdrive ;get current volume JSR L8EAD ;print " Drive " plus volume spec in A JSR yspace ;print number of spaces in Y JSR vstrng ;print "Volume size " EQUS "Volume size " NOP JSR pgcat1 ;page in catalogue sector 1 LDA dirhig+$07 ;copy volume size to sector count STA linnol ;LSB LDA dirhig+$06 ;get boot option/top bits volume size AND #$03 ;mask top bits volume size STA linnoh ;store MSB JSR LB380 ;print sector count as kilobytes JSR pcrlf ;print newline LDY #$0A ;set Y = $0A print 10 spaces JSR yspace ;print number of spaces in Y JSR vstrng ;print "Volume unused" EQUS "Volume unused " NOP JSR pgcat1 ;[D]calculate used space on volume LDY dirlen ;get number of files in catalogue * 8 LDA #$00 STA dsthi ;clear MSB number of used sectors on volume JSR LA4F8 ;return no. reserved sectors in data area STA dstlo ;set LSB number of used sectors on volume .L908A JSR unstep ;subtract 8 from Y CPY #$F8 ;if Y=$F8 then was 0, first (last) file done BEQ L909A ;[D]if all files added then continue, else: JSR LA714 ;calculate number of sectors used by file JSR LA733 ;add number of sectors to total JMP L908A ;loop for next file .L909A ;[BUG] can exceed vol size due to overlaps JSR pgcat1 ;page in catalogue sector 1 SEC ;c=1 for subtract LDA dirhig+$07 ;get LSB volume size from catalogue SBC dstlo ;subtract LSB used space STA linnol ;store LSB result in zero page LDA dirhig+$06 ;get boot option/top bits volume size AND #$03 ;extract MSB volume size SBC dsthi ;subtract MSB used space, store in zp STA linnoh ;[BUG] can underflow due to overlaps JSR LB380 ;print sector count as kilobytes JMP pcrlf ;print newline .L8DBC ;Print "No file" JSR vstrng ;print string immediate [D]not VDU sequence EQUB $0D ;newline EQUS "No file" EQUB $0D ;newline NOP RTS .L8DCA ;List files in catalogue JSR pgcat1 ;page in catalogue sector 1 LDA dirlen ;get number of files in catalogue * 8 BEQ L8DBC ;if catalogue empty then print "No file" STA zdirln ;[D]else copy file count to zero page LDY #$FF STY nlflag ;print a newline before first entry INY STY catqua ;CSD printed first, directory char = NUL .cat0 JSR pgcat0 ;page in catalogue sector 0 CPY zdirln ;have we reached the end of the catalogue? BCS catscn ;if so then start sorting entries LDA modify,Y ;else get directory character of cat entry JSR pgmain ;page in main workspace EOR defqua ;compare with default (CSD) directory JSR pgcat0 ;page in catalogue sector 0 AND #$7F ;mask off lock bit BNE cat1 ;if directories differ skip to next entry LDA modify,Y ;else set directory character to NUL AND #$80 ;and preserve lock bit STA modify,Y .cat1 JSR step ;add 8 to Y BCC cat0 ;and loop (always) .catscn JSR pgcat0 ;page in catalogue sector 0 LDY #$00 ;y=$00, start at first file entry JSR findir ;find unlisted catalogue entry BCC L8E12 ;[D]if entry found then list it JSR pgmain ;[D]else finish catalogue. JSR L9753 ;[D]forget catalogue in JIM pages 2..3 JMP pcrlf ;[D]print newline and exit .L8E12 STY catptr ;save catalogue pointer LDX #$00 ;set filename offset = 0 .L8E16 JSR pgcat0 ;page in catalogue sector 0 LDA catlow,Y ;copy name and directory of first entry AND #$7F ;with b7 clear JSR pgmain ;page in main workspace STA dosram,X ;to workspace INY ;loop until 8 characters copied INX CPX #$08 BNE L8E16 .cattry JSR pgcat0 ;page in catalogue sector 0 JSR findir ;find unlisted catalogue entry BCS scand ;if none remaining then print lowest entry SEC ;else set C=1 for subtraction LDX #$06 ;start at 6th character (LSB) of leaf name: .catsbc JSR pgcat0 ;page in catalogue sector 0 LDA catlow+$06,Y ;get character of entry JSR pgmain ;page in main workspace SBC dosram,X ;subtract character of workspace DEY ;loop until 7 characters compared DEX BPL catsbc JSR step7 ;add 7 to Y JSR pgcat0 ;page in catalogue sector 0 LDA modify,Y ;get directory character (MSB) of entry AND #$7F ;mask off lock bit JSR pgmain ;page in main workspace SBC dosram+$07 ;subtract directory character in workspace BCC L8E12 ;if entry < wksp then copy entry to wksp JSR step ;else add 8 to Y BCS cattry ;and loop (always) .scand JSR pgcat0 ;page in catalogue sector 0 LDY catptr ;get catalogue pointer LDA catdun,Y ;set b7 in first character of leaf name ORA #$80 ;marking entry as listed STA catdun,Y JSR pgmain ;page in main workspace LDA dosram+$07 ;get directory character from workspace CMP catqua ;compare with last one printed BEQ sameq ;if same then add entry to group LDX catqua ;else test previous directory STA catqua ;set previous directory = current directory BNE sameq ;if prev=NUL we go from CSD to other dirs JSR pcrlf ;so print double newline: .cat3 JSR pcrlf ;print newline LDY #$FF ;set Y = $FF going to 0, start of line BNE firstc ;branch (always) .sameq LDY nlflag ;have we printed two entries on this line? BNE cat3 ;if so then print newline and reset counter LDY #$05 ;else tab to next field. Y = 5 spaces JSR yspace ;print number of spaces in Y, set index = 1: .firstc INY STY nlflag ;y = index of next entry on this line LDY catptr ;get catalogue pointer JSR pdspc ;print two spaces JSR prtnam ;print filename from catalogue JMP catscn ;loop until all files listed .nxtcat ;Find next unlisted catalogue entry JSR step ;add 8 to Y .findir ;Find unlisted catalogue entry CPY zdirln ;if catalogue pointer beyond last file BCS L8EA7 ;then return C=1 LDA catdun,Y ;else test first character of leaf name BMI nxtcat ;if b7=1 then already listed, skip .L8EA7 RTS ;else return C=0, catalogue pointer in Y .L8EA8 ;Print volume spec in A (assuming DD) BIT L8EA7 ;set V=1 BVS L8EBE ;always print volume letter B..H after drive .L8EAD ;Print " Drive " plus volume spec in A JSR vstrng EQUS "Drive " NOP .L8EB8 ;Print volume spec in A JSR pgmain ;test density flag BIT denflg .L8EBE PHP ;save density flag on stack PHA ;save volume on stack AND #$07 ;extract bits 2..0, drive 0..7 JSR digout ;print hex nibble PLA ;restore volume PLP ;restore density flag BVC L8ECF ;if single density then only print drive no. LSR A ;else shift volume letter to bits 2..0 LSR A LSR A LSR A BNE L8ED0 ;if volume letter is not A then print it .L8ECF RTS ;else exit .L8ED0 DEY ;decrement Y (no. spaces to print later) CLC ;add ASCII value of "A" ADC #$41 ;to produce volume letter B..H JMP pchr ;print character in A (OSASCI) and exit .L8ED7 ;Print volume title LDY #$0B ;set y = $0B print 11 spaces JSR yspace ;print number of spaces in Y .cat8 JSR pgcat0 ;page in catalogue sector 0 LDA dirlow,Y ;y=0; if Y=0..7 get char from sector 0 CPY #$08 ;if Y=8..11 BCC cat9 JSR pgcat1 ;page in catalogue sector 1 LDA dirhig-$08,Y ;then get character of title from sector 1 .cat9 JSR pchr ;print character in A (OSASCI) INY ;loop until 12 characters of title printed CPY #$0C BNE cat8 JSR vstrng ;print "(" EQUB $0D EQUS "(" NOP JSR pgcat1 ;page in catalogue sector 1 LDA cycno ;get BCD catalogue cycle number JSR bytout ;print hex byte JSR vstrng ;print ")" +newline EQUS ")" EQUB $0D NOP RTS .L8F0B JSR pgmain ;page in main workspace JSR LB74C ;[D]set Z=1 iff current drive is a RAM disc BEQ L8F79 ;[D]if so then print "RAM Disk" BIT denflg ;else test density flag BVS L8F21 ;if double density print "Double density" JSR vstrng ;else print "Single density" EQUS "Sing" BCC L8F29 .L8F21 JSR vstrng EQUS "Doub" NOP .L8F29 JSR vstrng EQUS "le density" LDY #$0F ;set Y = 15 spaces for single density BIT denflg ;test density flag BVC L8F62 ;if single density skip list of volumes LDY #$06 ;else Y = 6 spaces for double density JSR yspace ;print number of spaces in Y LDX #$00 ;set volume index = 0, start at volume A: .L8F45 CLC ;clear carry for add JSR pgaux ;page in auxiliary workspace LDA voltks,X ;test if number of tracks in volume > 0 PHP ;preserve result TXA ;copy index to A to make volume letter PLP ;restore result BNE L8F53 ;if volume present print its letter LDA #$ED ;else A=$ED + $41 = $2E, ".": .L8F53 ADC #$41 ;add ASCII value of "A" JSR pchr ;print character in A (OSASCI) INX ;point to next volume CPX #volums ;have all 8 volumes been listed? BNE L8F45 ;if not then loop JSR pgmain ;page in main workspace LDY #$01 ;else Y=1 space separating volume list: .L8F62 BIT t40flg ;test double-stepping flag BPL L8F76 ;if set manually (*OPT 8,0/1) then end line BVC L8F76 ;if 1:1 stepping was detected then end line JSR yspace ;else print 1 or 14 spaces JSR vstrng ;print "40in80" EQUS "40in80" NOP .L8F76 JMP pcrlf ;print newline .L8F79 ;Print "RAM Disk" JSR LA917 ;print VDU sequence immediate EQUS "RAM Disk" EQUB $FF BCS L8F76 ;print newline (always) .L8F88 ;Print volume spec and boot option LDY #$0D ;set Y = $0D print 13 spaces LDA fdrive ;get current volume JSR L8EAD ;print " Drive " plus volume spec in A JSR yspace ;print number of spaces in Y JSR pgcat1 ;page in catalogue sector 1 JSR vstrng ;print "Option " EQUS "Option " LDA option ;get boot option/top bits volume size JSR sfour ;shift A right 4 places JSR digout ;print hex nibble JSR vstrng ;print " (" EQUS " (" TAX ;transfer to X for use as index JSR L8FF7 ;[D]print boot or Challenger config descriptor JSR vstrng ;print ")"+newline EQUS ")" ;[BUG]prints Challenger config descriptor EQUB $0D ;or garbage if boot option exceeds 3 NOP ;(i.e. if offset $0106 b7 or b6 set) RTS .L8FB8 ;Print CSD and library directories JSR vstrng ;print "Directory :" EQUS "Directory :" LDY #$06 ;6 characters in next field JSR pgmain ;page in main workspace LDX #defqua-defqua ;[D]x = 0 point to default (CSD) directory JSR L8FE8 ;[D]print default or library directory JSR yspace ;print number of spaces in Y JSR vstrng ;print "Library :" EQUS "Library :" LDX #libqua-defqua ;[D]x = 2 point to library directory JSR L8FE8 ;[D]print default or library directory JMP pcrlf ;print newline .L8FE8 ;Print default or library directory LDA defdsk,X ;get default or library volume JSR L8EA8 ;print volume spec in A (assuming DD) JSR pdot ;print a dot LDA defqua,X ;get default or library directory JMP pchr ;print character in A (OSASCI) .L8FF7 ;Print boot or Challenger config descriptor LDA L9007,X ;look up offset of message selected by X TAX ;replace X with offset of message: .L8FFB LDA opttab,X ;get character of message BEQ L9006 ;if NUL terminator reached then exit JSR pchr ;else print character in A (OSASCI) INX ;increment offset BPL L8FFB ;and loop (always) .L9006 .return ;Return from unrecognised keyword RTS ;Table of offsets of boot descriptors 0..3 .L9007 EQUB boff -opttab EQUB bload -opttab EQUB brun -opttab EQUB bexec -opttab ;Table of offsets of Challenger configuration descriptors 4..6 EQUB bout -opttab EQUB b256k -opttab EQUB b512k -opttab ;Table of boot option descriptors 0..3 .opttab .boff EQUS "off" EQUB $00 .bload EQUS "LOAD" EQUB $00 .brun EQUS "RUN" EQUB $00 .bexec EQUS "EXEC" EQUB $00 ;Table of Challenger configuration descriptors 4..6 .bout EQUS "inactive" EQUB $00 .b256k EQUS "256K" EQUB $00 .b512k EQUS "512K" EQUB $00 .comtab ;Challenger command table EQUS "ACCESS" EQUB >access, (L) EQUS "BACKUP" EQUB >cpydsk, EQUS "COMPACT" EQUB >compct,) EQUS "CONFIG" EQUB >LAAF6, ) EQUS "COPY" EQUB >cpyfil, EQUS "DELETE" EQUB >delete, EQUS "DESTROY" EQUB >destry, EQUS "DIR" EQUB >set, ) EQUS "DRIVE" EQUB >drive, ) EQUS "ENABLE" EQUB >enable,L8BFF, ) EQUS "FDCSTAT" EQUB >LB76C, L8C1C, EQUS "LIB" EQUB >slib, ) EQUS "MAP" EQUB >map, ) EQUS "RENAME" EQUB >rename, EQUS "STAT" EQUB >L8D66, ) EQUS "TITLE" EQUB >title, EQUS "WIPE" EQUB >wipe, <wipe EQUB $02 ;syntax $2: <afsp> EQUB >defcom,<defcom ;unrecognised command, *RUN it $9825 .initbl ;Utility command table EQUS "BUILD" EQUB >build, <build EQUB $01 ;syntax $1: <fsp> EQUS "DISC" EQUB >L820E, <L820E EQUB $00 ;syntax $0: no arguments EQUS "DUMP" EQUB >dump, <dump EQUB $01 ;syntax $1: <fsp> EQUS "FORMAT" EQUB >LAE88, <LAE88 EQUB $8A ;syntax $A: (<drv>) b7=1 EQUS "LIST" EQUB >list, <list EQUB $01 ;syntax $1: <fsp> EQUS "TYPE" EQUB >type, <type EQUB $01 ;syntax $1: <fsp> EQUS "VERIFY" EQUB >verify,<verify EQUB $8A ;syntax $A: (<drv>) b7=1 EQUS "VOLGEN" EQUB >LB140, <LB140 EQUB $8A ;syntax $A: (<drv>) b7=1 ;entry not printed in *HELP UTILS EQUS "DISK" EQUB >L820E, <L820E EQUB $00 ;syntax $0: no arguments EQUB >return,<return ;unrecognised utility, return $91A7 .hlptab ;*HELP keyword table EQUS "CHAL" EQUB >help, <help EQUB $00 ;syntax $0: no arguments EQUS "DFS" EQUB >help, <help EQUB $00 ;syntax $0: no arguments EQUS "UTILS" EQUB >pmhelp,<pmhelp EQUB $00 ;syntax $0: no arguments EQUB >return,<return ;unrecognised keyword, return $91A7 ;on entry A=string offset (=Y to GSINIT) ;XY=address of table .wname0 ;Search for command or keyword in table JSR L921E ;set up trampoline to read table at XY TAY JSR setupr ;call GSINIT with C=0 TYA ;save offset of start of command line PHA LDA (linptr),Y ;[D]get first character of command AND #$5F ;make uppercase CMP #$43 ;is it C? BNE L91C4 ;if not then search in table INY ;else skip to next character LDA (linptr),Y ;fetch it CMP #$20 ;is it a space? BNE L91C4 ;if not then search in table PLA ;else discard offset of start of command line INY ;skip past the space TYA ;save new offset of start of command: PHA ;accept *C <command> as alias of *<command>. .L91C4 PLA ;restore offset of start of command line TAY PHA LDX #$00 ;start at current trampoline address JSR tramp ;fetch first byte SEC ;if terminator,empty keyword matches anything BMI L920A ;so jump to following action address with C=1 DEX ;else decrement X and Y to stay in place: DEY .firch INX ;advance command line and table offsets INY JSR tramp ;get byte from table BMI chklst ;if terminator, check command also terminates EOR (linptr),Y ;else compare with character of command AND #$5F ;make comparison case-insensitive BEQ firch ;if equal then compare next characters LDA (linptr),Y ;else get mismatching character of command CMP #$2E ;is it a dot? PHP ;save the result .minus INX ;scan keyword in table JSR tramp ;get byte from table BPL minus ;loop until terminator reached INX ;skip action address, 2 bytes INX PLP ;is the command an abbreviation or a mismatch? BNE L91F3 ;if mismatch then skip syntax, scan next kywd JSR tramp ;else test syntax byte BPL L9206 ;if b7=0 accept cmd, else abbrev. not allowed: .L91F3 INX ;skip syntax byte JSR L922D ;add X to trampoline address JMP L91C4 ;scan next keyword .chklst LDA (linptr),Y ;get character of command JSR caps ;set C=0 iff character in A is a letter BCS finish ;if C=1 accept command, else longer than kywd INX ;so skip action address, 2 bytes INX BCC L91F3 ;skip syntax byte and scan next keyword (always) .L9206 ;Accept abbreviated command DEX ;backtrack to action address, 2 bytes DEX INY ;advance command line offset past the dot: .finish ;Accept command CLC ;set C=0 command valid .L920A PLA ;discard offset to start of command JSR tramp ;get action address high byte STA action+$01 ;store high byte of vector INX ;advance to next byte of table JSR tramp ;get action address low byte STA action+$00 ;store low byte of vector INX ;return X=offset of syntax byte RTS ;y=offset of command line tail. .L921E ;Set up trampoline to read table at XY PHA LDA #$BD ;$BD = LDA abs,X .L9221 STA tramp+$00 ;instruction at $00AA = LDA xy,X STX tramp+$01 STY tramp+$02 LDA #$60 ;instruction at $00AD = RTS STA tramp+$03 PLA ;restore A RTS .L922D ;Add X to trampoline address CLC TXA ADC tramp+$01 ;add X to low byte of LDA,X address STA tramp+$01 BCC L9237 ;carry out to high byte INC tramp+$02 .L9237 RTS .stxylp ;Set GSINIT pointer to XY, set Y=0 STX linptr+$00 STY linptr+$01 LDY #$00 RTS .wipe ;*WIPE JSR L92DD ;ensure file matching wildcard argument .qdel0 JSR prtnam ;print filename from catalogue JSR vstrng ;print " : " EQUS " : " NOP JSR pgcat0 ;page in catalogue sector 0 LDA modify,Y ;test lock bit BPL L925A ;if unlocked then ask to delete JSR LA94B ;else deletion not allowed, print letter N JMP qdel2 ;find next matching file .L925A JSR getyn ;ask user yes or no BNE qdel2 ;if user replies no then find next match JSR chksam ;else ensure disc not changed JSR delfil ;delete catalogue entry JSR dirout ;write volume catalogue JSR L9300 ;shift cat pointer to follow shifted files .qdel2 JSR pcrlf ;print newline JSR next ;find next matching file BCS qdel0 ;if found then wipe the file RTS ;else exit .delete ;*DELETE JSR clrwld ;disallow wildcard characters in filename JSR L92E0 ;ensure file matching argument JSR inform ;print *INFO line if verbose JSR delfil ;delete catalogue entry JMP dirout ;write volume catalogue ;Note: *DESTROY deletes all matching files, locked or unlocked. .destry ;*DESTROY JSR chkena ;ensure *ENABLE active JSR L92DD ;ensure file matching wildcard argument .L9289 JSR prtnam ;print filename from catalogue JSR pcrlf ;print newline JSR next ;find next matching file BCS L9289 ;loop until all matching files listed JSR vstrng EQUB $0D EQUS "Delete (Y/N) ? " NOP JSR getyn ;ask user yes or no BEQ L92B0 ;if user replies yes then proceed JMP pcrlf ;else print newline and exit .L92B0 JSR chksam ;ensure disc not changed JSR lookup ;search for file in catalogue .L92B6 JSR pgcat0 ;page in catalogue sector 0 LDA modify,Y ;unlock catalogue entry! AND #$7F STA modify,Y JSR delfil ;delete catalogue entry JSR L9300 ;subtract 8 from catalogue pointer JSR next ;find next matching file BCS L92B6 JSR dirout ;write volume catalogue JSR vstrng ;print "Deleted" and exit EQUB $0D EQUS "Deleted" EQUB $0D NOP RTS .L92EC ;Pack b17,16 of length into catalogue entry JSR lfour ;shift A left 4 places JSR pgcat1 ;page in catalogue sector 1 EOR cathig+$06,X ;replace b5,b4 of top bits with b5,b4 from A AND #$30 EOR cathig+$06,X STA cathig+$06,X ;store top bits back in catalogue JMP pgmain ;page in main workspace .L9300 ;Subtract 8 from catalogue pointer LDY catofs ;get catalogue pointer JSR unstep ;subtract 8 from Y STY catofs ;store catalogue pointer RTS .drive ;*DRIVE JSR LAA0E ;parse volume spec from argument LDA fdrive ;get current volume STA defdsk ;set as default volume RTS .set ;*DIR LDX #defqua-defqua ;point to default drive and directory EQUB $AD ;$AD=LDA abs; skip next two bytes .slib ;*LIB LDX #libqua-defqua ;point to library drive and directory LDA defqua,X ;get default/library directory STA qualif ;set as current directory LDA defdsk,X ;get default/library volume STA fdrive ;set as current volume TXA ;save offset PHA JSR setupr ;call GSINIT with C=0 BEQ L932C ;if argument present JSR readd0 ;then parse directory spec .L932C PLA ;restore offset TAX LDA qualif ;get current directory STA defqua,X ;set as default/library directory LDA fdrive ;get current volume STA defdsk,X ;set as default/library volume RTS .title ;*TITLE JSR chksyn ;call GSINIT with C=0 and require argument JSR setdef ;set current vol/dir = default, set up drive JSR L962F ;load volume catalogue L4 LDX #$0B ;first offset to store = 11 LDA #$00 ;set title to 12 NULs: .clrti0 JSR titwit ;store character of title DEX ;loop until 12 characters stored BPL clrti0 ;finish with X=$FF .L934C JSR gsread ;call GSREAD BCS titend ;if end of argument write catalogue INX ;[D]else point X to next character JSR titwit ;store character of title CPX #$0B ;is this the twelfth character written? BNE L934C ;if not then loop to write more, else: .titend JMP dirout ;write volume catalogue and exit .titwit ;Store character of title CPX #$08 ;if offset is 8 or more BCC titllw JSR pgcat1 ;page in catalogue sector 1 STA dirhig-$08,X ;then store in sector 1, X=8..11 RTS .titllw JSR pgcat0 ;page in catalogue sector 0 STA dirlow,X ;else store in sector 0, X=0..7 RTS .access ;*ACCESS JSR setwld ;allow wildcard characters in filename JSR L92E6 ;set current file from argument LDX #$00 ;preset X=$00 file unlocked JSR setupr ;call GSINIT with C=0 BNE acces5 ;if argument is empty .L937B STX attrib ;then attribute mask = $00, file unlocked JSR errlok ;ensure matching file in catalogue .acces1 JSR chkopn ;ensure file not open (mutex) JSR pgcat0 ;page in catalogue sector 0 LDA modify,Y ;get directory character from catalogue AND #$7F ;mask off old attribute ORA attrib ;apply new attribute STA modify,Y ;put back in catalogue JSR inform ;print *INFO line if verbose JSR next ;find next matching file BCS acces1 ;if found then set its attribute BCC titend ;else write volume catalogue and exit .acces6 LDX #$80 ;found L, set bit 7 to indicate file locked: .acces5 JSR gsread ;call GSREAD, get character of attribute BCS L937B ;if end of string then set attribute CMP #$4C ;else is character capital L? BEQ acces6 ;if so then set bit 7 JSR illmsg ;else raise "Bad attribute" error. EQUB $CF EQUS "attribute" EQUB $00 .dirdo ;Create file from OSFILE block JSR frmnam ;set current file from argument pointer JSR lookup ;search for file in catalogue BCC filels ;if found JSR delfil ;then delete catalogue entry .filels LDA strtlo ;save start address low word PHA LDA strthi PHA SEC ;subtract end address - start address LDA endlo ;(24 bits) yielding file length SBC strtlo STA lenlo LDA endhi SBC strthi STA lenhi LDA endhl SBC strthl STA lenhl JSR genfil ;create catalogue entry LDA strthh ;copy start address high word to data pointer STA ldlow+$03 LDA strthl STA ldlow+$02 PLA ;restore low word to data pointer STA lodhi PLA STA lodlo RTS .noroom ;Raise "Disk full" error JSR dskmsg EQUB $C6 ;number = $C6, "Disk full", cf. $9EDF EQUS "full" EQUB $00 .genfil ;Create catalogue entry LDA #$00 STA lbahi ;set MSB of LBA = 0 JSR LA4F8 ;return no. reserved sectors in data area STA lbalo ;set as LSB of LBA JSR pgcat1 ;page in catalogue sector 1 LDY dirlen ;get number of files in catalogue * 8 CPY #$F8 ;if there are fewer than 31 files BCC L947F ;then jump into loop JSR estrng ;else raise "Catalogue full" error. EQUB $BE EQUS "Catalogue full" EQUB $00 .L9420 BIT nice ;if b6=0 will not accept shorter allocation BVC noroom ;then raise "Disk full" error LDA #$00 STA lenhi ;else zero LSB size of file to be fitted STA lenhl ;and MSB STA lbahi ;set MSB of LBA = 0 JSR LA4F8 ;return no. reserved sectors in data area STA lbalo ;set as LSB of LBA JSR pgcat1 ;page in catalogue sector 1 LDY dirlen ;get number of files in catalogue * 8 JMP L9443 ;jump into loop .L943A TYA BEQ L9460 ;if cat ptr = 0 then test fit JSR unstep ;else subtract 8 from Y JSR L94D6 ;[D]calculate LBA of end of file .L9443 JSR L9507 ;calculate slack space after file BEQ L943A ;if no slack space then test prev cat entry SEC ;else C=1 for subtraction JSR L9521 ;test if new file will fit after current file BCC L943A ;if file won't fit then test prev cat entry STX lenhl ;else set MSB file size to MSB slack space LDA headlo ;get LSB slack space STA lenhi ;set LSB file size LDA lbahi ;this finds the largest slack space on volume STA sklbah ;save MSB LBA of slack space LDA lbalo STA sklbal ;save LSB LBA of slack space STY icatmp ;save catalogue offset of insertion point BCS L943A ;and loop (always) .L9460 LDA lenhi ;test slack space found ORA lenhl ;if no slack space available BEQ noroom ;then raise "Disk full" error LDA sklbah ;else get MSB LBA of slack space STA lbahi ;set MSB start LBA of file LDA sklbal ;get LSB LBA of slack space STA lbalo ;set LSB start LBA of file LDY icatmp ;restore catalogue offset of insertion point LDA #$00 STA lenlo ;clear LSB length?? (probably redundant) BEQ L9489 ;and branch (always) .L9476 TYA BEQ L9420 ;if cat ptr = 0 then test fit JSR unstep ;else subtract 8 from Y JSR L94D6 ;calculate LBA of end of file .L947F JSR L9507 ;calculate slack space after file BEQ L9476 ;if no slack space then test prev cat entry JSR dskadr ;test if new file will fit after current file BCC L9476 ;if file won't fit then test prev cat entry .L9489 STY inscat ;else insert new catalogue entry here JSR pgcat1 ;page in catalogue sector 1 LDY dirlen ;point Y to last valid catalogue entry: .moveup CPY inscat ;compare pointer with insertion point BEQ L94AA ;stop copying if insertion point reached JSR pgcat0 ;page in catalogue sector 0 LDA catlow-$01,Y ;else copy current catalogue entry STA catlow+$07,Y ;to next slot JSR pgcat1 ;page in catalogue sector 1 LDA cathig-$01,Y ;leaving one slot open STA cathig+$07,Y ;for new catalogue entry DEY ;decrease pointer to work back from end BCS moveup ;and loop (always) .L94AA JSR pgmain ;page in main workspace JSR encode ;compose top bits exec/length/load/start JSR L9529 ;write filename+dir into catalogue at Y=0..$F0 JSR pgcat1 ;page in catalogue sector 1 .varin ;Write load/exec/length/start into catalogue LDA wrkcat-$01,X ;x=8..1 copy from $BE..$C5 DEY ;y=catalogue pointer + 7..0 STA cathig,Y ;copy to catalogue address fields DEX ;loop until 8 bytes copied BNE varin JSR inform ;print *INFO line if verbose TYA ;save catalogue pointer PHA JSR pgcat1 ;page in catalogue sector 1 LDY dirlen ;get number of files in catalogue * 8 JSR step ;add 8 to Y STY dirlen ;store new file count JSR dirout ;write volume catalogue PLA ;restore catalogue pointer TAY RTS .L94D6 ;Calculate LBA of end of file JSR L94EB ;calculate number of sectors used by file CLC LDA cathig+$07,Y ;get LSB start sector ADC lbalo ;add LSB file length in sectors STA lbalo ;replace with new LSB start sector LDA cathig+$06,Y ;get top bits exec/length/load/start sector AND #$03 ;extract MSB start sector ADC lbahi ;add MSB file length in sectors STA lbahi ;replace with new MSB start sector RTS ;Challenger version of routine; DDOS version is at LA714. ;Returns to lbalo/hi and leaves catalogue paged in (vs. todolo/hi and main). .L94EB ;Calculate number of sectors used by file JSR pgcat1 ;page in catalogue sector 1 LDA cathig+$04,Y ;get LSB length CMP #$01 ;c=1 iff LSB >0 LDA cathig+$05,Y ;add C to 2MSB length, rounding up ADC #$00 ;(Y points to 8 bytes before file entry) STA lbalo ;[D]next two instructions swapped vs DDOS PHP ;save carry flag from addition LDA cathig+$06,Y ;get top bits exec/length/load/start sector JSR isolen ;extract length from b5,4 to b1,0 PLP ;restore carry flag ADC #$00 ;add C to MSB length, rounding up STA lbahi ;store length in sectors in zero page RTS .L9507 ;Calculate slack space after file JSR pgcat1 ;page in catalogue sector 1 SEC LDA cathig-$01,Y ;get LSB LBA of preceding file in catalogue SBC lbalo ;subtract LSB LBA of end of this file STA headlo ;store LSB size of slack space LDA cathig-$02,Y ;get top bits exec/length/load/start sector AND #$03 ;extract MSB start sector SBC lbahi ;subtract MSB LBA of end of this file TAX ;return MSB slack size in X, LSB in $B0 ORA headlo ;test result, Z=1 if file follows without gap RTS ;[BUG] can underflow due to overlaps .dskadr ;Test if new file will fit after current file LDA #$00 ;if file includes partial sector CMP lenlo ;then C=0 include it in the comparison: .L9521 LDA headlo ;get LSB slack space SBC lenhi ;subtract LSB file size in sectors TXA ;a=MSB slack space SBC lenhl ;subtract MSB file size in sectors RTS ;return C=1 if file will fit .L9529 ;Write filename+dir into catalogue at Y=0..$F0 JSR pgcat0 ;page in catalogue sector 0 LDX #$00 .L952E LDA wrknam,X ;get character of current filename+dir STA catlow,Y ;store in catalogue INY ;increment both offsets INX CPX #$08 ;loop until 8 bytes copied. BNE L952E RTS .encode ;Compose top bits exec/length/load/start LDA exlow+$02 ;get b17,b16 exec address AND #$03 ;place in b1,b0 of A, clear b7..b2 ASL A ;shift A left 2 places ASL A ;a = ....ee.. EOR lenhl ;place b17,b16 of length in b1,b0 AND #$FC ;keep b7..b2 of A EOR lenhl ;a = ....eell ASL A ;shift A left 2 places ASL A ;a = ..eell.. EOR ldlow+$02 ;place b17,b16 of load address in b1,b0 AND #$FC ;keep b7..b2 of A EOR ldlow+$02 ;a = ..eelldd ASL A ;shift A left 2 places ASL A ;a = eelldd.. EOR lbahi ;place b10,b9 of start LBA in b1,b0 AND #$FC ;keep b7..b2 of A EOR lbahi ;a = eellddss STA wrkcat+$06 ;set top bits exec/length/load/start sector RTS .rename ;*RENAME JSR clrwld ;disallow wildcard characters in filename JSR L92E6 ;set current file from argument JSR LAAD9 ;map current volume to physical volume PHA ;save source volume TYA ;save command line offset PHA JSR errlok ;ensure matching file in catalogue JSR chkopl ;ensure file not locked or open (mutex) STY lokcat ;save pointer to file entry PLA ;restore command line offset TAY JSR chksyn ;call GSINIT with C=0 and require argument LDA defqua ;set current directory = default directory STA qualif JSR getnm0 ;parse file spec PLA ;restore source volume STA renvol ;save in workspace JSR LAAD9 ;map current volume to physical volume CMP renvol ;compare with source volume BEQ L95F6 ;if equal then rename the file .L9587 JMP L9849 ;else rename across volumes, "Bad command". .enable ;*ENABLE JSR setupr ;[D]call GSINIT with C=0 BEQ L957D ;if no argument then enable *commands LDX #$00 ;else X=0 offset into "CAT" string: .L9563 JSR gsread ;call GSREAD BCS L9587 ;if end of argument then "Bad command" CMP L958A,X ;else compare char with char of "CAT" BNE L9587 ;if unequal then "Bad command" INX ;else increment offset CPX #$03 ;loop until whole "CAT" string compared BNE L9563 JSR gsread ;call GSREAD BCC L9587 ;if argument continues then "Bad command" LDA #$80 ;else *ENABLE CAT STA enacat ;b7=1 emulate Acorn DFS's main memory use RTS .L957D ;*ENABLE (no argument) LDA #$80 STA dopint ;>0 disc operation is interruptible LDA #$01 ;set *ENABLE flag = 1; will be nonnegative STA enaflg ;(after FSC 8) for next *command only. RTS .L958A EQUS "CAT" ;CAT keyword for *ENABLE .decodl ;Expand 18-bit load address to 32-bit PHA LDA #$00 ;set MSB of address = $00 PHA LDA wrkcat+$06 ;get top bits exec/length/load/start sector JSR isolod ;extract b3,b2 of A CMP #$03 ;if either bit clear then a Tube address BNE L95A0 ;so set high word = high word of tube address PLA ;else discard the high word: PLA .L959C ;Set high word of OSFILE load address = $FFFF PHA LDA #$FF PHA .L95A0 ;Set high word of OSFILE load address JSR pgmain ;page in main workspace STA ldlow+$02 PLA STA ldlow+$03 PLA RTS .decode ;Expand 18-bit exec address to 32-bit JSR pgmain ;page in main workspace LDA #$00 ;clear MSB of 32-bit address STA exlow+$03 LDA wrkcat+$06 ;get top bits exec/length/load/start sector JSR isoexe ;extract b7,b6 of A CMP #$03 ;if b7,b6 both set BNE decdl0 LDA #$FF ;then a host address, set high word = $FFFF STA exlow+$03 .decdl0 STA exlow+$02 ;else set 2MSB parasite address $0..2FFFF RTS .L95F6 JSR lookup ;search for file in catalogue BCC L9606 ;if not found then update filename+dir JSR filmsg ;else raise "File exists" error. EQUB $C4 EQUS "exists" EQUB $00 .L9606 ;Update filename+dir in catalogue LDY lokcat ;get pointer to file entry JSR L9529 ;write filename+dir into catalogue: .dirout ;Write volume catalogue L4 JSR pgcat1 ;page in catalogue sector 1 CLC ;add 1 to BCD catalogue cycle number SED LDA cycno ADC #$01 STA cycno CLD JSR wrint0 ;set xfer call no. = write, claim NMI JMP dirot1 ;transfer volume catalogue and exit .dirld ;Ensure current volume catalogue loaded JSR pgmain ;page in main workspace JSR LAAD9 ;map current volume to physical volume CMP catdrv ;compare with volume of loaded catalogue BNE L962F ;if unequal then load volume catalogue JSR LBD06 ;else if motor is on BEQ L9657 ;then present cat. and release NMI else: .L962F JSR savita ;save AXY .dirldy ;Load volume catalogue L4 JSR rdint0 ;set xfer call no. = read, claim NMI: .dirot1 ;Transfer volume catalogue LDA #$00 STA tubflg ;transferring to host, not Tube LDA #$80 STA dopint ;>0 disc operation is interruptible LDA txcall ;get data transfer call number ORA #$80 ;b7=1 data address in JIM space STA txcall ;update data transfer call number JSR LABB5 ;detect disc format/set sector address JSR LAAD9 ;map current volume to physical volume STA catdrv ;set drive and volume of loaded catalogue JSR L968B ;transfer disc/volume catalogue L3 BEQ L9657 ;if zero status release NMI and exit JMP dskflt ;else raise "Disk fault" error. .L9657 ;Present catalogue and release NMI JSR pgmain ;page in main workspace BIT enacat ;[D]test b7=*ENABLE CAT BPL L9679 ;if enabled JSR pgcat0 ;page in catalogue sector 0 LDX #$00 ;then start at offset 0: .L9664 LDA dirlow,X ;copy catalogue sector 0 STA ddirlo,X ;to main memory page $E, emulating DFS use INX ;loop until entire sector copied BNE L9664 JSR pgcat1 ;page in catalogue sector 1 .L9670 LDA dirhig,X ;copy catalogue sector 1 STA ddirhi,X ;to main memory page $F, emulating DFS use INX ;loop until entire sector copied BNE L9670 .L9679 JSR pgmain ;page in main workspace JMP nmirel ;release NMI .LB05B ;Initialise volume catalogue by no. tracks JSR LB552 ;multiply by no. sectors per track LDY #$00 ;sector number = 0 .LB062 ;Initialise volume catalogue JSR savita ;save AXY STY txlbal ;set LSB absolute LBA = 2 * volume letter LDA #$00 STA txlbah ;set MSB of absolute LBA = 0 JSR LB3EA ;clear catalogue sectors JSR pgcat1 ;page in catalogue sector 1 LDA prodh ;set MSB volume size, boot option OFF STA dirhig+$06 LDA prodl ;set LSB volume size STA dirhig+$07 JSR LAAD9 ;map current volume to physical volume STA catdrv ;set drive and volume of loaded catalogue: ;Write disc/volume catalogue L3 LDA #$81 ;data transfer call $81 = write data from JIM JSR pgmain ;page in main workspace STA txcall ;set data transfer call number .L968B ;Transfer disc/volume catalogue L3 JSR savit ;save XY JSR L96A5 ;set data pointer to $0200 LDX #l3tris ;set X = $03, three possible attempts: .L9693 LDA #$00 ;512 bytes to transfer STA rtxszl LDA #$02 STA rtxszh JSR LBA18 ;transfer data L2 BEQ L96A4 ;if zero status then success, return DEX ;else decrement attempts counter BNE L9693 ;if not tried 3 times then try again DEX ;else return Z=0, failed .L96A4 RTS .L96A5 ;Set data pointer to $0200 LDA #<jcat ;[D]this addresses catalogue sector 0 in JIM STA nmiusr+$00 ;[D]DDOS had $0E00 LDA #>jcat STA nmiusr+$01 RTS .chktub ;Open Tube data transfer channel JSR pgmain ;page in main workspace PHA ;a=Tube service call, save in stack[D]no wksp LDA lodlo ;reform address at $FDB3..B6 from $BE,F STA ldlow+$00 LDA lodhi STA ldlow+$01 LDA ldlow+$02 ;and high bytes of address AND ldlow+$03 ;a=$FF if address is in the host ORA notube ;a=$FF if Tube absent ($FDCD=NOT MOS flag!) EOR #$FF ;invert; A>0 if transferring over Tube STA tubflg ;store Tube flag SEC BEQ cktub1 ;if A=0 then no need for Tube, exit C=1 JSR clatub ;else claim Tube LDX #<ldlow ;point XY at address LDY #>ldlow PLA ;restore Tube call number PHA JSR taddrl ;call Tube service CLC ;exit C=0 as Tube was called .cktub1 PLA ;preserve Tube call number on exit RTS .clatub ;Claim Tube PHA .clatb0 LDA #$C0+dftbid ;tube service call = $C0 + ID for DFS (1) JSR taddrl ;call Tube service BCC clatb0 ;loop until C=1, indicating claim granted PLA RTS .saver ;OSFILE 0 = save file LDA #$00 STA nice ;b6=0 will not accept shorter allocation JSR dirdo ;create file from OSFILE block JSR tryfl0 ;set up pointer to user's OSFILE block JSR chukbk ;return catalogue information to OSFILE block .blkwr ;Write ordinary file L5 JSR wtint ;prepare to write from user memory JMP blkxx ;transfer ordinary file L5 .loader ;OSFILE $FF = load file JSR frmlok ;ensure file matching argument in catalogue JSR tryfl0 ;set up pointer to user's OSFILE block JSR chukbk ;return catalogue information to OSFILE block .loadt ;Load file into memory STY lodcat LDX #$00 LDA exelo ;test offset 6, LSB exec from OSFILE block BNE reloc ;if non-zero, use load address in catalogue INY ;else skip first two bytes of catalogue entry INY LDX #$02 ;skip over user-supplied load address in zp BNE LA214 ;branch (always) .reloc JSR pgcat1 ;page in catalogue sector 1 LDA cathig+$06,Y ;get top bits exec/length/load/start sector STA wrkcat+$06 JSR pgmain ;page in main workspace JSR decodl ;expand 18-bit load address to 32-bit .LA214 JSR pgcat1 ;page in catalogue sector 1 .loadt0 LDA cathig,Y ;copy load/exec/length/start from catalogue STA wrkcat,X ;into low words of OSFILE block INY ;(our copy, gave user theirs at $A1F5) INX CPX #$08 ;loop until 8 or 6 bytes copied, 0..7/2..7 BNE loadt0 JSR decode ;expand 18-bit exec address to 32-bit LDY lodcat JSR inform ;print *INFO line if verbose: .blkrd ;Read ordinary file L5 JSR rdint ;prepare to read to user memory .blkxx ;Transfer ordinary file L5 JSR atot ;prepare ordinary file transfer JMP L9719 ;transfer data and release Tube .L970D ;Read extended file L5 JSR rdint ;prepare to read to user memory JMP L9716 ;transfer extended file L5 .L9713 ;Write extended file L5 JSR wtint ;prepare to write from user memory .L9716 ;Transfer extended file L5 JSR L8AE4 ;prepare extended file transfer JMP L9719 ;transfer data and release Tube .L9721 ;Write ordinary file from JIM L5 LDA #$81 ;data transfer call $81 = write data from JIM EQUB $AE ;$AE=LDA abs; skip next two bytes .L9724 ;Read ordinary file to JIM L5 LDA #$80 STA txcall ;set data transfer call number JSR nmicla ;claim NMI JSR atot ;prepare ordinary file transfer .L9719 JSR chkget ;transfer data and report errors L4: .L96E6 ;Release Tube PHA LDA tubflg ;load Tube flag, A>0 if Tube in use BEQ reltb0 ;if not in use then exit, else: .reltb1 LDA #$80+dftbid ;tube service call = $80 + ID for DFS (1) JSR taddrl ;call Tube service .reltb0 PLA RTS .reltub ;Release Tube if present PHA LDA #$EA ;OSBYTE $EA = read Tube presence flag JSR readby ;call OSBYTE with X=0, Y=$FF TXA ;test X, X=$FF if Tube present BNE reltb1 ;if Tube present then release Tube PLA RTS .rdint ;Prepare to read to user memory LDA #$01 ;Tube service 1 = write single bytes to R3 JSR chktub ;open Tube data transfer channel .rdint0 ;Set xfer call no. = read, claim NMI LDA #$00 ;data transfer call $00 = read data BEQ L974A ;branch (always) .wtint ;Prepare to write from user memory LDA #$00 ;Tube service 0 = read single bytes from R3 JSR chktub ;open Tube data transfer channel .wrint0 ;Set xfer call no. = write, claim NMI JSR LB905 ;[D]test write protect state of current drive BNE L975C ;[D]if not then "Disk read only" LDA #$01 ;else data transfer call $01 = write data .L974A JSR pgmain ;page in main workspace STA txcall ;set data transfer call number JSR nmicla ;claim NMI: .L9753 ;Forget catalogue in JIM pages 2..3 JSR pgmain ;page in main workspace LDA #$FF STA catdrv ;no catalogue in JIM pages 2..3 RTS .L975C .LA884 ;Raise "Disk read only" error JSR dskmsg EQUB $C9 EQUS "read only" EQUB $00 .wfscm ;FSC JSR pgmain ;page in main workspace CMP #fschtb-fscltb ;[D]if call outside range 0..11 BCS wbgpbr ;then exit STX fscx ;else save X TAX ;transfer call number to X as index LDA fschtb,X ;get action address high byte PHA ;save on stack LDA fscltb,X ;get action address low byte PHA ;save on stack TXA ;restore call number to A LDX fscx ;restore X on entry .wbgpbr RTS ;jump to action address .wfopt ;FSC 0 = *OPT JSR savita ;save AXY CPX #(wfopte-L97F4)>>1 ;[D]is option outside range 0..9? BCS L9788 ;if so then raise "Bad option" error TXA ;else double option in X for use as offset ASL A TAX LDA L97F4+$01,X ;get action address high byte PHA ;save it on stack LDA L97F4+$00,X ;get action address low byte PHA ;save it on stack RTS ;jump to action address .L9788 ;Raise "Bad option" error JSR illmsg EQUB $CB EQUS "option" EQUB $00 ;*OPT 0 = restore default FS options .setmon ;*OPT 1 = set reporting level LDX #$FF TYA ;is verbosity level =0? BEQ stmon0 ;if so then set flag = $FF INX ;else level >0, set flag = 0. .stmon0 STX monflg RTS .booto ;*OPT 4 set boot option TYA ;save requested option PHA JSR setdef ;set current vol/dir = default, set up drive JSR dirldy ;load volume catalogue PLA ;restore option JSR lfour ;shift A left 4 places JSR pgcat1 ;page in catalogue sector 1 EOR option ;xor new option with old AND #$30 ;clear all but option bits 5,4 EOR option ;b5,4 contain new option, others preserved STA option ;store new option in catalogue JMP dirout ;write volume catalogue and exit. ;The structure and syntax of these options derive from those of Opus EDOS ;where they operate natively. Here Challenger translates the parameters ;confirming that EDOS precedes the release of Challenger and may even ;have had an installed base with which compatibility was to be maintained. ;Opus literature confirms that these options replaced the original ;command set still found in DDOS. .L97BA ;*OPT 6 = set density LDA #$40 ;preset A=$40 force double density CPY #$12 ;if parameter = 18 BEQ L97CA ;then force double density in disc ops ASL A ;else A=$80 automatic density CPY #$00 ;if parameter = 0 BEQ L97CA ;then detect density during FS operations ASL A ;else A=$00 force single density CPY #$0A ;if parameter <> 10 BNE L9788 ;then raise "Bad option" error, else: .L97CA STA denflg ;store *OPT 6 density setting RTS ;Challenger's *OPT 7 replaces EDOS's high-risk setting of the number ;of volume catalogues on track 0. .L97CE ;*OPT 7 = set stepping rate CPY #$04 ;if parameter outside range 0..3 BCS L9788 ;then raise "Bad option" error TYA ;else 0=slow..3=fast; reverse mapping EOR #$03 ;now in internal format 0=fast..3=slow STA speed ;store mask to apply to WD 1770 commands RTS .L97D9 ;*OPT 8 = set double-stepping LDA #$40 ;preset A=$40 force double-stepping INY ;map $FF,0,1 to 0..2 CPY #$02 ;if parameter = 1 BEQ L97E8 ;then force double-stepping BCS L9788 ;if not $FF, 0 or 1 then "Bad option" ASL A ;else A=$80 automatic stepping CPY #$01 ;if parameter = $FF BCC L97E8 ;then detect stepping during FS operations ASL A ;else A=$00 force 1:1 stepping: .L97E8 STA t40flg ;store *OPT 8 tracks setting RTS .L97EC ;*OPT 9 = set save ROM slot no. CPY #$10 ;if parameter not in range $0..F BCS L9788 ;then raise "Bad option" error STY savrom ;else store *OPT 9 saverom during disc ops RTS ;Table of action addresses for *OPT commands 0..9 .L97F4 EQUW setmon-$01 ;*OPT 0 = restore default FS opts $9793 EQUW setmon-$01 ;*OPT 1 = set reporting level $9793 EQUW L9788-$01 ;*OPT 2 = (invalid) $9788 EQUW L9788-$01 ;*OPT 3 = (invalid) $9788 EQUW booto-$01 ;*OPT 4 = set boot option $979D EQUW L9788-$01 ;*OPT 5 = (invalid) $9788 EQUW L97BA-$01 ;*OPT 6 = set density $97BA EQUW L97CE-$01 ;*OPT 7 = set stepping rate $97CE EQUW L97D9-$01 ;*OPT 8 = set double-stepping $97D9 EQUW L97EC-$01 ;*OPT 9 = set save ROM slot no. $97EC .wfopte .wfeof ;FSC 1 = read EOF state PHA ;save AY TYA PHA TXA ;transfer file handle to Y TAY JSR dcrych ;ensure file handle valid and open TYA ;a=y = channel workspace pointer JSR pcmp ;compare PTR - EXT BNE wfeof0 ;if PTR <> EXT (blech!) then return 0 LDX #$FF ;else return $FF, we are at end of file BNE wfeof2 .wfeof0 LDX #$00 .wfeof2 PLA ;restore AY and exit TAY PLA RTS .wnota ;FSC 2/4/11 = */, *RUN, *RUN from library JSR stxylp ;set GSINIT pointer to XY, set Y=0 IF _MASTER BEQ L9826 ;always branch .defcom ;FSC 3 with *command not in table ASL A ;ensure A is even: .L9826 STA fscno ;save call number (<> $0B from FSC 3) ELSE .defcom ;FSC 3 with *command not in table ENDIF JSR supld ;copy argument ptr and load to cat address STY linadr+$01 ;store offset of start of command line JSR frmnam ;set current file from argument pointer STY linadr+$00 ;store offset of command line tail JSR lookup ;search for file in catalogue BCS defsuc ;if found then execute command binary LDY linadr+$01 ;else restore command line start offset LDA libqua ;get library directory STA qualif ;set as current directory LDA libdsk ;get library drive and volume STA fdrive ;select volume in A JSR frmnm1 ;parse file spec from argument pointer JSR lookup ;search for file in catalogue BCS defsuc ;if found then execute it IF _MASTER LDA #$0B ;else FSC 11 = *RUN from library CMP fscno ;if not already serving FSC 11 BEQ L9849 LDX linptr+$00 ;then XY = address of command line LDY linptr+$01 JMP osfscm ;issue Filing System Call ENDIF .L9849 JSR illmsg ;else raise "Bad command" error. EQUB $FE EQUS "command" EQUB $00 .defsuc ;Execute command binary JSR loadt ;load file into memory CLC LDA linadr+$00 ;get offset of command line tail TAY ;and pass to command in Y (if on host) ADC linptr+$00 ;add it to GSINIT pointer in $F2,3 STA linadr+$00 ;giving command line tail pointer LDA linptr+$01 ;[D]save it in $FDE2,3 for OSARGS 1 ADC #$00 STA linadr+$01 LDA exlow+$02 ;and high bytes of address AND exlow+$03 ;a=$FF if address is in the host ORA notube ;a=$FF if Tube absent ($FDCD=NOT MOS flag!) CMP #$FF ;if host address or Tube absent BEQ runho ;then jump indirect LDA exelo ;else copy low word of exec address STA exlow+$00 ;over high word of load addr in OSFILE block LDA exehi STA exlow+$01 JSR clatub ;claim Tube LDX #<exlow ;point XY to 32-bit execution address LDY #>exlow LDA #$04 ;tube service call $04 = *Go JMP taddrl ;jump into Tube service .runho ;Execute command on host TAX ;on entry A=$FF, C=1 LDA #$01 ;emulate Acorn DFS: A=1, C=1, JMP (exelo) ;X=$FF filename cmp complete .supld ;Copy argument ptr and load to cat address LDA #$FF ;lsb exec address in our OSFILE block = $FF: STA exelo ;load executable to load address in catalogue LDA linptr+$00 ;copy GSINIT string pointer to zero page STA work+$00 ;= command line pointer LDA linptr+$01 STA work+$01 RTS .wname ;FSC 3 = unrecognised *command JSR stxylp ;set GSINIT pointer to XY, set Y=0 LDX #<comtab ;point XY to command table at $90B4 LDY #>comtab LDA #$00 ;command starts at XY with zero offset JSR wname0 ;search for command or keyword in table TSX STX abrtsp ;save stack pointer to restore on abort JMP L80D7 ;execute command .wdcat ;FSC 5 = *CAT JSR stxylp ;set GSINIT pointer to XY, set Y=0 JSR setupr ;[D]call GSINIT with C=0 JSR LAA72 ;select specified or default volume TXA ;[D]test b7 of detected specification type BPL L98F3 ;if b7=0 then spec specific, *CAT single vol LDA #$80 ;else data transfer call $80 = read to JIM STA txcall JSR LABB5 ;detect disc format/set sector address BIT denflg ;test density flag BVC L98F3 ;if double density then *CAT eight volumes: JSR L8F0B ;print disc type and volume list LDX #$00 ;for each volume letter A..H: .L98CC JSR pgaux ;page in auxiliary workspace LDA voltks,X ;test if number of tracks in volume > 0 BEQ L98E4 ;if = 0 then no such volume, skip TXA ;save volume counter PHA JSR dirld ;ensure current volume catalogue loaded JSR L8ED7 ;print volume title JSR L8F88 ;print volume spec and boot option JSR L8DCA ;list files in catalogue PLA ;restore volume counter TAX .L98E4 CLC LDA fdrive ;get current volume ADC #$10 ;select next volume letter STA fdrive ;set as current volume INX ;increment counter CPX #volums ;have 8 volumes A..H been listed? BNE L98CC ;if not then loop JMP pgmain ;else page in main workspace and exit .L98F3 ;*CAT single volume JSR dirld ;ensure current volume catalogue loaded JSR L8ED7 ;print volume title JSR L8F0B ;print disc type and volume list JSR L8F88 ;print volume spec and boot option JSR L8FB8 ;print CSD and library directories JMP L8DCA ;list files in catalogue .wfdie ;FSC 6 = new filing system starting up JSR L990F ;close *SPOOL/*EXEC files ASL senti1 ;[D]clear b7=0 Challenger not current FS LSR senti1 RTS .L990F JSR savita ;save AXY LDA #$77 ;call OSBYTE $77 = close *SPOOL/*EXEC files JMP osbyte .whlim ;FSC 7 = range of valid file handles LDX #$11 LDY #$15 RTS .wstus ;FSC 8 = *command has been entered BIT enaflg ;if *ENABLEd flag b7=0 (i.e. byte = 0 or 1) BMI L9924 DEC enaflg ;then enable this command, not the ones after .L9924 JMP L9753 ;[D]forget catalogue in JIM pages 2..3 .vlook ;Ensure open file still in drive JSR setq ;set current vol/dir from open filename .vlook3 ;Ensure open file still on current volume JSR pgmain ;page in main workspace LDX #$07 ;start at seventh character of leaf name: .vshutl LDA seqcat+$0C,Y ;copy leaf name of file to current leaf name STA wrknam-$01,X DEY ;skip odd bytes containing length and addrs DEY ;select previous character of leaf name (Y>0) DEX ;decrement offset in current leaf name BNE vshutl ;loop until 7 characters copied (X=7..1) JSR lookup ;search for file in catalogue BCC L995E ;if file not found then raise "Disk changed" STY seqwb ;else save offset in catalogue JSR pgcat1 ;page in catalogue sector 1 LDA cathig+$06,Y ;get top bits exec/length/load/start sector LDX cathig+$07,Y ;put LSB start sector in X JSR pgmain ;page in main workspace LDY dcby ;put channel workspace pointer in Y EOR seqlh,Y ;compare start sector with one in workspace AND #$03 ;mask off other fields BNE L995E ;if not equal then raise "Disk changed" error TXA ;else compare low bytes of start sector (LBA) CMP seqloc,Y BEQ vfound ;if equal then exit, else: .L995E JMP dskchn ;raise "Disk changed" error .close2 ;Close all files JSR L990F ;close *SPOOL/*EXEC files .shtall LDY #$04 ;[D]5 file handles to close: .shtal0 TYA ;save counter PHA LDA L9C91,Y ;y=0..4. get workspace pointer from table TAY JSR vshut ;close file L7 PLA ;restore counter TAY DEY ;loop until all files closed. BPL shtal0 .vfound RTS .wfind ;OSFIND CMP #$40 BCS wfind0 ;if A>=$40 then open a file JSR savita ;else close a file/all files. save AXY .wshut TYA ;if handle = 0 BEQ close2 ;then close all files JSR dcrypt ;else convert to pointer: TAY .vshut ;Close file L7 JSR pgmain ;page in main workspace PHA JSR cheeky ;validate workspace offset BCS L99D7 ;if channel invalid or closed then exit LDA seqbit,Y ;else get bit mask corresponding to channel EOR #$FF ;invert it, bit corresponding to channel =0 AND dcbmap ;clear bit of channel open flag byte STA dcbmap ;update flag byte LDA seqflg,Y ;get channel flags AND #$60 ;if either buffer or EXT changed BEQ L99D7 JSR vlook ;then ensure open file still in drive LDA seqflg,Y ;if EXT changed AND #$20 BEQ L99D4 LDX seqwb ;then set X = catalogue pointer LDA seqlla,Y ;copy low word of EXT to length in catalogue JSR pgcat1 ;page in catalogue sector 1 STA cathig+$04,X JSR pgmain ;page in main workspace LDA seqlma,Y JSR pgcat1 ;page in catalogue sector 1 STA cathig+$05,X JSR pgmain ;page in main workspace LDA seqlha,Y ;get high byte of EXT JSR L92EC ;pack b17,16 of length into catalogue entry JSR dirout ;write volume catalogue LDY dcby ;put channel workspace pointer in Y .L99D4 JSR bflush ;ensure buffer up-to-date on disc L6 .L99D7 PLA ;restore A on entry [D]X not restored here .nofile RTS .wfind0 ;Open a file JSR savit ;save XY STX work+$00 STY work+$01 STA bitwrk ;store file open mode in temporary var. LDA #$08 ;test bit 3 = error if not found (RISC OS) BIT bitwrk ;set N, V and Z from temporary variable PHP JSR frmnam ;set current file from argument pointer JSR L9AF7 ;find unused file handle BCC L9A05 ;if all file handles in use .tmopen JSR estrng ;then raise "Too many files open" error. EQUB $C0 EQUS "Too many files open" EQUB $00 .L9A05 LDX #<wrknam ;point XY+A to current filename LDA #>wrknam TAY JSR L9B10 ;compare filename at XY+A with open filenames BCC L9A29 ;if file not open then continue .find2 JSR pgmain ;page in main workspace LDA seqrdo,Y ;else test if the channel is open read-write BPL filopn ;if so, reopening is a conflict; raise error PLP ;else if reopening a r-o channel read-only PHP ;(i.e. channel b7=1, OSFIND call no. b7=0) BPL L9A24 ;then this is also safe; continue ;NB loop is redundant; can BPL L9A29 instead .filopn JSR filmsg ;else reopening a r-o channel r-w is conflict EQUB $C2 ;raise "File open" error. EQUS "open" EQUB $00 .L9A24 JSR L9B2A ;find any other channels open on this file BCS find2 ;if another channel found then loop .L9A29 JSR clrwld ;disallow wildcard characters in filename JSR lookup ;search for file in catalogue BCS L9A4E ;if not found LDA #$00 ;then preset A=0, no file handle to return PLP ;if opening for read or update BVC L9A37 ;(i.e. OSFIND call no. b6=1) BEQ nofile ;else existing file was expected ;return A=0 if OSFILE call no. b3=0 JMP L8B46 ;else raise "File not found" error .L9A37 PHP JSR pgmain ;page in main workspace LDX #$07 ;else opening new file for output. .L9A3D STA wrkcat,X ;clear load, exec, start and length = 0 STA hiwork,X DEX BPL L9A3D LDA #$40 ;initial length = $4000 = 16 KiB STA endhi STA nice ;b6=1 will accept shorter allocation JSR dirdo ;create file from OSFILE block .L9A4E TYA ;transfer catalogue pointer to X TAX PLP PHP BVS L9A57 ;if opening for output (OSFIND b6=0) JSR chklok ;then ensure file not locked .L9A57 JSR pgmain ;page in main workspace LDY dcby ;put channel workspace pointer in Y .L9A5D JSR pgcat0 ;page in catalogue sector 0 LDA catlow,X ;copy name and attributes of file JSR pgmain ;page in main workspace STA seqcat,Y ;to bottom half of channel workspace INY JSR pgcat1 ;page in catalogue sector 1 LDA cathig,X JSR pgmain ;page in main workspace STA seqcat,Y INY INX TXA ;loop until 8 byte pairs copied AND #$07 BNE L9A5D LDX #$10 ;a=0 .L9A7F STA seqmap,Y ;clear top half of channel workspace INY DEX BNE L9A7F LDY dcby ;put channel workspace pointer in Y ;[D]buffer page no. irrelevant, not set here TYA STA seqdah,Y ;set buffer address out of range ;[D]buffer page no. irrelevant, not set here LDA dcbbit ;get bit mask corresponding to channel STA seqbit,Y ;store in channel workspace ORA dcbmap ;set that bit in channel open flags byte STA dcbmap ;marking this channel open LDA seqll,Y ;test LSB of file length CMP #$01 ;[D]set C=1 iff partial sector LDA seqlm,Y ;copy 2MSB length to allocation ADC #$00 ;rounding up to whole sector STA seqem,Y LDA seqlh,Y ;get top bits exec/length/load/start sector ORA #$0F ;mask off load/start sector ADC #$00 ;carry out to length in bits 5 and 4 JSR isolen ;extract b5,b4 of A STA seqeh,Y ;store MSB allocation PLP ;restore OSFIND call number to N and V BVC L9AF0 ;if opening for output then branch BMI vfind1 ;if opening for update then branch LDA #$80 ;else opening for input. ORA seqrdo,Y ;set b7=1 of seventh char of leaf name STA seqrdo,Y ;marking channel read-only. .vfind1 LDA seqll,Y ;input or update; set EXT = length of file STA seqlla,Y LDA seqlm,Y STA seqlma,Y LDA seqlh,Y JSR isolen ;extract b5,b4 of A STA seqlha,Y .vfinxx LDA fdrive ;get current volume STA seqchk,Y ;set as volume of open file JSR L853F ;[D]pack drive parameters STA seqbuf,Y ;store in place of buffer page number LDA voltrk ;get first track of current volume STA sequal,Y ;store in spare byte of channel workspace TYA ;transfer channel workspace pointer to A JSR sfive ;shift A right 5 places ADC #$10 ;c=0; add $10 to return file handle $11..15 RTS .L9AF0 LDA #$20 ;set channel flag b5=1, "EXT changed" STA seqflg,Y ;to truncate file's initial allocation BNE vfinxx ;branch to return file handle (always) .L9AF7 ;Find unused file handle LDA dcbmap ;get channel open flags LDX #$FB ;test up to 5 channel bits:[D]count up here .L9AFC ASL A ;shift next channel open flag into carry BCC L9B03 ;if C=0 channel unused, calculate ptr+mask INX ;else loop until 5 channels tested BMI L9AFC RTS ;if C=1 all channels in use, none free .L9B03 ;Calculate workspace pointer and bit mask LDA L9C91-$FB,X ;[D]get workspace pointer from $9C91..5 STA dcby ;return in workspace pointer variable LDA L9C96-$FB,X ;[D]get channel open bit mask from $9C96..A STA dcbbit ;return in bit mask variable. .L9B32 RTS .L9B10 ;Compare filename at XY+A with open filenames STX namptr+$00 ;save XY as filename pointer STY namptr+$01 STA namofs ;save A as offset JSR pgmain ;page in main workspace LDA dcbmap ;get channel open flags AND #$F8 ;extract flags for channels $11..15 STA dcbsr ;save as shift register LDX #$20 ;start at channel workspace offset $20: .L9B22 STX dcbofs ASL dcbsr ;shift next channel open flag into carry BCS L9B34 ;if C=1 channel open then compare names BEQ L9B32 ;if no more channels open exit C=0, else: .L9B2A LDA dcbofs ;add $20 to channel workspace pointer CLC ADC #$20 TAX BCC L9B22 ;and loop to test next channel (always) .L9B34 LDA seqchk,X ;get volume of open file JSR LAADB ;[D]map volume in A to physical volume STA seqvol ;store in temporary variable JSR LAAD9 ;map current volume to physical volume EOR seqvol ;compare with volume of open file BNE L9B2A ;if unequal then no match LDY namofs ;else put offset in Y: .L9B49 JSR pgcat0 ;page in catalogue sector 0 LDA (namptr),Y ;get character of filename to compare JSR pgmain ;page in main workspace EOR seqcat,X ;compare with char of open filename AND #$7F ;ignore bit 7 BNE L9B2A ;if unequal then no match INY ;skip to next character of comparand INX ;skip even addresses cont'g file attributes INX ;skip to next character of open filename TXA AND #$0E BNE L9B49 ;loop until 7 leaf name chars + dir tested LDY dcbofs ;then restore channel workspace offset to Y RTS ;return C=1 matching filename found. .wargs ;OSARGS JSR pgmain ;page in main workspace CPY #$00 ;file handle in Y; if Y = 0 BEQ wargs1 ;then perform Y = 0 functions JSR savita ;else save AXY CMP #$FF ;if A=$FF BEQ L9BAC ;then ensure file up-to-date on disc CMP #$04 ;else if A>=4 BCS wargsr ;then return LSR A ;else place bit 0 of A in carry flag .vstar4 BCS vstar1 ;if not A=1 set PTR or A=3 set EXT, then: .vradr ;OSARGS A=0/2, Y>0 return PTR/EXT JSR savita ;save AXY JSR dcrych ;ensure file handle valid and open ASL A ;A=0 or 1, multiply by 4 ASL A ;A=0 offset of PTR, A=4 offset of EXT ADC dcby ;add offset to channel workspace pointer TAY ;transfer to Y as index LDA seqpl,Y ;copy PTR or EXT STA $00,X ;to 3 LSBs of user's OSARGS block LDA seqpm,Y STA $01,X LDA seqph,Y STA $02,X LDA #$00 ;clear MSB of user's OSARGS block STA $03,X ;PTR <= EXT < 16 MiB RTS .ensur ;OSARGS A=$FF, Y=0 LDA dcbmap ;Ensure all files up-to-date on disc (flush) PHA ;save channel open flags JSR shtall ;close all files [D](returns N=1) BMI ensur0 ;branch (always) .L9BAC ;OSARGS A=$FF, Y>0 ensure file up-to-date LDA dcbmap ;Ensure file up-to-date on disc (flush) PHA ;save channel open flags JSR wshut ;close a file/all files .ensur0 PLA ;restore channel open flags. STA dcbmap RTS .wargs1 ;OSARGS Y=0 JSR savit ;save XY TAY ;A=call number, transfer to Y INY ;convert $FF,0,1 to 0..2 CPY #LAE30-LAE2D ;if call number was $02..$FE BCS wargsr ;then return LDA LAE30,Y ;else get action address high byte PHA ;save on stack LDA LAE2D,Y ;get action address low byte PHA ;save on stack .wargsr RTS ;jump to action address. .vstar0 PLA ;extend file. restore file handle TAY JSR vstar ;set PTR = request, to extend file LDA #$80 ;a=$80 so that c=1 to restore original PTR: .vstar1 ;OSARGS A=1/3, Y>0 set PTR/EXT BEQ vstar ;if call number = 1, set PTR, else: ASL A ;OSARGS 3,Y. c=0 read PTR to restore later TXA ;save OSARGS pointer PHA LDX #zgbptr ;point X to PTR store used by OSGBPB LDA #$00 ;set A=0, Z=1 to read/set PTR not EXT! JSR vstar4 ;read temporary PTR if C=0 or set PTR if C=1 PLA ;restore user's OSARGS pointer TAX TYA ;save file handle PHA JSR LAA33 ;clear EOF warning flag JSR scmp ;compare EXT - request BCC vstar0 ;if EXT < request then extend file LDA seqrdo,Y ;else get channel read-only bit in b7 ORA seqlok,Y ;or with channel file locked bit in b7 BMI vstar2 ;if either is set do not write new EXT to cat LDA #$20 ;else b5=1 EXT changed JSR setbit ;set channel flag bits (A = OR mask) .vstar2 JSR LA9AD ;truncate file. add 4 to Y JSR vstarb ;copy request to EXT PLA ;restore file handle TAY TXA ;save OSARGS pointer PHA TYA ;copy file handle to X TAX JSR wfeof ;compare PTR - EXT PLA ;restore OSARGS pointer TAX ;if PTR < EXT BCC wargsr ;then return, else set PTR = request: .vstar ;OSARGS A=1, Y>0 set PTR JSR savita ;save AXY JSR dcrych ;ensure file handle valid and open JSR scmp ;compare EXT - requested PTR BCS vstarb ;if EXT >= request then just set PTR LDA $01,X ;else compare 3MSB request - 2MSB EXT CMP seqlma,Y BNE L9BF1 ;if unequal then extend file LDA $02,X ;else compare 2MSB request - MSB EXT CMP seqlha,Y BEQ L9C21 ;if equal then within allocation, set PTR,EXT .L9BF1 LDA $00,X ;get LSB requested PTR CMP #$01 ;c=1 iff LSB >0 LDA $01,X ;add C to 3MSB request, rounding up ADC #$00 STA allocl ;store LSB requested length in sectors LDA $02,X ;carry out to 2MSB request ADC #$00 STA alloch ;store MSB requested length in sectors TXA ;save OSARGS pointer PHA JSR vlook3 ;ensure open file still on current volume JSR L9E6E ;calculate maximum available allocation SEC LDA allocl ;get LSB requested length in sectors SBC ceillo ;subtract LSB maximum available allocation STA excesl ;save LSB excess LDA alloch ;get MSB requested length in sectors SBC ceilhi ;subtract MSB maximum available allocation STA excesh ;save MSB excess, C=0 if negative (headroom) BCC L9C1F ;if allocation > request then set PTR ORA excesl ;else test excess BEQ L9C1F ;if allocation = request then set PTR JSR L9EC7 ;else move files .L9C1F PLA ;restore OSARGS pointer TAX .L9C21 LDA seqlla,Y ;set PTR = EXT STA seqpl,Y LDA seqlma,Y STA seqpm,Y LDA seqlha,Y STA seqph,Y JSR vstarc ;set b7 according to $FCFD..E,Y .L9C36 LDA #$00 ;a = $00 filler byte JSR vbput ;write byte to end of file JSR scmp ;compare EXT - request BCC L9C36 ;loop until last byte is just before new PTR .vstarb LDA $00,X ;copy requested PTR in user's OSARGS block STA seqpl,Y ;to channel pointer LDA $01,X STA seqpm,Y LDA $02,X ;copy MSB PTR STA seqph,Y .vstarc ;Update buffer-contains-PTR channel flag LDA #$6F ;b7=0 PTR not in buffer, b4=0 EOF warning clr JSR vstar6 ;clear channel flag bits LDA seqpm,Y ADC seqloc,Y ;c=0; add LSB start sector to 2MSB PTR STA vslbal ;save LSB new buffer address LDA seqph,Y ADC seqlh,Y ;add top bits exec/length/load/start sector AND #$03 ;mask b1,b0 MSB new buffer address CMP seqdah,Y ;compare MSB current buffer address BNE L9C90 ;if equal LDA vslbal ;get back LSB new buffer address CMP seqdal,Y ;compare LSB current buffer address BNE L9C90 ;if equal JMP setcbf ;then set b7=1 buffer contains PTR. .rdfsno ;OSARGS A=0, Y=0 return filing system number LDA #$04 ;a=4 for Disc Filing System RTS .L9B8F ;OSARGS A=1, Y=0 read command line tail LDA #$FF ;command line is always in I/O processor STA $02,X ;so return a host address, $FFFFxxxx STA $03,X LDA linadr+$00 ;copy address of command line arguments STA $00,X ;from workspace where stored by FSC 2..4 LDA linadr+$01 ;to user's OSARGS block STA $01,X LDA #$00 ;return A=0 RTS .cheeky ;Validate workspace offset PHA ;save A [D]not X TYA ;transfer workspace offset to A AND #$E0 ;mask bits 7..5, offset = 0..7 * $20 STA dcby ;save channel workspace pointer BEQ L9C8E ;if offset = 0 (i.e. channel $10) return C=1 ASL A ;else rotate b7..b5 into b2..b0 ROL A ;to produce an offset 1..7 ROL A ;corresponding to channels $11..17 ROL A ;[BUG]accepts $16..17, undefined results! TAY ;transfer to Y for use as index LDA L9C96-$01,Y ;get channel open bit mask from table LDY dcby ;put channel workspace pointer in Y BIT dcbmap ;if channel's open bit in flag byte = 0 BNE L9C8F .L9C8E SEC ;then return C=1 .L9C8F PLA ;else return C=0 .L9C90 RTS ;Table of channel workspace pointers for file handles $11..15 .L9C91 EQUB $20,$40,$60,$80 EQUB $A0 ;Table of channel open bit masks for file handles $11..15 .L9C96 EQUB $80,$40,$20,$10 EQUB $08 .dcrych ;Ensure file handle valid and open PHA ;save A on entry, Y = file handle JSR dcrypt ;[D]convert file handle to workspace pointer STA dcby ;save in temporary location LDA L9C96,Y ;get channel open bit mask from table LDY dcby ;put channel workspace pointer in Y BIT dcbmap ;if channel's open bit in flag byte = 0 BEQ wfeof1 ;then raise "Channel" error PLA ;else restore A RTS .dcrypt ;Convert file handle to workspace pointer TYA SEC SBC #$11 CMP #$05 ;if file handle is $16 or more BCS wfeof1 ;then raise "Channel" error, else: TAY LDA L9C91,Y ;y=0..4. get workspace pointer from table RTS .wfeof1 ;Raise "Channel" error JSR estrng EQUB $DE EQUS "Channel" EQUB $00 .illeof ;Raise "EOF" error JSR estrng EQUB $DF EQUS "EOF" EQUB $00 .wbget ;OSBGET JSR pgmain ;page in main workspace STX stashx STY stashy JSR dcrych ;ensure file handle valid and open TYA JSR pcmp ;compare PTR - EXT BNE noteof ;if at EOF LDA seqflg,Y ;then test EOF warning flag b4 AND #$10 BNE illeof ;if set then raise "EOF" error LDA #$10 ;else set EOF warning flag b4=1 JSR setbit ;set channel flag bits (A = OR mask) LDA #$FE ;return A=$FE, "file end" SEC ;return C=1 indicating end-of-file BCS L9D0C ;restore XY and exit .noteof LDA seqflg,Y ;not at EOF. get channel flags BMI vbgetb ;if PTR not within current buffer JSR setq ;then set current vol/dir from open filename JSR bflush ;ensure buffer up-to-date on disc L6 SEC ;c=1 read buffer from disc JSR xblock ;read/write sector buffer L6 (returns C=0) .vbgetb JSR L9E59 ;[D]increment PTR and page in channel buffer LDA slots,X ;get byte from channel buffer at old PTR JSR pgmain ;page in main workspace .L9D0C LDX stashx ;restore X and Y on entry LDY stashy PHA ;set N and Z according to A PLA RTS ;exit .setda ;Set buffer sector address from PTR CLC LDA seqloc,Y ;get LSB start sector of open file ADC seqpm,Y ;add 2MSB of PTR STA lbalo ;store LSB sector address STA seqdal,Y ;store LSB sector address of buffer LDA seqlh,Y ;get top bits exec/length/load/start sector AND #$03 ;extract MSB start sector ADC seqph,Y ;add MSB of PTR STA lbahi ;store MSB sector address STA seqdah,Y ;store MSB sector address of buffer .setcbf ;Set buffer-contains-PTR channel flag LDA #$80 ;b7=1 buffer contains byte at PTR: .setbit ;Set channel flag bits (A = OR mask) ORA seqflg,Y BNE clrbt3 ;store if >0 else fall through harmlessly: .clrcbf ;Clear buffer-contains-PTR channel flag: LDA #$7F .clrbit ;Clear channel flag bits (A = AND mask) AND seqflg,Y .clrbt3 STA seqflg,Y CLC RTS .LAA33 JSR dcrych ;ensure file handle valid and open LDA #$EF ;b4=0 EOF warning flag clear BNE clrbit ;clear channel flag bits .bflush ;Ensure buffer up-to-date on disc L6 LDA seqflg,Y ;test b6 of channel flag AND #$40 BEQ bflx ;if buffer not changed then return CLC ;c=0 write buffer to disc: .xblock ;Read/write sector buffer L6 PHP JSR pgmain ;get channel workspace pointer LDY dcby ;put channel workspace pointer in Y TYA ;and A LSR A ;shift A right 5 places LSR A LSR A LSR A LSR A ADC #<jslots-$01 ;c=0; A=4..8 for handles $11..15 STA lodlo ;[D]set LSB address of buffer in JIM space LDA #>jslots STA lodhi ;clear MSB buffer address STA lenlo LDA #$01 ;256 bytes to transfer STA lenhi PLP BCS xblrd ;if C was 1 on entry then read buffer LDA seqdal,Y ;else copy channel's sector buffer address STA lbalo ;to $C5,4 (big-endian) LDA seqdah,Y STA lbahi JSR L9721 ;write ordinary file from JIM L5 LDA #$BF ;b6=0 buffer not changed .vstar6 LDY dcby ;put channel workspace pointer in Y JMP clrbit ;clear channel flag bits and exit .xblrd ;Read channel buffer from disc L6 JSR setda ;set buffer sector address from PTR JSR L9724 ;read ordinary file to JIM L5 LDY dcby ;put channel workspace pointer in Y .bflx RTS .vbput1 JMP delprt ;raise "File locked" error. .vbput2 ;Raise "File read only" error. JSR filmsg EQUB $C1 EQUS "read only" EQUB $00 .vbput JSR savita ;save AXY JMP wbput1 .wbput ;OSBPUT JSR pgmain ;page in main workspace STA stasha ;save AXY on entry STX stashx STY stashy JSR dcrych ;ensure file handle valid and open .wbput1 PHA ;save byte to write LDA seqrdo,Y ;test channel read-only bit BMI vbput2 ;if b7=1 then raise "File read only" error LDA seqlok,Y ;else test file locked bit BMI vbput1 ;if b7=1 then raise "File locked" error JSR setq ;else set current vol/dir from open filename TYA ;a=y = channel workspace pointer CLC ;add 4 to point A to allocated length not EXT ADC #seqem-seqlma JSR pcmp ;compare PTR - allocated length BNE notful ;if within allocation then write JSR vlook3 ;else ensure open file still on current volume .L9DC7 JSR L9E6E ;calculate maximum available allocation LDA ceilhi ;get MSB maximum available allocation CMP seqeh,Y ;compare MSB length of file per workspace BNE vok ;if not equal then extend file LDA ceillo ;else restore LSB maximum available allocation CMP seqem,Y ;compare 2MSB length of file per workspace BNE vokspl ;if not equal then extend file LDA #<bpmove ;else excess = 1 sector STA excesl LDA #>bpmove STA excesh JSR L9EC7 ;move files (to yield one sector) BCC L9DC7 ;and try again .vok CLC LDA seqeh,Y ;[D]increment MSB of file length in workspace ADC #$01 ;strictly increasing length to n*64 KiB STA seqeh,Y JSR L92EC ;pack b17,16 of length into catalogue entry LDA #$00 ;set 2MSB file length to 0: .vokspl STA seqem,Y ;store 2MSB file length in workspace JSR pgcat1 ;page in catalogue sector 1 STA cathig+$05,X ;store 2MSB file length in catalogue LDA #$00 STA cathig+$04,X ;clear LSB file length in catalogue JSR dirout ;write volume catalogue LDY dcby ;put channel workspace pointer in Y .notful LDA seqflg,Y ;test channel flags BMI vbputb ;if b7=1 buffer-contains-PTR then write byte JSR bflush ;else ensure buffer up-to-date on disc L6 LDA seqlla,Y ;does EXT equal a whole number of sectors? BNE notend ;if not then read buffer from disc TYA ;else a=y = channel workspace pointer JSR pcmp ;compare PTR - EXT BNE notend ;if not at EOF then read buffer from disc JSR setda ;else set buffer sector address from PTR BNE vbputb ;branch (always) .notend SEC ;c=1 read buffer from disc JSR xblock ;read/write sector buffer L6 .vbputb JSR L9E59 ;increment PTR and page in channel buffer PLA ;restore byte to write STA slots,X ;put byte in channel buffer at old PTR JSR pgmain ;page in main workspace LDA #$40 ;b6=1, buffer has changed JSR setbit ;set channel flag bits (A = OR mask) TYA ;a=y = channel workspace pointer JSR pcmp ;compare PTR - EXT BCC vbputx ;if at EOF (i.e. pointer >= EXT) LDA #$20 ;then b5=1, EXT has changed JSR setbit ;set channel flag bits (A = OR mask) LDA seqpl,Y ;copy EXT = PTR STA seqlla,Y LDA seqpm,Y STA seqlma,Y LDA seqph,Y STA seqlha,Y .vbputx LDA stasha ;restore AXY on entry LDX stashx LDY stashy RTS ;exit .L9E59 ;Increment PTR and page in channel buffer LDA seqpl,Y ;get current LSB of PTR to return PHA JSR L9E8D ;increment PTR TYA ;transfer workspace pointer to A LSR A ;shift A right 5 places LSR A LSR A LSR A LSR A ADC #<jslots-$01 ;c=0; A=4..8 for handles $11..15 JSR pga ;page in JIM page in A PLA TAX ;return old LSB of PTR in X as buffer offset RTS .L9E6E ;Calculate maximum available allocation JSR pgmain ;page in main workspace LDX seqwb ;get offset of file in catalogue JSR pgcat1 ;page in catalogue sector 1 SEC LDA cathig-$01,X ;get LSB start LBA of previous file in cat SBC cathig+$07,X ;subtract LSB start LBA of open file STA ceillo ;save LSB maximum available allocation LDA cathig-$02,X ;get MSB start LBA of previous file in cat SBC cathig+$06,X ;subtract MSB start LBA of open file AND #$03 ;extract b1,b0 STA ceilhi ;store MSB maximum available allocation JMP pgmain ;page in main workspace .L9E8D ;Increment PTR TYA ;transfer channel workspace pointer to X TAX ;[D]no buffer pointer to write back INC seqpl,X ;increment LSB of PTR BNE pcmpx ;if within same sector then return INC seqpm,X ;else sector boundary crossed. BNE L9E9C ;carry out to high bytes of PTR INC seqph,X .L9E9C JMP clrcbf ;and clear buffer-contains-PTR channel flag. .pcmp ;Compare PTR - EXT (A=Y), - allocation (A=Y+4) TAX ;return C=1 iff at/past EOF or allocation LDA seqph,Y ;return Z=1 iff at EOF or equal to allocation CMP seqlha,X BNE pcmpx LDA seqpm,Y CMP seqlma,X BNE pcmpx LDA seqpl,Y CMP seqlla,X .pcmpx RTS .scmp ;Compare EXT - OSARGS parameter LDA seqlla,Y ;return C=1 iff EXT >= parameter CMP $00,X LDA seqlma,Y SBC $01,X LDA seqlha,Y SBC $02,X RTS .L9EC7 ;Move files JSR savita ;save AXY STX movcat ;store catalogue offset of file to extend JSR pgcat1 ;page in catalogue sector 1 LDA dirlen ;get number of files in catalogue * 8 STA movlen ;save in zero page JSR LA053 ;push file map on stack TSX ;x = stack pointer STX mapsp ;save pointer to file map JSR LA0CE ;confirm space available BCS L9EE8 ;if space cannot be made JSR dskmsg ;then raise "Disk full" error EQUB $BF ;number = $BF, "Can't extend", cf. $93EF EQUS "full" EQUB $00 ;Move files with space confirmed available .L9EE8 JSR LA0C9 ;confirm space available after file BCC L9F02 ;if not then shift previous files up LDA dstlo ;else get LSB LBA of end of slack to use SBC mheadl ;subtract LSB headroom STA dstlo ;store LSB LBA of new end of block LDA dsthi ;get MSB LBA of end of slack to use SBC mheadh ;subtract MSB headroom STA dsthi ;update MSB LBA of new end of block LDA #$00 STA mvuplo ;do not move previous files STA mvuphi BEQ L9F0F ;and branch (always) ;Shifting later files down is insufficient; must shift earlier files up .L9F02 SEC LDA #$00 SBC mheadl ;negate LSB negative headroom STA mvuplo ;store LSB excess to move previous files by LDA #$00 SBC mheadh ;negate MSB negative headroom STA mvuphi ;store MSB excess ;Shift files after current file down .L9F0F LDA mfreel ;test total slack space after file ORA mfreeh BEQ L9F45 ;if none then all space must come from prev .L9F15 CLC LDA stack+$08,Y ;get LSB length of file in sectors STA todolo ;store at C6 ADC stack+$06,Y ;add LSB LBA of file STA srclo ;store LSB source LBA LDA stack+$07,Y ;get MSB length of file in sectors STA todohi ;store at C7 ADC stack+$05,Y ;add MSB LBA of file STA srchi ;store MSB source LBA JSR L9F9E ;move file data LDA dcby ;get channel workspace pointer for open file STA movofs ;store at C3 JSR L9FF1 ;update LBAs in channel workspaces LDA dsthi ;get MSB destination LBA after transfer STA stack+$05,Y ;replace MSB LBA of file LDA dstlo ;get LSB destination LBA after transfer STA stack+$06,Y ;replace LSB LBA of file JSR LA9AD ;add 4 to Y DEX ;loop until all files after current moved BNE L9F15 ;Shift files before current file up .L9F45 LDA mvuplo ;get LSB excess to move previous files by STA excesl ;replace LSB total excess LDA mvuphi ;get MSB excess to move previous files by STA excesh ;replace LSB total excess ORA excesl ;test excess to move previous files by BEQ L9F93 ;if none then update catalogue and exit JSR LA0FC ;else confirm space available before file CLC ;(certain to succeed) LDA stack+$06,Y ;get LSB LBA of previous file ADC stack+$08,Y ;add LSB length of file in sectors STA dstlo ;store LSB LBA of end of previous file LDA stack+$05,Y ;get MSB LBA of previous file ADC stack+$07,Y ;add MSB length of file in sectors STA dsthi ;store MSB LBA of end of previous file: .L9F65 LDA stack+$04,Y ;get LSB length of current file in sectors STA todolo ;store at C6 LDA stack+$03,Y ;get MSB length of current file in sectors STA todohi ;store at C7 LDA stack+$02,Y ;get LSB LBA of current file STA srclo ;store at C8 LDA stack+$01,Y ;get MSB LBA of current file STA srchi ;store at C9 LDA dstlo ;get LSB LBA of end of previous file STA stack+$02,Y ;replace LSB LBA of file LDA dsthi ;get MSB LBA of end of previous file STA stack+$01,Y ;replace MSB LBA of file LDA #$00 ;set workspace pointer out of range: STA movofs ;update LBAs even of file being extended JSR L9FF1 ;update LBAs in channel workspaces JSR L891D ;shift data JSR LA9B6 ;subtract 4 from Y DEX ;loop until beginning of catalogue reached BNE L9F65 ;Update catalogue and exit .L9F93 JSR dirldy ;load volume catalogue L4 JSR LA09A ;update LBAs in catalogue JSR dirout ;write volume catalogue L4 CLC ;return C=0 sufficient space was made RTS .L9F9E ;Move file data JSR savita ;save AXY LDA #$00 STA lodhi ;clear MSB load address in JIM space STA lenlo ;clear LSB number of bytes to transfer .L9FA7 LDY todolo ;compare size of file in sectors - $0002 CPY #<shftsz LDA todohi SBC #>shftsz BCC L9FB3 ;if size of file >= 2 sectors LDY #<shftsz ;then transfer size = 2: .L9FB3 STY lenhi ;set number of sectors to transfer SEC LDA srclo ;get LSB source LBA SBC lenhi ;subtract transfer size STA lbalo ;store LSB of transfer LBA STA srclo ;update LSB source LBA LDA srchi ;get MSB source LBA SBC #$00 ;borrow in from transfer size STA wrkcat+$06 ;store MSB transfer LBA STA srchi ;update MSB source LBA LDA #<jshift STA lodlo ;set load address in JIM space = $0002 JSR L959C ;set high word of OSFILE load address = $FFFF JSR L9724 ;read ordinary file to JIM L5 SEC LDA dstlo ;get LSB destination LBA SBC lenhi ;subtract transfer size STA lbalo ;store LSB of transfer LBA STA dstlo ;update LSB destination LBA LDA dsthi ;get MSB destination LBA SBC #$00 ;borrow in from transfer size STA wrkcat+$06 ;store MSB transfer LBA STA dsthi ;update MSB destination LBA ;NB always works upwards and shifts downwards ;sector reads and writes will not overlap LDA #<jshift STA lodlo ;set load address in JIM space = $0002 JSR L959C ;set high word of OSFILE load address = $FFFF JSR L9721 ;write ordinary file from JIM L5 JSR cpyfl7 ;subtract transfer size from remainder BNE L9FA7 ;loop while sectors remaining to transfer RTS .L9FF1 ;Update LBAs in channel workspaces JSR savita ;save AXY LDX #$00 ;start at channel $11 LDA dcbmap ;get channel open flags: .L9FF9 ASL A ;shift next channel open flag into C PHA ;save other flags BCC LA04C ;if C=0 channel closed then skip, else: LDA L9C91,X ;x=0..4. get workspace pointer from table TAY LDA seqchk,Y ;get volume of open file JSR LAADB ;map volume in A to physical volume STA mapvol JSR LAAD9 ;map current volume to physical volume CMP mapvol ;compare with current volume BNE LA04C ;if unequal then no match LDA seqlh,Y ;get top bits exec/length/load/start sector AND #$03 ;extract b1,b0 of A CMP srchi ;compare MSB LBA of start of open file BNE LA04C ;with LBA of current file; skip if unequal LDA seqloc,Y ;else get LSB start LBA of open file CMP srclo ;compare with LSB LBA of current file BNE LA04C ;if unequal then skip CPY movofs ;else compare wksp pointer with current file BEQ LA04C ;skip if equal (don't move it even if empty) LDA dstlo ;else get LSB new starting LBA STA seqloc,Y ;update LSB start LBA of open file SBC srclo ;subtract LSB old starting LBA STA mapdfl ;store LSB difference LDA dsthi ;get MSB new starting LBA SBC srchi ;subtract MSB old starting LBA PHA ;save MSB difference LDA seqlh,Y ;get top bits exec/length/load/start sector AND #$FC ;mask off MSB start LBA in b1,b0 ORA dsthi ;apply MSB new starting LBA STA seqlh,Y ;update top bits CLC LDA mapdfl ;get LSB difference in LBAs ADC seqdal,Y ;add LSB LBA of sector in buffer STA seqdal,Y ;update LSB LBA of sector in buffer PLA ;restore MSB difference ADC seqdah,Y ;add MSB LBA of sector in buffer STA seqdah,Y ;update LSB LBA of sector in buffer .LA04C PLA ;restore channel open flags INX ;select next channel CPX #$05 ;loop until channels $11..15 updated BNE L9FF9 RTS .LA053 ;Push file map on stack PLA ;pop caller's address into pointer STA movret+$00 PLA STA movret+$01 JSR pgcat1 ;page in catalogue sector 1 LDY dirlen ;point Y to last catalogue entry LDA #$00 PHA ;push word $0000 PHA JSR LA4F8 ;return no. reserved sectors in data area PHA ;push as big-endian word LDA #$00 PHA JSR pgcat1 ;page in catalogue sector 1 .LA06D LDA cathig-$04,Y ;get LSB file length CMP #$01 ;c=1 iff LSB >0 LDA cathig-$03,Y ;add C to 2MSB file length, rounding up ADC #$00 PHA ;push LSB length in sectors PHP ;save carry flag LDA cathig-$02,Y ;get top bits exec/length/load/start sector JSR isolen ;extract b5,b4 of A PLP ;restore carry flag ADC #$00 ;carry out to MSB file length PHA ;push MSB length in sectors LDA cathig-$01,Y ;get LSB start sector PHA ;push LSB start sector LDA cathig-$02,Y ;get top bits exec/length/load/start sector AND #$03 ;extract b1,b0 of A PHA ;push MSB start sector JSR unstep ;subtract 8 from Y CPY #$F8 ;loop until all entries +volume size pushed BNE LA06D JSR pgmain ;page in main workspace JMP LA932 ;return to caller ;Returns the following stack frame to the caller: ;0101,S MSB LBA of end of volume ;0102,S LSB LBA of end of volume ;0103,S Undefined ;0104,S Undefined ;0105,S MSB LBA of first file in catalogue (highest LBA) ;0106,S LSB LBA of first file in catalogue (highest LBA) ;0107,S MSB length of first file in sectors ;0108,S LSB length of first file in sectors ;0109,S MSB LBA of second file in catalogue ;010A,S LSB LBA of second file in catalogue ;010B,S MSB length of second file in sectors ;010C,S LSB length of second file in sectors ;... ;nn+1,S MSB volume-relative LBA of data area, =$00 ;nn+2,S LSB volume-relative LBA of data area, =$02 (SD) or =$00 (DD) ;nn+3,S MSB file map terminator, =$00 ;nn+4,S LSB file map terminator, =$00 .LA09A ;Update LBAs in catalogue PLA ;pop caller's address into pointer STA movret+$00 PLA STA movret+$01 JSR pgcat1 ;page in catalogue sector 1 LDY #$F8 ;y = catalogue offset $F8 going to $00: .LA0A5 JSR step ;add 8 to Y PLA ;pop MSB LBA end of volume/start of file EOR cathig-$02,Y ;XOR top bits exec/length/load/start sector AND #$03 ;mask b1,b0 old XOR new EOR cathig-$02,Y ;preserve b7..b2, replace b1,b0 STA cathig-$02,Y ;update top bits exec/length/load/start PLA ;pop LSB LBA end of volume/start of file STA cathig-$01,Y ;store LSB LBA end of volume/start of file PLA ;discard undefined/file length PLA CPY dirlen ;have all files in catalogue been updated? BNE LA0A5 ;loop until true PLA ;discard LBA of start of data area PLA PLA ;discard file map terminator PLA JSR pgmain ;page in main workspace JMP LA932 ;return to caller .LA0C9 ;Confirm space available after file LDA movcat ;get catalogue offset of file to extend JMP LA0D0 ;jump into routine .LA0CE ;Confirm space available LDA movlen ;get number of files in catalogue * 8 .LA0D0 LSR A ;divide by two, =no. four-byte records PHA ;save on stack CLC ADC mapsp ;add to file map pointer TAY ;terminator located at $0105..08,Y PLA ;restore A LSR A ;divide by four, = no. files LSR A STA mapctr ;store file count INC mapctr ;increment it to include end of volume LDX #$00 STX mfreel ;clear total slack space STX mfreeh BEQ LA0E9 ;jump into loop (always) .LA0E5 INX JSR LA9B6 ;subtract 4 from Y (toward higher LBAs) .LA0E9 JSR LA12C ;calculate LBA of end of previous file ;calculate slack space before current file ;add slack space to total JSR LA160 ;subtract total slack space - excess BCS LA0FB ;if slack will absorb excess then return C=1 DEC mapctr ;else loop BNE LA0E5 ;until all files in map tested .LA0FB RTS ;return C=0 cannot absorb excess .LA0FC ;Confirm space available before file LDA movcat ;get catalogue offset of file to extend LSR A ;divide by two, =no. four-byte records CLC ADC mapsp ;add to file map pointer TAY ;file's entry located at $0105..08,Y SEC LDA movlen ;get number of files in catalogue * 8 SBC movcat ;subtract offset of file to extend LSR A ;divide by 8 LSR A LSR A STA mapctr ;=count of files after file to extend, >=0 INC mapctr ;increment it to include file itself LDX #$00 STX mfreel ;clear total slack space STX mfreeh .LA115 JSR LA9AD ;add 4 to Y (toward lower LBAs) INX JSR LA12C ;calculate LBA of end of previous file ;calculate slack space before current file ;add slack space to total JSR LA160 ;subtract total slack space - excess BCS LA12B ;if slack will absorb excess then return C=1 DEC mapctr ;else loop BNE LA115 ;until all files before subject tested .LA12B RTS ;return C=0 cannot absorb excess .LA12C ;Calculate LBA of end of previous file CLC LDA stack+$06,Y ;get LSB LBA of previous file ADC stack+$08,Y ;add LSB length of file in sectors STA lbaxlo ;store at C4 LDA stack+$05,Y ;get MSB LBA of previous file ADC stack+$07,Y ;add MSB length of file in sectors STA lbaxhi ;store at C5 ;Calculate slack space before current file SEC LDA stack+$02,Y ;get LSB LBA of current file STA dstlo ;store at CA SBC lbaxlo ;subtract LSB LBA of end of previous file STA slackl ;store LSB slack space LDA stack+$01,Y ;get MSB LBA of current file STA dsthi ;store at CB SBC lbaxhi ;subtract MSB LBA of end of previous file STA slackh ;store MSB slack space ;Add slack space to total CLC LDA mfreel ;get LSB total slack space ADC slackl ;add LSB slack space before current file STA mfreel ;update LSB total slack space LDA mfreeh ;get MSB total slack space ADC slackh ;add MSB slack space before current file STA mfreeh ;update MSB total slack space RTS .LA160 ;Subtract total slack space - excess SEC LDA mfreel ;get LSB total slack space SBC excesl ;subtract LSB excess (i.e. space to be made) STA mheadl ;store LSB headroom LDA mfreeh ;get MSB total slack space SBC excesh ;subtract MSB excess STA mheadh ;store MSB headroom RTS ;c=1 if slack space will absorb excess .wfile ;OSFILE JSR savit ;save XY JSR pgmain ;page in main workspace PHA ;push A JSR clrwld ;disallow wildcard characters in filename STX blkptr+$00 ;set up pointer from XY STX fcbadr+$00 STY blkptr+$01 STY fcbadr+$01 LDX #$00 LDY #$00 JSR shfttw ;copy word at pointer to $BC,D .wfile4 JSR shftbo ;copy next four dwords to $BE..C5 (low words) CPY #$12 ;$FDB5..C (high words) BNE wfile4 PLA ;transfer call number to X TAX INX ;increment for use as index CPX #filjph-filjpl ;was call number $FF or 0..6? BCS LA19F ;if not then exit LDA filjph,X ;else get action address high byte PHA ;save on stack LDA filjpl,X ;get action address low byte PHA ;save on stack .LA19F RTS ;jump to action address .fwrcat ;OSFILE 1 = write catalogue information ;[BUG] can set attributes on open file JSR tryflc ;ensure unlocked file exists JSR mvilod ;set load address from OSFILE block JSR mviexe ;set exec address from OSFILE block BVC dort1b ;branch to set attributes and write (always) .fwrlod ;OSFILE 2 = write load address JSR tryflc ;ensure unlocked file exists JSR mvilod ;set load address from OSFILE block BVC dort1a ;branch to write catalogue (always) .fwrexe ;OSFILE 3 = write execution address JSR tryflc ;ensure unlocked file exists JSR mviexe ;set exec address from OSFILE block BVC dort1a ;branch to write catalogue (always) .fwratt ;OSFILE 4 = write file attributes JSR tryfil ;ensure file exists JSR chkopn ;ensure file not open (mutex) ;[BUG] destroys OSFILE block pointer, $B0..1 .dort1b JSR mviatt ;set file attributes from OSFILE block .dort1a JSR titend ;write volume catalogue LDA #$01 ;return A=1, file found RTS .frdcat ;OSFILE 5 = read catalogue information JSR tryfil ;ensure file exists JSR chukbk ;return catalogue information to OSFILE block LDA #$01 ;return A=1, file found RTS .fdefil ;OSFILE 6 = delete file JSR tryflc ;ensure unlocked file exists JSR chukbk ;return catalogue information to OSFILE block JSR delfil ;delete catalogue entry JMP dort1a ;write volume catalogue, return A=1 .mvilod ;Set load address from OSFILE block JSR savita ;save AXY LDY #$02 ;set offset = 2 LDA (blkptr),Y ;get LSB load address from OSFILE block JSR pgcat1 ;page in catalogue sector 1 STA cathig+$00,X ;store in catalogue entry INY ;increment offset; Y=3 LDA (blkptr),Y ;get 3MSB load address STA cathig+$01,X ;store in catalogue entry INY ;increment offset; Y=4 LDA (blkptr),Y ;get 2MSB load address ASL A ;extract b17,b16, place in b3,b2 ASL A EOR cathig+$06,X ;XOR with existing top bits AND #$0C ;mask b3,b2; A=....XX.. BPL mviin0 ;branch to update top bits (always) .mviexe ;Set exec address from OSFILE block JSR savita ;save AXY LDY #$06 ;set offset = 6 LDA (blkptr),Y ;get LSB exec address from OSFILE block JSR pgcat1 ;page in catalogue sector 1 STA cathig+$02,X ;store in catalogue entry INY ;increment offset; Y=7 LDA (blkptr),Y ;get 3MSB exec address STA cathig+$03,X ;store in catalogue entry INY ;increment offset; Y=8 LDA (blkptr),Y ;get 2MSB load address ROR A ;extract b17,b16, place in b7,b6 ROR A ROR A EOR cathig+$06,X ;XOR with existing top bits AND #$C0 ;mask b7,b6; A=XX...... .mviin0 EOR cathig+$06,X ;XOR old top bits with A; 6 bits old, 2 new STA cathig+$06,X ;set top bits exec/length/load/start sector CLV ;return V=0 JMP pgmain ;page in main workspace .mviatt ;Set file attributes from OSFILE block JSR savita ;save AXY JSR tryfl0 ;set up pointer to user's OSFILE block LDY #$0E ;set Y=14, offset of file attributes LDA (blkptr),Y ;get LSB of file attributes AND #$0A ;test b3=file locked, b1=writing denied ;NB b2..b0 are in opposite sense to RISC OS ;where they enable execute, write, read resp. ;this is well-documented in DFS and RISC OS TAY ;hold result, Y>0 iff file is to be locked JSR pgcat0 ;page in catalogue sector 0 LDA modify,X ;get directory character ROL A ;shift out old bit 7 CPY #$01 ;set C=1 iff Y>0 ROR A ;shift in new bit 7, b6..b0 = directory char STA modify,X ;save directory char with new lock attribute RTS .tryflc ;Ensure unlocked file exists JSR tryfl1 ;test if file exists BCC tryfl2 ;if not then return A=0 from caller, else: .chklok ;Ensure file not locked JSR pgcat0 ;page in catalogue sector 0 LDA modify,Y ;if directory character b7=1 BPL chkopr .delprt JSR filmsg ;then raise "File locked" error. EQUB $C3 EQUS "locked" EQUB $00 .chkopl ;Ensure file not locked or open (mutex) JSR chklok ;ensure file not locked .chkopn ;Ensure file not open (mutex) JSR savita ;save AXY TYA ;save catalogue pointer LDX #<catlow ;point XY to filename in catalogue, $FD08 LDY #>catlow JSR L9B10 ;compare filename at XY+A with open filenames BCC chkopr ;if unequal then return JMP filopn ;else raise "File open" error. .tryfil ;Ensure file exists JSR tryfl1 ;test if file exists BCS chkopr ;if present then return, else: .tryfl2 ;Return A=0 from caller PLA ;discard return address on stack (ew!) PLA LDA #$00 ;return A=0 as if from caller. .chkopr RTS .tryfl1 ;Test if file exists JSR frmnam ;set current file from argument pointer JSR lookup ;search for file in catalogue BCC tryfl3 ;if file not found then exit C=0 TYA ;else transfer catalogue pointer to X: TAX .tryfl0 ;Set up pointer to user's OSFILE block LDA fcbadr+$00 STA blkptr+$00 LDA fcbadr+$01 STA blkptr+$01 .tryfl3 RTS .wbgpb ;OSGBPB CMP #wgptbh-wgptbl BCS tryfl3 ;if call number >=9 then return JSR savita ;else save AXY JSR pgmain ;page in main workspace JSR wopa ;have A=0 returned on exit STX btemp+$00 ;save OSGBPB block pointer in workspace STY btemp+$01 TAY ;transfer call number to Y for use as index JSR wbrest ;execute OSGBPB call PHP JSR reltub ;release Tube if present PLP RTS .wbrest LDA wgptbl,Y ;get low byte of action address from table STA qtemp+$00 LDA wgptbh,Y ;get high byte of action address from table STA qtemp+$01 LDA wbrwtb,Y ;get microcode byte from table LSR A ;push bit 0 as C PHP LSR A ;push bit 1 as C PHP STA ctemp ;store Tube service call number as bits 0..5 JSR makatp ;set up pointer to user's OSGBPB block LDY #$0C ;13 bytes to copy, $0C..$00: .wbgpb0 LDA (gbadr),Y ;copy user's OSGBPB block STA dosram,Y ;to workspace DEY ;loop until 13 bytes copied BPL wbgpb0 LDA dosram+$03 ;and high bytes of address AND dosram+$04 ;a=$FF if address is in the host ORA notube ;a=$FF if Tube absent ($FDCD=NOT MOS flag!) CLC ADC #$01 ;set A=0, C=1 if transferring to/from host BEQ wbgpba ;if A>0 JSR clatub ;then claim Tube CLC LDA #$FF ;and set A=$FF, C=0, transferring to/from Tube .wbgpba STA tumflg ;set Tube transfer flag LDA ctemp ;set A=0 if writing user mem, A=1 if reading BCS wbgpb9 ;if transferring to/from Tube LDX #<(dosram+$01) ;then point XY to OSGBPB data address LDY #>(dosram+$01) JSR taddrl ;call Tube service to open Tube data channel .wbgpb9 PLP ;set C=microcode b1 BCS wbgpbb ;if reading/writing data then transfer it PLP ;else C=microcode b0 (=0), pop off stack .qjmi JMP (qtemp) ;and jump to action address. .wbgpbb LDX #$03 ;4 bytes to copy, 3..0: .wbgpb8 LDA dosram+$09,X ;copy OSGBPB pointer field STA zgbptr,X ;to zero page DEX BPL wbgpb8 LDX #zgbptr ;point X to pointer in zero page LDY dosram+$00 ;set Y=channel number LDA #$00 ;set A=0, read PTR not EXT PLP ;set C=microcode b0 BCS wbgpb1 ;if C=0 JSR vstar ;then call OSARGS 1,Y set PTR. .wbgpb1 JSR vradr ;call OSARGS 0,Y return PTR LDX #$03 ;4 bytes to copy, 3..0: .wbgpb7 LDA zgbptr,X ;copy pointer in zero page STA dosram+$09,X ;to OSGBPB pointer field DEX BPL wbgpb7 .wcbat0 JSR comwrk ;invert OSGBPB length field BMI wbgpb4 ;and branch into loop (always) .wbgpb3 LDY dosram+$00 ;set Y = channel number JSR qjmi ;transfer byte / element BCS wbgpb6 ;if attempted read past EOF then finish LDX #$09 ;else set X = $09, point to OSGBPB pointer JSR zerinc ;increment pointer .wbgpb4 LDX #$05 ;set X = $05, point to OSGBPB length field JSR zerinc ;increment OSGBPB length field (inverted) BNE wbgpb3 ;if not overflowed to zero then loop CLC ;else set C = 0, no read past EOF: .wbgpb6 PHP JSR comwrk ;invert OSGBPB length field LDX #$05 ;add one to get two's complement (0 -> 0) JSR zerinc ;thus, number of elements not transferred JSR makatp ;set up pointer to user's OSGBPB block ;clear EOF warning flag in OSGBPB LDY qtemp+$00 ;get LSB of OSGBPB action address CPY #<wcbatr ;does it match 'return one filename'? BEQ LBFD2 ;if not LDY dosram+$00 ;set Y = file handle from OSGBPB block JSR LAA33 ;clear EOF warning flag .LBFD2 LDY #$0C ;13 bytes to copy, offsets 0..$C: .wbgpb5 LDA dosram,Y ;copy OSGBPB block back to user memory STA (gbadr),Y DEY BPL wbgpb5 PLP .LA39B RTS ;OSGBPB 1 = set pointer and write data .wbptr ;OSGBPB 2 = write data JSR wbread ;get byte from user memory JSR wbput ;call OSBPUT; write byte to file CLC ;return C=0 no end-of-file condition RTS ;3 join wbgtr+wbwrit ;OSGBPB 3 = set pointer and read data .wbgtr ;OSGBPB 4 = read data JSR wbget ;call OSBGET; read byte from file BCS LA39B ;if end-of-file reached return C=1 JMP wbwrit ;else write data byte to user memory .rdtco ;OSGBPB 5 = read title, boot option and drive JSR setdef ;set current vol/dir = default, set up drive JSR dirld ;ensure current volume catalogue loaded LDA #$0C ;write 12 to user memory JSR wbwrit ;= length of title LDY #$00 ;set offset to 0 .LA3B9 JSR pgcat0 ;page in catalogue sector 0 LDA dirlow,Y ;get first eight characters of title JSR wbwrit ;write to user memory INY CPY #$08 ;loop until 8 characters written BNE LA3B9 .LA3C7 JSR pgcat1 ;page in catalogue sector 1 LDA dirhig-$08,Y ;get last four characters from $FD00..3 JSR wbwrit ;write to user memory (Y = 8..11) INY CPY #$0C ;loop until 4 more characters written BNE LA3C7 JSR pgcat1 ;page in catalogue sector 1 LDA option ;get boot option/top bits volume size JSR sfour ;shift A right 4 places JMP wbwrit ;write boot option to user memory and exit ;[D]does not return CSD drive number .rdbir ;OSGBPB 6 = read default (CSD) drive and dir LDA defdsk ;get default volume JSR LA480 ;write length+drive identifier to user memory JSR wowrit ;write binary 1 to user memory LDA defqua ;get default directory character JMP wbwrit ;write it to user memory and exit .rlbir ;OSGBPB 7 = read library drive and directory LDA libdsk ;get library volume JSR LA480 ;write length+drive identifier to user memory JSR wowrit ;write binary 1 to user memory LDA libqua ;get library directory character JMP wbwrit ;write it to user memory and exit .wcbat ;OSGBPB 8 = read filenames in default dir JSR setdef ;set current vol/dir = default, set up drive JSR dirld ;ensure current volume catalogue loaded LDA #<wcbatr ;replace action address with $A412 STA qtemp+$00 ;= return one filename LDA #>wcbatr STA qtemp+$01 JMP wcbat0 ;and return requested number of filenames. .wcbatr ;Return one filename (called during OSGBPB 8) JSR pgmain ;page in main workspace LDY dosram+$09 ;set Y = catalogue pointer (0 on first call) .wcbat4 JSR pgcat1 ;page in catalogue sector 1 CPY dirlen ;compare with no. files in catalogue BCS LA44E ;if out of files return C=1, read past EOF JSR pgcat0 ;else page in catalogue sector 0 LDA modify,Y ;get directory character of cat entry JSR caps ;set C=0 iff character in A is a letter EOR qualif ;compare with current directory character BCS wcbat5 ;if directory character is a letter AND #$DF ;then ignore case. .wcbat5 AND #$7F ;mask off attribute bit b7 BEQ wcbat3 ;if catalogue entry not in current directory JSR step ;then add 8 to Y BNE wcbat4 ;and loop (always) .wcbat3 LDA #$07 ;else write 7 to user memory JSR wbwrit ;= length of filename STA gbctr ;set counter to 7 .wcbat1 JSR pgcat0 ;page in catalogue sector 0 LDA catlow,Y ;get character of leaf name JSR wbwrit ;write byte to user memory INY ;increment catalogue pointer DEC gbctr ;loop until 7 characters transferred BNE wcbat1 ;(Y is 7 up, inc at $A379 puts pointer 8 up) CLC ;c=0, did not run out of filenames: .LA44E JSR pgcat1 ;page in catalogue sector 1 LDA cycno ;get catalogue cycle number JSR pgmain ;page in main workspace STY dosram+$09 ;put updated cat ptr in OSGBPB pointer field STA dosram+$00 ;return catalogue cycle no. in channel field RTS .adrld ;Set up pointer to user I/O memory PHA LDA dosram+$01 STA gbusr+$00 LDA dosram+$02 STA gbusr+$01 LDX #$00 ;offset = 0 for indexed indirect load/store PLA RTS .wbread ;Read data byte from user memory BIT tumflg ;test Tube transfer flag BPL wbrea0 ;if b7=0 then read from I/O memory LDA reg3 ;else read from R3DATA JMP adrinc ;increment OSGBPB address field .wbrea0 JSR adrld ;set up pointer to user I/O memory LDA (gbusr,X) ;read byte from user I/O memory JMP adrinc ;increment OSGBPB address field .LA480 ;Write length+drive identifier to user memory PHA LDY #$01 ;return Y=1 AND #$F0 ;unless volume letter is B..H BEQ LA488 INY ;in which case return Y=2 .LA488 TYA JSR wbwrit ;write length of drive ID to user memory PLA PHA AND #$0F ;extract drive number CLC ADC #$30 ;convert to ASCII digit JSR wbwrit ;write data byte to user memory PLA JSR sfour ;shift A right 4 places BEQ wbgtr0 ;if volume letter is A then exit CLC ADC #$41 ;else convert binary to letter B..H JMP wbwrit ;write it to user memory and exit .wowrit ;Write binary 1 to user memory LDA #$01 .wbwrit ;Write data byte to user memory JSR pgmain ;page in main workspace BIT tumflg ;test Tube flag BPL LA4B1 ;if Tube not in use then write to I/O memory STA reg3 ;else put byte in R3DATA BMI adrinc ;and increment OSGBPB address field (always) .LA4B1 JSR adrld ;set up pointer to user I/O memory STA (gbusr,X) ;store byte at pointer: .adrinc ;Increment OSGBPB address field JSR savita ;save AXY LDX #$01 ;set X = $01, point to OSGBPB data address: .zerinc ;Increment OSGBPB field LDY #$04 .zerin0 INC dosram,X BNE zerin1 INX DEY BNE zerin0 .zerin1 RTS ;return Z=1 iff field overflows .comwrk ;Invert OSGBPB length field LDX #$03 .wbgpb2 LDA #$FF EOR dosram+$05,X STA dosram+$05,X DEX BPL wbgpb2 RTS .makatp ;Set up pointer to user's OSGBPB block LDA btemp+$00 STA gbadr+$00 LDA btemp+$01 STA gbadr+$01 .wbgtr0 RTS ;[D]The next two routines are in opposite order to DDOS .LA4E0 ;Put data byte in user memory BIT tubflg ;test Tube data transfer flag BMI LA4E8 ;if transferring to host STA (nmiusr),Y ;then write to address in I/O memory RTS .LA4E8 STA reg3 ;else write to R3DATA. RTS .LA4EC ;Get data byte from user memory BIT tubflg ;test Tube data transfer flag BMI LA4F4 ;if transferring from host LDA (nmiusr),Y ;then read address in I/O memory RTS .LA4F4 LDA reg3 ;else read from R3DATA. RTS .LA4F8 ;Return no. reserved sectors in data area JSR pgmain ;page in main workspace JSR LB74C ;is the physical drive a RAM disc? BEQ LA507 ;if so then return A=2 LDA #$00 ;else A=0 BIT denflg ;test density flag BVS LA509 ;if single density .LA507 LDA #$02 ;then return A=2 .LA509 RTS ;else return A=0 .getlsz ;Get start and size of user memory JSR pgmain ;page in main workspace LDA #$83 ;call OSBYTE $83 = read OSHWM JSR osbyte STY frpage ;save MSB LDA #$84 ;call OSBYTE $84 = read HIMEM JSR osbyte TYA STA hipage ;save MSB SEC SBC frpage ;subtract MSB of OSHWM STA frsize ;save result = no. pages of user memory. RTS .pmhelp ;*HELP UTILS LDX #<initbl ;Print utility command table at $914D LDY #>initbl LDA #$08 ;8 entries to print (not *DISK) BNE help1 .help ;*HELP CHAL / *HELP DFS LDX #<comtab ;Print Challenger command table at $90B4 LDY #>comtab LDA #$13 ;19 entries to print .help1 JSR L921E ;set up trampoline to read table at XY STA hlpcot ;store number of printable entries in counter JSR pcrlf ;print newline CLC ;c=0 print version number in banner JSR LAE5E ;print Challenger banner JSR vstrng ;print copyright message EQUS "(C) Otus 2024" EQUB $0D LDX #$00 ;set offset in command table = 0 .help0 JSR pdspc ;print two spaces JSR psyntx ;print command name and syntax JSR pcrlf ;print newline DEC hlpcot ;decrement count of entries BNE help0 ;loop until none remain RTS .psyntx ;Print command name and syntax JSR savita ;save AXY LDX #$00 ;set offset in command table = 0 LDY #$09 ;9 characters in command name column .LA585 JSR tramp ;get byte of command name BMI LA592 ;if terminator reached then print syntax JSR pchr ;else print character in A (OSASCI) DEY ;decrement number of spaces remaining INX ;increment offset BNE LA585 ;and loop (always) .LA592 ;Print syntax DEY ;if Y in range 1..128 BMI LA599 ;then command not reached edge of column INY ;so JSR yspace ;print number of spaces in Y .LA599 INX ;skip action address INX JSR tramp ;get syntax byte PHA ;save it INX ;skip over it JSR L922D ;add X to trampoline address PLA JSR psynt5 ;print syntax element JSR sfour ;shift A right 4 places AND #$07 ;mask b2..0 ignore restricted cmd bit: .psynt5 ;Print syntax element JSR savita ;save AXY AND #$0F ;mask b3..0 current syntax element BEQ LA5D0 ;if null element then return TAY ;else transfer to Y for use as counter LDA #$20 ;print a space JSR pchr ;print character in A (OSASCI) LDX #$FF ;set offset=$FF going to 0: .psynt2 INX ;increment offset LDA argtbl,X ;get character of syntax element table BNE psynt2 ;loop until NUL reached DEY ;decrement number of NULs to skip BNE psynt2 ;when Y=0 we've reached correct element: .LA5C4 INX ;increment offset LDA argtbl,X ;get character of syntax element table BEQ LA5D0 ;if NUL reached then return JSR pchr ;else print character in A (OSASCI) JMP LA5C4 ;and loop until element printed. .LA5D0 RTS ;Table of syntax elements .argtbl EQUB $00 ;element $0, "" EQUS "<fsp>" ;element $1, <fsp> EQUB $00 EQUS "<afsp>" ;element $2, <afsp> EQUB $00 EQUS "(L)" ;element $3, (L) EQUB $00 EQUS "<src drv>" ;element $4, <src drv> EQUB $00 EQUS "<dest drv>" ;element $5, <dest drv> EQUB $00 EQUS "<dest drv> <afsp>" ;element $6, <dest drv> <afsp> EQUB $00 EQUS "<new fsp>" ;element $7, <new fsp> EQUB $00 EQUS "<old fsp>" ;element $8, <old fsp> EQUB $00 EQUS "(<dir>)" ;element $9, (<dir>) EQUB $00 EQUS "(<drv>)" ;element $A, (<drv>) EQUB $00 EQUS "<title>" ;element $B, <title> EQUB $00 ;terminator byte .compct ;*COMPACT JSR LAA0E ;parse volume spec from argument STA fdriv ;set as source drive STA tdriv ;set as destination drive JSR vstrng EQUS "Compacting " NOP JSR L8EAD ;print " Drive " plus volume spec in A JSR pcrlf ;print newline JSR close2 ;[D]close all files JSR getlsz ;get start and size of user memory JSR L962F ;load volume catalogue L4 JSR L8523 ;save parameters of source drive JSR L852A ;save parameters of destination drive JSR pgcat1 ;page in catalogue sector 1 LDY dirlen ;get number of files in catalogue STY cpycat ;set as catalogue pointer LDA #$00 ;initialise LBA to start of data area STA dsthi JSR LA4F8 ;return no. reserved sectors in data area STA dstlo .LA673 LDY cpycat ;set Y to catalogue pointer JSR unstep ;subtract 8 from Y CPY #$F8 ;if we've reached end of catalogue BEQ LA6D6 ;then finish STY cpycat ;else set new catalogue pointer JSR inform ;print *INFO line if verbose LDY cpycat JSR LA703 ;test length of file BEQ cmpct4 ;if empty then only print *INFO line LDA #$00 STA lodlo ;else set LSB load address = 0 STA lenlo ;set LSB transfer size = 0 JSR LA714 ;calculate number of sectors used by file JSR pgcat1 ;page in catalogue sector 1 LDA cathig+$07,Y ;get LSB start sector STA srclo ;set LSB source LBA LDA cathig+$06,Y ;get top bits exec/length/load/start sector AND #$03 ;extract b1,b0 of A STA srchi ;set MSB source LBA CMP dsthi ;compare with destination LBA BNE cmpct2 ;if unequal then compact file LDA srclo ;else compare LSBs source - destination LBA CMP dstlo BNE cmpct2 ;if unequal then compact file JSR LA733 ;else add number of sectors to total JMP cmpct4 ;print *INFO line and loop for next file .cmpct2 ;Compact file JSR pgcat1 ;page in catalogue sector 1 LDA dstlo ;set LSB start sector = destination LBA STA cathig+$07,Y LDA cathig+$06,Y ;get top bits exec/length/load/start sector AND #$FC ;clear b1,b0 MSB start sector ORA dsthi ;replace with MSB destination LBA STA cathig+$06,Y ;set top bits exec/length/load/start sector LDA #$00 STA cbcont ;no catalogue entry waiting to be created STA swaps ;$00 = source and dest. are different drives ;(no swapping) JSR mvdkda ;copy source drive/file to destination JSR dirout ;write volume catalogue L4 .cmpct4 LDY cpycat JSR prtinf ;print *INFO line JMP LA673 ;loop for next file .LA6D6 JSR vstrng ;print "Disk compacted " EQUS "Disk compacted " NOP SEC JSR pgcat1 ;page in catalogue sector 1 LDA dirhig+$07 ;get LSB volume size SBC dstlo ;subtract LSB sectors used on volume STA mfreel ;=LSB sectors free. save on stack LDA dirhig+$06 ;get boot option/top bits volume size AND #$03 ;extract volume size in b1,b0 SBC dsthi ;subtract MSB sectors used on volume STA mfreeh JSR L8BDD ;[D]print number of free sectors .L88CA ;Store empty BASIC program at OSHWM (NEW) LDA frpage ;get start of user memory STA page+$01 ;store as high byte of pointer LDY #$00 ;clear low byte STY page+$00 ;PAGE is always on a page boundary LDA #$0D STA (page),Y ;$0D = first byte of end-of-program marker INY ;store at start of user memory LDA #$FF ;$FF = second byte of end-of-program marker STA (page),Y ;store in user memory RTS .LA703 ;Test length of file JSR pgcat1 ;page in catalogue sector 1 LDA cathig+$06,Y ;get top bits exec/length/load/start sector AND #$30 ;extract length in b5,b4 ORA cathig+$05,Y ;OR with 2MSB, LSB of length ORA cathig+$04,Y ;return Z=1 if length=0, empty file. JMP pgmain ;page in main workspace ;DDOS version of routine; Challenger version is at L94EB. ;Returns to todolo/hi and leaves main workspace paged (vs. lbalo/hi and catalogue). .LA714 ;Calculate number of sectors used by file JSR pgcat1 ;page in catalogue sector 1 LDA cathig+$04,Y ;get LSB length CMP #$01 ;c=1 iff LSB >0 LDA cathig+$05,Y ;add C to 2MSB length, rounding up ADC #$00 ;(Y points to 8 bytes before file entry) STA todolo LDA cathig+$06,Y ;get top bits exec/length/load/start sector PHP ;save carry flag from addition JSR isolen ;extract length from b5,4 to b1,0 PLP ;restore carry flag ADC #$00 ;add C to MSB length, rounding up STA todohi ;store length in sectors in zero page JMP pgmain ;page in main workspace .LA733 ;Add number of sectors to total CLC ;add LSB LDA dstlo ADC todolo STA dstlo LDA dsthi ;add MSB ADC todohi STA dsthi RTS ;Note: Challenger does not give the option to copy between opposite sides of ;different discs with one drive. *BACKUP 0 2 for instance copies one side ;of the disc to the other, without the chance to swap discs. .LA741 ;Set swapping and current disc flags LDA tdriv ;[D]get destination drive JSR LAADB ;map volume in A to physical volume STA swaps ;store in temporary variable LDA fdriv ;get source drive JSR LAADB ;map volume in A to physical volume CMP swaps ;compare with destination drive BNE LA75A ;if equal LDA #$FF ;then A=$FF STA swaps ;b7=1 source and dest. share drive (swapping) STA tindrv ;b7=1 dest. disc in drive (ask for source) RTS .LA75A LDA #$00 ;$00 = source and dest. are different drives STA swaps RTS .chkena ;Ensure *ENABLE active JSR savita ;[D]save AXY BIT enaflg ;test *ENABLE flag BPL chkna1 ;if b7=0 then current command is enabled JSR vstrng ;[D]else print "Are you sure ? Y/N " EQUB $0D ;(DDOS raises "Not enabled" error) EQUS "Are you sure ? Y/N " NOP JSR getyn ;ask user yes or no BEQ chkna1 ;if user typed Y then return LDX abrtsp ;else reset to stack pointer set at $80C0 TXS ;and exit from *command. .chkna1 JMP pcrlf ;[D]print newline .get2dr ;Parse and print source and dest. volumes JSR chksyn ;call GSINIT with C=0 and require argument JSR getdrv ;parse volume spec STA fdriv ;store source volume JSR chksyn ;call GSINIT with C=0 and require argument JSR getdrv ;parse volume spec STA tdriv ;store destination volume TYA ;save GSINIT offset in Y PHA JSR LA741 ;set swapping and current disc flags JSR getlsz ;get start and size of user memory JSR vstrng ;print "Copying from drive " EQUS "Copying from drive " LDA fdriv ;get source volume JSR L8EB8 ;print volume spec in A JSR vstrng ;print " to drive " EQUS " to drive " LDA tdriv ;get destination volume JSR L8EB8 ;print volume spec in A JSR pcrlf ;print newline PLA ;restore GSINIT offset to Y TAY CLC RTS .iprdec SED ;set decimal mode CLC ;increment low byte LDA linnol ADC #$01 ;only ADC and SBC have decimal mode STA linnol ;carry out in C, the only valid flag LDA linnoh ;carry out to high byte ADC #$00 STA linnoh CLD ;clear decimal mode .LA7E9 ;Print space-padded hex word CLC ;set C=1, pad numeric field with spaces LDA linnoh ;[D]get high byte of word JSR LA800 ;print hex byte, C=0 if space-padded BCS LA7F2 ;c=digit printed; preserve over entry point .LA7F1 ;Print space-padded hex byte CLC .LA7F2 ;Print hex byte, C=0 if space-padded LDA linnol ;get low byte of word BNE LA800 ;if non-zero then print it BCS LA800 ;else if not space-padded then print zeroes JSR pspace ;else print a space JMP digout ;and print hex nibble (0). .LA800 ;Print hex byte, C=0 if space-padded PHA PHP ;save space padding flag in C JSR sfour ;shift A right 4 places PLP ;restore C JSR LA80A ;print top nibble of byte PLA ;restore bottom nibble: .LA80A ;Print space-padded hex nibble PHA ;test accumulator, Z=1 if zero PLA BCS LA810 ;if digit has been printed print another BEQ pspace ;else if nibble is zero print a space .LA810 JSR digout ;else print hex nibble SEC ;set C=1 to suppress space padding RTS ;and exit .pdspc ;Print two spaces JSR pspace .pspace ;Print a space PHA ;preserve A LDA #$20 JSR pchr ;print character in A (OSASCI) PLA CLC ;return C=0 RTS .zerchk ;Claim service call and set up argument ptr TSX LDA #$00 ;have A=0 returned on exit STA stack+$07,X TYA ;save string offset PHA JSR chksyn ;call GSINIT with C=0 and require argument PLA ;restore string offset ;set XY to GSINIT pointer + Y [D]vestigial ep CLC ;add Y to LSB of GSINIT pointer ADC linptr+$00 TAX ;hold in X LDA linptr+$01 ;carry out to high byte of GSINIT pointer ADC #$00 TAY ;hold in Y .LA838 ;Set line number = 0 LDA #$00 ;[D]new entry point STA linnol STA linnoh RTS .wopa ;Have A=0 returned on exit PHA ;caller called Save AXY, A was at $0105,S TXA ;save caller's AX PHA ;these two bytes plus return address make 4 TSX ;superroutine's A is thus 5+4 = 9 bytes down LDA #$00 STA stack+$09,X PLA ;restore caller's AX TAX PLA RTS .savita ;Save AXY PHA ;stack = $4F,$A8,a,cl,ch,sl,sh JSR savit0 ;return to caller,RTS jumps to next line .savrta ;Restore AXY and return to superroutine PLA ;stack = y,x,a,sl,sh TAY ;cl,ch=caller return address PLA ;sl,sh=superroutine return address TAX PLA RTS .savit0 ;Poke AXY into stack, return to caller PHA PHA PHA TXA PHA TSX ;stack = x,a,a,a,$4F,$A8,a,cl,ch,sl,sh LDA stack+$09,X ;caller address high byte STA stack+$04,X LDA stack+$08,X ;caller address low byte STA stack+$03,X LDA stack+$07,X ;A on entry STA stack+$09,X TYA ;Y on entry STA stack+$07,X ;stack = x,a,cl,ch,$4F,$A8,y,cl,a,sl,sh PLA ;X on entry STA stack+$08,X ;stack = a,cl,ch,$4F,$A8,y,x,a,sl,sh TAX PLA ;restore A on entry RTS ;return to caller .savit ;Save XY PHA ;stack = $7C,$A8,a,cl,ch,sl,sh JSR savit0 ;return to caller,RTS jumps to next line TSX STA stack+$03,X ;replace A on entry with A from caller JMP savrta ;restore AXY and return to superroutine .dskmsg ;Raise "Disk " error JSR fstrng EQUS "Disk " BCC estrng .illmsg ;Raise "Bad " error JSR fstrng EQUS "Bad " BCC estrng .filmsg ;Raise "File " error JSR fstrng EQUS "File " .estrng ;Append error message immediate STA stra ;save A on entry PLA ;pop caller's address into pointer STA strret+$00 PLA STA strret+$01 LDA stra ;restore A on entry and save on stack PHA TYA ;save Y PHA LDY #$00 ;set Y=0 for indirect indexed load JSR tmpinc ;increment $AE,F LDA (strret),Y ;get error number from byte after JSR STA errbuf+$01 ;store at bottom of stack JSR LA940 ;[D]if error message already being built BMI vstrlp ;then complete it LDA #$02 ;else A = $02 STA errbuf+$00 ;error message being built from offset 2 BNE vstrlp ;build error message (always) .fstrng ;Prefix error message immediate JSR LA93B ;[D]begin error message .vstrng ;Print string immediate STA stra ;save A on entry PLA ;pop caller's address into pointer STA strret+$00 PLA STA strret+$01 LDA stra ;restore A on entry and save on stack PHA TYA ;save Y PHA LDY #$00 ;set Y=0 for indirect indexed load: .vstrlp JSR tmpinc ;increment $AE,F LDA (strret),Y ;get character from after JSR BMI LA8F1 ;if b7=1 then opcode terminator, execute it BEQ LA8F8 ;else if NUL then raise error JSR pchr ;else print the character JMP vstrlp ;and loop .LA8F1 PLA ;restore AY TAY PLA CLC JMP (strret) ;jump to address of end of string .LA8F8 ;Terminate error message, raise error LDA #$00 LDX errbuf+$00 ;get offset of end of error message STA errbuf,X ;set NUL error message terminator STA errbuf+$00 ;instruction at $0100 = BRK JSR L8219 ;get Challenger unit type AND #$7F ;b7=0 STA priptr,X ;no error message being built print to screen JSR L9753 ;forget catalogue in JIM pages 2..3 JSR reltub ;release Tube if present JSR nmirel ;release NMI JMP errbuf ;jump to BRK to raise error .LA917 ;Print VDU sequence immediate PLA ;pop caller's address into pointer STA strret+$00 PLA STA strret+$01 TYA ;[D]save Y PHA LDY #$00 ;offset = 0 for indirect indexed load .LA921 JSR tmpinc ;increment $AE,F LDA (strret),Y ;get character from after JSR CMP #$FF ;if $FF terminator byte BEQ LA930 ;then skip it and return to code after it JSR oswrch ;else call OSWRCH JMP LA921 ;and loop .LA930 PLA ;[D]restore Y TAY .LA932 JSR tmpinc ;increment $AE,F JMP (strret) ;jump to address at end of string .LA938 ;Begin error message, number in A STA errbuf+$01 ;set first byte after BRK to error number .LA93B ;Begin error message LDA #$02 STA errbuf+$00 ;error message being built from offset 2 .LA940 JSR L8219 ;get Challenger unit type PHP ORA #$80 ;b7=1 STA priptr,X ;error message being built, $0100 = offset PLP RTS .LA94B ;Print letter N LDA #$4E BNE pchr ;branch (always) .pdot ;Print a dot LDA #$2E .pchr ;Print character in A (OSASCI) JSR savita ;save AXY PHA ;save character JSR L8219 ;[D]get Chal. unit type. if error being built BMI LA96D ;then append character to error message else: JSR LADDC ;call OSBYTE $EC = read/write char dest status TXA ;save current output stream setting PHA ORA #$10 ;b4=1 disable *SPOOL output JSR wriwde ;call OSBYTE $03 = specify output stream in A PLA ;restore previous output stream setting TAX PLA ;restore character JSR osasci ;call OSASCI JMP wriwdx ;call OSBYTE $03 = specify output stream .LA96D ;Append character to error message PLA ;[D]restore character LDX errbuf+$00 ;get pointer to end of error message STA errbuf,X ;store character there INC errbuf+$00 ;and increment pointer RTS .bytout ;Print hex byte PHA ;save A JSR sfour ;shift A right 4 places JSR digout ;print top nibble of byte PLA ;restore bottom nibble: .digout ;Print hex nibble PHA ;save A AND #$0F ;extract b3..0 SED ;set decimal mode for 6502 deep magic CLC ADC #$90 ;a=$90..99, C=0 or A=$00..05, C=1 ADC #$40 ;a=$30..39 or A=$41..46 CLD ;clear decimal mode JSR pchr ;print character in A (OSASCI) PLA ;restore A RTS .ackesc ;Acknowledge ESCAPE condition LDA #$7E ;OSBYTE $7E = acknowledge ESCAPE condition JMP osbyte ;call OSBYTE and exit .isoexe ;Extract b7,b6 of A LSR A LSR A .isolen ;Extract b5,b4 of A LSR A LSR A .isolod ;Extract b3,b2 of A LSR A LSR A AND #$03 RTS .sfive ;Shift A right 5 places LSR A .sfour ;Shift A right 4 places LSR A LSR A LSR A LSR A RTS .lfour ;Shift A left 4 places ASL A ASL A ASL A ASL A RTS .step ;Add 8 to Y INY .step7 ;Add 7 to Y INY INY INY .LA9AD ;Add 4 to Y INY INY INY INY RTS .unstep ;Subtract 8 from Y DEY DEY DEY DEY .LA9B6 ;Subtract 4 from Y DEY DEY DEY DEY RTS .LA9BB ;Uppercase and validate letter in A CMP #$41 ;is character less than capital A? BCC LA9CB ;if so then return C=1 CMP #$5B ;else is it more than capital Z? BCC LA9CD ;if not then uppercase and return C=0 CMP #$61 ;else is it less than lowercase a? BCC LA9CB ;if so then return C=1 CMP #$7B ;else is it more than lowercase z? BCC LA9CD ;if not then uppercase and return C=0 .LA9CB SEC ;else return C=1 RTS .LA9CD AND #$DF ;mask bit 5, convert letter to uppercase CLC RTS .caps ;Set C=0 iff character in A is a letter PHA JSR LA9BB ;uppercase and validate letter in A PLA RTS .LA9E1 ;Convert ASCII hex digit to binary CMP #$41 ;if digit is less than A BCC LA9E7 ;then convert 0..9 to binary SBC #$07 ;else convert A..F to binary .LA9E7 SEC SBC #$30 RTS .tmpinc ;Increment $AE,F INC strret+$00 BNE tmpin0 INC strret+$01 .tmpin0 RTS .setupr ;Call GSINIT with C=0 CLC ;c=0 space or CR terminates unquoted strings JMP gsinit ;jump to GSINIT .LA9F6 ;Set current drive from ASCII digit JSR LAABA ;convert and validate ASCII drive digit BPL dodriv ;set as current drive .LA9FB ;Set volume from ASCII letter JSR LA9BB ;uppercase and validate letter in A SEC ;subtract ASCII value of A SBC #$41 ;obtain ordinal 0..25 CMP #volums ;else is ordinal 8 or more? BCS drverr ;if so then raise "Bad drive" error JSR lfour ;else shift A left 4 places ORA fdrive ;combine volume letter with current drive BCC dodriv ;set as current volume, return C=0 .LAA0E ;Parse volume spec from argument JSR setupr ;call GSINIT with C=0 BEQ setddr ;if no argument then current vol = default BNE getdrv ;else parse volume spec .setdef ;Set current volume and directory = default JSR pgmain ;page in main workspace LDA defqua ;get default directory STA qualif ;set as current directory: .setddr ;Set current volume = default volume LDA defdsk ;get default volume .dodriv STA fdrive ;set as current volume .LAC19 RTS .LAC1A ;Ensure volume letter is A LDA fdrive ;get current volume AND #$F0 ;extract volume letter BEQ LAC19 ;if volume letter is not A .drverr ;Raise "Bad drive" error JSR illmsg EQUB $CD EQUS "drive" EQUB $00 .LAA3B ;Call GSINIT and parse directory spec JSR setupr ;call GSINIT with C=0, fall through: .readd0 ;Parse directory spec JSR gsread ;call GSREAD BCS LAA71 ;if end of argument then exit C=1 CMP #$3A ;else is character a colon? BNE LAA69 ;if not then accept directory character JSR gsread ;else call GSREAD BCS drverr ;if ":" by itself then "Bad drive" error JSR LA9F6 ;else set current drive from ASCII digit JSR gsread ;call GSREAD BCS LAA71 ;if ":<drv>" keep current volume and dir CMP #$2E ;else is character a full stop? BEQ LAA64 ;if so then expect a directory character JSR LA9FB ;else set volume from ASCII letter JSR gsread ;call GSREAD BCS LAA71 ;if ":<drv><vol>" keep current directory CMP #$2E ;else ".<dir>" must follow BNE drverr ;if next char not full stop "Bad drive" .LAA64 JSR gsread ;else call GSREAD BCS drverr ;directory char expected else "Bad drive" .LAA69 JSR LAAB0 ;set directory from ASCII character JSR gsread ;if not at end of argument BCC drverr ;then raise "Bad drive" error. .LAA71 RTS .LAA72 ;Select specified or default volume JSR setddr ;set current volume = default volume LDX #$00 ;x=0, nothing specified JSR gsread ;call GSREAD BCS LAA71 ;if end of argument then exit C=1 SEC ;else C=1, ambiguous vol spec allowed BCS LAA86 ;jump into parse volume spec .getdrv ;Parse volume spec LDX #$00 ;x=0, nothing specified JSR gsread ;call GSREAD BCS LAA71 ;if end of argument then exit C=1 ;The ambiguity flag and this entry point are vestigial in DDOS. ;These features appeared simultaneously in DDOS 3.x5 revision 3.0 and in ;Challenger 1.00. Whereas DDOS retains its original *XCAT command, ;in Challenger the new code enables the *CAT * syntax which was probably ;copied from EDOS along with the extended *OPT settings. .LAA86 PHP ;[D]else save ambiguity flag in C CMP #$3A ;is character a colon? BNE LAA90 ;if not then set drive from digit JSR gsread ;else call GSREAD BCS drverr ;if ":" by itself then "Bad drive" error .LAA90 JSR LA9F6 ;set current drive from ASCII digit LDX #$02 ;x=2, only drive specified JSR gsread ;call GSREAD BCS LAAA9 ;if no more chars return drive, volume=A PLP ;[D]else restore ambig. flag, if not allowed BCC LAAA4 ;then set volume and return current volume CMP #$2A ;[D]else is character an asterisk? if not BNE LAAA4 ;then set volume and return current volume LDX #$83 ;else X=$83, drive and ambiguous volume spec RTS .LAAA4 JSR LA9FB ;set volume letter from ASCII letter INX ;x=3, drive and volume specified PHP ;push dummy flag .LAAA9 PLP ;discard ambiguity flag LDA fdrive ;get current volume and exit RTS .LAAB0 ;Set directory from ASCII character CMP #$2A ;make * an alias of # BNE LAAB6 LDA #$23 .LAAB6 STA qualif ;set as current directory CLC RTS .LAABA ;Convert and validate ASCII drive digit SEC ;convert to binary drive no. 0..7 SBC #$30 CMP #$08 ;is it more than 8? BCS LAAD6 ;then invalid as a logical drive, "Bad drive" PHA ;else save result JSR LAADB ;map volume in A to physical volume CMP #$05 ;if not physical drive 5, the second RAM disc BNE LAAD4 ;then accept digit JSR L8219 ;else get Challenger unit type AND #$03 ;mask bits 1,0 CMP #$02 ;is a 512 KiB unit attached? BNE LAAD6 ;if not then phys. drive 5 is a "Bad drive" .LAAD4 PLA ;else return logical drive number, N=0 .floppy RTS .LB741 ;Parse floppy volume spec from argument JSR LAA0E ;parse volume spec from argument JSR LB74C ;set Z=1 iff current drive is a RAM disc BNE floppy ;if so .LAAD6 JMP drverr ;raise "Bad drive" error .LAAD9 ;Map current volume to physical volume LDA fdrive ;get current volume: .LAADB ;Map volume in A to physical volume JSR pgaux ;page in auxiliary workspace JSR savit ;save XY TAY ;save volume AND #$07 ;mask logical drive number in bits 2..0 TAX ;transfer to X for use as index TYA ;restore volume AND #$F0 ;mask volume letter in bits 6..4 ORA config,X ;apply physical drive mapped to drive in X JMP pgmain ;page in main workspace and exit ;ChADFS ROM call 0 .LAAF6 ;*CONFIG JSR setupr ;call GSINIT with C=0 BNE LAB31 ;if argument present then parse it JSR vstrng ;else list mapping. Print "L drv:" EQUS "L drv:" LDX #$00 ;start at logical drive 0: .LAB06 TXA ;perform identity mapping to print log.drive JSR LAB90 ;print digit and compare X=8 BNE LAB06 ;loop until logical drives 0..7 listed JSR vstrng ;print newline + "P drv:" EQUB $0D EQUS "P drv:" LDX #$00 ;start at logical drive 0: .LAB18 BIT chadfs ;test b6=ChADFS is current FS JSR pgaux ;page in auxiliary workspace LDA config,X ;preload Challenger physical drive mapping BVC LAB26 ;if ChADFS is current FS LDA adfsmp,X ;then replace with ChADFS physical drive .LAB26 JSR pgmain ;page in main workspace JSR LAB90 ;print digit and compare X=8 BNE LAB18 ;loop until logical drives 0..7 listed JMP pcrlf ;print newline and exit .LAB31 ;Parse *CONFIG argument CMP #$52 ;if first character of argument is capital R BEQ LAB6A ;then reset drive mappings .LAB35 JSR gsread ;else call GSREAD JSR LAABA ;convert and validate ASCII drive digit BIT chadfs ;test b6=ChADFS is current FS BVC LAB43 ;if ChADFS is current FS CLC ;then add 8 to A making offset to ChADFS map: ADC #adfsmp-config .LAB43 STA cnfofs ;save offset into drive mapping table JSR gsread ;call GSREAD BCS LAB67 ;if only log. drive given then "Syntax" error CMP #$3D ;else is next character "="? BNE LAB67 ;if not then "Syntax" error JSR gsread ;else "<drv>="; call GSREAD BCS LAB67 ;if no phys. drive given then "Syntax" error JSR LAABA ;else convert and validate ASCII drive digit JSR pgaux ;page in auxiliary workspace LDX cnfofs ;restore offset into drive mapping table STA config,X ;save physical drive mapping in table JSR pgmain ;page in main workspace JSR setupr ;call GSINIT with C=0 BNE LAB35 ;if argument present then parse it .synok RTS ;else exit (can string together e.g. 0=42=5) .chksyn ;Call GSINIT with C=0 and require argument JSR setupr ;call GSINIT with C=0 BNE synok ;if string empty (and unquoted), syntax error .LAB67 .synerr ;Raise "Syntax: " error JSR estrng EQUB $DC EQUS "Syntax: " NOP JSR psyntx ;print command name and syntax JMP LA8F8 ;terminate error message, raise error .LAB6A ;*CONFIG R BIT chadfs ;if b6=1 ChADFS is current FS BVS LAB81 ;then only reset ChADFS mapping, else: .LAB6F ;Reset *CONFIG mapping JSR pgaux ;page in auxiliary workspace LDX #$07 ;loop for X = 7..0: .LAB74 TXA STA config,X ;configure logical drive X = physical drive X DEX BPL LAB74 ;loop until all 8 drive mappings reset JMP pgmain ;page in main workspace and exit .LAB7E ;ChADFS ROM call 3 JSR LAB6F ;reset *CONFIG mapping .LAB81 JSR pgaux ;page in auxiliary workspace LDX #$07 ;loop for X = 7..0: .LAB86 TXA ;configure ChADFS mapping drive X = drive X STA adfsmp,X DEX BPL LAB86 ;loop until all 8 drive mappings reset JMP pgmain ;page in main workspace and exit .LAB90 JSR pspace ;print a space JSR digout ;print hex nibble INX ;increment counter CPX #$08 ;return Z=1 iff counter has reached 8 RTS .setq ;Set current vol/dir from open filename JSR pgmain ;page in main workspace LDA seqlok,Y ;get directory character of open file AND #$7F ;mask off b7 =channel file locked bit STA qualif ;set as current directory LDA seqchk,Y ;get volume containing open file STA fdrive ;set as current volume LDA sequal,Y ;[D]get first track of volume of open file STA voltrk ;set as first track of current volume LDA seqbuf,Y ;get packed drive parameters of open file JMP L850D ;restore packed drive parameters and exit .LABB5 ;Detect disc format/set sector address JSR pgmain ;page in main workspace JSR nmicla ;claim NMI LDA #$00 STA track ;set track number = 0 STA sector ;[D]set sector number = 0 JSR LB74C ;set Z=1 iff current drive is a RAM disc BEQ LAC23 ;[D]if not JSR LB916 ;[D]then seek logical track LDA txcall ;if data transfer call is not 0 = read data AND #$7F BNE LAC37 ;then set sector number = 2 * volume letter LDA #$00 ;else data area starts on track 0 STA voltrk JSR LAC46 ;set number of sectors per track BIT denflg ;test density flag BVS LABE0 ;if disc is single density JSR LAC1A ;[D]then ensure volume letter is A .LABE0 BIT t40flg ;if double-stepping is automatic BPL LABE8 JSR LAC5D ;then detect track stepping .LABE8 BIT denflg ;if disc is single density BVC LAC37 ;then set sector number = 0 JSR LB52E ;else copy volume allocations to wksp JSR pgcat0 ;page in catalogue sector 0 LDA dcver ;test configuration/version number CMP #$E5 ;of disc catalogue BNE LABFD ;if byte is $E5 formatting fill byte .LAC9F ;then "Disk not configured by VOLGEN" JSR estrng EQUB $CD EQUS "No config" EQUB $00 .LABFD JSR LAC3C ;set sector number = 2 * volume letter TAY ;= offset into the track allocation table LDA dcvtrk,Y ;get first track of volume from disc cat. LDY dcszhi ;get MSB number of sectors on surface LDX dcszlo ;get LSB number of sectors on surface JSR pgmain ;page in main workspace STX dscszl ;save in workspace STY dscszh STA voltrk ;set as first track of current volume TAX BEQ LAC84 ;if =0 raise "Volume . n/a" error RTS .LAC23 ;Set up for RAM disc JSR LAC1A ;ensure volume letter is A LDA denflg ;set density flag to single density AND #$80 ;preserving automatic density setting STA denflg LDA #$00 STA sectrk ;number of sectors per track is undefined STA voltrk ;data area starts on track 0 RTS .LAC37 ;Set sector number = 2 * volume letter LDA denflg ;test density flag BEQ LAC43 ;if single (and manual!) then start sector =0 .LAC3C LDA fdrive ;else get current volume AND #$F0 ;extract volume letter LSR A ;shift right three places LSR A ;to get sector offset of volume catalogue LSR A .LAC43 STA sector ;set as sector number RTS .LAC46 ;Set number of sectors per track BIT denflg ;if density setting is automatic BMI LAC55 ;then ensure disc is formatted LDA #sdspt ;else set 10 sectors per track BVC LAC51 ;unless disc is double density LDA #ddspt ;in which case set 18 sectors per track .LAC51 STA sectrk .LAC54 RTS .LAC55 ;Ensure disc is formatted LDA #$00 ;read one ID without waiting for index STA sector JSR LB95F ;read ID and detect density BEQ LAC54 ;if record found then exit .LBCE3 ;else raise "Disk not formatted" error. JSR estrng EQUB $C5 EQUS "Disk not formatted" EQUB $00 .LAC5D ;Detect track stepping LDA #$00 ;[D]b7=0 manual, b6=0 1:1 stepping STA t40flg ;set stepping flag LDA #$02 ;track number = 2 STA track JSR LB9B2 ;execute Read Address command [D]no seek LDX rdide-LBDBA+intnmi+$00 ;get C cylinder number LDA #$C0 DEX ;is it 1? BEQ LAC7C ;then disc is 40 track, set double stepping ASL A DEX ;else is it 2? BEQ LAC7C ;then 1:1 stepping is correct DEX ;else the format is wrong, raise an error DEX ;is the head over logical track 4? BEQ LACAD ;[D]if so then raise "80 in 40" error JMP dskflt ;[D]else raise "Disk fault" error. .LAC7C STA t40flg ;set stepping flag LDA #$00 ;[D]track number = 0 STA track ;[D]no recalibration command here RTS .LAC84 ;Raise "Volume . n/a" error JSR estrng ;begin error message "Volume " EQUB $CD EQUS "Volume " LDA sector ;transfer sector offset to A LSR A ;divide by 2; A=0..7, C=0 ADC #$41 ;convert to ASCII character "A".."H" JSR pchr ;print character in A (to error message) JSR vstrng ;print " n/a" and raise error EQUS " n/a" ;[D]short for "not allocated" EQUB $00 .LACAD JSR estrng EQUB $CD EQUS "80 in 40" EQUB $00 .LACBA ;Load disc catalogue L3 LDA #$80 ;data transfer call $80 = read data to JIM EQUB $AE ;$AE=LDA abs; skip next two bytes .LACBD ;Write disc catalogue L3 LDA #$81 ;data transfer call $81 = write data from JIM JSR pgmain ;page in main workspace STA txcall ;set data transfer call number JSR L9753 ;forget catalogue in JIM pages 2..3 LDX #l3tris ;x = 3 number of attempts allowed: .LACC7 JSR L96A5 ;set data pointer to $0200 LDA #$10 ;set sector number = 16 STA sector LDA #$00 ;set track number = 0 STA track STA rtxszl ;$0100 = 256 bytes to transfer LDA #$01 STA rtxszh JSR LBA18 ;transfer data to disc L2 BEQ LACE3 ;[D]if command succeeded then exit DEX ;else decrement attempts remaining BNE LACC7 ;if not run out then try again .LACE0 .dskflt ;Raise "Disk fault" error JSR dskmsg ;begin error message with "Disk fault " EQUB $C5 EQUS "fault " LDA fdstat JSR bytout ;print hex byte JSR vstrng ;print " at Trk " EQUS " at Trk " LDA track ;get track number JSR bytout ;print hex byte JSR vstrng ;print ", Sct " EQUS ", Sct " LDA sector ;get sector number JSR bytout ;print hex byte JMP LA8F8 ;terminate error message, raise error .chkget ;Transfer data and report errors L4 JSR LACF0 ;transfer data L3 [D]AXY not saved STA fdstat ;store result of transfer BNE LACE0 ;if result >0 then "Disk fault" LDA #$01 ;else return A=$01 from OSFILE .LACE3 RTS .LBFA8 ;ChADFS ROM call 1 LDA #$00 STA voltrk ;first track of volume = 0, no track offset LDA denflg ;get *OPT 6 density setting ORA #$40 ;set b6=1, double density STA denflg ;update *OPT 6 density setting (preserve auto) JSR L8AE4 ;prepare extended file transfer: .LACF0 ;Transfer data L3 JSR savit ;save XY LDA #$80 STA dopint ;>0 disc operation is interruptible LDY #l3tris ;set attempt counter to 3 LDA txsizh ;test MSB of transfer size BEQ LACF9 ;if less than 64 KiB then use main routine, else: .bigram ;transfer large file to RAM disc LDA sectrk ;get number of sectors per track BNE LACF9 ;if >0 then floppy disc, use main routine STA rtxszl ;else A=0; set transfer size = 64 KiB STA rtxszh JSR LBA18 ;transfer data L2, RAM transfers never fail INC txlbah ;advance next transfer location by 256 pages DEC txsizh ;subtract 64 KiB from transfer size BNE bigram ;if >= 64 KiB more to do then loop, else: .LACF9 LDA sectrk ;get number of sectors per track PHP ;save Z flag LDX txsizl ;set X=LSB byte count LDA txsizm ;set A=2MSB byte count PLP ;restore Z flag BEQ LAD1C ;if 0 sectors per track then RAM disc, branch SEC ;else subtract LDA sectrk ;number of sectors per track SBC sector ;- starting sector STA sectog ;= sectors until end, store temp LDA txsizh ;test MSB byte count BNE LAD18 ;if >=64 KiB then transfer rest of track ;else X=LSB byte count LDA txsizm ;set A=2MSB byte count CMP sectog ;if transfer ends before end of track BCC LAD1C ;then only transfer byte count, else: .LAD18 ;transfer rest of track LDX #$00 ;X=0 byte count is a multiple of 256 LDA sectog ;A=number of sectors (not bytes) to transfer: .LAD1C STX rtxszl ;store LSB byte count STA rtxszh ;store MSB byte count ORA rtxszl ;test if byte count > 0 BEQ nmirel ;if no data to transfer then finish LDA sectrk ;[D]else test number of sectors per track BEQ LAD35 ;if 0 sectors per track then RAM disc, branch SEC ;else subtract LDA track ;track number SBC #badtrk ;- 80 BCC LAD35 ;if track number in range 0..79 then proceed STA track ;else set new track number 80 less JSR LAD9B ;select side 2 of current drive. .LAD35 JSR LBA18 ;transfer data L2 BNE LAD6C ;if non-zero status then try again INC track ;else increment track STA sector ;next transfer starts at sector 0 LDX rtxszh ;x = ?$A1 = MSB number of bytes transferred LDA rtxszl ;a = ?$A0 = LSB number of bytes transferred BIT txcall ;test data transfer call number BPL LAD4A ;if b7=1, transferring to JIM TXA ;then a = ?$A1 = number of pages LDX #$00 ;x = 0, less than 64 KiB transferred .LAD4A CLC ;add expected transfer size to xfer. address ADC nmiusr+$00 ;(byte address in CPU space, STA nmiusr+$00 ;page address in JIM space) TXA ADC nmiusr+$01 STA nmiusr+$01 SEC ;subtract expected transfer size LDA txsizl ;from 24-bit byte count SBC rtxszl STA txsizl LDA txsizm SBC rtxszh STA txsizm BCS LAD65 DEC txsizh .LAD65 ORA txsizl ;test remaining no. bytes to transfer ORA txsizh ;if no more data to transfer then finish BEQ nmirel ;by releasing NMI; else attempt counter >0 INY ;preserve and loop to transfer rest of file. .LAD6C DEY ;decrement attempt counter BNE LACF9 ;if not tried 3 times then try again TAY ;else Y=A=status>0, return Z=0 RTS .LAFF8 ;Initialise RAM disc catalogue LDA LB015,X ;look up LSB of selected volume size STA prodl LDA LB019,X ;and MSB STA prodh JSR nmicla ;claim NMI LDA #$00 STA ramden ;b6=0 RAM disc is single density STA denflg ;*OPT 6,10 single density [BUG]cancels auto! LDY #$00 ;write catalogue to sector 0 JSR LB062 ;initialise volume catalogue: .nmirel ;Release NMI BIT nmiflg ;if NMI is not already ours BPL LAD82 ;then exit LDY prenmi ;else Y = ID of previous NMI owner CPY #$FF ;if Y=$FF no previous owner BEQ LAD82 ;then skip release call [BUG]improper! LDX #$0B ;else service call $0B = NMI release JSR doswcl ;call OSBYTE $8F = issue service call .LAD82 LDA #$00 STA nmiflg ;$00 = NMI not ours RTS ;fake WD1770 status = 0, succeeded. .nmicla ;Claim NMI BIT nmiflg ;if NMI is already ours BMI nmicl0 ;then exit LDA #$8F ;else OSBYTE $8F = issue service call LDX #$0C ;service call $0C = claim NMI JSR readit ;call OSBYTE with Y=$FF STY prenmi ;save ID of previous NMI owner LDA #$FF ;set NMI ownership flag STA nmiflg ;b7=1 iff we own the NMI .nmicl0 RTS .LAD9B ;Select side 2 of current drive JSR LAAD9 ;map current drive to physical drive TAX ;transfer to X for use as index JSR pgaux ;page in auxiliary workspace LDA LADB7,X ;look up side 2 of physical drive LDX #$07 ;loop for logical drives 7..0: .LADA7 CMP config,X ;does this drive map to the drive we want? BEQ LADB2 ;if so then set current logical drive DEX ;else try next logical drive BPL LADA7 ;loop until all 8 logical drives tested JMP drverr ;if physical drive not mapped "Bad drive" .LADB2 STX fdrive ;else set current drive = logical drive JMP pgmain ;page in main workspace and exit .clrkey ;Flush input buffer JSR savita ;save AXY LDA #$0F ;OSBYTE $0F = flush selected buffer class LDX #$01 ;x = $01 flush input buffer only BNE writby ;call OSBYTE with Y=$00 .LADD1 ;Call OSBYTE with X=$00, Y=$00 LDX #$00 .writby ;Call OSBYTE with Y=$00 LDY #$00 BEQ bytjmp .wriwde TAX ;Call OSBYTE $03 = specify output stream in A .wriwdx LDA #$03 ;Call OSBYTE $03 = specify output stream BNE bytjmp .LADDC ;Call OSBYTE $EC = read/write char dest status LDA #$EC BNE readby .LADE4 ;Call OSBYTE $EA = read Tube presence flag LDA #$EA BNE readby .LADE8 ;Call OSBYTE $A8 = get ext. vector table addr LDA #$A8 BNE readby .doswcl ;Call OSBYTE $8F = issue service call LDA #$8F BNE bytjmp .readsw ;Call OSBYTE $FF = read/write startup options LDA #$FF .readby ;Call OSBYTE with X=$00, Y=$FF LDX #$00 .readit ;Call OSBYTE with Y=$FF LDY #$FF .bytjmp ;Call OSBYTE JMP osbyte ;Side 2 of physical drives 0..4 .LADB7 EQUB $02 EQUB $03 EQUB $FF ;invalid, raise "Bad drive" error EQUB $FF ;invalid, raise "Bad drive" error EQUB $05 ;next three bytes more than 7, invalid physical drives ;[BUG]if auxiliary workspace is not initialised the map bytes can be $FF ;Table of addresses of extended vector handlers .vtabb EQUW $FF1B ;FILEV, $0212 = $FF1B EQUW $FF1E ;ARGSV, $0214 = $FF1E EQUW $FF21 ;BGETV, $0216 = $FF21 EQUW $FF24 ;BPUTV, $0218 = $FF24 EQUW $FF27 ;GBPBV, $021A = $FF27 EQUW $FF2A ;FINDV, $021C = $FF2A EQUW $FF2D ;FSCV, $021E = $FF2D ;Table of action addresses for extended vector table .vtabf EQUW wfile ;E FILEV, evt + $1B = $A16E EQUW wargs ;E ARGSV, evt + $1E = $9B62 EQUW wbget ;E BGETV, evt + $21 = $9CD1 EQUW wbput ;E BPUTV, evt + $24 = $9D9E EQUW wbgpb ;E GBPBV, evt + $27 = $A2DC EQUW wfind ;E FINDV, evt + $2A = $9961 EQUW wfscm ;E FSCV, evt + $2D = $975F ;Table of action addresses for FSC calls 0..11, low bytes .fscltb EQUB <(wfopt -$01) ;FSC 0 = *OPT $9775 EQUB <(wfeof -$01) ;FSC 1 = read EOF state $9808 EQUB <(wnota -$01) ;FSC 2 = */ $9820 EQUB <(wname -$01) ;FSC 3 = unrecognised *cmd $989C EQUB <(wnota -$01) ;FSC 4 = *RUN $9820 EQUB <(wdcat -$01) ;FSC 5 = *CAT $98AE EQUB <(wfdie -$01) ;FSC 6 = new FS starting up $9905 EQUB <(whlim -$01) ;FSC 7 = valid file handles $9917 EQUB <(wstus -$01) ;FSC 8 = *command entered $991C EQUB <(ex -$01) ;FSC 9 = *EX $8BFC EQUB <(info -$01) ;FSC 10 = *INFO $8C10 EQUB <(wnota -$01) ;FSC 11 = *RUN from library $9820 ;Table of action addresses for FSC calls 0..11, high bytes .fschtb EQUB >(wfopt -$01) EQUB >(wfeof -$01) EQUB >(wnota -$01) EQUB >(wname -$01) EQUB >(wnota -$01) EQUB >(wdcat -$01) EQUB >(wfdie -$01) EQUB >(whlim -$01) EQUB >(wstus -$01) EQUB >(ex -$01) EQUB >(info -$01) EQUB >(wnota -$01) ;Table of action addresses for OSARGS calls A=$FF,0,1, Y=0, low bytes .LAE2D EQUB <(ensur -$01) ;OSARGS $FF = ensure all files $9BA2 EQUB <(rdfsno-$01) ;OSARGS 0 = return FS number $9B8C EQUB <(L9B8F -$01) ;OSARGS 1 = command line tail $9B8F ;Table of action addresses for OSARGS calls A=$FF,0,1, Y=0, high bytes .LAE30 EQUB >(ensur -$01) EQUB >(rdfsno-$01) EQUB >(L9B8F -$01) ;Table of action addresses for OSFILE calls $FF,0..6, low bytes .filjpl EQUB <(loader-$01) ;OSFILE $FF = load file $A1EF EQUB <(saver -$01) ;OSFILE 0 = save file $A1A0 EQUB <(fwrcat-$01) ;OSFILE 1 = wr. catalog info $A1B0 EQUB <(fwrlod-$01) ;OSFILE 2 = wr. load address $A1BB EQUB <(fwrexe-$01) ;OSFILE 3 = wr. exec address $A1C3 EQUB <(fwratt-$01) ;OSFILE 4 = wr. attributes $A1CB EQUB <(frdcat-$01) ;OSFILE 5 = read catalog info $A1DA EQUB <(fdefil-$01) ;OSFILE 6 = delete file $A1E3 EQUB <(maker -$01) ;OSFILE 7 = create file EQUB <(return-$01) ;OSFILE 8 = create directory EQUB <(fwrlod-$01) ;OSFILE 9 = wr. timestamp EQUB <(saver -$01) ;OSFILE $0A = save w/timestamp EQUB <(maker -$01) ;OSFILE $0B = creat.w/timestamp ;Table of action addresses for OSFILE calls $FF,0..6, high bytes .filjph EQUB >(loader-$01) EQUB >(saver -$01) EQUB >(fwrcat-$01) EQUB >(fwrlod-$01) EQUB >(fwrexe-$01) EQUB >(fwratt-$01) EQUB >(frdcat-$01) EQUB >(fdefil-$01) EQUB >(maker -$01) EQUB >(return-$01) EQUB >(fwrlod-$01) EQUB >(saver -$01) EQUB >(maker -$01) ;Table of action addresses for OSGBPB calls 0..8, low bytes .wgptbl EQUB <LA39B ;OSGBPB 0 = no operation $A39B EQUB <wbptr ;OSGBPB 1 = set PTR and write $A39C EQUB <wbptr ;OSGBPB 2 = write data $A39C EQUB <wbgtr ;OSGBPB 3 = set PTR and read $A3A4 EQUB <wbgtr ;OSGBPB 4 = read data $A3A4 EQUB <rdtco ;OSGBPB 5 = read title/opt/drv $A3AC EQUB <rdbir ;OSGBPB 6 = read CSD drv/dir $A3E1 EQUB <rlbir ;OSGBPB 7 = read lib'y drv/dir $A3F0 EQUB <wcbat ;OSGBPB 8 = read CSD filenames $A3FF ;Table of action addresses for OSGBPB calls 0..8, high bytes .wgptbh EQUB >LA39B EQUB >wbptr EQUB >wbptr EQUB >wbgtr EQUB >wbgtr EQUB >rdtco EQUB >rdbir EQUB >rlbir EQUB >wcbat ;Table of microcode bytes for OSGBPB calls 0..8 .wbrwtb EQUB $04 ;%000001 0 . to memory, special handler EQUB $02 ;%000000 1 0 from memory, xfer data, set PTR EQUB $03 ;%000000 1 1 from memory, xfer data, leave PTR EQUB $06 ;%000001 1 0 to memory, xfer data, set PTR EQUB $07 ;%000001 1 1 to memory, xfer data, leave PTR EQUB $04 ;%000001 0 . to memory, special handler EQUB $04 ;%000001 0 . to memory, special handler EQUB $04 ;%000001 0 . to memory, special handler EQUB $04 ;%000001 0 . to memory, special handler .LAE5E ;Print Challenger banner PHP ;save C on entry JSR vstrng ;print "CHALLENGER " EQUS "CHALLENGER " NOP PLP ;restore C BCS LAE7A ;if C=0 on entry JSR vstrng ;then print version number "200B " EQUS "2.10 " NOP .LAE7A JSR L8219 ;get Challenger unit type AND #$03 ;extract bits 1,0 ORA #$04 ;add 4 to make 4..7 TAX ;transfer to X to select message JSR L8FF7 ;print boot or Challenger config descriptor JMP pcrlf ;print newline and exit .tmope0 JMP tmopen ;raise "Too many files open" error .LAE88 ;*FORMAT LDA dcbmap ;save channel open flags PHA AND #$18 ;if channels $14 or $15 open BNE tmope0 ;then CHRN table will destroy their workspaces TYA ;else save offset of command line tail LDY #$60 ;point to workspace of channel $13 JSR vshut ;close file to ensure file up-to-date on disc TAY ;restore offset of command line tail PLA ASL seqflg+$60 ;clear bit 7 of channel $13 flags LSR seqflg+$60 ;buffer does not contain PTR STA seqdah+$60 ;a>=$20; invalidate sector buffer of ch. $13 STA dcbmap ;restore channel open flags JSR setupr ;call GSINIT with C=0 BEQ LAEA4 ;if no argument then skip .LAE8D JSR gsread ;[D]else call GSREAD BCC LAE96 ;type character of argument if present LDA #$0D ;else end of argument, type RETURN LDY #$00 ;offset = 0, indicate end of argument .LAE96 STY frmclo ;save command line offset TAY ;transfer character of argument to Y LDX #$00 ;x=$00 insert into keyboard buffer LDA #$99 ;OSBYTE $99 = insert char into buffer ck/ESC JSR osbyte ;call OSBYTE LDY frmclo ;restore command line offset BNE LAE8D ;loop until argument inserted in buffer .LAEA4 ;e.g. *FORMAT 4|M2|MF auto-formats RAM disc JSR wopa ;have A=0 returned on exit TSX STX restsp ;set stack pointer to restore on restart STX abrtsp ;set stack pointer to restore on exit JSR L959C ;set high word of buffer address = $FFFF .LAEAF ;command restart point set at $AEE5 JSR LB01D ;set command restart to exit command JSR LB2B2 ;print "FORMAT" heading .LAEB5 JSR LB2DE ;clear row 23 JSR LA917 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,19) EQUB $00 EQUB $13 EQUS "Drive number (0-7) " EQUB $FF JSR LB5CB ;get printable input character SEC ;convert to binary drive no. 0..7 SBC #$30 ;is it less than ASCII "0"? CMP #$08 ;never; is drive number in range? BCC LAEE3 ;if so then proceed JSR LB73C ;[D]else make a short beep JMP LAEB5 ;and input new drive number .LAEE3 STA fdrive ;set as current volume .LAEE5 LDX #<LAEAF ;point XY at *FORMAT entry point, $AEAF LDY #>LAEAF JSR LB021 ;set command restart action address JSR LA917 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,20) EQUB $00 EQUB $14 EQUS "0=40, 1=80 tracks : " EQUB $7F ;backspace and erase 2 characters EQUB $7F EQUB $FF JSR LB74C ;set Z=1 iff current drive is a RAM disc BNE LAF12 ;if so JMP LAFB7 ;then format RAM disc .LAF12 JSR LB5CB ;else get printable input character LDX #badtrk>>1 ;set X = 40 tracks CMP #$30 ;if user typed 0 BEQ LAF21 ;then proceed with 40 track format LDX #badtrk ;else set X = 80 tracks CMP #$31 ;if user typed 1 then format 80 tracks BNE LAEE5 ;else invalid character, prompt again .LAF21 STX ftraks ;set number of tracks to format LDA #badtrk>>1 ;40 tracks maximum can be formatted BIT t40flg ;test double-stepping flag BVC LAF31 ;if 1:1 stepping then maximum = 80 tracks BPL LAF32 ;if *OPT 8,1 then maximum = 40 tracks LDX #$80 ;else *OPT 8,255; keep automatic on STX t40flg ;force 1:1 stepping .LAF31 ASL A ;and format up to 80 tracks. .LAF32 CMP ftraks ;compare max - chosen number of tracks BCS LAF3F ;[D]if max >= number chosen then proceed JSR LB73C ;[D]else make a short beep JMP LAEE5 ;and input new format size .LAF3F JSR LB2DE ;clear row 23 .LAF42 JSR LA917 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,21) EQUB $00 EQUB $15 EQUS "Density (S/D) " EQUS " " EQUS $7F ;backspace and delete EQUB $FF JSR LB5CB ;get printable input character CMP #$53 ;is it capital S? BEQ LAF96 ;if so then format single density CMP #$44 ;else is it capital D? BEQ LAF68 ;if so then format double density JSR LB73C ;[D]else make a short beep JMP LAF42 ;and re-input .LAF68 ;Format double density LDA denflg ;set double density ORA #$40 ;preserve automatic setting bit 7 STA denflg ;set double density bit 6 LDA #ddspt ;set 18 sectors per track STA sectrk JSR LB65C ;prompt user and start format BCS LAF8E ;if failed then prompt to repeat LDX ftraks ;else get number of tracks on disc DEX ;all but one track available for volumes STX muland ;set multiplicand JSR LB552 ;multiply by no. sectors per track JSR LB412 ;set default volume sizes JSR LB45E ;write volume catalogues JSR LB477 ;generate disc catalogue JSR LACBD ;write disc catalogue L3 .LAF8E JSR LB035 ;prompt to repeat BEQ LAF68 ;if user chooses repeat then format another BNE LB028 ;else exit command .LAF96 ;Format single density LDA denflg ;set single density AND #$80 ;preserve automatic setting bit 7 STA denflg ;clear double density bit 6 LDA #sdspt ;set 10 sectors per track STA sectrk JSR LB65C ;prompt user and start format BCS LAFAF ;if failed then prompt to repeat LDX ftraks ;else get number of tracks on disc STX muland ;set multiplicand JSR LB05B ;initialise volume catalogue by no. tracks .LAFAF JSR LB035 ;prompt to repeat BEQ LAF96 ;if user chooses repeat then format another BNE LB028 ;else exit command .LAFB7 JSR LA917 ;print VDU sequence immediate EQUB $7F ;delete " :" EQUB $7F EQUS ", 2=Max RAM disk " EQUB $FF JSR LB5CB ;get printable input character SEC ;convert to binary option 0..2 SBC #$30 CMP #$03 ;is option in range? BCS LAFF2 ;if not 0..2 then prompt again PHA ;else save option JSR LB61B ;prompt to start format PLA ;restore option TAX CPX #$02 ;if 40 or 80 track format selected BNE LAFEC ;then format to that size JSR LAAD9 ;else map current volume to physical volume CMP #$05 ;if formatting drive 4 BNE LAFEC ;then use option 2, size = $3F5 sectors INX ;else use option 3, size = $3FF sectors .LAFEC JSR LAFF8 ;initialise RAM disc catalogue .LB028 ;Exit command LDX abrtsp ;restore stack pointer from workspace TXS JSR LB650 ;clear rows 20..22 LDX #$00 ;set XY to screen coordinates (0,24) LDY #$18 JMP LB2D1 ;move cursor to (X,Y) .LAFF2 JSR LB73C ;make a short beep JMP LAEE5 ;and input new volume size option ;Table of single density volume sizes, X=0..3, low bytes .LB015 EQUB <(sdspt*(badtrk>>1)) ;x=0, 40 track disc, $0190 sectors, 100 KiB EQUB <(sdspt*badtrk) ;x=1, 80 track disc, $0320 sectors, 200 KiB EQUB <d4size ;x=2, RAM disc 4, $03F5 sectors, 253 KiB EQUB <d5size ;x=3, RAM disc 5, $03FF sectors, 255 KiB ;Table of single density volume sizes, X=0..3, high bytes .LB019 EQUB >(sdspt*(badtrk>>1)) EQUB >(sdspt*badtrk) EQUB >d4size EQUB >d5size .LB01D ;Set command restart to exit command LDX #<LB028 ;point XY at command exit routine, $B028 LDY #>LB028 .LB021 ;Set command restart action address STX restrt+$00 STY restrt+$01 RTS .LB035 ;Prompt to repeat JSR LA917 ;print VDU sequence immediate EQUB $1F ;move cursor to (8,16) EQUB $08 EQUB $10 EQUS "Format complete" EQUB $0D ;newline EQUB $0A EQUS "Repeat? " EQUB $FF JSR LB5EE ;get input character and acknowledge ESCAPE CMP #$59 ;return Z=1 iff user typed capital Y RTS .verify ;*VERIFY JSR LB741 ;parse floppy volume spec from argument TSX STX abrtsp ;set stack pointer to restore on exit STX restsp ;set stack pointer to restore on restart JSR wopa ;have A=0 returned on exit JSR L959C ;set high word of OSFILE load address = $FFFF .LB08C ;command restart point set at $B0B6 JSR LB01D ;set command restart to exit command JSR LB758 ;set display MODE 7 and place heading JSR LA917 ;print VDU sequence immediate EQUS "V E R I F Y" EQUB $1F ;move cursor to (0,10) EQUB $00 EQUB $10 EQUS "Insert disk" EQUB $FF JSR LB706 ;prompt for keypress LDX #<LB08C ;point XY at *VERIFY entry point, $B08C LDY #>LB08C JSR LB021 ;set command restart action address LDA #$00 STA txcall ;data transfer call $00 = read data LDA #$80 STA dopint ;>0 disc operation is interruptible JSR LABB5 ;detect disc format/set sector address BIT denflg ;test density flag BVS LB0F4 ;if double density then examine disc catalog JSR dirldy ;else load volume catalogue JSR pgcat1 ;page in catalogue sector 1 LDA dirhig+$06 ;get boot option/top bits volume size AND #$03 ;extract top bits volume size STA divenh ;store MSB of dividend LDA dirhig+$07 ;get LSB volume size STA divenl ;store LSB of dividend JSR pgmain ;page in main workspace LDA #$00 STA divorh ;clear MSB of divisor LDA sectrk ;get number of sectors per track (= 10) STA divorl ;store LSB of divisor JSR LB35F ;divide word by word STX ftraks ;store quotient as number of tracks JMP LB0FF ;verify disc .LB0F4 JSR LACBA ;load disc catalogue L3 JSR pgcat0 ;page in catalogue sector 0 LDA dctrks ;get number of tracks on disc STA ftraks ;store number of tracks to verify .LB0FF ;Verify disc JSR pgmain ;page in main workspace LDA #$00 STA track ;set starting track = 0 CLC ;[D]set C=0, no error PHP .LB108 JSR LB6B3 ;print track number in table JSR LB121 ;verify track with display BEQ LB113 ;if hard error occurred PLP ;[D]then set C=1, verify failed SEC PHP .LB113 INC track ;increment track number LDA track ;compare track number - number of tracks CMP ftraks BCC LB108 ;if less then verify next track PLP ;[D]else test return code BCS LB13A ;if error occurred print "ERROR" BCC verfin ;[D]else exit command. .LB121 ;Verify track with display LDX #l3tris ;[D]make 3 attempts LDY #$03 ;[D]erase next 3 characters JSR LB72C ;erase Y characters ahead of cursor .LB128 JSR LB2EA ;poll for ESCAPE JSR LBA05 ;verify track BEQ LB139 ;if verify succeeded then exit LDA #$2E ;else print a dot JSR oswrch ;call OSWRCH DEX ;decrement attempt counter BNE LB128 ;if attempts remaining then try again DEX ;else X=$FF, Z=0 to indicate failure .LB139 RTS .LB13A JSR LB60B ;print "ERROR" .verfin JMP LB028 ;and exit command. .LB140 ;*VOLGEN JSR LB741 ;[D]parse floppy volume spec from argument JSR wopa ;have A=0 returned on exit TSX STX abrtsp ;set stack pointer to restore on exit JSR chkena ;ensure *ENABLE active JSR L959C ;set high word of OSFILE load address = $FFFF LDA #$80 STA dopint ;>0 disc operation is interruptible LDA #$00 STA voltrk ;data area starts on track 0 STA track ;[D]set track number = 0 JSR LB916 ;[D]seek logical track JSR LB279 ;ensure disc is double density LDA fdrive ;get current volume AND #$0F ;extract physical drive number, clear b7..4 STA fdrive ;set current volume letter to A JSR LB758 ;[D]set display MODE 7 and place heading JSR LA917 ;print VDU sequence immediate EQUS "V O L G E N" EQUB $FF JSR LA917 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,4) EQUB $00 EQUB $04 EQUB $0D EQUS "Vol Size (K) " EQUB $FF LDA fdrive ;get current volume JSR L8EAD ;print " Drive " plus volume spec in A JSR LA917 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,15) EQUB $00 EQUB $0F EQUS "Free" EQUB $FF JSR LB4D2 ;read volume sizes and allocations LDA #volums-$01 ;8 volumes to list STA volidx ;set as counter .LB1A7 JSR LB3BC ;print tabulated volume size DEC volidx ;loop until 8 volumes listed BPL LB1A7 .LB1AE JSR LB01D ;set command restart to exit command .LB1B1 JSR LB56D ;sum volume sizes LDX #$05 ;move cursor to (5,15) LDY #$0F JSR LB2D1 JSR LB380 ;print sector count as kilobytes JMP LB1C4 ;and prompt for command. .LB1C1 JSR LB73C ;make a short beep .LB1C4 JSR LA917 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,23) EQUB $00 EQUB $17 EQUS "VOLUME : (W to configure)" EQUB $1F ;move cursor to (8,23) EQUB $08 EQUB $17 EQUB $FF JSR LB5EE ;get input character and acknowledge ESCAPE CMP #$57 ;[D]if user typed capital W BNE LB1F6 .LB255 ;Generate volumes LDX #<LB1AE ;point XY at *VOLGEN entry point, $B1AE LDY #>LB1AE JSR LB021 ;set command restart action address JSR LB706 ;prompt for keypress JSR LB279 ;ensure disc is double density JSR LB6D3 ;ensure disc is write enabled BNE LB1AE ;if write protected then try again BEQ LB26A ;if write enabled then proceed .LB1F6 SEC ;else convert letter A..H to volume index 0..7 SBC #$41 CMP #volums BCS LB1C1 ;if out of range then display error STA volidx ;else set volume index ADC #$41 ;convert back to letter A..H, print it JSR oswrch ;call OSWRCH LDA #$20 ;print a space JSR oswrch ;call OSWRCH JSR LB305 ;input number up to 3 digits BCS LB1C4 ;[D]if invalid input then prompt again LDA inputl ;else test entered volume size ORA inputh BNE LB223 ;if >0 then set volume size LDA volidx ;else RETURN pressed, delete volume BEQ LB1C1 ;[D]volume A can't be deleted, prompt again JSR LB2A2 ;else clear volume size JSR LB3BC ;print tabulated volume size JMP LB1B1 ;update free space and take next command. .LB223 ;Fit volume request LDA inputh ;test MSB of entered number CMP #$04 ;if <256 KiB requested then continue BCS LB1C1 ;else display error and prompt again JSR LB2A2 ;clear volume size JSR LB56D ;sum volume sizes LDA linnol ;compare free space - request CMP inputl LDA linnoh SBC inputh BCS LB241 ;if request fits then assign request LDA linnol ;else set request = free space on disc STA inputl LDA linnoh STA inputh .LB241 LDA volidx ;get volume index ASL A ;double it TAY ;transfer to Y as index LDA inputh ;set assigned volume size STA volszh,Y ;= min(request, free_space) LDA inputl STA volszl,Y JSR LB3BC ;print tabulated volume size JMP LB1B1 ;update free space display and take input. .LB26A JSR LB3EA ;clear catalogue sectors JSR LB45E ;write volume catalogues JSR LB477 ;generate disc catalogue JSR LACBD ;write disc catalogue L3 JMP LB028 ;exit command .LB279 ;Ensure disc is double density JSR LAC55 ;ensure disc is formatted BIT denflg ;test density flag BVS LB2A1 ;if single density JSR estrng ;then raise "must be double density" error. EQUB $C9 EQUS "Disk must be double density" EQUB $00 .LB2A1 RTS .LB2A2 ;Clear volume size LDA volidx ;get volume index ASL A ;double to get offset TAY JSR pgaux ;page in auxiliary workspace LDA #$00 STA volszh,Y ;set size of selected volume = 0 STA volszl,Y RTS .LB2B2 ;Print "FORMAT" heading JSR LB758 ;set display MODE 7 and place heading JSR LA917 ;print VDU sequence immediate EQUS "F O R M A T" EQUB $FF .LB2C4 RTS .LB2C5 ;Set display MODE 7 LDA #$07 PHA LDA #$16 JSR oswrch PLA JMP oswrch .LB2FA ;Move cursor to table row in $C1 LDX #$01 ;[D](1,6+?$C1) vs (6,9+?$C1) in DDOS CLC LDA volidx ADC #$06 TAY .LB2D1 ;Move cursor to (X,Y) LDA #$1F ;issue VDU 31 = PRINT TAB(X,Y) JSR oswrch TXA ;send X coordinate to OSWRCH, 0=leftmost col JSR oswrch TYA ;send Y coordinate to OSWRCH, 0=top row JMP oswrch .LB2EA ;Poll for ESCAPE BIT escflg ;[D]if ESCAPE was pressed BPL LB2C4 JSR ackesc ;then acknowledge ESCAPE condition JSR nmirel ;release NMI LDX restsp ;restore stack pointer from $B7 TXS JMP (restrt) ;and restart command .LB305 ;Input hex number up to 3 digits LDY #$00 ;start with no characters in line buffer STY inputl ;clear accumulator STY inputh ;[D]accepted decimals up to 5 digits in DDOS .LB30B JSR LB5EE ;get input character and acknowledge ESCAPE CMP #$0D ;if user pressed RETURN BNE LB314 CLC ;then return C=0 RTS .LB314 CMP #$7F ;else if user pressed DELETE BNE LB32D TYA ;then test number of characters entered BNE LB31D ;if no characters on line SEC ;then return C=1 RTS .LB31D JSR LB348 ;else backspace and erase last character DEY ;decrement number of characters entered LDX #$04 ;[D]else 4 places to shift by: .LB323 LSR inputh ;[D]shift MSB of accumulator right ROR inputl ;shift old b0 into LSB of accumulator DEX ;loop until 4 bits shifted BNE LB323 ;removing last digit entered BEQ LB30B ;and loop (always) .LB32D CPY #$03 ;[D]if 3 characters already entered BEQ LB30B ;then ignore latest, loop to read DEL/CR JSR LA9E1 ;[D]else convert ASCII hex digit to binary JSR digout ;print hex nibble [BUG]no validation! LDX #$04 ;4 places to shift by: .LB339 ASL inputl ;shift LSB of accumulator left ROL inputh ;shift old b7 into MSB of accumulator DEX ;loop until 4 bits shifted BNE LB339 ;now b3..0 of $AA = $0 ORA inputl ;apply LSB to nibble typed by user STA inputl ;update LSB of accumulator INY ;increment number of digits typed JMP LB30B ;loop to input more digits .LB348 ;Backspace and erase characters ;[BUG]can type e.g. AvF (A = 255K) in *VOLGEN JSR LB350 ;print DEL LDA #$20 ;print space: JSR oswrch .LB350 ;Print DEL LDA #$7F ;set A = ASCII value of DEL character JMP oswrch ;call OSWRCH to print it and exit. .LB35F ;Divide word by word LDX #$00 ;initialise quotient = 0: .LB361 LDA divenh ;Compare dividend - divisor CMP divorh BCC LB37F BNE LB36F LDA divenl CMP divorl BCC LB37F ;if dividend >= divisor .LB36F LDA divenl ;then subtract dividend - divisor SBC divorl STA divenl ;ultimately leaving remainder LDA divenh SBC divorh STA divenh INX ;increment quotient in X JMP LB361 ;and loop as remainder >= 0 .LB37F RTS .LB380 ;Print sector count as kilobytes JSR LA7E9 ;[D]print sector count LDY #$02 ;print 2 spaces JSR yspace LSR linnoh ;divide sector count by 4 to get kilobytes ROR linnol LSR linnoh ROR linnol LDA linnol JSR LB39D ;convert byte to three decimal digits JSR LA7E9 ;print space-padded hex word LDA #$4B ;print "K" JMP oswrch .LB39D ;Convert byte to three decimal digits SEC ;[D]versus word to four in DDOS LDX #$FF STX linnoh .LB3A2 INC linnoh SBC #$64 BCS LB3A2 ADC #$64 .LB3AA INX SBC #$0A BCS LB3AA ADC #$0A STA linnol TXA JSR lfour ORA linnol STA linnol RTS .LB3BC ;Print tabulated volume size JSR LB2FA ;move cursor to table row in $C1 CLC LDA volidx ;get volume letter ADC #$41 ;convert to ASCII letter A..H JSR oswrch ;call OSWRCH LDY #$0D ;erase next 13 characters JSR LB72C ;erase Y characters ahead of cursor LDA volidx ;get volume index ASL A ;double it TAY ;transfer to Y as index JSR pgaux ;page in auxiliary workspace LDA volszh,Y ;get MSB volume size STA linnoh ;store it in zero page LDA volszl,Y ;get LSB volume size STA linnol ;store it in zero page ORA linnoh ;test volume size BEQ LB3E7 ;if =0 then leave row blank JSR pdspc ;else print two spaces JSR LB380 ;print sector count as kilobytes .LB3E7 JMP pgmain ;page in main workspace .LB3EA ;Clear catalogue sectors LDA #$00 TAY JSR pgcat0 ;page in catalogue sector 0 .LB3F0 STA dirlow,Y INY BNE LB3F0 JSR pgcat1 ;page in catalogue sector 1 .LB3F9 STA dirhig,Y INY BNE LB3F9 JMP pgmain ;page in main workspace .LB402 ;Clear volume sizes JSR pgaux ;page in auxiliary workspace LDA #$00 LDY #(volums<<1)-$01 ;8 words to clear for volumes A..H .LB409 STA volszh,Y ;set assigned size of volume to $0000 DEY BPL LB409 ;loop until all words cleared JMP pgmain ;page in main workspace .LB412 ;Set default volume sizes JSR pgmain ;page in main workspace LDA prodl ;set free space = sectors avail. for volumes STA freelo LDA prodh STA freehi LDA sectrk ;get number of sectors per track STA asklo LDA #$00 ;clear MSB of word LDX #$04 ;4 places to shift, multiply by 16: .LB426 ASL asklo ;shift word one place left ROL A DEX ;repeat 4 times BNE LB426 ;max. 16 tracks = 72 KiB per volume STA askhi JSR pgaux ;page in auxiliary workspace LDY #$00 .LB433 JSR LB547 ;compare requested allocation with free space BCC LB440 ;if it fits then set allocation = request LDA freehi ;[D]else set request = free space STA askhi LDA freelo STA asklo .LB440 LDA askhi ;set allocation = request STA volszh,Y LDA asklo STA volszl,Y SEC ;subtract LSB request from free space LDA freelo SBC asklo STA freelo LDA freehi ;subtract MSB request from free space SBC askhi STA freehi ;[D]no disc full test, last volumes = 0 INY ;add 2 to offset, point to next volume INY CPY #volums<<1 ;loop until volumes A..H set. BNE LB433 RTS .LB45E ;Write volume catalogues LDY #$00 ;start at volume A .LB460 JSR pgaux ;page in auxiliary workspace LDA volszh,Y ;copy MSB sector count STA prodh ;[D]to size of volume to be created LDA volszl,Y ;and copy LSB STA prodl JSR LB062 ;[D]initialise volume catalogue INY ;advance volume letter by 1/sector by 2 INY CPY #volums<<1 ;have we initialised 8 volumes/16 sectors? BNE LB460 ;if not then loop to init all volumes RTS .LB477 ;Generate disc catalogue JSR pgcat0 ;page in catalogue sector 0 LDA #$20 ;set version/configuration number = $20 STA dcver ;indicating that sector count is big-endian LDA #ddspt ;18 sectors per track STA dcspt LDY ftraks ;set number of tracks on disc STY dctrks LDA #$00 ;mystery field (MSB no. tracks?), always 0 STA dcmyst JSR LB5AE ;multiply track count by 18 LDA linnol STA dcszlo ;store LSB number of sectors on disc LDA linnoh STA dcszhi ;[D]store MSB LDY #$01 STY gentrk ;data area starts on track 1 DEY .LB4A0 JSR pgaux ;page in auxiliary workspace TYA ;save 2 * volume letter PHA LDA volszh,Y ;get MSB no. sectors in volume's data area STA askhi ;store MSB dividend LDA volszl,Y ;get LSB no. sectors in volume's data area STA asklo ;store LSB dividend ORA askhi ;test number of requested sectors BEQ LB4C9 ;if zero then volume absent, assign no tracks JSR pgcat0 ;page in catalogue sector 0 LDA gentrk ;else set starting track of volume data area STA dcvtrk,Y LDA #$00 ;clear next byte (MSB track number?) STA dcvmst,Y JSR LB597 ;[D]generate track multiple of at least req. CLC TYA ADC gentrk ;add number of tracks in Y to starting track STA gentrk .LB4C9 PLA ;skip to next volume entry TAY INY INY CPY #volums<<1 ;loop until tracks assigned to 8 volumes BNE LB4A0 RTS .LB4D2 ;Read volume sizes and allocations JSR LB52E ;copy volume allocations to workspace JSR pgcat0 ;page in catalogue sector 0 SEC LDA dcszlo ;get LSB number of sectors on disc SBC #ddspt ;subtract 18 sectors of catalogue track STA prodl ;set LSB total sectors allocated to volumes LDA dcszhi ;borrow from MSB SBC #$00 STA prodh LDA dctrks ;get number of tracks on disc STA ftraks JSR LB402 ;clear volume sizes LDY #(volums-$01)<<1 ;start at volume H, cat. sector 14: ;Read volume sizes from the catalogue of each volume. ;This allows the size of each volume to be less than its allocation, ;in particular an allocation of 57 or more tracks may contain a volume ;of the maximum size, 1023 sectors ($3FF). .LB4F1 JSR pgaux ;page in auxiliary workspace TYA ;y=2*volume LSR A ;A=volume TAX ;transfer to X for use as index LDA voltks,X ;look up number of tracks in volume BEQ LB529 ;if volume absent then skip STY sector ;else set sector number = 2*volume INC sector ;add 1, point to 2nd sector of cat. JSR L96A5 ;[D]set data pointer to $0200 LDA #$01 ;256 bytes to transfer STA rtxszh LDA #$00 STA rtxszl LDA #$80 ;data transfer call $80 = read data to JIM STA txcall ;[D]set data transfer call number JSR LBA18 ;transfer data L2 JSR pgcat0 ;page in catalogue sector 0 LDA dirlow+$06 ;sector 1 (not 0) in cat page 0 AND #$03 ;extract MSB volume size PHA LDA dirlow+$07 ;get LSB volume size from catalogue JSR pgaux ;page in auxiliary workspace STA volszl,Y ;set as LSB size of this volume PLA STA volszh,Y ;set as MSB size of this volume .LB529 DEY ;proceed to previous volume DEY ;whose catalogue sector no. is two less BPL LB4F1 ;loop until all eight volumes read RTS .LB52E ;Copy volume allocations to workspace JSR LACBA ;load disc catalogue L3 LDY #(volums-$01)<<1 ;start at sector offset 14, volume H LDX #volums-$01 ;start at workspace offset 7, volume H .LB535 JSR pgcat0 ;page in catalogue sector 0 LDA dcvtrk,Y ;get first track of data area of volume JSR pgaux ;page in auxiliary workspace STA voltks,X ;store in workspace DEY ;skip mystery field in sector DEY ;decrement offset, work back from H to A DEX ;decrement workspace offset BPL LB535 ;loop until 8 track numbers copied RTS .LB547 ;Compare requested allocation with limit LDA askhi CMP freehi BNE LB551 LDA asklo CMP freelo .LB551 RTS .LB552 ;Multiply by no. sectors per track JSR pgmain ;page in main workspace LDY sectrk ;get number of sectors per track LDA #$00 ;clear product STA prodl STA prodh .LB55E CLC ;add number of tracks to product LDA muland ADC prodl STA prodl BCC LB569 ;carry out to high byte INC prodh .LB569 DEY ;loop until all sectors per track added BNE LB55E RTS .LB56D ;Sum volume sizes LDX #$00 ;clear offset = 0, point to volume A STX trktot ;clear total .LB571 JSR pgaux ;page in auxiliary workspace LDA volszl,X ;get LSB requested size of volume at X STA asklo ;set LSB current request LDA volszh,X ;get MSB requested size of volume at X STA askhi ;get MSB current request JSR LB597 ;generate track multiple of at least req. CLC TYA ;a = track count for this volume ADC trktot ;add to total allocations STA trktot INX ;add 2 to offset INX CPX #volums<<1 ;loop until 8 allocations added BNE LB571 SEC ;subtract disc size - total allocations LDA ftraks SBC trktot TAY ;=disc space free DEY ;subtract 1 for catalogue track .LB5AE ;Multiply track count by 18 LDA #$00 STA linnol ;clear LSB sector count STA linnoh ;clear MSB sector count INY ;pre-increment track count to exit on 0: .LB5B5 DEY ;have we added all tracks? BEQ LB5AD ;if so then return sector count JSR LB5BE ;else add 18 sectors to sector count BCC LB5B5 ;and loop (always) .LB597 ;Generate track multiple of at least req. LDY #$00 STY linnol ;clear LSB sector count STY linnoh ;clear MSB sector count .LB59D LDA linnol ;compare sector count - request CMP asklo LDA linnoh SBC askhi BCS LB5AD ;if sector count >= request then return it INY ;else add one track to track count JSR LB5BE ;add 18 sectors to sector count BCC LB59D ;and loop (always) .LB5BE ;Add 18 sectors to sector count CLC LDA linnol ADC #ddspt STA linnol BCC LB5C9 ;carry out to MSB INC linnoh .LB5C9 CLC .LB5AD RTS .LB5CB ;Get printable input character JSR LB5EE ;get input character and acknowledge ESCAPE CMP #$30 ;is ASCII value less than that of "0"? BCC LB5CB ;if so then discard, get another character CMP #$5B ;else is ASCII value higher than "Z"? BCS LB5CB ;if so then discard, get another character PHA ;else save input character JSR oswrch ;call OSWRCH to print it: .LB5DA JSR LB5EE ;get input character and acknowledge ESCAPE CMP #$0D ;is it CR? BNE LB5E3 ;if not then test for DEL PLA ;else restore first character and exit RTS .LB5E3 CMP #$7F ;was DELETE key pressed? BNE LB5DA ;if neither CR or DEL then get another PLA ;else discard first character JSR LB348 ;backspace and erase characters JMP LB5CB ;and loop to get another character. .LB5EE ;Get input character and acknowledge ESCAPE JSR osrdch ;call OSRDCH BCS LB5F4 ;if C=1 then error occurred, test err. code RTS ;else return character in A .LB5F4 CMP #$1B ;test if error code from OSRDCH is $1B BEQ LB5F9 ;if so then ESCAPE was pressed RTS ;else return .LB5F9 JSR pgmain ;page in main workspace JSR ackesc ;acknowledge ESCAPE condition JSR nmirel ;release NMI JSR LB650 ;[D]clear rows 20..22 LDX restsp ;[D]restore stack pointer from $B7 TXS JMP (restrt) ;jump to action address .LB60B ;Print "ERROR" JSR LB650 ;clear rows 20..22 JSR LA917 ;print VDU sequence immediate EQUB $1F ;move cursor to (21,23) EQUB $15 EQUB $17 EQUS "ERROR" ;[D]common routine shared by *FORMAT,*VERIFY EQUB $FF RTS .LB61B ;Prompt to start format JSR LA917 ;print VDU sequence immediate EQUB $1C ;define text window (0,13)..(39,4) EQUB $00 EQUB $0D EQUB $27 EQUB $04 EQUB $0C ;clear text window EQUB $1A ;restore default windows EQUB $FF JSR LB650 ;clear rows 20..22 JSR LA917 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,16) EQUB $00 EQUB $10 EQUS "Press F(ret) to start " EQUB $7F ;backspace and erase character EQUB $FF JSR LB5CB ;get printable input character CMP #$46 ;is it capital F? BNE LB61B ;if not then reprint heading and try again RTS .LB65C ;Prompt user and start format JSR LB61B ;prompt to start format JSR LB6D3 ;ensure disc is write enabled BNE LB65C ;if write protected then try again JSR LB650 ;else clear rows 20..22 LDA #$80 STA dopint ;>0 disc operation is interruptible LDA #$00 STA track ;set track number = 0 STA skewit ;set running track skew counter = 0 JSR LBB18 ;create ID table and format track .LB674 LDA #l3tris ;make three attempts (outer) STA tries ;set attempt counter .LB678 JSR LB2EA ;poll for ESCAPE JSR LB6B3 ;print track number in table LDY #$03 ;erase next 3 characters JSR LB72C ;erase Y characters ahead of cursor JSR LBB18 ;create ID table and format track JSR LB121 ;verify track with display BEQ LB694 ;if succeeded then format next track DEC tries ;else decrement attempt counter BNE LB678 ;if attempts remaining then try again JSR LB60B ;else print "ERROR" SEC ;[D]set C=1, format failed RTS .LB694 LDA #<($100-skew) ;implement track skew BIT denflg ;a=-2 (in two's complement) BVC LB69C ;[D]if double density ASL A ;then A=-4: .LB69C CLC ;subtract 2 or 4 from first R of track ADC skewit BCS LB6A4 ;if it underflows ADC sectrk ;then add number of sectors per track .LB6A4 STA skewit ;set first sector number of track INC track ;increment track number LDA track CMP ftraks ;compare with total tracks BCS LB6B1 ;if >= total tracks then format complete BCC LB674 ;else loop to format next track .LB6B1 CLC ;[D]set C=0, format succeeded. RTS .LB6B3 ;Print track number in table LDX #$00 ;set column to 0 LDY track ;copy track number as row number .LB6B7 SEC TYA SBC #$0A ;[D]subtract 10 from row number BCC LB6C5 ;if underflow then keep current row TAY ;else set as new row number CLC ;add 10 to column TXA ADC #$05 TAX BCC LB6B7 ;and loop until row < 0 .LB6C5 ADC #$0E ;[D]c=0, add 14 to negative remainder TAY ;set Y = row 4..13 JSR LB2D1 ;move cursor to (X,Y) LDA track ;get track number JSR LB39D ;[D]convert byte to three decimal digits JMP LA7F1 ;[D]print space-padded hex byte .LB6D3 ;Ensure disc is write enabled JSR LB905 ;test write protect state of current drive BEQ LB705 ;if write enabled then return JSR LA917 ;else print VDU sequence immediate EQUB $1F ;[D]move cursor to (0,16) EQUB $00 EQUB $10 EQUS "Disk R/O...remove write protect" EQUB $0D EQUB $0A EQUB $FF JSR LB706 ;prompt for keypress LDA #$FF ;return Z=0 [D]omit redundant row erase .LB705 RTS .LB706 ;Prompt for keypress JSR LA917 ;print VDU sequence immediate EQUB $1F ;[D]move cursor to (4,17) EQUB $04 EQUB $11 EQUS "Press any key to continue" EQUB $FF JSR LB5EE ;get input character and acknowledge ESCAPE: .LB650 ;Clear rows 20..22 LDX #$00 ;move cursor to (0,20) LDY #$10 JSR LB2D1 ;move cursor to (X,Y) LDY #$78 ;print 120 spaces and exit BNE yspace .LB2DE ;Clear row 23 LDX #$00 ;move cursor to (0,23) LDY #$17 JSR LB2D1 ;move cursor to (X,Y) LDY #$28 ;[D]set Y = 40, width of one MODE 7 row: .yspace ;Print number of spaces in Y JSR pspace ;print a space DEY ;loop until Y = 0 BNE yspace RTS .LB72C ;Erase Y characters ahead of cursor TYA PHA JSR yspace ;print number of spaces in Y PLA TAY .LB733 LDA #$7F ;print number of DELs in Y JSR oswrch DEY BNE LB733 RTS .LB73C ;Make a short beep LDA #$07 ;BEL = make a short beep JMP oswrch ;call OSWRCH .LB74C ;Set Z=1 iff current drive is a RAM disc JSR LAAD9 ;map current volume to physical volume AND #$07 ;mask drive no in b2..0 mask off volume letter CMP #$04 ;if physical drive = 4 BEQ LB757 ;then return Z=1 CMP #$05 ;else return Z=1 if physical drive = 5. .LB757 RTS .LB758 ;Set display MODE 7 and place heading JSR LB2C5 ;set display MODE 7 LDY #$01 LDX #$0D ;set X=13, Y=1 JMP LB2D1 ;move cursor to (X,Y) .LB76C ;*FDCSTAT JSR LA917 ;print VDU sequence immediate EQUB $0D ;newline EQUB $0A EQUS "WD 1770 status : " EQUB $FF LDA fdstat ;get status of last command .LB786 JSR bytout ;print hex byte JMP pcrlf ;print newline .LB78C ;$13 Read data / $17 Read data and deleted data LDX #$00 EQUB $AD ;$AD=LDA abs; skip next two bytes .LB78F ;$0B Write data LDX #$01 EQUB $AD .LB795 ;$0F Write deleted data LDX #$03 EQUB $AD .LB798 ;$1F Verify data LDX #$04 STX txcall ;set data transfer call number LDA (blkptr),Y ;get 2nd parameter = starting sector number STA sector ;set starting sector JSR LB74C ;[D]set Z=1 iff current drive is a RAM disc BNE LB7B9 ;if so LDX #sdspt ;then convert CS address per Acorn DFS to LBA LDY #$00 ;x = 10 sectors per track, Y = 0 MSB of LBA LDA sector ;begin with LSB of LBA = starting sector: .LB7AC CLC ;add one sector for each track skipped ADC track BCC LB7B2 ;carry out to MSB INY .LB7B2 DEX ;loop until 10 sectors per track added BNE LB7AC ;thereby adding product = no. sectors skipped STA txlbal ;store LSB of LBA STY txlbah ;store MSB of LBA (big-endian) .LB7B9 LDY #$09 LDA (blkptr),Y ;get number of sectors + size code JSR sfive ;shift A right 5 places TAX ;save size code in X LDA #$00 ;set LSB of byte count = 0 STA rtxszl LDA (blkptr),Y ;get number of sectors + size code INY ;increment offset; Y = 10, points to status AND #$1F ;extract number of sectors LSR A ;A,$A0 = 256 x sector count; divide by two ROR rtxszl ;= byte count if X=0, 128-byte sectors BCC LB7D2 ;jump into doubling loop (always) .LB7CF ASL rtxszl ;multiply byte count by two ROL A .LB7D2 DEX ;subtract 1 from X BPL LB7CF ;if X was >0 then double byte count STA rtxszh ;else store high byte of byte count JMP LBA18 ;[D]transfer data L2 and exit .LB7DA ;$29 Seek JSR LB74C ;set Z=1 iff current drive is a RAM disc BEQ LB83B ;if a RAM disc then nothing to do, exit JSR LB916 ;else seek logical track RTS .LB7E3 ;Print number of sectors LDA lbahi ;get MSB size of file or slack space JSR digout ;print hex nibble LDA lbalo ;get LSB size of file or slack space JMP bytout ;print hex byte .LB7ED ;$1B Read ID LDY #$09 ;offset 9 = third parameter LDA (blkptr),Y ;get number of IDs to return BNE LB7F5 ;zero is reserved for internal use LDA #$01 ;in which case return one ID .LB7F5 STA sector ;set number of IDs to return JSR LB74C ;set Z=1 iff current drive is a RAM disc BEQ LB818 ;if so then emulate IDs JSR LB916 ;seek logical track JSR LB95F ;read ID and detect density BNE LB817 ;if command failed then exit PHA ;else save command result = 0 LDA sector ;get number of IDs to return ASL A ;multiply by 4 = number of ID bytes ASL A TAX ;transfer to X to use as counter LDY #$00 ;start at offset 0: .LB80C LDA rdide-LBDBA+intnmi,Y ;get byte of ID read from workspace JSR LA4E0 ;put data byte in user memory INY ;increment offset DEX ;loop until X bytes returned to user BNE LB80C PLA ;restore command result .LB817 RTS .LB818 ;emulate RAM disc sector IDs LDY #$00 ;start at beginning of user memory LDX #$00 ;first sector number = 0 .LB81C ;Create ID table LDA track ;get track number C JSR LA4E0 ;put data byte in user memory INY LDA #$00 ;head number = 0 H JSR LA4E0 ;put data byte in user memory INY TXA ;transfer sector number R JSR LA4E0 ;put data byte in user memory INY LDA #$01 ;size code = 1, 256 b N JSR LA4E0 ;put data byte in user memory INY INX ;increment sector number DEC sector ;loop until required no. sector IDs created BNE LB81C JSR LB996 ;set up drive for single density .LB83B LDA #$00 ;fake WD1770 status = 0, succeeded. RTS .LB83E ;$23 Format track JSR LB74C ;set Z=1 iff current drive is a RAM disc BEQ LB84E ;if RAM then set density of RAM disc INY ;else offset 9 = no. sectors + size code LDA (blkptr),Y AND #$1F ;extract number of sectors STA sectrk ;store number of sectors per track JMP LBB58 ;format track .LB84E LDA denflg ;get density flag AND #$40 ;mask bit 6 = double density STA ramden ;store RAM disc density flag LDA #$00 ;fake WD1770 status = 0, succeeded. RTS .LB859 ;$2C Read drive status DEY ;y = 8 going to 7, offset of result JSR LB905 ;test write protect state LSR A ;returned in bit 6 LSR A ;move to bit 3 = WR PROT LSR A ORA #$44 ;set b6 = RDY 1, b2 = RDY 0 .LB862 RTS ;return result to user's OSWORD $7F block .LB863 ;$35 Specify LDA track ;get first parameter CMP #$0D ;is it $0D = Specify Initialization? BNE LB862 ;if not then exit LDA (blkptr),Y ;else get second parameter = step rate TAX ;(WD1770 format; 0=fast..3=slow; b7..2=0) JMP LB901 ;save as track stepping rate ;returns stepping rate as result ;NB 8271 command result undefined .LB86F ;$3A Write special registers LDA (blkptr),Y ;get second parameter = value to write LDX track ;get first parameter = register address CPX #$05 ;[D]if address in range 0..4 BCS LB87B STA spregs,X ;then set parameter of current drive RTS ;returns it as result ;NB 8271 command result undefined .LB87B LDY #$00 ;[D]else point to unit 0 track position CPX #$12 ;if address = 18 BEQ LB886 ;then set unit 0 position INY ;else point to unit 1 track position CPX #$1A ;if address <> 26 BNE LB8AA ;then exit with result = 0 .LB886 STA head,Y ;else store physical position of head LDA #$00 ;return result = 0, succeeded. RTS ;NB 8271 command result undefined .LB88C ;$3D Read special registers LDX track ;get first parameter = register address CPX #$05 ;if address in range 0..3 BCS LB898 LDA spregs,X ;then return parameter of current drive STA (blkptr),Y ;return to offset 8 of OSWORD control block RTS .LB898 LDA #$00 ;[D]else point to unit 0 track position CPX #$12 ;if address = 18 BEQ LB8A4 ;then return unit 0 position ROL A ;else point to unit 1 track position CPX #$1A ;if address <> 26 BNE LB8AA ;then exit with result = 0 .LB8A4 TAX ;else transfer offset to X LDA head,X ;get physical track number for drive RTS ;return result byte to OSWORD $7F block .LB8AA LDA #$00 RTS ;Table of 8271 floppy drive controller commands with action addresses .LB8AD EQUB $13 ;$13 Read data $B78C EQUW LB78C-$01 EQUB $0B ;$0B Write data $B78F EQUW LB78F-$01 EQUB $29 ;$29 Seek $B7DA EQUW LB7DA-$01 EQUB $1F ;$1F Verify data $B798 EQUW LB798-$01 EQUB $17 ;$17 Read data and deleted data $B78C EQUW LB78C-$01 EQUB $0F ;$0F Write deleted data $B795 EQUW LB795-$01 EQUB $1B ;$1B Read ID $B7ED EQUW LB7ED-$01 EQUB $23 ;$23 Format track $B83E EQUW LB83E-$01 EQUB $2C ;$2C Read drive status $B859 EQUW LB859-$01 EQUB $35 ;$35 Specify $B863 EQUW LB863-$01 EQUB $3A ;$3A Write special registers $B86F EQUW LB86F-$01 EQUB $3D ;$3D Read special registers $B88C EQUW LB88C-$01 EQUB $00 ;terminator byte .LB8D2 ;Set control latch for drive JSR savita ;[D]save AXY JSR LB74C ;set Z=1 iff current drive is a RAM disc BEQ LB8EE ;if so then nothing to do, return JSR LAAD9 ;else map current volume to physical volume AND #$07 ;extract physical drive number, clear b7..3 TAX ;put drive number in X LDA denflg ;get density flag AND #$40 ;mask b6=double density BEQ setdd ;if b6 clear then A=0, single density LDA #latchd ;else toggle double density latch .setdd EOR LB8EF,X ;apply flags for drive 0..7 in X STA latch ;store in control latch .LB8EE RTS ;+---+---+---+---+---+---+---+---+--------+ ;|b 7|b 6|b 5|b 4|b 3|b 2|b 1|b 0| | ;+---+---+---+---+---+---+---+---+--------+ ;|CLK|DEN| - | - | - | - |SEL|DS | 2791HD | ;| - | - |RES|INT|DEN|SEL|DS1|DS0| B+ | ;| - | - |DEN|SEL|DS2|RES|DS1|DS0| Master | ;| - | - |DEN|RES|DS2|DS1|DS0|SEL|Chal'ger| ;+---+---+---+---+---+---+---+---+--------+ ;| 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | X = 00 | ;+---+---+---+---+---+---+---+---+--------+ ;| 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | X = 01 | ;+---+---+---+---+---+---+---+---+--------+ ;| 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | X = 02 | ;+---+---+---+---+---+---+---+---+--------+ ;| 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | X = 03 | ;+---+---+---+---+---+---+---+---+--------+ ;| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | X = 04 | ;+---+---+---+---+---+---+---+---+--------+ ;| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | X = 05 | ;+---+---+---+---+---+---+---+---+--------+ ;| 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | X = 06 | ;+---+---+---+---+---+---+---+---+--------+ ;| 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | X = 07 | ;+---+---+---+---+---+---+---+---+--------+ ;Table of drive control latch values for drives 0..7 .LB8EF EQUB latch0 EQUB latch0 EOR latch1 EQUB latch0 EOR latch2 EQUB latch0 EOR latch1 EOR latch2 EQUB $FF ;drives 4 and 5 are RAM discs EQUB $FF EQUB latch0 EOR latch6 EQUB latch0 EOR latch2 EOR latch6 .LB8F7 ;Set track stepping rate from startup options JSR savita ;save AXY JSR readsw ;call OSBYTE $FF = read/write startup options TXA ;transfer keyboard links to A JSR isolen ;extract b5,b4 of A .LB901 STA speed ;save as track stepping rate RTS ;1770 doesn't need Force Interrupt?? .LB905 ;Test write protect state of current drive JSR LB74C ;set Z=1 iff current drive is a RAM disc BEQ LB913 ;if so then return A=$00, Z=1 write enabled JSR LB9F3 ;else issue Seek and Force Interrupt JSR LBD16 ;wait for command completion AND #$40 ;z=0 if WD1770 S6 = write protect. RTS .LB913 LDA #$00 RTS ;The Challenger unit does not connect the WD 1770's INTRQ pin. ;No NMI occurs as a result of the Seek command and the contents of the ;NMI service area at $0D00 may safely be left undefined. .LB916 ;Seek logical track JSR pgmain ;page in main workspace LDA track ;get logical track number BIT t40flg ;test double-stepping flag BVC LB921 ;if b6=1 then double stepping is enabled ASL A ;so double track number: .LB921 ;Seek physical track JSR LB8D2 ;[D]set control latch for drive JSR savit ;[D]save XY PHA ;save target physical track JSR LBCA0 ;set X=physical floppy unit for current drive LDA head,X ;get physical track number for drive JSR LBC9C ;write to FDC track register LDA #$40 ;instruction at $0D00 = RTI STA intnmi+$00 PLA ;get back A STA head,X ;store physical track number for drive JSR LBD02 ;write to FDC data register EOR #sense ;if track number = 0 BEQ LB93E ;then issue WD1770 FDC command $00 = Restore LDA #$10 ;else issue WD1770 FDC command $10 = Seek: .LB93E ;Execute Restore/Seek command BIT fdcsta ;[D]test FDC status register PHP ;[D]save WD1770 S7 = motor on in N ORA speed ;apply track stepping rate EOR #hdload ;b3=1 spin up/load head JSR LBCFA ;write to FDC command register JSR LBD16 ;wait for command completion PLP ;[D]restore previous status IF (stsens EOR sense) AND $80 BMI LB95E ;if motor was on then exit ELSE BPL LB95E ;if motor was on then exit ENDIF LDA txcall ;else get data transfer call number LSR A ;test bit 0 BCC LB95E ;if reading or verifying data then exit LDY #$00 ;else wait 295 milliseconds then exit: .LB956 NOP ;allow extra head settling time NOP ;before writing DEX BNE LB956 DEY ;this point reached every 1.1 milliseconds BNE LB956 .LB95E RTS .LB95F ;Read ID and detect density JSR savit ;save XY JSR pgmain ;page in main workspace JSR LB9F3 ;issue Seek and Force Interrupt LDX #idtris ;[D]5 attempts to make, 3 in SD + 2 in DD BIT denflg ;if current density is single BVC LB982 ;then attempt in single density first DEX ;[D]else only 2 attempts in DD + 2 in SD: .LB970 LDA denflg ;get density flag ORA #$40 ;set b6=1, double density LDY #ddspt ;18 sectors per track JSR LB9AC ;execute Read Address at specified density BEQ LB9A9 ;if record found then return success BIT denflg ;[D]else test density flag BPL LB996 ;if b7=0 manual density then return failure DEX ;else decrement number of attempts remaining ;[D]not testing it here. [BUG] when SD ;selected, detection takes longer to fail .LB982 LDA denflg ;get density flag AND #$BF ;set b6=0, single density LDY #sdspt ;10 sectors per track JSR LB9AC ;execute Read Address at specified density BEQ LB9A9 ;if record found then return success BIT denflg ;[D]else test density flag BPL LB996 ;if b7=0 manual density then return failure DEX ;else decrement number of attempts remaining BNE LB970 ;if attempts remaining try double density .LB996 ;Set up drive for single density LDA denflg ;else set b6=0, single density AND #$BF STA denflg JSR LB8D2 ;set control latch for drive LDA #sdspt ;set 10 sectors per track STA sectrk LDA #$18 ;fake WD1770 S4 = record not found RTS ;fake WD1770 S3 = CRC error. .LB9A9 LDA #$00 ;fake WD1770 status = 0, succeeded. RTS .LB9AC ;Execute Read Address at specified density STA denflg ;store density flag STY sectrk ;store number of sectors per track: .LB9B2 ;Execute Read Address command JSR savit ;save XY JSR LB916 ;[D]seek logical track LDY #rdide-LBDBA-$01 ;[D]12 bytes to copy, $0D00..0B: .LB9BA LDA LBDBA,Y ;get byte of NMI read ID STA intnmi,Y ;store in NMI area DEY ;loop until all bytes copied BPL LB9BA PHP ;save interrupt state LDX sector ;[D]test no. IDs to read BEQ LB9D0 ;0 = internal use, skip wait for index SEI ;else disable interrupts .LB9C9 LDA fdcsta ;load FDC status register AND #$02 ;test WD1770 S1 = index IF (stsens EOR sense) AND $02 BNE LB9C9 ;loop until index pulse from drive ELSE BEQ LB9C9 ;loop until index pulse from drive ENDIF .LB9D0 LDY #$00 ;then wait 640.5 microseconds .LB9D2 DEY BNE LB9D2 LDA #$C0 EOR sense ;WD1770 command $C0 = Read address STA fdccmd ;write to FDC command register JSR LBD16 ;wait for command completion BNE LB9EA ;[D]if command succeeded DEC LBDBE-LBDBA+intnmi+$01 ;then backspace over CRC bytes DEC LBDBE-LBDBA+intnmi+$01 DEX ;decrement number of IDs to read BMI LB9EA ;if an internal call then finish BNE LB9D0 ;else loop until all IDs read, then: .LB9EA PLP ;restore interrupt state LDA fdcsta ;WD1770 S4 = record not found IF (stsens EOR sense) AND $18 EOR #stsens EOR sense ;invert for WD 2791 ENDIF AND #$18 ;WD1770 S3 = CRC error JMP pgmain ;mask off other bits, page in main workspace. .LBA05 ;Verify track JSR savit ;save XY LDA #$00 STA sector ;sector number = 0 STA rtxszl ;whole number of sectors to transfer LDA sectrk ;get number of sectors per track STA rtxszh ;set number of sectors to transfer LDA #$04 ;set call number to $04, verify data STA txcall ;set data transfer call number .LBA18 ;Transfer data L2 JSR savit ;save XY (inner) JSR LB74C ;set Z=1 iff current drive is a RAM disc BNE getts ;if floppy then transfer data to disc L2 JMP LBE67 ;else transfer data to paged RAM .getts ;Transfer data to disc L2 LDA rtxszl ;save ?$A0, ?$A1 on stack PHA LDA rtxszh PHA JSR LB8D2 ;set control latch for drive JSR LB916 ;seek logical track LDA track ;get logical track number JSR LBC94 ;store physical position of head ;This sets the FDC's track register to the logical track number so that ;sectors on 40-in-80 discs can be recognised. The track register and the ;drive's current track number in the workspace are re-set to the physical ;track number after the operation at $BA93. JSR LBAFB ;copy NMI read from disc/polling loop to NMI LDA savrom ;get *OPT 9 saverom slot number STA LBD88-LBD5C+intnmi+$01 ;store in polling loop to page in on entry LDA txcall ;get data transfer call number PHA ;[D]save on stack AND #$05 ;if call=0 or 2, read (deleted) data BEQ LBA4E ;then branch ROR A ;else if b2..0 = 1x0, A=$04 verify data BCS LBA58 JSR LBAEF ;[D]then instruction at $0D06 = JMP $0D11 BMI LBA6A ;discard byte from FDC data register (always) .LBA4E LDA rtxszl ;increment MSB byte count if LSB >0 BEQ LBA54 ;not rounding up, converting number format; INC rtxszh ;Z=1 from both DECs means zero reached .LBA54 LDY #rdios-LBD5C+$01 ;if call=0 or 2, read (deleted) data BNE LBA67 ;then data address is located at $0D07. .LBA58 LDA rtxszl ;increment MSB byte count if LSB >0 BEQ LBA5E ;not rounding up, converting number format; INC rtxszh ;Z=1 from both DECs means zero reached .LBA5E LDA #$00 ;if b0=1, A=1 or 3 write (deleted) data STA rtxszl ;then clear ?$A0, write whole sectors JSR LBB07 ;copy NMI write to disc to NMI area LDY #LBD5F-LBD5C+wriol-LBDAC+$01 ;data address is located at $0D04 .LBA67 JSR LBAA0 ;set data address in NMI ISR .LBA6A LDA romid ;get Challenger ROM slot number STA LBD93-LBD5C+intnmi+$01 ;save in NMI area LDA sector ;get start sector number JSR LBCFE ;write to FDC sector register PLA ;restore data transfer call number AND #$07 ;[D]mask bits 2..0 PHA ;save it again TAY ;transfer to Y LDA LBD52,Y ;get FDC command for call JSR LBCFA ;write to FDC command register LDX #$1E ;wait 76 microseconds .LBA81 DEX BNE LBA81 JSR LBD88-LBD5C+intnmi ;page SROM in and wait until finished L0 PLA TAY JSR LBD27 ;load FDC status register and store b6..0 AND LBD57,Y ;apply status mask from table to set Z TAY ;present FDC status register in A JSR LBC8C ;store head position for this drive .LBA96 PLA ;restore ?$A0, ?$A1 from stack STA rtxszh PLA STA rtxszl TYA JMP pgmain ;page in main workspace .LBAA0 ;Set data address in NMI ISR LDA txcall ;[D]test data transfer call number BMI LBACF ;if b7=1 then transferring to JIM, branch LDA tubflg ;else test Tube data transfer flag BEQ LBAC4 ;if transferring data to Tube LDA #<reg3 ;then paste address of R3DATA at $0D00+Y STA intnmi+$00,Y LDA #>reg3 STA intnmi+$01,Y LDA #$4C ;instruction at $0D09 = JMP $0D11 STA LBD65-LBD5C+intnmi+$00 ;do not increment R3DATA address LDA #<(LBD6D-LBD5C+intnmi) STA LBD65-LBD5C+intnmi+$01 LDA #>(LBD6D-LBD5C+intnmi) STA LBD65-LBD5C+intnmi+$02 RTS .LBAC4 LDA nmiusr+$00 ;else copy data pointer to NMI ISR at $0D00+Y STA intnmi+$00,Y LDA nmiusr+$01 STA intnmi+$01,Y RTS .LBACF ;Enable JIM select in NMI read from disc LDA #$20 ;$0D0E = JSR $0D3D STA LBD6A-LBD5C+intnmi+$00 LDA #<(LBD99-LBD5C+intnmi) STA LBD6A-LBD5C+intnmi+$01 LDA #>(LBD99-LBD5C+intnmi) STA LBD6A-LBD5C+intnmi+$02 LDA nmiusr+$00 ;insert 2MSB of JIM address = LSB page no. STA LBD9C-LBD5C+intnmi+$01 STA jpglo ;and page it in LDA nmiusr+$01 ;insert MSB of JIM address = MSB page no. STA LBDA6-LBD5C+intnmi+$01 STA jpghi ;and page it in RTS .LBAEF ;Copy NMI verify to NMI area LDY #LBDC9-LBDC6-$01 ;3 bytes to copy, $0D06..8: .LBAF1 LDA LBDC6,Y ;get byte of NMI verify STA LBD62-LBD5C+intnmi,Y ;store in NMI area DEY ;loop until all bytes copied BPL LBAF1 RTS .LBAFB ;Copy NMI read from disc/polling loop to NMI LDY #LBDAC-LBD5C-$01 ;80 bytes to copy, $0D00..4F: .LBAFD LDA LBD5C,Y ;get byte of NMI read from disc/polling loop STA intnmi,Y ;store in NMI area DEY ;loop until all bytes copied BPL LBAFD RTS .LBB07 ;Copy NMI write to disc to NMI area LDY #LBDBA-LBDAC-$01 ;14 bytes to copy, $0D03..10: .LBB09 LDA LBDAC,Y ;get byte of NMI write to disc STA LBD5F-LBD5C+intnmi,Y ;patch NMI read to disc routine with it DEY ;loop until all bytes copied BPL LBB09 LDA #<(LBD7C+$FE-LBD7E) ;enable 123 microsecond delay STA LBD7E-LBD5C+intnmi+$01 ;before interrupting write operation RTS ;so that FDC will write CRC of sector .LBB18 ;Create ID table and format track LDA #sdspt ;set A = 10 sectors per track BIT denflg ;if double density format BVC LBB21 LDA #ddspt ;then set A = 18 sectors per track .LBB21 STA fsectk ;store as limit to sector count STA sectrk ;store as no. sectors per track of disc ASL A ;multiply by 4 ASL A STA chrnsz ;store as size of CHRN table LDX skewit ;set X = number of first sector LDY #$00 ;(inverse track skew) Y=0 CHRN tbl index .LBB2E LDA track ;Get logical track number STA chrn,Y ;store cylinder number C INY LDA #$00 ;head number = 0 STA chrn,Y ;store head humber H INY TXA ;transfer sector number to A STA chrn,Y ;store record number R INY LDA #$01 ;size code = 1, 256-byte sector STA chrn,Y ;store size code N INY INX ;increment sector number CPX fsectk ;has it reached no. sectors per track? BCC LBB4C LDX #$00 ;if so then wrap around to 0 .LBB4C CPY chrnsz ;has table offset reached 4x s.p.t? BCC LBB2E ;if not then loop LDA #<chrn ;else set pointer to start of CHRN table: STA nmiusr+$00 LDA #>chrn STA nmiusr+$01 .LBB58 ;Format track LDA #<runofs ;set run table pointer to $000612 in JIM STA runptl ;(page breaks occur 5/8 through fifth, LDA #>runofs ;1/8 through eleventh and in gap2 of PHA ;seventeenth sector of track.) STA runpth LDX #LBC42-LBC42 ;point to single density table, X = $00 BIT denflg ;if double density format BVC LBB6A LDX #LBC65-LBC42 ;then point to double density table, X = $23 .LBB6A LDA sectrk ;[D]get number of sectors per track STA fsectg ;set as counter JSR LBC3A ;page in JIM page 6..9 LDY #$05 ;set Y = 5 as counter: .LBB74 JSR LBBBB ;add entry to track format RLE table DEY ;loop until 5 entries added BNE LBB74 ;this copies gap 5, IAM and start of gap 1 STX romofs ;X points to repeating sector block .LBB7C LDX romofs ;[D]reset X to start of sector block .LBB7E JSR LBBBB ;add entry to track format RLE table BCC LBB7E ;loop until terminator byte reached DEC fsectg ;decrement number of sectors remaining BNE LBB7C ;loop until all sectors added to track LDA #$00 ;data byte = $00 (run length = $10 or $16) JSR LBC14 ;add gap 4 to table JSR pgmain ;page in main workspace JSR LB916 ;seek logical track LDX #$FF LDY #sdgap5 ;A = $10 BIT denflg ;if double density format BVC LBB9F LDY #ddgap5 ;then A = $28 LDX #$4E .LBB9F STY frmrun ;set number of filler bytes in gap 5 PLA JSR pga ;page in JIM page in A STX rundat+<runofs+$00 ;set filler byte in gap 5 (redundant) LDY #LBE06-LBDC9-$01 ;[D]61 bytes to copy, $0D00..3D: .LBBAA LDA LBDC9,Y ;get byte of NMI format code STA intnmi,Y ;store in NMI handler area DEY ;loop until all bytes transferred BPL LBBAA LDA #$F4 ;$F4=Write track, settling delay JSR LBCFA ;write to FDC command register JMP LBD16 ;wait for command completion and exit. .LBBBB ;Add entry to track format RLE table TXA ;save ROM table offset PHA TYA ;save number of sectors remaining PHA SEC LDA LBC42+$00,X ;get run length from ROM table BMI LBBD9 ;if b7=1 then process special entry BEQ LBBD2 ;if the terminator byte then finish C=1 STA frmrun ;else store run length in zero page LDA LBC42+$01,X ;get data byte from ROM table JSR LBC14 ;store run in table .LBBD1 CLC ;c=0, sector not completed .LBBD2 PLA ;restore number of sectors remaining TAY PLA ;restore ROM table offset TAX INX ;add 2 to ROM table offset INX RTS .LBBD9 ;Process special table entry (length=$FF) LDA LBC42+$01,X ;get data byte from ROM format table BNE LBC00 ;if non-zero then add sector data area LDA #$01 ;else add ID bytes. run length of bytes = 1 STA frmrun ;store run length in zero page LDX #$04 ;4 bytes in sector ID: .LBBE4 JSR pgmain ;page in main workspace LDY #$00 ;y=0 for user memory load JSR LA4EC ;get data byte from user memory JSR LBC3A ;page in JIM page 6..9 JSR LBC14 ;store run in table INC nmiusr+$00 ;increment CHRN table pointer BNE LBBF8 ;carry out to high byte INC nmiusr+$01 .LBBF8 DEX ;loop until 4 ID bytes stored BNE LBBE4 STA szcode ;store last byte read = N = size code BEQ LBBD1 ;restore XY and return (always) .LBC00 ;Add sector data area LDX szcode ;load sector size code LDA LBC88,X ;get run length from table STA frmrun ;store in zero page LDX #$08 ;repeat prescribed run 8 times: LDA #$E5 ;A=$E5 = sector filler byte .LBC0B JSR LBC14 ;store run in table DEX ;loop until 8 copies of run stored BNE LBC0B BEQ LBBD1 ;restore XY and return .LBC14 ;Store run in table PHA ;save data byte LDY runptl ;get offset into data/run tables IF sense EOR #sense ;invert for WD 2791 ENDIF STA rundat,Y ;store data byte in data table LDA frmrun ;get run length STA runlen,Y ;store run length in run table DEY BPL LBC2B ;if pointers are on a page boundary EOR #$80 ;then toggle b7=1 of run length STA runlen+$00 .LBC2B INC runptl ;increment data table pointer BPL LBC38 ;if LSB of pointer reaches $80 LDA #$00 ;then the tables fill each half page STA runptl ;so reset LSB of pointer = $00 INC runpth ;and carry out to high byte JSR LBC3A ;page in next page of table .LBC38 PLA ;restore data byte and return RTS .LBC3A ;Page in current page of RLE table PHA ;save A LDA runpth ;get MSB of data table pointer JSR pga ;page in JIM page in A PLA ;restore A RTS ;RLE tables of formatting bytes ;Single density .LBC42 ;Single density EQUB sdgap5,$FF ; 16x $FF filler bytes } Gap 5 EQUB $03,$00 ; 6x $00 synchronization bytes } EQUB $03,$00 EQUB $01,$FC ; 1x $FC index address mark (clock $D7) EQUB sdgap1,$FF ; 11x $FF filler bytes } Gap 1 ;block repeated for each sector EQUB $03,$00 ; 6x $00 synchronization bytes } EQUB $03,$00 EQUB $01,$FE ; 1x $FE ID address mark (clock $C7) EQUB $FF,$00 ;id bytes are inserted here EQUB $01,$F7 ; 1x $F7 CRC character insert (2 bytes) EQUB $0B,$FF ; 11x $FF filler bytes } Gap 2 EQUB $03,$00 ; 6x $00 synchronization bytes } EQUB $03,$00 EQUB $01,$FB ; 1x $FB data address mark (clock $C7) EQUB $FF,$01 ;data bytes are inserted here EQUB $01,$F7 ; 1x $F7 CRC character insert (2 bytes) EQUB sdgap3,$FF ; 16x $FF filler bytes } Gap 3... ;end of repeated block EQUB $00 ;terminator byte (not part of format) ;Double density .LBC65 ;Double density EQUB ddgap5,$4E ; 40x $4E filler bytes } EQUB $0C,$00 ; 12x $00 preamble bytes } Gap 5 EQUB $03,$F6 ; 3x $F6 synchronization bytes } EQUB $01,$FC ; 1x $FC index address mark EQUB ddgap1,$4E ; 25x $4E filler bytes } Gap 1 ;block repeated for each sector EQUB $0C,$00 ; 12x $00 preamble bytes } EQUB $03,$F5 ; 3x $F5 synchronization bytes } EQUB $01,$FE ; 1x $FE ID address mark EQUB $FF,$00 ;id bytes are inserted here EQUB $01,$F7 ; 1x $F7 CRC character insert (2 bytes) EQUB $16,$4E ; 22x $4E filler bytes } EQUB $0C,$00 ; 12x $00 preamble bytes } Gap 2 EQUB $03,$F5 ; 3x $F5 synchronization bytes } EQUB $01,$FB ; 1x $FB data address mark EQUB $FF,$01 ;data bytes are inserted here EQUB $01,$F7 ; 1x $F7 CRC character insert (2 bytes) EQUB ddgap3,$4E ; 22x $4E filler bytes } Gap 3... ;end of repeated block EQUB $00 ;terminator byte (not part of format) .LBC88 EQUB $10 ;8x runs of 16 bytes for 128-byte sectors EQUB $20 ;8x runs of 32 bytes for 256-byte sectors EQUB $40 ;8x runs of 64 bytes for 512-byte sectors EQUB $80 ;8x runs of 128 bytes for 1024-byte sectors .LBC8C ;Store per-drive head position LDA track ;get logical track number of disc operation BIT t40flg ;test double-stepping flag BVC LBC94 ;if b6=1 then double stepping is enabled ASL A ;so double track number: .LBC94 ;Store physical position of head PHA ;save physical track JSR LBCA0 ;set X=physical floppy unit for current drive PLA ;restore physical track STA head,X ;store physical track number for drive: .LBC9C ;Write to FDC track register IF sense EOR #sense ;invert for WD 2791 ENDIF STA fdctrk RTS .LBCA0 ;Set X=physical floppy unit for current drive JSR LAAD9 ;map current volume to physical volume AND #$07 ;mask drive no in b2..0 mask off volume letter LDX #$02 ;preset X=2 to select third floppy drive CMP #$06 ;if physical drive number = 6 or 7 BCS LBCAE ;then return X=2 AND #$01 ;else return X=0 drv 0 or 2, X=1 drv 1 or 3 TAX .LBCAE RTS .LB9F3 ;Issue Seek and Force Interrupt JSR LB8D2 ;set control latch for drive LDA #$18 ;WD1770 command $18 = Seek w/o spin up ;NB always set bit 3; do not unload head! JSR LBCFA ;write to FDC command register LDX #$0F ;wait 38 microseconds .LB9FD DEX BNE LB9FD .LBA00 ;Issue Force Interrupt LDA #$D0 ;WD1770 command $D0 = Force interrupt .LBCFA ;Write to FDC command register EOR #sense ;invert for WD 2791 IF sense AND $80 BMI go ;if a Type I command, start now ELSE BPL go ;if a Type I command, start now ENDIF CMP #$D0 EOR sense ;if Force Interrupt, start now BEQ go .dwait BIT fdcsta ;else poll status register NOP ;allow indeterminate level to settle IF (stsens EOR sense) AND $80 BPL dwait ;loop until Ready ELSE BMI dwait ;loop until Ready ENDIF .go STA fdccmd ;then send command to FDC RTS .LBCFE ;Write to FDC sector register IF sense EOR #sense ;invert for WD 2791 ENDIF STA fdcsec RTS .LBD02 ;Write to FDC data register IF sense EOR #sense ;invert for WD 2791 ENDIF STA fdcdat RTS .LBD06 ;Set Z=1 iff drive motor is on JSR LB74C ;set Z=1 iff current drive is a RAM disc BEQ LBD13 ;if RAM disc then treat as motor on LDA fdcsta ;else load FDC status register EOR #stsens EOR sense ;return A=0, Z=1 iff motor is on [D]swapped AND #$80 ;mask b7 extract WD1770 S7 = motor on RTS .LBD13 LDA #$00 ;return A=0, Z=1 indicating motor on. RTS .LBD16 ;Wait for command completion JSR savit ;save XY LDX #$FF ;wait 638 microseconds .LBD1B DEX BNE LBD1B .LBD1E JSR LBD33 ;[D]poll for ESCAPE LDA fdcsta ;load FDC status register IF (stsens EOR sense) AND $80 BPL LBD1E ;loop until b7=1 WD1770 S7 = motor on ELSE BMI LBD1E ;loop until b7=0 WD1770 S7 = motor on ENDIF ROR A ;place bit 0 in carry flag IF (stsens EOR sense) AND $01 BCC LBD1E ;loop until b0=1 WD1770 S0 = busy ELSE BCS LBD1E ;loop until b0=0 WD1770 S0 = busy ENDIF .LBD27 LDA fdcsta ;load FDC status register IF (stsens EOR sense) AND $7F EOR #stsens EOR sense ;invert for WD 2791 ENDIF AND #$7F ;mask bits 6..0 ignore WD1770 S7 = motor on JSR pgmain ;page in main workspace STA fdstat ;save final status RTS .LBD33 ;Poll for ESCAPE LDA dopint ;if =0 disc operation is uninterruptible BEQ LBD51 ;then return BIT escflg ;else if ESCAPE pressed BPL LBD51 JSR LBA00 ;then send Force Interrupt LDA #latchr ;RES b4=0, reset WD 1770 floppy controller STA latch ;store in control latch JSR ackesc ;acknowledge ESCAPE condition JSR estrng ;raise "Escape" error. EQUB $11 EQUS "Escape" EQUB $00 .LBD51 RTS ;Table of WD1770 FDC commands for data transfer call numbers 0..4 .LBD52 EQUB $90 ;$00 = Read data EQUB $B4 ;$01 = Write data EQUB $90 EQUB $B5 ;$03 = Write deleted data EQUB $90 ;$04 = Verify data ;Table of status mask bytes for data transfer call numbers 0..4 .LBD57 ;{RecordNotFound CRCError LostData} ($1C) plus: EQUB $3C ;$00 = Read data: {RecordType} EQUB $7C ;$01 = Write data: {WriteProtect RecordType} EQUB $1C ;{} EQUB $5C ;$03 = Write deleted data: {WriteProtect} EQUB $3C ;$04 = Verify data: {RecordType} ;NMI read from disc, $0D00..2B ;opcode read 4+e..8 microseconds after NMI ;(up to 13.5 us if code running in 1 MHz mem) .LBD5C STA LBD85-LBD5C+intnmi+$01 ;save accumulator to restore on exit .LBD5F ;NMI write to disc pasted here .rdiol LDA fdcdat ;read FDC data register .LBD62 ;NMI verify pasted here IF sense EOR #sense ;invert for WD 2791 ENDIF .rdios STA jim ;or any alias of base .LBD65 ;NMI read/write Tube jump pasted here INC rdios-LBD5C+intnmi+$01 ;increment user memory address BNE LBD6D ;carry out to high byte .LBD6A ;enable JIM select pasted here INC rdios-LBD5C+intnmi+$02 .LBD6D DEC rtxszl ;decrement count of bytes to transfer BNE LBD85 ;($0101 = 1; $0000 = 0) DEC rtxszh ;if count has not reached zero BNE LBD85 ;then restore A and return from interrupt LDA #$40 ;else set 0D00=RTI; ignore further NMIs STA intnmi+$00 ;ISR safe by 23+e..30.5 us after NMI LDA #$CE ;write complete by 25.5+e..33 us .LBD7C ADC #$01 ;wait 123 microseconds (if loop enabled) .LBD7E BCC LBD80 ;0D23=$FC loops back to $0D20 .LBD80 LDA #$D0 EOR sense ;FDC command $D0 = Force Interrupt STA fdccmd ;write to FDC command register .LBD85 LDA #$00 ;restore value of A on entry RTI ;return from interrupt ;NMI polling loop, $0D2C..3C .LBD88 LDA #srom ;page *OPT 9 saverom slot in STA romsw .LBD8D LDA fdcsta ;load FDC status register ROR A ;place bit 0 in carry flag IF (stsens EOR sense) AND $01 BCC LBD8D ;loop until b0=1 WD1770 S0 = busy ELSE BCS LBD8D ;loop until b0=0 WD1770 S0 = busy ENDIF .LBD93 LDA #$00 ;page Challenger ROM back in STA romsw RTS ;return ;JIM page select routine, $0D3D..4F ;made reachable by JSR installed at LBACF .LBD99 INC LBD9C-LBD5C+intnmi+$01 ;increment LSB of JIM page address .LBD9C LDA #$00 ;set LSB of JIM page address STA jpglo BNE LBDAB ;if carry out INC LBDA6-LBD5C+intnmi+$01 ;then increment MSB of JIM page address .LBDA6 LDA #$00 ;set MSB of JIM page address STA jpghi .LBDAB RTS ;NMI write to disc, $0D03..10 .LBDAC .wriol LDA jim ;or any alias of base IF sense EOR #sense ;invert for WD 2791 ENDIF .wrios STA fdcdat INC LBD5F-LBD5C+wriol-LBDAC+intnmi+$01 BNE LBDBA INC LBD5F-LBD5C+wriol-LBDAC+intnmi+$02 ;NMI read ID, $0D00..0B .LBDBA PHA LDA fdcdat ;load FDC data register IF sense EOR #sense ;invert for WD 2791 ENDIF .LBDBE STA rdide-LBDBA+intnmi+$00 ;store ID byte in buffer INC LBDBE-LBDBA+intnmi+$01 ;increment offset PLA RTI .rdide ;NMI verify, $0D06..08 .LBDC6 JMP LBD6D-LBD5C+intnmi ;discard byte from FDC data register ;A run-length encoded table of formatting bytes is stored in two ;interleaved arrays in JIM space starting at $000612 and $000692. ;Data bytes occupy the top half of each page. Valid range of counts is ;$01..$80, but bit 7 is then toggled on the first count of the page. ;When the byte source address crosses a page, the next JIM page is selected ;in the same interrupt and the count source address is reset to the start ;of the page in the next interrupt. One byte from the next page is sent to ;the controller in the meantime, and so the first byte of the page cannot ;be a singleton. The page crossings occur 5/8 of the way through the data ;area of the fifth sector after the index pulse, 1/8th through the eleventh ;sector and during gap2 of the seventeenth sector. ;The table alignment shows that the RLE code was ported from Challenger ;into DDOS 3.46. ;NMI format, $0D00..3C .LBDC9 PHA ;save A on entry .LBDCA LDA rundat+<runofs ;fetch current data byte STA fdcdat ;write to FDC data register DEC frmrun ;decrement run counter BNE LBDEA ;if all bytes in run written INC LBDCA-LBDC9+intnmi+$01 ;then increment data byte address low BNE LBDFC ;if no carry then fetch next run length LDA #<rundat ;[D]else reset data address low = $80 STA LBDCA-LBDC9+intnmi+$01 .LBDDE LDA #>runofs+$01 ;page in next JIM page STA jpglo LDA runlen+$00 ;fetch next run length [D]marked b7=1 STA frmrun ;set run counter .LBDE8 PLA ;restore A on entry RTI ;exit .LBDEA BPL LBDE8 ;if run still in progress then exit LDA frmrun ;else page was crossed last time: AND #$7F ;[D]mask off page marker in b7 STA frmrun ;update run counter LDA #<runlen ;reset run length address low = $00 STA LBDFF-LBDC9+intnmi+$01 INC LBDDE-LBDC9+intnmi+$01 ;increment data byte address high PLA ;restore A on entry RTI ;exit .LBDFC INC LBDFF-LBDC9+intnmi+$01 ;increment run length address .LBDFF LDA runlen+<runofs ;fetch next run length STA frmrun ;set run counter PLA ;restore A on entry RTI ;exit .LBE06 .pgaux ;Page in auxiliary workspace PHA LDA #<jaux BEQ LBE1E .pgmain ;Page in main workspace PHA LDA #<jmain BNE LBE1E .pgcat0 ;Page in catalogue sector 0 PHA LDA #<(jcat+$00) BNE LBE1E .pgcat1 ;Page in catalogue sector 1 PHA LDA #<(jcat+$01) BNE LBE1E .pgbuil ;Page in line buffer LDA #<jbuild .pga ;Page in JIM page in A PHA .LBE1E STA jpglo ;store LSB JIM paging register LDA #$00 STA jpghi ;set MSB JIM paging register = $00 PLA ;restore A on entry RTS .LBE28 ;ChADFS ROM call 4 JSR nmicla ;claim NMI JSR pgmain ;page in main workspace LDA #$01 ;data transfer call $01 = write data STA txcall LDA #<fstsiz ;transfer size = 512 bytes STA rtxszl LDA #>fstsiz STA rtxszh LDA #<fstadr ;source address = HAZEL, $C000 STA nmiusr+$00 LDA #>fstadr STA nmiusr+$01 LDA #$00 ;b7=0 transfer from host STA tubflg LDA #<fstlba ;starting sector/LBA = $0000 STA txlbah STA txlbal LDA #$04 ;destination physical drive = 4 JSR LBE80 ;transfer data to paged RAM LDA #>rootad ;source address = HAZEL, $C900 STA nmiusr+$01 LDA #<rootlb ;starting sector/LBA = $0002 STA txlbal LDA #>rootsz ;transfer size = 1280 bytes STA rtxszh LDA #$04 ;destination physical drive = 4 JSR LBE80 ;transfer data to paged RAM .LBE64 LDA #$00 ;fake WD1770 status = 0, succeeded. RTS .LBE67 ;Transfer data to paged RAM JSR pgmain ;page in main workspace LDA denflg ;get density flag EOR ramden ;compare with RAM disc density flag ASL A ;move result into N BPL LBE76 ;if densities do not match LDA #$10 ;then WD1770 S4 = record not found RTS .LBE76 LDA txcall ;else get data transfer call number AND #$7C ;mask off b7=JIM, b1,b0=calls 0..3 BNE LBE64 ;if call=4 verify, return status $00 JSR LAAD9 ;map current volume to physical volume .LBE80 LDY #<jdrv4 ;volume 4 starts at JIM address $000A00 LDX #>jdrv4 CMP #$04 ;if physical drive is not 4 BEQ LBE8C LDY #<jdrv5 ;then volume starts at JIM address $040000 LDX #>jdrv5 .LBE8C LDA rtxszl ;save ?$A0, ?$A1 on stack PHA LDA rtxszh PHA TXA ;save volume start address in YX on stack PHA ;MSB first TYA PHA LDA rtxszl ;increment MSB byte count if LSB >0 BEQ LBE9C ;not rounding up, converting number format; INC rtxszh ;Z=1 from both DECs means zero reached .LBE9C LDY #LBF76-LBF2F-$01 ;71 bytes to copy, $0D00..46 [D]: .LBE9E LDA LBF2F,Y ;get byte of RAM disc transfer code STA intnmi,Y ;store in NMI handler area DEY ;loop until all bytes transferred BPL LBE9E LDA txcall ;get data transfer call number BMI LBEF6 ;if data address in JIM space then branch LSR A ;else put b0 = write data in C LDA nmiusr+$00 ;set AY = user memory address LDY nmiusr+$01 BCS LBEC4 ;if call number = 0 read data STA LBF50-LBF2F+intnmi+$01 ;then paste user memory address at $0D22,3 STY LBF50-LBF2F+intnmi+$02 LDA tubflg ;test Tube transfer flag BEQ LBF16 ;if b7=0 then an I/O transfer, branch LDA #$8D ;else instruction at $0D21 = STA $FEE5 LDY #LBF50-LBF4D BNE LBED8 ;modify RAM transfer code for Tube. .LBEC4 ;Modify RAM transfer code for write STA LBF4D-LBF2F+intnmi+$01 ;paste user memory address at $0D1F,20 STY LBF4D-LBF2F+intnmi+$02 LDA #<(LBF4D-LBF2F+intnmi+$02) ;0D27=INC $0D20 STA LBF56-LBF2F+intnmi+$01 ;increment user memory address LDA tubflg ;test Tube transfer flag BEQ LBF16 ;if b7=0 then an I/O transfer, branch LDA #$AD ;else instruction at $0D1E = LDA $FEE5 LDY #LBF4D-LBF4D .LBED8 ;Modify RAM transfer code for Tube STA LBF4D-LBF2F+intnmi+$00,Y ;store opcode LDA abs at $D1E/STA abs at $D21 LDA #<reg3 ;store address of R3DATA, $FEE5 STA LBF4D-LBF2F+intnmi+$01,Y ;at $0D1F,20 or $0D22,3 LDA #>reg3 STA LBF4D-LBF2F+intnmi+$02,Y LDA #<(LBF4A+$FE-LBF54) ;0D25=BNE $0D1B STA LBF54-LBF2F+intnmi+$01 ;enable 25 microsecond interval per byte LDA #<(LBF4A+$FE-LBF67) ;0D38=BNE $0D1B STA LBF67-LBF2F+intnmi+$01 ;enable 38.5 microsecond delay to next page LDA #$AD ;0D27=LDA $0D23 STA LBF56-LBF2F+intnmi+$00 ;do not increment R3DATA address BNE LBF16 ;branch (always) .LBEF6 ;Copy data between JIM pages LDY #ramxe-LBF76-$01 ;50 bytes to copy, $0D00..31 [D]: .LBEF8 LDA LBF76,Y ;get byte of RAM disc copy code STA intnmi,Y ;store in NMI handler area DEY ;loop until all bytes transferred BPL LBEF8 LDY #txent-LBF2F ;LBA goes to $0D06,01 LDX #LBF83-LBF76+LBF45-LBF40 ;JIM page number goes to $0D13 LDA txcall ;get data transfer call number AND #$7F ;mask bits 0..6 BEQ LBF10 ;if not =0, read data LDY #LBF83-LBF76 ;then LBA goes to $0D13,0E LDX #txent-LBF2F+LBF45-LBF40 ;JIM page number goes to $0D06 .LBF10 LDA nmiusr+$00 STA intnmi+$01,X ;paste JIM page number at $0D06/13 EQUB $AD ;$AD=LDA abs; skip next two bytes .LBF16 LDY #LBF40-LBF2F ;LBA goes to $0D17,12 CLC PLA ;restore LSB volume start address ADC txlbal ;add LSB relative LBA STA LBF45-LBF40+intnmi+$01,Y ;paste LSB absolute LBA at $0D06/13/17 PLA ;restore MSB volume start address ADC txlbah ;add MSB relative LBA STA intnmi+$01,Y ;paste MSB absolute LBA at $0D01/0E/12 LDY #$00 ;starting offset = $00 JSR txent-LBF2F+intnmi ;do transfer to/from paged RAM LDY #$00 ;fake WD1770 status = 0, succeeded JMP LBA96 ;restore $A0,1 and page in main workspace. ;Transfer code copied to $0D00..46 .LBF2F ;start of object RTI ;ignore spurious interrupts .txent ;transfer code entry point LDA savrom ;get *OPT 9 saverom setting STA romsw ;set ROM bank to *OPT 9 saverom .LBF35 LDA rtxszh ;if 256 bytes or less remaining CMP #$01 BNE LBF40 LDA #<(LBF65+$FE-LBF54) ;then 0D25=BNE $0D36 STA LBF54-LBF2F+intnmi+$01 ;transfer bytes of last page .LBF40 LDX #$00 ;0D11 STX jpghi ;set MSB of JIM page number .LBF45 LDX #$00 ;0D16 STX jpglo ;set LSB of JIM page number .LBF4A JSR LBF6F-LBF2F+intnmi ;wait 18 microseconds (only needed for Tube) .LBF4D LDA jim,Y ;0D1E read byte from JIM page .LBF50 STA jim,Y ;0D21 write byte to JIM page INY ;increment offset .LBF54 BNE LBF4D ;0D25 loop until page boundary reached .LBF56 INC LBF50-LBF2F+intnmi+$02 ;0D27 increment MSB write address INC LBF45-LBF2F+intnmi+$01 ;increment LSB of JIM page number BNE LBF61 ;carry out to MSB of JIM page number INC LBF40-LBF2F+intnmi+$01 .LBF61 DEC rtxszh ;decrement MSB transfer byte count BNE LBF35 ;loop to transfer next page (always) .LBF65 DEC rtxszl ;0D36 decrement LSB transfer byte count .LBF67 BNE LBF4D ;loop until last bytes transferred LDA romid ;page Challenger ROM back in STA romsw RTS ;return .LBF6F JSR LBF75-LBF2F+intnmi ;0D40 wait 18 microseconds JSR LBF75-LBF2F+intnmi .LBF75 RTS ;RAM disc copy code copied to $0D00..31 .LBF76 ;start of object ;NB code entered at (txent-LBF2F)+intnmi ;i.e. code between LBF2F and txent ;MUST be repeated here! RTI ;ignore spurious interrupts ;transfer code entry point LDX #$00 STX jpghi ;set MSB JIM paging register to source LDX #$00 STX jpglo ;set LSB JIM paging register to source LDA jim,Y ;get byte from source page .LBF83 LDX #$00 ;0D0D STX jpghi ;set MSB JIM paging register to destination LDX #$00 ;0D12 STX jpglo ;set LSB JIM paging register to destination STA jim,Y ;store byte in destination page INY ;loop to copy whole page BNE txent-LBF2F+LBF76 INC txent-LBF2F+LBF45-LBF40+intnmi+$01 ;increment LSB source page BNE LBF9B ;carry out to MSB source page INC txent-LBF2F+intnmi+$01 .LBF9B INC LBF83-LBF76+LBF45-LBF40+intnmi+$01 ;increment LSB destination page BNE LBFA3 ;carry out to MSB destination page INC LBF83-LBF76+intnmi+$01 .LBFA3 DEC rtxszh ;loop until required number of pages copied BNE txent-LBF2F+LBF76 RTS .ramxe IF _MASTER SKIPTO $BFBF ;Filing system information block, in reverse .L88F0 EQUB $04 ;filing system number EQUB $15 ;highest file handle EQUB $11 ;lowest file handle EQUS " CSID" ;filing system name EQUB $04 ;filing system number EQUB $15 ;highest file handle EQUB $11 ;lowest file handle EQUS " KSID" ;filing system name .fsblke ENDIF SKIPTO $BFD5 ;Table of action addresses for ChADFS ROM calls 0..4, low bytes .LBFBB EQUB <(LAAF6-$01) ;ChADFS 0 = *CONFIG $AAF6 EQUB <(LBFA8-$01) ;ChADFS 1 = transfer data $BFA8 EQUB <(L821F-$01) ;ChADFS 2 = probe unit RAM size $821F EQUB <(LAB7E-$01) ;ChADFS 3 = reset drv mappings $AB7E EQUB <(LBE28-$01) ;ChADFS 4 = format RAM disc $BE28 ;Table of action addresses for ChADFS ROM calls 0..4, high bytes .LBFC0 EQUB >(LAAF6-$01) EQUB >(LBFA8-$01) EQUB >(L821F-$01) EQUB >(LAB7E-$01) EQUB >(LBE28-$01) .ajump LDA LBFC0,X ;get action address high byte PHA ;save on stack LDA LBFBB,X ;get action address low byte PHA ;save on stack RTS ;jump to action address .LBFDB ;ChADFS ROM call dispatcher TAX ;transfer call number to X as index JSR pgmain ;page in main workspace LDA #$FF STA chadfs ;b6=1 ChADFS is current FS JSR ajump ;push address of ChADFS return, $BFF4 ;'Revolving bookcase' routine to perform bank switching from code executing ;in that same bank. ChADFS 300M contains a near-identical routine at the ;same location. The thread comes to the routine on either 'side', sets the ;latch and vanishes, to appear on the opposite 'side'. ;In this instance, Challenger and ChADFS are hard-coded to reside in ;neighbouring paged ROM slots, with ChADFS higher. Challenger never ;initiates the crossing; the thread always enters from ChADFS and returns ;after Challenger has serviced the call. .LBFF4 ;Return to ChADFS ROM LDX romid ;get our ROM slot number INX ;ChADFS is in the slot above STX romid ;set MOS copy of ROMSEL to new slot number ;At this point MOS may service an interrupt then switch 'back' to ChADFS. ;Likewise an interrupt service in ChADFS may cause premature entry here. ;In either case the next instruction has no effect. STX romsw ;switch to ChADFS ROM and continue there ;Absent an interrupt, the thread leaves or enters here. ;entry point from ChADFS ROM NOP ;allow address bus to stabilise JMP LBFDB ;jump to dispatcher .end IF _O2791C save "o2791c.rom",lang,end ELIF _O2793C save "o2793c.rom",lang,end ELIF _O1770C save "o1770c.rom",lang,end ELIF _W1770C save "w1770c.rom",lang,end ELIF _A1770C save "a1770c.rom",lang,end ELIF _M1770C save "m1770c.rom",lang,end ELIF _PG400C save "pg400c.rom",lang,end ELSE save "challc.rom",lang,end ENDIF ;End of chal210.asm.txt