;Assembly of Opus DDOS 3.x8 ;Greg Cook, 17 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/ ; ;Hints on Tube host code (all attributed): ;(NAUG) M. Holmes and A. Dickens, The new advanced user guide for ; the BBC Microcomputer (Cambridge: Adder, 1987) ;(SB) Steven Bass's comments on Opus EDOS 0.4, ; http://regregex.bbcmicro.net/edos04.asm.txt ;(JGH) J. G. Harston's disassembly of the Tube host code, ; https://mdfs.net/Software/Tube/BBC/Host100.lst ;Comments beginning with capital letters are entry points. ;Based on the Opus DDOS 3.x6 filing system ROMs released by ;Opus Supplies and Slogger Software. ;Define any of the following symbols on the BeebAsm command line ;to select build options. ;For example: ; beebasm -i master357.asm.txt -D _DDOS358 ;Floppy drive controller selection _DDOS358 =? 0 ;Built-in interface on Master 128, WD 1770 _DDOS318 =? 0 ;Opus DDOS interface for Model B, WD 2791 _DDOS338 =? 0 ;Opus DDOS interface for Model B, WD 2793 _DDOS328 =? 0 ;Acorn interface on Model B/B+, WD 1770 _DDOS368 =? 0 ;Opus Challenger 3-in-1 unit on Model B, WD 1770 _DDOS378 =? 0 ;Watford WD 1770 controller on Model B, WD 1770 _DDOS388 =? 0 ;Slogger Pegasus 400 controller, WD 1770 _DDOS348 =? 0 ;Opus DDOS interface for Model B, WD 1770 (default) IF _DDOS358 _MASTER =? 1 _MOS320 =? 1 ENDIF ;Operating system selection ;Required by *command processing and *TAPEDISK _MOS350 =? 0 ;MOS 3.50 _MOS320 =? 0 ;MOS 3.20 _MOS120 =? 0 ;MOS 1.00, 1.20, 2.00 (default) ;Symbols to enable RAM disc use. ;Maximum paged RAM supported (some boards fitted with less): _MASTER =? 0 ;Master, 128 KiB; 64 KiB internal + 2x 32 KiB cartridges _DDOSRAM =? 0 ;Model B, 256 KiB on DDOS RAM pack _BMEM =? 0 ;Model B, 128 KiB paged RAM to Greg Cook's BMem recipe _IFEL =? 0 ;Model B, 128 KiB on IFEL RAM/ROM board _WILLIAMS =? 0 ;Model B, 128 KiB on Alan Williams' SWRAM/Flash board _PLUGIN =? 0 ;Model B, 128 KiB on 4x RAM/ROM modules from BooBip.com _PLUGIN2 =? 0 ;Model B, 128 KiB on 4x RAM/ROM modules, alternate layout _PLUGIN3 =? 0 ;Model B, 128 KiB on 4x RAM/ROM modules, alternate layout _BPLUS =? 0 ;Model B+, 76 KiB internal paged RAM _TWOMEG =? 0 ;Model B, 128 KiB on Solidisk Twomeg board IF _BPLUS _RAMINIT76K =? 1 ENDIF _RAMINIT76K =? 0 ;*RAMINIT creates 76 KiB RAM disc catalogue _RAMINIT64K =? 0 ;*RAMINIT creates 64 KiB RAM disc catalogue _RAMINIT256K=? 0 ;*RAMINIT creates 256 KiB RAM disc catalogue _BEEBEM =? 0 ;Get BeebEm to write the last byte of a track ;(NOT FOR HARDWARE) ;An accompanying module to speed up OSGBPB calls is available. ;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 _DDOS358 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) latchd = $20 ;latch XOR mask to select double density _WD279X = 0 ELIF _DDOS318 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) latchd = $40 ;latch XOR mask to select double density _WD279X = 1 ELIF _DDOS338 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) latchd = $40 ;latch XOR mask to select double density _WD279X = 1 ELIF _DDOS328 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) latchd = $08 ;latch XOR mask to select double density _WD279X = 0 ELIF _DDOS368 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) latchd = $20 ;latch XOR mask to select double density _WD279X = 0 ELIF _DDOS378 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) latchd = $01 ;latch XOR mask to select double density _WD279X = 0 ELIF _DDOS388 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) latchd = $08 ;latch XOR mask to select double density _WD279X = 0 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 = $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) latchd = $40 ;latch XOR mask to select double density _WD279X = 0 ENDIF ;DDOS parameters IF _RAMINIT76K ramsiz = $0130 ;RAM disc volume size in sectors to set during *RAMINIT ELIF _RAMINIT64K ramsiz = $0100 ;RAM disc volume size in sectors to set during *RAMINIT ELIF _RAMINIT256K ramsiz = $03FF ;RAM disc volume size in sectors to set during *RAMINIT ELSE ramsiz = $0200 ;RAM disc volume size in sectors to set during *RAMINIT ENDIF ctries = $03 ;number of volume/disc catalogue read/write attempts l3tris = $05 ;number of disc operation attempts at level 3 vtries = $06 ;number of track verify attempts ftries = $03 ;number of track format attempts idtris = $04 ;number of Read ID attempts (must be even) vwtris = $02 ;number of times to verify sector before writing (>0) srom = $0E ;default *OPT 9 setting; ROM slot to page in ;during disc operations dftbid = $01 ;Tube claimant ID for Disc Filing System fset = $05 ;drive parameter slot number for source disc tset = $06 ;drive parameter slot number for destination disc ;Variables in zero-page language area, $0000..$009F zram = $0000 ;OSFILE/OSGBPB control blocks used by Tube host qram = zram +$0012 ;pointer used by Tube host claimt = qram +$0002 ;b7=Tube claimed claime = claimt+$0001 ;b5..b0 = Tube claimant ID of current claimant zptube = claime+$0001 ;Tube BRK handler placed here L0050 = zptube+$003A ;=LBB44-newbr+zptube; modifiable jump to Tube task routine L0053 = zptube+$003D ;=LBB47-newbr+zptube; Tube parasite address ;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 rdidit = $00A0 ;offset of next byte to store in NMI read ID rtxszh = $00A1 ;MSB number of bytes to transfer to RAM disc or current track szcode = $00A1 ;sector size code while preparing format rtxslt = $00A2 ;b7..0=sideways slot number of disc during RAM disc transfer datptr = $00A2 ;data table pointer while preparing format txsizl = $00A3 ;LSB total number of bytes to transfer to floppy disc rtxrom = $00A3 ;*SROM number of paged ROM slot to page in during RAM disc transfer txsizm = $00A4 ;2MSB total number of bytes to transfer to floppy disc runptr = $00A4 ;run length table pointer while preparing format rtxlbl = $00A4 ;LSB starting logical block address (LBA) of RAM disc transfer txsizh = $00A5 ;MSB total number of bytes to transfer to floppy disc rtxlbh = $00A5 ;MSB starting logical block address (LBA) of RAM disc transfer 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 action = $00A8 ;2 bytes; action address of *command linnol = $00A8 ;LSB line number in *BUILD, *LIST or file offset in *DUMP cmdmld = $00A8 ;multiplicand while multiplying by 6 in commands cbcont = $00A8 ;b7=catalogue entry is waiting to be created in *COPY, *COMPACT nlflag = $00A8 ;flag for printing newlines in *CAT linnoh = $00A9 ;LSB line number in *BUILD, *LIST or file offset in *DUMP swaps = $00A9 ;b7=Copying between different discs in same drive (swapping) 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 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 dmpptr = $00AC ;pointer to second private page in *BUILD bldtbl = $00AC ;5 bytes; OSWORD 0 control block in *BUILD ;starting with address of buffer strret = $00AE ;2 bytes; pointer to error message string or VDU sequence synofs = $00AF ;offset of syntax byte of selected *command = 2 + length of name ;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 divenl = $00B0 ;LSB dividend inscat = $00B0 ;offset of insertion point while creating catalogue entry tdflnl = $00B0 ;LSB size of file in *TAPEDISK 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) gbctr = $00B0 ;counter for copying filename in OSGBPB 8 inputl = $00B0 ;LSB decimal word input by user, or tentative volume size muland = $00B0 ;multiplicand vduptr = $00B0 ;2 bytes; pointer to VDU character sequence asklo = $00B0 ;LSB requested size of volume during *FORMAT, *VOLGEN vototl = $00B0 ;LSB total number of sectors allocated to volumes delay = $00B0 ;counter for delay while displaying error indication row = $00B0 ;Teletext row while printing track number divenh = $00B1 ;MSB dividend tdflnh = $00B1 ;MSB size of file in *TAPEDISK inputh = $00B1 ;MSB decimal word input by user, or tentative volume size askhi = $00B1 ;LSB requested size of volume during *FORMAT, *VOLGEN vototh = $00B1 ;MSB total number of sectors allocated to volumes column = $00B1 ;Teletext column while printing track number namofs = $00B2 ;offset of filename to compare with names of open handles o7fcmd = $00B2 ;b5..0=OSWORD $7F command to compare with commands in table divorl = $00B2 ;LSB divisor floorl = $00B2 ;LSB requested size of volume, rounded down to whole tracks freelo = $00B2 ;LSB number of sectors available for allocation to volumes bootfs = $00B3 ;boot option in Y on entry to service call $03 lokcat = $00B3 ;offset of filename to be updated in catalogue namctr = $00B3 ;counter to compare filename with names of open handles stra = $00B3 ;saved A register on entry to print string/append error message divorh = $00B3 ;MSB divisor floorh = $00B3 ;MSB requested size of volume, rounded down to whole tracks freehi = $00B3 ;MSB number of sectors available for allocation to volumes 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 abrtsp = $00B8 ;stack pointer to restore on abort gbusr = $00B8 ;2 bytes; pointer to user memory in OSGBPB hlpcot = $00B8 ;counter of *HELP entries to print skewit = $00B9 ;running track skew counter while formatting 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 work = $00BC ;18 bytes; rearranged copy of OSFILE control block plus temporaries: lodcat = work +$0000 ;temp cat offset in OSFILE $FF wrkcat = work +$0002 ;load/exec/length/start sector in catalogue format page = work +$0002 ;pointer to OSHWM (PAGE) while writing empty BASIC program lodlo = work +$0002 ;LSB load address in OSFILE lodhi = work +$0003 ;3MSB load address in OSFILE vfreel = work +$0004 ;LSB number of free sectors in volume during *STAT exelo = work +$0004 ;LSB exec address in OSFILE ftraks = work +$0004 ;number of tracks on disc in *FORMAT, *VERIFY, *VOLGEN vfreeh = work +$0005 ;MSB number of free sectors in volume during *STAT exehi = work +$0005 ;3MSB exec address in OSFILE volidx = work +$0005 ;volume index in *VOLGEN lenlo = work +$0006 ;LSB file length in OSFILE strtlo = work +$0006 ;LSB start address in OSFILE 0 dfreel = work +$0006 ;LSB number of free sectors on disc during *VOLGEN lenhi = work +$0007 ;2MSB file length in OSFILE strthi = work +$0007 ;3MSB start address in OSFILE 0 dfreeh = work +$0007 ;MSB number of free sectors on disc during *VOLGEN lbahi = work +$0008 ;MSB LBA in OSFILE endlo = work +$0008 ;LSB end address in OSFILE 0 headhi = work +$0008 ;MSB length of current file while fitting new file prodl = work +$0008 ;LSB product of multiplication lbalo = work +$0009 ;LSB LBA in OSFILE endhi = work +$0009 ;3MSB end address in OSFILE 0 headlo = work +$0009 ;LSB number of free sectors after current file while fitting new file prodh = work +$0009 ;MSB product of multiplication todolo = work +$000A ;LSB length in sectors of file being copied lenhl = work +$000A ;MSB length of file while creating catalogue entry todohi = work +$000B ;MSB length in sectors of file being copied wrknam = work +$000B ;current filename srclo = work +$000C ;LSB LBA of next sector to copy from source 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 cpycat = work +$0010 ;offset of catalogue entry in *MCOPY, *COMPACT 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 reptr = $00FD ;2 bytes; pointer to current error message 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 wram = $0128 ;OSWORD control blocks stored by Tube host brkvec = $0202 ;BRKV vector to handle BRK instructions (errors) vtab2 = $0212 ;FILEV, first FS vector claimed by DFS fscvec = $021E ;FSCV vector to control/shut down current FS evtvec = $0220 ;EVNTV vector to handle events bromid = $024B ;BASIC ROM slot number IF _MOS350 fblock = $02ED ;internal OSFILE control block in MOS 3.50 ELIF _MOS320 fblock = $02ED ;internal OSFILE control block in MOS 3.20 ELSE fblock = $02EE ;internal OSFILE control block in MOS 1.xx ENDIF cfsinf = $03B2 ;12 bytes; internal CFS header buffer; NUL-terminated filename cfslod = $03BE ;4 bytes; internal CFS header buffer; load address cfsbnl = $03C6 ;internal CFS header buffer; LSB block number cfsbnh = $03C7 ;internal CFS header buffer; MSB block number cfsbll = $03C8 ;internal CFS header buffer; LSB block length cfsblh = $03C9 ;internal CFS header buffer; MSB block length rtube = $0400 ;language area; Tube host code placed here taddrl = $0406 ;Set up address for Tube sram = $0700 ;language area; Tube host string workspace intnmi = $0D00 ;NMI service routine priptr = $0DF0 ;table of private pages to each ROM ;Shared workspace IF _DDOS358 base = $C000 ;start of absolute workspace ELSE base = $0E00 ;start of absolute workspace ENDIF dirlow = base +$0000 ;first sector of volume catalogue 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 = dirlow+$0100 ;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 mainws = dirhig+$0100 ;when saved/restored in bulk txcall = mainws+$0000 ;data transfer call number 0=read data 1=write data savrom = mainws+$0001 ;*OPT 9 number of paged ROM slot to page in during disc operations rdidrt = mainws+$0002 ;4 bytes; sector ID returned by Read ID command, CHRN voltks = mainws+$0006 ;8 bytes; first track of data area of volumes A..H dscszh = mainws+$000E ;MSB size of disc in sectors (big-endian) dscszl = mainws+$000F ;LSB size of disc in sectors (big-endian) restsp = mainws+$0010 ;stack pointer to restore on command restart restrt = mainws+$0011 ;2 bytes; command restart action address frmret = mainws+$0013 ;return code in *FORMAT, *VERIFY 0=no error 1=stand-alone verify ;failed 2=format failed 3=verify failed after format volszh = mainws+$0014 ;MSB sizes assigned to volumes A..H in *VOLGEN (big-endian) volszl = mainws+$0015 ;LSB sizes assigned to volumes A..H in *VOLGEN (big-endian) cursx = mainws+$0026 ;X position of cursor stasha = mainws+$0027 ;saved A register on entry to OSBPUT stashx = mainws+$0028 ;saved X register on entry to OSBPUT, OSBGET stashy = mainws+$0029 ;saved Y register on entry to OSBPUT, OSBGET tries = mainws+$002A ;retry counter at data transfer level 3 digits = mainws+$002B ;4 bytes; result of binary word to decimal digit conversion inputs = mainws+$0031 ;5 bytes; line buffer during numeric input routine tmpcin = mainws+$0045 ;18 bytes; saved arg ptr, cat attributes and filename tmpcat = tmpcin+$0002 ;load/exec/length/start sector of file being copied namtra = tmpcin+$000B ;filename and directory of file being copied tmpnam = mainws+$0058 ;filename and directory portion of file spec to *COPY dosram = mainws+$0060 ;18 bytes; copy of OSFILE/OSGBPB control block ldlow = mainws+$006D ;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+$007F ;2 bytes; pointer to user's OSGBPB control block ctemp = mainws+$0081 ;transfer direction 0=writing from memory 1=reading to memory tumflg = mainws+$0083 ;$00=transferring to/from host $FF=transferring to/from Tube catdrv = mainws+$0084 ;drive and volume of catalogue in pages 2..3; b7=catalogue invalid expecl = mainws+$0085 ;LSB expected size of bulk data transfer expech = mainws+$0086 ;MSB expected size of bulk data transfer nmiflg = mainws+$0087 ;NMI ownership flag b7=we own NMI prenmi = mainws+$0088 ;previous owner of NMI area frpage = mainws+$0089 ;MSB of OSHWM; lowest page number of user memory hipage = mainws+$008A ;MSB of HIMEM; 1 + highest page number of user memory frsize = mainws+$008B ;number of pages of user memory; = hipage - frpage head = mainws+$008C ;2 bytes; physical track number under heads on drive 0/2, 1/3 speed = mainws+$008E ;track stepping rate in WD 1770 format 0=fast..3=slow fdstat = mainws+$008F ;status of last FDC command, reported by *FDCSTAT rdidbf = mainws+$0090 ;6 bytes; sector ID read by Read ID NMI service routine, CHRN + CRC prsist = mainws+$00C0 ;start of persistent workspace saved to private page colds = mainws+$00C0 ;$FF=allow warm start $00=force cold start memflg = $00C1 ;$FF=we have shared workspace $00=don't have workspace senti1 = mainws+$00C2 ;first workspace sentinel $41=workspace valid dcbmap = mainws+$00C3 ;channel open flags dcbbit = mainws+$00C4 ;channel open bit mask for current open file dcby = mainws+$00C5 ;channel workspace pointer for current open file seqwa = mainws+$00C6 ;MSB of maximum allocation available to current open file seqwb = mainws+$00C7 ;offset of catalogue entry of current open file seqwc = mainws+$00C8 ;not used in _BUGFIX seqwx = mainws+$00C9 ;temporary copy of X register during sequential file ops defqua = mainws+$00CA ;default (CSD) directory character defdsk = mainws+$00CB ;default (CSD) drive (b3..0) and volume (b6..4) libqua = mainws+$00CC ;library directory character libdsk = mainws+$00CD ;library drive (b3..0) and volume (b6..4) catofs = mainws+$00CF ;offset of catalogue entry, 0..$F0, multiple of 8 cursy = mainws+$00CF ;Y position of cursor wildch = mainws+$00D0 ;$23=wildcard characters allowed in filename $FF=no wildcards monflg = mainws+$00D1 ;*OPT 1 monitor 0=verbose $FF=quiet enaflg = mainws+$00D2 ;*ENABLE counter 1=*ENABLE just called 0=current command enabled ;$FF=current command not enabled fdriv = mainws+$00D3 ;source volume in *BACKUP, *COPY tdriv = mainws+$00D4 ;destination volume in *BACKUP, *COPY tubflg = mainws+$00D5 ;b7=Tube data transfer notube = mainws+$00D6 ;$00=Tube coprocessor present $FF=Tube absent (inverted MOS flag) srcopt = mainws+$00D7 ;b7..4=boot option of source volume in *BACKUP, *MCOPY qtemp = mainws+$00D7 ;2 bytes; action address in OSGBPB linadr = mainws+$00D9 ;2 bytes; offsets of command line start and tail; ;pointer to arguments of *RUN, */ command fcbadr = mainws+$00DB ;2 bytes; pointer to user's OSFILE control block etemp = mainws+$00DD ;<$80 "print" to error message >=$80 print to screen ;(if <$80, offset of next error character in page $01) senti2 = mainws+$00DE ;second workspace sentinel $48=workspace valid spregs = mainws+$00E0 ;4 bytes; special registers 0..3 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 regset = mainws+$00E6 ;drive parameters for channel (6 bytes per channel, Y=6*n, n=0..6) ;n=0..4 for channel $11..15 n=5 source drive n=6 destination drive seqmap = mainws+$0100 ;workspaces for channels $11..$15 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 ;page of memory containing open file's sector buffer 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 seqchk = seqmap+$001F ;volume and drive of open file chrn = seqmap+$00A0 ;table of sector headers during *FORMAT slots = seqmap+$0100 ;channel sector buffers rundat = slots +$0112 ;data bytes in RLE formatting table runlen = slots +$0312 ;run lengths in RLE formatting table ;Acorn MOS system ROM IF _MOS350 fswch = $F8A5 ;internal FileSwitch entry point in MOS 3.50 ELIF _MOS320 fswch = $FB69 ;internal FileSwitch entry point in MOS 3.20 ELSE fswch = $FF2D ;internal ROMFSC entry point ENDIF ;Expansion hardware fred = $FC00 ;page of addresses for external MMIO devices ;System hardware sheila = $FE00 ;page of addresses for internal MMIO devices romsw = sheila+$0030 ;ROMSEL paged ROM selection latch r1stat = sheila+$00E0 ;Tube ULA status register 1 r1data = r1stat+$0001 ;Tube ULA FIFO 1 r2stat = r1data+$0001 ;Tube ULA status register 2 r2data = r2stat+$0001 ;Tube ULA FIFO 2 r3stat = r2data+$0001 ;Tube ULA status register 3 r3data = r3stat+$0001 ;Tube ULA FIFO 3 reg3 = sheila+$00E5 ;Tube FIFO 3 r4stat = r3data+$0001 ;Tube ULA status register 4 r4data = r4stat+$0001 ;Tube ULA FIFO 4 ;Floppy drive controller IF _DDOS358 fdc = sheila+$0028 ;base of floppy drive controller registers latch = sheila+$0024 ;floppy drive interface control latch ELIF _DDOS318 fdc = sheila+$0080 ;base of floppy drive controller registers latch = sheila+$0084 ;floppy drive interface control latch ELIF _DDOS338 fdc = sheila+$0080 ;base of floppy drive controller registers latch = sheila+$0084 ;floppy drive interface control latch ELIF _DDOS328 fdc = sheila+$0084 ;base of floppy drive controller registers latch = sheila+$0080 ;floppy drive interface control latch ELIF _DDOS368 fdc = fred +$00F8 ;base of floppy drive controller registers latch = fred +$00FC ;floppy drive interface control latch ELIF _DDOS378 fdc = sheila+$0084 ;base of floppy drive controller registers latch = sheila+$0080 ;floppy drive interface control latch ELIF _DDOS388 fdc = fred +$00C4 ;base of floppy drive controller registers latch = fred +$00C0 ;floppy drive interface control latch ELSE fdc = sheila+$0080 ;base of floppy drive controller registers latch = sheila+$0084 ;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 osgbpb = $FFD1 ;Read or write a block of memory to a sequential file osbput = $FFD4 ;Write byte to sequential file osbget = $FFD7 ;Read byte from sequential file osargs = $FFDA ;Read or set parameters of a sequential file osfile = $FFDD ;Load, save or perform functions on a 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 $38 ;version No. EQUS "Otus DDOS " ;title EQUB $00 ;terminator byte IF _DDOS358 EQUS "3.58" ;version string ELIF _DDOS318 EQUS "3.18" ;version string ELIF _DDOS338 EQUS "3.38" ;version string ELIF _DDOS328 EQUS "3.28" ;version string ELIF _DDOS368 EQUS "3.68" ;version string ELIF _DDOS378 EQUS "3.78" ;version string ELIF _DDOS388 EQUS "3.88" ;version string ELSE EQUS "3.48" ;version string ENDIF .copyr EQUB $00 ;terminator byte EQUS "(C)2024 Otus" EQUB $00 ;terminator byte .osfscm ;Issue Filing System Call JMP (fscvec) EQUW LA899 ;pointer to DDOS version information $A899 .called ;ROM service CMP #$05 ;Service call $05 = unrecognised interrupt BNE not05h ;pass on for service ASAP RTS .not05h CMP #$15 ;Service call $15 = centisecond polling interrupt BEQ nocall ;high volume call, pass without delay CMP #$0E ;Service call $0E = ROMFS get byte BEQ nocall ;high volume call, pass without delay JSR there1 ;service calls $FE, $FF PHA ;else save call type LDA priptr,X ;test private page pointer CMP #$40 IF _DDOS358 BPL plano ;if in range $40..$BF then ignore call ELSE BCS plano ;if in range $40..$FF then ignore call ENDIF PLA ;else restore call type IF _DDOS358 CMP #$21 ;Service call $21 = request absolute HAZEL workspace ELSE CMP #$01 ;Service call $01 = request absolute workspace ENDIF BNE savpri CPY #(>slots)+$05 BCS savpri ;if workspace < 9 pages (+$0E = $17) LDY #(>slots)+$05 ;then reserve 9 pages abs workspace. RTS IF _DDOS358 .sv02h ;Service call $02 = reserve private workspace PHA TYA ;this call cannot offer high memory BMI unset ;in which case turn this ROM off CPY #$0E ;if it wrapped around below $0E BCC unset ;then ignore all further service calls LDA priptr,X ;pointer +ve if in main, zero if no call $22 ;frugal test above ruled out $40..$BF CMP #$DB ;if in range $DB..$FF (no room in HAZEL) OR $00..$3F BPL setpri ;then set it to main memory from Y, else ignore PLA RTS ENDIF .unset LDA #$7F STA priptr,X .plano PLA .nocall RTS .savpri CMP #$02 BCC L806F ;if call no. < $02 then return IF _DDOS358 BEQ sv02h ;if call no. = $02 then reserve private wksp CMP #$22 BNE pmsg ;Service call $22 = request private HAZEL workspace PHA ;save call number CPY #$C0 ;this call cannot offer main or sideways memory BCC unset ;in which case turn this ROM off .setpri TYA STA priptr,X ;save private page pointer in assigned array CPY #$DB ;if no room in HAZEL for our private pages BCS plano ;then don't touch or reserve HAZEL memory ELSE BNE pmsg PHA ;Service call $02 = reserve private workspace TYA ;this call cannot offer high memory BMI unset ;in which case turn this ROM off CPY #$0E ;if it wrapped around below $0E BCC unset ;then ignore all further service calls STA priptr,X ;else save private page pointer in assigned array ENDIF STA blkptr+$01 ;y=lowest free page, store in pointer LDA #$00 ;clear LSB of pointer STA blkptr+$00 LDY #colds-mainws ;clear offset $C0 of page STA (blkptr),Y ;b7=1 iff private page initialised INY ;and offset $C1; y=memflg STA (blkptr),Y ;b7=1 iff we own the shared workspace LDY blkptr+$01 ;y = 2 + address of private page INY ;thus reserving two private pages INY PLA ;restore call number and pass to next ROM. .L806F RTS .pmsg CMP #$04 BEQ chkcom BCS dohelp STY bootfs ;Service call $03 = boot JSR savita ;save boot flag in scratch space 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 #$32 ;else if key pressed is not D BNE L806F ;then exit LDA #$78 ;else register keypress for two-key rollover JSR osbyte .aboot0 .L81DB ;Initialise DDOS and boot default volume LDA bootfs ;get back boot flag (Y on entry to call $3) PHA ;save on stack LDX #$FF STX etemp ;no error message being built print to screen LDA #$FD ;OSBYTE $FD = read/write type of last reset JSR readby ;call OSBYTE with X=0, Y=$FF JSR L8202 ;validate shared workspace BNE L81F0 ;if invalid then initialise shared workspace TXA ;else test type of last reset BEQ L81F3 ;if A=0 then soft break so skip .L81F0 JSR L82D2 ;else initialise shared workspace .L81F3 JSR LA884 ;print DDOS banner JSR pcrlf ;print second newline after banner JSR sinit0 ;initialise DDOS PLA ;if boot flag was >0 BNE L81FF ;then return A=0 to claim call JMP L82F5 ;else examine and boot default volume .L81FF ;Return A=0 ; LDA #$00 ;redundant; returns zero poked by sinit0 RTS ;in savita set by pmsg .chkcom ;Service call $04 = unrecognised *command JSR savita 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 L806F ;if not found then exit STX synofs ;else save pointer to syntax byte in workspace TYA ;save offset of command line tail on stack PHA LDA fscvec+$00 ;test address in FSCV CMP #fswch BNE L80CA JSR LA814 ;else FSCV not intercepted. STX evtptr+$00 ;call OSBYTE $A8 = get extended vector table STY evtptr+$01 ;address in XY. store XY as pointer LDY #$2D ;test sixteenth entry in table = FSCV LDA (evtptr),Y ;if FSCV vector address is not $BF40 CMP #wfscm BNE L80CA .L80C6 PLA ;else restore offset of command line tail TAY .L80C7 JMP (action) ;and execute *command. .dohelp CMP #$09 BNE inifsy JSR savita ;Service call $09 = *HELP JSR setupr ;save AXY, test for keyword BNE L80E9 ;if present then scan keyword LDX #hlptab LDA #$04 ;4 entries to print JMP help1 ;print *HELP keywords and pass on the call. .L80CA ;*command while FSCV does not point to us LDX synofs ;get syntax byte from command table JSR tramp BPL L80C6 ;if b7=0 then execute *command JMP L84F2 ;else restore stack pointer and exit .L80E9 ;Scan *HELP keyword TYA ;y=offset of keyword from GSINIT pointer PHA ;save on stack and transfer to A LDX #hlptab JSR wname0 ;search for keyword in table BCS L80F7 ;if keyword found JSR L80C7 ;then call its action address; print help .L80F7 PLA ;restore string offset TAY .L80F8 JSR gsread ;call GSREAD BCC L80F8 ;until end of argument (discarding it) JSR setupr ;then test for next keyword BNE L80E9 ;if present then scan next *HELP keyword RTS ;else exit .inifsy CMP #$12 BNE relmem CPY #$04 ;Service call $12 = initialise FS BNE unkswz ;if number of FS to initialise = 4 JSR savita ;then save AXY .init ;*DISC / *DISK PHA JSR sinit0 ;initialise DDOS PLA RTS .relmem CMP #$0A BNE msters .releas JSR savita ;Service call $0A = workspace claimed JSR suspri ;set up pointer to private page LDY #memflg ;y = offset = $C1 shared workspace ownership LDA (blkptr),Y ;fetch byte at offset $C1 of private page BPL unkswz ;if b7 clear then we already vacated, exit LDA #$00 ;clear flag at offset $C1 of private page BEQ L8128 ;b7=1 iff we own the shared workspace. .L8125 LDA seqmap,Y ;store bytes $10C0..$11BF in private page .L8128 STA (blkptr),Y ;(wrapped around so that private page INY ;contains: $1100..BF, $10C0..FF) LDA mainws,Y CPY #prsist-mainws BCC L8125 BNE L8128 STA (blkptr),Y ;store $10C0 in private page at offset $C0 JMP ensur ;ensure all files up-to-date on disc (flush) IF _DDOS358 .sv24h DEY ;Service call $24 = private HAZEL workspace count DEY ;bid for two private pages RTS .sv25h ;Service call $25 = filing system info LDX #fsblke-L8719-$01 ;22 bytes to write: .LBFEE LDA L8719,X ;get byte of filing system info STA (linptr),Y ;add to MOS table INY ;increment table offset DEX ;decrement count BPL LBFEE ;loop until 22 bytes written LDX romid ;restore AX, pass updated Y LDA #$25 ENDIF .unkswz RTS .msters IF _DDOS358 CMP #$0F BEQ releas ;if call no. = $0F then release shared wksp CMP #$24 BEQ sv24h ;if call no. = $24 then bid for private wksp 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 isddos ;Service call $27 = reset occurred BCC nreset ;if DDOS not initialised then pass call JSR L958A ;else forget catalogue in pages $0E..F .nreset LDA #$27 ;restore call number .nshut RTS ;pass on service call. .sv26h ;Service call $26 = *SHUT command issued JSR isddos ;if DDOS initialised LDA #$26 JSR savita BCS shut ;then close all files LDY #dcbmap-mainws ;else if no files are open LDA (blkptr),Y AND #$F8 BEQ nshut ;then pass on the call JSR sinit1 ;else initialise DDOS preserving call no. .shut JMP close2 ;close a file/all files and return .isddos ;Test whether DDOS active ;on entry X=romid JSR suspri TYA LDY #colds-mainws PHA LDA (blkptr),Y ASL A ;return C=0 if DDOS uninitialised BCC cold INY ;y=memflg LDA (blkptr),Y ASL A ;return C=1 iff DDOS initialised and active .cold PLA TAY RTS ENDIF .unkwrd CMP #$08 ;if call not $1,2,3,4,8,9,A,FE,FF then return BNE unkswz JSR savit ;Service call $08 = unrecognised OSWORD LDY wordx ;save XY (X will be clobbered on return) STY blkptr+$00 ;set $B0..1 = pointer to OSWORD control block LDY wordy STY blkptr+$01 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 L8166 ;if b7=1 then use previous drive JSR dodriv ;else select drive in A .L8166 INY ;offset 1 = address LDX #$02 JSR shftbo ;copy address to $BE,F,$106F,70 INY ;y = 5 on exit; increment 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: .L8184 INX ;add 3 to X INX INX LDA LB611+$00,X ;get command byte from table BEQ L81A2 ;if the terminator byte then exit CMP o7fcmd ;else compare with OSWORD $7F command BNE L8184 ;if not the same try next entry PHP ;else save interrupt state CLI ;enable interrupts LDA #>(L81A1-$01) ;push return address $81A1 on stack PHA LDA #<(L81A1-$01) PHA LDA LB611+$02,X ;fetch action address high byte PHA ;push on stack LDA LB611+$01,X ;fetch action address low byte PHA ;push on stack RTS ;jump to action address. .L81A1 ;Finish OSWORD $7F PLP .L81A2 JSR nmirel ;release NMI JSR L9338 ;release Tube LDA #$00 ;exit A=0 to claim service call RTS .gtdsks ;OSWORD A <> $7F BCS L81DA ;if A > $7F then exit CPY #$7D ;else if A < $7D BCC L81DA ;then exit JSR setdef ;set current vol/dir = default, set up drive JSR L929E ;load volume catalogue L4 CPY #$7E BEQ getdsz ;OSWORD A = $7D 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 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 .L81DA RTS ;return A = 0, claiming service call. .L8202 ;Validate shared workspace LDA senti1 ;compare first sentinel with "A" CMP #$41 BNE L820E ;return Z=0 if unequal LDA senti2 ;else compare second sentinel with "H" CMP #$48 ;return Z=0 if unequal, Z=0 if both equal .L820E RTS .sinit0 ;Initialise DDOS 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 LA814 ;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 DDOS 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 LDX #$0F ;service call $0F = vectors claimed JSR doswcl ;call OSBYTE $8F = issue service call LDA #$FF STA catdrv ;no catalogue in pages $0E..F JSR suspri ;set up pointer to private page LDY #colds-mainws ;test offset $C0 of private page LDA (blkptr),Y ;b7=1 if private page initialised BPL L8297 ;if b7=0 then initialise private page INY ;else Y=memflg LDA (blkptr),Y ;b7=1 iff we own the shared workspace BMI L828E ;if we already own it then ensure wksp valid JSR getmem ;else claim shared workspace LDY #$00 .init2 LDA (blkptr),Y ;restore bytes $10C0..$11BF from private page CPY #colds-mainws BCC init3 STA mainws,Y BCS init4 .init3 STA seqmap,Y .init4 DEY BNE init2 LDA #$A0 ;set channel workspace pointer = $A0: .init6 TAY ;transfer channel workspace pointer to Y PHA LDA #$3F ;b7=0 PTR not in buffer; b6=0 buf unchanged JSR clrbit ;clear channel flag bits PLA STA seqdah,Y ;set MSB buffer address out of range SBC #$1F ;(to force a read) C=0 subtract $20 BNE init6 ;loop until all buffers discarded .L828E JSR L8202 ;validate shared workspace BEQ L8296 ;if invalid JSR L82D2 ;then initialise shared workspace .L8296 RTS .L8297 ;Initialise private page LDA #$FF ;set offset $C0 of private page = $FF STA (blkptr),Y ;b7=1 iff private page initialised JSR getmem ;claim shared workspace STA colds ;set same flag in shared workspace JSR LA810 ;call OSBYTE $EA = read Tube presence flag TXA EOR #$FF ;invert; 0=tube present $FF=Tube absent STA notube ;save Tube presence flag JSR L828E ;ensure shared workspace is valid LDA #srom ;a=$0E STA savrom ;*SROM E page in ROM slot 14 during disc ops LDY #$00 ;y=$00 STY dcbmap ;no files are open STY nmiflg ;NMI resource is not ours DEY ;y=$FF STY enaflg ;*commands are not *ENABLEd STY monflg ;*OPT 1,0 quiet operation STY etemp ;no error message being built print to screen STY catdrv ;no catalogue in pages $0E..F JMP LB665 ;set track stepping rate from startup options .L82D2 ;Initialise shared workspace LDA #$00 STA t40flg ;*4080 OFF no double-stepping STA defdsk ;set default volume = "0A" STA libdsk ;set library volume = "0A" LDA #$24 STA defqua ;set default directory = "$" STA libqua ;set library directory = "$" LDA #$80 STA denflg ;*DENSITY AUTO LDA #$41 ;set $10C2 first sentinel = "A" STA senti1 LDA #$48 ;set $10DE second sentinel = "H" STA senti2 RTS .L82F5 JSR setdef ;set current vol/dir = default, set up drive JSR dirldy ;load volume catalogue LDY #$00 LDX #$00 LDA option ;get boot option/top bits volume size JSR sfour ;shift A right 4 places BEQ L832C ;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 L832D ;if !BOOT found then boot from it JSR gstrng ;else print "File not found" and return EQUS "File not found" EQUB $0D EQUB $0D NOP .L832C RTS .L832D CMP #$02 BCC L833F ;if boot option = 1 then load !BOOT BEQ L8339 ;if boot option = 2 then run !BOOT LDX #exec ;point XY to "E.!BOOT" BNE L8343 ;call OSCLI .L8339 LDX #run BNE L8343 ;call OSCLI .L833F LDX #load .L8343 JMP oscli ;call OSCLI .load EQUS "L.!BOOT" EQUB $0D .exec EQUS "E." .run EQUS "!BOOT" EQUB $0D IF _DDOS358 ;Filing system information block, in reverse .L8719 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 .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 L839C ;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 L8394 ;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 .L8394 JSR pcrlf ;print newline .type9 LDA #$00 ;OSFIND $00 = close file JMP osfind ;call OSFIND and exit .L839C JMP nofil ;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 L839C ;if file not found raise error LDX romid ;else get our ROM slot number LDA priptr,X ;get address of our private page STA dmpptr+$01 ;set up pointer INC dmpptr+$01 ;point to second private page .dump3 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 LDA #$07 ;set counter, 8 bytes to print STA dmpptr+$00 ;set up pointer LDX #$00 ;offset = 0 for indexed indirect load .dump2 JSR osbget ;call OSBGET BCS dump4 ;if EOF then finish STA (dmpptr,X) ;else store byte in 2nd private page JSR bytout ;print hex byte JSR pspace ;print a space DEC dmpptr+$00 ;decrement counter BPL dump2 ;loop until line complete CLC ;c=0, end of file not reached .dump4 PHP ;save carry flag BCC dumpb ;if not EOF then print full ASCII column .L83DF JSR gstrng ;else print "** " EQUS "** " LDA #$00 ;clear rest of row with NULs STA (dmpptr,X) ;to print dots in ASCII column DEC dmpptr+$00 ;decrement counter BPL L83DF ;loop until hex column complete .dumpb LDA #$07 ;set counter, 8 bytes to print STA dmpptr+$00 ;set up pointer .dump6 LDA (dmpptr,X) ;get saved byte from file 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 DEC dmpptr+$00 ;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 INC linnoh .dump9 PLP ;restore carry flag from OSBGET BCC dump3 ;if not at end of file then print next row BCS type9 ;else close file and exit .build ;*BUILD 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 .L8421 JSR iprdec ;increment and print BCD word JSR pspace ;print a space LDX romid ;get our ROM slot number LDY priptr,X ;get address of our private page INY ;point to second private page STY 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 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 BEQ build3 .build2 LDA (bldtbl,X) ;get character of line JSR osbput ;call OSBPUT INC bldtbl+$00 ;increment low byte of pointer .build3 LDA bldtbl+$00 ;get low byte of pointer CMP lineln ;compare with length of line BNE build2 ;if not at end of line then loop PLP ;else restore flags from OSWORD BCS build4 ;if user escaped from input then finish LDA #$0D ;else A = carriage return JSR osbput ;write to file JMP L8421 ;and loop to build next line .build4 JSR ackesc ;acknowledge ESCAPE condition JSR type9 ;close file: .pcrlf ;Print newline PHA LDA #$0D JSR pchr ;print character in A (OSASCI) PLA RTS .chkdsf ;Select source volume JSR savita ;save AXY LDX fdriv ;set X = source volume LDA #$00 ;a=$00 = we want source disc BEQ L8481 ;branch (always) .chkdst ;Select destination volume JSR savita ;save AXY LDX tdriv ;set X = destination volume LDA #$80 ;a=$80 = we want destination disc .L8481 PHA ;save A STX fdrive ;set wanted volume as current volume JSR LA7D6 ;set up for current drive PLA ;restore A BIT swaps ;if disc swapping required BMI L848D ;then branch to prompt .L848C RTS ;else exit .L848D CMP tindrv ;compare wanted disc with disc in drive BEQ L848C ;if the same then do not prompt STA tindrv ;else wanted disc is going into drive JSR gstrng ;print "Insert " EQUS "Insert " NOP BIT tindrv ;if b7=1 BMI L84AD ;then print "destination" JSR gstrng ;else print "source" EQUS "source" BCC L84BC ;and branch (always) .L84AD JSR gstrng ;print " destination" EQUS "destination" NOP .L84BC JSR gstrng ;print " disk and hit a key" EQUS " disk and hit a key" NOP JSR L84EA ;wait for keypress JMP pcrlf ;print newline and exit .getyn ;Ask user yes or no JSR L84EA ;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 .L84EA ;Wait for keypress JSR clrkey ;call *FX 15,1 = clear input buffer JSR osrdch ;call OSRDCH, wait for input character BCC L84F5 ;if ESCAPE was pressed .L84F2 LDX abrtsp ;then abort our routine TXS ;clear our stacked items, return to caller .L84F5 RTS .L84F6 ;Restore parameters of source drive LDY #fset ;use slot of invalid channel $16 BNE L84FC ;restore drive parameters of open file .L84FA ;Restore parameters of destination drive LDY #tset ;use slot of invalid channel $17: .L84FC ;Restore drive parameters of open file JSR L852C ;multiply Y by 6 LDX #$00 .L8501 LDA regset,Y ;copy from parameter set $10E6..B,Y STA spregs,X ;to current drive parameters $10E0..5 INY INX CPX #$06 ;loop until 6 bytes copied BNE L8501 RTS .L850E ;Save parameters of source drive JSR savita ;save AXY LDY #fset ;use slot of invalid channel $16 BNE L851A ;save drive parameters of open file .L8515 ;Save parameters of destination drive JSR savita ;save AXY LDY #tset ;use slot of invalid channel $17: .L851A ;Save drive parameters of open file JSR L852C ;multiply Y by 6 LDX #$00 .L851F LDA spregs,X ;copy from current drive parameters $10E0..5 STA regset,Y ;to parameter set $10E6..B,Y INY INX CPX #$06 ;loop until 6 bytes copied BNE L851F RTS .L852C ;Multiply Y by 6 LDA cmdmld ;save content of $A8 PHA TYA ;a=y STA cmdmld ;save in temp ASL A ;multiply A by two ADC cmdmld ;add starting value making 3x ASL A ;double the result making 6x TAY ;put back in Y PLA ;restore content of $A8 STA cmdmld 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 L8638 ;load source volume catalogue LDA #$00 STA voltrk ;data area starts on track 0 JSR L850E ;save parameters of source drive JSR L8619 ;return volume size in XY/boot option in A STA srcopt ;save source volume boot option STX todolo STY todohi JSR L8632 ;load destination volume catalogue LDA #$00 STA voltrk ;data area starts on track 0 JSR L8515 ;save parameters of destination drive LDA denflg-spregs+regset+(fset*$06) ;get density of source drive EOR denflg-spregs+regset+(tset*$06) ;xor with density flag of destination drive AND #$40 ;extract bit 6 density flag, ignore auto b7 BEQ L85B1 ;if the same density then skip LDA #$D5 ;else error number = $D5 JSR LA3EF ;begin error message, number in A JSR fstrng ;print error message EQUS "Both disks MUST be same density" EQUB $0D EQUB $0A EQUS "Hint...use MCOPY" EQUB $00 ;terminator byte $00 raises error .L85B1 JSR L8619 ;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 L85C3 ;if dest < source then raise error BNE L85E7 ;if dest > source then proceed CPX todolo ;else compare LSBs dest - source BCS L85E7 ;if dest >= source then proceed .L85C3 LDA #$D5 ;else error number = $D5 JSR LA3EF ;begin error message, number in A LDA fdriv ;get source drive JSR L8D08 ;print " Drive " plus volume spec in A JSR gstrng ;print " larger than " EQUS " larger than " LDA tdriv ;get destination drive JSR L8D08 ;print " Drive " plus volume spec in A JMP LA3B9 ;terminate error message, raise error .L85E7 JSR mvdkda ;copy source drive/file to destination BIT denflg ;test density flag BVS L8608 ;if double density then update disc catalogue JSR chkdst ;else select destination volume JSR dirldy ;load volume catalogue PLA ;pop MSB destination volume size AND #$0F ;mask bits 0..3 ORA srcopt ;apply source boot option in bits 4..5 STA dirhig+$06 ;store in catalogue PLA ;pop LSB destination volume size STA dirhig+$07 ;store in catalogue JSR dirout ;write volume catalogue JMP L86BA ;and do a NEW for BASIC ;[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. .L8608 ;Update disc catalogue JSR LA6D9 ;load disc catalogue L3 PLA ;pop MSB disc size STA dcszhi ;store in disc catalogue PLA ;pop LSB disc size STA dcszlo ;store in disc catalogue JSR LA6DD ;write disc catalogue L3 JMP L86BA ;and do a NEW for BASIC .L8619 ;Return volume size in XY/boot option in A 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 BIT denflg ;test density flag BVC L862E ;if double density LDX dscszl ;then load disc size from workspace instead LDY dscszh .L862E PLA ;return disc size in XY AND #$F0 ;return boot option in A bits 5 and 4 RTS .L8632 ;Load destination volume catalogue JSR chkdst ;select destination volume JMP dirldy ;load volume catalogue L4 .L8638 ;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 L850E ;save parameters of source drive .L8653 STY catptr ;save cat. offset of found file in zero page JSR prtinf ;print *INFO line LDX #$00 .L865A LDA wrknam,X ;save file spec in workspace STA tmpnam,X LDA catlow,Y ;copy matching filename+dir to current file STA wrknam,X STA namtra,X ;and to workspace LDA cathig,Y ;copy matching file's catalogue information STA wrkcat-$01,X ;to OSFILE block? STA tmpcat,X ;and to workspace INX INY CPX #$08 ;loop until 8 bytes of each field copied BNE L865A LDA wrkcat-$01+$06 ;get top bits exec/length/load/start sector JSR isolen ;extract b5,b4 of A STA lenhl-$01 ;set MSB length LDA wrkcat-$01+$04 ;get LSB length CMP #$01 ;set C=1 iff file includes partial sector LDA wrkcat-$01+$05 ;get 2MSB length ADC #$00 ;round up to get LSB length in sectors STA todolo LDA lenhl-$01 ;get extracted MSB length ADC #$00 ;carry out to get MSB length in sectors STA todohi LDA tmpcat+$07 ;get LSB start LBA (also at $C4) STA srclo LDA tmpcat+$06 ;get top bits exec/length/load/start sector AND #$03 ;extract b1,b0 of A STA srchi ;store MSB start LBA LDA #$FF STA cbcont ;catalogue entry is waiting to be created JSR mvdkda ;copy source drive/file to destination JSR chkdsf ;select source volume JSR L929E ;load volume catalogue L4 LDX #$07 .L86A8 LDA tmpnam,X ;restore file spec to current file STA wrknam,X DEX BPL L86A8 ;loop until 8 characters copied LDY catptr ;restore cat. offset of found file STY catofs ;to workspace JSR next ;find next matching file BCS L8653 ;loop while match found, else: .L86BA ;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 ;$0D = first byte of end-of-program marker STA (page),Y ;store at start of user memory INY ;$FF = second byte of end-of-program marker LDA #$FF ;store in user memory STA (page),Y RTS .regen ;Create destination catalogue entry JSR swpfcb ;swap $BC..CD with $1045..56 JSR chkdst ;select destination volume LDA qualif ;save current directory PHA JSR L929E ;load volume catalogue L4 JSR lookup ;search for file in catalogue BCC copyfe ;if found JSR delfil ;then delete catalogue entry .copyfe PLA ;restore current directory STA qualif ;set as current directory JSR L8515 ;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 LDA wrkcat+$06 ;get top bits exec/length/load/start sector AND #$03 ;extract b1,b0 of A PHA ;save MSB start sector LDA wrkcat+$07 ;get LSB start sector PHA ;save LSB start sector JSR swpfcb ;swap $BC..CD with $1045..56 PLA STA dstlo ;store LSB start sector PLA STA dsthi ;store MSB start sector RTS .swpfcb ;Swap work with tmpcin LDX #$11 .L870B LDY tmpcin,X LDA work,X STY work,X STA tmpcin,X DEX BPL L870B RTS .mvdkda ;Copy source drive/file to destination LDA #$00 STA lodlo ;set LSB load address = 0 STA lenlo ;set LSB transfer size = 0 .cpyfl5 LDA todolo ;compare remaining file size TAY ;- available memory CMP frsize LDA todohi SBC #$00 BCC sizet ;if remainder fits then Y=file size in pages LDY frsize ;else Y=size of available memory in pages .sizet STY lenhi ;set MSB transfer size = no. pages in Y LDA srclo ;set LBA = source volume LBA STA lbalo LDA srchi STA lbahi LDA frpage ;set MSB load address = start of user memory STA lodhi JSR L84F6 ;restore parameters of source drive JSR chkdsf ;select source volume JSR L9228 ;set high word of OSFILE load address = $FFFF JSR L901D ;read extended file L5 BIT cbcont ;if catalogue entry is waiting to be created BPL cpyfl9 JSR regen ;then create destination catalogue entry LDA #$00 STA cbcont ;no catalogue entry waiting to be created .cpyfl9 LDA dstlo ;set LBA = destination volume LBA STA lbalo LDA dsthi STA lbahi LDA frpage ;set MSB save address = start of user memory STA lodhi JSR L84FA ;restore parameters of destination drive JSR chkdst ;select destination volume JSR L9228 ;set high word of OSFILE load address = $FFFF JSR L9023 ;write extended file L5 LDA lenhi ;add transfer size to destination volume LBA CLC ADC dstlo STA dstlo BCC cpyfl6 ;carry out to high byte INC dsthi .cpyfl6 LDA lenhi ;add transfer size to source volume LBA CLC ADC srclo STA srclo BCC cpyfl7 ;carry out to high byte INC srchi .cpyfl7 SEC LDA todolo ;get LSB remaining size of file in sectors SBC lenhi ;subtract number of pages transferred STA todolo ;update LSB remaining size BCS L87C6 DEC todohi ;borrow in from MSB remaining size if req'd .L87C6 ORA todohi ;or MSB with LSB, Z=0 if pages remain BNE cpyfl5 ;if file transfer is incomplete then loop RTS .shftbo ;Copy address to (work,hiwork)+X JSR shfttw ;copy low word to zero page DEX ;backtrack destination offset DEX JSR shftt0 ;copy high word to workspace: .shftt0 ;copy byte of high word to workspace LDA (blkptr),Y STA hiwork-$02,X INX ;increment source and destination offsets INY RTS .shfttw ;copy low word to zero page: JSR shfton .shfton ;copy byte of low word to zero page LDA (blkptr),Y STA work,X INX ;increment source and destination offsets INY RTS .L8FEA ;Set current file from argument JSR chksyn ;call GSINIT with C=0 and require argument .getnam ;Set current file from file spec JSR LA4F0 ;set current volume and dir = default JMP getnm0 ;parse file spec .frmnam ;Set current file from argument pointer JSR LA4F0 ;set current volume and dir = default: .frmnm1 ;Parse file spec from argument pointer LDA work+$00 ;copy argument pointer to GSINIT pointer STA linptr+$00 LDA work+$01 STA linptr+$01 LDY #$00 ;set Y = 0 offset for GSINIT JSR setupr ;call GSINIT with C=0: .getnm0 ;Parse file spec JSR L8866 ;set current filename to all spaces JSR gsread ;call GSREAD BCS namerr ;if argument empty then "Bad filename" CMP #$3A ;else is first character ":"? BNE L8829 ;if not then skip to dir/filename JSR gsread ;else a drive is specified, call GSREAD BCS L8863 ;if no drive number then "Bad drive" JSR LA4BF ;else set current drive from ASCII digit JSR gsread ;call GSREAD BCS namerr ;if only drive specified then "Bad filename" CMP #$2E ;else if next character is "." BEQ L8824 ;then get first character of filename JSR LA4CB ;else set volume from ASCII letter JSR gsread ;call GSREAD BCS namerr ;if only volume spec'd then "Bad filename" CMP #$2E ;if separator character "." missing BNE namerr ;then raise "Bad filename" error .L8824 JSR gsread ;call GSREAD, get first character of filename BCS namerr ;if filename is empty then "Bad filename" .L8829 STA wrknam+$00 ;else save first character of filename LDX #$00 ;set filename offset = 0 JSR gsread ;call GSREAD, get second filename character BCS getnm4 ;if absent then process one-character name INX ;else offset = 1 CMP #$2E ;is the second character "."? BNE L8842 ;if not then read in rest of leaf name LDA wrknam+$00 ;else first character was a directory spec JSR LA580 ;set directory from ASCII character JSR gsread ;call GSREAD, get first character of leaf name BCS namerr ;if leaf name is empty then "Bad filename" DEX ;else offset = 0, read in leaf name: .L8842 CMP #$2A ;is filename character "*"? BEQ L887C ;if so then process "*" in filename CMP #$21 ;else is it a control character or space? BCC namerr ;if so then raise "Bad filename" error STA wrknam,X ;else store character of filename INX ;point X to next character of current filename JSR gsread ;call GSREAD, get next character of leaf name BCS L887B ;if no more then filename complete, return CPX #$07 ;else have seven characters been read already? BNE L8842 ;if not then loop, else: .namerr ;Raise "Bad filename" error. JSR illmsg EQUB $CC EQUS "filename" EQUB $00 .L8863 JMP LA511 ;raise "Bad drive" error. .L8866 ;Set current filename to all spaces LDA #$20 .L8868 LDX #$00 BEQ L8874 ;branch (always) .getnm4 ;Process one-character filename LDA wrknam+$00 ;if filename is "*", then: CMP #$2A BNE L887B .L8872 ;Pad current filename with "#"s LDA #$23 ;x=offset of end of filename .L8874 STA wrknam,X INX CPX #$07 BNE L8874 .L887B RTS .L887C ;Process "*" in filename JSR gsread ;call GSREAD BCS L8872 ;if end of argument pad filename with "#"s CMP #$20 ;else if next character is space BEQ L8872 ;then pad filename with "#"s BNE namerr ;else raise "Bad filename" error. .chksam ;Ensure disc not changed JSR savita ;save AXY LDA cycno ;get cycle number of last catalogue read JSR L929E ;load volume catalogue L4 CMP cycno ;compare with freshly loaded cycle number BEQ L887B ;return if equal, else: .dskchn ;Raise "Disk changed" error. JSR vstrng EQUB $C8 EQUS "Disk changed" EQUB $00 .prtnam ;Print filename from catalogue JSR savita ;save AXY LDA modify,Y ;get directory character PHP ;save N = lock attribute AND #$7F ;extract ASCII character BNE ptnam2 ;if NUL then file is in CSD JSR pdspc ;so print two spaces BEQ ptnam3 ;branch (always) .ptnam2 JSR pchr ;else print directory character JSR pdot ;print a dot .ptnam3 LDX #$06 ;repeat 7 times: .ptnam0 LDA catlow,Y ;get character of leaf name AND #$7F ;mask bit 7 JSR pchr ;print character INY DEX BPL ptnam0 ;and loop JSR pdspc ;print two spaces LDA #$20 ;a = space PLP ;restore lock attribute in N BPL ptnam1 ;if lock bit set LDA #$4C ;then A = capital L .ptnam1 JSR pchr ;print attribute character JMP pspace ;print a space and exit .yspace ;Print number of spaces in Y JSR pspace ;print a space DEY ;loop until Y = 0 BNE yspace RTS .L88E1 ;Prepare extended file transfer LDX wrkcat+$06 ;x = MSB of relative LBA LDA #$00 ;set MSB length = 0; transfer less than 64 KiB STA txsizh BEQ L88F6 .atot ;Prepare ordinary file transfer LDA wrkcat+$06 ;get top bits exec/length/load/start sector JSR isolen ;extract b5,b4 of A STA txsizh ;?$A5 = b17..16 (MSB) of length LDA wrkcat+$06 ;x = b9..8 (MSB) of relative LBA AND #$03 TAX .L88F6 LDA wrkcat+$00 ;copy user data address to NMI area STA nmiusr+$00 LDA wrkcat+$01 STA nmiusr+$01 LDA wrkcat+$05 ;copy 2MSB length STA txsizm LDA wrkcat+$04 ;copy LSB length STA txsizl STX txlbah ;store MSB of LBA LDA wrkcat+$07 ;copy LSB of LBA STA txlbal LDA sectrk ;get number of sectors per track BEQ loksuc ;if not defined then just use the LBA LDA voltrk ;else get first track of current volume STA track ;set track number for transfer DEC track ;decrement, to increment at start of loop LDA wrkcat+$07 ;get LSB of relative LBA: .trsca SEC ;set C=1 to subtract without borrow: .trscb INC track ;increment track number SBC sectrk ;subtract sectors-per-track from LBA BCS trscb ;loop until LSB borrows in DEX ;then decrement MSB of relative LBA BPL trsca ;loop until MSB borrows in/underflows ADC sectrk ;add sectors per track to negative remainder STA sector ;set sector number. .loksuc RTS .setwld ;Allow wildcard characters in filename LDA #$23 BNE L8931 .clrwld ;Disallow wildcard characters in filename LDA #$FF .L8931 STA wildch RTS .L8FE1 ;Ensure file matching wildcard argument JSR setwld ;allow wildcard characters in filename .L8FE4 ;Ensure file matching argument JSR chksyn ;call GSINIT with C=0 and require argument .getlok ;Ensure file matching spec in catalogue JSR getnam ;set current file from file spec JMP errlok ;ensure matching file in catalogue .frmlok ;Ensure file matching argument in catalogue JSR frmnam ;set current file from argument pointer: .errlok ;Ensure matching file in catalogue JSR lookup ;search for file in catalogue BCS loksuc ;if found then return .nofil ;Raise "Not found" error JSR filmsg ;else raise "File not found" error. EQUB $D6 EQUS "not found" EQUB $00 .fsc09 ;FSC 9 = *EX JSR savita ;save AXY JSR stxylp ;set GSINIT pointer to XY, set Y=0 .ex JSR setupr ;call GSINIT with C=0 JSR setwld ;allow wildcard characters in filename JSR L8868 ;a=$23; set current filename = "#######" JSR LA4F0 ;set current volume and dir = default JSR readd0 ;parse directory spec (Y presv'd from GSINIT) JSR errlok ;ensure matching file in catalogue BCS info0 ;always branch .fsc10 ;FSC 10 = *INFO JSR savita ;save AXY JSR stxylp ;set GSINIT pointer to XY, set Y=0 LDX #infcom JSR L8F28 ;set up trampoline to read *INFO entry LDY #$00 ;set Y = 0 offset for GSINIT .info ;*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 .info0 JSR prtinf ;print *INFO line JSR next ;find next matching file BCS info0 ;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 L896D ;and jump into search loop (always) .next ;Find next matching file LDY catofs ;set Y = catalogue pointer .L896D 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: .L897A LDA wrknam,X ;get character of current filename CMP wildch ;compare with wildcard mask BEQ L898F ;if ='#' and wildcards allowed accept char JSR caps ;else set C=0 iff character in A is a letter EOR catlow-$01,Y ;compare with character in catalogue BCS L898B ;if character in current filename is letter AND #$DF ;then ignore case .L898B AND #$7F ;ignore bit 7, Z=1 if characters equal BNE L8998 ;if not equal then test next file .L898F DEY ;loop to test next (previous) char of name DEX BPL L897A ;if no more chars to test then files match STY catofs ;save cat. offset of found file in workspace SEC ;return C=1 file found RTS .L8998 ;catalogue entry does not match file spec DEY ;advance catalogue pointer to next file DEX BPL L8998 BMI L896D ;loop until file found or not .delfil ;Delete catalogue entry JSR chkopl ;ensure file not locked or open (mutex) .dellop LDA catlow+$08,Y ;copy next file's entry over previous entry STA catlow+$00,Y ;shifting entries up one place 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 .infrts RTS .inform ;Print *INFO line if verbose BIT monflg ;test *OPT 1 setting BMI infrts ;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 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 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 $1062,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 .maker ;OSFILE 7 = create file, 11 = create w/ stamp JSR dirdo ;create file from OSFILE block JSR tryfl0 ;set up pointer to user's OSFILE block .chukbk ;Return catalogue information to OSFILE block LDA #$01 ;return A=$01 from OSFILE 7/11 JSR savita ;save AXY TYA ;save catalogue pointer on stack PHA TAX ;and copy to X LDY #$12 ;clear bytes at offsets 2..17 LDA #$00 .chukb7 DEY STA (blkptr),Y CPY #$02 ;end with Y = offset 2 = LSB load address BNE chukb7 .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 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 STA (blkptr),Y ;store in OSFILE block .chukb3 LDA cathig+$06,X ;get top bits exec/length/load/start sector 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 ;PA43 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 L8A51 ;then save both as b1,0 of 2MSB LDA #$FF ;else set MSB and 2MSB = $FF. STA (blkptr),Y INY .L8A51 STA (blkptr),Y PLA ;discard byte on stack RTS .chukb4 ;Copy two bytes from catalogue to OSFILE block JSR chukb6 .chukb6 LDA cathig,X STA (blkptr),Y INX INY RTS .L8A60 ;*STAT LDX #$00 ;x=0, nothing specified JSR LA4E5 ;select specified or default volume TXA ;test bit 0 of X AND #$01 ;if X=3 drive and volume specified BNE L8AA7 ;then stat specified volume, else: .L8A6D ;*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 #$00 ;data transfer call $00 = read data STA txcall ;set data transfer call number JSR LA5A4 ;detect disc format/set sector address ;[BUG]NMI area use before claim ;if last disc double density BIT denflg ;test density flag BVS L8A83 ;if double density then *STAT eight volumes .L8AA7 ;*STAT specified volume JSR dirld ;ensure current volume catalogue loaded JSR L8C00 ;print disc type and volume list .L8D16 ;Print volume statistics LDY #$03 ;y=3 print 2 spaces/ 1 space LDA fdrive ;get current volume JSR L8D08 ;print " Drive " plus volume spec in A JSR yspace ;print number of spaces in Y JSR gstrng EQUS "Volume size " NOP LDA dirhig+$07 ;copy volume size to sector count STA divenl ;LSB LDA dirhig+$06 ;get boot option/top bits volume size AND #$03 ;mask top bits volume size STA divenh ;store MSB JSR L8D72 ;print sector count as kilobytes JSR L8D7B ;print "K" JSR pcrlf ;print newline LDY #$0B ;set Y = $0B print 11 spaces JSR yspace ;print number of spaces in Y JSR gstrng ;print "Volume unused" EQUS "Volume unused " NOP JSR LA1EA ;calculate free space on volume LDA vfreel ;copy result to sector count STA divenl LDA vfreeh STA divenh JSR L8D72 ;print sector count as kilobytes JSR L8D7B ;print "K" JMP pcrlf ;print newline .L8A83 ;*STAT eight volumes JSR L8C00 ;print disc type and volume list LDX #$00 ;for each volume letter A..H: .L8A88 LDA voltks,X ;test if number of tracks in volume > 0 BEQ L8A9A ;if = 0 then no such volume, skip TXA ;save volume counter PHA JSR pcrlf ;print newline JSR dirld ;ensure current volume catalogue loaded JSR L8D16 ;print volume statistics PLA ;restore volume counter TAX .L8A9A 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 L8A88 RTS .L8AB0 ;*XCAT JSR LA4E5 ;select specified or default volume LDA fdrive ;get current volume AND #$0F ;extract drive number STA fdrive ;set current volume letter to A LDA #$00 ;data transfer call $00 = read data STA txcall ;set data transfer call number JSR LA5A4 ;detect disc format/set sector address ;[BUG]NMI area use before claim ;if last disc double density BIT denflg ;test density flag BVS L8ACB ;if double density then *CAT eight volumes JMP L9563 ;else *CAT the single volume .L8ACB ;*CAT eight volumes JSR L8C00 ;print disc type and volume list LDX #$00 ;for each volume letter A..H: .L8AD0 LDA voltks,X ;test if number of tracks in volume > 0 BEQ L8AE5 ;if = 0 then no such volume, skip TXA ;save volume counter PHA JSR dirld ;ensure current volume catalogue loaded JSR L8BD6 ;print volume title JSR L8C7F ;print volume spec and boot option JSR L8B02 ;list files in catalogue PLA ;restore volume counter TAX .L8AE5 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 L8AD0 ;if not then loop RTS .L8AF2 ;Print "No file" JSR LA3D2 ;print VDU sequence immediate EQUB $0D ;newline EQUB $0A EQUS "No file" EQUB $0D ;newline EQUB $0A EQUB $FF RTS .L8B02 ;List files in catalogue LDA dirlen ;get number of files in catalogue * 8 BEQ L8AF2 ;if catalogue empty then print "No file" LDY #$FF STY nlflag ;else print a newline before first entry INY STY catqua ;CSD printed first, directory char = NUL .cat0 CPY dirlen ;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 EOR defqua ;compare with default (CSD) directory 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 LDY #$00 ;y=$00, start at first file entry JSR findir ;find unlisted catalogue entry BCS L8B9F ;if none remaining then finish catalogue .L8B31 STY catptr ;save catalogue pointer LDX #$00 ;set filename offset = 0 .L8B35 LDA catlow,Y ;copy name and directory of first entry AND #$7F ;with b7 clear STA dosram,X ;to workspace INY INX CPX #$08 ;loop until 8 characters copied BNE L8B35 .cattry 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 LDA catlow+$06,Y ;get character of entry SBC dosram,X ;subtract character of workspace DEY ;loop until 7 characters compared DEX BPL catsbc JSR step7 ;add 7 to Y LDA modify,Y ;get directory character (MSB) of entry AND #$7F ;mask off lock bit SBC dosram+$07 ;subtract directory character in workspace BCC L8B31 ;if entry < wksp then copy entry to wksp JSR step ;else add 8 to Y BCS cattry ;and loop (always) .scand 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 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 .L8B9F ;finish catalogue LDA #$FF STA catdrv ;forget catalogue in pages $0E..F JMP pcrlf ;print newline .nxtcat ;Find next unlisted catalogue entry JSR step ;add 8 to Y .findir ;Find unlisted catalogue entry CPY dirlen ;if catalogue pointer beyond last file BCS findx ;then return C=1 LDA catdun,Y ;else test first character of leaf name BMI nxtcat ;if b7=1 then already listed, skip .findx RTS ;else return C=0, catalogue pointer in Y .L8BB5 ;Print volume spec in A (assuming DD) BIT findx ;set V=1 BVS L8BBD ;always print volume letter B..H after drive .L8D08 ;Print " Drive " plus volume spec in A JSR gstrng EQUS " Drive " NOP .L8BBA ;Print volume spec in A BIT denflg ;test density flag .L8BBD 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 L8BCE ;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 L8BCF ;if volume letter is not A then print it .L8BCE RTS ;else exit .L8BCF 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 .L8BD6 ;Print volume title LDY #$0B ;set y = $0B print 11 spaces JSR yspace ;print number of spaces in Y .cat8 LDA dirlow,Y ;y=0; if Y=0..7 get char from sector 0 CPY #$08 ;if Y=8..11 BCC cat9 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 gstrng ;print " (" EQUB $0D EQUS " (" LDA cycno ;get BCD catalogue cycle number JSR bytout ;print hex byte JSR gstrng ;print ")" +newline EQUS ")" EQUB $0D NOP RTS .L8C00 ;Print disc type and volume list LDA #$83 ;teletext char $83 = yellow alphanumerics JSR pchr ;print character in A (OSASCI) LDA fdrive ;get current volume AND #$0C ;extract bits 2,3 of drive number BNE L8C70 ;if drive number more than 3 print "RAM Disk" BIT denflg ;else test density flag BVS L8C19 ;if double density print "Double density" JSR gstrng ;else print "Single density" EQUS "Sing" BCC L8C21 .L8C19 JSR gstrng EQUS "Doub" NOP .L8C21 JSR gstrng EQUS "le density" NOP LDA #$87 ;teletext char $87 = white alphanumerics JSR pchr ;print character in A (OSASCI) LDY #$0E ;set Y = 14 spaces for single density BIT denflg ;test density flag BVC L8C59 ;if single density skip list of volumes LDY #$05 ;else Y = 5 spaces for double density JSR yspace ;print number of spaces in Y LDX #$00 ;set volume index = 0, start at volume A: .L8C42 CLC ;clear carry for add 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 L8C4D ;if volume present print its letter LDA #$ED ;else A=$ED + $41 = $2E, ".": .L8C4D 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 L8C42 ;if not then loop LDY #$01 ;else Y=1 space separating volume list: .L8C59 BIT t40flg ;test double-stepping flag BPL L8C6D ;if set manually (*4080 ON/OFF) then end line BVC L8C6D ;if 1:1 stepping was detected then end line JSR yspace ;else print 1 or 14 spaces JSR gstrng ;print "40in80" EQUS "40in80" NOP .L8C6D JMP pcrlf ;print newline .L8C70 ;Print "RAM Disk" JSR LA3D2 ;print VDU sequence immediate EQUS "RAM Disk" EQUB $FF JMP pcrlf ;print newline .L8C7F ;Print volume spec and boot option LDY #$0D ;set Y = $0D print 13 spaces LDA fdrive ;get current volume JSR L8D08 ;print " Drive " plus volume spec in A JSR yspace ;print number of spaces in Y JSR gstrng ;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 gstrng ;print " (" EQUS " (" LDY #$03 ;4 characters to print ASL A ;multiply boot option by 4 ASL A TAX ;transfer to X for use as offset .cat5 LDA opttab,X ;get character of boot option descriptor JSR pchr ;print character in A (OSASCI) INX ;increment offset DEY ;decrement count BPL cat5 ;loop until 4 characters printed JSR gstrng ;print ")" + newline EQUS ")" EQUB $0D NOP RTS .L8CB7 ;Print CSD and library directories JSR gstrng ;print "Directory :" EQUS " Directory :" LDY #$06 ;6 characters in next field LDA defdsk ;get default volume JSR L8BB5 ;print volume spec in A (assuming DD) JSR pdot ;print a dot LDA defqua ;get default directory JSR pchr ;print character in A (OSASCI) JSR yspace ;print number of spaces in Y JSR gstrng ;print "Library :" EQUS "Library :" LDA libdsk ;get library volume JSR L8BB5 ;print volume spec in A (assuming DD) JSR pdot ;print a dot LDA libqua ;get library directory .L8CF2 JSR pchr ;print character in A (OSASCI) JMP pcrlf ;print newline ;Table of boot option descriptors 0..3 .opttab EQUS "off" EQUB $00 EQUS "LOAD" EQUS "RUN" EQUB $00 EQUS "EXEC" .L8D7B ;Print "K" JSR gstrng EQUS "K" NOP RTS .L8D81 ;Divide word by 4 LSR divenh ROR divenl LSR divenh ROR divenl .return ;Return from unrecognised keyword RTS ;DFS command table .comtab EQUS "4080" EQUB >LB401, b7=1 EQUS "ACCESS" EQUB >access, (L) EQUS "BACKUP" EQUB >cpydsk, EQUS "COMPACT" EQUB >compct,) EQUS "COPY" EQUB >cpyfil, EQUS "DELETE" EQUB >delete, EQUS "DENSITY" EQUB >LB3D7, b7=1 EQUS "DESTROY" EQUB >destry, EQUS "DIR" EQUB >set, ) EQUS "DRIVE" EQUB >drive, ) EQUS "ENABLE" EQUB >enable,ex, ) .infcom EQUS "INFO" EQUB >info, EQUS "LIB" EQUB >slib, ) EQUS "MCOPY" EQUB >L9E2D, b7=1 EQUS "RENAME" EQUB >rename, EQUS "SROM" EQUB >L905D, b7=1 EQUS "STAT" EQUB >L8A60, ) EQUS "TAPEDISK" EQUB >L936C, b7=1 EQUS "TITLE" EQUB >title, EQUS "WIPE" EQUB >wipe, <wipe EQUB $02 ;syntax $2: <afsp> EQUS "XCAT" EQUB >L8AB0, <L8AB0 EQUB $0A ;syntax $A: (<drv>) ;DDOSX command table .L8E35 EQUS "COPYRIGHT" EQUB >LBE42, <LBE42 EQUB $80 ;syntax $0: no arguments b7=1 EQUS "FDCSTAT" EQUB >LB56C, <LB56C EQUB $80 ;syntax $0: no arguments b7=1 EQUS "RAMINIT" EQUB >LB437, <LB437 EQUB $80 ;syntax $0: no arguments b7=1 EQUS "ROMID" EQUB >L942D, <L942D EQUB $80 ;syntax $0: no arguments b7=1 EQUB >defcom,<defcom ;unrecognised command, *RUN it $94D7 ;Utility command table .initbl EQUS "BUILD" EQUB >build, <build EQUB $01 ;syntax $1: <fsp> EQUS "DISC" EQUB >init, <init EQUB $00 ;syntax $0: no arguments EQUS "DUMP" EQUB >dump, <dump EQUB $01 ;syntax $1: <fsp> EQUS "FORMAT" EQUB >LA8A1, <LA8A1 EQUB $8D ;syntax $D: <argument> 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 >LAB47, <LAB47 EQUB $8A ;syntax $A: (<drv>) b7=1 ;entry not printed in *HELP UTILS EQUS "DISK" EQUB >init, <init EQUB $00 ;syntax $0: no arguments EQUB >return,<return ;unrecognised utility, return $8EC6 ;*HELP keyword table .hlptab EQUS "DDOS" EQUB >help, <help EQUB $00 EQUS "DFS" EQUB >help, <help EQUB $00 EQUS "UTILS" EQUB >pmhelp,<pmhelp EQUB $00 EQUS "DDOSX" EQUB >L9FF0, <L9FF0 EQUB $00 EQUB >return,<return ;unrecognised keyword, return $8EC6 .wname0 ;Search for command or keyword in table ;on entry A=string offset (=Y to GSINIT) ;XY=address of table JSR L8F28 ;set up trampoline to read table at XY PHA ;save string offset .L8ECB PLA ;restore offset of start of command line TAY PHA JSR setupr ;call GSINIT with C=0 LDX #$00 ;start at current trampoline address JSR tramp ;fetch first byte SEC ;if terminator,empty keyword matches anything BMI L8F14 ;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 BPL minus ;loop until terminator reached INX ;skip action address, 2 bytes INX PLP ;is the command an abbreviation or a mismatch? BNE L8EFD ;if mismatch then skip syntax, scan next kywd JSR tramp ;else test syntax byte BPL L8F10 ;if b7=0 accept cmd, else abbrev. not allowed: .L8EFD INX ;skip syntax byte JSR L8F37 ;add X to trampoline address JMP L8ECB ;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 JMP L8EFD ;skip syntax byte and scan next keyword .L8F10 ;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 .L8F14 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. .L8F28 ;Set up trampoline to read table at XY PHA LDA #$BD ;$BD = LDA abs,X .L8F2B 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 .L8F37 ;Add X to trampoline address CLC TXA ADC tramp+$01 ;add X to low byte of LDA,X address STA tramp+$01 BCC L8F41 ;carry out to high byte INC tramp+$02 .L8F41 RTS .stxylp ;Set GSINIT pointer to XY, set Y=0 STX linptr+$00 STY linptr+$01 LDY #$00 RTS .wipe ;*WIPE JSR L8FE1 ;ensure file matching wildcard argument .qdel0 JSR prtnam ;print filename from catalogue JSR gstrng EQUS " : " NOP LDA modify,Y ;test lock bit BPL L8F61 ;if unlocked then ask to delete JSR LA3FD ;else deletion not allowed, print letter N JMP qdel2 ;find next matching file .L8F61 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 L8FFF ;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 L8FE4 ;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 L8FE1 ;ensure file matching wildcard argument .L8F90 JSR prtnam ;print filename from catalogue JSR pcrlf ;print newline JSR next ;find next matching file BCS L8F90 ;loop until all matching files listed JSR gstrng EQUB $0D EQUS "Delete (Y/N) ? " NOP JSR getyn ;ask user yes or no BEQ L8FB7 ;if user replies yes then proceed JMP pcrlf ;else print newline and exit .L8FB7 JSR chksam ;ensure disc not changed JSR lookup ;search for file in catalogue .L8FBD LDA modify,Y ;unlock catalogue entry! AND #$7F STA modify,Y JSR delfil ;delete catalogue entry JSR L8FFF ;subtract 8 from catalogue pointer JSR next ;find next matching file BCS L8FBD JSR dirout ;write volume catalogue JSR gstrng ;print "Deleted" and exit EQUB $0D EQUS "Deleted" EQUB $0D NOP RTS .L8FF0 ;Pack b17,16 of length into catalogue entry JSR lfour ;shift A left 4 places 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 RTS .L8FFF ;Subtract 8 from catalogue pointer LDY catofs ;get catalogue pointer JSR unstep ;subtract 8 from Y STY catofs ;store catalogue pointer RTS .saver ;OSFILE 0 = save file 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 loadt0 ;branch (always) .reloc LDA cathig+$06,Y ;get top bits exec/length/load/start sector STA wrkcat+$06 JSR decodl ;expand 18-bit load address to 32-bit .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 $9B5C) 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 LDA #$01 ;a=$01, unused ;(appears in A on exit from OSFILE 0/$FF ;but these calls define no return value) JSR chkget ;transfer data and report errors L4 JMP L9338 ;release Tube and exit .L901D ;Read extended file L5 ;called only from $877E JSR rdint ;prepare to read to user memory JMP L9026 ;transfer extended file L5 .L9023 ;Write extended file L5 ;called only from $87A2 JSR wtint ;prepare to write from user memory .L9026 ;Transfer extended file L5 JSR L88E1 ;prepare extended file transfer LDA #$01 ;a=$01, unused JMP chkget ;transfer data and report errors L4 .drive ;*DRIVE JSR LA4E5 ;select specified or default volume LDA fdrive ;get current volume STA defdsk ;set as default volume RTS .set ;*DIR LDX #$00 ;point to default drive and directory EQUB $AD ;$AD=LDA abs; skip next two bytes .slib ;*LIB LDX #$02 ;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 L9050 JSR readd0 ;parse directory spec .L9050 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 .L905D ;*SROM JSR chksyn ;call GSINIT with C=0 and require argument JSR gsread ;call GSREAD, get first argument character JSR LA4A0 ;convert ASCII hex digit to binary BCS L9073 ;if invalid then raise "Bad command" error PHA ;else save digit JSR gsread ;call GSREAD; if more characters in argument BCC L9073 ;then raise "Bad command" error PLA ;else get back digit STA savrom ;set *SROM slot number and exit. RTS .L9073 JMP LB9CC ;raise "Bad command" error .title ;*TITLE JSR chksyn ;call GSINIT with C=0 and require argument JSR setdef ;set current vol/dir = default, set up drive JSR L929E ;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 .titset INX ;x=$FF, set X=0 offset of first character JSR gsread ;call GSREAD BCS titend ;if end of argument write catalogue JSR titwit ;else store character of title CPX #$0B ;is this the twelfth character written? BCC titset ;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 STA dirhig-$08,X ;then store at $0F00..3, X=8..11 RTS .titllw STA dirlow,X ;else store at $0E00..7, X=0..7 RTS .access ;*ACCESS JSR setwld ;allow wildcard characters in filename JSR L8FEA ;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 .L90B2 STX attrib ;then attribute mask = $00, file unlocked JSR errlok ;ensure matching file in catalogue .acces1 JSR chkopn ;ensure file not open (mutex) 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 L90B2 ;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 .noroom ;Raise "Disk full" error. JSR dskmsg EQUB $C6 EQUS "full" 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 .genfil ;Create catalogue entry LDA #$00 STA lbahi ;set MSB of LBA = 0 JSR L9F8E ;return no. reserved sectors in data area STA lbalo ;set as LSB of LBA LDY dirlen ;get number of files in catalogue * 8 CPY #$F8 ;if there are already 31 files BCS dirful ;then raise "Catalogue full" error, else: JSR dskspc ;test if new file will fit at current LBA JMP spachk ;jump into loop .spalop BEQ noroom ;if cat ptr = 0 then raise "Disk full" error JSR unstep ;else subtract 8 from Y JSR dskadr ;test if new file will fit after current file .spachk TYA ;test if catalogue pointer > 0 BCC spalop ;if file won't fit then test prev cat entry STY inscat ;else insert new catalogue entry here LDY dirlen ;point Y to last valid catalogue entry: .moveup CPY inscat ;compare pointer with insertion point BEQ L9165 ;stop copying if insertion point reached LDA catlow-$01,Y ;else copy current catalogue entry STA catlow+$07,Y ;to next slot 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) .L9165 JSR encode ;compose top bits exec/length/load/start JSR L91E3 ;write filename+dir into catalogue at Y=0..$F0 .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 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 .dirful ;Raise "Cat full" error. JSR vstrng EQUB $BE EQUS "Catalogue full" EQUB $00 .dskadr ;Test if new file will fit after current file LDA cathig+$06,Y ;get top bits exec/length/load/start sector JSR isolen ;extract b5,b4 of A = MSB length STA headhi ;save length in zero page (big-endian) CLC LDA #$FF ;subtract 1 from LSB length ADC cathig+$04,Y ;setting C=1 if file includes partial sector LDA cathig+$07,Y ;add LSB start LBA + 2MSB length + C ADC cathig+$05,Y ;=LSB LBA after last sector of file STA lbalo ;save LBA in zero page (big-endian) LDA cathig+$06,Y ;get top bits exec/length/load/start sector AND #$03 ;extract MSB start LBA ADC headhi ;add MSB start LBA + MSB length + C STA lbahi ;=MSB LBA after last sector of file: .dskspc ;Test if new file will fit at current LBA LDA lbalo ;save LSB current LBA PHA SEC ;subtract LSBs LBA of file - current LBA LDA cathig-$01,Y SBC lbalo STA headlo ;=LSB no. free sectors after file LDA cathig-$02,Y ;get top bits exec/length/load/start sector AND #$03 ;extract MSB start LBA SBC lbahi ;subtract MSB current LBA TAX ;=MSB no. free sectors after file ORA headlo ;are there any free sectors? BNE L91D4 ;if so compare with file sector size CLC ;else return C=0 file won't fit. BCC L91DF ;[BUG] empty files don't fit on a full disc .L91D4 LDA #$00 ;set C=1 if LSB length = 0, no borrow: CMP lenlo LDA headlo ;subtract LSB free sectors - 2MSB length SBC lenhi TXA ;subtract MSB free sectors - MSB length SBC lenhl ;return C = 1 if file fits in free space .L91DF PLA ;restore LSB current LBA STA lbalo RTS .L91E3 ;Write filename+dir into catalogue at Y=0..$F0 LDX #$00 .L91E5 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 L91E5 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 .enable ;*ENABLE LDA #$01 ;set *ENABLE flag = 1; will be nonnegative STA enaflg ;(after FSC 8) for next *command only. RTS .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 L922C ;so set high word = high word of tube address PLA ;else discard the high word: PLA .L9228 ;Set high word of OSFILE load address = $FFFF PHA LDA #$FF PHA .L922C ;Set high word of OSFILE load address STA ldlow+$02 PLA STA ldlow+$03 PLA RTS .decode ;Expand 18-bit exec address to 32-bit 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 decde0 LDA #$FF ;then a host address, set high word = $FFFF STA exlow+$03 .decde0 STA exlow+$02 ;else set 2MSB parasite address $0..2FFFF RTS .rename ;*RENAME JSR clrwld ;disallow wildcard characters in filename JSR L8FEA ;set current file from argument LDA fdrive ;get current volume PHA 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 L8FEA ;set current file from argument PLA ;restore current volume CMP fdrive ;compare with destination volume BEQ L926C ;if equal then rename the file JMP LB9CC ;else rename across volumes, "Bad command". .L926C JSR lookup ;search for file in catalogue BCC L927C ;if not found then update filename+dir JSR filmsg ;else raise "File exists" error. EQUB $C4 EQUS "exists" EQUB $00 .L927C ;Update filename+dir in catalogue LDY lokcat ;get pointer to file entry JSR L91E3 ;write filename+dir into catalogue: .dirout ;Write volume catalogue L4 CLC ;add 1 to BCD catalogue cycle number SED LDA cycno ADC #$01 STA cycno CLD JSR wrint0 ;set $1000 = write, claim NMI JMP dirot1 ;transfer volume catalogue and exit .dirld ;Ensure current volume catalogue loaded LDA catdrv ;get drive and volume of loaded catalogue CMP fdrive ;compare with current drive and volume BNE L929E ;if unequal then load volume catalogue JSR LB9FB ;load FDC status register BEQ L92C3 ;if motor is on then finish .L929E JSR savita ;else save AXY .dirldy ;Load volume catalogue L4 JSR rdint0 ;set $1000 = read, claim NMI: .dirot1 ;Transfer volume catalogue LDA #$00 STA tubflg ;transferring to host, not Tube JSR LA7D6 ;set up for current drive JSR LA5A4 ;detect disc format/set sector address LDA fdrive ;get current volume STA catdrv ;set drive and volume of loaded catalogue JSR L92D3 ;transfer disc/volume catalogue L3 BEQ L92C3 ;if zero status release NMI and exit AND #$40 BNE L92C0 ;else if b6=0 WD1770 S6 = write protect .dskflt ;Raise "Disk fault" error JSR dskmsg EQUB $C5 EQUS "fault" EQUB $00 .L92C0 .LA338 ;Raise "Disk read only" error JSR dskmsg EQUB $C9 EQUS " read only" EQUB $00 .L92C3 JMP nmirel ;release NMI and exit. .L92CE ;Write disc/volume catalogue L3 LDA #$01 ;data transfer call $01 = write data STA txcall ;set data transfer call number .L92D3 ;Transfer disc/volume catalogue L3 JSR savit ;save XY JSR L92ED ;set data pointer to $0E00 LDX #ctries ;set X = $03, three possible attempts: .L92DB LDA #$00 ;512 bytes to transfer STA rtxszl LDA #$02 STA rtxszh JSR LB467 ;transfer data L2 BEQ L92EC ;if zero status then success, return DEX ;else decrement attempts counter BNE L92DB ;if not tried 3 times then try again DEX ;else return Z=0, failed .L92EC RTS .L92ED ;Set data pointer to $0E00 LDA #<dirlow STA nmiusr+$00 LDA #>dirlow STA nmiusr+$01 RTS .chktub ;Open Tube data transfer channel PHA ;a=Tube service call, save in stack LDA lodlo ;reform address at $106D..70 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 ($10D6=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 .L9338 ;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 L9360 ;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 LDA #$01 ;data transfer call $01 = write data .L9360 STA txcall ;set data transfer call number JSR nmicla ;claim NMI JMP L958A ;forget catalogue in pages $0E..F .L936C ;*TAPEDISK JSR LA2E2 ;set XY to GSINIT pointer + Y STX fblock+$00 ;store filename address in MOS OSFILE block STY fblock+$01 JSR getlsz ;get start and size of user memory LDA frpage ;get start of user memory STA fblock+$03 ;set as load address of OSFILE block LDA #$00 ;loading starts on page boundary STA fblock+$02 ;[BUG] loads to coprocessor memory! JSR L9409 ;clear top of MOS OSFILE block LDX #$0C ;x = $0C 1200 baud LDA #$8C ;OSBYTE $8C = *TAPE JSR osbyte ;call OSBYTE LDA #$FF ;a=$FF load file JSR L9402 ;call OSFILE using MOS OSFILE block LDX #<L9416 ;point XY to *DISC command LDY #>L9416 JSR oscli ;call OSCLI LDA #<cfsinf ;point filename field to last read CFS block STA fblock+$00 ;$03B2 begins with NUL-terminated filename LDA #>cfsinf STA fblock+$01 LDX #$00 .L93A5 LDA cfsinf,X ;scan filename of last CFS block read BEQ L93AF ;until NUL found INX CPX #$07 ;or 7 characters scanned BNE L93A5 ;(no allowance for directory specifiers) .L93AF LDA #$0D ;terminate filename with CR STA cfsinf,X JSR L941B ;calculate CFS file length LDA tdflnl ;round up to whole number of pages in $B1 BEQ L93BD INC tdflnh .L93BD LDA frsize ;compare available memory - file size CMP tdflnh BCS L93D7 ;if file fit in memory then continue JSR filmsg ;else "File size too large" error (too late!) EQUB $D4 EQUS "size too large" EQUB $00 .L93D7 JSR L941B ;calculate CFS file length LDA #$00 ;set OSFILE start address = start of user mem STA fblock+$0A LDA frpage STA fblock+$0B CLC ;set end address = start addr + length of file ADC tdflnh STA fblock+$0F LDA tdflnl STA fblock+$0E LDA #$00 ;a=$00 save file JSR L9402 ;call OSFILE using MOS OSFILE block LDX #$07 ;8 bytes to copy, 7..0: .L93F7 LDA cfslod,X ;copy load and exec addresses from CFS block STA fblock+$02,X ;to OSFILE block DEX BPL L93F7 LDA #$01 ;a=$01 write catalogue information: .L9402 ;Call OSFILE using MOS OSFILE block LDX #<fblock LDY #>fblock JMP osfile ;call OSFILE and exit .L9409 ;Clear top of MOS OSFILE block LDX #$04 ;start at top half of load address field LDA #$00 ;a=$00 write zeroes .L940D STA fblock,X ;clear load, exec, length, attribute fields INX CPX #$11 BCC L940D RTS .L9416 EQUS "DISC" ;*DISC command string EQUB $0D .L941B ;Calculate CFS file length LDA cfsbll ;get LSB length of last block of file STA tdflnl ;store LSB length of file CLC ;set c=0 LDA cfsbnl ;get LSB number of last block ADC cfsblh ;add MSB length of file (0 or 1) STA tdflnh ;store MSB length of file RTS .L942D ;*ROMID LDA LA899+$00 ;get version number integer part CLC ;c=0 to enable space padding JSR LA2B4 ;print hex byte, C=0 if space-padded JSR pdot ;print a dot LDA LA899+$01 ;get version number decimal part JSR bytout ;print hex byte JSR pdspc ;print two spaces LDY #$02 ;repeat with offset 2..4: .L9442 LDA LA899,Y ;get byte of release date dd/mm/yy JSR bytout ;print hex byte INY CPY #$05 ;move on at offset 5 BEQ L9455 LDA #$2D ;else print a dash JSR pchr ;print character in A (OSASCI) JMP L9442 ;and loop .L9455 JSR pspace ;print a space CLC ;c=0 to enable space padding LDX #$03 ;repeat with offsets 5..7: .L945B LDA LA899,Y ;get byte of release number JSR LA2B4 ;print hex byte, C=0 if space-padded INY ;increment offset DEX ;decrement counter BNE L945B ;loop until 3 bytes printed JMP pcrlf ;print newline and exit .wfscm ;FSC CMP #fschtb-fscltb ;if call outside range 0..8 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 TXA CMP #$04 ;is it *OPT 4? BEQ booto ;if so go and set boot option CMP #$02 ;else is it *OPT 0 or *OPT 1? BCC setmon ;if so go and set monitoring option JSR illmsg ;else raise "Bad option" error. EQUB $CB EQUS "option" EQUB $00 .setmon ;*OPT 0 / *OPT 1 monitor 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 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. .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 BEQ L94D8 ;always branch .defcom ;FSC 3 with *command not in table ASL A ;ensure A is even: .L94D8 STA fscno ;save call number (<> $0B from FSC 3) 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 LDA libqua ;get library directory STA qualif ;set as current directory LDA libdsk ;get library drive and volume JSR dodriv ;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 ;command binary not found LDA #$0B ;FSC 11 = *RUN from library CMP fscno ;if not already serving FSC 11 BEQ LB9CC LDX linptr+$00 ;then XY = address of command line LDY linptr+$01 JMP osfscm ;issue Filing System Call .LB9CC 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 ;save it in $10D9,A 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 ($10D6=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 $8D8A LDY #>comtab LDA #$00 ;zero offset, *command starts at XY JSR wname0 ;search for command or keyword in table TSX STX abrtsp ;save stack pointer to restore on abort JMP L80C7 ;execute command .wdcat ;FSC 5 = *CAT JSR stxylp ;set GSINIT pointer to XY, set Y=0 JSR LA4E5 ;select specified or default volume .L9563 JSR dirld ;ensure current volume catalogue loaded JSR L8BD6 ;print volume title JSR L8C00 ;print disc type and volume list JSR L8C7F ;print volume spec and boot option JSR L8CB7 ;print CSD and library directories JMP L8B02 ;list files in catalogue .wfdie ;FSC 6 = new filing system starting up 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 L958A DEC enaflg ;then enable this command, not the ones after .L958A LDA #$FF STA catdrv ;no catalogue in pages $0E..F RTS .vlook ;Ensure open file still in drive JSR setq ;set current vol/dir from open filename .vlook3 ;Ensure open file still on current drive 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 L95BE ;if file not found then raise "Disk changed" STY seqwb ;else save offset in catalogue LDA cathig+$06,Y ;get top bits exec/length/load/start sector LDX cathig+$07,Y ;put LSB start sector in X 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 L95BE ;if not equal then raise "Disk changed" error TXA ;else compare low bytes of start sector (LBA) CMP seqloc,Y BNE L95BE ;if not equal then raise "Disk changed" error RTS ;else exit .L95BE JMP dskchn ;raise "Disk changed" error .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, if valid ($11..17) BCC vshut ;then close file JMP wfeof1 ;else raise "Channel" error. .close2 ;Close all files JSR wfdie ;close *SPOOL/*EXEC files .shtall LDA #$A0 ;set channel workspace pointer = $A0: .shtal0 TAY ;transfer channel workspace pointer to Y JSR vshut ;close file SEC ;subtract $20 to point to next channel SBC #$20 BNE shtal0 ;if >0 then loop (close 5 files $A0..20). RTS .vshut ;Close file L7 PHA JSR cheeky ;validate workspace offset BCS L9622 ;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 L9622 JSR vlook ;then ensure open file still in drive LDA seqflg,Y ;if EXT changed AND #$20 BEQ L961F LDX seqwb ;then set X = catalogue pointer LDA seqlla,Y ;copy low word of EXT to length in catalogue STA cathig+$04,X LDA seqlma,Y STA cathig+$05,X LDA seqlha,Y ;get high byte of EXT JSR L8FF0 ;pack b17,16 of length into catalogue entry JSR dirout ;write volume catalogue LDY dcby ;put channel workspace pointer in Y .L961F JSR bflush ;ensure buffer up-to-date on disc L6 .L9622 LDX seqwx ;restore X on entry PLA ;restore A on entry .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 L9732 ;find unused file handle BCC L9653 ;if all file handles in use .tmopen JSR vstrng ;then raise "Too many files open" error. EQUB $C0 EQUS "Too many files open" EQUB $00 .L9653 LDX #<wrknam ;point XY+A to current filename LDA #>wrknam TAY JSR L9753 ;compare filename at XY+A with open filenames BCC L9674 ;if file not open then continue .find2 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 L966F ;then this is also safe; continue ;NB loop is redundant; can BPL Q674 instead .filopn JSR filmsg ;else reopening a r-o channel r-w is conflict EQUB $C2 ;raise "File open" error. EQUS "open" EQUB $00 .L966F JSR L976A ;find any other channels open on this file BCS find2 ;if another channel found then loop .L9674 JSR clrwld ;disallow wildcard characters in filename JSR lookup ;search for file in catalogue BCS L9694 ;if not found LDA #$00 ;then preset A=0, no file handle to return PLP ;if opening for read or update BVC L9682 ;(i.e. OSFIND call no. b6=1) BEQ nofile ;else existing file was expected ;return A=0 if OSFILE call no. b3=0 JMP nofil ;else raise "File not found" error .L9682 PHP LDX #$07 ;else opening new file for output. .vnewl STA wrkcat,X ;clear load, exec, start and length = 0 STA hiwork,X DEX BPL vnewl LDA #$40 ;initial length = $4000 = 16 KiB STA endhi JSR dirdo ;create file from OSFILE block .L9694 TYA ;transfer catalogue pointer to X TAX PLP PHP BVS L969D ;if opening for output (OSFIND b6=0) JSR chklok ;then ensure file not locked .L969D LDY dcby ;put channel workspace pointer in Y .L96A0 LDA catlow,X ;copy name and attributes of file STA seqcat,Y ;to bottom half of channel workspace INY LDA cathig,X STA seqcat,Y INY INX TXA ;loop until 8 byte pairs copied AND #$07 BNE L96A0 LDX #$10 ;a=0 .L96B6 STA seqmap,Y ;clear top half of channel workspace INY DEX BNE L96B6 LDY dcby ;put channel workspace pointer in Y TYA STA seqdah,Y ;set buffer address out of range ;(to force a read) JSR sfive ;shift A right 5 places, A=1..5, C=0 ADC #(>slots)-$01 ;add 17; A=$12..16 STA seqbuf,Y ;store page number of channel buffer 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 ADC #$FF ;c=0 from $96CB; c=1 if 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 L972B ;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 TYA ;set A=workspace pointer JSR sfive ;shift A right 5 places PHA ;save A=1..5 TAY DEY ;select parameter slot 0..4 JSR L851A ;save drive parameters of open file PLA ORA #$10 ;return A=file handle $11..15. RTS .L972B ;opening for output 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) .L9732 ;Find unused file handle LDA dcbmap ;get channel open flags LDX #$04 ;test up to 5 channel bits: .L9737 ASL A ;shift next channel open flag into carry BCC L973E ;if C=0 channel unused, calculate ptr+mask DEX ;else loop until 5 channels tested BPL L9737 RTS ;if C=1 all channels in use, none free .L973E ;Calculate workspace pointer and bit mask LDA L974E,X ;x=0..4. get workspace pointer from table STA dcby ;return in workspace pointer variable LDA #$04 ;set b2=1, becoming b3..7=1: .L9746 ASL A ;shift bit mask left by X+1 places DEX BPL L9746 STA dcbbit ;return in bit mask variable. RTS ;Table of channel workspace pointers for file handles $15..11 .L974E EQUB $A0 EQUB $80 EQUB $60 EQUB $40 EQUB $20 .L9753 ;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 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: .L9762 STX dcbofs ASL dcbsr ;shift next channel open flag into carry BCS L9774 ;if C=1 channel open then compare names BEQ L9772 ;if no more channels open exit C=0, else: .L976A ;no match LDA dcbofs ;add $20 to channel workspace pointer CLC ADC #$20 TAX BCC L9762 ;and loop to test next channel (always) .L9772 CLC RTS .L9774 LDA seqchk,X ;get volume of open file EOR fdrive ;compare with current volume BNE L976A ;if unequal then no match LDA #$08 ;else set counter = 8 STA namctr LDY namofs ;put offset in Y: .L9781 LDA (namptr),Y ;get character of filename to compare EOR seqcat,X ;compare with char of open filename AND #$7F ;ignore bit 7 BNE L976A ;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 DEC namctr ;decrement counter BNE L9781 ;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 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 L97DA ;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 .L97A4 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 (returns Z=1) BEQ ensur0 ;branch (always) .L97DA ;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 #LA856-LA853 ;if call number was $02..$FE BCS wargsr ;then return LDA LA856,Y ;else get action address high byte PHA ;save on stack LDA LA853,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 L97A4 ;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 LBFBA ;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 LA476 ;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 seqlla,Y ;else set PTR = EXT STA seqpl,Y LDA seqlma,Y STA seqpm,Y LDA seqlha,Y STA seqph,Y JSR vstarc ;set b7 according to $111C..D,Y .L9826 LDA #$00 ;a = $00 filler byte JSR vbput ;write byte to end of file JSR scmp ;compare EXT - request BCC L9826 ;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 L998F ;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 L9882 ;if equal LDA vslbal ;get back LSB new buffer address CMP seqdal,Y ;compare LSB current buffer address BNE L9882 ;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 .L97BE ;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 STX seqwx ;save X in workspace TYA ;transfer workspace offset to A AND #$E0 ;mask bits 7..5, offset = 0..7 * $20 STA dcby ;save channel workspace pointer BEQ chekyz ;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 L988B-$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 chekyb .chekyz SEC ;then return C=1 .chekyb PLA ;else return C=0 .L9882 RTS ;Table of channel workspace pointers for file handles $11..15 .L9886 EQUB $20 EQUB $40 EQUB $60 EQUB $80 EQUB $A0 ;Table of channel open bit masks for file handles $11..18 .L988B EQUB $80 EQUB $40 EQUB $20 EQUB $10 EQUB $08 EQUB $04 EQUB $02 EQUB $01 .dcrypt ;Convert file handle to channel pointer PHA ;save A TYA ;if Y outside range $10..17 CMP #$10 BCC L989D CMP #$18 BCC L989F .L989D LDA #$08 ;then return Y=0, C=1 .L989F ASL A ;else multiply Y by 32 ASL A ;yielding $00..E0 ASL A ASL A ASL A TAY ;transfer to Y as index PLA ;restore A on entry RTS .dcrych ;Ensure file handle valid and open PHA TYA SEC SBC #$11 ;subtract lowest valid handle; if result <0 BCC wfeof1 ;then raise "Channel" error CMP #$05 ;else if result >= 5 BCS wfeof1 ;then raise "Channel" error TAY ;else transfer to Y as offset 0..4 LDA L9886,Y ;get channel workspace pointer from table STA dcby ;save in temporary location LDA L988B,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 RTS .wfeof1 ;Raise "Channel" error JSR vstrng EQUB $DE EQUS "Channel" EQUB $00 .illeof ;Raise "EOF" error JSR vstrng EQUB $DF EQUS "EOF" EQUB $00 .wbget ;OSBGET 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 L991B ;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 .vbgetb LDA seqpl,Y ;get LSB of PTR STA work+$00 ;set LSB of buffer pointer LDA seqbuf,Y ;get MSB buffer pointer from channel workspace STA work+$01 ;set MSB of buffer pointer JSR L9A9D ;increment PTR LDY #$00 ;set Y=0 for indirect indexed load LDA (work),Y ;get byte from channel buffer at old PTR CLC ;c=0, not at EOF: .L991B LDX stashx ;restore X and Y on entry LDY stashy EOR #$00 ;set N and Z according to A 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 .LBFBA 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 LDA dcby ;get channel workspace pointer JSR sfive ;shift A right 5 places TAY DEY ;y=0..4 for handles $11..15 JSR L84FC ;copy channel's drive parameters to $10E0..5 JSR LA7D6 ;set up for current drive LDY dcby ;put channel workspace pointer in Y LDA seqbuf,Y ;get MSB address of buffer in shared wksp STA lodhi JSR L9228 ;set high word of buffer address = $FFFF LDA #$00 STA lodlo ;clear LSB 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 blkwr ;write ordinary file L5 LDA #$BF ;b6=0 buffer not changed .L998F 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 blkrd ;read ordinary file 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 ;Write byte JSR savita ;save AXY JMP wbput1 .wbput ;OSBPUT 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 #$04 JSR pcmp ;compare PTR - allocated length BNE notful ;if within allocation then write JSR vlook3 ;else ensure open file still on current volume LDX seqwb ;get offset of file in catalogue 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 PHA ;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 seqwa ;store MSB maximum available allocation JSR L8FF0 ;pack b17,16 of length into catalogue entry LDA seqwa ;get MSB maximum available allocation CMP seqeh,Y ;compare MSB length of file per workspace BNE vok ;if not equal then extend file PLA ;else restore LSB maximum available allocation CMP seqem,Y ;compare 2MSB length of file per workspace BNE vokspl ;if not equal then extend file STY dcbofs ;else save workspace pointer JSR LA80C ;call OSBYTE $C7 = read/write *SPOOL handle TXA ;if *SPOOL not in use BEQ L9A15 ;then close file and raise "Can't extend" LDA L9886-$11,X ;else get workspace pointer to *SPOOL file CMP dcbofs ;compare with workspace pointer to this file BNE L9A15 ;if equal JSR LA7FB ;then disable *SPOOL output. .L9A15 LDY dcbofs ;get workspace pointer JSR vshut ;close file JSR vstrng ;raise "Can't extend" error. EQUB $BF EQUS "Can't extend" EQUB $00 .vok ;extend file PLA ;restore LSB maximum allocation .vokspl STA cathig+$05,X ;store 2MSB file length in catalogue STA seqem,Y ;store 2MSB file length in workspace LDA seqwa ;get MSB maximum allocation STA seqeh,Y ;store MSB file length in workspace LDA #$00 ;clear LSB file length in catalogue STA cathig+$04,X JSR dirout ;write volume catalogue LDY dcby ;put channel workspace pointer in Y .notful ;write byte to file 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 LDA seqpl,Y ;get LSB of PTR STA work+$00 ;set LSB of buffer pointer LDA seqbuf,Y ;get MSB buffer pointer from channel workspace STA work+$01 ;set MSB of buffer pointer JSR L9A9D ;increment PTR PLA ;restore byte to write LDX #$00 ;set Y=0 for indirect indexed store STA (work,X) ;put byte in channel buffer at old PTR 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 .L9A9D ;Increment PTR TYA ;transfer channel workspace pointer to X TAX LDA work+$00 ;copy LSB of buffer pointer to LSB of PTR STA seqpl,X INC seqpl,X ;increment LSB of PTR BNE pcmpx ;if within same sector then return INC seqpm,X ;else sector boundary crossed. BNE L9AB1 ;carry out to high bytes of PTR INC seqph,X .L9AB1 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 .wfile ;OSFILE JSR savit ;save XY 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 ;$106F..76 (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 L9B0A ;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 .L9B0A 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) .dort1b ;[BUG] destroys OSFILE block pointer, $B0..1 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 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 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 RTS .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 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 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 PHA LDX #<catlow ;point XY to filename in catalogue, $0E08 LDY #>catlow PLA JSR L9753 ;compare filename at XY+A with open filenames BCC L9C0D ;if unequal then return JMP filopn ;else raise "File open" error. .L9C0D RTS .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 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 ($10D6=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 .L9C91 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 L9C91 ;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 LBFCF ;if not LDY dosram+$00 ;set Y = file handle from OSGBPB block JSR LBFBA ;clear EOF warning flag .LBFCF 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 .L9CE9 ;OSGBPB 0 = no operation 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 ;OSGBPB 3 = set pointer and read data .wbgtr ;OSGBPB 4 = read data JSR wbget ;call OSBGET; read byte from file BCS L9CE9 ;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 .L9D07 LDA dirlow,Y ;get first eight characters of title JSR wbwrit ;write to user memory INY CPY #$08 ;loop until 8 characters written BNE L9D07 .rdtc1 LDA dirhig-$08,Y ;get last four characters from $0F00..3 JSR wbwrit ;write to user memory (Y = 8..11) INY CPY #$0C ;loop until 4 more characters written BNE rdtc1 LDA option ;get boot option/top bits volume size JSR sfour ;shift A right 4 places JSR wbwrit ;write boot option to user memory LDA fdrive ;get current volume (incl. vol letter b6..4) JMP wbwrit ;write to user memory and exit .rdbir ;OSGBPB 6 = read default (CSD) drive and dir LDA defdsk ;get default volume JSR L9DB8 ;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 L9DB8 ;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 $9D5C 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) LDY dosram+$09 ;set Y = catalogue pointer (0 on first call) .wcbat4 CPY dirlen ;compare with no. files in catalogue BCS wcbat2 ;if out of files return C=1, read past EOF LDA modify,Y ;else 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 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 $9CC7 puts pointer 8 up) CLC ;c=0, did not run out of filenames: .wcbat2 STY dosram+$09 ;put updated cat ptr in OSGBPB pointer field LDA cycno ;return catalogue cycle no. in channel field STA dosram+$00 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 .L9DB8 ;Write length+drive identifier to user memory PHA LDY #$01 ;return Y=1 AND #$F0 ;unless volume letter is B..H BEQ L9DC0 INY ;in which case return Y=2 .L9DC0 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 BIT tumflg ;test Tube flag BPL L9DE6 ;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) .L9DE6 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 .L9E15 ;Get data byte from user memory BIT tubflg ;test Tube data transfer flag BMI L9E1D ;if transferring from host LDA (nmiusr),Y ;then read address in I/O memory RTS .L9E1D LDA reg3 ;else read from R3DATA. RTS .L9E21 ;Put data byte in user memory BIT tubflg ;test Tube data transfer flag BMI L9E29 ;if transferring to host STA (nmiusr),Y ;then write to address in I/O memory RTS .L9E29 STA reg3 ;else write to R3DATA. RTS ;The use of location $CC as a catalogue pointer is a red herring; it occurs ;in Acorn DFS 0.9 as well as in EDOS. .L9E2D ;*MCOPY JSR chkena ;ensure *ENABLE active JSR LA4DF ;call GSINIT and parse mandatory vol spec STA fdriv ;set as source volume JSR LA4DF ;call GSINIT and parse mandatory vol spec STA tdriv ;set as destination volume JSR LA227 ;set swapping and current disc flags JSR getlsz ;get start and size of user memory JSR L8632 ;load destination volume catalogue JSR L8515 ;save parameters of destination drive LDA dirhig+$06 ;save destination volume size on stack PHA LDA dirhig+$07 PHA JSR L9F84 ;initialise LBA to start of data area JSR L8638 ;load source volume catalogue JSR L850E ;save parameters of source drive LDA dirlen ;get number of files in catalogue * 8 STA cpycat ;store in catalogue pointer BEQ L9EAF ;if no files then raise "File not found" JSR L9F6D ;calculate number of sectors used in volume PLA ;restore destination volume size STA todolo PLA STA todohi AND #$0F ;extract b3..0 CMP dsthi ;compare MSBs destination - src sectors used BCC L9E77 ;if destination < source raise error BNE L9E7A ;if destination > source then continue LDA todolo ;else compare LSBs destination - source CMP dstlo BCS L9E7A ;if destination >= source then continue .L9E77 JMP L85C3 ;else raise "Drive .. larger than Drive .." .L9E7A LDA todohi ;save destination volume size on stack PHA LDA todolo PHA JSR L84FA ;restore parameters of destination drive JSR L9F84 ;initialise LBA to start of data area JSR L8638 ;load source volume catalogue JSR L9EB2 ;copy files JSR L84FA ;restore parameters of destination drive JSR L9F84 ;initialise LBA to start of data area JSR L9F6D ;calculate number of sectors used in volume PLA ;restore destination volume size to catalogue STA dirhig+$07 PLA STA dirhig+$06 JSR L84FA ;restore parameters of destination drive JSR chkdst ;select destination volume JSR dirout ;write volume catalogue JSR L86BA ;do a NEW for BASIC JMP L958A ;forget catalogue in pages $0E..F .L9EAF JMP nofil ;raise "File not found" error .L9EB2 ;Copy files JSR L9228 ;set high word of OSFILE load address = $FFFF LDA #$00 STA lodlo ;clear LSB of load address STA lenlo LDA frpage ;get start of user memory STA lodhi ;set OSFILE load address = OSHWM on I/O proc. .L9EC0 LDY cpycat JSR unstep ;subtract 8 from Y STY cpycat JSR prtinf ;print *INFO line LDY cpycat JSR LA1C4 ;test length of file BEQ L9F28 ;if empty then skip to next file LDA cathig+$07,Y ;else get LSB start sector STA srclo LDA cathig+$06,Y ;get top bits exec/length/load/start sector AND #$03 ;extract b1,b0 of A STA srchi ;set MSB start sector JSR LA1D0 ;calculate number of sectors used by file .L9EE0 SEC ;subtract HIMEM - OSHWM LDA hipage SBC lodhi TAY ;= number of pages of user memory LDA todohi ;test MSB file size in sectors BNE L9EF1 ;if >= 64 KiB then fill user memory CPY todolo ;else compare available pages - LSB sectors BCC L9EF1 ;if file won't fit then fill user memory LDY todolo ;else transfer number of sectors in file .L9EF1 STY lenhi ;save transfer size LDA srclo ;set LBA = source volume LBA STA lbalo LDA srchi STA wrkcat+$06 JSR L84F6 ;restore parameters of source drive JSR chkdsf ;select source volume JSR blkrd ;read ordinary file L5 CLC ;add transfer size to source volume LBA LDA srclo ADC lenhi STA srclo BCC L9F0F ;carry out to high byte INC srchi .L9F0F SEC LDA todolo ;get LSB remaining size of file in sectors SBC lenhi ;subtract number of pages transferred STA todolo ;update LSB remaining size BCS L9F1A DEC todohi ;borrow in from MSB remaining size if req'd .L9F1A CLC ;add transfer size to load address LDA lodhi ADC lenhi STA lodhi CMP hipage ;compare new load address - HIMEM BEQ L9F2C ;if user memory not full .L9F28 LDY cpycat ;then test catalogue pointer BNE L9EC0 ;if all files not copied then copy next file. .L9F2C LDA frpage ;compare start of user memory - load address CMP lodhi BEQ L9F65 ;if memory empty then loop until files done LDA dstlo ;else set LBA = destination volume LBA STA lbalo LDA dsthi STA wrkcat+$06 SEC ;subtract load address - start of user memory LDA lodhi SBC frpage STA lenhi ;= number of pages used -> MSB transfer size LDA frpage ;set MSB load address = start of user memory STA lodhi JSR L84FA ;restore parameters of destination drive JSR chkdst ;select destination volume JSR blkwr ;write ordinary file L5 CLC ;add transfer size to destination volume LBA LDA dstlo ADC lenhi STA dstlo BCC L9F5C ;carry out to high byte INC dsthi .L9F5C LDA todolo ;test no. sectors remaining to transfer ORA todohi BEQ L9F65 ;if none then copy next file JMP L9EE0 ;else loop to transfer remaining sectors .L9F65 LDA cpycat ;test catalogue pointer BEQ L9F6C ;if at start of catalogue then exit JMP L9EC0 ;else loop to copy next file .L9F6C RTS .L9F6D ;Calculate number of sectors used in volume LDY dirlen ;get number of files in catalogue * 8 .L9F70 JSR unstep ;subtract 8 from Y CPY #$F8 ;loop until entire catalogue done BNE L9F78 RTS .L9F78 JSR L9F9C ;set LBA of catalogue entry JSR LA1D0 ;calculate number of sectors used by file JSR LA219 ;add number of sectors to total JMP L9F70 ;loop to add next file .L9F84 ;Initialise LBA to start of data area LDA #$00 STA dsthi ;set MSB volume-relative LBA = 0 JSR L9F8E ;return no. reserved sectors in data area STA dstlo ;set as LSB volume-relative LBA RTS .L9F8E ;Return no. reserved sectors in data area LDA fdrive ;get current volume AND #$0C ;is the physical drive a RAM disc? BNE L9F99 ;if so then return A=2 BIT denflg ;else A=0; test density flag BVS L9F9B ;if single density .L9F99 LDA #$02 ;then return A=2 .L9F9B RTS ;else return A=0 .L9F9C ;Set LBA of catalogue entry LDA dstlo ;replace start sector of catalogue entry STA cathig+$07,Y ;with value in $CA..B LDA cathig+$06,Y AND #$FC ORA dsthi STA cathig+$06,Y RTS .getlsz ;Get start and size of user memory 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 .getmem ;Claim shared workspace IF _DDOS358 ;do not issue service call $0A ;leave main workspace with current owner ;HAZEL workspace became ours with call $0F ELSE LDX #$0A ;service call $0A = workspace claimed JSR doswcl ;call OSBYTE $8F = issue service call ENDIF JSR suspri ;set up pointer to private page LDY #memflg ;set b7 of offset $C1 in private page LDA #$FF ;b7=1 iff we own the shared workspace STA (blkptr),Y RTS .suspri ;Set up pointer to private page LDX romid LDA #$00 STA blkptr+$00 LDA priptr,X STA blkptr+$01 RTS .pmhelp ;*HELP UTILS LDX #<initbl ;Print utility command table at $8E5F LDY #>initbl LDA #$08 ;8 entries to print (not *DISK) BNE help1 .help ;*HELP DDOS / *HELP DFS LDX #<comtab ;Print DFS command table at $8D8A LDY #>comtab LDA #$16 ;22 entries to print BNE help1 .L9FF0 ;*HELP DDOSX LDX #<L8E35 ;Print DDOSX command table at $8E35 LDY #>L8E35 LDA #$04 ;4 entries to print .help1 JSR L8F28 ;set up trampoline to read table at XY STA hlpcot ;store number of printable entries in counter JSR pcrlf ;print newline before banner JSR LA884 ;print DDOS banner 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 .chksyn ;Call GSINIT with C=0 and require argument JSR setupr ;call GSINIT with C=0 BEQ synerr ;if string empty (and unquoted), syntax error RTS .synerr ;Raise "Syntax: " error JSR vstrng EQUB $DC EQUS "Syntax: " EQUB $EA JSR psyntx ;print command name and syntax JMP LA3B9 ;terminate error message, raise error .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 .LA02E JSR tramp ;get byte of command name BMI LA03B ;if terminator reached then print syntax JSR pchr ;else print character in A (OSASCI) DEY ;decrement number of spaces remaining INX ;increment offset BNE LA02E ;and loop (always) .LA03B ;Print syntax DEY ;if Y in range 1..128 BMI LA042 ;then command not reached edge of column INY ;so JSR yspace ;print number of spaces in Y .LA042 INX ;skip action address INX JSR tramp ;get syntax byte PHA ;save it INX ;skip over it JSR L8F37 ;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 LA079 ;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: .LA06D INX ;increment offset LDA argtbl,X ;get character of syntax element table BEQ LA079 ;if NUL reached then return JSR pchr ;else print character in A (OSASCI) JMP LA06D ;and loop until element printed. .LA079 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 EQUS "<Hex no.>" ;element $C, <Hex no.> EQUB $00 EQUS "<argument>" ;element $D, <argument> EQUB $00 ;terminator byte .compct ;*COMPACT JSR LA4E5 ;select specified or default volume STA fdriv ;set as source drive STA tdriv ;set as destination drive JSR gstrng EQUS "Compacting" NOP JSR L8D08 ;print " Drive " plus volume spec in A JSR pcrlf ;print newline LDY #$00 ;point Y to workspace of invalid channel $10 JSR close2 ;close all files JSR getlsz ;get start and size of user memory JSR L929E ;load volume catalogue L4 JSR L850E ;save parameters of source drive JSR L8515 ;save parameters of destination drive LDY dirlen ;get number of files in catalogue STY cpycat ;set as catalogue pointer JSR L9F84 ;initialise LBA to start of data area .LA12A LDY cpycat ;set Y to catalogue pointer JSR unstep ;subtract 8 from Y CPY #$F8 ;if we've reached end of catalogue BEQ LA187 ;then finish STY cpycat ;else set new catalogue pointer JSR inform ;print *INFO line if verbose LDY cpycat JSR LA1C4 ;test length of file BEQ LA17F ;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 LA1D0 ;calculate number of sectors used by file 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 LA219 ;else add number of sectors to total JMP LA17F ;print *INFO line and loop for next file .cmpct2 ;Compact file JSR L9F9C ;set LBA of catalogue entry 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 .LA17F LDY cpycat JSR prtinf ;print *INFO line JMP LA12A ;loop for next file .LA187 JSR gstrng ;print "Disk compacted " EQUS "Disk compacted " NOP SEC LDA dirhig+$07 ;get LSB volume size SBC dstlo ;subtract LSB sectors used on volume PHA ;=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 JSR digout ;print hex nibble PLA ;restore LSB sectors free JSR bytout ;print hex byte JSR gstrng ;print " free sectors" EQUS " free sectors" EQUB $0D NOP JMP L86BA ;store empty BASIC program at OSHWM (NEW) .LA1C4 ;Test length of file 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. RTS .LA1D0 ;Calculate number of sectors used by file CLC LDA cathig+$04,Y ;get LSB length ADC #$FF ;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 ;store LSB length of file in sectors 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 RTS .LA1EA ;Calculate free space on volume JSR LA1FF ;start with used space on volume SEC LDA dirhig+$07 ;get LSB volume size from catalogue SBC dstlo ;subtract LSB used space STA vfreel ;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 vfreeh ;[BUG] can underflow due to overlaps RTS .LA1FF ;Calculate used space on volume LDY dirlen ;get number of files in catalogue * 8 LDA #$00 ;clear total number of used sectors on vol STA dstlo ;[BUG] does not include DFS catalogue STA dsthi .LA208 JSR unstep ;subtract 8 from Y CPY #$F8 ;loop until entire catalogue done BNE LA210 ;[BUG] can exceed vol size due to overlaps RTS .LA210 JSR LA1D0 ;calculate number of sectors used by file JSR LA219 ;add number of sectors to total JMP LA208 ;loop for next file .LA219 ;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: DDOS 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. .LA227 ;Set swapping and current disc flags LDA #$00 STA swaps ;$00 = source and dest. are different drives LDA tdriv ;get destination drive CMP fdriv ;compare with source drive BNE LA239 ;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) .LA239 RTS .chkena ;Ensure *ENABLE active BIT enaflg ;test *ENABLE flag BPL chken0 ;if b7=0 then current command is enabled JSR vstrng ;else raise "Not enabled" error. EQUB $BD EQUS "Not enabled" EQUB $00 .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 LA227 ;set swapping and current disc flags JSR getlsz ;get start and size of user memory JSR gstrng ;print "Copying from drive " EQUS "Copying from drive " LDA fdriv ;get source volume JSR L8BBA ;print volume spec in A JSR gstrng ;print " to drive " EQUS " to drive " LDA tdriv ;get destination volume JSR L8BBA ;print volume spec in A JSR pcrlf ;print newline PLA ;restore GSINIT offset to Y TAY CLC .chken0 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 CLC ;set C=0, pad numeric field with spaces JSR LA2B4 ;print hex byte, C=0 if space-padded LDA linnol .LA2B4 ;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 LA2BE ;print top nibble of byte PLA ;restore bottom nibble: .LA2BE ;Print space-padded hex nibble PHA ;test accumulator, Z=1 if zero PLA BCS LA2C4 ;if digit has been printed print another BEQ pspace ;else if nibble is zero print a space .LA2C4 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 TAY .LA2E2 ;Set XY to GSINIT pointer + Y TYA ;add Y to LSB of GSINIT pointer CLC ADC linptr+$00 TAX ;hold in X LDA linptr+$01 ;carry out to high byte of GSINIT pointer ADC #$00 TAY ;hold in Y LDA #$00 ;set line number = 0 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 = $03,$A3,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,$03,$A3,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,$03,$A3,y,cl,a,sl,sh PLA ;X on entry STA stack+$08,X ;stack = a,cl,ch,$03,$A3,y,x,a,sl,sh TAX PLA ;restore A on entry RTS ;return to caller .savit ;Save XY PHA ;stack = $30,$A3,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 vstrng .illmsg ;Raise "Bad " error JSR fstrng EQUS "Bad " BCC vstrng .filmsg ;Raise "File " error JSR fstrng EQUS "File " .vstrng ;Print string immediate (via PCHR) 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 BIT etemp ;if error message already being built BPL vstrlp ;then complete it LDA #$02 ;else A = $02 STA etemp ;error message being built from offset 2 LDA #$00 ;instruction at $0100 = BRK STA errbuf+$00 BEQ vstrlp ;build error message (always) .fstrng ;Prefix error message immediate LDA #$02 ;error message being built from offset 2 STA etemp LDA #$00 ;instruction at $0100 = BRK: STA errbuf+$00 .gstrng ;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: .vstrlp JSR tmpinc ;increment $AE,F LDA (strret),Y ;get character from after JSR BMI LA3B2 ;if b7=1 then opcode terminator, execute it BEQ LA3B9 ;else if NUL then raise error JSR pchr ;else print the character JMP vstrlp ;and loop .LA3B2 PLA ;restore AY TAY PLA CLC JMP (strret) ;jump to address of end of string .LA3B9 ;Terminate error message, raise error LDA #$00 LDX etemp ;terminate error message with NUL STA errbuf,X LDA #$FF STA etemp ;no error message being built print to screen STA catdrv ;no catalogue in pages $0E..F JSR reltub ;ensure Tube is released JMP errbuf ;jump to BRK to raise error .LA3D2 ;Print VDU sequence immediate PLA ;pop caller's address into pointer STA strret+$00 PLA STA strret+$01 LDY #$00 ;offset = 0 for indirect indexed load .LA3DA JSR tmpinc ;increment $AE,F LDA (strret),Y ;get character from after JSR CMP #$FF ;if $FF terminator byte BEQ LA3E9 ;then skip it and return to code after it JSR oswrch ;else call OSWRCH JMP LA3DA ;and loop .LA3E9 JSR tmpinc ;increment $AE,F JMP (strret) ;jump to address at end of string .LA3EF ;Begin error message, number in A STA errbuf+$01 ;$0101 = error number LDA #$00 ;instruction at $0100 = BRK STA errbuf+$00 LDA #$02 ;error message being built from offset 2 STA etemp RTS ;[BUG] If ?$10DD < $80 (because DDOS is not the static workspace owner) ;then the location is corrupted when DDOS prints a message such as ;*HELP text; also, DDOS writes the first few characters of the message ;to the stack page instead of the screen. .LA3FD ;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 BIT etemp ;if error message is being built BPL LA41F ;then append character to error message PHA ;else save character JSR LA808 ;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 .LA41F ;Append character to error message LDX etemp ;get pointer to end of error message STA errbuf,X ;store character there INC etemp ;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 .LA440 ;Poll for ESCAPE JSR savit ;save XY JSR LA7F7 ;call INKEY(0) CPY #$1B ;if ESCAPE was pressed BNE LA457 JSR ackesc ;then acknowledge ESCAPE condition JSR nmirel ;release NMI LDX restsp ;restore stack pointer from $1010 TXS JMP (restrt) ;and restart command .LA457 RTS ;else return .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 .LA476 ;Add 4 to Y INY INY INY INY RTS .unstep ;Subtract 8 from Y DEY DEY DEY DEY DEY DEY DEY DEY RTS .LA484 ;Uppercase and validate letter in A CMP #$41 ;is character less than capital A? BCC LA494 ;if so then return C=1 CMP #$5B ;else is it more than capital Z? BCC LA496 ;if not then uppercase and return C=0 CMP #$61 ;else is it less than lowercase a? BCC LA494 ;if so then return C=1 CMP #$7B ;else is it more than lowercase z? BCC LA496 ;if not then uppercase and return C=0 .LA494 SEC ;else return C=1 RTS .LA496 AND #$DF ;mask bit 5, convert letter to uppercase CLC RTS .caps ;Set C=0 iff character in A is a letter PHA JSR LA484 ;uppercase and validate letter in A PLA RTS .LA4A0 ;Convert ASCII hex digit to binary JSR LA4AA ;c=0 iff digit valid BCC LA4A8 ;[BUG]accepts characters $3A..F (:..?) CMP #$10 RTS .LA4A8 SEC RTS .LA4AA CMP #$41 ;if digit is less than A BCC LA4B0 ;then convert 0..9 to binary SBC #$07 ;else convert A..F to binary .LA4B0 SEC SBC #$30 RTS .tmpinc ;Increment strret 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 .LA4CB ;Set volume from ASCII letter JSR LA484 ;uppercase and validate letter in A SEC ;subtract ASCII value of A SBC #$41 ;obtain ordinal 0..25 CMP #volums ;is ordinal 8 or more? BCS LA511 ;if so then raise "Bad drive" error JSR lfour ;else shift A left 4 places ORA fdrive ;combine volume letter with current drive BCC LA4F8 ;set as current volume, return C=0 .LA4E5 ;Select specified or default volume JSR setupr ;call GSINIT with C=0 BEQ setddr ;if argument empty select default volume JSR getdrv ;else parse volume spec JMP LA7D6 ;set up for current drive and exit .LA4F0 ;Set current volume and dir = default LDA defqua ;get default directory STA qualif ;set as current directory LDA defdsk ;get default volume .LA4F8 STA fdrive ;set as current volume RTS .setdef ;Select default volume and directory LDA defqua ;get default directory STA qualif ;set as current directory: .setddr ;Select default volume LDA defdsk ;get default volume: .dodriv ;Select volume in A STA fdrive ;save as current volume .LA7D6 ;Set up for current drive LDA fdrive ;get current drive AND #$0C ;if (drive number mod 16) = 0..3 BNE LA7DF JSR LB658 ;then set control latch for drive .LA7DF LDA fdrive ;return current drive in A RTS .readd0 ;Parse directory spec JSR gsread ;call GSREAD BCS LA54E ;if end of argument then exit C=1 CMP #$3A ;else is character a colon? BNE LA546 ;if not then accept directory character JSR gsread ;else call GSREAD BCS LA511 ;if ":" by itself then "Bad drive" error JSR LA4BF ;else set current drive from ASCII digit JSR gsread ;call GSREAD BCS LA54E ;if ":<drv>" keep current volume and dir CMP #$2E ;else is character a full stop? BEQ LA541 ;if so then expect a directory character JSR LA4CB ;else set volume from ASCII letter JSR gsread ;call GSREAD BCS LA54E ;if ":<drv><vol>" keep current directory CMP #$2E ;else ".<dir>" must follow BNE LA511 ;if next char not full stop "Bad drive" .LA541 JSR gsread ;else call GSREAD BCS LA511 ;directory char expected else "Bad drive" .LA546 JSR LA580 ;set directory from ASCII character JSR gsread ;if not at end of argument BCC LA511 ;then raise "Bad drive" error. .LA54E RTS .LA4BF ;Set current drive from ASCII digit CMP #$52 ;is drive number capital R? BNE rdrive LDA #$34 ;if so then substitute drive 4 .rdrive SEC SBC #$30 ;convert ASCII digit to binary CMP #$08 ;if drive number in range 0..7 BCC LA4F8 ;then save it as current drive .LA511 ;else raise "Bad drive" error. JSR illmsg EQUB $CD EQUS "drive" EQUB $00 .LA4DF ;Call GSINIT and parse mandatory vol spec JSR chksyn ;call GSINIT with C=0 and require argument .getdrv ;Parse volume spec LDX #$00 ;x=0, nothing specified JSR gsread ;call GSREAD BCS LA54E ;if end of argument then exit C=1 PHP ;else save status, C=0 CMP #$3A ;is character a colon? BNE LA560 ;if not then set drive from digit JSR gsread ;else call GSREAD BCS LA511 ;if ":" by itself then "Bad drive" error .LA560 JSR LA4BF ;set current drive from ASCII digit LDX #$02 ;x=2, only drive specified JSR gsread ;call GSREAD BCS LA579 ;if no more chars return drive, volume=A PLP ;else restore status, C=0 JSR LA4CB ;set volume letter from ASCII letter INX ;x=3, drive and volume specified PHP ;save status, C=0 .LA579 PLP ;restore status, C=0 LDA fdrive ;get current volume and exit RTS .LA580 ;Set directory from ASCII character CMP #$2A ;make * an alias of # BNE LA586 LDA #$23 .LA586 STA qualif ;set as current directory CLC RTS .LA58F ;Set up for RAM disc LDA #$00 STA sectrk ;number of sectors per track is undefined STA voltrk ;a=0; data area starts on track 0 STA txlbal ;LSB LBA = 0 LDA #$80 ;set a=$80 AND denflg ;preserve b7 = automatic density STA denflg ;clear b6=0 single density RTS ;exit .setq ;Set current vol/dir from open filename 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 JMP dodriv ;select volume in A and exit ;NB NMI area must be claimed before use. .LA5A4 ;Detect disc format/set sector address LDA #$00 ;set track number = 0 STA track ;alias txlbah for RAM disc LDA fdrive ;get current volume AND #$0C ;if b3 or b2 are set BNE LA58F ;then a RAM drive so set up for RAM disc JSR LB67C ;else recalibrate drive (seek track 0) LDA txcall ;if data transfer call is not 0 = read data BNE LA610 ;then set sector number = 2 * volume letter STA voltrk ;else data area starts on track 0 JSR LA61F ;set number of sectors per track BIT denflg ;if disc is double density BVS LA5CA ;then accept any volume letter LDA fdrive ;else get current volume AND #$F0 ;extract volume letter BNE LA511 ;if volume letter not A then "Bad drive" .LA5CA BIT t40flg ;if double-stepping is automatic BPL LA5D2 JSR LA631 ;then detect track stepping .LA5D2 BIT denflg ;if disc is single density BVC LA610 ;then set sector number = 0 JSR LAFF5 ;else copy volume allocations to wksp LDA dcver ;test configuration/version number CMP #$E5 ;of disc catalogue BNE LA5E4 ;if byte is $E5 formatting fill byte JSR vstrng ;raise "Disk not configured" error EQUB $CD EQUS "Disk not configured by VOLGEN" EQUB $00 .LA5E4 LDA dcszhi ;get number of sectors on surface STA dscszh ;save in workspace LDA dcszlo STA dscszl LDA dcspt ;get number of sectors per track STA sectrk ;save in workspace JSR LA615 ;set sector number = 2 * volume letter TAY ;= offset into the track allocation table LDA dcvtrk,Y ;get first track of volume from disc cat. STA voltrk ;set as first track of current volume BEQ LA658 ;if =0 raise "Volume . not allocated" error RTS .LA610 ;Set sector number = 2 * volume letter LDA denflg ;test density flag BEQ LA61C ;if single (and manual!) then start sector =0 .LA615 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 .LA61C STA sector ;set as sector number RTS .LA61F ;Set number of sectors per track BIT denflg ;if density setting is automatic BPL LA627 JMP LB6C2 ;then ensure disc is formatted .LA627 LDA #sdspt ;else set 10 sectors per track BVC LA62D ;unless disc is double density LDA #ddspt ;in which case set 18 sectors per track .LA62D STA sectrk RTS .LA631 ;Detect track stepping LDA #$80 ;b7=1 automatic, b6=0 1:1 stepping STA t40flg ;set stepping flag LDA #$02 ;track number = 2 JSR LB697 ;seek physical track JSR LB712 ;execute Read Address command LDX rdidrt+$00 ;get C cylinder number DEX ;is it 1? BEQ LA64E ;then disc is 40 track, set double stepping DEX ;else is it 2? BEQ LA653 ;then 1:1 stepping is correct, recalibrate DEX ;else the format is wrong, raise an error DEX ;is the head over logical track 4? BNE LA6A0 ;if not then raise "Bad track format" error JSR vstrng ;else "80 track disk in 40 track drive". EQUB $CD EQUS "80 track disk in 40 track drive" EQUB $00 .LA64E LDA #$C0 ;b7=1 automatic, b6=1 2:1 stepping STA t40flg ;set stepping flag .LA653 LDA #$00 ;track number = 0 JMP LB697 ;seek physical track .LA658 ;Raise "Volume . not allocated" error JSR vstrng ;begin error message "Volume " EQUB $CD EQUS "Volume " EQUB $EA CLC TYA ;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 gstrng ;print " not allocated" and raise error EQUS " not allocated" EQUB $00 .LA6A0 JSR vstrng EQUB $CD EQUS "Bad track format" EQUB $00 .LA6D9 ;Load disc catalogue L3 LDA #$00 ;data transfer call $00 = read data BEQ LA6DF ;branch (always) .LA6DD ;Write disc catalogue L3 LDA #$01 ;data transfer call $01 = write data .LA6DF STA txcall ;set data transfer call number LDX #ctries ;x = 3 number of attempts allowed: .LA6E4 JSR L92ED ;set data pointer to $0E00 LDA #volums<<1 ;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 getts ;transfer data to disc L2 BNE LA6FB ;if command failed try again RTS ;else exit .LA6FB DEX ;decrement attempts remaining BNE LA6E4 ;if not run out then try again JMP dskflt ;else raise "Disk fault" error .chkget ;Transfer data and report errors L4 JSR savita ;save AXY JSR LA715 ;transfer data L3 BNE LA70B ;if non-zero status report error RTS ;else return .LA70B AND #$40 BNE LA712 ;if b6=0 WD1770 S6 = write protect JMP dskflt ;then raise "Disk fault" error .LA712 JMP LA338 ;else raise "Disk read only" error .LA715 ;Transfer data L3 JSR savit ;save XY LDA #l3tris ;set attempt counter to 5 STA tries LDA txsizh ;test MSB of transfer size BEQ LA71D ;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 LA71D ;if >0 then floppy disc, use main routine STA rtxszl ;else A=0; set transfer size = 64 KiB STA rtxszh JSR LB467 ;transfer data L2, RAM transfers never fail INC ldlow+$02 BNE biginc INC ldlow+$03 .biginc 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: .LA71D 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 LA740 ;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 LA73C ;if >=64 KiB then transfer rest of track LDA txsizm ;set A=2MSB byte count CMP sectog ;if transfer ends before end of track BCC LA740 ;then only transfer byte count, else: .LA73C ;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: .LA740 STX expecl ;store LSB byte count STA expech ;store MSB byte count STX rtxszl ;store LSB byte count STA rtxszh ;store MSB byte count ORA rtxszl ;test if byte count > 0 BEQ LA7A1 ;if no data to transfer then finish JSR LB467 ;else transfer data L2 BEQ LA75F ;if zero status then continue AND #$40 ;else if b6=1 WD1770 S6 = write protect BNE LA75E ;then return DEC tries ;decrement attempt counter BNE LA71D ;if not tried 5 times then try again LDA #$01 ;else return A=1, Z=0 .LA75E RTS .LA75F INC track ;increment track LDA #$00 STA sector ;next transfer starts at sector 0 CLC LDA expech ;add MSB of expected transfer size ADC ldlow+$01 ;to 3MSB Tube transfer address? STA ldlow+$01 BCC LA776 ;carry out to high word of Tube transfer addr INC ldlow+$02 ;= high word of OSFILE load address BNE LA776 INC ldlow+$03 .LA776 CLC ;add expected transfer size to xfer. address LDA nmiusr+$00 ADC expecl STA nmiusr+$00 LDA nmiusr+$01 ADC expech STA nmiusr+$01 SEC ;subtract expected transfer size LDA txsizl ;from 24-bit byte count SBC expecl STA txsizl LDA txsizm SBC expech STA txsizm BCS LA798 DEC txsizh .LA798 ORA txsizl ;test remaining no. bytes to transfer ORA txsizh BEQ LA7A1 ;if no more data to transfer then finish JMP LA71D ;else loop to transfer rest of file. .LA7A1 .nmirel ;Release NMI BIT nmiflg ;if NMI is not already ours BPL LA7B8 ;then exit LDY prenmi ;else Y = ID of previous NMI owner CPY #$FF ;if Y=$FF no previous owner BEQ LA7B8 ;then skip release call [BUG]improper! LDX #$0B ;else service call $0B = NMI release JSR doswcl ;call OSBYTE $8F = issue service call .LA7B8 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 .clrkey ;Call *FX 15,1 = clear input buffer JSR savita ;save AXY LDA #$0F LDX #$01 BNE writby .LA7F7 ;Call INKEY(0) LDA #$81 ;OSBYTE $81 = read key within time limit BNE LA7FD ;call OSBYTE with X=0, Y=0 .LA7FB ;Disable *SPOOL output LDA #$C7 ;OSBYTE $C7 = read/write *SPOOL file handle .LA7FD ;Call OSBYTE with X=0, Y=0 LDX #$00 .writby ;Call OSBYTE with Y=0 LDY #$00 BEQ bytjmp ;call OSBYTE .wriwde ;Call OSBYTE $03 = specify output stream in A TAX .wriwdx ;Call OSBYTE $03 = specify output stream LDA #$03 BNE bytjmp .LA808 ;Call OSBYTE $EC = read/write char dest status LDA #$EC BNE readby .LA80C ;Call OSBYTE $C7 = read/write *SPOOL handle LDA #$C7 BNE readby .LA810 ;Call OSBYTE $EA = read Tube presence flag LDA #$EA BNE readby .LA814 ;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 ;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 = $9ADC EQUW wargs ;E ARGSV, evt + $1E = $9794 EQUW wbget ;E BGETV, evt + $21 = $98DA EQUW wbput ;E BPUTV, evt + $24 = $99B6 EQUW wbgpb ;E GBPBV, evt + $27 = $9C2D EQUW wfind ;E FINDV, evt + $2A = $BFD2 EQUW wfscm ;E FSCV, evt + $2D = $BF40 ;Table of action addresses for FSC calls 0..8, low bytes .fscltb EQUB <(wfopt -$01) ;FSC 0 = *OPT $947B EQUB <(wfeof -$01) ;FSC 1 = read EOF state $94B6 EQUB <(wnota -$01) ;FSC 2 = */ $94D2 EQUB <(wname -$01) ;FSC 3 = unrecognised *cmd $954B EQUB <(wnota -$01) ;FSC 4 = *RUN $94D2 EQUB <(wdcat -$01) ;FSC 5 = *CAT $955D EQUB <(wfdie -$01) ;FSC 6 = new FS starting up $9575 EQUB <(whlim -$01) ;FSC 7 = valid file handles $957D EQUB <(wstus -$01) ;FSC 8 = *command entered $9582 EQUB <(fsc09 -$01) ;FSC 9 = *EX EQUB <(fsc10 -$01) ;FSC 10 = *INFO EQUB <(wnota -$01) ;FSC 11 = *RUN from library $94D2 ;Table of action addresses for FSC calls 0..8, 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 >(fsc09 -$01) EQUB >(fsc10 -$01) EQUB >(wnota -$01) ;Table of action addresses for OSARGS calls A=$FF,0,1, Y=0, low bytes .LA853 EQUB <(ensur -$01) ;OSARGS $FF = ensure all files $97D1 EQUB <(rdfsno-$01) ;OSARGS 0 = return FS number $97BB EQUB <(L97BE -$01) ;OSARGS 1 = command line tail $97BE ;Table of action addresses for OSARGS calls A=$FF,0,1, Y=0, high bytes .LA856 EQUB >(ensur -$01) EQUB >(rdfsno-$01) EQUB >(L97BE -$01) ;Table of action addresses for OSFILE calls $FF,0..6, low bytes .filjpl EQUB <(loader-$01) ;OSFILE $FF = load file $9B56 EQUB <(saver -$01) ;OSFILE 0 = save file $9B0B EQUB <(fwrcat-$01) ;OSFILE 1 = wr. catalog info $9B17 EQUB <(fwrlod-$01) ;OSFILE 2 = wr. load address $9B22 EQUB <(fwrexe-$01) ;OSFILE 3 = wr. exec address $9B2A EQUB <(fwratt-$01) ;OSFILE 4 = wr. attributes $9B32 EQUB <(frdcat-$01) ;OSFILE 5 = read catalog info $9B41 EQUB <(fdefil-$01) ;OSFILE 6 = delete file $9B4A 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 <L9CE9 ;OSGBPB 0 = no operation $9CE9 EQUB <wbptr ;OSGBPB 1 = set PTR and write $9CEA EQUB <wbptr ;OSGBPB 2 = write data $9CEA EQUB <wbgtr ;OSGBPB 3 = set PTR and read $9CF2 EQUB <wbgtr ;OSGBPB 4 = read data $9CF2 EQUB <rdtco ;OSGBPB 5 = read title/opt/drv $9CFA EQUB <rdbir ;OSGBPB 6 = read CSD drv/dir $9D2B EQUB <rlbir ;OSGBPB 7 = read lib'y drv/dir $9D3A EQUB <wcbat ;OSGBPB 8 = read CSD filenames $9D49 ;Table of action addresses for OSGBPB calls 0..8, high bytes .wgptbh EQUB >L9CE9 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 EQUB $02 EQUB $03 EQUB $06 EQUB $07 EQUB $04 EQUB $04 EQUB $04 EQUB $04 .LA884 ;Print DDOS banner JSR gstrng IF _DDOS358 EQUS "Otus DDOS 3.58" ELIF _DDOS318 EQUS "Otus DDOS 3.18" ELIF _DDOS338 EQUS "Otus DDOS 3.38" ELIF _DDOS328 EQUS "Otus DDOS 3.28" ELIF _DDOS368 EQUS "Otus DDOS 3.68" ELIF _DDOS378 EQUS "Otus DDOS 3.78" ELIF _DDOS388 EQUS "Otus DDOS 3.88" ELSE EQUS "Otus DDOS 3.48" ENDIF EQUB $0D NOP RTS .LA899 IF _DDOS358 EQUB $03,$58 ;bcd version number = 3.58 ELIF _DDOS318 EQUB $03,$18 ;bcd version number = 3.18 ELIF _DDOS338 EQUB $03,$38 ;bcd version number = 3.38 ELIF _DDOS328 EQUB $03,$28 ;bcd version number = 3.28 ELIF _DDOS368 EQUB $03,$68 ;bcd version number = 3.68 ELIF _DDOS378 EQUB $03,$78 ;bcd version number = 3.78 ELIF _DDOS388 EQUB $03,$88 ;bcd version number = 3.88 ELSE EQUB $03,$48 ;bcd version number = 3.48 ENDIF EQUB $17,$10,$24 ;bcd date dd/mm/yy = 17 October 2024 EQUB $00,$00,$03 ;bcd revision number = 0.0.3 (shown as 3) .tmope0 JMP tmopen ;raise "Too many files open" error .LA8A1 ;*FORMAT LDA dcbmap ;save channel open flags PHA AND #$08 ;if channel $15 open BNE tmope0 ;then CHRN table will destroy its workspace TYA ;else save offset of command line tail PHA LDA #$20 ;start at channel $11 going to $12: .frmclr CLC ;advance to next channel's workspace ADC #$20 TAY JSR vshut ;close file to ensure file up-to-date on disc JSR clrcbf ;clear buffer-contains-PTR channel flag TYA STA seqdah,Y ;a>=$20; invalidate sector buffer of channel BPL frmclr ;loop until channel $12..$14 buffers invalidated PLA ;restore offset of command line tail TAY PLA ;restore channel open flags STA dcbmap JSR setupr ;call GSINIT with C=0 BEQ LA8C1 ;if argument empty then skip JSR gsread ;else call GSREAD CMP #$49 ;does argument begin with capital I? BNE LA8BC ;if not then extract drive number JSR gsread ;call GSREAD, read next argument character BCS LA8C1 ;if end of string then skip .LA8BC JSR LA4A0 ;else convert ASCII hex digit to binary ;(validated then discarded) .LA8C1 JSR wopa ;have A=0 returned on exit TSX STX restsp ;set stack pointer to restore on restart JSR L9228 ;set high word of buffer address = $FFFF .LA8CE ;command restart point set at $A93F JSR LAA39 ;set command restart to exit command JSR LAD74 ;set display MODE 7 JSR LAD38 ;print "DISK FORMATTER" heading .LA8D7 JSR LA3D2 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,21) EQUB $00 EQUB $15 EQUB $83 ;yellow alphanumerics EQUS "Input no. of tracks (35/40/80) " EQUB $FF LDY #$08 ;print 8 spaces, 8 DELs JSR LB308 ;erase Y characters ahead of cursor JSR LADF4 ;input number up to 5 digits TYA ;if user entered no digits BEQ LA8D7 ;then repeat prompt and input JSR LB109 ;else convert ASCII numeric to unsigned word BCS LA925 ;if numeric invalid then display error LDA #badtrk>>1 ;else 40 tracks maximum can be formatted BIT t40flg ;test double-stepping flag BVC LA91C ;if 1:1 stepping then maximum = 80 tracks BPL LA91D ;if *4080 ON then maximum = 40 tracks LDX #$80 ;else *4080 AUTO; keep automatic on STX t40flg ;force 1:1 stepping .LA91C ASL A ;and format up to 80 tracks. .LA91D CMP inputl ;compare max - LSB of number entered BCC LA925 ;if max < LSB then display error LDA inputl ;else set A = LSB number of tracks requested BNE LA93D ;can be any number up to maximum except 0: .LA925 JSR LB0C2 ;briefly display error indication JMP LA8D7 ;and re-prompt and input number of tracks .LA93D STA ftraks ;set number of tracks and fall through: .LA93F LDX #<LA8CE ;point XY at *FORMAT entry point, $A8CE LDY #>LA8CE JSR LAA3D ;set command restart action address JSR LADD7 ;clear row 23 .LA949 JSR LA3D2 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,22) EQUB $00 EQUB $16 EQUB $83 ;yellow alphanumerics EQUS "Drive number (0/1/2/3) " EQUB $FF JSR LB087 ;get printable input character CMP #$30 ;is it less than ASCII "0"? BCC LA949 ;if so then input new drive number SBC #$30 ;else convert to binary drive no. 0..3 CMP #$04 ;is drive number in range? BCC LA97B ;if so then proceed JSR LB0C2 ;else briefly display error indication JMP LA93F ;and input new drive number .LA97B STA fdrive ;set as current volume JSR LADD7 ;clear row 23 .LA980 JSR LA3D2 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,23) EQUB $00 EQUB $17 EQUB $83 ;yellow alphanumerics EQUS "Density (S/D) " EQUS " " EQUS $7F ;backspace and delete EQUB $FF JSR LB087 ;get printable input character CMP #$53 ;is it capital S? BEQ LA9F5 ;if so then format single density CMP #$44 ;else is it capital D? BEQ LA9AA ;if so then format double density JSR LB0C2 ;else briefly display error indication JMP LA980 ;and re-input .LA9AA ;Format double density LDX #<LA8CE ;point XY at *FORMAT entry point, $A8CE LDY #>LA8CE JSR LAA3D ;set command restart action address 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 LA7D6 ;set up for current drive LDA #$00 STA frmret ;set return code = 0, no error JSR LB197 ;prompt user and start format LDA frmret ;test return code BNE LA9F2 ;if error then exit command JSR LAEB3 ;clear pages $0E,$0F JSR LAEC0 ;clear volume sizes LDX ftraks ;get number of tracks on disc DEX ;all but one track available for volumes STX muland ;set multiplicand JSR LB025 ;multiply by no. sectors per track JSR LAECB ;set default volume sizes JSR LAF1D ;write volume catalogues JSR LAF41 ;generate disc catalogue JSR LA6DD ;write disc catalogue L3 .LA9F2 JMP LAA44 ;exit command .LA9F5 ;Format single density LDX #<LA8CE ;point XY at *FORMAT entry point, $A8CE LDY #>LA8CE JSR LAA3D ;set command restart action address 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 LA7D6 ;set up for current drive LDA #$00 STA frmret ;set return code = 0, no error JSR LB197 ;prompt user and start format LDA frmret ;test return code BNE LAA36 ;if format or verify failed then exit LDX ftraks ;else get number of tracks on disc STX muland ;set multiplicand JSR LB025 ;multiply by no. sectors per track JSR LAEB3 ;clear pages $0E,$0F LDA prodh ;set MSB volume size, boot option OFF STA dirhig+$06 LDA prodl ;set LSB volume size STA dirhig+$07 LDA #$00 STA txlbah ;destination LBA = $0000 STA txlbal JSR L92CE ;write disc/volume catalogue L3 .LAA36 .LAA44 ;Exit command LDX restsp ;restore stack pointer from workspace TXS JSR LB18B ;clear rows 20..22 LDX #$00 ;set XY to screen coordinates (0,24) LDY #$18 JSR LAD80 ;move cursor to (X,Y) RTS ;exit .LAA39 ;Set command restart to exit command LDX #<LAA44 ;point XY at command exit routine, $AA44 LDY #>LAA44 .LAA3D ;Set command restart action address STX restrt+$00 STY restrt+$01 .LA931 RTS .verify ;*VERIFY JSR LA4E5 ;select specified or default volume TSX STX restsp ;set stack pointer to restore on restart JSR wopa ;have A=0 returned on exit AND #$0C ;ensure drive number refers to floppy drive BNE LA931 ;if b3 or b2 set then RAM disc, quit command JSR L9228 ;set high word of OSFILE load address = $FFFF .LAA65 ;command restart point set at $AAAC JSR LAA39 ;set command restart to exit command JSR LA3D2 ;print VDU sequence immediate EQUB $16 ;set display MODE 7 EQUB $07 EQUB $0A ;two line feeds EQUB $0A EQUB $FF LDX #$02 ;repeat next line twice: .LAA72 JSR LA3D2 ;print VDU sequence immediate EQUB $83 ;yellow alphanumerics EQUB $8D ;double height EQUS " V E R I F Y D I S K" EQUB $0D EQUB $0A EQUB $FF DEX ;loop until line printed twice BNE LAA72 JSR LA3D2 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,10) EQUB $00 EQUB $0A EQUS "Insert disk" EQUB $FF JSR LB2E4 ;prompt for keypress LDX #<LAA65 ;point XY at *VERIFY entry point, $AA65 LDY #>LAA65 JSR LAA3D ;set command restart action address JSR LAD74 ;set display MODE 7 LDA #$00 STA txcall JSR LA5A4 ;detect disc format/set sector address ;[BUG]NMI area use before claim BIT denflg ;test density flag BVS LAAE3 ;if double density then examine disc catalog JSR dirldy ;else load volume catalogue 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 LDA #$00 STA divorh ;clear MSB of divisor LDA sectrk ;get number of sectors per track (= 10) STA divorl ;store LSB of divisor JSR LAE3C ;divide word by word STX ftraks ;store quotient as number of tracks JMP LAAEB ;verify disc .LAAE3 JSR LA6D9 ;load disc catalogue L3 LDA dctrks ;get number of tracks on disc STA ftraks ;store number of tracks to verify .LAAEB ;Verify disc LDA #$00 STA track ;set starting track = 0 STA frmret ;set return code = 0, no error .LAAF2 JSR LB253 ;print track number in table JSR LAB28 ;verify track with display BEQ LAAFD ;if hard error occurred INC frmret ;then set return code = 1, verify failed .LAAFD INC track ;increment track number LDA track ;compare track number - number of tracks CMP ftraks BCC LAAF2 ;if less then verify next track LDA frmret ;else test return code BNE LAB41 ;if error occurred print "VERIFY ERROR" JSR LA3D2 ;else print VDU sequence immediate EQUB $1F ;move cursor to (10,23) EQUB $0A EQUB $17 EQUB $83 ;yellow alphanumerics EQUS "Verify complete" EQUB $0D EQUB $FF JSR LB67C ;recalibrate drive (seek track 0) JMP LAA44 ;exit command .LAB28 ;Verify track with display LDX #vtries ;make 6 attempts LDY #vtries ;erase next 6 characters JSR LB308 ;erase Y characters ahead of cursor .LAB2F JSR LA440 ;poll for ESCAPE JSR LB74A ;verify track BEQ LAB40 ;if verify succeeded then exit LDA #$2E ;else print a dot JSR oswrch ;call OSWRCH DEX ;decrement attempt counter BNE LAB2F ;if attempts remaining then try again DEX ;else X=$FF, Z=0 to indicate failure .LAB40 RTS .LAB41 JSR LB167 ;print "VERIFY ERROR" JMP LAA44 ;exit command .LAB47 ;*VOLGEN JSR chkena ;ensure *ENABLE active JSR LA4E5 ;select specified or default volume TSX STX restsp ;set stack pointer to restore on restart JSR wopa ;have A=0 returned on exit JSR L9228 ;set high word of OSFILE load address = $FFFF JSR LAA39 ;set command restart to exit command JSR LB67C ;recalibrate drive (seek track 0) LDA #$00 STA voltrk ;data area starts on track 0 JSR LAD0F ;ensure disc is double density LDA fdrive ;get current volume AND #$03 ;extract physical drive number, clear b7..2 STA fdrive ;set current volume letter to A JSR LA3D2 ;print VDU sequence immediate EQUB $16 ;select display MODE 7 EQUB $07 EQUB $0A ;two line feeds EQUB $0A EQUB $FF LDX #$02 ;print next row twice: .LAB75 JSR LA3D2 ;print VDU sequence immediate EQUB $83 ;yellow alphanumerics EQUB $8D ;double height EQUS "V O L U M E G E N E R A T I O N" EQUB $0D EQUB $0A EQUB $FF DEX ;loop until two copies printed BNE LAB75 JSR LA3D2 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,7) EQUB $00 EQUB $07 EQUB $0D EQUB $83 ;yellow alphanumerics EQUS "Vol Size (K) " EQUB $FF LDA fdrive ;get current volume JSR L8D08 ;print " Drive " plus volume spec in A JSR LA3D2 ;print VDU sequence immediate EQUB $0D EQUB $0A EQUB $0A EQUB $FF LDX #$41 ;start at volume letter A .LABC6 JSR LA3D2 ;print start of line EQUB $83 ;yellow alphanumerics EQUS " " EQUB $FF TXA ;transfer volume letter to A JSR L8CF2 ;print character in A and newline INX ;take next volume letter CPX #$49 ;is it beyond "H"? BNE LABC6 ;if not then loop to print letters A..H JSR LA3D2 ;else print footer EQUB $0A EQUB $83 ;yellow alphanumerics EQUS "No. of Kbytes remaining" EQUB $FF JSR LAFAB ;read volume sizes and allocations LDX #$00 ;start at volume A: .LAC0A STX volidx ;set table position TXA PHA JSR LAE8D ;print tabulated volume size PLA TAX INX ;increment volume letter CPX #volums ;loop until 8 volumes listed BNE LAC0A .LAC18 JSR LB03D ;sum volume sizes LDA dfreel STA divenl LDA dfreeh ;divide sector count by 4 LSR A ;to give kilobytes ROR divenl LSR A ROR divenl STA divenh JSR LAE5D ;convert binary word to four decimal digits LDX #$19 LDY #$12 JSR LAD80 ;move cursor to (X,Y) JSR LB066 ;print four decimal digits, space-padded JMP LAC3C ;and start taking user input. .LAC39 JSR LB0C2 ;briefly display error indication .LAC3C LDX #<LB318 ;point XY to "Volume :" at $B318 LDY #>LB318 JSR LAD8D ;print VDU sequence at XY JSR LB0AA ;get input character and acknowledge ESCAPE CMP #$0D ;if user pressed RETURN BNE LAC4D JMP LACF1 ;then generate volumes and exit .LAC4D SEC ;else convert letter A..H to volume index 0..7 SBC #$41 CMP #volums ;if out of range then display error BCS LAC39 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 LADF4 ;input number up to 5 digits BCS LAC3C ;if invalid input then display error TYA ;else test number of characters entered BNE LAC81 ;if >0 then convert to binary LDA volidx ;else RETURN pressed, delete volume ASL A TAY ;y = volume offset LDA #$00 ;volume size = zero STA volszh,Y STA volszl,Y JSR LADE9 ;move cursor to table row in $C1 LDX #$04 ;4 spaces to print JSR LADE0 ;print X spaces JMP LAC18 ;update free space and take next command. .LAC81 JSR LB109 ;convert ASCII numeric to unsigned word BCS LAC39 ;if overflow then display error LDA inputh ;else test MSB of entered number BNE LAC39 ;if >=256 KiB requested then display error ASL inputl ;multiply KiB by 4 to get sector count ROL inputh ASL inputl ROL inputh LDA #$00 ;clear rounded-down sector count STA floorl STA floorh .LAC9E SEC LDA inputl ;subtract 18 sectors = 4.5 KiB from request SBC #ddspt STA inputl LDA inputh SBC #$00 STA inputh BCC LACBB ;if underflow then fit rounded-down request CLC ;else add 4.5 KiB to rounded-down request LDA #ddspt ADC floorl STA floorl BCC LACB8 ;carry out to high byte INC floorh .LACB8 JMP LAC9E ;and loop until request underflows. .LACBB ;Fit volume request LDA volidx ;get volume index ASL A ;double it TAY ;transfer to Y as index LDA #$00 ;clear assigned volume size STA volszh,Y STA volszl,Y JSR LB03D ;sum volume sizes SEC LDA dfreel ;compare free space - rounded-down request SBC floorl LDA dfreeh SBC floorh BCS LACDD ;if request fits then assign request LDA dfreel ;else set request = free space on disc STA floorl LDA dfreeh STA floorh .LACDD LDA volidx ;get volume index ASL A ;double it TAY ;transfer to Y as index LDA floorh ;set assigned volume size STA volszh,Y ;= min(rounded_down_request, free_space) LDA floorl STA volszl,Y JSR LAE8D ;print tabulated volume size JMP LAC18 ;update free space display and take input. .LACF1 ;Generate volumes JSR LAD0F ;ensure disc is double density .LACF4 JSR LB285 ;ensure disc is write enabled BNE LACF4 ;if write protected then try again JSR LAEB3 ;clear pages $0E,$0F LDX #<LB377 ;point XY to "generating volumes" at $BE77 LDY #>LB377 JSR LAD8D ;print VDU sequence at XY JSR LAF1D ;write volume catalogues JSR LAF41 ;generate disc catalogue JSR LA6DD ;write disc catalogue L3 JMP LAA44 ;exit command .LAD0F ;Ensure disc is double density JSR LB6C2 ;ensure disc is formatted BIT denflg ;test density flag BVS LAD37 ;if single density JSR vstrng ;then raise "must be double density" error. EQUB $C9 EQUS "Disk must be double density" EQUB $00 .LAD37 RTS .LAD38 ;Print "DISK FORMATTER" heading JSR LA3D2 ;print VDU sequence immediate EQUB $1F ;move cursor to (0,2) EQUB $00 EQUB $02 EQUB $FF LDX #$02 ;print next row twice: .LAD41 JSR LA3D2 ;print VDU sequence immediate EQUB $83 ;yellow alphanumerics EQUB $8D ;double height EQUS " D I S K F O R M A T T E R" EQUB $0D EQUB $0A EQUB $FF DEX ;loop until two copies printed BNE LAD41 JSR LA3D2 ;print VDU sequence immediate EQUB $0D ;CR + 4x LF EQUB $0A EQUB $0A EQUB $0A EQUB $0A EQUB $FF RTS .LAD74 ;Set display MODE 7 LDA #$07 PHA LDA #$16 JSR oswrch PLA JMP oswrch .LADE9 ;Move cursor to table row in volidx LDX #$06 CLC LDA volidx ADC #$09 TAY .LAD80 ;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 .LAD8D ;Print VDU sequence at XY STX vduptr+$00 ;set up pointer at $B0,1 = XY STY vduptr+$01 LDY #$00 ;set offset to 0: .LAD93 LDA (vduptr),Y ;get character of VDU sequence CMP #$FF ;is it the terminator byte? BEQ LADA3 ;if so then do not print, exit JSR oswrch ;else call OSWRCH to print it INY ;increment offset BNE LAD93 ;loop until offset overflows INC vduptr+$01 ;then increment high byte of pointer BNE LAD93 ;and loop until terminator byte reached .LADA3 RTS .LADA4 ;Erase VDU sequence at XY STX vduptr+$00 ;set up pointer at $B0,1 = XY STY vduptr+$01 LDY #$00 ;set offset to 0: .LADAA LDA (vduptr),Y ;get character of VDU sequence CMP #$1F ;is it 31 = TAB(X,Y)? BNE LADC0 ;if not then test for terminator byte JSR LADCD ;else VDU 31 and increment pointer LDA (vduptr),Y ;send X coordinate to VDU (OSWRCH) JSR LADCD ;print character in A and increment pointer LDA (vduptr),Y ;send Y coordinate to VDU (OSWRCH) JSR LADCD ;print character in A and increment pointer JMP LADAA ;and test next character .LADC0 CMP #$FF ;is it $FF = the terminator byte? BNE LADC5 ;if not then print space RTS ;else exit .LADC5 LDA #$20 ;print a space JSR LADCD ;print character in A and increment pointer JMP LADAA ;and loop to test next character .LADCD ;Print character in A and increment pointer JSR oswrch ;call OSWRCH with character in A INC vduptr+$00 ;increment low byte of pointer at $B0 BNE LADD6 ;carry out to high byte INC vduptr+$01 .LADD6 RTS .LB2E4 ;Prompt for keypress JSR LA3D2 ;print VDU sequence immediate EQUB $0D EQUS "Press any key to continue" EQUB $FF JSR LB0AA ;get input character and acknowledge ESCAPE .LB18B ;Clear rows 20..22 LDX #$00 ;move cursor to (0,20) LDY #$14 JSR LAD80 ;move cursor to (X,Y) LDX #$78 ;print 120 spaces and exit BNE LADE0 ;print X spaces .LADD7 ;Clear row 23 LDX #$00 ;move cursor to (0,23) LDY #$17 JSR LAD80 ;move cursor to (X,Y) LDX #$28 ;set X = 40, width of one MODE 7 row: .LADE0 ;Print X spaces LDA #$20 ;set A to ASCII value of space: .LADE2 ;Print character X times JSR oswrch ;call OSWRCH DEX ;decrement X BNE LADE2 ;loop until X = 0, then exit RTS .LADF4 ;Input number up to 5 digits LDY #$00 ;start with no characters in line buffer .LADF6 JSR LB0AA ;get input character and acknowledge ESCAPE CMP #$0D ;if user pressed RETURN BNE LADFF CLC ;then return C=0 RTS .LADFF CMP #$7F ;else if user pressed DELETE BNE LAE0F TYA ;then test number of characters entered BNE LAE08 ;if no characters on line SEC ;then return C=1 RTS .LAE08 JSR LAE25 ;else backspace and erase last character DEY ;decrement number of characters entered JMP LADF6 ;and loop .LAE0F CPY #$05 ;if 5 characters already entered BEQ LADF6 ;then ignore latest, loop to read DEL/CR CMP #$30 ;else if character less than "0" BCC LADF6 ;then ignore it CMP #$3A ;else if character more than "9" BCS LADF6 ;then ignore it JSR oswrch ;else print input character STA inputs,Y ;store in line buffer INY ;increment number of characters entered JMP LADF6 ;and loop until user presses RETURN. .LAE25 ;Backspace and erase characters JSR LAE2D ;print DEL LDA #$20 ;print space: JSR oswrch .LAE2D ;Print DEL LDA #$7F ;set A = ASCII value of DEL character JMP oswrch ;call OSWRCH to print it and exit. .LAE32 ;Convert ASCII digit to binary and validate SEC ;C=1 iff invalid SBC #$30 CMP #$0A RTS .LAE3C ;Divide word by word LDX #$00 ;initialise quotient = 0: .LAE3E LDA divenh ;Compare dividend - divisor CMP divorh BCC LAE5C BNE LAE4C LDA divenl CMP divorl BCC LAE5C ;if dividend >= divisor .LAE4C 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 LAE3E ;and loop as remainder >= 0 .LAE5C RTS .LAE5D ;Convert binary word to four decimal digits LDA #$03 ;set divisor = $03E8 = 1000 STA divorh LDA #$E8 STA divorl JSR LAE3C ;divide word by word STX digits+$00 ;store quotient as first digit (big-endian) LDA #$00 ;set divisor = $0064 = 100 STA divorh LDA #$64 STA divorl JSR LAE3C ;divide word by word STX digits+$01 ;store quotient as second digit LDA #$00 ;set divisor = $000A = 10 STA divorh LDA #$0A STA divorl JSR LAE3C ;divide word by word STX digits+$02 ;store quotient as third digit LDA divenl ;store remainder as fourth digit STA digits+$03 RTS .LAE8D ;Print tabulated volume size LDA volidx ;get volume index ASL A ;double it TAY ;transfer to Y as index LDA volszh,Y ;get MSB volume size STA divenh ;store it in zero page LDA volszl,Y ;get LSB volume size STA divenl ;store it in zero page ORA divenh ;test volume size BEQ LAEB2 ;if =0 then leave row blank JSR LADE9 ;else move cursor to table row in $C1 LSR divenh ;divide sector count by 4 to get kilobytes ROR divenl LSR divenh ROR divenl JSR LAE5D ;convert binary word to four decimal digits JSR LB066 ;print four decimal digits, space-padded .LAEB2 RTS .LAEB3 ;Clear pages $0E,$0F LDA #$00 TAY .LAEB6 STA dirlow,Y STA dirhig,Y INY BNE LAEB6 RTS .LAEC0 ;Clear volume sizes LDA #$00 LDY #(volums<<1)-$01 ;8 words to clear for volumes A..H .LAEC4 STA volszh,Y ;set assigned size of volume to $0000 DEY BPL LAEC4 ;loop until all words cleared RTS .LAECB ;Set default volume sizes 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 STA askhi LDX #$04 ;4 places to shift, multiply by 16: .LAEDE ASL asklo ;shift word one place left ROL askhi DEX ;repeat 4 times BNE LAEDE ;max. 16 tracks = 72 KiB per volume LDY #$00 .LAEE7 JSR LB01A ;compare requested allocation with free space BCC LAEF9 ;if it fits then set allocation = request LDA freehi ;else set allocation = free space STA volszh,Y LDA freelo STA volszl,Y RTS ;and exit .LAEF9 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 ORA freelo ;test free space BEQ LAF1C ;if disc full then exit INY ;else add 2 to offset, point to next volume INY CPY #volums<<1 ;loop until volumes A..H set or disc full. BNE LAEE7 .LAF1C RTS .LAF1D ;Write volume catalogues LDY #$00 ;start at volume A STY txlbah ;set MSB of absolute LBA = 0 .LAF21 LDA volszh,Y ;test sector count of volume ORA volszl,Y ;load MSB, or with LSB BEQ LAF3A ;if no sectors assigned then skip cat write LDA volszh,Y ;else copy MSB sector count STA dirhig+$06 ;to catalogue LDA volszl,Y ;and copy LSB STA dirhig+$07 STY txlbal ;set LSB absolute LBA = 2 * volume letter JSR L92CE ;write disc/volume catalogue L3 .LAF3A INY ;advance volume letter by 1/sector by 2 INY CPY #volums<<1 ;have we initialised 8 volumes/16 sectors? BNE LAF21 ;if not then loop to init all volumes RTS .LAF41 ;Generate disc catalogue LDA #$20 ;set version/configuration number = $20 STA dcver ;indicating that sector count is big-endian CLC LDA prodl ;get LSB of total sectors alloc. to volumes ADC #ddspt ;add 18 sectors for the catalogue track STA dcszlo ;store LSB number of sectors on disc LDA prodh ;carry out to MSB ADC #$00 STA dcszhi LDA #ddspt ;18 sectors per track STA dcspt LDA ftraks ;set number of tracks on disc STA dctrks LDA #$00 ;mystery field (MSB no. tracks?), always 0 STA dcmyst LDY #$01 STY gentrk ;data area starts on track 1 DEY .LAF6A LDA volszh,Y ;get LSB requested sector count for volume STA freehi ;set as LSB of comparand LDA volszl,Y ;copy MSB STA freelo LDA freelo ;test number of requested sectors ORA freehi BEQ LAFA4 ;if zero then volume absent, assign no tracks LDA gentrk ;else set starting track of volume data area STA dcvtrk,Y LDA #$00 ;clear next byte (MSB track number?) STA dcvmst,Y LDX #$00 ;start with 0 tracks allocated to volume STX asklo ;and 0 sectors allocated to volume: STX askhi .LAF8A JSR LB01A ;compare requested allocation with current BEQ LAF9E ;if equal (blech!) then assign these tracks INX ;else add one track to allocation CLC LDA asklo ;and add 18 sectors to allocation ADC #ddspt STA asklo BCC LAF9B INC askhi .LAF9B JMP LAF8A ;loop until allocation fulfilled .LAF9E CLC ;add number of tracks in X to starting track TXA ADC gentrk STA gentrk .LAFA4 INY ;skip to next volume entry INY CPY #volums<<1 ;loop until tracks assigned to 8 volumes BNE LAF6A RTS .LAFAB ;Read volume sizes and allocations JSR LAFF5 ;copy volume allocations to workspace 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 LAEC0 ;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). .LAFC7 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 LAFEE ;if volume absent then skip STY sector ;else set sector number = 2*volume INC sector ;add 1, point to 2nd sector of cat. JSR L92ED ;set data pointer to $0E00 LDA #$01 ;256 bytes to transfer STA rtxszh LDA #$00 STA rtxszl LDA #$00 ;data transfer call $00 = read data STA txcall ;set data transfer call number JSR LB467 ;transfer data L2 LDA dirlow+$06 ;get boot option/top bits volume size AND #$03 ;extract MSB volume size STA volszh,Y ;set as MSB size of this volume LDA dirlow+$07 ;get LSB volume size from catalogue STA volszl,Y ;set as LSB size of this volume .LAFEE DEY ;proceed to previous volume DEY ;whose catalogue sector no. is two less BPL LAFC7 ;loop until all eight volumes read RTS ;exit .LAFF5 ;Copy volume allocations to workspace JSR LA6D9 ;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 .LAFFC LDA dcvtrk,Y ;get first track of data area of volume 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 LAFFC ;loop until 8 track numbers copied RTS .LB01A ;Compare requested allocation with limit LDA askhi CMP freehi BNE LB024 LDA asklo CMP freelo .LB024 RTS .LB025 ;Multiply by no. sectors per track LDY sectrk ;get number of sectors per track LDA #$00 ;clear product STA prodl STA prodh .LB02E CLC ;add number of tracks to product LDA muland ADC prodl STA prodl BCC LB039 ;carry out to high byte INC prodh .LB039 DEY ;loop until all sectors per track added BNE LB02E RTS .LB03D ;Sum volume sizes LDY #$00 ;clear offset = 0, point to volume A STY vototl ;clear total STY vototh .LB043 CLC ;add volume size at offset Y to total LDA vototl ADC volszl,Y STA vototl LDA vototh ADC volszh,Y STA vototh INY ;add 2 to offset INY CPY #volums<<1 ;loop until 8 allocations added BNE LB043 SEC ;subtract disc size - total allocations LDA prodl SBC vototl STA dfreel ;=disc space free LDA prodh SBC vototh STA dfreeh RTS .L8D72 ;Print sector count as kilobytes JSR L8D81 ;divide sector count by 4 JSR LAE5D ;convert binary word to four decimal digits .LB066 ;Print four decimal digits, space-padded LDY #$FF ;set Y=$FF going to 0, start at first digit .LB068 ;Print decimal digits, space-padded SEC ;set C=1 pad with spaces .LB069 INY LDA digits,Y ;get digit BNE LB07D ;if >0 then print it BCC LB07D ;if a digit was printed then print the rest CPY #$03 ;else if at the fourth digit BEQ LB07D ;then always print it LDA #$20 ;else print a space JSR oswrch ;call OSWRCH JMP LB068 ;and print the rest .LB07D ORA #$30 ;convert binary to ASCII "0".."9" JSR oswrch ;call OSWRCH CPY #$03 ;if fewer than 4 digits printed BNE LB069 ;then print the rest; C=0 print digits RTS ;else exit .LB087 ;Get printable input character JSR LB0AA ;get input character and acknowledge ESCAPE CMP #$30 ;is ASCII value less than that of "0"? BCC LB087 ;if so then discard, get another character CMP #$5B ;else is ASCII value higher than "Z"? BCS LB087 ;if so then discard, get another character PHA ;else save input character JSR oswrch ;call OSWRCH to print it: .LB096 JSR LB0AA ;get input character and acknowledge ESCAPE CMP #$0D ;is it CR? BNE LB09F ;if not then test for DEL PLA ;else restore first character and exit RTS .LB09F CMP #$7F ;was DELETE key pressed? BNE LB096 ;if neither CR or DEL then get another PLA ;else discard first character JSR LAE25 ;backspace and erase characters JMP LB087 ;and loop to get another character. .LB0AA ;Get input character and acknowledge ESCAPE JSR osrdch ;call OSRDCH BCS LB0B0 ;if C=1 then error occurred, test err. code RTS ;else return character in A .LB0B0 CMP #$1B ;test if error code from OSRDCH is $1B BEQ LB0B5 ;if so then ESCAPE was pressed RTS ;else return .LB0B5 JSR ackesc ;acknowledge ESCAPE condition JSR nmirel ;release NMI LDX restsp ;restore stack pointer from $1010 TXS JMP (restrt) ;and restart command .LB0C2 ;Briefly display error indication LDA #$07 JSR oswrch ;print BEL = make a short beep LDX #<LB3C4 ;point XY to "E R R O R" at $B3C4 LDY #>LB3C4 TXA ;save XY PHA TYA PHA LDA #$86 JSR osbyte ;call OSBYTE $86 = read cursor pos'n STX cursx ;save POS in workspace STY cursy ;save VPOS in workspace PLA ;restore pointer to "E R R O R" TAY PLA TAX JSR LAD8D ;print VDU sequence at XY LDX cursx ;restore previous cursor position LDY cursy JSR LAD80 ;move cursor to (X,Y) LDA #$05 ;wait 822 milliseconds + interrupts: STA delay ;clear X and Y for use as counters LDX #$00 LDY #$00 .LB0F2 DEY BNE LB0F2 DEX ;this point reached every 640 microseconds BNE LB0F2 DEC delay ;this point reached every 164 milliseconds BNE LB0F2 JSR LB18B ;clear rows 20..22 LDX cursx ;restore previous cursor position LDY cursy JSR LAD80 ;move cursor to (X,Y) and exit RTS .LB109 ;Convert ASCII numeric to unsigned word LDX #$00 STX inputl STX inputh .LB10F LDA inputs,X ;get character of string JSR LAE32 ;convert ASCII digit to binary and validate BCS LB142 ;if invalid return with digits added so far PHA ;else $B0,1 = $B0,1 * 10 + A: LDA inputh ;save current value of $B1 PHA LDA inputl ;save current value of $B0 ASL inputl ;shift $B0,$B1 left twice ROL inputh ;to multiply by 4 ASL inputl ROL inputh CLC ADC inputl ;add old $B0,$B1 to it making 5x STA inputl PLA ADC inputh STA inputh ASL inputl ;shift $B0,$B1 left once to double it ROL inputh ;making 10x CLC PLA ;add saved value of A to $B0 ADC inputl STA inputl BCC LB13D ;and carry out to $B1. INC inputh .LB13D INX DEY BNE LB10F CLC .LB142 RTS .LB143 ;Print "FORMAT ERROR" JSR LB18B ;clear rows 20..22 JSR LA3D2 ;print VDU sequence immediate EQUB $1F ;move cursor to (5,23) EQUB $05 EQUB $17 EQUB $88 ;flashing text EQUB $83 ;yellow alphanumerics EQUS "F O R M A T E R R O R" EQUB $FF RTS .LB167 ;Print "VERIFY ERROR" JSR LB18B ;clear rows 20..22 JSR LA3D2 ;print VDU sequence immediate EQUB $1F ;move cursor to (5,23) EQUB $05 EQUB $17 EQUB $88 ;flashing text EQUB $83 ;yellow alphanumerics EQUS "V E R I F Y E R R O R" EQUB $FF RTS .LB197 ;Prompt user and start format LDX #<LB334 ;point XY at "READY TO FORMAT" heading LDY #>LB334 ;at $B334 JSR LAD8D ;print VDU sequence at XY JSR LB087 ;get printable input character CMP #$46 ;is it capital F? BNE LB197 ;if not then reprint heading and try again JSR LB285 ;else ensure disc is write enabled BNE LB197 ;if write protected then try again JSR LA3D2 ;else print VDU sequence immediate EQUB $0C ;clear screen EQUB $1F ;move cursor to (0,20) EQUB $00 EQUB $14 EQUB $88 ;flashing text EQUB $83 ;yellow alphanumerics EQUS "Please wait while formatting disk" EQUB $FF JSR LB67C ;recalibrate drive (seek track 0) LDA #$00 STA track ;set track number = 0 LDA #skew STA skewit ;set running track skew counter = 2 STA sector ;set first sector of track 0 = 2 JSR LB84E ;create ID table and format track .LB1E5 SEC ;implement track skew LDA skewit ;subtract 2 from first R of track SBC #skew BCS LB1EF ;if it underflows ADC sectrk ;then add number of sectors per track .LB1EF STA skewit ;set first sector number of track JSR LB68F ;seek logical track LDA #ftries ;make three attempts (outer) STA tries ;set attempt counter .LB1F9 JSR LA440 ;poll for ESCAPE JSR LB253 ;print track number in table LDY #$06 ;erase next 6 characters JSR LB308 ;erase Y characters ahead of cursor LDA skewit ;copy first sector number of track STA sector JSR LB84E ;create ID table and format track BEQ LB21B ;if succeeded then verify track DEC tries ;else decrement attempt counter BNE LB1F9 ;if attempts remaining then retry JSR LB143 ;else print "FORMAT ERROR" LDA #$02 STA frmret ;set return code = 2, format failed RTS .LB21B JSR LAB28 ;verify track with display BEQ LB22E ;if succeeded then format next track DEC tries ;else decrement attempt counter BNE LB1F9 ;if attempts remaining then try again JSR LB167 ;else print "VERIFY ERROR" LDA #$03 STA frmret ;set return code = 3, verify failed RTS .LB22E INC track ;increment track number LDA track CMP ftraks ;compare with total tracks BCC LB1E5 ;if < total then format next track JSR LA3D2 ;print VDU sequence immediate EQUB $1F ;move cursor to (8,23) EQUB $08 EQUB $17 EQUB $83 ;yellow alphanumerics EQUS "Format complete" EQUB $0D ;newline EQUB $0A EQUB $FF RTS .LB253 ;Print track number in table LDA #$00 ;set column to 0 STA column LDA track ;copy track number as row number STA row .LB25B SEC ;subtract 20 from row number LDA row SBC #$14 BCC LB26E ;if underflow then keep current row STA row ;else set as new row number CLC ;add 10 to column LDA column ADC #$0A STA column JMP LB25B ;and loop until row < 20 .LB26E LDX column ;set X = column LDY row ;set Y = row JSR LAD80 ;move cursor to (X,Y) LDA track ;copy track number as low byte of word STA divenl LDA #$00 ;clear high byte of word STA divenh JSR LAE5D ;convert binary word to four decimal digits LDY #$01 ;set Y=1 print 3 - Y = 2 digits JMP LB068 ;print decimal digits, space-padded .LB285 ;Ensure disc is write enabled JSR LA7E2 ;test write protect state of current drive BEQ LB2E3 ;if write enabled then return JSR LA3D2 ;else print VDU sequence immediate EQUB $1F ;move cursor to (0,20) EQUB $00 EQUB $14 EQUB $88 ;flashing text EQUB $83 ;yellow alphanumerics EQUS " *** Disk write protected ***" EQUB $0D EQUB $0A EQUB $83 ;yellow alphanumerics EQUS "Remove write protect label from disk" EQUB $0D EQUB $0A EQUB $FF JSR LB2E4 ;prompt for keypress JSR LB18B ;clear rows 20..22 LDA #$FF ;return Z=0 .LB2E3 RTS .LB308 ;Erase Y characters ahead of cursor TYA PHA JSR yspace ;print number of spaces in Y PLA TAY .LB30F LDA #$7F ;print number of DELs in Y JSR oswrch DEY BNE LB30F RTS .LB318 EQUB $1F ;move cursor to (0,23) EQUB $00 EQUB $17 EQUS "Volume : " EQUB $7F ;backspace and delete 8 characters EQUB $7F EQUB $7F EQUB $7F EQUB $7F EQUB $7F EQUB $7F EQUB $7F EQUB $FF .LB334 EQUB $16 ;set display MODE 7 EQUB $07 EQUB $0A ;three line feeds EQUB $0A EQUB $0A EQUB $83 EQUS " R E A D Y T O F O R M A T" EQUB $0D EQUB $0A EQUB $0A EQUB $0A EQUB $83 ;yellow alphanumerics EQUS "Press F(ret) to start " EQUB $FF .LB377 EQUB $1F ;move cursor to (0,20) EQUB $00 EQUB $14 EQUB $88 ;flashing text EQUB $83 ;yellow alphanumerics EQUS "Please wait while generating volumes" EQUB $FF .LB3A1 EQUB $1F ;move cursor to (2,14) EQUB $02 EQUB $0E EQUB $83 ;yellow alphanumerics EQUS "VOLUME GENERATION COMPLETE" EQUB $0D EQUB $0A EQUB $0A EQUB $0A EQUB $FF .LB3C4 EQUB $1F ;move cursor to (4,20) EQUB $04 EQUB $14 EQUB $88 ;flashing text EQUB $83 ;yellow alphanumerics EQUS " E R R O R" EQUB $FF .LB3D7 ;*DENSITY JSR chksyn ;call GSINIT with C=0 and require argument TYA ;transfer command line offset to A LDX #<LB3E6 ;point XY to keyword table at $B3E6 LDY #>LB3E6 JSR wname0 ;search for keyword in table LDX #denflg-spregs ;density flag goes to $10E3 BNE LB40E ;jump to action address ;*DENSITY keyword table .LB3E6 EQUS "SINGLE" EQUB >LB428,<LB428 EQUB $00 EQUS "DOUBLE" EQUB >LB425,<LB425 EQUB $00 EQUS "AUTO" EQUB >LB42B,<LB42B EQUB $00 ;unused syntax byte EQUB >LB9CC,<LB9CC ;wrong keyword, "Bad command" $B9CC .LB401 ;*4080 JSR chksyn ;call GSINIT with C=0 and require argument TYA ;transfer command line offset to A LDX #<LB411 ;point XY to keyword table at $B411 LDY #>LB411 JSR wname0 ;search for keyword in table LDX #t40flg-spregs ;stepping flag goes to $10E0 .LB40E JMP L80C7 ;jump to action address ;*4080 keyword table .LB411 EQUS "ON" EQUB >LB425,<LB425 EQUB $00 EQUS "OFF" EQUB >LB428,<LB428 EQUB $00 EQUS "AUTO" EQUB >LB42B,<LB42B EQUB $00 ;unused syntax byte EQUB >LB9CC,<LB9CC ;wrong keyword, "Bad command" $B9CC .LB425 LDA #$40 ;*4080 ON *DENSITY DOUBLE EQUB $AC ;$AC=LDY abs; skip next two bytes .LB428 LDA #$00 ;*4080 OFF *DENSITY SINGLE EQUB $AC ;$AC=LDY abs; skip next two bytes .LB42B LDA #$80 ;*4080 AUTO *DENSITY AUTO STA spregs,X RTS .LB437 ;*RAMINIT JSR savit ;save XY (inner) JSR nmicla ;claim NMI JSR L958A ;forget catalogue in pages $0E..F LDA #$00 ;destination LBA = $0000, start of RAM disc STA txlbah STA txlbal JSR LAEB3 ;clear pages $0E,$0F LDA #>ramsiz ;set volume size in catalogue = 128 KiB STA dirhig+$06 LDA #<ramsiz STA dirhig+$07 LDA #$02 ;set transfer size in $A0,1 = 512 bytes STA rtxszh LDA #$00 STA rtxszl LDA #$01 ;data transfer call $01 = write data STA txcall ;set data transfer call number JSR L92ED ;set data pointer to $0E00 JSR LB470 ;transfer data to paged RAM JSR nmirel ;release NMI RTS .LB467 ;Transfer data L2 LDA fdrive ;get current volume AND #$0C ;if not on physical drive 0..3 BNE LB470 ;then transfer data to paged RAM JMP getts ;else transfer data to disc L2 .LB470 ;Transfer data to paged RAM JSR savit ;save XY LDA rtxslt ;save $A2..5 PHA LDA txsizl PHA LDA txsizm PHA LDA txsizh PHA LDY #ramxe-LB511-$01 ;copy 85 bytes: .LB481 LDA LB511,Y ;copy RAM transfer code from $B511..65 STA intnmi,Y ;to NMI handler area at $0D00..54 DEY ;loop until 85 bytes copied BPL LB481 LDA savrom ;copy *SROM slot number STA rtxrom ;to NMI zero page area LDA txlbah ;copy starting LBA HIGH byte STA rtxlbh ;to $A5 LDA txlbal ;copy starting LBA LOW byte STA rtxlbl ;to $A4 LDA rtxszl ;increment MSB byte count if LSB >0 BEQ LB49D ;not rounding up, converting number format; INC rtxszh ;Z=1 from both DECs means zero reached .LB49D LDA txcall ;if data transfer call is not 0 = read data BNE LB4B8 ;then modify RAM transfer code for write LDA nmiusr+$00 ;else paste user memory address at $0D14,5 STA LB524-LB511+intnmi+$01 LDA nmiusr+$01 STA LB524-LB511+intnmi+$02 LDA tubflg ;test Tube transfer flag BEQ LB4FA ;if b7=0 then an I/O transfer, branch LDA #$8D ;else instruction at $0D13 = STA $FEE5 LDY #LB524-LB51C BNE LB4DF ;modify RAM transfer code for Tube. .LB4B8 ;Modify RAM transfer code for write LDA #<(LB524-LB511+intnmi+$02) ;instruction at $0D51 = STA $0D15 STA LB562-LB511+intnmi+$01 LDA #rtxrom ;instruction at $0D06 = LDX $A3 STA LB517-LB511+intnmi+$01 LDA #rtxslt ;instruction at $0D0E = LDX $A2 STA LB51F-LB511+intnmi+$01 LDA nmiusr+$00 ;paste user memory address at $0D0C,D STA LB51C-LB511+intnmi+$01 LDA nmiusr+$01 STA LB51C-LB511+intnmi+$02 LDA #<(LB51C-LB511+intnmi+$02) ;instruction at $0D19 = INC $0D0D STA LB52A-LB511+intnmi+$01 LDA tubflg ;test Tube transfer flag BEQ LB4FA ;if b7=0 then an I/O transfer, branch LDA #$AD ;else instruction at $0D0B = LDA $FEE5 LDY #LB51C-LB51C ;fall through: .LB4DF ;Modify RAM transfer code for Tube STA LB51C-LB511+intnmi+$00,Y ;store opcode LDA abs at $D0B/STA abs at $D13 LDA #<reg3 ;store address of R3DATA, $FEE5 STA LB51C-LB511+intnmi+$01,Y ;at $0D0D,E or $0D15,6 LDA #>reg3 STA LB51C-LB511+intnmi+$02,Y LDA #$EA ;instructions at $0D19,$0D1A,$0D1B = NOP STA LB52A-LB511+intnmi+$00 ;which was INC $0D15/INC $0D0D STA LB52A-LB511+intnmi+$01 STA LB52A-LB511+intnmi+$02 LDA #<(LB514+$FE-ramxa) ;enable 24 microsecond interval per byte STA ramxa-LB511+intnmi+$01 LDA #<(LB514+$FE-ramxb) ;enable 27.5 microsecond delay to next page STA ramxb-LB511+intnmi+$01 .LB4FA LDY #$00 ;starting offset = $00 JSR LB547-LB511+intnmi ;compute source RAM slot number JSR LB55C-LB511+intnmi ;paste high byte of source address JSR LB517-LB511+intnmi ;do transfer to/from paged RAM PLA ;restore $A2..5 from stack STA txsizh PLA STA txsizm PLA STA txsizl PLA STA rtxslt LDA #$00 ;fake WD1770 status = 0, succeeded. RTS ;Paged RAM transfer code copied to $0D00 .LB511 RTI ;ignore spurious interrupts .LB514 INC ramxe-LB511+intnmi ;wait 6.5 microseconds PHA PLA .LB517 LDX rtxslt STX romsw .LB51C LDA $FF00,Y ;high byte of source address pasted to $0D0D .LB51F LDX rtxrom STX romsw .LB524 STA $FF00,Y ;high byte of dest. address pasted to $0D15 INY BNE LB539 .LB52A INC LB524-LB511+intnmi+$02 ;increment high byte of STA address INC rtxlbl BNE LB533 INC rtxlbh .LB533 JSR LB547-LB511+intnmi ;compute source RAM slot number JSR LB55C-LB511+intnmi ;paste high byte of source address .LB539 DEC rtxszl ;decrement byte count .ramxa BNE LB517 ;loop until all bytes transferred DEC rtxszh .ramxb BNE LB517 LDA romid ;page DDOS ROM back in STA romsw RTS ;return .LB547 ;0D36 IF _MASTER LDA rtxlbh LSR A LDA rtxlbl ;abcdefgh H ROL A ROL A ROL A ;defghHab c AND #$07 ;.....Hab EOR #$04 ;45670123 STA rtxslt RTS ELIF _DDOSRAM LDA rtxlbh STA rtxslt ;copy bits of $A5: ABCDEFGH LDA rtxlbl ;load bits of $A4: abcdefgh AND #$C0 ;mask b7,6: ab...... LSR rtxslt ROR A ;a=Hab....., $A2=.ABCDEFG LSR rtxslt ROR A ;a=GHab...., $A2=..ABCDEF ADC #$10 ;c=0; skip slot $0E, reserved for paged ROM ORA #$0E ;a=GHab111. adjusted STA rtxslt ;save result as source RAM 'slot' number RTS ELIF _BMEM LDA rtxlbh LSR A LDA rtxlbl AND #$C0 ;ab...... H ADC #$0F ;ab.H~~~~ LSR A LSR A LSR A LSR A ;....ab.H EOR #$03 ;37BF26AE STA rtxslt RTS ELIF _IFEL LDA rtxlbl ;abcdefgh EOR rtxlbh AND #$01 ;.......x EOR rtxlbl ;abcdefgH CMP #$80 ;abcdefgH a ROL A ROL A ROL A ;defgHaab c AND #$0D ;....Ha.b EOR #$00 ;014589CD STA rtxslt RTS ELIF _WILLIAMS LDA rtxlbh LSR A LDA rtxlbl ;abcdefgh H ROL A ROL A ROL A ;defghHab c AND #$07 ;.....Hab EOR #$08 ;89ABCDEF STA rtxslt RTS ELIF _PLUGIN LDA rtxlbh LSR A LDA rtxlbl ;abcdefgh H ROL A ROL A ;cdefghHa b AND #$03 BCC LB554 EOR #$04 ;.....bHa .LB554 EOR #$00 ;04152637 or $03 to go right-to-left STA rtxslt RTS ELIF _PLUGIN2 LDA rtxlbh LSR A LDA rtxlbl ROL A AND #$81 ;b......H a BPL LB553 EOR #$84 ;.....b.H a .LB553 BCC LB557 EOR #$02 ;.....baH .LB557 EOR #$00 ;04261537 or slot no. of first plugin STA rtxslt RTS ELIF _PLUGIN3 LDA rtxlbh LSR A LDA rtxlbl ROL A AND #$81 ;b......H a BPL LB553 EOR #$84 ;.....b.H a .LB553 BCC LB557 EOR #$03 ;.....ba~ .LB557 EOR #$01 ;15260437 or slot no. of first plugin STA rtxslt RTS ELIF _BPLUS LDA rtxlbh LSR A LDA #$18 ROR A ;H...11.. . BIT rtxlbl BPL LB556 ;PSR = ab1xxxx. EOR bromid ;BASIC ROM slot no. EOR #$03 ;H...xxxx . .LB556 BVC LB55A EOR #$01 ;CD01 or CDEF then 8C .LB55A STA rtxslt ;fall through to calculate page no. ELSE LDA rtxlbl ;abcdefgh EOR rtxlbh AND #$01 ;.......x EOR rtxlbl ;abcdefgH ASL A ROL A ROL A ;defgH.ab AND #$0B ;....H.ab EOR #$04 ;4567CDEF STA rtxslt RTS ENDIF .LB55C LDA rtxlbl ;0D4B AND #$3F ;take high byte of source address ORA #$80 ;confine to paged ROM address space $80..BF .LB562 STA LB51C-LB511+intnmi+$02 ;paste in high byte of LDA $FF00,Y instr. RTS .ramxe .LB56C ;*FDCSTAT JSR LA3D2 ;print VDU sequence immediate EQUB $0D EQUB $0A IF _DDOS358 EQUS "WD 1770 status : " ELIF _DDOS318 EQUS "WD 2791 status : " ELIF _DDOS338 EQUS "WD 2793 status : " ELSE EQUS "WD 1770 status : " ENDIF EQUB $FF LDA fdstat ;get status of last command JSR bytout ;print hex byte JMP pcrlf ;print newline .LB58C ;$13 Read data / $17 Read data and deleted data LDX #$00 EQUB $AD ;$AD=LDA abs; skip next two bytes .LB58F ;$0B Write data LDX #$01 EQUB $AD ;$AD=LDA abs; skip next two bytes .LB595 ;$0F Write deleted data LDX #$03 EQUB $AD ;$AD=LDA abs; skip next two bytes .LB598 ;$1F Verify data LDX #$04 STX txcall JSR LB636 JSR getts LDY #$0A STA (blkptr),Y RTS .LB5A8 ;$29 Seek JSR LB68F ;seek logical track LDY #$08 ;y = 8 = offset of status in seek command STA (blkptr),Y ;return status to user's OSWORD $7F block RTS .LB5B0 ;$1B Read ID JSR LB6CB ;read ID and detect density JSR LB6CB ;read ID and detect density LDY #$0A ;y = 10 = offset of status in read/write cmd STA (blkptr),Y ;return status to user's OSWORD $7F block TAY ;if status = 0 then no error BNE LB5C8 .LB5BD LDA rdidbf,Y ;so get byte of ID read from workspace JSR L9E21 ;put data byte in user memory INY ;loop until 4 bytes returned to user CPY #$04 BNE LB5BD .LB5C8 RTS .LB5CC ;$2C Read drive status DEY ;y = 8 going to 7, offset of result JSR LB673 ;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 STA (blkptr),Y ;return result to user's OSWORD $7F block .LB5D7 RTS .LB5D8 ;$35 Specify LDA track ;get first parameter CMP #$0D ;is it $0D = Specify Initialization? BNE LB5D7 ;if not then exit LDA (blkptr),Y ;else get second parameter = step rate TAX ;(WD1770 format; 0=fast..3=slow; b7..2=0) JMP LB66F ;save as track stepping rate and force int. .LB5E4 ;$3A Write special registers LDA (blkptr),Y ;get second parameter = value to write LDX track ;get first parameter = register address CPX #$04 ;if address in range 0..3 BCS LB5F0 STA spregs,X ;then set parameter of current drive RTS .LB5F0 BNE LB5F6 ;else if address = 4 STA savrom ;then set *SROM slot number RTS .LB5F6 CPX #$12 ;else if address = 18 BNE LB610 JMP LB9A4 ;then store physical position of head .LB5FD ;$3D Read special registers ;NB sets head addressed by ?XY, not head 0 LDX track ;get first parameter = register address CPX #$04 ;if address in range 0..3 BCS LB609 LDA spregs,X ;then return parameter of current drive STA (blkptr),Y ;return to offset 8 of OSWORD control block RTS .LB609 BNE LB610 ;else if address = 4 LDA savrom ;then return *SROM slot number STA (blkptr),Y ;return to offset 8 of OSWORD control block .LB610 RTS ;Table of 8271 floppy drive controller commands with action addresses .LB611 EQUB $13 ;$13 Read data EQUW LB58C-$01 EQUB $0B ;$0B Write data EQUW LB58F-$01 EQUB $29 ;$29 Seek EQUW LB5A8-$01 EQUB $1F ;$1F Verify data EQUW LB598-$01 EQUB $17 ;$17 Read data and deleted data EQUW LB58C-$01 EQUB $0F ;$0F Write deleted data EQUW LB595-$01 EQUB $1B ;$1B Read ID EQUW LB5B0-$01 EQUB $23 ;$23 Format track EQUW LB5C9-$01 EQUB $2C ;$2C Read drive status EQUW LB5CC-$01 EQUB $35 ;$35 Specify EQUW LB5D8-$01 EQUB $3A ;$3A Write special registers EQUW LB5E4-$01 EQUB $3D ;$3D Read special registers EQUW LB5FD-$01 EQUB $00 ;terminator byte .LB636 ;Set starting sector and byte count LDA (blkptr),Y ;get 2nd parameter = starting sector number INY ;increment offset; Y = 9 STA sector ;store in zero page, $BA = track number 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 LB652 ;jump into doubling loop (always) .LB64F ASL rtxszl ;multiply byte count by two ROL A .LB652 DEX ;subtract 1 from X BPL LB64F ;if X was >0 then double byte count STA rtxszh ;else store high byte of byte count and exit RTS .LB658 ;Set control latch for drive TXA ;save X PHA LDA fdrive ;get current volume AND #$03 ;extract physical drive number, clear b7..2 TAX LDA denflg ;get density flag in bit 6 AND #$40 ;extract bit 6 (1 = double density) BEQ setdd ;if b6 clear then A=0, single density LDA #latchd ;else toggle double density latch .setdd EOR lchtbl,X ;apply flags for drive 0..3 in X STA latch ;store in control latch PLA ;restore X and exit TAX RTS .lchtbl EQUB latch0 EQUB latch0 EOR latch1 EQUB latch0 EOR latch2 EQUB latch0 EOR latch1 EOR latch2 .LB665 ;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 .LB66F STA speed ;save as track stepping rate IF _WD279X JMP LB744 ;send Force Interrupt command ELSE RTS ;1770 doesn't need Force Interrupt?? ENDIF .LA7E2 ;Test write protect state of current drive LDA fdrive ;get current volume AND #$0C ;is the physical drive a RAM disc? BNE LA7EB ;if so return Z=1, write enabled .LB673 ;Test write protect state JSR LB73A ;issue Seek and Force Interrupt JSR LBA03 ;wait for command completion AND #$40 ;z=0 if WD1770 S6 = write protect. RTS .LA7EB LDA #$00 RTS .LB67C ;Recalibrate drive (seek track 0) JSR savit ;save XY LDA #$00 EOR hdload ;WD1770 FDC command $00 = Restore JSR L92F6 ;execute command and wait for completion LDA fdrive ;get current volume AND #$01 ;extract physical unit number 0/2 or 1/3 TAX ;transfer to X for use as index LSR A ;set physical position of head to 0 STA head,X RTS .LB68F ;Seek logical track LDA track ;get logical track number BIT t40flg ;test double-stepping flag BVC LB697 ;if b6=1 then double stepping is enabled ASL A ;so double track number: .LB697 ;Seek physical track CMP #$00 ;if track number = 0 BEQ LB67C ;then issue Restore command JSR savit ;else save XY PHA ;save target physical track LDA fdrive ;get current volume AND #$01 ;extract physical unit number 0/2 or 1/3 TAX ;transfer to X for use as index LDA head,X ;get physical track number for drive JSR LB9AE ;store in track register of FDC PLA ;restore target physical track STA head,X ;store physical track number for drive JSR LB9F7 ;write to FDC data register LDA #$10 EOR hdload ;WD1770 FDC command $10 = Seek JSR L92F6 ;execute command and wait for completion AND #$10 ;z=0 if WD1770 S4 = record not found. .LB6CA RTS .L92F6 PHA ;save FDC command LDA #$40 ;instruction at $0D00 = RTI STA intnmi+$00 PLA ;restore FDC command ;Execute Restore/Seek command ORA speed ;apply track stepping rate JSR LB9EF ;write to FDC command register JMP LBA03 ;wait for command completion and exit. .LB6C2 ;Ensure disc is formatted JSR LB6CB ;read ID and detect density BEQ LB6CA ;if record found then exit JSR vstrng ;else raise "Disk not formatted" error. EQUB $C5 EQUS "Disk not formatted" EQUB $00 RTS .LB6CB ;Read ID and detect density ;[BUG]cannot force density, always tests both JSR savit ;save XY JSR LB73A ;issue Seek and Force Interrupt LDX #idtris ;4 attempts to make, 2 in each density: BIT denflg ;if current density is single BVC LB6E7 ;then attempt in single density first, else: .LB6D8 LDA denflg ;get density flag ORA #$40 ;set b6=1, double density LDY #ddspt ;18 sectors per track JSR LB70C ;execute Read Address at specified density BEQ LB709 ;if record found then return success DEX ;else decrement number of attempts remaining BEQ LB6F6 ;if run out of tries then return failure .LB6E7 LDA denflg ;else get density flag AND #$BF ;set b6=0, single density LDY #sdspt ;10 sectors per track JSR LB70C ;execute Read Address at specified density BEQ LB709 ;if record found then return success DEX ;else decrement number of attempts remaining BNE LB6D8 ;if attempts remaining try double density .LB6F6 LDA denflg ;else set b6=0, single density AND #$BF STA denflg JSR LB658 ;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. .LB709 LDA #$00 ;fake WD1770 status = 0, succeeded. RTS .LB70C ;Execute Read Address at specified density STA denflg ;store density flag STY sectrk ;store number of sectors per track: .LB712 ;Execute Read Address command LDY #LBA84-LBA6F-$01 ;21 bytes to copy, $0D00..14: .LB714 LDA LBA6F,Y ;get byte of NMI read ID STA intnmi,Y ;store in NMI area DEY ;loop until all bytes copied BPL LB714 LDA #$00 ;initialise offset = 0 STA rdidit JSR LB658 ;set control latch for drive LDA #$C0 ;WD1770 command $C0 = Read address JSR LB9EF ;write to FDC command register JSR LBA03 ;wait for command completion PHA ;save exit status LDY #$03 ;4 bytes to copy, $1090..3: .LB72F LDA rdidbf,Y ;get CHRN byte of sector ID STA rdidrt,Y ;copy to workspace DEY ;loop until all bytes copied BPL LB72F PLA ;restore Read Address command status and exit RTS .LB74A ;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 .getts ;Transfer data to disc L2 JSR savit ;save XY JSR LA7D6 ;set up for current drive IF _WD279X ELSE LDX #$0A ;wait 26 microseconds .LB765 DEX BNE LB765 LDA fdcsta ;load FDC status register PHA ;save status ENDIF JSR LB68F ;seek logical track LDA track ;get logical track number JSR LB9A4 ;store as 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 $B794. IF _WD279X ELSE PLA ;restore status BMI LB790 ;if WD1770 S7 = motor on then skip LDA txcall ;else get data transfer call number CMP #$01 ;if writing to disc BNE LB790 PHA ;then save call number, 1 LDA #$04 ;set call number to $04, verify data STA txcall LDX #vwtris ;execute floppy drive command twice: .LB786 JSR LB799 ;execute verify data command L1 DEX ;decrement counter BNE LB786 ;loop until two verify passes made PLA ;restore call number. STA txcall ENDIF .LB790 JSR LB799 ;execute floppy drive command L1 PHA ;save masked status JSR LB99C ;store head position for this drive PLA ;restore masked status, setting Z RTS ;and exit .LB799 ;Execute floppy drive command L1 JSR savit ;save XY LDA rtxszl ;save ?$A0, ?$A1 on stack PHA LDA rtxszh PHA JSR LB831 ;copy NMI read from disc/polling loop to NMI LDA savrom ;get *SROM slot number STA LBA50-LBA24+intnmi+$01 ;store in polling loop to page in on entry LDA rtxszl ;increment MSB byte count if LSB >0 BEQ LB7B1 ;not rounding up, converting number format; INC rtxszh ;Z=1 from both DECs means zero reached .LB7B1 LDA txcall ;get data transfer call number AND #$05 ;if call=0 or 2, read (deleted) data BEQ LB7CC ;then branch ROR A ;else if b2..0 = 1x0, A=$04 verify data BCS LB7D0 LDA #$4C ;then instruction at $0D06 = JMP $0D11 STA LBA2A-LBA24+intnmi+$00 ;discard byte from FDC data register LDA #<(LBA35-LBA24+intnmi) STA LBA2A-LBA24+intnmi+$01 LDA #>(LBA35-LBA24+intnmi) STA LBA2A-LBA24+intnmi+$02 BNE LB7DC .LB7CC LDY #LBA2A-LBA24+$01 ;if call=0 or 2, read (deleted) data BNE LB7D9 ;then data address is located at $0D07. .LB7D0 LDA #$00 ;if b0=1, A=1 or 3 write (deleted) data STA rtxszl ;then clear ?$A0, write whole sectors JSR LB83D ;copy NMI write to disc to NMI area LDY #LBA27-LBA24+$01 ;data address is located at $0D04 .LB7D9 JSR LB807 ;set data address in NMI ISR .LB7DC LDA romid ;get DDOS ROM slot number STA LBA5B-LBA24+intnmi+$01 ;save in NMI area LDA sector ;get start sector number JSR LB9F3 ;write to FDC sector register LDY txcall ;get data transfer call number LDA LBA1A,Y ;get FDC command for call JSR LB9EF ;write to FDC command register LDX #$1E ;wait 76 microseconds .LB7F1 DEX BNE LB7F1 JSR LBA50-LBA24+intnmi ;page SROM in and wait until finished L0 PLA ;restore ?$A0, ?$A1 from stack STA rtxszh PLA STA rtxszl JSR LBA11 ;load FDC status register and store b6..0 LDY txcall AND LBA1F,Y ;apply status mask from table to set Z. RTS .LB807 ;Set data address in NMI ISR LDA tubflg ;test Tube data transfer flag BEQ LB826 ;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 LBA2D-LBA24+intnmi+$00 ;do not increment R3DATA address LDA #<(LBA35-LBA24+intnmi) STA LBA2D-LBA24+intnmi+$01 LDA #>(LBA35-LBA24+intnmi) STA LBA2D-LBA24+intnmi+$02 RTS .LB826 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 .LB831 ;Copy NMI read from disc/polling loop to NMI LDY #LBA61-LBA24-$01 ;61 bytes to copy, $0D00..3C: .LB833 LDA LBA24,Y ;get byte of NMI read from disc/polling loop STA intnmi,Y ;store in NMI area DEY ;loop until all bytes copied BPL LB833 RTS .LB83D ;Copy NMI write to disc to NMI area LDY #wrioe-LBA61-$01 ;14 bytes to copy, $0D03..10: .LB83F LDA LBA61,Y ;get byte of NMI write to disc STA LBA27-LBA24+intnmi,Y ;patch NMI read to disc routine with it DEY ;loop until all bytes copied BPL LB83F LDA #<(LBA44+$FE-LBA46) ;enable 123 microsecond delay STA LBA46-LBA24+intnmi+$01 ;before interrupting write operation RTS ;so that FDC will write CRC of sector .LB84E ;Create ID table and format track LDA #sdspt ;set A = 10 sectors per track BIT denflg ;if double density format BVC LB857 LDA #ddspt ;then set A = 18 sectors per track .LB857 STA fsectk ;store as limit to sector count ASL A ;multiply by 4 ASL A STA chrnsz ;store as size of CHRN table LDX sector ;set X = number of first sector LDY #$00 ;(inverse track skew) Y=0 CHRN tbl index .LB861 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 LB87F LDX #$00 ;if so then wrap around to 0 .LB87F CPY chrnsz ;has table offset reached 4x s.p.t? BCC LB861 ;if not then loop LDA #<chrn ;else set pointer to start of CHRN table: STA nmiusr+$00 LDA #>chrn STA nmiusr+$01 .LB5C9 ;$23 Format track LDA #<rundat ;set data table pointer to $1312 STA datptr+$00 ;(page break occurs 1/8 of the way through STA runptr+$00 ;the 11th sector of the track.) LDA #>rundat STA datptr+$01 LDA #>runlen ;set run table pointer to $1512 STA runptr+$01 LDX #LB956-LB956 ;point to single density table, X = $00 BIT denflg ;if double density format BVC LB8A2 LDX #LB979-LB956 ;then point to double density table, X = $23 .LB8A2 LDY #$05 ;set Y = 5 as counter: .LB8A4 JSR LB8E5 ;add entry to track format RLE table DEY ;loop until 5 entries added BNE LB8A4 ;this copies gap 5, IDAM and start of gap 1 LDY #sdspt ;set Y = 10 sectors per track BIT denflg ;if double density format BVC LB8B3 LDY #ddspt ;then Y = 18 sectors per track .LB8B3 TXA ;X points to repeating sector block PHA ;save it .LB8B5 JSR LB8E5 ;add entry to track format RLE table BCC LB8B5 ;loop until terminator byte reached PLA ;reset X to start of sector block TAX DEY ;decrement number of sectors remaining BNE LB8B3 ;loop until all sectors added to track LDA #$00 ;data byte = $00 (run length = $10 or $16) JSR LB93D ;add gap 4 to table JSR LB68F ;seek logical track LDA #sdgap5-$01 ;A = $0F (or just do LDY $1512:DEY:STY $A0!) BIT denflg ;if double density format BVC LB8D0 LDA #ddgap5-$01 ;then A = $27 .LB8D0 STA frmrun ;set number of filler bytes in gap 5 (- 1) LDY #forme-LBA84-$01 ;36 bytes to copy, $0D00..23: .LB8D4 LDA LBA84,Y ;get byte of NMI format code STA intnmi,Y ;store in NMI handler area DEY ;loop until all bytes transferred BPL LB8D4 LDA #$F4 ;$F4=Write track, settling delay JSR LB9EF ;write to FDC command register JMP LBA03 ;wait for command completion and exit. .LB8E5 ;Add entry to track format RLE table TXA ;save ROM table offset PHA TYA ;save number of sectors remaining PHA SEC LDA LB956+$00,X ;get run length from ROM table BMI LB903 ;if b7=1 then process special entry BEQ LB8FC ;if the terminator byte then finish C=1 STA frmrun ;else store run length in zero page LDA LB956+$01,X ;get data byte from ROM table JSR LB93D ;store run in table .LB8FB CLC ;c=0, sector not completed .LB8FC PLA ;restore number of sectors remaining TAY PLA ;restore ROM table offset TAX INX ;add 2 to ROM table offset INX RTS .LB903 ;Process special table entry (length=$FF) LDA LB956+$01,X ;get data byte from ROM format table BNE LB924 ;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: .LB90E LDY #$00 ;y=0 for user memory load JSR L9E15 ;get data byte from user memory JSR LB93D ;store run in table INC nmiusr+$00 ;increment CHRN table pointer BNE LB91C ;carry out to high byte INC nmiusr+$01 .LB91C DEX ;loop until 4 ID bytes stored BNE LB90E STA szcode ;store last byte read = N = size code BEQ LB8FB ;restore XY and return .LB924 ;Add sector data area LDX szcode ;load sector size code LDA LB939,X ;get run length from table STA frmrun ;store in zero page LDX #$08 ;repeat prescribed run 8 times: .LB92D LDA #$E5 ;A=$E5 = sector filler byte JSR LB93D ;store run in table DEX ;loop until 8 copies of run stored BNE LB92D BEQ LB8FB ;restore XY and return .LB939 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 .LB93D ;Store run in table LDY #$00 ;offset = 0 for indirect indexed store STA (datptr),Y ;store data byte in data table LDA frmrun ;get run length CPY runptr+$00 ;if pointers are on a page boundary BNE LB949 ;then subtract 1 from run length (C=1) SBC #$01 .LB949 STA (runptr),Y ;store run length in run table INC datptr+$00 ;increment data table pointer INC runptr+$00 ;increment run table pointer BNE LB955 ;carry out to high bytes INC datptr+$01 INC runptr+$01 .LB955 RTS ;RLE tables of formatting bytes ;Single density .LB956 ;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 .LB979 ;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) .LB99C ;Store per-drive head position LDA track ;get logical track number of disc operation BIT t40flg ;test double-stepping flag BVC LB9A4 ;if b6=1 then double stepping is enabled ASL A ;so double track number: .LB9A4 ;Store physical position of head PHA LDA fdrive ;get current volume AND #$01 ;extract physical unit number 0/2 or 1/3 TAX ;transfer to X for use as index PLA ;get back A STA head,X ;store physical track number for drive .LB9AE IF sense EOR #sense ;[DIFF] invert for WD 2791 ENDIF STA fdctrk ;store in track register of FDC RTS .LB73A ;Issue Seek and Force Interrupt LDA #$18 ;WD1770 command $18 = Seek w/spin up JSR LB9EF ;write to FDC command register LDX #$0F ;wait 38.5 microseconds .LB741 DEX BNE LB741 .LB744 LDA #$D0 ;WD1770 command $D0 = Force interrupt .LB9EF ;Write to FDC command register IF sense EOR #sense ;[DIFF] invert for WD 2791 ENDIF STA fdccmd RTS .LB9F3 ;Write to FDC sector register IF sense EOR #sense ;[DIFF] invert for WD 2791 ENDIF STA fdcsec RTS .LB9F7 ;Write to FDC data register IF sense EOR #sense ;[DIFF] invert for WD 2791 ENDIF STA fdcdat RTS .LB9FB ;Load FDC status register LDA fdcsta IF (sense EOR stsens) AND $80 EOR #sense EOR stsens ;return A=0, Z=1 iff motor is on ENDIF AND #$80 ;mask b7 extract WD1770 S7 = motor on RTS .LBA03 ;Wait for command completion JSR savit ;save XY LDX #$FF ;wait 638 microseconds .LBA08 DEX BNE LBA08 .LBA0B JSR LBA11 ;load FDC status register IF _WD279X BMI LBA0B ;[DIFF] loop until b7=0 WD2793 S7 = not rdy ENDIF AND #$01 ;test bit 0 BNE LBA0B ;loop until b0=0 WD1770 S0 = busy .LBA11 LDA fdcsta ;load FDC status register IF sense EOR #sense ;[DIFF] flip bits 7..0 keep WD2791 S7 = not rdy ENDIF IF _WD279X ELSE AND #$7F ;mask bits 6..0 ignore WD1770 S7 = motor on ENDIF STA fdstat ;save final status RTS ;Table of WD1770 FDC commands for data transfer call numbers 0..4 .LBA1A 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 .LBA1F ;{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) .LBA24 STA LBA4D-LBA24+intnmi+$01 ;save accumulator to restore on exit .LBA27 LDA fdcdat ;read FDC data register IF sense EOR #sense ;[DIFF] invert for WD 2791 ENDIF .LBA2A STA $FFFF ;store in user memory or R3DATA .LBA2D INC LBA2A-LBA24+intnmi+$01 ;increment user memory address BNE LBA35 ;carry out to high byte INC LBA2A-LBA24+intnmi+$02 .LBA35 DEC rtxszl ;decrement count of bytes to transfer BNE LBA4D ;($0101 = 1; $0000 = 0) DEC rtxszh ;if count has not reached zero BNE LBA4D ;then restore A and return from interrupt LDA #$40 ;else set 0D00=RTI; ignore further NMIs STA intnmi+$00 ;ISR safe by 22+e..29.5 us after NMI ;write complete by 24.5+e..32 us IF _BEEBEM JSR bounce-LBA24+intnmi ;call trampoline and come back with an RTI .bounce PHP ;save status; RTI unfreezes BeebEm's WD 1770 RTI ;return to $0D20 and execute ORA $4008 ENDIF LDA #$CE ;wait 123 microseconds (if loop enabled) .LBA44 ADC #$01 .LBA46 BCC LBA48 ;0D23=$FC loops back to $0D20 .LBA48 LDA #$D0 ;FDC command $D0 = Force Interrupt IF sense EOR #sense ;[DIFF] invert for WD 2791 ENDIF STA fdccmd ;write to FDC command register .LBA4D LDA #$00 ;restore value of A on entry RTI ;return from interrupt .LBA50 LDA #srom ;NMI polling loop, $0D2C..3C STA romsw ;page *SROM slot in .LBA55 LDA fdcsta ;load FDC status register IF _WD279X IF (sense EOR stsens) AND $80 BPL LBA55 ;[DIFF] loop until b7=1 WD2791 S7 = not ready ELSE BMI LBA55 ;[DIFF] loop until b7=0 WD2793 S7 = not ready ENDIF ENDIF ROR A ;place bit 0 in carry flag IF (sense EOR stsens) AND $01 BCC LBA55 ;loop until b0=1 WD2791 S0 = busy ELSE BCS LBA55 ;loop until b0=0 WD1770 S0 = busy ENDIF .LBA5B LDA #$00 ;page DDOS ROM back in STA romsw RTS ;NMI write to disc, $0D03..10 .LBA61 .wriol LDA $FFFF IF sense EOR #sense ;[DIFF] invert for WD 2791 ENDIF .wrios STA fdcdat INC LBA27-LBA24+wriol-LBA61+intnmi+$01 BNE wrioe INC LBA27-LBA24+wriol-LBA61+intnmi+$02 .wrioe ;NMI read ID, $0D00..14 .LBA6F STA LBA81-LBA6F+intnmi+$01 ;save AY to restore on exit STY LBA7F-LBA6F+intnmi+$01 LDY rdidit ;load offset in Y LDA fdcdat ;load FDC data register IF sense EOR #sense ;[DIFF] invert for WD 2791 ENDIF STA rdidbf,Y ;store ID byte in buffer INC rdidit ;increment offset .LBA7F LDY #$00 ;restore AY on entry .LBA81 LDA #$00 RTI ;A run-length encoded table of formatting bytes is stored in two arrays ;starting at $1312 and $1512. Valid range of counts is $01..$80. ;When the byte source address crosses a page, the high byte is incremented ;in the same interrupt and the new count is fetched (from the next page) ;in the next interrupt. One byte from the next page is sent to the ;controller in the meantime, and so the first count of a page is one less ;than the number of bytes actually sent, i.e. the first byte of the page ;cannot be a singleton. The page crossing occurs 1/8th of the way through ;the data area of the eleventh sector after the index pulse. ;NMI format, $0D00..23 .LBA84 PHA .LBA85 LDA rundat ;save A on entry, fetch current data byte IF sense EOR #sense ;[DIFF] invert for WD 2791 ENDIF STA fdcdat ;write to FDC data register DEC frmrun ;decrement run counter BNE LBA99 ;if all bytes in run written INC LBA85-LBA84+intnmi+$01 ;then increment data byte address low BNE LBA9E ;if no carry then fetch next run length INC LBA85-LBA84+intnmi+$02 ;else increment data byte address high .LBA97 PLA ;restore A on entry RTI ;exit .LBA99 BPL LBA97 ;if run still in progress then exit INC LBAA1-LBA84+intnmi+$02 ;else page was crossed last time: .LBA9E INC LBAA1-LBA84+intnmi+$01 ;increment run length address .LBAA1 LDA runlen ;fetch next run length STA frmrun ;set run counter PLA ;restore A on entry RTI ;exit .forme ;Tube hosting .there1 CMP #$FE ;is service call number <$FE? BCC LBB09 ;if so then return to process other calls BNE tgo ;if A=$FF then branch to do main init, else: CPY #$00 ;Service call $FE = Tube post initialisation BEQ LBB09 ;ignore call if Y=0 on entry LDX #$06 ;else X=6 = fully exploded LDA #$14 ;OSBYTE $14 = explode soft character RAM JSR osbyte ;call OSBYTE .tmessa BIT r1stat ;print Tube coprocessor banner: BPL tmessa ;poll until character in R1DATA LDA r1data ;then read R1DATA BEQ tout ;if =NUL then claim service call and exit JSR oswrch ;else print the character and loop JMP tmessa .tgo LDA #<L06AD ;EVNTV = $06AD STA evtvec+$00 LDA #>L06AD STA evtvec+$01 LDA #<zptube ;BRKV = $0016 STA brkvec+$00 LDA #>zptube STA brkvec+$01 LDA #$8E ;set Tube status (NAUG p.329) STA r1stat ;enable NMI on R3, IRQ on R1,R4 LDY #$00 ;initialise offset to 0: .cloop LDA LBB4B+$0000,Y ;copy Tube host code from $BB4B..$BE4A STA rtube+$0000,Y ;to $0400..$06FF LDA LBC4B+$0000,Y ;offset 0 first, then 255..1 STA rtube+$0100,Y LDA LBC4B+$0100,Y STA rtube+$0200,Y DEY BNE cloop JSR L0421 ;mark Tube unclaimed LDX #$60 ;initialise offset to $60 .floop LDA newbr,X ;copy Tube BRK handler from $BB0A..6A STA zptube,X ;to $0016..76 DEX BPL floop .tout LDA #$00 ;return A=0 to claim service call .LBB09 RTS ;Tube BRK handler copied to $0016..76 .newbr LDA #$FF ;set A=$FF to interrupt coprocessor (JGH) JSR L069E ;write A to R4DATA LDA r2data ;empty inward R2DATA and discard byte LDA #$00 ;set A=0 to specify error (JGH) JSR L0695 ;write A to R2DATA TAY ;set Y=0 offset into error message LDA (reptr),Y ;get error number at MOS error pointer JSR L0695 ;write A to R2DATA .newba INY ;increment offset LDA (reptr),Y ;get character of error message JSR L0695 ;write A to R2DATA TAX ;test last character written BNE newba ;loop until it is NUL .tstart LDX #$FF ;reset stack pointer TXS CLI ;enable interrupts: .main BIT r1stat ;test R1STAT BPL maina ;if b7=1, data available .mainb LDA r1data ;then read R1DATA to A JSR oswrch ;call OSWRCH. .maina BIT r2stat ;test R2STAT BPL main ;if b7=0, data not available then test R1 BIT r1stat ;else Tube call waiting. test R1STAT BMI mainb ;first print waiting bytes to OSWRCH (if any) LDX r2data ;then read R2DATA to X =call number STX L0050+$01 ;modify LSB indirect address of next inst. .LBB44 JMP (rtube+$0100) ;0050 handle Tube call via jump table ;Default Tube entry address = $00008000 .LBB47 EQUB $00 EQUB $80 EQUB $00 EQUB $00 ;Tube host code copied to $0400..$06FF .LBB4B ;0400 Copy language to coprocessor (NAUG) JMP L0484 ;0403 Copy ESCAPE flag to coprocessor (NAUG) JMP L06A7 ;0406 Tube service entry CMP #$80 ;if A = $00..7F BCC addrok ;then set up data transfer CMP #$C0 ;else if A = $C0..FF BCS addrw ;then handle Tube claim ORA #$40 ;else A=$80..BF release Tube CMP claime ;set b6=1 to compare with claimant ID BNE addrwz ;if releaser is not claimant then ignore else: .adrrel ;0414 Release Tube PHP ;save interrupt state SEI ;disable interrupts LDA #$05 ;type byte=5 No transfer (FS release) JSR L069E ;write A to R4DATA LDA claime ;set A=claimant ID JSR L069E ;write A to R4DATA PLP ;restore interrupt state: .adrral LDA #$80 ;0421 Mark Tube unclaimed STA claime ;not in range $C0..FF =no claimant STA claimt ;$80=Tube unclaimed RTS .addrw ASL claimt ;$00=Tube claimed BCS addrwa ;if it was unclaimed then set claimant CMP claime ;else compare caller's ID - current claimant BEQ addrwz ;if same claimant reclaims then return C=1 CLC ;else reject claim, return C=0 RTS .addrwa STA claime ;set current claimant, C=1 claim granted .addrwz RTS .addrok PHP ;save interrupt state SEI ;disable interrupts STY qram+$01 ;set control block pointer from XY STX qram+$00 ;a=type byte/reason code JSR L069E ;write A to R4DATA TAX ;hold type byte in X LDY #$03 ;1+4 bytes to write LDA claime ;set A=claimant ID JSR L069E ;write A to R4DATA: .adrrb LDA (qram),Y ;get byte of transfer address, MSB to LSB JSR L069E ;write A to R4DATA DEY ;in descending/big-endian order BPL adrrb ;loop until claimant+4 address bytes written LDY #$18 ;set Tube status (NAUG p.329) STY r1stat ;set V,M=0 disable NMI and word mode on R3 LDA L0518,X ;get status setting for transfer type in X STA r1stat ;write to R1STAT LSR A ;test I in bit 1 = modify interrupts on R1 LSR A ;(if I was modified, then I was set) BCC adrrwp ;if interrupts were enabled on R1 BIT r3data ;then transferring to host (C=1, used later) BIT r3data ;discard word in R3 to empty it .adrrwp JSR L069E ;write A to R4DATA = synchronising byte .adrrwt BIT r4stat ;wait for it to be taken; test R4STAT BVC adrrwt ;loop until b6=1, not full BCS adrrp ;if transferring to host then branch CPX #$04 ;else if type<>4 address only BNE anrts ;then return without handshake, else: .adrrgo JSR L0414 ;release Tube JSR L0695 ;write A to R2DATA =$80 =transfer complete ;This value is used by code implementing OSCLI on the coprocessor to decide ;whether to return to the user or to jump to code at the type 4 transfer ;address. This call does not return. If the *command is not a binary to ;download to the coprocessor, DDOS's OSCLI code returns to the MOS which in ;turn exits to $059C ($BCE7), and there $7F is written to R2DATA indicating ;command completion. If that value is the first to arrive, it means there was ;no type 4 transfer address issued, and the coprocessor's OSCLI returns. JMP L0032 ;go to idle loop .adrrp LSR A ;test J in bit 2 = modify interrupts on R4 BCC anrts ;if J=1, types 0 or 2, bytes/words to host LDY #$88 STY r1stat ;then set M=1 enable NMIs on R3 .anrts PLP ;restore interrupt state RTS .begin ;0484 Copy language to coprocessor (NAUG) CLI ;enable interrupts BCS beginr ;if C=1, entry from *FX142 (JGH) then copy BNE begink ;else if A>0 then enter language (JGH) JMP L059C ;else A=0 no language, signal completion .begink LDX #$00 ;set X=$00 do not alter LDY #$FF ;set Y=$FF do not update LDA #$FD ;OSBYTE $FD = read/write type of last reset JSR osbyte ;call OSBYTE TXA ;if type = 0 soft BREAK BEQ adrrgo ;then release Tube and write $80 to R2DATA .beginr LDA #$FF ;$FF = claim Tube with claimant ID = $3F JSR taddrl ;call Tube service BCC beginr ;loop until claim granted JSR L04D2 ;set up Tube destination address .sendw LDA #$07 ;type byte = 7, 256 byte transfer to host JSR L04CB ;set up Tube data transfer LDY #$00 ;clear page offset STY zram+$00 ;align ROM pointer to page boundary .sendl LDA (zram),Y ;get byte from ROM STA r3data ;write A to R3DATA NOP ;wait 3 microseconds NOP NOP INY ;increment offset BNE sendl ;loop until page boundary reached (10us/byte) INC L0053+$01 ;then add 256 to Tube transfer address BNE sendt ;carry out to 2MSB INC L0053+$02 BNE sendt ;and MSB INC L0053+$03 .sendt INC zram+$01 ;increment MSB of ROM pointer BIT zram+$01 ;test b14 of ROM pointer BVC sendw ;loop until b14=1, when it has reached $C000 JSR L04D2 ;set up Tube destination address LDA #$04 ;type byte = 4 no transfer, execute 2P code: .taddr ;04CB set up Tube data transfer LDY #>L0053 ;point XY at Tube transfer address at $0053 LDX #<L0053 JMP taddrl ;jump into Tube service. .setadr ;04D2 Set up Tube destination address LDA #$80 STA L0053+$01 ;set 3MSB of Tube transfer address = $80 STA zram+$01 ;set MSB of ROM pointer = $80 LDA #$20 ;set b5=1 to test custom address flag AND lang+$06 ;AND with ROM type byte TAY ;place result in AY for MSB, 2MSB of addr STY L0053+$00 ;store as LSB (on the assumption that it's 0) BEQ begina ;if b5=0 then no custom addr, set $00008000 LDX lang+$07 ;else get offset of copyright string .beginl INX ;skip leading NUL, increment offset LDA lang+$00,X ;test character of copyright string BNE beginl ;loop until terminating NUL reached LDA lang+$01,X ;store next byte as LSB of Tube address STA L0053+$00 LDA lang+$02,X ;store byte after that as 3MSB STA L0053+$01 LDY lang+$03,X ;get next byte as 2MSB LDA lang+$04,X ;get byte after that as MSB: .begina STA L0053+$03 ;store MSB of Tube transfer address STY L0053+$02 ;store 2MSB of Tube transfer address RTS ;0500 Tube call handler jump table (SB) .LBC4B EQUW L0537 ;R2 = $00 OSRDCH rdchz EQUW L0596 ;R2 = $02 OSCLI cli EQUW L05F2 ;R2 = $04 OSBYTE A < $80 sbyte EQUW L0607 ;R2 = $06 OSBYTE A >=$80 byte EQUW L0627 ;R2 = $08 OSWORD A >=$01 word EQUW L0668 ;R2 = $0A OSWORD A = $00 rdln EQUW L055E ;R2 = $0C OSARGS args EQUW L052D ;R2 = $0E OSBGET bget EQUW L0520 ;R2 = $10 OSBPUT bput EQUW L0542 ;R2 = $12 OSFIND find EQUW L05A9 ;R2 = $14 OSFILE file EQUW L05D1 ;R2 = $16 OSGBPB gbpb ;0518 Tube status settings for transfer types 0..7 .LBC63 EQUB $86 ;0=bytes to host J,I=1 enable IRQ on R4,R1 EQUB $88 ;1=bytes from host M=1 enable NMI on R3 EQUB $96 ;2=words to host V,J,I=1 enable IRQ on R4,R1 EQUB $98 ;3=words from host V,M=1 enable NMI on R3 EQUB $18 ;4=address only V,M=0 disable NMI on R3 EQUB $18 ;5=(reserved) V,M=0 disable NMI on R3 EQUB $82 ;6=page to host I=1 enable IRQ on R1 EQUB $18 ;7=page from host V,M=0 disable NMI on R3 .bput ;osbput_call JSR L06C5 ;read R2DATA to A TAY ;set Y=channel JSR L06C5 ;read R2DATA to A =byte to write JSR osbput ;call OSBPUT JMP L059C ;signal completion .bget ;osbget_call JSR L06C5 ;read R2DATA to A TAY ;set Y=channel JSR osbget ;call OSBGET JMP L053A ;send C, A to R2DATA and idle .rdchz ;osrdch_call JSR osrdch ;call OSRDCH .rdchy ;053A send C, A to R2DATA and idle ROR A ;rotate C into A b7, save A b0 in C JSR L0695 ;write A to R2DATA ROL A ;restore A on entry JMP L059E ;write to R2DATA and idle .find ;osfind_call JSR L06C5 ;read R2DATA to A BEQ finda ;if A=0 then close file PHA ;else save A=call no. = open mode JSR L0582 ;read string to buffer PLA ;restore A JSR osfind ;call OSFIND JMP L059E ;write to R2DATA and idle .finda JSR L06C5 ;read R2DATA to A TAY ;set Y=file handle LDA #$00 ;restore A=0 call no. = close file JSR osfind ;call OSFIND JMP L059C ;signal completion .args ;osargs_call JSR L06C5 ;read R2DATA to A TAY ;set Y=channel LDX #$04 ;4 bytes to read: .argsw JSR L06C5 ;read R2DATA to A STA <(zram+$FF),X ;save in locations 3..0 DEX ;in descending order BNE argsw ;loop until X bytes read JSR L06C5 ;read R2DATA to A =call number JSR osargs ;call OSARGS JSR L0695 ;write A to R2DATA =return value LDX #$03 ;4 bytes to write: .argss LDA zram,X ;get locations 3..0 JSR L0695 ;write A to R2DATA DEX ;in descending order BPL argss ;loop until X+1 bytes written JMP L0036 ;go to idle loop .strng ;0582 read string to buffer LDX #<sram ;set X=0 LSB of buffer address LDY #$00 ;set Y=0 buffer offset .strnh JSR L06C5 ;read R2DATA to A STA sram,Y ;save in string buffer INY ;in ascending order, increment offset BEQ strnj ;if end of buffer reached then stop CMP #$0D ;else test character read BNE strnh ;if =CR then string terminated else loop .strnj LDY #>sram ;set Y=$07 MSB of buffer address RTS .cli ;oscli_call JSR L0582 ;read string to buffer JSR oscli ;call OSCLI .qrply ;059C Signal completion LDA #$7F .rply BIT r2stat ;test R2STAT BVC rply ;loop until b6=1, not full STA r2data ;write A to R2DATA .mj JMP L0036 ;go to idle loop .file ;osfile_call LDX #$10 ;16 bytes to read: .filea JSR L06C5 ;read R2DATA to A STA zram+$01,X ;save to locations 17..2 DEX ;in descending order BNE filea ;loop until X bytes read JSR L0582 ;read string to buffer STX zram+$00 ;save buffer address at 0,1 STY zram+$01 ;=$0700 LDY #>zram ;set Y=0; X=0 from 0582 JSR L06C5 ;read R2DATA to A =call number JSR osfile ;call OSFILE JSR L0695 ;write A to R2DATA =return value LDX #$10 ;16 bytes to write: .fileb LDA zram+$01,X ;get locations 17..2 JSR L0695 ;write A to R2DATA DEX ;in descending order BNE fileb ;loop until X bytes written BEQ mj ;then go to idle loop .gbpb ;osgbpb_call LDX #$0D ;13 bytes to read: .gbpba JSR L06C5 ;read R2DATA to A STA <(sram+$FF),X ;save to locations 12..0 DEX ;in descending order BNE gbpba ;loop until X bytes read JSR L06C5 ;read R2DATA to A LDY #>zram ;set Y=0; X=0 from loop JSR osgbpb ;call OSGBPB PHA ;save return value LDX #$0C ;13 bytes to write: .gbpbb LDA zram,X ;get locations 12..0 JSR L0695 ;write A to R2DATA DEX ;in descending order BPL gbpbb ;loop until X bytes written PLA ;restore A=return value JMP L053A ;send C, A to R2DATA and idle .sbyte ;short_osbyte JSR L06C5 ;read R2DATA to A TAX ;save X=first parameter JSR L06C5 ;read R2DATA to A =call number JSR osbyte ;call OSBYTE .sbytc BIT r2stat ;test R2STAT BVC sbytc ;loop until b6=1, not full STX r2data ;write X to R2DATA =result .bytex JMP L0036 ;go to idle loop .byte ;long_osbyte JSR L06C5 ;read R2DATA to A TAX ;set X=first parameter JSR L06C5 ;read R2DATA to A TAY ;set Y=second parameter JSR L06C5 ;read R2DATA to A =call number JSR osbyte ;call OSBYTE EOR #$9D ;if A=$9D fast Tube BPUT BEQ bytex ;then end call without handshake ROR A ;else rotate C into A b7 JSR L0695 ;write A to R2DATA .bytec BIT r2stat ;test R2STAT BVC bytec ;loop until b6=1, not full STY r2data ;write Y to R2DATA =second result BVS sbytc ;write X to R2DATA and idle (always) .word ;osword_call JSR L06C5 ;read R2DATA to A =call number TAY ;hold in Y .LBD76 BIT r2stat ;test R2STAT BPL LBD76 ;loop until b7=1, data available LDX r2data ;read R2DATA to X =control block size DEX ;decrement X BMI wordc ;if X was not in range 1..128 then no bytes .wordb BIT r2stat ;else test R2STAT BPL wordb ;loop until b7=1, data available LDA r2data ;read R2DATA to A STA wram,X ;save to locations $0128+(X-1..0) DEX ;in descending order BPL wordb ;loop until X bytes written TYA ;restore A=call number .wordc LDX #<wram ;point XY to OSWORD control block at $0128 LDY #>wram JSR osword ;call OSWORD .wordd BIT r2stat ;test R2STAT BPL wordd ;loop until b7=1, data available LDX r2data ;read R2DATA to X =control block size DEX ;decrement X BMI woren ;if X was not in range 1..128 then no bytes .worde LDY wram,X ;else get byte of control block at ..$0128 .wordf BIT r2stat ;test R2STAT BVC wordf ;loop until b6=1, not full STY r2data ;write Y to R2DATA DEX ;in descending order BPL worde ;loop until X bytes written .woren JMP L0036 ;go to idle loop .rdln ;osword0_call LDX #$04 ;5 bytes to read: .rdlna JSR L06C5 ;read R2DATA to A STA zram,X ;save to locations 4..0 DEX ;in descending order BPL rdlna ;loop until X+1 bytes read INX ;set X=0 LDY #>zram ;set Y=0; point XY to OSWORD control block TXA ;set A=0 read line from input stream JSR osword ;call OSWORD BCC rdlnc ;if user pressed ESCAPE LDA #$FF ;then A=$FF carry set/error condition JMP L059E ;write to R2DATA and idle .rdlnc LDX #$00 ;else X=0 offset into string buffer LDA #$7F ;set A=$7F carry clear/no error JSR L0695 ;write A to R2DATA .rdlnd LDA sram,X ;get character from string buffer JSR L0695 ;write A to R2DATA INX ;increment offset CMP #$0D ;test character just written BNE rdlnd ;if =CR then string terminated else loop JMP L0036 ;go to idle loop .rdcha BIT r2stat ;test R2STAT BVC rdcha ;loop until b6=1, not full STA r2data ;write A to R2DATA RTS .wrifor BIT r4stat ;test R4STAT BVC wrifor ;loop until b6=1, not full STA r4data ;write A to R4DATA RTS .escape ;06A7 Copy ESCAPE flag to coprocessor (NAUG) LDA escflg ;get MOS ESCAPE flag SEC ;rotate 1 into bit 7, ESCAPE flag in bit 6 ROR A ;A >= $80 indicating ESCAPE flag update BMI esca ;write A to R1DATA (always) .event ;06AD Event handler PHA ;save event type LDA #$00 ;set A=$00 to indicate event JSR L06BC ;write A to R1DATA TYA ;transfer Y=second event parameter to A JSR L06BC ;write A to R1DATA TXA ;transfer X=first event parameter to A JSR L06BC ;write A to R1DATA PLA ;restore event type to A: .esca BIT r1stat ;test R1STAT BVC esca ;loop until b6=1, not full STA r1data ;write A to R1DATA RTS .newrc BIT r2stat ;test R2STAT BPL newrc ;loop until b7=1, data available LDA r2data ;read R2DATA to A and return RTS .LBE19 ;Print "COPYRIGHT NOTICE" JSR LA3D2 ;print VDU sequence immediate EQUB $83 ;yellow alphanumerics EQUB $8D ;double height EQUS " C O P Y R I G H T N O T I C E" EQUB $0D EQUB $0A EQUB $FF RTS .LBE42 ;*COPYRIGHT JSR LAD74 ;set display MODE 7 JSR pcrlf ;print newline JSR LBE19 ;print "COPYRIGHT NOTICE" twice JSR LBE19 JSR LA3D2 ;print VDU sequence immediate EQUB $0D EQUB $0A EQUB $0A EQUS "This Double Density Operating System" EQUB $0D EQUB $0A EQUS "was developed for the BBC computer by" EQUB $0D EQUB $0A EQUB $83 ;yellow alphanumerics EQUS "SLOGGER SOFTWARE and OPUS SUPPLIES" EQUB $0D EQUB $0A EQUS "Any unauthorised copying of this" EQUB $0D EQUB $0A EQUS "product is unlawful and may result in" EQUB $0D EQUB $0A EQUS "Slogger or Opus taking appropriate" EQUB $0D EQUB $0A EQUS "action." EQUB $0D EQUB $0A EQUB $0A EQUB $FF RTS SKIPTO $BFFF ;make 16 KiB ROM image EQUB $00 .end L0032 = tstart-newbr+zptube L0036 = main -newbr+zptube L0414 = adrrel-LBB4B+rtube L0421 = adrral-LBB4B+rtube L0484 = begin -LBB4B+rtube L04CB = taddr -LBB4B+rtube L04D2 = setadr-LBB4B+rtube L0518 = LBC63 -LBC4B+rtube+$0100 L0520 = bput -LBC4B+rtube+$0100 L052D = bget -LBC4B+rtube+$0100 L0537 = rdchz -LBC4B+rtube+$0100 L0542 = find -LBC4B+rtube+$0100 L053A = rdchy -LBC4B+rtube+$0100 L055E = args -LBC4B+rtube+$0100 L0582 = strng -LBC4B+rtube+$0100 L0596 = cli -LBC4B+rtube+$0100 L059C = qrply -LBC4B+rtube+$0100 L059E = rply -LBC4B+rtube+$0100 L05A9 = file -LBC4B+rtube+$0100 L05D1 = gbpb -LBC4B+rtube+$0100 L05F2 = sbyte -LBC4B+rtube+$0100 L0607 = byte -LBC4B+rtube+$0100 L0627 = word -LBC4B+rtube+$0100 L0668 = rdln -LBC4B+rtube+$0100 L0695 = rdcha -LBC4B+rtube+$0100 L069E = wrifor-LBC4B+rtube+$0100 L06A7 = escape-LBC4B+rtube+$0100 L06AD = event -LBC4B+rtube+$0100 L06BC = esca -LBC4B+rtube+$0100 L06C5 = newrc -LBC4B+rtube+$0100 IF _DDOS358 save "ddos358.rom",lang,end ELIF _DDOS318 save "ddos318.rom",lang,end ELIF _DDOS338 save "ddos338.rom",lang,end ELIF _DDOS328 save "ddos328.rom",lang,end ELIF _DDOS368 save "ddos368.rom",lang,end ELIF _DDOS378 save "ddos378.rom",lang,end ELIF _DDOS388 save "ddos388.rom",lang,end ELSE save "ddos348.rom",lang,end ENDIF ;End of master358.asm.txt