;Disassembly of Opus DDOS 3.46 variants ;Greg Cook, 27 September 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. ;[D] marks differences from Opus DDOS 3.46. ;Potential space savings can be found near comments containing ;"redundant", "unused", "can save", "unreachable code", ";" ;Define any of the following symbols on the BeebAsm command line ;to assemble one of the six enclosed versions of Opus DDOS ;and to select build options. ;For example: ; beebasm -i master357.asm.txt -D _DDOS357 ; ;Alternatively pass this file through the attached beebasmpp.pl ;to strip unused conditional assembly paths, for readability ;or to make the current build options permanent. ;Symbol: _DDOS357 ;Source: https://mdfs.net/Mirror/Image/Challenger/DISS.ZIP ;Path: DDOS357 (edited) ;Compatibility: Built-in interface on Master 128, WD 1770 ;Banner version: 3.58 ;*ROMID string: 3.45 01-03-86 ;Code length: &3FEC (=&3F59-&0013+&00A6) ;Acorn CRC: &557B ;PKZIP CRC: &FCEE5296 ;XFER CRC: &19BB3FE0 ;Cksum: 3101184042 ;Symbol: _DDOS316 ;Source: http://regregex.bbcmicro.net/romsearch.zip ;Path: romsearch/slogger/Slogger-DDOS-3.16-Copyright-c-1984-Slogger-Software-alt1 (edited) ;Compatibility: Opus DDOS interface for Model B, WD 2791 ;Banner version: 3.16 ;*ROMID string: 3.16 01-03-86 ;Code length: &3FB0 (=&3F30-&000F+&008F) ;Acorn CRC: &2BF9 ;PKZIP CRC: &D8113C59 ;XFER CRC: &98FE31F7 ;Cksum: 273484074 ;Symbol: _DDOS336 ;Source: http://regregex.bbcmicro.net/romsearch.zip ;Path: romsearch/slogger/Slogger-DDOS-3.36-Copyright-c-1984-Slogger-Software (edited) ;Compatibility: Opus DDOS interface for Model B, WD 2793 ;Banner version: 3.36 ;*ROMID string: 3.36 01-03-86 ;Code length: &3F9E (=&3F1E-&000F+&008F) ;Acorn CRC: &D2FF ;PKZIP CRC: &87E8F243 ;XFER CRC: &FC13F32F ;Cksum: 1774514682 ;Symbol: _DDOS326 ;Source: https://mdfs.net/Mirror/Image/Challenger/DISS.ZIP ;Path: DDOS346 (edited) ;Compatibility: Acorn interface on Model B/B+, WD 1770 ;Banner version: 3.26 ;*ROMID string: 3.26 16-12-22 ;Code length: &3F46 (=&3F52-&0010+$008F) ;Acorn CRC: &C67B ;PKZIP CRC: &BDCB356E ;XFER CRC: &E7133A8C ;Cksum: 3000826861 ;Symbol: _DDOS346 ;Source: http://wouter.bbcmicro.net/bbc/bestanden/roms-2009.01.28.zip ;Path: roms/kopie_van_disk/Opus/Opus__Slogger_DDOS__3.46 (edited) ;Compatibility: Opus DDOS interface for Model B, WD 1770 ;Banner version: 3.46 ;*ROMID string: 3.46 01-03-86 ;Code length: &3FC0 (=&3F40-&000F+&008F) ;Acorn CRC: &1DB9 ;PKZIP CRC: &206135B2 ;XFER CRC: &2D33008D ;Cksum: 198100095 ;Symbol: _DDOS346PRE (default) ;Source: https://mdfs.net/Mirror/Image/Challenger/DISS.ZIP ;Path: DDOS346 (edited) ;Compatibility: Opus DDOS interface for Model B, WD 1770 ;Banner version: 3.46 ;*ROMID string: 3.45 01-03-86 ;Code length: &3FBF (=&3F3F-&000F+&008F) ;Acorn CRC: &5062 ;PKZIP CRC: &F93E5CCA ;XFER CRC: &87B9AB8A ;Cksum: 2039173377 _DDOS357 =? 0 _DDOS316 =? 0 _DDOS336 =? 0 _DDOS326 =? 0 _DDOS346 =? 0 IF _DDOS346 _DDOS346PRE =? 1 _LATE =? 1 ENDIF _DDOS346PRE =? 0 _LATE =? 0 ;AND #$01 alteration in latest DDOS 3.46 release ;Symbols to enable RAM disc use. ;Maximum paged RAM supported (some boards fitted with less): _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 _TWOMEG =? 0 ;Model B, 128 KiB on Solidisk Twomeg 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 _MASTER =? 0 ;Master, 128 KiB; 64 KiB internal + 2x 32 KiB cartridges _RAMINIT64K =? 0 ;*RAMINIT creates 64 KiB RAM disc catalogue _RAMINIT76K =? 0 ;*RAMINIT creates 76 KiB RAM disc catalogue _RAMINIT256K=? 0 ;*RAMINIT creates 256 KiB RAM disc catalogue ;Other interoperability symbols: _MOS350 =? 0 ;(DDOS 3.57 only) Enable all *commands under MOS 3.50 ;An accompanying module to speed up OSGBPB calls is available. ;http://regregex.bbcmicro.net/#prog.dfsfix CPU 1 ;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 _DDOS357 sense = $00 ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $80 ;sense of FDC status register compared to WD 2793 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 ELIF _DDOS316 sense = $FF ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $00 ;sense of FDC status register compared to WD 2793 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 ELIF _DDOS336 sense = $00 ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $00 ;sense of FDC status register compared to WD 2793 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 ELIF _DDOS326 sense = $00 ;sense of FDC data bus pins (WD 2791 = $FF, else $00) stsens = $80 ;sense of FDC status register compared to WD 2793 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 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 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 ENDIF ;DDOS parameters IF _RAMINIT64K ramsiz = $0100 ;RAM disc volume size in sectors to set during *RAMINIT ELIF _RAMINIT76K ramsiz = $0130 ;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 wrongl = $00B0 ;probable mistake for rtxszl 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 wrongh = $00B1 ;probable mistake for rtxszh 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 noop = $00B4 ;startup options while initialising private page (unused) bitwrk = $00B4 ;reason code in A on entry to OSFIND, for testing by BIT dcbofs = $00B4 ;channel workspace offset while comparing names of open handles gbadr = $00B4 ;2 bytes; pointer to user's OSGBPB control block mulerl = $00B4 ;LSB multiplier (unused) 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 mulerh = $00B5 ;MSB multiplier (unused) 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 frmdrv = work +$0000 ;drive argument to *FORMAT (unused) frmilv = work +$0001 ;interleave argument to *FORMAT (unused) 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 _DDOS357 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 base = $0E00 ;start of absolute workspace 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) exitsp = mainws+$0024 ;stack pointer to restore on command exit (redundant to restsp) 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 tubcom = mainws+$0082 ;direction of Tube transfer, written at $9301, otherwise unused 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) unused = mainws+$00CE ;cleared at $82B7, otherwise unused 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 _DDOS357 IF _MOS350 fswch = $F8A5 ;internal FileSwitch entry point in MOS 3.50 ELSE fswch = $FB69 ;internal FileSwitch entry point in MOS 3.20 ENDIF ELSE fswch = $FF2D ;internal ROMFSC entry point ENDIF ;System hardware sheila = $FE00 ;page of addresses for internal MMIO devices romsw = sheila+$0030 ;ROMSEL paged ROM selection latch IF _DDOS357 latch = sheila+$0024 ;floppy drive interface control latch fdc = sheila+$0028 ;base of floppy drive controller registers ELIF _DDOS316 latch = sheila+$0084 ;floppy drive interface control latch fdc = sheila+$0080 ;base of floppy drive controller registers ELIF _DDOS336 latch = sheila+$0084 ;floppy drive interface control latch fdc = sheila+$0080 ;base of floppy drive controller registers ELIF _DDOS326 latch = sheila+$0080 ;floppy drive interface control latch fdc = sheila+$0084 ;base of floppy drive controller registers ELSE latch = sheila+$0084 ;floppy drive interface control latch fdc = sheila+$0080 ;base of floppy drive controller registers 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 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 ;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 IF _DDOS357 JMP LBFE8 ;Service entry ELSE JMP called ;Service entry ENDIF EQUB $82 ;rom type: service only EQUB $1A ;copyright offset pointer EQUB $35 ;version No. IF _DDOS357 EQUS "Master DDOS " ;title EQUB $00 ;terminator byte EQUS "3.57" ;version string EQUB $00 ;terminator byte EQUS "(C)Copyright (c) 1990 F.T.C." EQUB $00 ;terminator byte EQUS "Software " EQUB $00 ELIF _DDOS316 EQUS "Slogger DDOS" ;title EQUB $00 ;terminator byte EQUS "3.x5" ;version string EQUB $00 ;terminator byte EQUS "(C)Copyright (c) 1984 Slogger Software" EQUB $00 ;terminator byte ELIF _DDOS336 EQUS "Slogger DDOS" ;title EQUB $00 ;terminator byte EQUS "3.x5" ;version string EQUB $00 ;terminator byte EQUS "(C)Copyright (c) 1984 Slogger Software" EQUB $00 ;terminator byte ELIF _DDOS326 EQUS "Otus DDOS B+" ;title EQUB $00 ;terminator byte EQUS "3.26" ;version string EQUB $00 ;terminator byte EQUS "(C)Copyright (c) 2022 Otus" EQUB $00 ;terminator byte ELSE EQUS "Slogger DDOS" ;title EQUB $00 ;terminator byte EQUS "3.x5" ;version string EQUB $00 ;terminator byte EQUS "(C)Copyright (c) 1984 Slogger Software" EQUB $00 ;terminator byte ENDIF SKIPTO $8042 .osfscm ;Issue Filing System Call JMP (fscvec) IF _DDOS357 .called ;ROM service JSR there1 ;service calls $FE, $FF CMP #$01 BNE savpri CPY #(>slots)+$05 ;Service call $01 = reserve absolute workspace BCS savpri ;if workspace < 9 pages (+$0E = $17) LDY #(>slots)+$05 ;then reserve 9 pages abs workspace. .savpri CMP #$02 BCC L806F BNE pmsg ELSE EQUW LA899 ;pointer to DDOS version information $A899 .called ;ROM service JSR there1 ;service calls $FE, $FF CMP #$01 BNE L8054 CPY #(>slots)+$05 ;Service call $01 = reserve absolute workspace BCS L8053 ;if workspace < 9 pages (+$0E = $17) LDY #(>slots)+$05 ;then reserve 9 pages abs workspace. .L8053 RTS .L8054 CMP #$02 BNE pmsg ENDIF TYA ;Service call $02 = reserve private workspace STA blkptr+$01 ;y=lowest free page, store in pointer STA priptr,X ;and in the MOS workspace for this purpose 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 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 LDA #$02 ;restore call number and pass to next ROM. .L806F RTS .pmsg IF _DDOS357 CMP #$04 BEQ chkcom BCS dohelp ELSE CMP #$03 BNE chkcom ENDIF 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 JMP L81DB ;initialise DDOS and boot default volume .chkcom ;Service call $04 = unrecognised *command IF _DDOS357 ELSE CMP #$04 BNE dohelp ENDIF 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 unkswz ;if not found then exit STX synofs ;else save pointer to syntax byte in workspace IF _DDOS357 PHY ;save offset of command line tail on stack ELSE TYA ;save offset of command line tail on stack PHA ENDIF 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 #LBF40 BNE L80CA .L80C6 IF _DDOS357 PLY ;else restore offset of command line tail ELSE PLA ;else restore offset of command line tail TAY ENDIF .L80C7 JMP (action) ;and execute *command. .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 .dohelp CMP #$09 BNE inifsy JSR savita ;Service call $09 = *HELP IF _DDOS357 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. .L80E9 ;Scan *HELP keyword PHY ;y=offset of keyword from GSINIT pointer TYA ;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 PLY ;restore string offset .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 .unkswz 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 JMP init ;and initialise DDOS ELSE LDA (linptr),Y ;save AXY, test char at start of *HELP string CMP #$0D ;if not CR then *HELP called with keyword BNE dhelp0 ;so scan keyword LDX #hlptab LDA #$04 ;4 entries to print JSR help1 ;print *HELP keywords and pass on the call. .unkswz RTS .dhelp0 ;Scan *HELP keyword JSR setupr ;call GSINIT with C=0 BEQ unkswz ;if string is empty then pass call on TYA ;else a=offset of keyword from GSINIT pointer PHA ;also save on stack LDX #hlptab JSR wname0 ;search for keyword in table BCS hlpnxt ;if keyword found JSR L80C7 ;then call its action address; print help .hlpnxt PLA ;restore string offset TAY .L8105 JSR gsread ;call GSREAD BCC L8105 ;until end of argument (discarding it) JMP dhelp0 ;then scan next *HELP keyword .inifsy ENDIF .relmem CMP #$0A BNE unkwrd 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 L8139 ;if b7 clear then we already vacated, exit IF _DDOS357 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) ELSE LDY #$00 ;else set offset = 0: .L811F CPY #prsist-mainws ;store bytes $10C0..$11BF in private page BCC L8128 ;(wrapped around so that private page LDA mainws,Y ;contains: $1100..BF, $10C0..FF) BCS L812B .L8128 LDA seqmap,Y .L812B STA (blkptr),Y INY BNE L811F JSR ensur ;ensure all files up-to-date on disc (flush) LDY #memflg ;clear flag at offset $C1 of private page LDA #$00 ;b7=1 iff we own the shared workspace. STA (blkptr),Y ENDIF .L8139 RTS .unkwrd CMP #$08 ;if call not $1,2,3,4,8,9,A,FE,FF then return BNE L8139 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 LDA #$00 CPY #$7E BEQ getdsz ;OSWORD A = $7D TAY ;store in OSWORD control block offset 0 LDA cycno ;get catalogue cycle number BCC L81D7 ;return A = 0, claiming service call. .getdsz ;OSWORD A = $7E get size of volume in bytes LDY #$03 STA (blkptr),Y ;store 0 at offset 3: less than 16 MiB LDA #$03 ;set A = 3 as mask DEY ;offset 2 AND dirhig+$06 ;extract MSB volume size STA (blkptr),Y ;save as 2MSB volume size DEY ;offset 1 LDA dirhig+$07 ;get LSB volume size from catalogue STA (blkptr),Y ;save as 3MSB volume size DEY ;offset 0 TYA ;store 0: volume size multiple of 256 bytes .L81D7 STA (blkptr),Y ;Y = 0; store LSB volume size or cycle no. TYA ;return A = 0, claiming service call .L81DA RTS .L81DB 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 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 RTS .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 .init ;*DISC / *DISK PHA JSR sinit0 ;initialise DDOS PLA RTS .sinit0 ;Initialise DDOS LDA #$00 TSX STA stack+$08,X ;have A=0 returned on exit 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 STA colds ;set same flag in shared workspace JSR getmem ;claim 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 unused ;unused 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 JSR readsw ;call OSBYTE $FF = read/write startup options STX noop ;save them in zero page (unused) JMP LB665 ;re-read options and set drive stepping rate .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 .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 TXA ;else compare LSBs dest - source CMP todolo ;1 CPX 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 CLC ;1 CMP ADC #$FF ;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 IF _DDOS357 LDY tmpcin,X LDA work,X STY work,X STA tmpcin,X DEX BPL L870B RTS ;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 .L872F CMP #$09 BNE L874E JSR savita ;FSC 9 = *EX. save AXY JSR stxylp ;set GSINIT pointer to XY, set Y=0 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 JMP info0 ;print *INFO lines for all matching files .L874E JMP L94CE ;serve other FSC calls ELSE LDA tmpcin,X LDY work,X STA work,X TYA STA tmpcin,X DEX BPL L870B RTS ;unreachable code JSR chkdsf ;select source volume JSR dirldy ;load volume catalogue JSR L850E ;save parameters of source drive LDA dirhig+$07 ;get LSB volume size STA todolo ;store in zero page LDA dirhig+$06 ;get boot option/top bits volume size AND #$03 ;extract MSB volume size STA todohi ;store in zero page LDA dirhig+$06 ;get boot option/top bits volume size AND #$F0 ;extract boot option to b7..4 STA srcopt ;store in workspace JSR chkdst ;select destination volume JSR dirldy ;load volume catalogue JMP L8515 ;save parameters of destination drive ;unreachable code ;compare new volume size - old volume size LDA dirhig+$06 ;get boot option/top bits volume size AND #$03 ;extract MSB volume size CMP todohi ;compare with stored MSB volume size BCC L8750 ;return C=0, Z=0 if new size < old size BNE L8750 ;return C=1, Z=0 if new size > old size LDA dirhig+$07 ;if MSBs equal, load LSB old size CMP todolo ;return C=1, Z=1 if sizes equal, or as above .L8750 RTS ENDIF .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 .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 IF _DDOS357 LDA #$20 .L8868 LDX #$00 BEQ L8874 ;branch (always) ELSE LDX #$00 LDA #$20 BNE L8874 ;branch (always) ENDIF .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 LDA #$00 ;set MSB length = 0; transfer less than 64 KiB STA txsizh LDX wrkcat+$06 ;x = MSB of relative LBA JMP 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 .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 .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 .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 BEQ L8A6D JMP 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 ;[BUG]*STAT 4 prints stats 0..8 times ;if last disc double density BIT denflg ;test density flag BVS L8A83 ;if double density then *STAT eight volumes JMP L8AA7 ;else *STAT the single volume .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 .L8AA7 ;*STAT specified volume JSR dirld ;ensure current volume catalogue loaded JSR L8C00 ;print disc type and volume list JMP L8D16 ;print volume statistics .L8AB0 ;*XCAT LDX #$00 ;x=0, nothing specified (unused) 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 ;[BUG]*XCAT 4 prints catalogue 0..8 times ;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 .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 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" .L8D08 ;Print " Drive " plus volume spec in A JSR gstrng EQUS " Drive " NOP JMP L8BBA ;print volume spec in A .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 .L8D72 ;Print sector count as kilobytes JSR L8D81 ;divide sector count by 4 JSR LAE5D ;convert binary word to four decimal digits JMP LB066 ;print four decimal digits, space-padded .L8D7B ;Print "K" JSR gstrng EQUS "K" NOP RTS .L8D81 ;Divide word by 4 LSR divenh ROR divenl LSR divenh ROR divenl 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,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 .return ;Return from unrecognised keyword RTS .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. ;unreachable code PHA ;set up trampoline to write table at XY LDA #$9D JMP L8F2B .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 .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 JMP getlok ;ensure file matching spec in catalogue .L8FEA ;Set current file from argument JSR chksyn ;call GSINIT with C=0 and require argument JMP getnam ;set current file from file spec .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 .blkwr ;Write ordinary file L5 JSR wtint ;prepare to write from user memory JMP blkxx ;transfer ordinary file L5 .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 JMP dskflt ;then raise "Disk fault" error .L92C0 JMP LA338 ;else raise "Disk read only" error. .L92C3 JMP nmirel ;release NMI and exit. ;unreachable code LDA #$00 ;data transfer call $00 = read data STA txcall ;set data transfer call number JMP L92D3 ;transfer disc/volume catalogue L3 .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 .L92F6 PHA ;save FDC command LDA #$40 ;instruction at $0D00 = RTI STA intnmi+$00 PLA ;restore FDC command JMP LB6B9 ;and write it to command register .chktub ;Open Tube data transfer channel PHA STA tubcom ;a=Tube service call, save in stack and wksp 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 LDA #$FF STA catdrv ;no catalogue in pages $0E..F RTS .L936C ;*TAPEDISK JSR LA2E2 ;save XY 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 ;*ROMID JSR wopa ;have A=0 returned on exit .L942D 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 IF _DDOS357 ELSE .LBF40 ENDIF .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 IF _DDOS357 .L94CE CMP #$0B ;if call number is not 11 BNE wfscm ;then serve other FSC calls, else: .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) ELSE .wnota ;FSC 2 = */; FSC 4 = *RUN JSR stxylp ;set GSINIT pointer to XY, set Y=0 .defcom ;FSC 3 with *command not in table ENDIF JSR supld ;copy argument ptr and load to cat address STY linadr+$01 ;store offset of start of command line JSR frmnam ;set current file from argument pointer STY linadr+$00 ;store offset of command line tail JSR lookup ;search for file in catalogue BCS defsuc ;if found then execute command binary LDY linadr+$01 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 IF _DDOS357 JMP LB9BF ;else issue FSC 11 or raise "Bad command" ELSE .LB9CC JSR illmsg ;else raise "Bad command" error. EQUB $FE EQUS "command" EQUB $00 ENDIF .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 JMP LA92B ;emulate Acorn DFS entry conditions .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 #$00 BNE wfind0 ;if A>0 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 RTS .wfind0 ;Open a file JSR savit ;save XY STX work+$00 STY work+$01 STA bitwrk ;store file open mode in temporary var. BIT bitwrk ;set N and V 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 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) RTS ;then existing file was expected, return A=0 .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 JMP L96C8 ;(to force a read) SKIPTO $96C8 .L96C8 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 BCC vradr ;if A=0 or A=2 then return PTR or EXT JMP LBF7C ;else A=1 set PTR or A=3 set EXT .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. .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 .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 .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 .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. .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 LSR A ;else shift right five times, divide by 32 LSR A ;to produce an offset 1..7 LSR A ;corresponding to channels $11..17 LSR A LSR A 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 = 1 BNE chekyb ;then return C=0 .chekyz PLA ;else return C=1 SEC .L9882 RTS .chekyb PLA CLC 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 ;3 rm LDY 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 PHA ;set N and Z according to A PLA RTS ;exit .setda ;Set buffer sector address from PTR CLC LDA seqloc,Y ;get LSB start sector of open file ADC seqpm,Y ;add 2MSB of PTR STA lbalo ;store LSB sector address STA seqdal,Y ;store LSB sector address of buffer LDA seqlh,Y ;get top bits exec/length/load/start sector AND #$03 ;extract MSB start sector ADC seqph,Y ;add MSB of PTR STA lbahi ;store MSB sector address STA seqdah,Y ;store MSB sector address of buffer .setcbf ;Set buffer-contains-PTR channel flag LDA #$80 ;b7=1 buffer contains byte at PTR: .setbit ;Set channel flag bits (A = OR mask) ORA seqflg,Y BNE clrbt3 ;store if >0 else fall through harmlessly: .clrcbf ;Clear buffer-contains-PTR channel flag: LDA #$7F .clrbit ;Clear channel flag bits (A = AND mask) AND seqflg,Y .clrbt3 STA seqflg,Y CLC RTS ;unreachable code JSR savita ;save AXY .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 JSR LBF5D ;handle OSFILE calls 7,9-11 or validate index 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 .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 JMP blkwr ;write ordinary file L5 .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 .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 JMP blkrd ;read ordinary file L5 and exit .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 SKIPTO $9BE3 .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 LBFC2 ;set up user pointer and clear EOF warning 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 LDA #$FF STA catdrv ;forget catalogue in pages $0E..F RTS .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 ;2 rm LDA LDA lodhi ;compare new load address - HIMEM CMP hipage 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 LDX #$0A ;service call $0A = workspace claimed JSR doswcl ;call OSBYTE $8F = issue service call 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 #$15 ;21 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 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) INX ;increment offset DEY ;decrement number of spaces remaining JMP LA02E ;and loop .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 IF _DDOS357 BRA LA17F ;print *INFO line and loop for next file .LA163 TAX ;put drive number in X LDA denflg ;get density flag AND #$40 ;extract bit 6 (1 = double density) LSR A ;move to bit 5 EOR LA16E,X ;apply flags for drive 0..3 in X RTS ;return latch in A .LA16E EQUB latch0 EQUB latch0 EOR latch1 EQUB latch0 EOR latch2 EQUB latch0 EOR latch1 EOR latch2 .cmpct2 ;Compact file JSR L9F9C ;set LBA of catalogue entry STZ cbcont ;no catalogue entry waiting to be created STZ swaps ;$00 = source and dest. are different drives ;(no swapping) ELSE JMP LA17F ;print *INFO line and loop for next file ;can save 12 bytes here; A164 = JSR L9F9C .cmpct2 ;Compact file LDA dstlo ;set LSB start sector = destination LBA STA cathig+$07,Y LDA cathig+$06,Y ;get top bits exec/length/load/start sector AND #$FC ;clear b1,b0 MSB start sector ORA dsthi ;replace with MSB destination LBA STA cathig+$06,Y ;set top bits exec/length/load/start sector LDA #$00 STA cbcont ;no catalogue entry waiting to be created STA swaps ;$00 = source and dest. are different drives ;(no swapping) ENDIF 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 .LA338 ;Raise "Disk read only" error JSR dskmsg EQUB $C9 EQUS " read only" EQUB $00 .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 LB749 ;no-op 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 ;unreachable code ASL A .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 .LA4BF ;Set current drive from ASCII digit SEC SBC #$30 ;convert ASCII digit to binary BCC LA511 ;if invalid raise "Bad drive" error STA fdrive ;else set current drive=digit, volume=A CMP #$22 ;was drive number capital R? JMP LA509 ;if so replace with 4 else accept digits 0..7. .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 BCC LA511 ;if ordinal negative then "Bad drive" CMP #volums ;else 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 STA fdrive ;set as current volume, return C=0 RTS .LA4DF ;Call GSINIT and parse mandatory vol spec JSR chksyn ;call GSINIT with C=0 and require argument JMP getdrv ;parse volume spec .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 JSR LA7D6 ;set up for drive RTS .LA509 BNE LA50D ;if drive number equals R LDA #$04 ;then substitute drive 4 .LA50D 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 .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 .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 BCC LA574 ;set volume and return current volume ;unreachable code CMP #$2A BNE LA574 LDX #$83 RTS .LA574 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 ;unreachable code JSR gsread .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 SKIPTO $A58F .LA58F ;Set up for RAM disc LDA #$00 STA sectrk ;number of sectors per track is undefined SEC ;set carry flag for later BCS LA603 ;jump to patch to continue .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. ;[BUG] If called on RAM disc then double density flag is not cleared. .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 BEQ LA5CA ;if volume letter is not A JMP LA511 ;then raise "Bad drive" error .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 JMP LA67E ;then "Disk not configured by VOLGEN" .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 .LA603 ;Set up for RAM disc part 2 STA voltrk ;a=0; data area starts on track 0 STA txlbal ;LSB LBA = 0 ROR A ;c=1; set a=$80 AND denflg ;preserve b7 = automatic density STA denflg ;clear b6=0 single density RTS ;exit .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 JMP LA6B5 ;else "80 track disk in 40 track drive". .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 .LA67E JSR vstrng EQUB $CD EQUS "Disk not configured by VOLGEN" EQUB $00 .LA6A0 JSR vstrng EQUB $CD EQUS "Bad track format" EQUB $00 .LA6B5 JSR vstrng EQUB $CD EQUS "80 track disk in 40 track drive" 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 ;unreachable code RTS .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 ;[BUG] OSFILE does not load or save files 64 KiB or larger to the RAM ;disc correctly. Only the requested length of data modulo 64 KiB ;($10000 bytes) is transferred. The rest of the file is unread or ;undefined. Such calls are only relevant to large coprocessors. ;The RAM disc code transfers up to 64 KiB at a time between the RAM disc ;and user memory. Files on the RAM disc are treated specially here and ;moved in a single operation. They can't be split into 'tracks' to ;work around the limit because the RAM code has different variables at ;locations $A0..7 which the floppy-oriented track advance code would ;clobber. This only matters when saving and loading files to a large ;coprocessor (using OSFILE). Just use OSFIND and OSGBPB instead. .LA715 ;Transfer data L3 JSR savit ;save XY LDA #l3tris ;set attempt counter to 5 STA tries .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 LDX txsizl ;else X=LSB byte count (redundant to $A721) 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? (and discard!) ;[BUG] ?$106E is constant, high word either ;never changes or increments every track. ;The wrong address never reaches the copro- ;cessor which increments the initial address ;locally and so large Tube transfers work 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 JSR nmirel ;release NMI LDA #$00 ;fake WD1770 status = 0, succeeded. RTS .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 JSR LB749 ;no-op RTS .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 .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 .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 JMP LB673 ;else test write protect state .LA7EB LDA #$00 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 LBFD2 ;E FINDV, evt + $2A = $BFD2 EQUW LBF40 ;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 ;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) ;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 ;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) ;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 _DDOS357 EQUS "FTC DDOS v3.58" ELIF _DDOS316 EQUS "OPUS DDOS 3.16" ELIF _DDOS336 EQUS "OPUS DDOS 3.36" ELIF _DDOS326 EQUS "Otus DDOS 3.26" ELSE EQUS "OPUS DDOS 3.46" ENDIF EQUB $0D EQUB $0D NOP RTS .LA899 IF _DDOS357 EQUB $03,$45 ;bcd version number = 3.45 EQUB $01,$03,$86 ;bcd date dd/mm/yy = 1 March 1986 ELIF _DDOS316 EQUB $03,$16 ;bcd version number = 3.16 EQUB $01,$03,$86 ;bcd date dd/mm/yy = 1 March 1986 ELIF _DDOS336 EQUB $03,$36 ;bcd version number = 3.36 EQUB $01,$03,$86 ;bcd date dd/mm/yy = 1 March 1986 ELIF _DDOS326 EQUB $03,$26 ;bcd version number = 3.26 EQUB $16,$12,$22 ;bcd date dd/mm/yy = 16 December 2022 ELIF _LATE EQUB $03,$46 ;bcd version number = 3.46 EQUB $01,$03,$86 ;bcd date dd/mm/yy = 1 March 1986 ELSE EQUB $03,$45 ;bcd version number = 3.45 EQUB $01,$03,$86 ;bcd date dd/mm/yy = 1 March 1986 ENDIF EQUB $00,$00,$00 ;bcd revision number = 0 (printed as spaces) .LA8A1 ;*FORMAT LDA #$05 ;set drive number = 5 (unused) STA frmdrv LDA #$00 STA frmilv ;clear interleaved format flag 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 DEC frmilv ;else set interleaved format flag (unused) 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 STA frmdrv ;save as drive number (unused) .LA8C1 JSR wopa ;have A=0 returned on exit TSX STX restsp ;set stack pointer to restore on restart STX exitsp ;set stack pointer to restore on exit 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 .LA92B ;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 .LA931 PLA ;discard return address to *VERIFY PLA ;the RTS will exit the *VERIFY command LDA #$00 ;have A=0 returned on exit: .LA935 ;Validate *VERIFY drive ;on entry A=drive number, X=stack pointer AND #$0C ;ensure drive number refers to floppy drive BNE LA931 ;if b3 or b2 set then RAM disc, quit command STA stack+$05,X ;else A=0, have A=0 returned on exit RTS ;return to *VERIFY .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) " EQUB $FF JSR LB087 ;get printable input character CMP #$53 ;is it capital S? BEQ LA9A7 ;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; [BUG] old density on screen! .LA9A7 JMP LA9F5 ;jump to format single density (was in range) .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 BEQ LA9D1 ;if no error then generate volumes JMP LA9F2 ;else exit command .LA9D1 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 LDX #<LB377 ;point XY to "Please wait" at $B377 LDY #>LB377 JSR LADA4 ;erase VDU sequence at XY (redundant) .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 JMP LAA44 ;exit command .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 RTS .LAA44 ;Exit command LDX exitsp ;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) JMP LB749 ;exit (jump to RTS) .verify ;*VERIFY JSR LA4E5 ;select specified or default volume TSX STX exitsp ;set stack pointer to restore on exit STX restsp ;set stack pointer to restore on restart JSR LA935 ;validate *VERIFY drive 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 ;[BUG]*VERIFY 4 divides by zero, hangs 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 exitsp ;set stack pointer to restore on exit 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 $83 ;yellow alphanumerics EQUS " A" EQUB $0D ;carriage return EQUB $0A ;line feed EQUB $83 ;yellow alphanumerics EQUS " B" EQUB $0D EQUB $0A EQUB $83 ;yellow alphanumerics EQUS " C" EQUB $0D EQUB $0A EQUB $83 ;yellow alphanumerics EQUS " D" EQUB $0D EQUB $0A EQUB $83 ;yellow alphanumerics EQUS " E" EQUB $0D EQUB $0A EQUB $83 ;yellow alphanumerics EQUS " F" EQUB $0D EQUB $0A EQUB $83 ;yellow alphanumerics EQUS " G" EQUB $0D EQUB $0A EQUB $83 ;yellow alphanumerics EQUS " H" EQUB $0D EQUB $0A 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 BCC LAC39 ;if out of range then display error CMP #volums 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 BEQ LAC90 ;if <256 KiB requested then continue JSR LB0C2 ;else briefly display error indication JMP LAC3C ;and ask user for another command ;can save 6 bytes here; AC88 = BNE RC39 .LAC90 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 .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 .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 .LADE9 ;Move cursor to table row in volidx LDX #$06 CLC LDA volidx ADC #$09 TAY JMP LAD80 ;move cursor to (X,Y) .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 BCC LAE3A ;(redundant) CMP #$0A RTS .LAE3A SEC 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 LDA divenl ;2 rm 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 JMP LAF1C ;and exit (jump to RTS) .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 LDA freelo ;test free space ORA freehi ;2 ORA freelo 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 LDA #$01 ;1 LDY (Chal) STA gentrk LDY #$00 .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. LDA #$01 ;256 bytes to transfer STA wrongh LDA #$00 STA wrongl LDA #$00 ;a=$00 (discarded) 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 JMP LB749 ;exit (jump to RTS) .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 ;unreachable code .LB008 ;multiply word by byte CLC LDA freelo ADC mulerl STA freelo LDA freehi ADC mulerh STA freehi DEC muland BNE LB008 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 .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 .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 JMP LADE0 ;print X spaces .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 BCS LB239 ;if >= total tracks then "Format complete" JMP LB1E5 ;else loop to format next track .LB239 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 .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 JMP LB18B ;clear rows 20..22 and exit .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 .LB428 LDA #$00 ;*4080 OFF *DENSITY SINGLE EQUB $AC .LB42B LDA #$80 ;*4080 AUTO *DENSITY AUTO STA spregs,X RTS TSX LDA #$00 STA stack+$05,X .LB437 ;*RAMINIT JSR savit ;save XY (inner) JSR nmicla ;claim NMI 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 JMP 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 NOP ;no operation 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 EOR #$00 ;no operation .LB4FA LDY #$00 ;starting offset = $00 JSR LB547-LB511+intnmi ;compute source RAM slot number JSR LB514-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 SKIPTO $B514 .LB514 JSR LB55C-LB511+intnmi ;paste high byte of source address .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 BNE LB517 ;loop until all bytes transferred DEC rtxszh BNE LB517 LDA romid ;page DDOS ROM back in STA romsw RTS ;return .LB547 ;0D36 IF _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 _TWOMEG 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 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 rtxlbh LSR A LDA rtxlbl ;abcdefgh H ROL A ROL A ROL A ;defghHab c AND #$07 ;.....Hab EOR #$04 ;45670123 STA rtxslt RTS ENDIF SKIPTO $B55C .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 ;6 rm TSX LDA #$00 STA stack+$05,X .LB56C ;*FDCSTAT JSR LA3D2 ;print VDU sequence immediate EQUB $0D EQUB $0A IF _DDOS357 EQUS "WD 1770 status : " ELIF _DDOS316 EQUS "WD 2791 status : " ELIF _DDOS336 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 ;unreachable code LDX #$02 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 .LB5C9 ;$23 Format track JMP LB88B ;3 rm .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 IF _DDOS357 LDA fdrive ;get current volume AND #$03 ;extract physical drive number, clear b7..2 PHX JSR LA163 PLX STA latch RTS ELIF _DDOS316 LDA fdrive ;get current volume AND #$03 ;extract physical drive number, clear b7..2 ORA denflg ;apply density flag in bit 6 AND #$7F ;mask off bit 7, *DENSITY AUTO STA latch RTS ELIF _DDOS336 LDA fdrive ;get current volume AND #$03 ;extract physical drive number, clear b7..2 ORA denflg ;apply density flag in bit 6 AND #$7F ;mask off bit 7, *DENSITY AUTO STA latch RTS ELIF _DDOS326 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 JMP LBF44 ;jump to patch to continue ELSE LDA fdrive ;get current volume AND #$03 ;extract physical drive number, clear b7..2 ORA denflg ;apply density flag in bit 6 AND #$7F ;mask off bit 7, *DENSITY AUTO STA latch RTS ENDIF .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 _DDOS357 RTS ;1770 doesn't need Force Interrupt?? ELIF _DDOS316 JMP LB744 ;send Force Interrupt command ELIF _DDOS336 JMP LB744 ;send Force Interrupt command ELSE RTS ;1770 doesn't need Force Interrupt?? ENDIF .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 ;[BUG] The recalibrate and seek routines raise the INTRQ pin which ;causes an NMI, but do not install code in the NMI service area. ;Any code left at the NMI entry point is executed. ;Typically there is an RTI instruction at $0D00 left by the MOS on ;reset, or by a previous NMI service routine that self-sealed. An FDC ;command that did not complete, however, leaves its ISR active and this ;services the NMI - mistreating the command completion interrupt as a ;data request - and interferes with the byte transfer count at $A0..1. ;A floppy disc operation performs a seek as a subtask after storing the ;count and before installing its service routine. It expects NMIs to be ;ignored in the meantime and because they are not, it goes on to ;transfer the wrong number of bytes. ;If re-entered, the RAM disc transfer code overwrites memory and crashes ;(original version; it is patched in this file under symbol _RAMBUGFIX.) ;Machines with Econet are also affected. ;Opus 2791/2793 board users (running DDOS 3.1x/3.3x) have their INTRQ ;pin disconnected from the NMI line and get off scot-free. .LB67C ;Recalibrate drive (seek track 0) JSR savit ;save XY IF _DDOS357 LDA #$00 ;WD1770 FDC command $00 = Restore ELIF _DDOS316 LDA #$08 ;WD2791 FDC command $08 = Restore ELIF _DDOS336 LDA #$08 ;WD2793 FDC command $08 = Restore ELSE LDA #$00 ;WD1770 FDC command $00 = Restore ENDIF 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 LDA #$00 ;set physical position of head to 0 STA head,X ;1 LSRA 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 IF _DDOS357 LDA #$10 ;WD1770 FDC command $10 = Seek ELIF _DDOS316 LDA #$18 ;WD2793 FDC command $18 = Seek ELIF _DDOS336 LDA #$18 ;WD2793 FDC command $18 = Seek ELSE LDA #$10 ;WD1770 FDC command $10 = Seek ENDIF JSR L92F6 ;execute command and wait for completion AND #$10 ;z=0 if WD1770 S4 = record not found. RTS .LB6B9 ;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 JMP LB9D8 ;else raise "Disk not formatted" error. .LB6CA 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 .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 JMP LB9EF ;write to FDC command register and exit .LB749 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 _DDOS357 LDX #$0A ;wait 26 microseconds .LB765 DEX BNE LB765 LDA fdcsta ;load FDC status register PHA ;save status ELIF _DDOS316 ELIF _DDOS336 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 _DDOS357 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 ELIF _DDOS316 ELIF _DDOS336 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 .LB88B ;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 LDY #$00 ;y=$00, unused 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 JMP 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 JMP LB8FB ;restore XY and return IF _DDOS357 SKIPTO $B939 .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 ELSE .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 STA (runptr),Y ;store run length in run table DEC runptr+$00 ;if pointers are on a page boundary INC runptr+$00 BNE LB94B SEC ;then subtract 1 from run length SBC #$01 STA (runptr),Y .LB94B ENDIF 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) IF _DDOS357 ELSE .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 ENDIF .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 _DDOS357 ELIF _DDOS316 EOR #$FF ;[DIFF] invert for WD 2791 ENDIF STA fdctrk ;store in track register of FDC RTS .dskflt ;Raise "Disk fault" error JSR dskmsg EQUB $C5 EQUS " fault" EQUB $00 IF _DDOS357 SKIPTO $B9BF .LB9BF ;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 ELSE ;unreachable code JSR vstrng EQUS $C5 EQUS "Cannot recalibrate" EQUB $00 ENDIF .LB9D8 JSR vstrng EQUB $C5 EQUS "Disk not formatted" EQUB $00 .LB9EF ;Write to FDC command register IF _DDOS357 ELIF _DDOS316 EOR #$FF ;[DIFF] invert for WD 2791 ENDIF STA fdccmd RTS .LB9F3 ;Write to FDC sector register IF _DDOS357 ELIF _DDOS316 EOR #$FF ;[DIFF] invert for WD 2791 ENDIF STA fdcsec RTS .LB9F7 ;Write to FDC data register IF _DDOS357 ELIF _DDOS316 EOR #$FF ;[DIFF] invert for WD 2791 ENDIF STA fdcdat RTS .LB9FB ;Load FDC status register LDA fdcsta AND #$80 ;mask b7 extract WD1770 S7 = motor on IF _DDOS357 EOR #$80 ;return A=0, Z=1 iff motor is on ELIF _DDOS316 ELSE EOR #$80 ;return A=0, Z=1 iff motor is on ENDIF 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 _DDOS357 ELIF _DDOS316 BMI LBA0B ;[DIFF] loop until b7=0 WD2793 S7 = not rdy ELIF _DDOS336 BMI LBA0B ;[DIFF] loop until b7=0 WD2793 S7 = not rdy ENDIF IF _LATE AND #$01 ;test bit 0 IF _DDOS357 ELIF _DDOS316 EOR #$01 ;[DIFF] invert for WD 2791 ENDIF BNE LBA0B ;loop until b0=0 WD1770 S0 = busy ELSE ROR A ;place bit 0 in carry flag BCS LBA0B ;loop until b0=0 WD1770 S0 = busy ENDIF .LBA11 LDA fdcsta ;load FDC status register IF _DDOS357 AND #$7F ;mask bits 6..0 ignore WD1770 S7 = motor on ELIF _DDOS316 EOR #$FF ;[DIFF] flip bits 7..0 keep WD2791 S7 = not rdy ELIF _DDOS336 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 _DDOS357 ELIF _DDOS316 EOR #$FF ;[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 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 _DDOS357 ELIF _DDOS316 EOR #$FF ;[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 _DDOS357 ELIF _DDOS316 BPL LBA55 ;[DIFF] loop until b7=1 WD2791 S7 = not ready ELIF _DDOS336 BMI LBA55 ;[DIFF] loop until b7=0 WD2793 S7 = not ready ENDIF ROR A ;place bit 0 in carry flag IF _DDOS357 BCS LBA55 ;loop until b0=0 WD1770 S0 = busy ELIF _DDOS316 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 _DDOS357 ELIF _DDOS316 EOR #$FF ;[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 _DDOS357 ELIF _DDOS316 EOR #$FF ;[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 _DDOS357 ELIF _DDOS316 EOR #$FF ;[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 ;unreachable code NOP ;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 EQUW L0596 EQUW L05F2 EQUW L0607 EQUW L0627 EQUW L0668 EQUW L055E EQUW L052D EQUW L0520 EQUW L0542 EQUW L05A9 EQUW L05D1 ;0518 Tube status settings for transfer types 0..7 .LBC63 EQUB $86 EQUB $88 EQUB $96 EQUB $98 EQUB $18 EQUB $18 EQUB $82 EQUB $18 .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 $BF40 IF _DDOS357 .LBF40 ;FSC ;referenced at $80BB..C3, $A83F..40 CMP #$0A BNE LBF56 JSR savita ;FSC 10 = *INFO. save AXY JSR stxylp ;set GSINIT pointer to XY, set Y=0 LDX #<infcom ;point XY to *INFO command table entry LDY #>infcom JSR L8F28 ;set up trampoline to read *INFO entry LDY #$00 ;set Y = 0 offset for GSINIT JMP info ;jump into *INFO .LBF56 JMP L872F ;serve other FSC calls ELIF _DDOS316 ELIF _DDOS336 ELIF _DDOS326 .lchtbl EQUB latch0 EQUB latch0 EOR latch1 EQUB latch0 EOR latch2 EQUB latch0 EOR latch1 EOR latch2 .LBF44 AND #$40 ;extract bit 6 (1 = double density) LSR A ;move to bit 3 LSR A LSR A EOR lchtbl,X ;apply flags for drive 0..3 in X STA latch ;store in control latch PLA ;restore X and exit TAX RTS ENDIF SKIPTO $BF59 .LBF59 ;call OSFILE 2 write load addr or 0 save: ASL A ;move bit 0 of A to bit 1 AND #$02 ;extract it; 9 -> 2, 10 -> 0 TAX ;set table offset to 3 or 1: .LBF5D ;Handle OSFILE calls 7,9-11 or validate index INX ;enter with X = A, convert X to table offset CPX #$0C ;is it OSFILE 11 = create file with stamp? BCS LBF68 ;if OSFILE $0C..FE then C=1, return on exit CPX #$0A ;else if OSFILE 9 = set filetype/stamp BCS LBF59 ;or OSFILE 10 = save file w/stamp, translate CPX #$08 ;else is it OSFILE 7 = create file? if not .LBF68 BNE LBF74 ;then return X=offset, C=call unknown, else: ;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 JSR chukbk ;return catalogue information to OSFILE block SEC ;set C=1, return on exit .LBF74 RTS .LBF75 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: .LBF7C ;OSARGS A=1/3, Y>0 set PTR/EXT BEQ LBFB4 ;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 LBF75 ;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 LBFA1 ;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) .LBFA1 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 BCC LBF74 ;if PTR < EXT then return, else: .LBFB4 ;OSARGS A=1, Y>0 JMP vstar ;set PTR = request JSR savita ;save AXY again .LBFBA JSR dcrych ;ensure file handle valid and open LDA #$EF ;b4=0 EOF warning flag clear JMP clrbit ;clear channel flag bits .LBFC2 ;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 JMP makatp ;set up pointer to user's OSGBPB block .LBFD2 ;OSFIND PHA ;save call number AND #$48 ;test b6=read access, b3=error if not found CMP #$48 ;set carry flag iff b6, b3 both set PLA ;restore call number PHP ;save carry flag AND #$C0 ;pass only b7=write access, b6 to DDOS JSR wfind ;call DDOS routine PLP ;restore carry flag EOR #$00 ;test if file handle in A is non-zero BCC LBF74 ;if no error is required then return BNE LBF74 ;if a file was opened then return JMP nofil ;else raise "File not found" error. IF _DDOS357 .LBFE8 ;ROM service CMP #$25 BNE LBFFC ;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 RTS .LBFFC JMP called ;service other calls ENDIF SKIPTO $BFFF EQUB $00 ;make 16 KiB ROM image .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 _DDOS357 save "ddos357.rom",lang,end ELIF _DDOS316 save "ddos316.rom",lang,end ELIF _DDOS336 save "ddos336.rom",lang,end ELIF _DDOS326 save "ddos326.rom",lang,end ELIF _LATE save "d346346.rom",lang,end ELSE save "d346345.rom",lang,end ENDIF ;This Perl script strips unused conditional assembly paths ;from this listing. ;Copy the text between the cut lines. ;Paste it into a new file, beebasmpp.pl, and remove the column of ; ;Then call it with ; perl beebasmpp.pl -o bplus326.asm.txt -D _DDOS326 \ ; -D _BPLUS -D _RAMINIT76K master357.asm.txt ;-------->8--- ;#!/usr/bin/perl ; ;#Usage: perl beebasmpp.pl ;# {-D SYMBOL[=VALUE]} [-l] [-v] -o OUTFILE [FILE...] ; ;use Getopt::Std; ;use IO::Seekable qw(SEEK_SET SEEK_CUR SEEK_END); ; ;@x=(2); @t[6,7]=(2,4); ; ;for($i=0;$i<@ARGV && ($arg = $ARGV[$i]) ne '--'; ++$i) { ; if(substr($arg,0,2) eq '-D') { ; if(($dfn=substr($arg,2)) eq '') { ; $dfn = $ARGV[++$i]; ; } ; if(($x=index($dfn,'=')) > 0) { ; $symbol{substr($dfn,0,$x)} = substr($dfn,$x+1); ; } elsif($dfn ne '') { ; $symbol{$dfn} = 1; ; } ; } ;} ; ;getopts("D:Edlo:v"); ;die "No output file specified" if $opt_o eq ''; ;$opt_b = hex($opt_b);$l=$opt_l ? "\n" : ''; ; ;open(BIN,"+>$opt_o") or die; ;while(<>) { ; y/\n\r//d; ; if($opt_v || /^\s*(?:IF|ELIF|ELSE|ENDIF)/i) { ; while(($key, $value) = each %symbol) { ; $x=0; ; while(($x = index($_,$key,$x)) >= 0) { ; substr($_,$x,length($key),$value); ; $x+=length($value); ; } ; } ; } ; if(/^\s*((?:EL)?)IF\s+NOT\s+(\S*)\s*$/i) { ; unshift@x,4 if$1 eq''; ; $x[0]=$t[$x[0]|$x[1]&2|($2 !=0)];$_=$l; ; }elsif(/^\s*((?:EL)?)IF\s+(\S*)\s*$/i) { ; unshift@x,4 if$1 eq''; ; $x[0]=$t[$x[0]|$x[1]&2|($2 ==0)];$_=$l; ; }elsif(/^\s*ELSE(?!\S)/i) { ; $x[0]=$t[$x[0]|$x[1]&2];$_=$l; ; }elsif(/^\s*ENDIF(?!\S)/i) { ; shift@x;@x=(2)unless@x;$_=$l; ; }elsif($x[0] & 2 && /^\s*(\S+)\s*=\?\s*(\S+)/) { ; $symbol{$1}=$2 unless defined($symbol{$1});$_.="\n"; ; }elsif($x[0] & 2 && /^\s*(\S+)\s*=\s*(\S+)/) { ; $symbol{$1}=$2;$_.="\n"; ; }else{ ; $_.="\n"; ; } ; print BIN ($x[0] & 2 ? $_ : $l); ;} ;close(BIN); ;-------->8--- ;End of master357.asm.txt