title Peripheral Utility Transfer Routines ;++ ; ; File transfer program, knows many DEC formats. ; ; By John Wilson . ; ; Copyright (C) 1995-2001 By John Wilson. All rights reserved. ; ; This program may be freely distributed provided that no charge is made for ; distribution and that all versions include this copyright notice and give ; proper credit to the original author (including first billing in the message ; displayed when the program is started). OK so I'm vain, but I put a *lot* of ; work into this program, and I don't want anyone else taking credit for it. ; ; This program still has a few loose ends. The Files-11 and TSS/8 support is ; read-only, and the Catweasel driver needs to be finished and debugged ; (possibly after the hardware is debugged?!). Also it would be nice to ; support SCSI tapes, if I can figure out how to fit them into the command ; language. The sequence of operations involved in locating a directory and ; opening and accessing files there is really too complicated. ; ; A basic assumption of this program is that there are never more than one ; input and one output file open at any given time, since the user can only ; type one COPY command (and wildcards are handled iteratively like on any ; normal system). So we can use static storage for variables related to open ; files, and we don't have to worry about multiple output files competing for ; contiguous disk space or directory slots or anything like that. Converting ; this code into live filesystem drivers would be a big project though. ; ; TO DO LIST: ; ; Should handle bad block replacement with RT-11 DL: and DM: volumes. Doesn't ; matter though, if the image file was made using the RT-11 drivers, since they ; will have untangled the block replacements. Actually in that case doing our ; own relocation would screw it up so maybe it's better to leave this alone. ; ; RENAME/DATE:xxx ??? ; ; Several different cleanup concepts need to be sorted out: ; ; SQUEEZE should move empty blocks to end under RT-11 and OS/8. ; ; CLEAN should update SATT.SYS under RSTS, or BITMAP.SYS under Files-11, or ; bitmap under DOS/BATCH, and check for blocks that are allocated to >1 file. ; ; Something should combine contiguous empty blocks under RT-11 (SQUEEZE/DIR ?). ; ; Defragmenters for all the supported file systems would be wonderful, but ; they'd be a whole lot of work. It might be better to do that in PDP-11 ; (etc.) code so that it would be useful on real machines too. ; ; 09/18/1990 JMBW Created (OS/278, COS-310 read-only version). ; 03/22/1994 JMBW Bypass BIOS to emulate RX50, RX02/03 (sort of) and RX01 ; (maybe -- it seems most PC FDC's blow off SD support). ; 03/23/1994 JMBW Concept of "logged-in" device. ; 03/30/1994 JMBW TYPE command works (DOS, OS/8, RT-11). ; 04/02/1994 JMBW TU58/COM port driver added. ; 07/09/1994 JMBW COPY command can write into RT-11 file system. ; 07/23/1994 JMBW LD: disk files work like subdirectories. ; 07/30/1994 JMBW Reads RSTS disks (RDS 0.0-1.2). ; 11/01/1994 JMBW FORMAT creates image files, WIPEOUT added for RT-11. ; 01/10/1995 JMBW V1.0 released. ; 01/29/1995 JMBW Cached dir code for OS/8 (adapted from RT-11 code). ; 05/18/1995 JMBW Writes and inits OS/8 disks. V1.1 released. ; 05/30/1995 JMBW Exclude tracks 0, 78, 79 when initting OS/278 RX50s. ; 07/09/1995 JMBW RX23, RX33 support (RX33 untested w/real media). ; 07/11/1995 JMBW RX26, "RX52" support (RX26 untested). ; 08/05/1996 JMBW Auto-sense image file type on MOUNT. ; 08/08/1996 JMBW Indirect command files ("@ file"), PUTR.INI. ; 08/11/1996 JMBW COPY/FILE/DEV, PC-style floppy disks and images. ; 08/24/1996 JMBW RX26 support debugged (finally got a 2.88 MB drive). ; 08/26/1996 JMBW Maintains software TG43 signal for CompatiCard. V1.2. ; 10/24/1996 JMBW Fixed problems with null OS/8 tentative files. V1.21. ; 07/24/1997 JMBW Supports SCSI disks using ASPI driver. ; 07/27/1997 JMBW INIT/SEGMENTS:n switch. ; 10/30/1997 JMBW Fixed TU58 bug with COPY/FILE/DEV. V1.22. ; 11/24/1997 JMBW MOUNT/PARTITION:n switch (for RT-11 DU:). V1.23. ; 12/01/1997 JMBW Fixed TU58 baud rate parser. DISMOUNT/UNLOAD. V1.24. ; 12/02/1997 JMBW SET DISMOUNT command. ; 04/11/1998 JMBW Added RAxx series drive types. ; 04/12/1998 JMBW Fixed handling of OS/278 disks with 2nd word of dir seg ; set wrong, also fixed size for INIT /OS8/RX50. V1.25. ; 06/04/1998 JMBW Fixed math error in RTWDIR, got incorrect # of dir segs ; for disks with exactly 289. files. V1.26. ; 06/09/1998 JMBW Fixed DXILV for OS/8 RX02s (never worked!). V1.27. ; 06/11/1998 JMBW I'm an idiot. Now split into DXILV/DYILV. V1.28. ; 07/14/1998 JMBW Added Massbus RMxx/RPxx series drive types. ; 07/26/1998 JMBW Added RP02/03 drive types. ; 11/06/1998 JMBW Reads DOS/BATCH disks. ; 11/10/1998 JMBW Reads new-format XXDP+ disks (old ones use DOS/BATCH). ; 11/12/1998 JMBW Allows BLOCKS/MB/etc. in file size for FORMAT /MSCP. ; 03/26/1999 JMBW File sizes over 2 GB on FAT32 systems (only). ; SHOW w/no arg shows all mounted devices. ; 06/19/1999 JMBW Beginnings of Catweasel floppy support. ; 12/06/1999 JMBW Added RD32, RD51-53 drive types. ; 01/12/2000 JMBW Fixed hang reading RSTS files with clustersize >= 128. ; 02/08/2000 JMBW Fixed CHKXX to check bitmap sequence numbers. ; 03/04/2000 JMBW CDROMx: driver reads raw CDs with MSCDEX/NWCDEX. ; 03/06/2000 JMBW Writes DOS/BATCH and XXDP+ disks. ; 03/31/2000 JMBW Writes RSTS disks, finally! ; 04/26/2000 JMBW Reads ODS-1 disks in text or image mode. ; 05/18/2000 JMBW Reads/writes TSS/8 PUTR DECtapes. Can get dir listing ; and *some* files on PS/8 DECtapes too (I have only one ; image to test and it has weird interleave). ; 05/23/2000 JMBW Reads TSS/8.24 disks (SEGSIZ=256 only). ; 06/11/2001 JMBW Fixed pesky pointer bug in RSTS writing (RSWL). ; 06/15/2001 JMBW V2.0 released at very long last. ; 09/05/2001 JMBW Fixed typo in OPN, also DOS/BATCH and XXDP+ buffer bug, ; added support for DOS/BATCH DECtapes, and cleaned up ; auto-detection of OS/8 and PUTR.SAV DECtapes. V2.01. ; ; FDCs that work in single density mode: ; SMC FDC37C65+ (non-+ version suspected to work too) ; Goldstar GM82C765B ; NatSemi PC8477B ; ;-- .radix 8 ;por supuesto ; ; include sym.inc ;(symbols for SYMDEB/386SWAT only, this line optional) ; tab= 11 lf= 12 cr= 15 crlf= cr+(lf*400) ; dbufl= 512d ;length of floppy buffer indsiz= 128d ;length of indirect file buffer bufsiz= 2000 ;size of general purpose buf (one RT-11 dir segment) ; .386 ;allow 32-bit constant cdsize= 700000000d ;max size of CD in bytes (approx., MSCDEX won't tell) .8086 ; ; Cram in-line string into buffer at ES:DI. ; (or call RTN with in-line arg, if specified.) cram macro string,rtn local a,b ifb call cram1 ;;CRAM1 crams in-line counted arg into ES:DI else call rtn endif a db b,string b= $-a-1 endm ; ; Define a byte field in a record (_ is current offset). ; Num is # bytes, default is 1. defb macro nam,num:=<1> &nam= byte ptr _ _= _+&num endm ; ; Define a word field in a record (_ is current offset). ; Num is # words, default is 1. defw macro nam,num:=<1> &nam= word ptr _ _= _+(&num*2) endm ; ; Define a dispatch table entry, giving an error message if order doesn't ; agree with NAM. ADDR is the call address. disp macro nam,addr if nam eq _ dw &addr _=_+2 else %out Dispatch table out of order: &nam .err endif endm ; ; Define a keyword record for TBLUK, given a string containing exactly one ; hyphen to show the minimum allowable abbreviation. ; db length to match ; db total length ; db 'KEYWORD' ; dw ADDR kw macro text,addr kh= 0 ki= 0 irpc kc,text ki= ki+1 ifidn ,<-> kh= ki endif endm ;; irpc ife kh .err %out No hyphen in string: &text exitm endif ;; ife kh db kh-1,ki-1 irpc kc,text ifdif ,<-> db '&kc' endif endm ;; irpc dw addr endm ; ; Give an error message if an assumption is false (for code that depends on ; special knowledge of something in order to work). .assume macro cond if &cond else .err %out Assumption &cond is false endif endm ; ; Define an ASPI error message table entry amsg macro val,str local a,b a db &val,0,b,&str b= $-a-3 endm ; ; Define a token for file system, dev type etc. token macro name &name=_ _=_+2 endm ; ; Call and return. callr macro dst jmp &dst endm ; ; Set up OS types, each must be unique, low byte is word size (12. or 16.) ; ostype macro nam,sym &nam= &sym &sym= &sym+100h endm ; twelv= 12d ;PDP-8 file structures sixtn= 16d ;PDP-11 file structures ; ostype frgn,sixtn ;foreign (no file system, PDP-11 interleave) ostype cos310,twelv ;COS-310 (binary files only) ostype dosbat,sixtn ;DOS/BATCH ostype ods1,sixtn ;RSX-11, IAS, early VAX/VMS ostype os8,twelv ;OS/8, OS/78, OS/278 ostype ps8,twelv ;PS/8 (DECtapes seem to have interleave???) ostype putr,twelv ;TSS/8, PUTR.SAV format ostype rsts,sixtn ;RSTS disk structure 0.0, 1.1, 1.2 ostype rt11,sixtn ;RT-11 ostype tss8,twelv ;TSS/8.24 disk structure (SEGSIZ=256.) ostype xxdp,sixtn ;XXDP+ V2 mutation of DOS/BATCH format ;(older versions use regular DOS/BATCH format) ; ; Drive type (real, equivalent to real (5.25" vs. 8"), or simulated (image)): ; _=2 ; (all valid types are non-zero) token mscp ;size is whatever they say token ra60 ;UDA50/RA60 (205 MB) token ra70 ;UDA50/RA70 (280 MB) token ra71 ;UDA50/RA71 (700 MB) token ra72 ;UDA50/RA72 (1.0 GB) token ra73 ;UDA50/RA73 (2.0 GB) token ra80 ;UDA50/RA80 (121 MB) token ra81 ;UDA50/RA81 (456 MB) token ra82 ;UDA50/RA82 (622 MB) token ra90 ;UDA50/RA90 (1.2 GB) token ra92 ;UDA50/RA92 (1.5 GB) ; token rc25 ;KLESI/RC25 (25 GB) ; (need exact size -- saw 50840 blocks in one table on fiche but is it right?) ; (NetBSD/VAX /etc/disktab says 50736 -- is *it* right?) ;; token rd31 ;RQDX3/RD31 (20 MB -- Seagate ST225) token rd32 ;RQDX3/RD32 (40 MB -- Seagate ST238 I think?) ;; token rd50 ;RQDX3/RD50 (5 MB -- Seagate ST412) token rd51 ;RQDX3/RD51 (10 MB -- Seagate ST506) token rd52 ;RQDX3/RD52 (30 MB) token rd53 ;RQDX3/RD53 (69 MB -- Micropolis 1325) token rd54 ;RQDX3/RD54 (159 MB -- Maxtor XT2190) token rk02 ;RK11/RK02 (1.2 MB) token rk05 ;RK11/RK05 (2.5 MB) token rk06 ;RK611/RK06 (14 MB) token rk07 ;RK611/RK07 (27 MB) token rl01 ;RL11/RL01 (5 MB) token rl02 ;RL11/RL02 (10 MB) token rm02 ;RH11/RM02 (67 MB) token rm03 ;RH11/RM03 (67 MB) token rm05 ;RH70/RM05 (256 MB) token rm80 ;RH70/RM80 (124 MB) token rp02 ;RP11C/RP02 (20 MB) token rp03 ;RP11C/RP03 (40 MB) token rp04 ;RH11/RP04 (88 MB) token rp05 ;RH11/RP05 (88 MB -- Memorex version of RP04) token rp06 ;RH11/RP06 (174 MB) token rp07 ;RH70/RP07 (516 MB) token rs03 ;RH11/RS03 (512 KB) token rs04 ;RH11/RS04 (1.0 MB) token rs08 ;RF08/RS08 (256 KW PDP-8 fixed-head disk) flpbeg= _ ; beginning of floppy types token rx01 ;RX11/RX01 (250.25 KB) 77x1x26 token rx02 ;RX211/RX02 (500.5 KB) 77x1x26 token rx03 ;RX211/RX03 (1001 KB) -- semi-documented RX02 DS mod token rx23 ;???/RX23 (1.44 MB) 80x2x18 token rx24 ;???/RX24 (720 KB) 80x2x9 token rx26 ;???/RX26 (2.88 MB) 80x2x36 token rx31 ;???/RX31 (360 KB) 40x2x9 (needs double-step on AT) ;;; not currently supported by anything since we don't double-step token rx33 ;RQDX3/RX33 (1.2 MB) 80x2x15 token rx50 ;RX50 (400 KB) 80x1x10 token rx52 ;RX50/double sided (800 KB) -- "RX52" is my name for it ;(may not really exist, but supported by P/OS) flpend= _-1 ; end of floppy types .assume ;FDCMOS and FDTYPE expect token to fit in a byte token tu56 ;TC08/TU56 (129.x12.x2702'), TC11/TU56 (256.x16.x578.) token tu58 ;DL11/TU58 (256 KB) ; ; Actual hardware type used to simulate the drive ; _=0 token hwfile ;image file token hwflop ;PC floppy drive (DU:, DX:, DY:, DZ:) token hwcwsl ;Catweasel floppy drive token hwpart ;hard disk partition (never finished this one) token hwtu58 ;TU58 attached to serial port (DD:) token hwaspi ;ASPI SCSI disk token hwcdex ;raw CD-ROM via MSCDEX.EXE (or DR-DOS NWCDEX.EXE) ; ; Logical device record (one for each mounted device) ; _=0 defw next ;ptr to next record defw logd ;logical device name (right-justified) defw logu ;flag,,unit # defw hwtype ;hardware type (image file, floppy, etc.) defw fsys,0 ;file system structure defb wsize,2 ; low byte is word size (12d or 16d) defw med ;medium type (RX01, etc.) defw dsrini ;init DSR (called on each dev change) defw rdblk ;routine to read CX blocks starting at DX:AX defw rdasc ;RDBLK, ASCII mode defw wrblk ;routine to write CX blocks starting at DX:AX defw wrasc ;WRBLK, ASCII mode (if there's a difference) defw handle ;handle to file (if disk image) defw fintlv ;routine to compute floppy interleave for AX iovecs=_ ; I/O vectors start here defw defset ;set up defaults before first access defw savcwd ;save dir context before switching devs defw rstcwd ;restore dir context after switching devs defw volnam ;print vol name defw gdir ;get dir name defw gfile ;get dir/filename defw pfile ;print dir/filename defw dirhdr ;print directory listing header defw dirinp ;init for dir lookup defw dirget ;get next dir entry defw dirdis ;display dir entry defw diremp ;display empty dir entry defw dirnam ;print dir name defw dirfin ;finish up dir access (flush dirty bufs etc.) defw dirsum ;print summary after directory listing defw open ;open most recently DIRGETted file for input defw read ;read from input file defw reset ;close input file defw dirout ;init for dir write defw create ;create output file defw write ;write to output file defw wlast ;write last buffer (partial block) to file defw close ;close output file defw delfil ;delete file defw chdir ;change current dir defw wboot ;write bootstrap defw wipout ;wipe out (zero) unused areas for compression niovec=(_-iovecs)/2 ; (I'm labeling what sets each of these size fields, just to get my ; head straight, the code is pretty cluttered and I want to fix it up) defw totsiz,4 ;total dev size in bytes (including trk 0 etc.) ;set by: ; FORMAT (image files) ; SETMED (unless /MSCP) ; per-device "open" routines called by IMAGE defw totblk,4 ;total dev size in blocks (including DEC166) ;set by: ; SETMED (unless /MSCP) ; per-device "open" routines called by IMAGE defw devsiz,4 ;usable (for data) device size in 512-byte blks ;set by: ; SETMED (unless /MSCP) ; per-device "open" routines called by IMAGE defw actsiz,2 ;active device (subset) size (RT-11) ;set by: RTGD, RTCD defb initf ;init flag, cleared in all devs at each prompt defb ecache ;NZ => enable dir cache (RSTS) ;(doesn't really work, dir blocks are always cached) defb tapflg ;NZ => tape (otherwise disk) ; filesystem-dependent data defw pcs ;pack clustersize (in bytes under DOS/BATCH) defw dcs ;dev clustersize defw numclu,2 ;# of clusters on device (DOS/BATCH) ;# of PCs on device (RSTS) ;# of PCs on device (32 bits, ODS-1) defw pcsdcs ;PCS/DCS, # of DCs per PC (RSTS) defw blkbuf ;block buffer (DOS/BATCH) odpmax= 8d ;max # of path elements for ODS-1 defw curdir,2 ;.WORD current PPN (RSTS, DOS/BATCH) ;or .LONG starting blk # (RT-11, OS/8) ;or .RAD50 path name (ODS-1) _= curdir+(odpmax*6) ;(ODS-1: 3 words for each dir element) ;(ends when full, or if following word is 0) defw cursiz,2 ;current LD: size (RT-11) ;or dir entry size (DOS/BATCH) defw actdir,2 ;active dir (during search, usually = CURDIR) _= actdir+(odpmax*6) ;(3 words for each dir element) ;(ends when full, or if following word is 0) defw mfddcn ;DCN of first cluster of MFD (RSTS, TSS/8) defw satptr,2 ;DCN of last cluster allocated (RSTS) ;same for ODS-1, but 32 bits wide defw bitmap,2 ;first cluster of storage bitmap (DOS/BATCH) ;first LBN of SATT.SYS (RSTS) defw indmap,2 ;first LBN of INDEXF.SYS header bitmap (ODS-1) defw indbsz ;size of INDEXF.SYS header bitmap (ODS-1) defw filhdr,2 ;LBN of first file header in INDEXF.SYS (ODS-1) ;(first 16 are contig with bitmap) defw extra ;# extra bytes/dir segment (OS/8, RT-11) defw dirbuf ;ptr to head of dir buffer chain defw dirclu ;dir clustersize defw dirwnd,7 ;dir retrieval window (MUST FOLLOW DIRCLU) defw rdslev ;RDS level word (RSTS) ;ODS level word (ODS-1) defw paksts ;pack status word (RSTS) defw filext ;default # blks to add when extending (ODS-1) defw defprt ;default protection bits (ODS-1) ; device-dependent data defb drive ;physical BIOS drive #, TU58 unit, SCSI target defb host ;host adapter # (SCSI) defb lun ;logical unit # (SCSI) defw port ;COM port base I/O address defw secsz,2 ;sector size (only LSW valid unless SCSI) defb ilimg ;1 => interleaved image file (must be undone) defb blksec ;shift count to convert blk # to sec # ;=9-LOG2(SECSZ) defb ncyls ;# cylinders defb nhds ;# heads defb nsecs ;# sectors per track defw rdsec ;read sector (FDRDS or equiv.) defw wrsec ;write sector (FDWRS or equiv.) defw rdid ;read ID (FDRID or equiv.) reclen= _ ; ; ODS-1 file header offsets: _= 0 defb h_idof ;word offset of identification area defb h_mpof ;word offset of map area defw h_fnum ;file number defw h_fseq ;file sequence # defw h_flev ;ODS-1 version # used to create file (=101h) defw h_fown ;owner UIC defw h_fpro ;file protection (4-bit fields: W/G/O/S) fp_rdv= 1 ;deny read access fp_wrv= 2 ;deny write access fp_ext= 4 ;deny extend access fp_del= 10 ;deny delete access defw h_fcha,0 ;file characteristics defb h_ucha ;user-controlled characteristics uc_con= 200 ;contiguous uc_dlk= 100 ;deaccess locked, not properly closed defb h_scha ;system-controlled characteristics sc_mdl= 200 ;marked for delete sc_bad= 100 ;file contains bad block(s) sc_dir= 40 ;file is a dir (not stored on disk, only in memory) sc_spl= 20 ;this is a spool intermediate file defb h_ufat,32d ;user file attributes, see below s_hdhd= _ ;length w/o ODS 1.4 extended file # defb h_fext,2 ;8 MSBs of file number (2nd byte is what?) ; ; FCS file attributes (RMS attrs are a superset) _=0 defb f_rtyp ;record type r_fix= 1 ;fixed-length records r_var= 2 ;variable-length records r_seq= 3 ;sequenced variable-length records r_stm= 4 ;stream format records (RSTS/E) defb f_ratt ;record attributes fd_ftn= 1 ;FORTRAN carriage control (1st char of line) fd_cr= 2 ;implied carriage control, each record is a line fd_prn= 4 ;R_SEQ sequence #s are actually print control fd_blk= 10 ;records may not span block boundaries defw f_rsiz ;record size (F_FIX), or max record size defw f_hibk,2 ;highest VBN allocated (MSW first) defw f_efbk,2 ;VBN of EOF (MSW first) defw f_ffby ;first free byte, i.e. byte following EOF in ;block F_EFBK -- if F_FFBY.EQ.0, F_EFBK is ;actually the block *following* the EOF block s_fatt= 14d ; ; BINFLG values: bin= 0 ;binary transfer text= 2 ;text transfer (translate to ASCII and back) .assume <(rdasc-rdblk) eq (text-bin)> .assume <(wrasc-wrblk) eq (text-bin)> ; ; File size table entry: ; (for figuring out medium type based on image file size) _=0 defw flmed ;medium type defw flsiz,2 ;actual image file size in bytes defw flusz,2 ;usable device size in bytes defw flbsz,2 ;size of bad sector track (etc.) in bytes defw flcyl ;# cylinders defb flhd ;# heads defw flsec ;# sectors defw flbs ;# b/s defb flilv ;1 if interleaved (must be undone) fllen=_ ; ; Macro to define a file size table entry: ; dw med ;medium type code ; dd size ;image size in bytes ; dd use ;usable part of disk in bytes ; dd bad ;size of bad sector file in bytes ; dw cyl ;cylinders ; db hd ;heads ; dw sec ;sectors ; dw bs ;bytes/sector ; db ilv ;NZ => interleaved file (logical blk order) ; filsiz macro med,siz,use,bad,cyl,hd,sec,bs,ilv dw &med ;;medium type code ;; size of image file in bytes ifnb <&siz> dd &siz ;;file size given explicitly else dd &cyl*&hd*&sec*&bs ;;default is the whole device endif ;; size of usable part of device in bytes ifnb <&use> dd &use ;;usable size given explicitly else ifnb <&bad> dd (&cyl*&hd*&sec*&bs)-(&bad) ;;all but bad block list else dd &cyl*&hd*&sec*&bs endif endif ;; size of bad sector file in bytes ifnb <&bad> dd &bad ;;size of bad sector file else dd 0 ;;no bad sector file endif dw &cyl ;;cylinders db &hd ;;heads (i.e. tracks) dw &sec ;;sectors dw &bs ;;bytes/sector ifnb <&ilv> db &ilv ;;NZ => file is interleaved (logical blk order) else db 0 endif endm ; ; DOS/BATCH dir buffer format: ; ; N.B. Cluster comes after fixed fields instead of before, since the cluster ; size is variable ; _=0 defw dbblk ;block # defw dbnxt ;ptr to next buf or 0 defb dbdrt ;NZ => buf is dirty defw dbdat,0 ;data start here ; ; OS/8 dir segment buffer format: ; _=1000 ;256. words of data first defw ossblk ;block # defw ossnxt ;ptr to next buf or 0 defb ossdrt ;NZ => buf is dirty osslen=_ ; ; RSTS dir/bitmap/etc. buffer format (used for ODS-1 too): ; _=1000 ;512. bytes of data first defw rdsbkl ;abs 24-bit block number (LSW) (or 0 if not used yet) defw rdsbkh ; " " " (MSW, we store 32 bits for future expansion) defb rdsdrt ;NZ => buf is dirty defw rdsnxt ;ptr to next buf or 0 rdslen=_ ; ; RT-11 dir segment buffer format: ; _=2000 ;1024. bytes of data first defw rtsblk,2 ;starting abs blk # of block pair defw rtsnxt ;ptr to next buf or 0 defb rtsdrt ;NZ => buf is dirty rtslen=_ ; ; FDC hardware registers: ; fdc= 3F0h ;base address fdccc4= fdc+0 ;Micro Solutions CompatiCard IV control register fdcfcr= fdc+1 ;FDC format control register (SMC FDC37C65C+ only) ;(my homemade FDC has it at 3F6h) fdcdor= fdc+2 ;FDC digital output register fdcmsr= fdc+4 ;FDC main status register fdcdat= fdc+5 ;FDC data register fdcdcr= fdc+7 ;FDC diskette control register (data rate, etc.) fdcdir= fdc+7 ;FDC digital input register (disk change line in b7) ; mfm= 100 ;MF flag set (double density) fm= 0 ;MF flag clear (single density) ; seek_status equ byte ptr 043Eh ;int flag (b7), "recal needed" bits (b3-b0) motor_status equ byte ptr 043Fh ;motor bits (b7 = spin-up first (write)) motor_count equ byte ptr 0440h ;# timer ticks until motor turnoff timer_low equ word ptr 046Ch ;low word of system time ; code segment assume cs:code org 100h ;.COM file ; ; Global register usage: ; ; BP generally points at a dev record ; DS points at code ; ES points at code (except when temporarily used for buffers) ; SS points at code (stack is after code, before buffers) ; DF=0 ; ; Note that we sometimes have to reload ES from DS before doing directory ; manipulations, in routines which are otherwise using ES to point at a buffer, ; because many of the "read/write directory block" routines depend on ES=DS. ; start: cld ;DF=0 mov ah,30h ;func=get DOS version int 21h mov ds:dosver,ax ;save ; make sure we have enough memory to run and sp,not 0Fh ;round down to paragraph boundary cmp sp,offset mem ;enough? jb notmem ;no mov ds:himem,sp ;save ptr (PSP:0006 isn't quite right) ;;; huh? shouldn't SP be set to MEM here, and MEM should be mult of 16. mov bx,sp ;copy mov cl,4 ;shift count shr bx,cl ;find # paragraphs to keep mov sp,offset pdl ;shrink stack mov ah,4Ah ;func=setblock int 21h ; allocate a sector I/O buffer (for CD-ROMs, Catweasel etc.) mov dx,8192d ;size of buffer to get getm3: mov bx,dx ;copy size in bytes mov cl,4 ;shift count shr bx,cl ;find # paragraphs mov ah,48h ;func=getblock int 21h jnc getm4 ;got it shr dx,1 ;req /2 cmp dh,512d/400 ;keep going as long as there's a point ja getm3 xor ax,ax ;didn't get anything getm4: mov ds:secbuf,ax ;save ptr to sector buf, or 0 mov ds:secsiz,dx ;save size of sector buf ; allocate a large I/O buffer mov bx,-1 ;get all available memory getm1: mov ah,48h ;func=getblock int 21h ;give it a try jnc getm2 ;got it, go see how big test bx,bx ;anything left? jnz getm1 ;yes notmem: cram '?Not enough memory',wrng mov ax,4C01h ;func=punt int 21h getm2: cmp bx,800h ;at least 32KB? jb notmem ;no, can't even hold one big cluster mov ds:bigbuf,ax ;save ptr mov ax,16d ;segment size mul bx ;find total # bytes mov ds:bigsiz,ax ;save mov ds:bigsiz+2,dx mov dx,offset ctrlc ;pt at ^C vector mov ax,2523h ;func=set INT 23h vector int 21h ; get # of floppy drives int 11h ;among other things xor bl,bl ;assume none test al,1 ;are there any? jz numfd1 ;no rol al,1 ;left 2 rol al,1 and al,3 ;isolate inc ax ;get actual # mov bl,al ;copy numfd1: mov al,bl ;copy mov ds:numfd,al ;save add al,'A'-1 ;get max valid floppy drive letter mov ds:maxfd,al ;save cmp bl,1 ;exactly one floppy? jne numfd2 inc bx ;yes, pretend there are two numfd2: mov ds:firhd,bl ;unit # of first HD add bl,'A' ;physical drive letter of first HD mov ds:lethd,bl ;save ; get drive types from CMOS if AT mov ax,0FFFFh ;point at last paragraph of real mode memory mov es,ax cmp byte ptr es:0Eh,0FCh ;AT? jne numfd3 mov al,10h ;floppy types are at CMOS location 0010 out 70h,al jmp short $+2 ;hocus pocus (I/O delay on non-local-bus ATs) in al,71h ;get types, A in high nibble, B in low mov bl,al ;copy and bx,0Fh ;isolate B: type mov ah,ds:fdcmos[bx] ;translate mov bl,al ;copy mov cl,4 ;shift count shr bl,cl ;right-justify A: drive type mov al,ds:fdcmos[bx] ;translate mov word ptr ds:fdtype,ax ;save numfd3: ; get # of hard drives xor ax,ax ;load 0 mov es,ax ;into ES mov al,byte ptr es:0475h ;get # drives mov ds:numhd,al ;save ; get row count for **MORE** processing mov al,byte ptr es:0484h ;EGA and later, # lines on screen -1 test al,al ;did we get anything? jnz numfd4 mov al,25d-1 ;no, all pre-EGA boards had 25d lines only numfd4: mov ds:lmax,al ;save push ds ;restore ES (trashed in INT 21h AH=35h above) pop es ; make sure disk buffer doesn't span 64 KB boundary mov ax,ds ;get seg addr mov cl,4 ;bit count sal ax,cl ;left 4 (lose high bits) add ax,offset dbuf1 ;pt at buffer add ax,dbufl-1 ;see if it spans (OK to stop just short) jnc nospan ;no, skip mov ds:dbuf,offset dbuf2 ;yes, well this won't nospan: ; get currently logged in DOS disk letter to use as default mov ah,19h ;func=get logged in DOS disk int 21h add al,'A' ;convert to drive letter mov ah,al ;duplicate mov word ptr ds:curdsk,ax ;save ; look for an ASPI manager (Advanced SCSI Programming Interface) mov dx,offset scsimg ;filename mov ax,3D00h ;func=open /RONLY int 21h jc lkasp3 ;failed, doesn't exist ; got something, make sure it's not a file and that it accepts IOCTL RD mov bx,ax ;copy handle mov ax,4400h ;func=IOCTL (get dev flags) int 21h jc lkasp1 ;failed and dx,40200 ;isolate IOCTL RD, DEVICE bits cmp dx,40200 ;both must be set jne lkasp1 ; OK so far, ask for entry point mov dx,offset buf ;point at scratch buf mov cx,4 ;byte count mov ax,4402h ;func=IOCTL (read control string) int 21h jc lkasp1 ;failed cmp ax,4 ;got all four bytes? jne lkasp1 ;no mov ax,word ptr ds:buf ;fetch entry point mov dx,word ptr ds:buf+2 mov ds:aspi,ax ;save mov ds:aspi+2,dx lkasp1: mov ah,3Eh ;func=close handle int 21h lkasp2: mov ax,ds:aspi ;see if we got anything or ax,ds:aspi+2 jz lkasp3 ;no, skip this ; do "SCSI HA inquiry" cmd, see if manager can handle incomplete xfrs mov si,offset hainq ;pt to cmd block mov cx,lhainq mov di,offset srb ;ES:DI=SRB buf push es ;save SRB addr as call arg push di rep movsb ;copy cmd call dword ptr ds:aspi ;call ASPI driver pop ax ;flush stack pop ax call aspwt ;await completion call serr ;check for error cmp word ptr ds:srb+4,55AAh ;did handler see our stupid sig? jne lkasp3 test byte ptr ds:srb+3Ah,2 ;residual byte count supported? jz lkasp3 inc ds:resbc ;set flag lkasp3: ; get SWITCHAR (undocumented!) mov ax,3700h ;func=get SWITCHAR int 21h mov ds:swchar,dl ;save it ; init CRC-CCITT lookup table (for Catweasel/ISA raw FDC) call bldcrc ; look for PUTR.INI mov dx,offset inifil ;point at init file name call openro ;try to open jnc infl9 ;got it ; check executable's directory if DOS 3.0 or later cmp byte ptr ds:dosver,3 ;DOS V3.X or higher? jb infl11 ;no push ds ;save mov ds,word ptr ds:[002Ch] ;point at environment xor si,si ;starting offset infl1: lodsb ;read a byte test al,al ;end of environment? jz infl3 infl2: lodsb ;no, look for end of string test al,al jnz infl2 jmp short infl1 ;now it could be end of env infl3: ; found end of environment, copy executable's full filespec lodsw ;skip # of parameters mov di,offset buf ;point at where to put filename mov dx,di ;save a ptr mov bx,di ;pt at begn of path element infl4: lodsb ;get a byte test al,al ;end of string? jz infl7 cmp al,':' ;colon? je infl5 cmp al,'/' ;slash? je infl5 cmp al,'\' ;backslash? jne infl6 infl5: lea bx,[di+1] ;pt at begn of next path element infl6: cmp di,offset buf+bufsiz ;off end of buf? je infl10 ;yes stosb ;otherwise save it jmp short infl4 ;loop infl7: ; replace last path element with init file's name pop ds ;restore mov di,bx ;point at last path element mov si,offset inifil ;point at init file name infl8: lodsb ;get a byte cmp di,offset buf+bufsiz ;off end of buf? je infl11 ;yes stosb ;save if not test al,al ;done all? jnz infl8 ;loop if not ; try to open it there call openro ;try to open jc infl11 ;forget about it infl9: mov ds:indhnd,ax ;save handle mov ds:indctr,0 ;buffer initially empty jmp short infl11 infl10: pop ds ;restore infl11: ; put up banner mov di,offset lbuf ;pt at buf cram 'PUTR V2.01 Copyright (C) 1995-2001 by John Wilson .' mov byte ptr ds:lnum,10d ;don't **MORE** call flush cram 'All rights reserved. See www.dbit.com for other DEC-related software.' call flush ;put up msg call flush ;+blank line cram 'COPY mode is ASCII, SET COPY BINARY to change' call flush ;+ ; ; Main loop. ; ;- mloop: ; reset everything (in case error abort, ^C, or 'Q' from **MORE**) mov ax,cs ;copy cs mov ds,ax ;to all mov es,ax cli ;ints off (may have been already via INT inst) mov sp,offset pdl ;;reset stack sti ;;ints on after next inst mov ss,ax ; flush dir buffers xor bp,bp ;load 0 (don't get caught in a loop!) xchg bp,ds:indev ;get input dev, zap it test bp,bp ;anything? jz mlp1 call ss:dsrini[bp] ;init xor bx,bx ;switch to input call ss:rstcwd[bp] call ss:dirfin[bp] ;finish up dir I/O mlp1: xor bp,bp ;load 0 xchg bp,ds:outdev ;get output dev, zap it test bp,bp ;anything? jz mlp2 call ss:dsrini[bp] ;init mov bx,2 ;switch to output call ss:rstcwd[bp] call ss:dirfin[bp] ;finish up dir I/O mlp2: mov ax,ds:kepmem ;flush unkept memory mov ds:fremem,ax ; close any extraneous open handles (they may have aborted an xfr) mov bx,-1 ;-1 means closed xchg bx,ds:inhnd ;see if it was open test bh,bh js mlp3 ;no mov ah,3Eh ;func=close int 21h mov bx,-1 ;-1 again mlp3: xchg bx,ds:outhnd ;see if this was open test bh,bh js mlp4 ;no mov ah,3Eh ;func=close int 21h mlp4: ; flush incomplete dev rec (from TYPE, or aborted MOUNT etc.) xor bp,bp ;load 0 xchg bp,ds:tmpdev ;clear it, see if NZ test bp,bp jz mlp5 call retrec ;return to free list mlp5: ; mark all devs as not initted mov bp,ds:logdev ;get head of dev list jmp short mlp7 ;jump into loop mlp6: mov ss:initf[bp],0 ;mark as not initted mov bp,ss:next[bp] ;follow link mlp7: test bp,bp ;end? jnz mlp6 mov byte ptr ds:fdrst,0 ;FDC may need resetting test byte ptr ds:cc4fdc,-1 ;is it a CompatiCard IV? jz mlp8 mov dx,fdccc4 ;yes, point at CC4 control port mov al,0Ch ;give it a freshly-reset value out dx,al mlp8: ; turn off Catweasel motors if needed test byte ptr ds:cwmot,-1 ;any motors on? jz short mlp9 ;no call cwdes ;deselect drive(s) call cwoff ;turn motor(s) off mlp9: ; print prompt mov di,offset lbuf ;pt at buffer mov al,'(' ;put in parentheses stosb test byte ptr ds:nprmpt,-1 ;no prompt? jnz mlp10 ;right, don't get caught in a loop inc byte ptr ds:nprmpt ;in case we don't survive this mov dl,ds:curdsk ;is a DOS disk current? test dl,dl jnz mlp11 ;yes mov bp,ds:logdev ;[get head of device list] push di ;save call ss:dsrini[bp] ;init DSR call ss:defset[bp] ;set defaults pop di ;restore call pdev ;show device name xor cx,cx ;no trailing \ (or whatever) call ss:dirnam[bp] ;show dir name jmp short mlp12 mlp10: mov si,offset badprm ;prompt mov cx,13d ;length rep movsb ;copy jmp short mlp13 ;continue mlp11: mov al,dl ;copy mov ah,':' ;add colon stosw xor cx,cx ;no trailing \ call dosddl ;display dir for drive in DL mlp12: dec byte ptr ds:nprmpt ;success, clear flag mlp13: mov ax,">)" ;add ")>" stosw mov byte ptr ds:lnum,2 ;no **MORE** call flush1 ;flush call gtlin ;get a line from the keyboard ; get today's date so we can interpret OS/8 dates push cx ;save mov ah,2Ah ;func=get date int 21h mlp14: mov word ptr ds:day,dx ;save mov ds:year,cx ;save sub cx,1970d ;subtract OS/8 base and cl,not 7 ;get mult of 8d add cx,1970d ;add this back in mov ds:yrbase,cx ;save OS/8 base year mov ah,2Ch ;func=get time int 21h mov word ptr ds:min,cx ;save mov ds:sec,dh mov ah,2Ah ;func=get date int 21h cmp word ptr ds:day,dx ;check for change jne mlp14 ;whoops, we passed midnight pop cx ;restore call skip ;skip blanks etc. jc mlp15 ;blank line, reprompt cmp al,'@' ;open indirect file? je mlp16 cmp al,';' ;comment? je mlp15 ;ignore line if so cmp al,'!' ;either way je mlp15 call getw ;get a word jc mlp15 ;just got a separator, reprompt mov ax,offset cmdtab ;pt at command table call tbluk ;look up jc mlp20 call ax ;call the routine mlp15: jmp mloop ;loop mlp16: ; starts with "@", look for filename inc si ;eat the "@" dec cx mov bx,-1 ;mark handle as empty xchg bx,ds:indhnd ;and get old value test bx,bx ;was it open? js mlp17 mov ah,3Eh ;yes, func=close int 21h mlp17: call getw ;get filename jc mlp18 ;failed call confrm ;should be EOL mov si,bx ;point at filename mov cx,dx mov di,offset dotcmd ;default extension call defext ;apply it (get filename ptr in DX) call openro ;try to open jc mlp19 mov ds:indhnd,ax ;save handle mov ds:indctr,0 ;buffer needs to be loaded jmp short mlp15 ;back into loop mlp18: jmp misprm mlp19: jmp fnf mlp20: ; undefined keyword, see if they want to log into a new device push bx ;save push dx mov si,bx ;back up add cx,dx mov ah,1 ;must include colon call getlog ;get logical device jc mlp27 ;invalid ; look for dev name on log dev list mov bp,ds:logdev ;get head of list xor dx,dx ;no prev entry jmp short mlp23 mlp21: cmp ss:logd[bp],ax ;is this it? jne mlp22 cmp ss:logu[bp],bx je mlp25 ;yes mlp22: mov dx,bp ;no, save ptr mov bp,ss:next[bp] ;follow link mlp23: test bp,bp ;is there more? jnz mlp21 ;loop if so ; not on list, see if it could be a DOS disk or bh,al ;2nd letter or unit specified? jnz mlp27 ;one or the other, invalid call confrm ;yes, make sure confirmed push ax ;save mov dl,ah ;copy sub dl,'A'-1 ;convert to number mov si,offset buf ;pt at buf mov ah,47h ;func=get CWD int 21h ;(make sure drive is really there) jc mlp24 ;nope dec dx ;numbers start at 0 for login mov ah,0Eh ;func=log in int 21h jc mlp24 ;error, don't log in pop ax ;restore mov al,ah ;duplicate mov word ptr ds:curdsk,ax ;log in mov byte ptr ds:nprmpt,0 ;assume we can prompt jmp mloop ;(will flush stack) mlp24: jmp baddrv ;no such drive mlp25: ; found it, relink at head of list call confrm ;make sure confirmed mov byte ptr ds:curdsk,0 ;don't use DOS drive test dx,dx ;was there a prev rec? jz mlp26 ;it was already logged in, easy (flush stack) mov bx,dx ;copy ptr mov ax,ss:next[bp] ;unlink bp mov ds:next[bx],ax mov ax,bp ;copy xchg ax,ds:logdev ;set new head of list, get old mov ss:next[bp],ax ;link rest of list in mlp26: mov byte ptr ds:nprmpt,0 ;assume we can prompt jmp mloop ;reprompt (flush stack) mlp27: ; error pop cx ;restore length pop dx ;and ptr mov di,dx ;copy add di,cx ;pt at end mov al,'?' ;? stosb mov ax,crlf ; stosw add cl,3 ;add to length mov bx,0002h ;handle=STDERR mov ah,40h ;func=write int 21h jmp mloop ; cmdtab: ;kw ,analyz ;analyze floppy format kw ,boot ;write bootstrap (RT-11 COPY/BOOT) kw ,cd ;change directory kw ,cd ;syn. for CD kw ,cls ;clear screen kw ,copy ;copy file(s) kw ,delete ;delete file(s) kw ,dir ;directory of device kw ,dismnt ;dismount mounted device kw ,dump ;dump a block kw ,exit ;exit from program kw ,format ;format floppy, create file, zero partition kw ,init ;write blank file system kw ,mount ;mount disk or pseudo-disk kw ,exit ;syn. for EXIT kw ,set ;set parameters kw ,show ;show device parameters ;;;; kw ,squeez ;make empty areas contiguous kw ,exit ;syn. for EXIT kw ,typ ;type a file kw ,vol ;print volume name kw ,wipe ;write zeros on unused blocks (for GZIP etc.) kw ,wprot ;;; cwhack=0 ;;;;; while hacking Catweasel if cwhack kw ,cwgid kw ,cwsav kw ,cwrst endif ;;; db 0 ;+ ; ; ^C exit vector. ; ;- ctrlc: mov bx,-1 ;mark indirect file as closed xchg bx,cs:indhnd ;and get current handle (DS value not known) test bx,bx ;is it open? js ctrlc1 ;no mov ah,3Eh ;func=close int 21h ctrlc1: jmp mloop ;restart ;+ ; ; Analyze floppy format. ; ; I haven't decided what would be useful output from this. ; ;- analyz: call gdevn ;parse device (if any) cmp ss:dirinp[bp],offset dosdi ;is it a PC-DOS disk? je anlz99 ;yes, doesn't count cmp ss:hwtype[bp],hwflop ;is it a floppy disk? jne anlz99 ;no call ss:defset[bp] ;set defaults call ss:dsrini[bp] ;init for input ;;; need to do lots of stepping and looping: mov byte ptr ds:flpcyl,0 ;init cyl to 0 ;;; do next cyl mov byte ptr ds:flphd,0 ;head too ;;; do next surface ;;; is there a way to wait for an index pulse? ;;; can't find it in FDC data sheets mov ch,ds:flpcyl ;get cyl mov dh,ds:flphd ;and head call fdrid ;read ID ; jc ... ;;; SI points to C,H,R,N ;;; loop until value repeats ;;; N.B. ints can screw us up, but we can't disable them ;;; maybe check twice and see if we got the same numbers? ret anlz99: cram '?Must be floppy disk',error ;+ ; ; Write bootstrap. ; ;- boot: call gdevn ;parse device (if any) call ss:defset[bp] ;set defaults callr ss:wboot[bp] ;go, return noboot: cram "?Don't know how to write bootstrap on that file system",error ;+ ; ; Change directory. ; ;- cd: call gdev ;parse device (if any) call ss:defset[bp] ;set defaults call skip ;anything given? jnc cd1 ;yes mov di,offset lbuf ;pt at buffer call pdev ;show device name xor cx,cx ;no filename call ss:dirnam[bp] ;show dir name jmp flush ;flush, return cd1: callr ss:chdir[bp] ;change dir, return ;+ ; ; Clear screen. ; ;- cls: call confrm ;make sure confirmed ;; should check for ANSI.SYS etc. and use ESC [2J if found mov ah,0Fh ;func=get video mode int 10h xor ah,ah ;func=set video mode int 10h cret: ret ;+ ; ; COPY inspec [outspec] ; ;- copy: mov ax,ds:bindef ;get default ASCII/binary flag mov ds:binflg,ax ;set text mode by default xor ax,ax ;load 0 mov ds:binswt,al ;no /BINARY or /ASCII yet mov ds:devflg,al ;no /DEV yet mov ds:clusiz,ax ;no /CLUSTERSIZE:n mov ds:contig,al ;no /CONTIGUOUS yet mov ds:ovride,al ;don't override protection on output call copswt ;check for switches ; get input filespec call gdev ;parse device name mov ds:indev,bp ;save input dev rec call ss:defset[bp] ;set defaults mov di,offset fname1 ;pt at buffer xor bl,bl ;no implied wildcards call ss:gfile[bp] ;parse dir/filename xor bx,bx ;input file call ss:savcwd[bp] ;save dir call copswt ;look for any more switches mov byte ptr ds:notfnd,1 ;nothing found yet ; get output filespec call gdev ;get output dev name mov ds:outdev,bp ;save output dev rec call ss:defset[bp] ;set defaults mov di,offset fname3 ;pt at buffer xor bl,bl ;no implied wildcards call ss:gfile[bp] ;parse dir/filename mov bx,2 ;output file call ss:savcwd[bp] ;save dir call copswt ;check for switches call confrm ;make sure EOL ; decide what we're doing mov al,ds:devflg ;get /DEV, /FIL flag test al,al ;either specified? jz copy1 jmp icopy ;[device image copy, SF set on AL] copy1: ; copy file(s) mov si,offset fname1 ;pt at source filespec cmp byte ptr [si],0 ;anything? jnz copy2 mov word ptr [si],".*" ;no, change to *.* mov byte ptr [si+2],'*' copy2: mov di,offset fname3 ;pt at dest filespec cmp byte ptr [di],0 ;anything? jnz copy4 copy3: lodsb ;no, copy input filespec stosb test al,al ;end? jnz copy3 ;loop if not copy4: mov bp,ds:indev ;get input device again call ss:dsrini[bp] ;init DSR xor bx,bx ;input dev call ss:rstcwd[bp] ;restore CWD call ss:dirinp[bp] ;init dir input mov bp,ds:outdev ;get output dev call ss:dsrini[bp] ;init DSR mov bx,2 ;output dev call ss:rstcwd[bp] ;restore CWD call ss:dirout[bp] ;init dir I/O copy5: ; check next file mov bp,ds:indev ;get input device call ss:dsrini[bp] ;init DSR xor bx,bx ;switch to input dev call ss:rstcwd[bp] mov byte ptr ds:icont,0 ;assume not contig call ss:dirget[bp] ;get next entry jc copy6 ;no more jz copy5 ;skip empty areas and dirs mov si,offset fname1 ;pt at source wildcard mov bx,offset fname3 ;dest wildcard mov dx,offset fname4 ;dest buffer push di ;save call mapwld ;check for match pop di ;[restore] jne copy5 ;ignore ; it's a match, type filenames mov byte ptr ds:notfnd,0 ;found one call flush ;display filename ; open output file mov bp,ds:outdev ;get output device call ss:dsrini[bp] ;init DSR mov bx,2 ;switch to output call ss:rstcwd[bp] mov si,offset fname4 ;pt at filename call ss:create[bp] ;create the file ; copy the file mov bp,ds:indev ;get input dev call ss:dsrini[bp] ;init DSR xor bx,bx ;switch to input call ss:rstcwd[bp] call ss:open[bp] ;open jc copy7 call fcopy ;copy the file mov bp,ds:indev ;get input device again call ss:dsrini[bp] ;set up input dev xor bx,bx ;select input dir call ss:rstcwd[bp] call ss:reset[bp] ;close input file mov bp,ds:outdev ;get output dev call ss:dsrini[bp] ;init mov bx,2 ;switch to output call ss:rstcwd[bp] call ss:close[bp] ;close output file jmp short copy5 ;look for more copy6: ; no more (dir flushing is handled in MLOOP) cmp byte ptr ds:notfnd,0 ;find anything? jnz copy8 ret copy7: jmp dirio ;dir I/O err copy8: jmp fnf ;file not found ;+ ; ; Handle COPY switches. ; ;- copswt: call skip ;skip white space jc copsw3 cmp al,ds:swchar ;switch? jne copsw3 inc si ;eat the / dec cx call getw ;get the switch jc copsw1 mov ax,offset copsws ;point at switch list call tbluk ;look it up jc copsw2 call ax ;call the routine w/CF=0 jmp short copswt ;check for more copsw1: jmp synerr ;missing switch copsw2: jmp badswt ;bad switch copsw3: ret ; copsws label byte kw ,copasc ;/ASCII (convert to/from text) kw ,copbin ;/BINARY (don't convert to/from text) kw ,copclu ;/CLUSTERSIZE (override default FCS) kw ,copctg ;/CONTIGUOUS (contig output if possible) kw ,copdev ;/DEVICE (source and/or dest are whole dev) kw ,copfil ;/FILES (use w/DEV to say src or dst is file) kw ,copovr ;/OVERRIDE (override prot on output file) db 0 ; copasc: ; /ASCII mov ds:binflg,text ;[set text mode] mov byte ptr ds:binswt,-1 ;[flag given] ret ;CF=0 ; copbin: ; /BINARY mov ds:binflg,bin ;[set bin mode (or sector, for image copies)] mov byte ptr ds:binswt,-1 ret ; copclu: ; /CLUSTERSIZE:n jcxz cpcl2 ;can't be EOL lodsb ;eat the ':' dec cx ;count it off cmp al,':' ;must be ':' je cpcl1 cmp al,'=' ;or '=' jne cpcl3 cpcl1: call getn ;get number test ax,ax ;must be NZ (CF=0) jz cpcl4 ;isn't mov ds:clusiz,ax ;[save] ret cpcl2: jmp misprm ;missing parameter cpcl3: jmp synerr ;syntax error cpcl4: jmp outran ;number out of range ; copctg: ; /CONTIGUOUS mov byte ptr ds:contig,1 ;[set contiguous flag] ret ; copdev: ; /DEVICE or byte ptr ds:devflg,1 ;set device flag (src ior dst is device) ret ; copfil: ; /FILES or byte ptr ds:devflg,200 ;set files flag (src xor dst is file) ret ; copovr: ; /OVERRIDE mov byte ptr ds:ovride,1 ;[delete output file w/same name] ret ;[even if protected] ;+ ; ; COPY/DEV dev1: dev2: ; COPY/DEV/FILE dev1: dev2:file ; COPY/DEV/FILE dev1:file dev2: ; ; Make block-by-block image copy of entire device (or image file). ; ; /BINARY means to do it sector-by-sector (i.e. bypass floppy interleave). ; ; al DEVFLG value ; b0=/DEVICE, b7=/FILES ; FNAME1 .ASCIZ source filename ; FNAME3 .ASCIZ destination filename ; ;- icopy: mov cx,bin ;use binary mode xchg cx,ds:binflg ;set flag, get its value .assume <(bin eq 0) and (text lt 400)> xor cl,(bin xor text) ;flip it and cl,ds:binswt ;/BINARY only if explicitly given mov ds:secflg,cl ;save (NZ => /BINARY) mov bl,ds:fname1 ;get first char of each filename mov bh,ds:fname3 ;each will be NUL if no filename test al,al ;/FILES? js icpy2 ;yes ; just /DEVICE or bl,bh ;neither filename should be given jnz icpy1 ;error if one is ; make sure input and output aren't same device mov bp,ds:indev ;get input dev cmp bp,ds:outdev ;same as output dev? jne icpy3 ;skip if not jmp iosame ;input and output devs are same icpy1: jmp synerr icpy2: test al,1 ;can't have /FILES alone, needs /DEVICE too jz icpy1 cmp bl,1 ;normalize flags sbb bl,bl ;0 if was non-zero, otherwise -1 cmp bh,1 sbb bh,bh add bl,bh ;should have exactly one file inc bl ;right? jnz icpy1 ;no icpy3: ; open input device or file mov bp,ds:indev ;get ptr call ss:dsrini[bp] ;init DSR xor bx,bx ;input dev call ss:rstcwd[bp] ;restore CWD call ss:dirinp[bp] ;init dir input (required even if raw) test byte ptr ds:fname1,-1 ;is there an input filename? jnz icpy6 ;yes, find the file ; input is device cmp ss:dirinp[bp],offset dosdi ;is it a DOS disk? je icpy5 mov ax,ss:devsiz[bp] ;get size mov bx,ss:devsiz+2[bp] mov cx,ss:devsiz+4[bp] mov dx,ss:devsiz+6[bp] mov ds:isize,ax ;save mov ds:isize+2,bx mov ds:isize+4,cx mov ds:isize+6,dx ; get device size in bytes test byte ptr ds:secflg,-1 ;doing sector copy? jz icpy7 mov al,ss:nsecs[bp] ;get # sectors mul byte ptr ss:nhds[bp] ;* # heads mov dl,ss:ncyls[bp] ;get # cyls xor dh,dh ;DH=0 mul dx ;find total # of sectors mov ds:isize,ax ;that's the real input size mov ds:isize+2,dx mov word ptr ds:isize+4,0 mov word ptr ds:isize+6,0 ; calculate actual size in bytes, may differ from image file size mov bx,ax ;save low order mov ax,ss:secsz[bp] ;get sector size mul dx ;find high order xchg ax,bx ;save, get low order sector count mul word ptr ss:secsz[bp] ;multiply add dx,bx ;add high order mov bx,ss:med[bp] ;while we're here, check device type cmp bx,flpbeg ;must be floppy for sector copy jb icpy4 cmp bx,flpend jbe icpy8 icpy4: jmp notflp icpy5: jmp short icpy9 icpy6: jmp short icpy11 icpy7: mov cl,9d ;shift count rol ax,cl ;left 9 bits shl dx,cl ;make space mov bx,ax ;copy and bh,777/400 ;trim to 9 bits xor ax,bx ;isolate high 7 or dx,bx ;insert in MSW icpy8: mov ds:fbcnt,ax ;save device size in bytes mov ds:fbcnt+2,dx xor ax,ax ;load 0 mov ds:iblk,ax ;init block # mov ds:iblk+2,ax mov ds:iblk+4,ax mov ds:iblk+6,ax ; get current date and time in case output is file mov ax,ds:year ;get year mov bx,word ptr ds:day ;month,,day mov ds:fyear,ax ;save year mov word ptr ds:fday,bx ;month,,day mov byte ptr ds:fdatf,-1 ;date is valid mov ax,word ptr ds:min ;get hour,,min mov bl,ds:sec ;second mov word ptr ds:fmin,ax ;save hour,,min mov ds:fsec,bl ;second mov byte ptr ds:ftimf,-1 ;time is valid mov ax,offset iread ;pt at raw read routine jmp short icpy12 icpy9: cram "?Can't read raw DOS disk",error icpy10: jmp fnf icpy11: ; find file (or first match if wildcard) mov byte ptr ds:icont,0 ;assume not contig call ss:dirget[bp] ;get next entry jc icpy10 ;no more, file not found jz icpy11 ;skip empty areas and dirs mov si,offset fname1 ;pt at wildcard call wild ;check for match jne icpy11 ;ignore call ss:open[bp] ;open it mov ax,ss:read[bp] ;get read routine icpy12: ; AX=read routine, open output file or device push ax ;save mov bp,ds:outdev ;get ptr call ss:dsrini[bp] ;init DSR mov bx,2 ;output dev call ss:rstcwd[bp] ;restore CWD call ss:dirout[bp] ;init dir I/O (required even if raw) test byte ptr ds:fname3,-1 ;is there an output filename? jnz icpy17 ;yes, create the file ; output is device cmp ss:dirinp[bp],offset dosdi ;is it a DOS disk? je icpy16 test byte ptr ds:secflg,-1 ;doing sector copy? jz icpy14 mov al,ss:nsecs[bp] ;get # sectors mul byte ptr ss:nhds[bp] ;* # heads mov dl,ss:ncyls[bp] ;get # cyls xor dh,dh ;DH=0 mul dx ;find total # of sectors ; calculate actual size in bytes, may differ from image file size mov bx,ax ;save low order mov ax,ss:secsz[bp] ;get sector size mul dx ;find high order xchg ax,bx ;save, get low order sector count mul word ptr ss:secsz[bp] ;multiply add dx,bx ;add high order mov bx,ss:med[bp] ;while we're here, get device type cmp bx,flpbeg ;must be floppy for sector copy jb icpy13 cmp bx,flpend jbe icpy15 icpy13: jmp notflp ;isn't icpy14: mov cl,9d ;shift count rol ax,cl ;left 9 bits shl dx,cl ;make space mov bx,ax ;copy and bh,777/400 ;trim to 9 bits xor ax,bx ;isolate high 7 or dx,bx ;insert in MSW icpy15: xor ax,ax ;load 0 mov ds:oblk,ax ;init block # mov ds:oblk+2,ax mov bx,offset iwrite ;pt at raw write/write-last routines mov cx,offset iwlast jmp short icpy18 icpy16: cram "?Can't write raw DOS disk",error icpy17: ; create file mov si,offset fname3 ;pt at filename call ss:create[bp] ;create the file mov bx,ss:write[bp] ;get write routines mov cx,ss:wlast[bp] icpy18: ; actually copy the device pop ax ;restore read routine ptr call dcopy ;copy the device ; close input and output test byte ptr ds:fname1,-1 ;was there an input file? jz icpy19 ;no mov bp,ds:indev ;get input device again call ss:dsrini[bp] ;set up input dev xor bx,bx ;select input dir call ss:rstcwd[bp] call ss:reset[bp] ;close input file icpy19: test byte ptr ds:fname3,-1 ;was there an output file? jz icpy20 ;no mov bp,ds:outdev ;get output dev call ss:dsrini[bp] ;init mov bx,2 ;switch to output call ss:rstcwd[bp] call ss:close[bp] ;close output file icpy20: ret ;+ ; ; Read device data. ; ; On entry: ; es:di ptr to buf (normalized) ; cx # bytes free in buf (FFFF if >64 KB) ; ss:bp log dev rec ; ; On return: ; cx # bytes really read (.LE. value on entry) ; ; CF=1 if nothing was read: ; ZF=1 end of device ; ZF=0 buffer not big enough to hold anything, needs to be flushed ; ;- iread: mov bx,ds:isize ;get remaining size mov ax,ds:isize+2 ;check higher words or ax,ds:isize+4 or ax,ds:isize+6 jz ird1 ;zero, low word is right mov bx,-1 ;say 64 K blks (or sectors) -1 ird1: test bx,bx ;end of device? jz ird5 ;yes, ZF=1 test byte ptr ds:secflg,-1 ;sector copy? jnz ird6 ;yes ; logical block mode mov al,ch ;get # full blocks free in buf shr al,1 jz ird4 ;nothing, say so cbw ;AH=0 cmp ax,bx ;more than what's left? jb ird2 mov ax,bx ;stop at end of dev ird2: mov cx,ax ;copy block count mov ax,ds:iblk ;get starting block mov dx,ds:iblk+2 push cx ;save # blks requested push di ;save ptr (in case of retry) call ss:rdblk[bp] ;read data ;;; dammit, need 32 more bits of block number! pop di ;[restore] pop ax jc ird7 ;read error ird3: xor bx,bx ;load 0 add ds:iblk,ax ;success, update block adc ds:iblk+2,bx adc ds:iblk+4,bx adc ds:iblk+6,bx sub ds:isize,ax ;update size sbb ds:isize+2,bx ;CF=0 (already checked) sbb ds:isize+4,bx sbb ds:isize+6,bx ret ird4: or al,1 ;clear ZF (buf full) ird5: stc ;set CF ret ird6: jmp short ird8 ird7: ; read error, make sure we get the good surrounding blocks shr ax,1 ;try for half that many blocks (before bad one) jnz ird2 ;unless it was only one block push es ;save push di push ds ;copy DS to ES pop es mov di,offset lbuf ;point at buf cram '%Bad block ' mov ax,ds:iblk ;fetch it mov bx,ds:iblk+2 mov cx,ds:iblk+4 mov dx,ds:iblk+6 call pqnum cram ' on input device' call flush pop si ;restore (into ES:SI like RTRD etc.) pop es mov ax,1 ;block count=1 mov cx,512d ;byte count=one block jmp short ird3 ;go return it ird8: ; sector mode mov ax,cx ;copy count xor dx,dx ;zero-extend div ss:secsz[bp] ;find # complete sectors that will fit in buf test ax,ax ;space for anything? jz ird4 ;no cmp ax,bx ;more than what's left? jb ird9 mov ax,bx ;stop at end of dev ird9: mov si,di ;put buf addr in ES:SI mov cx,ax ;copy sector count mov ax,ds:iblk ;get starting sector # mov dx,ds:iblk+2 ;(fits in 32 bits in sector mode) mov ds:flpvec,offset secxrd ;point at read routine call secxfr ;read a track, or partial track if no space xor bx,bx ;load 0 add ds:iblk,cx ;update block # adc ds:iblk+2,bx adc ds:iblk+4,bx adc ds:iblk+6,bx sub ds:isize,cx ;remove from count sbb ds:isize+2,bx sbb ds:isize+4,bx sbb ds:isize+6,bx mov ax,cx ;copy mul ss:secsz[bp] ;find # bytes read mov cx,ax ;in CX clc ;happy ret ;+ ; ; Write to device. ; ; es:si buffer ; cx length of data ; ss:bp log dev rec ; ; On return, cx contains the number of bytes actually written (presumably a ; multiple of the clustersize), .LE. the value on entry. ; ;- iwrite: test byte ptr ds:secflg,-1 ;sector copy? jnz iwr3 ;yes mov al,ch ;get # full 512-byte blocks shr al,1 jz iwr1 ;nothing, skip cbw ;AH=0 push cx ;save mov cx,ax ;copy mov ax,ds:oblk ;get starting block # mov dx,ds:oblk+2 add ds:oblk,cx ;update adc ds:oblk+2,0 call ss:wrblk[bp] ;write data pop cx ;[restore] jc iwr2 ;failed iwr1: and cx,not 777 ;truncate byte count (wrote whole blks only) ret iwr2: jmp wrerr ;error iwr3: ; sector mode mov ax,cx ;copy count xor dx,dx ;zero-extend div ss:secsz[bp] ;find # complete sectors that can be written test ax,ax ;space for anything? jz iwr4 ;no mov cx,ax ;copy sector count mov ax,ds:oblk ;get starting sector # mov dx,ds:oblk+2 mov ds:flpvec,offset secxwr ;point at write routine call secxfr ;write a track, or partial track if no space add ds:oblk,cx ;update block # adc ds:oblk+2,0 mov ax,cx ;copy mul ss:secsz[bp] ;find # bytes written mov cx,ax ;in CX ret iwr4: xor cx,cx ;can't write even one sector ret ;+ ; ; Write last buffer of data to output device. ; ; es:si string ; cx length of data ; ss:bp log dev rec ; ;- iwlast: jcxz iwl1 ;none, skip mov bx,512d ;block size test byte ptr ds:secflg,-1 ;sector copy? jz iwl2 ;no mov bx,ss:secsz[bp] ;yes, use sector size instead iwl2: sub bx,cx ;find # NULs to add (in [1,SIZE)) xchg bx,cx ;swap lea di,[bx+si] ;point past data xor al,al ;load 0 rep stosb ;pad it out sub di,si ;find length mov cx,di ;copy jmp iwrite ;write data, return iwl1: ret ;+ ; ; Transfer sectors to/from floppy. ; ; We only try to transfer one track at a time. If there's more space in the ; buffer, FCOPY will call right back anyway so there's no point in getting all ; tangled up maintaining the current block number and counts of the number of ; sectors transferred and the number left to go. ; ; dx:ax starting sector # ; cx # sectors to copy ; es:si buffer (updated on return) ; FLPVEC points to single-sector read or write routine, called with: ; es:si buffer addr ; FLPCYL cylinder ; FLPHD head ; cl sector ; ; Returns: ; cx actual # sectors transferred ; ;- secxfr: ; transfer one sector at a time if not at beginning of track mov bl,ss:nsecs[bp] ;get # sectors/track xor bh,bh div bx ;DL=sector-1, AX=cyl*nhds+head mov di,ax ;save div byte ptr ss:nhds[bp] ;AH=head, AL=cyl mov ds:flphd,ah ;save mov ds:flpcyl,al mov ax,di ;get cyl*nhds+head again test dl,dl ;at begn of track? jnz secx1 ;no, just do one sector at a time ; also do one at a time if buf too small for whole track cmp cx,bx ;>=1 track? jae secx4 ;yes secx1: ; transfer one sector at a time until buf full or reached end of track mov di,cx ;copy # to go inc dx ;sectors start at 1, not 0 xor bx,bx ;init count of # actually done mov cl,dl ;copy starting sector secx2: push di ;save push bx push cx call ds:flpvec ;read or write pop cx ;[restore] pop bx pop di inc bx ;count it inc cx ;bump to next sector dec di ;done all? jz secx3 ;yes cmp cl,ss:nsecs[bp] ;more to do? jbe secx2 ;yes, loop until reach EOT or satisfy count secx3: mov cx,bx ;copy # sectors transferred ret secx4: ; transfer an entire track, doing the sectors out of order so as to get ; 2:1 soft interleave, the data are transferred to and from the buffer ; at the correct offsets so the interleave doesn't affect the data ; transferred, just the speed (this is assuming we're too slow for 1:1 ; interleave, if this isn't true, which may well be the case these ; days, then this is actually slower than the obvious approach! should ; do some timing to find out) mov cl,ss:nsecs[bp] ;get # sectors mul word ptr ds:copskw ;figure out soft skew factor xor ch,ch ;CH=0 div cx ;DL=starting sector (numbered starting at 0) mov bl,dl ;copy push es ;save push ds ;copy DS to ES pop es mov di,offset buf ;pt at buf xor ch,ch ;CX=# sectors xor al,al ;load 0 mov dx,cx ;save rep stosb ;nuke out table pop es ;restore xor bh,bh ;nuke high byte secx5: cmp ds:buf[bx],bh ;have we done this one? jz secx6 ;no inc bl ;+1 (wrapping to 0 not a prob) cmp bl,ss:nsecs[bp] ;off end of track? jb secx5 ;no, check this loc sub bl,ss:nsecs[bp] ;wrap jmp short secx5 secx6: not byte ptr ds:buf[bx] ;set flag push bx ;save push dx push si mov cl,bl ;get sector inc cx ;starts at 1 mov ax,ss:secsz[bp] ;get sector size mul bx ;find byte offset from base of track add si,ax ;index from curr ptr call ds:flpvec ;transfer the sector pop si ;restore pop dx pop bx dec dx ;done all? jz secx8 ;yes add bl,ds:copilv ;bump to next sector jc secx7 ;wrapped for sure cmp bl,ss:nsecs[bp] ;off EOT? jb secx5 secx7: sub bl,ss:nsecs[bp] ;wrap jmp short secx5 secx8: ; finished transferring track mov ax,ss:secsz[bp] ;get sector size mov cl,ss:nsecs[bp] ;get # sectors read xor ch,ch mul cx ;compute size of track in bytes add si,ax ;update ptr in case continuing ret ;+ ; ; Read sector for SECXFR. ; ; ss:bp log dev rec ; FLPCYL cyl # ; FLPHD head # ; cl sector # ; es:si buffer pointer (updated on return) ; ;- secxrd: push es ;save push si push cx push ds ;copy DS to ES pop es mov ch,ds:flpcyl ;get cyl mov dh,ds:flphd ;and head call ss:rdsec[bp] ;read it jc scxrd2 pop ax ;flush sector # scxrd1: pop di ;restore buffer ptr pop es mov si,ds:dbuf ;point at buf mov cx,ss:secsz[bp] ;get sector size shr cx,1 ;word count rep movsw ;copy mov si,di ;updated buf addr ret scxrd2: ; read error, print message push ds ;copy DS to ES pop es mov di,offset lbuf ;point at buf cram '%Read error, track ' mov al,ds:flpcyl ;print cyl call pbyte cmp ss:nhds[bp],2 ;double sided? jb scxrd3 ;no cram ', head ' mov al,ds:flphd ;print head call pbyte scxrd3: cram ', sector ' pop ax ;print sector call pbyte call flush jmp short scxrd1 ;+ ; ; Write sector for SECXFR. ; ; ss:bp log dev rec ; FLPCYL cyl # ; FLPHD head # ; cl sector # ; es:si buffer pointer (updated on return) ; ;- secxwr: push es ;save push cx ;save sector # mov di,ds:dbuf ;point at buf mov ax,ds ;get segs mov bx,es mov ds,bx ;swap mov es,ax mov cx,ss:secsz[bp] ;get sector size shr cx,1 ;/2 rep movsw ;copy into buf mov ds,ax ;restore pop cx mov ch,ds:flpcyl ;get cyl mov dh,ds:flphd ;and head cmp ch,ss:ncyls[bp] ;off end of disk? jae scxwr1 ;punt if so (wrote all that would fit already) push si ;save pointer call ss:wrsec[bp] ;write it jc scxwr2 ;error pop si ;restore pop es ret scxwr1: jmp odsmal scxwr2: mov di,offset lbuf ;point at buf cram '%Write error, track ' mov al,ds:flpcyl ;print cyl call pbyte cmp ss:nhds[bp],2 ;double sided? jb scxwr3 ;no cram ', head ' mov al,ds:flphd ;print head call pbyte scxwr3: cram ', sector ' pop ax ;print sector call pbyte call flush pop si ;restore pop es ret ;+ ; ; Copy a file or device. ; ; DS:INDEV ptr to input log dev rec ; DS:OUTDEV ptr to output log dev rec ; ; For copying devices, enter at DCOPY with: ; ax ptr to READ routine ; bx ptr to WRITE routine ; cx ptr to WLAST routine ; ;- fcopy: mov bp,ds:indev ;get input dev mov ax,ss:read[bp] ;get read routine mov bp,ds:outdev ;get output dev mov bx,ss:write[bp] ;get write routines mov cx,ss:wlast[bp] dcopy: ; enter here with AX, BX, CX set up mov ds:aread,ax ;save mov ds:awrite,bx mov ds:awlast,cx mov byte ptr ds:eof,0 ;not EOF yet mov ax,ds:bigbuf ;get ptr to buf mov ds:bigptr+2,ax ;save mov ds:bigptr,0 ;offset=0 mov ax,ds:bigsiz ;get size of buf mov ds:bigctr,ax mov ax,ds:bigsiz+2 mov ds:bigctr+2,ax fcpy1: ; fill buffer mov bp,ds:indev ;get input dev call ss:dsrini[bp] ;select it xor bx,bx ;select dir call ss:rstcwd[bp] fcpy2: ; do next read mov cx,ds:bigctr ;get # bytes left cmp ds:bigctr+2,0 ;is it more than 64 KB? jz fcpy3 mov cx,-1 ;stop just short fcpy3: les di,dword ptr ds:bigptr ;get ptr mov ax,cx ;copy add ax,di ;will this wrap around? jnc fcpy4 ;no mov cx,di ;copy not cx ;# bytes until EOB-1 (so ADD won't overflow) fcpy4: call ds:aread ;read as much as we can jc fcpy5 ;EOF or EOB, skip sub ds:bigctr,cx ;eat this chunk sbb ds:bigctr+2,0 add cx,ds:bigptr ;advance ptr (don't carry!) mov ax,cx ;copy and cx,0Fh ;normalize ptr mov ds:bigptr,cx mov cl,4 ;shift count shr ax,cl ;get # whole pars advanced add ds:bigptr+2,ax ;update jmp short fcpy2 ;scarf more fcpy5: jnz fcpy6 ;skip inc ds:eof ;set EOF flag fcpy6: ; empty buffer as much as possible mov bp,ss:outdev ;get output dev rec call ss:dsrini[bp] ;select it mov bx,2 ;select dir too call ss:rstcwd[bp] mov ax,ds:bigsiz ;get size of buffer mov dx,ds:bigsiz+2 sub ax,ds:bigctr ;-amount free = amount used sbb dx,ds:bigctr+2 mov ds:bigctr,ax ;save mov ds:bigctr+2,dx mov ax,ds:bigbuf ;reinit ptr mov ds:bigptr+2,ax mov ds:bigptr,0 ;offset=0 fcpy7: ; do next write mov cx,ds:bigctr ;get # bytes left cmp ds:bigctr+2,0 ;is it more than 64 KB? jz fcpy8 mov cx,-1 ;stop just short fcpy8: jcxz fcpy10 ;EOF les si,dword ptr ds:bigptr ;get ptr mov ax,cx ;copy add ax,si ;will this wrap around? jnc fcpy9 ;no mov cx,si ;copy not cx ;# bytes until EOB-1 fcpy9: call ds:awrite ;write as much as we can jcxz fcpy10 ;can't, skip sub ds:bigctr,cx ;eat this chunk sbb ds:bigctr+2,0 add cx,ds:bigptr ;advance ptr mov ax,cx ;copy and cx,0Fh ;normalize mov ds:bigptr,cx mov cl,4 ;shift count shr ax,cl ;get # whole pars advanced add ds:bigptr+2,ax ;update jmp short fcpy7 ;write more fcpy10: ; copy what's left back to begn of buffer so we can read more ; (assume all clustersizes<(64 KB-16.) so we can do it in one go) mov cx,ds:bigctr ;get low word of length push ds ;save mov es,ds:bigbuf ;set up dest xor di,di lds si,dword ptr ds:bigptr ;get source mov bx,cx ;save length shr cx,1 ;/2 to get wc rep movsw ;[copy words] rcl cl,1 ;catch CF rep movsb ;copy odd byte if any pop ds ;restore cmp byte ptr ds:eof,0 ;is that it? jnz fcpy11 ;yes mov ax,di ;copy ptr and di,0Fh ;offset mov ds:bigptr,di ;save mov cl,4 ;bit count shr ax,cl ;find offset in paragraphs add ax,ds:bigbuf ;add base mov ds:bigptr+2,ax ;save segment mov ax,ds:bigsiz ;get size of buf mov dx,ds:bigsiz+2 sub ax,bx ;find # free bytes sbb dx,0 mov ds:bigctr,ax ;save mov ds:bigctr+2,dx jmp fcpy1 ;refill buffer fcpy11: ; end of file, force whatever's left out (CX=byte count) mov cx,bx ;get count again mov es,ds:bigbuf ;pt at beginning xor si,si call ds:awlast ;write last buffer to disk push ds ;copy DS to ES pop es ret ;+ ; ; Delete file(s). ; ;- delete: mov byte ptr ds:noqry,0 ;init flags mov byte ptr ds:ovride,0 call delswt ;check for switches ; get filespec call gdev ;parse device name, get dev rec in bp call ss:defset[bp] ;set defaults mov di,offset fname1 ;pt at buffer xor bl,bl ;no implied wildcards call ss:gfile[bp] ;parse dir/filename call delswt ;check for switches call confrm ;make sure EOL call ss:dsrini[bp] ;init DSR call ss:dirinp[bp] ;init dir I/O mov byte ptr ds:notfnd,1 ;nothing found yet ; init fake output device mov ds:indev,bp ;set input device mov ds:outdev,bp ;it's both really, doesn't matter del1: ; check next file call ss:dirget[bp] ;get next entry jc del4 ;no more jz del1 ;skip empty areas mov si,offset fname1 ;pt at wildcard push di ;save call wild ;check for match pop di ;[restore] jne del1 ;ignore ; it's a match, type filename mov byte ptr ds:notfnd,0 ;found one cmp byte ptr ds:noqry,0 ;should we ask before deleting? jnz del2 ;no mov byte ptr ds:lnum,4 ;don't **MORE** (query, error, prompt, +1) mov ax," ?" ;'? ' stosw call flush1 ;print prompt call yorn ;yes or no? jc del1 ;loop if no jmp short del3 del2: call flush ;display filename del3: ; delete the file call ss:delfil[bp] ;delete it jmp short del1 ;look for more del4: ; no more call ss:dirfin[bp] ;finish up dir I/O cmp byte ptr ds:notfnd,0 ;find anything? jnz del6 ret del5: jmp dirio ;dir I/O err del6: jmp fnf ;file not found ;+ ; ; Handle DELETE switches. ; ;- delswt: call skip ;skip white space jc delsw3 cmp al,ds:swchar ;switch? jne delsw3 inc si ;eat the / dec cx call getw ;get the switch jc delsw1 mov ax,offset delsws ;point at switch list call tbluk ;look it up jc delsw2 call ax ;call the routine jmp short delswt ;check for more delsw1: jmp synerr ;missing switch delsw2: jmp badswt ;bad switch delsw3: ret ; delsws label byte kw ,delnoq ;/NOQUERY (don't prompt before deleting) kw ,delovr ;/OVERRIDE (override protection) db 0 ; delnoq: mov byte ptr ds:noqry,1 ;set flag ret ; delovr: mov byte ptr ds:ovride,1 ;set flag ret ;+ ; ; Directory of file(s). ; ;- dir: mov byte ptr ds:dirflg,0 ;no flags set call dirswt ;handle switches ; get filename call gdev ;parse device name, get dev rec in BP call ss:defset[bp] ;set defaults mov di,offset fname1 ;pt at buffer mov bl,1 ;implied wildcards call ss:gfile[bp] ;parse dir/filename call dirswt ;handle switches call confrm ;make sure confirmed call ss:dsrini[bp] ;init DSR (switch drives etc.) ; print banner call pvol ;print volume name (leave DI=LBUF) cram ' Directory of ' call pdev ;print dev name mov si,offset fname1 ;pt at buffer cmp byte ptr [si],0 ;is there a wildcard? jnz dir1 ;yes, good mov si,offset wldcrd ;no, use "*.*" dir1: call ss:pfile[bp] ;print filespec call flush ;print line call flush ;blank line call ss:dirhdr[bp] ;print header call ss:dirinp[bp] ;init dir input mov byte ptr ds:notfnd,1 ;nothing found yet dir2: ; process next file call ss:dirget[bp] ;get next entry jc dir5 ;end of list jnz dir3 ;not .EMPTY. ; empty area cmp byte ptr ds:fname1,0 ;do we have a filename? jnz dir2 ;yes, skip empties call ss:diremp[bp] ;display it call flush ;flush it jmp short dir2 dir3: cmp byte ptr ds:fname1,0 ;do we have a wildcard? jz dir4 ;no ; check to see if wildcard match push di ;save output line ptr mov si,offset fname1 ;pt at 1st call wild ;does it? pop di ;[restore] jne dir2 ;no, skip dir4: mov byte ptr ds:notfnd,0 ;found one call ss:dirdis[bp] ;display file ; end of line call flush ;flush it jmp short dir2 ;around for more dir5: cmp byte ptr ds:notfnd,0 ;find anything? jz dir6 jmp fnf ;no dir6: call ss:dirsum[bp] ;print summary call ss:dirfin[bp] ;finish up mov di,offset lbuf ;blank line callr flush ;+ ; ; Handle DIRECTORY switches. ; ;- dirswt: call skip ;skip white space jc dirsw3 cmp al,ds:swchar ;switch? jne dirsw3 inc si ;eat the / dec cx call getw ;get the switch jc dirsw1 mov ax,offset dirsws ;point at switch list call tbluk ;look it up jc dirsw2 call ax ;call the routine jmp short dirswt ;check for more dirsw1: jmp synerr ;missing switch dirsw2: jmp badswt ;bad switch dirsw3: ret ; dirsws label byte kw ,dirfu ;/FULL (verbose listing) db 0 ; dirfu: ; /FULL or byte ptr ds:dirflg,200 ;show everything ret ;+ ; ; DISMOUNT ddu: [/UNLOAD] ; ; Dismount mounted logical device. ; ;- dismnt: mov al,ds:dsmunl ;get /[NO]UNLOAD default mov byte ptr ds:unlswt,al ;init flag call dmtswt ;handle switches call gdevn ;look up device call dmtswt ;handle switches call confrm ;should be EOL cmp ss:dirinp[bp],offset dosdi ;is it a PC-DOS disk? je dsmt1 ;yes, no good (no permanent dev record anyway) mov al,ds:unlswt ;get switch jmp short dmnt ;dismount it, return dsmt1: cram "?Can't dismount DOS disk",error ; dmtswt: ; handle DISMOUNT switches call skip ;skip white space jc dmtsw3 cmp al,ds:swchar ;switch? jne dmtsw3 inc si ;eat the / dec cx call getw ;get the switch jc dmtsw1 mov ax,offset dmtsws ;point at switch list call tbluk ;look it up jc dmtsw2 mov ds:unlswt,al ;save value jmp short dmtswt ;check for more dmtsw1: jmp synerr ;missing switch dmtsw2: jmp badswt ;bad switch dmtsw3: ret ; dmtsws label byte kw ,0 kw ,1 db 0 ;+ ; ; Dismount device whose logical dev record is at SS:BP. ; ; al NZ => unload volume (if supported) ; ;- dmnt: cmp ss:hwtype[bp],hwfile ;image file? jne dmnt1 mov bx,ss:handle[bp] ;yes, get handle mov ah,3Eh ;func=close int 21h dmnt1: cmp ss:hwtype[bp],hwaspi ;ASPI? jne dmnt2 ;no test al,al ;/UNLOAD? jz dmnt2 ;no mov ds:ssunt1,ds ;set seg addr of BUF mov si,offset ssunt ;pt at cmd to start/stop drive mov cx,lssunt call aspcmd ;send cmd jnc dmnt2 ;success cram '?Error ejecting disk',error dmnt2: ; unlink from dev list mov ax,ss:next[bp] ;get link mov bx,ds:logdev ;get head of list cmp bx,bp ;is this it? je dmnt4 ;easy dmnt3: ; find it on logical dev list mov si,bx ;save ptr mov bx,ds:next[bx] ;follow link cmp bx,bp ;is this it? jne dmnt3 ;loop if not mov ds:next[si],ax ;unlink jmp short dmnt5 dmnt4: ; this rec was first on list, change back to DOS disk mov ds:logdev,ax ;unlink mov al,ds:dosdsk ;get DOS disk mov ds:curdsk,al ;make it the logged-in device dmnt5: jmp retrec ;flush record, return ; if 1 ;;; playing with Zip disks ; wprot: call gdevn ;look up device call confrm ;should be EOL cmp ss:hwtype[bp],hwaspi ;ASPI? jne wprot1 ;no mov ds:wpzip1,ds ;set seg addr of BUF mov si,offset wprots mov cx,wprotl mov ds:wpzipl,cx ;save length mov ds:wpzips,cl mov di,offset buf rep movsb ;copy into buf mov si,offset wpzip ;command to write prot/enable Zip mov cx,lwpzip call aspcmd ;send cmd jnc wprot1 ;success cram '?Error diddling Zip disk',error wprot1: ret ; ;wprots db 'APlaceForYourStuff' ;wprots db 'GoToHell' wprots db 'FOO' wprotl= $-wprots ; endif ;+ ; ; DUMP dev:nnnn ; ; Dump block nnnn (octal) in octal and sixbit. ; ;- dump: call gdev ;get dev name test word ptr ss:fsys[bp],-1 ;doesn't work for native file system jz dump3 call getn ;get a number cmp word ptr ss:fsys[bp],rt11 ;RT-11? je dump1 cmp word ptr ss:fsys[bp],os8 ;OS/8? jne dump2 dump1: add ax,ss:curdir[bp] ;yes, add base of current dir adc dx,ss:curdir+2[bp] dump2: mov cx,1 ;read 1 block mov di,offset buf ;pt at temp buf call ss:rdblk[bp] ;get the block jnc dump4 dump3: jmp rderr dump4: xor ax,ax ;offset dump5: ; do next line push ax ;save mov di,offset lbuf ;pt at buf mov cx,3 ;digit count call poct ;print offset mov ax," /" ;'/ ' stosw cmp byte ptr ss:wsize[bp],12d ;12-bit device? je dump10 ;yes, display as appropriate mov cx,4 ;loop count dump6: ; dump words in octal push cx ;save lodsw ;get a word mov cx,6 ;digit count call poct ;write it mov al,' ' ;blank stosb pop cx ;restore loop dump6 ;loop stosb ;add another blank sub si,8d ;back to begn mov cl,8d ;reload counter dump7: ; dump ASCII bytes lodsb ;get a byte and al,177 ;trim to 7 bits cmp al,' ' ;printing char? jb dump8 cmp al,177 jb dump9 dump8: mov al,'.' ;no, change to dot dump9: stosb ;save loop dump7 ;loop jmp short dump15 dump10: ; 12-bit mode mov cx,4 ;loop count dump11: ; dump words in octal push cx ;save lodsw ;get a word mov cx,4 ;digit count call poct ;write it mov al,' ' ;blank stosb pop cx ;restore loop dump11 ;loop stosb ;add another blank sub si,8d ;back to begn mov cl,4 ;reload counter dump12: ; dump TEXT characters lodsw ;get 2 chars add ax,ax ;left 2 add ax,ax mov al,ah ;get the char call dump17 ;display it mov al,[si-2] ;low char call dump17 loop dump12 ;loop if 0 ; dump TSS/8 ASCII (AAAAAAAABBBB BBBBCCCCCCCC => ABC) mov al,' ' ;blank stosb stosb sub si,8d ;back to begn mov cl,2 ;reload once again dump13: push cx ;save lodsw ;get a word mov cl,4 ;shift count shr ax,cl ;right-justify first char call dump19 ;save it mov al,[si-2] ;get prev low 4 bits shl al,cl ;left-justify or al,[si+1] ;high 4 bits of next word call dump19 ;save lodsw ;low 8 bits of next word call dump19 ;save pop cx ;restore loop dump13 mov al,' ' ;blank stosb sub si,8d ;back to begn mov cl,2 ;reload once again dump14: ; same thing in opposite order (1st byte will be wrong for sure) push cx ;save mov cl,4 ;shift count mov al,[si-2] ;get prev low 4 bits shl al,cl ;left-justify or al,[si+1] ;high 4 bits of this word call dump19 ;save lodsw ;low 8 bits of this word call dump19 ;save lodsw ;get next word shr ax,cl ;right-justify first char call dump19 ;save it pop cx ;restore loop dump14 endif ;;; 1 dump15: push si ;save call flush ;flush line pop si ;restore pop ax add ax,4 ;bump by 4 words cmp byte ptr ss:wsize[bp],12d ;12-bit dev? je dump16 add ax,4 ;no, skip to next 8. bytes dump16: cmp si,offset buf+512d ;done all of block? je dump18 ;yes, return jmp dump5 ;loop dump17: ; display TEXT char in AL and al,77 ;isolate mov ah,al ;copy sub ah,40 ;get sign bits and ah,100 ;the one we want or al,ah ;set it (or not) stosb dump18: ret if 0 ;;; dump19: and al,177 ;isolate low 7 bits cmp al,177 ;RUBOUT? je dump20 cmp al,' ' ;ctrl char? jae dump21 dump20: mov al,'.' ;one or the other, change to dot dump21: stosb ;save ret endif ;;; 1 ;+ ; ; Exit. ; ;- exit: ; dismount all devices mov bp,ds:logdev ;get head of list test bp,bp ;anything? jz exit1 mov al,ds:dsmunl ;get /[NO]UNLOAD default call dmnt ;dismount it jmp short exit ;handle new head of list, until none exit1: ; reset floppy system xor dl,dl ;say drive 0 xor ah,ah ;func=reset int 13h int 20h ;exit ;+ ; ; FORMAT dev: /switches ; (see GMOUNT for switches) ; ;- format: mov bx,-1 ;don't get logical name mov ds:othswt,offset inisw ;"INIT" switch table mov ds:nsegs,0 ;(use default if no /SEGMENTS:n switch) call gmount ;parse stuff, get temp rec at SS:BP call crtdev ;create device (instead of OPNDEV) cmp ss:hwtype[bp],hwfile ;image file? jne fmt3 mov ax,ss:med[bp] ;get medium type test ax,ax ;defaulted? jz fmt1 cmp ax,mscp ;MSCP? jne fmt2 fmt1: ; prompt for size of image file cram 'File size (bytes): ',wrng call gtlin ;get line call gtsize ;read size of image and ax,not 777 ;trim to 512 bytes mov bx,ax ;copy DX:AX to CX:BX mov cx,dx call setsiz ;set up dev size jmp short fmt3 fmt2: ; if image file of floppy, see if it should be interleaved cmp ax,flpbeg ;is it a floppy? jb fmt3 cmp ax,flpend ja fmt3 call askilv ;yes, decide whether to interleave fmt3: cmp ss:hwtype[bp],hwflop ;floppy? je fmt4 cmp ss:hwtype[bp],hwcwsl ;Catweasel floppy? jne fmt7 fmt4: test ss:med[bp],-1 ;yes, medium type specified? jnz fmt7 mov bl,ss:drive[bp] ;get drive # xor bh,bh mov al,ds:fdtype[bx] ;look up drive type test al,al ;known? jz fmt5 ;no, assume they have at least some plan cmp al,rx33 ;1.2MB drive? jne fmt6 ;no fmt5: mov al,rx50 ;otherwise default to RX50 fmt6: xor ah,ah ;AH=0 mov ss:med[bp],ax ;save fmt7: call setmed ;set medium-related stuff call setsys ;set file system related stuff fmt8: ; format volume mov bx,ss:hwtype[bp] ;get hardware type call ds:fmthw[bx] ;dispatch (format the volume) ;;; should do DLBAD/DMBAD as a separate step (dev-independent) ;;; then add null BAD.TSK style bad blk list in last good data block ; do file-system-specific software init test ss:fsys[bp],-1 ;did they specify a file system? jz fmt9 ;no, don't try to write a blank one ; write blank file system (if specified) mov bx,ss:fsys[bp] ;get file system mov si,offset inisys ;pt at table call wdluk ;look it up jc fmt9 call ax ;init fmt9: cmp ss:hwtype[bp],hwflop ;floppy drive? je fmt10 cmp ss:hwtype[bp],hwcwsl ;Catweasel floppy drive? jne fmt11 fmt10: ; prompt to format more if floppy cram 'Format more disks (Y/N)? ',wrng call gtlin ;read a line call getw ;get their answer jc fmt10 ;if any cmp byte ptr [bx],'Y' ;does it start with 'Y'? je fmt8 fmt11: ret ; _=0 fmthw label word disp hwfile,fmtimg ;format image file disp hwflop,fmtflp ;format floppy disp hwcwsl,fmtflp ;format Catweasel floppy disp hwpart,fmthdp ;format hard disk partition disp hwtu58,cret ;format TU58 (nothing to do) ;+ ; ; Format image file. ; ;- fmtimg: mov ax,ss:totsiz[bp] ;get total size mov dx,ss:totsiz+2[bp] sub ax,ds:badsec ;subtract bad sector file info from SETMED sbb dx,ds:badsec+2 mov ds:fbcnt,ax ;save mov ds:fbcnt+2,dx ; write FBCNT zeros mov es,ds:bigbuf ;point at buffer xor di,di ;point at beginning mov cx,ds:bigsiz ;get size cmp ds:bigsiz+2,di ;is that it? jz fmti1 mov cx,-1 ;no, >64 KB so stop at end fmti1: mov ds:bigctr,cx ;save length xor ax,ax ;load 0 shr cx,1 ;/2 rep stosw ;clear out begn of buf adc cl,cl ;get CF back rep stosb ;clear odd byte fmti2: ; write next buffer of zeros mov es,ds:bigbuf ;pt at buffer mov cx,ds:bigctr ;get # bytes in buf, or 64 KB-1 test ds:fbcnt+2,-1 ;almost done? jnz fmti3 ;no cmp ds:fbcnt,cx ;yes, should we truncate? jae fmti3 mov cx,ds:fbcnt ;yes fmti3: jcxz fmti4 ;nothing to write, done mov bx,ss:handle[bp] ;get file handle push ds ;save push es ;copy ES to DS pop ds xor dx,dx ;offset=0 mov ah,40h ;func=write int 21h pop ds ;[restore] jc fmti7 ;error cmp ax,cx ;did we write all? jne fmti7 sub ds:fbcnt,ax ;subtract sbb ds:fbcnt+2,0 jmp short fmti2 ;loop fmti4: ; all done with zeros, add bad block file if any xor di,di ;point at begn of buf mov cx,ds:bigctr ;get # bytes in first page of buf ; ES:DI=byte after last byte to be written, CX=# bytes free there ; add bad block file if appropriate mov bx,ds:tmed ;get medium type mov si,offset filbad ;pt at table call wdluk ;look it up jc fmti5 call ax ;add it, update DI fmti5: mov cx,di ;copy jcxz fmti6 ;nothing to write, skip mov bx,ss:handle[bp] ;get file handle push ds ;save push es ;copy ES to DS pop ds xor dx,dx ;offset=0 mov ah,40h ;func=write int 21h pop ds ;[restore] jc fmti7 ;error cmp ax,cx ;did we write all? jne fmti7 fmti6: push ds ;restore original ES pop es ret fmti7: jmp wrerr ;write error ; if 0 ;;;; save this for CREATE fmtimg: ; create image file (replace bp with new temp record) mov ds:indev,bp ;save temp rec ptr mov si,ds:tfnam ;get ptr to filename mov cx,ds:tfnam+2 ;length call gdev ;parse device name mov ds:outdev,bp ;save dev rec call ss:defset[bp] ;set defaults mov di,offset fname1 ;pt at buffer xor bl,bl ;no implied wildcards call ss:gfile[bp] ;parse dir/filename call confrm ;make sure EOL ; say what we're doing mov di,offset lbuf ;pt at buf cram 'Creating image file ' call pdev ;print dev name mov cx,1 ;filename follows directory name call ss:dirnam[bp] ;print dir name mov si,offset fname1 ;pt at buffer fmti1: lodsb ;get a byte test al,al ;end? jz fmti2 stosb ;store jmp short fmti1 fmti2: call flush ;print line call aysure ;see if they're sure ;;; this should be rigged for DOS, foreign container files are too confusing mov ax,ds:tfsys ;get file system (not defaulted) mov ss:fsys[bp],ax ;save it ;;; ; determine length xor ax,ax ;clear for now mov ds:fbcnt,ax mov ds:fbcnt+2,ax mov ds:badsec,ax ;no bad sector file mov ds:badsec+2,ax mov bx,ds:tmed ;get medium type mov si,offset filfmt ;pt at table call wdluk ;look it up jc fmti3 call ax ;go figure length, set up for bad sec file jmp short fmti4 fmti3: ; unknown, ask them cram 'File size (bytes): ',wrng call gtlin ;get line call getn ;read number and ax,not 777 ;trim to 512 bytes fmti4: mov ds:fbcnt,ax ;save mov ds:fbcnt+2,dx ; init DEVSIZ mov cx,ss:devsiz[bp] ;DEVSIZ initted? or cx,ss:devsiz+2[bp] ;;; when wouldn't it be? this may be needless now jnz fmti5 ;yes, skip it mov ss:totsiz[bp],ax ;set total size in bytes mov ss:totsiz+2[bp],dx mov cl,9d ;bit count shr ax,cl ;convert to blocks ror dx,cl mov cx,dx ;copy and cl,200 ;isolate high 9 bits or ax,cx ;compose low word mov ss:devsiz[bp],ax and dx,177 ;low 7 bits mov ss:devsiz+2[bp],dx mov ss:totblk[bp],ax ;set total size in blocks mov ss:totblk+2[bp],dx fmti5: ; get creation date mov ax,ds:year ;copy date mov ds:fyear,ax mov ax,word ptr ds:day mov word ptr ds:fday,ax mov ax,word ptr ds:min ;time mov word ptr ds:fmin,ax mov al,ds:sec mov ds:fsec,al mov word ptr ds:ftimf,-1 ;mark both as valid ; open the file mov bp,ds:outdev ;make sure we still have bp call ss:dsrini[bp] ;init DSR call ss:dirinp[bp] ;init dir I/O call ss:dirout[bp] ;both ways (in case different) mov si,offset fname1 ;pt at filename call ss:create[bp] ;create the file ; remove space reserved for bad sector file mov ax,ds:fbcnt ;fetch mov dx,ds:fbcnt+2 sub ax,ds:badsec ;subtract sbb dx,ds:badsec+2 mov ds:fbcnt,ax ;save mov ds:fbcnt+2,dx ; write FBCNT zeros mov ds:binflg,bin ;binary mode mov es,ds:bigbuf ;point at buffer xor di,di ;point at beginning mov cx,ds:bigsiz ;get size cmp ds:bigsiz+2,di ;is that it? jz fmti6 mov cx,-1 ;no, >64 KB so stop at end fmti6: mov ds:bigctr,cx ;save length xor ax,ax ;load 0 shr cx,1 ;/2 rep stosw ;clear out begn of buf adc cl,cl ;get CF back rep stosb ;clear odd byte fmti7: ; write next buffer of zeros mov es,ds:bigbuf ;pt at buffer xor si,si mov cx,ds:bigctr ;get # bytes in buf cmp ds:fbcnt+2,si ;almost done? jnz fmti8 ;no cmp ds:fbcnt,cx ;yes, should we truncate? jae fmti8 mov cx,ds:fbcnt ;yes fmti8: jcxz fmti9 ;nothing to write, done call ss:write[bp] ;write a buffer jcxz fmti9 ;must be just about done, go finish up sub ds:fbcnt,cx ;subtract sbb ds:fbcnt+2,0 jmp short fmti7 ;loop fmti9: ; wrote all except possibly one partial block, add bad block file xor si,si ;point at data cmp ds:fbcnt+2,si ;<64 KB left right? jnz fmti11 ;we're bugged if not mov di,ds:fbcnt ;get # bytes we need to write mov cx,ds:bigctr ;get # bytes in first page of buf sub cx,di ;find # free ; ES:DI=byte after last byte to be written, CX=# bytes free there ; add bad block file if appropriate mov bx,ds:tmed ;get medium type mov si,offset filbad ;pt at table call wdluk ;look it up jc fmti10 call ax ;add it, update di fmti10: xor si,si ;point at begn mov cx,di ;get length push cx ;save call ss:write[bp] ;write it mov si,cx ;skip whatever couldn't be written pop cx ;catch desired length sub cx,si ;find # bytes still to go call ss:wlast[bp] ;should have fit, well pad it anyway push ds ;restore ES pop es call ss:close[bp] ;close file call ss:dirfin[bp] ;clean up mov bp,ds:indev ;restore original BP ret fmti11: jmp wrerr ;BIGBUF must be too small for cluster, ugh endif ;;;; 0 ;+ ; ; Prompt to see if they want a block or sector image file. ; ;- askilv: mov ax,ds:tintlv ;get /INTERLEAVE flag mov byte ptr ss:ilimg[bp],al ;assume sector image, or what they say test ah,ah ;did they say anything? jnz askil1 ;yes, don't prompt cram 'Create (B)lock or (S)ector image file? [S] ',wrng call gtlin ;read a line call getw ;get their answer jc askil1 ;if any cmp byte ptr [bx],'S' ;does it start with 'S'? je askil1 cmp byte ptr [bx],'B' ;how about 'B'? jne askilv inc ss:ilimg[bp] ;block-by-block askil1: ret ; ; List of routines to construct bad block table. ; filbad dw rl01,dlbad ;RL01 dw rl02,dlbad ;RL02 dw rk06,dmbad ;RK06 dw rk07,dmbad ;RK07 dw rm02,dmbad ;RM02 dw rm03,dmbad ;RM03 dw rm05,dmbad ;RM05 dw rm80,dmbad ;RM80 dw rp04,dmbad ;RP04 dw rp05,dmbad ;RP05 dw rp06,dmbad ;RP06 dw rp07,dmbad ;RP07 dw 0 ; dlbad: ; construct RL01 bad sector file (ES:DI points to buf, updated) ; N.B. RSTS 7.0 DSKINT is bugged, displays SN as 32-bit octal # ; instead of skipping bit 15 of each word. 9.7 is OK though. cmp cx,40d*256d ;enough space for bad block file? jb membad ;no, we lose push es ;save push di push ds ;copy DS to ES pop es cram 'Serial # [1234512345]: ',wrng ;prompt for serial # call gtlin ;get a line call getw ;get a word jc dlbad2 call cvto ;get a number sal ax,1 ;left a bit rcl dx,1 ;into dx shr ax,1 ;fix, clear b15 and dh,177 ;clear b15 dlbad1: ; pack SN in DX:AX pop di ;point at free space pop es mov si,di ;save a copy stosw ;save low word mov ax,dx ;high word stosw xor ax,ax ;load 0 stosw ;(reserved) stosw ;data pack (not alignment pack) dec ax ;all ones mov cx,(126d*2)+(128d*2) ;pad out, 18-bit version is all ones rep stosw ;(different from other last-track disks which ;expect valid 18-bit version too -- but there ;was never an 18-bit controller for RLs) push ds ;save push es ;copy ES to DS pop ds mov cx,9d*256d*2 ;# words to copy (9 more block pairs) rep movsw pop ds ;restore ret dlbad2: mov ax,12345 ;default mov dx,ax jmp short dlbad1 membad: cram '?Out of memory writing bad block file',error ; dmbad: ; construct RK06 bad sector file (ES:DI points to buf, updated) ; (should be OK for any last-track device) cmp cx,ds:badsec ;enough space for bad block file? jb membad ;no, we lose push es ;save push di push ds ;copy DS to ES pop es cram 'Serial # [12345]: ',wrng ;prompt for serial # call gtlin ;get a line call getw ;get a word jc dmbad2 call cvto ;get a number dmbad1: ; pack SN in AX pop di ;point at free space pop es mov si,di ;save a copy stosw ;save SN xor ax,ax ;load 0 stosw ;(reserved, high word of SN on RLs) stosw ;(reserved) stosw ;data pack (not alignment pack) dec ax ;all ones mov cx,(126d*2) ;pad out rep stosw mov cx,ds:badsec ;# bytes in last track sub ch,512d/400 ;minus the block we already did push ds ;save push es ;copy ES to DS pop ds rep movsb ;fill the rest (16/18-bit vers are the same) pop ds ;restore ret dmbad2: mov ax,12345 ;default jmp short dmbad1 dmbad3: jmp membad ;+ ; ; Format floppy. ; ; ss:bp log dev rec ; ;- fmtflp: call prsent ;wait until they're ready call ss:dsrini[bp] ;init for I/O mov word ptr ds:flphd,0 ;init head, cyl # ; set up FLPVEC to give special handling for DECmate 5.25" disks mov ax,offset fdzrt ;normally use 1:1 mov bx,ss:med[bp] ;get medium type cmp bx,rx50 ;RX50 or RX52? je fflp1 cmp bx,rx52 jne fflp3 fflp1: mov bx,ss:fsys[bp] ;get file system cmp bx,os8 ;OS/8 etc.? je fflp2 cmp bx,ps8 je fflp2 cmp bx,putr je fflp2 cmp bx,cos310 jne fflp3 fflp2: mov ax,offset fdzos ;interleave depends on track # fflp3: mov ds:flpvec,ax ;save fflp4: ; format next track call ptrkhd ;display track (and head if DS) mov al,ds:flpcyl ;get cyl # call ds:flpvec ;compute hard interleave table for this track mov di,ds:dbuf ;pt at buffer push si ;save mov cl,ss:nsecs[bp] ;loop count xor ch,ch fflp5: ; write C,H,R,N values for each sector mov ax,word ptr ds:flphd ;get head,,cyl xchg al,ah ;>< stosw ;save C, H lodsb ;R is from table mov ah,ds:fdlen ;N is whatever we set stosw loop fflp5 mov dh,ds:flphd ;get head mov ch,ds:flpcyl ;and cyl call fdftk ;format track jc fflp7 pop si ;restore ptr to interleave table call vtrack ;verify cmp ss:nhds[bp],2 ;double sided? jb fflp6 xor byte ptr ds:flphd,1 ;yes, flip head # jnz fflp4 ;go do side 1 of this cyl fflp6: inc ds:flpcyl ;go to next cyl mov al,ds:flpcyl ;get it cmp al,ss:ncyls[bp] ;done all? jb fflp4 ;loop if not callr fmtcmp ;print msg, return fflp7: jmp fderr ;go decode error ;+ ; ; RX50 format interleave routines. ; ; Each of these routines is called once before each track is formated, with the ; cylinder number in AL of the track about to be formatted. They should return ; a pointer in si to a 10-byte list of sector numbers, in the order they should ; be written on the disk. ; ;- fdzrt: ; format RX50 for RT-11 mov si,offset ilv11 ;always 1:1, driver does 2:1 in software ret ; fdzos: ; format RX50 for OS/8 ; 1:1 except tracks 0, 78, and 79, which are 2:1 test al,al ;track 0? jz fdzos2 cmp al,78d ;78. or above? jae fdzos1 ; tracks 1-77, start 2 sectors later each track mov si,offset ostk1 ;pt at starting posn dec ax ;-1 (start pattern at track 1) cbw ;AH=0 mov bl,5 ;get track-1 mod 5 div bl ;into ah mov al,ah ;get remainder cbw ;AH=0 sal al,1 ;*2 sub si,ax ;back up ret fdzos1: ja fdzos3 ;79., skip fdzos2: ; track 0 or 78., use 2:1 interleave mov si,offset ostk78 ;pt at table ret fdzos3: mov si,offset ostk79 ;pt at table ret ; db 3,4,5,6,7,8d,9d,10d ostk1 db 1,2,3,4,5,6,7,8d,9d,10d ;1:1 2-sector skew tracks 1-77. ostk78 db 1,6,2,7,3,8d,4,9d,5,10d ;2:1 for 0 and 78. ostk79 db 5,10d,1,6,2,7,3,8d,4,9d ;2:1, 2-sector skew from track 78. ;+ ; ; Print cyl # from FLPCYL, and head from FLPHD if double sided. ; ; bp dev rec ptr ; ;- ptrkhd: mov di,offset lbuf ;pt at buf cram 'Track ' mov al,ds:flpcyl ;get curr cyl # call pbyte ;convert to decimal cmp ss:nhds[bp],2 ;double-sided? jb ptkhd1 ;no cram ' Head ' mov al,ds:flphd ;get head # call pbyte ;decimal ptkhd1: mov al,cr ;cr stosb inc ds:lnum ;don't count this because no LF jmp flush1 ;display, return ;+ ; ; Print "Format complete" message ; ;- fmtcmp: mov di,offset lbuf ;pt at buf cram 'Format complete' jmp flush ;display, return ;+ ; ; Verify a track. ; ; si ptr to interleave table used to format the track ; ;- vtrack: ; build table of sectors which we've already formated mov di,offset buf ;pt at BUF mov cl,ds:fdeot ;get track length mov dl,cl ;copy mov dh,cl ;again xor ch,ch ;CH=0 mov al,ch ;AL too rep stosb ;clear flags xor bx,bx ;starting offset vtk1: ; find next unread sector (2:1 soft interleave) inc bx ;+1 (will be +2 in a sec) jmp short vtk3 vtk2: cmp ds:buf[bx],bh ;have we done this one? jz vtk4 ;no, do it vtk3: inc bx ;+1 cmp bl,dh ;off end of track? jb vtk2 sub bl,dh ;yes, wrap jmp short vtk2 vtk4: inc ds:buf[bx] ;set flag push bx ;save push dx push si mov dh,ds:flphd ;get head # mov cl,[bx+si] ;sec # mov ch,ds:flpcyl ;cyl # call fdrds ;read sector (with retries) jc vtk5 ;error, punt pop si ;restore pop dx pop bx dec dl ;done? jnz vtk1 ;loop if not ret vtk5: call error ;punt verr1 db verr2,lf,'?Verify error' verr2= $-verr1-1 ;+ ; ; "Format" a hard disk partition. ; ;- fmthdp: cram "?Don't know how to do HDs yet",error ;+ ; ; INIT dev /FILESYSTEM /MEDIUM ; ; Wipe all files from the volume, initialize it for use. ; ;- init: mov bx,-1 ;don't get logical name mov ds:othswt,offset inisw ;"INIT" switch table mov ds:nsegs,0 ;(use default if no /SEGMENTS:n switch) call gmount ;parse stuff, get temp rec at SS:BP call opndev ;actually open the device call defmed ;figure out medium defaults if needed call setmed ;set medium-related stuff call setsys ;set file system related stuff ; write blank filesystem mov bx,ss:fsys[bp] ;get file system test bx,bx ;any? jz init1 mov si,offset inisys ;pt at table call wdluk ;look it up jc init2 ;not found callr ax ;init, return init1: cram '?Must specify file system type',error init2: cram "?Don't know how to init that filesystem",error ; inisw label byte kw ,iniseg ;set # segments in RT-11 directory db 0 ; inisys dw os8,zos ;init OS/8 disk/DECtape dw ps8,zos ;init PS/8 DECtape dw putr,zos ;init TSS/8 PUTR DECtape dw rt11,zrt ;init RT-11 disk dw 0 ;+ ; ; Zero a disk with OS/8 file structure. ; ; bp log dev rec ; ;- zos: call ss:dsrini[bp] ;init I/O ; get # info words per entry mov di,offset lbuf ;pt at buf cram 'Number of info words per dir entry [1]: ' call flush1 call gtlin ;get a line call getw ;get a word jc zos1 call cvto ;get a number jmp short zos2 zos1: ; default # info words is 1 (for date) mov ax,1 ;default is 1 cwd ;DX=0 zos2: test dx,dx ;DX must be 0 jnz zos3 cmp ax,256d-5 ;at least one entry must fit in dir seg jbe zos4 zos3: jmp zos8 ;invalid # of info words zos4: mov cx,10000 ;negate sub cx,ax and ch,7777/400 ;might be 0 push cx ;save mov di,offset lbuf ;pt at buf cram 'Allocate space for system [N]? ' call flush1 call yorn ;get response pop cx ;[restore] mov dx,7 ;[start of data area for data disk] jc zos5 mov dl,70 ;start of data area for system disk zos5: ; ensure that device size is in range mov ax,ss:devsiz+6[bp] ;make sure size .LT. 64 K blocks or ax,ss:devsiz+4[bp] or ax,ss:devsiz+2[bp] jnz zos7 ;nope cmp ss:devsiz[bp],dx ;space for dir plus at least one data blk? jbe zos7 ;(utterly useless if not) ; init home block mov di,offset buf ;point at buffer mov si,di ;save a copy mov ax,7777 ;0000/ one entry in seg stosw mov ax,dx ;0001/ starting blk # of empty area stosw xor ax,ax ;0002/ next dir seg (none) stosw stosw ;0003/ no tentative file mov ax,cx ;0004/ -(# info words) stosw xor ax,ax ;0005/ .EMPTY. block stosw mov ax,dx ;get starting block sub ax,ss:devsiz[bp] ;subtract device size (get -(size of .EMPTY.)) and ah,7777/400 ;trim to 12 bits stosw ;0006/ -length xor ax,ax ;might as well clear out rest mov cx,256d-7 ;first 7 words already done rep stosw ; write directory in block 1 (si=ptr to buf) mov ax,1 ;home block is block 1 cwd mov cx,ax ;write 1 block add ax,ss:curdir[bp] ;add base of partition adc dx,ss:curdir+2[bp] call ss:wrblk[bp] ;go jc zos6 ;;; write dummy boot block ret zos6: jmp wrerr zos7: cram '?Invalid device size',error zos8: cram '?Invalid # of info words',error ;+ ; ; Zero a disk with RT-11 file structure. ; ; bp log dev rec ; ;- zrt: call ss:dsrini[bp] ;init I/O ; ensure that device size is in range cmp ss:cursiz[bp],6+2+1 ;space for boot + one dir seg + one blk? jae zrt1 ;(utterly useless if not) cram '?Invalid device size',error zrt1: ; init home block mov bx,offset buf ;pt at disk buffer mov di,bx ;copy mov cx,1000/2 ;word count xor ax,ax ;load 0's rep stosw ;nuke it lea di,[bx+722] ;pt at cluster size inc ax ;(HB+722) cluster size is always 1 stosw mov al,6 ;(HB+724) dir starts at 6 (regardless of this) stosw mov ax,107123 ;(HB+726) version=.RAD50 /V05/ stosw mov ax," " ;blanks mov cx,3*12d/2 ;byte count (pad all 3 text fields) rep stosw ; get volume label mov di,offset lbuf ;pt at buf cram 'Volume ID : ' rt11a= $-8d call flush1 call gtlin ;read a line mov di,offset buf+730 ;pt at vol ID call skip ;did user enter anything? jc zrt4 mov dx,12d ;max field size zrt2: lodsb ;get a byte cmp al,' ' ;ctrl char? jb zrt3 ;(ZF=0) stosb ;save dec dx ;count zrt3: loopnz zrt2 ;loop until full or done jmp short zrt5 zrt4: mov si,offset rt11a ;pt at string mov cx,5 ;count rep movsb zrt5: ; get owner name mov di,offset lbuf ;pt at buf cram 'Owner name: ' call flush1 call gtlin ;read a line mov di,offset buf+744 ;pt at owner name call skip ;did user enter anything? jc zrt8 ;no mov dx,12d ;max field size zrt6: lodsb ;get a byte cmp al,' ' ;ctrl char? jb zrt7 ;(ZF=0) stosb ;save dec dx ;count zrt7: loopnz zrt6 ;loop until full or done zrt8: mov di,offset buf+760 ;system ID cram 'DECRT11A' mov si,offset buf ;pt at buf mov cx,(1000/2)-1 ;word count, except for first and last words xor bx,bx ;init sum zrt9: lodsw ;get next add bx,ax ;add it in loop zrt9 ;loop neg bx ;negate mov [si],bx ;poke it in ; write home block mov si,offset buf ;pt at buffer mov ax,ss:curdir[bp] ;get base of partition mov dx,ss:curdir+2[bp] add ax,1 ;home block is block 1 adc dx,0 mov cx,1 ;write 1 block call ss:wrblk[bp] ;go ; decide how many dir segments to use mov dx,ds:nsegs ;get value from /SEGMENTS:n switch test dx,dx ;if any jnz zrt11 ;yes, use it mov si,offset rtnseg ;pt at # of segments table zrt10: lodsw ;get size mov bx,ax ;copy lodsw ;get # segments mov dx,ax ;take it cmp bx,ss:cursiz[bp] ;< actual size? jb zrt10 ;loop if so zrt11: ; write a null directory mov di,offset buf ;pt at buffer xor ax,ax ;load 0 mov cx,1000/2 ;WC rep stosw ;nuke it mov di,offset buf ;pt at begn again ; write 5-word segment header mov ax,dx ;get # segs stosw ;word 1 -- total # segments xor ax,ax ;no next seg stosw ;word 2 -- ptr to next segment in chain inc ax ;1 stosw ;word 3 -- # of highest segment in use xor ax,ax ;0 stosw ;word 4 -- # of extra words per entry mov ax,dx ;get # segs sal ax,1 ;*2 = # blocks in directory add ax,6 ;add starting block # stosw ;word 5 -- starting data blk for this seg ; write < UNUSED > entry mov ax,1000 ;type=< UNUSED > stosw mov ax,23747 ;.RAD50/FOO/ stosw ;filename is meaningless stosw stosw mov ax,ss:cursiz[bp] ;get size of partition sub ax,word ptr ds:buf+8d ;find amount left for files stosw ;length xor ax,ax ;job/chan info stosw stosw ;date is meaningless on empties mov ax,4000 ;end-of-segment marker stosw mov si,offset buf ;pt at buffer mov ax,ss:curdir[bp] ;get base of partition mov dx,ss:curdir+2[bp] add ax,6 ;first dir block adc dx,0 mov cx,1 ;write 1 block call ss:wrblk[bp] ;go ;;; write dummy boot block ret ; ; Table from RT-11 V04.00 SUG: ; ; dev mnem segs total blocks ; RX01 DX 1 494 ; RX01 PD 1 494 ; TU58 DD 1 512 ; TU56 DT 1 578 N.B. SUG is *wrong*, DUP correctly writes 4 ; RX02 DY 4 988 ; RF11 RF 4 1024 ; RS03/4 DS 4 1024/2048 ; RK05 RK 16 4800 ; RL01 DL 16 10240/20480 ; RK07 DM 31 x/53790 ; RP02 DP 31 40000 ; RP03 DP 31 40000 per half (each drive works as two RT-11 units) ; ; Sizes from Tony Konashenok (who got them out ; of RT-11 V5.4D DUP.SAV), confirmed by Bob Schor ; (who got them by experimenting with DUP) rtnseg dw 512d ;Up to 512 blocks dw 1 ;we get 1 segment. dw 2048d ;Up to 2048 blocks dw 4 ;we get 4 segments. dw 12288d ;Up to 12288 blocks dw 16d ;we get 16 segments. dw -1 ;Over that dw 31d ;we get 31 segments (max allowed). ; iniseg: ; INIT /SEGMENTS:n switch jcxz insg2 ;can't be EOL lodsb ;eat the ':' dec cx ;count it off cmp al,':' ;must be ':' je insg1 cmp al,'=' ;or '=' jne insg3 insg1: call getn ;get number dec ax ;change 1 to 0 cmp ax,31d-1 ;in range [1,31.]? ja insg4 ;no inc ax ;yes, fix it mov ds:nsegs,ax ;save ret insg2: jmp misprm ;missing parameter insg3: jmp synerr ;syntax error insg4: jmp outran ;number out of range ;+ ; ; MOUNT log: [filespec]/struc/medium ; ; log: is logical device name (of the form d[d][n]:) ; filespec is name of disk image file (if not real disk) ; /STRUC is the file structure (e.g. /OS8, /RT11, default is to auto-detect) ; /MEDIUM is the medium type (default is to auto-detect) ; ; Possible commands: ; MOUNT A: /OS8 (defaults to A:, RX50) ; MOUNT DZ0: A: /OS8 ; MOUNT DY0: B: /RX02 /RT11 ; MOUNT DX0: A: /RX01 /RT11 ; MOUNT DL0: C:SY.DSK /RT11 ; MOUNT DU0: /DRIVE=0 /BPART=2 (doesn't work yet) ; MOUNT DD0: COM1:19200/DRIVE=1 ; MOUNT X: SCSI5: /MSCP/RT11/PART:1 ; ;- mount: xor bx,bx ;get logical dev name mov ds:othswt,0 ;no "other" switches call gmount ;get MOUNT command line push word ptr ds:fremem ;save free memory pointer ;(so MNTSYS code can alloc dir bufs) call opndev ;actually open the device call defmed ;figure out medium defaults if needed call setmed ;set medium-related stuff call defsys ;figure out file system if needed call setsys ;set file system related stuff ; do file-system-specific init mov bx,ss:fsys[bp] ;get it mov si,offset mntsys ;pt at table call wdluk ;look it up jc mount1 call ax mount1: ; we'll succeed, so make sure MLOOP doesn't close file or flush rec pop word ptr ds:kepmem ;make sure we don't flush the dev rec's RAM mov ds:tmpdev,0 ;tell MLOOP not to return it mov ds:outhnd,-1 ;and don't close the image file, if any ; link logical device record into table second (so as not to log in) mov bx,ds:logdev ;get head of list test bx,bx ;is there anything? jz mount2 mov ax,bp ;yes, copy xchg ax,ds:next[bx] ;link us in, fetch rest of list mov ss:next[bp],ax ;link it to us ret mount2: mov ss:next[bp],bx ;rest of list is empty mov ds:logdev,bp ;just us ret ; mnthd: ; mount hard drive partition ;;;; should get geometry ;;;; ret ; ; mount file system (once hardware is set up) mntsys dw cos310,mntos ;COS-310 dw dosbat,mntdb ;DOS/BATCH dw ods1,mntod ;Files-11 ODS-1 dw os8,mntos ;OS/8 dw ps8,mntos ;PS/8 dw putr,mntos ;TSS/8 PUTR dw rsts,mntrs ;RSTS dw rt11,mntrt ;RT11 dw tss8,mntts ;TSS/8.24 dw xxdp,mntxx dw 0 ; mntdb: ; mount DOS/BATCH file system mov ax,101h ;UIC=[1,1] mov ss:curdir[bp],ax ;set current dir mov ss:actdir[bp],ax ;active dir too call dbdi ;init for dir input call dbfd ;find this dir mov ss:cursiz[bp],cx ;save size of dir ents mov word ptr ss:dirbuf[bp],0 ;unlink dir bufs so MLOOP won't touch ;(after dir buf memory flushed by MOUNT) ret ; mntod: ; mount Files-11 ODS-1 file system call odhome ;find home block jnc mntod1 jmp rderr ;failed mntod1: ;; save DX:AX (home block LBN) somewhere if we need to mov dx,[si+2] ;get H.IBLB (used to be just 24 bits) mov ax,[si+4] ;(VBN 3 of INDEXF.SYS, start of header bitmap) mov ss:indmap[bp],ax ;save mov ss:indmap+2[bp],dx mov cx,[si] ;get H.IBSZ (# blks in header bitmap, was byte) mov ss:indbsz[bp],cx ;save add ax,cx ;add that in to get to first file header adc dx,0 mov ss:filhdr[bp],ax ;save (addr of first 16 file headers) mov ss:filhdr+2[bp],dx mov ax,[si+10] ;get H.SBCL (storage bitmap cluster factor) mov ss:pcs[bp],ax ;equivalent to RSTS PCS, use same variable mov ax,[si+14] ;get H.VLEV (ODS volume structure level) mov ss:rdslev[bp],ax mov ax,[si+44] ;get H.DFPR (default file protection bits) mov ss:defprt[bp],ax mov al,[si+55] ;get H.FIEX (default file extension value) cmp al,1 ;must be NZ for extending INDEXF.SYS and dirs sbb ah,ah ;all ones if was 0 and ah,16d ;say 16. if field is 0 or al,ah xor ah,ah ;zero-extend mov ss:filext[bp],ax mov ax,(36*50+36)*50+36 ;^R000 (36 is .RAD50 for zero) mov ss:curdir[bp],ax ;CWD=[0,0] mov ss:curdir+2[bp],ax xor ax,ax ;load 0 mov ss:curdir+4[bp],ax ;finish off .RAD50 /xxxyyyzzz/ mov ss:curdir+6[bp],ax ;end of path mov ss:satptr[bp],ax ;start allocating at begn mov ss:satptr+2[bp],ax ret ; mntos: ; mount OS/8 (or COS-310) file system call ospart ;set up partition jc mntos1 ;failed ret mntos1: jmp nxpart ; ospart: ; set up OS/8 partition selected by TPART xor ax,ax ;load 0 mov ss:curdir[bp],ax ;log in to root (no LD:s) mov ss:curdir+2[bp],ax mov ax,7777 ;assume max size cmp word ptr ss:med[bp],rk05 ;only matters for RK05s jne osprt1 ;skip it, all set mov ax,3200d ;# blocks in an OS/8 RK05 partition mov cx,ds:tpart ;get partition # cmp cx,2 ;must be 0 or 1 jae osprt2 ;isn't jcxz osprt1 ;part 0, already there mov ss:curdir[bp],ax ;change starting addr osprt1: mov ss:cursiz[bp],ax ;set size of partition clc ;happy ret osprt2: stc ;bad partition # ret ; mntrs: ; mount RSTS file system ; calculate device cluster size (smallest power of 2 for 16-bit clu #s) mov ax,1 ;assume DCS=1 mov bx,ss:devsiz+2[bp] ;get high order of dev size mov cx,ss:devsiz+4[bp] mov dx,ss:devsiz+6[bp] mtrs1: ; see how many times we have to divide the dev size by 2 to make the ; size fit in 16 bits mov si,bx ;see if high bits are all 0 or si,cx or si,dx jz mtrs2 ;they are, we're done shr dx,1 ;high-order size right a bit rcr cx,1 rcr bx,1 add ax,ax ;DCS left a bit cmp ax,64d ;<=64. right? jbe mtrs1 ;yes, keep looking cram '?Invalid DCS',error mtrs2: mov ss:dcs[bp],ax ;save DCS ; read pack label (in MFD if RDS 0.0) call ss:dsrini[bp] ;init DSR mov di,ds:dbuf ;pt at buffer mov ax,1 ;home block mov cx,ax ;read 1 block mul ss:dcs[bp] ;starting at DCN 1 call ss:rdblk[bp] ;fetch it mov si,ds:dbuf ;get ptr to home block mov ax,[si+12] ;get pack status word mov ss:paksts[bp],ax ;save (for NFF check in RSCL) mov di,offset lbuf ;pt at buf cram 'Pack ID=' mov ax,[si+14] ;get pack ID again call radnb mov ax,[si+16] call radnb cram ', PCS=' mov ax,[si+10] ;get pack clustersize mov ss:pcs[bp],ax ;save mov bx,ax ;copy dec bx ;-1 and bx,ax ;make sure power of 2 jnz mtrs3 ;nope cmp ax,64d ;.LE.64? ja mtrs3 cmp ax,ss:dcs[bp] ;PCS.GE.DCS? jb mtrs3 ;nope, invalid push ax ;yes, save xor dx,dx ;zero-extend div word ptr ss:dcs[bp] ;find # DCs per PC mov ss:pcsdcs[bp],ax ;save pop ax ;restore call pbyte ;convert it cram ', DCS=' mov ax,ss:dcs[bp] ;get DCS call pbyte cram ', RDS level ' test byte ptr [si+13],40 ;new (V8.0+) pack? jnz mtrs4 ; RDS 0.0 xor ax,ax ;RDS 0.0 jmp short mtrs5 mtrs3: cram '?Invalid PCS',error mtrs4: ; RDS 1.X mov ax,[si+4] ;DCN of MFD mov ss:mfddcn[bp],ax ;save mov ax,[si+6] ;get RDS level mtrs5: ; RDS level AH.AL mov ss:rdslev[bp],ax ;set in log dev rec push ax ;save mov al,ah call pbyte ;print major version mov al,'.' ;. stosb pop ax ;restore call pbyte call flush ;flush mov ax,1 ;load 1 mov ss:curdir[bp],ax ;PPN=[0,1] mov ss:actdir[bp],ax ;active dir too (for RSGD) mov ss:satptr[bp],ax ;init SATT.SYS cluster alloc ptr ; compute # of PCs on disk mov ax,ss:devsiz[bp] ;get dev size (known to fit in 32 bits) mov dx,ss:devsiz+2[bp] ;get high order of dev size sub ax,ss:dcs[bp] ;remove DCN 0 sbb dx,0 div word ptr ss:pcs[bp] ;find # PCs (remainder in DX is wasted) mov ss:numclu[bp],ax ;save ; look up [0,1]SATT.SYS xor cx,cx ;no command line call rsgd ;find [0,1] dir call rsdi ;init for dir input mtrs6: call rsdg ;get next dir entry jc mtrs10 ;shouldn't get this far mov si,offset satsys ;point at SATT.SYS filename mov cx,9d ;LEN('SATT.SYS'<0>) repe cmpsb ;is this it? jne mtrs6 ;loop if not ; make sure size is correct for the # of PCNs on this pack cmp ds:fsize+2,cx ;MSW of size should be 0 (CX=0 from REPE CMPSB) jnz mtrs10 ;error if not mov ax,ss:numclu[bp] ;get # PCs on disk add ax,(512d*8d)-1 ;round to next block boundary rcr ax,1 ;catch carry, if any mov cl,(9d+3)-1 ;shift count, minus the one we already did shr ax,cl ;find # blocks needed to map all PCs cmp ds:fsize,ax ;LSW of SATT.SYS size is big enough? jb mtrs10 ;no ; get SATT.SYS retrieval list mov ax,ds:iretr ;get first retrieval pointer call rslink ;follow it jc mtrs10 ;null! mov ax,[si+2] ;get first retrieval pointer mul word ptr ss:dcs[bp] ;find starting block of SATT.SYS mov ss:bitmap[bp],ax ;save mov ss:bitmap+2[bp],dx mov ax,ds:ifcs ;get file cluster size xor dx,dx ;DX=0 div word ptr ss:dcs[bp] ;find # DCs per FC mov di,ax ;save mov dx,[si+2] ;restore first retr pointer sub dx,di ;back up ; make sure SATT.SYS is contiguous mtrs7: mov cx,8d-1 ;# of retrieval pointers left lodsw ;get link to next mov bx,ax ;copy mtrs8: lodsw ;get next DCN from retrieval list add dx,di ;find expected DCN cmp ax,dx ;correct? jne mtrs10 ;no mov ax,ds:ifcs ;get SATT.SYS FCS sub ds:fsize,ax ;count off the cluster jbe mtrs9 ;done, file checks out OK loop mtrs8 ;handle next mov ax,bx ;copy link word push dx ;save push di call rslink ;follow it pop di ;[restore] pop dx jnc mtrs7 ;loop if OK jmp short mtrs10 mtrs9: mov word ptr ss:dirbuf[bp],0 ;unlink dir bufs so MLOOP won't touch ;(after dir buf memory flushed by MOUNT) ret mtrs10: cram '?Missing or invalid [0,1]SATT.SYS',error ; mntrt: ; mount RT11 file system call rtpart ;set up partition jc nxpart ;failed ret nxpart: cram '?Nonexistent partition',error ; rtpart: ; set up partition selected by TPART mov ss:curdir[bp],0 ;log in to root (no LD:s) mov ax,ds:tpart ;get partition # (default=0) mov ss:curdir+2[bp],ax ;save mov bx,ss:devsiz+4[bp] ;see if dev is huge or bx,ss:devsiz+6[bp] jnz rtprt2 ;yes, all partitions are full size cmp ax,ss:devsiz+2[bp] ;is this last partition? jb rtprt2 ;no ja rtprt1 ;beyond, error mov ax,ss:devsiz[bp] ;get low order size test ax,ax ;if any jnz rtprt3 ;yes ; exact 32 MB multiple so part # is too high rtprt1: stc ;bad partition # ret rtprt2: mov ax,0FFFFh ;give max possible size rtprt3: mov ss:cursiz[bp],ax ;set size of partition clc ;happy ret ; mntts: ; mount TSS/8.24 file system (SEGSIZ must be 256.) mov ax,ds:tpart ;get /PARTITION:n value (use to specify JOBMAX) test ax,ax ;defaulted? jnz mntts4 ;no, keep it mov ax,(5+1)*16d ;skip 5 fields for TSS/8 itself, assume JOBMAX ;is at least 1 so skip one more field mntts1: ; check next field to see if could be MFD (AX=starting block #) ; (format of first few accounts is fixed, laid down by REFRESH cmd) ; autodetection will be defeated if a swap track happens to contain a ; valid-looking MFD -- on real system # of swap tracks is known by the ; system so it doesn't have this problem xor dx,dx ;zero-extend block # mov di,offset buf ;pt at buffer mov cx,1 ;read 1 block push ax ;save call ss:rdblk[bp] ;go pop ax ;[restore] jc mntts3 ;error cmp cx,512d ;got all 512. bytes of block? jb mntts3 ;no mov di,offset tssmfd ;point at list xor bh,bh ;load 0 mntts2: mov bl,[di] ;get next address (zero-extended) inc di ;+1 cmp bl,-1 ;end of list? je mntts5 ;yes, success mov cx,[si+bx] ;get value at that offset mov bl,[di] ;get expected value (still zero-extended) inc di ;+1 cmp bx,cx ;match? je mntts2 ;yes, keep going add ax,16d ;no, skip to next field cmp ax,(5+25d)*16d ;past maximum JOBMAX of 25? jbe mntts1 ;loop if not mntts3: cram "?Can't find MFD",error mntts4: cmp ax,25d ;too high? ja mntts6 ;yes, out of range add ax,5 ;skip 5 fields for TSS/8 itself mov cl,4 ;shift count to multiply by 16 shl ax,cl ;find base mntts5: mov ss:mfddcn[bp],ax ;set addr of MFD mov ss:curdir[bp],0001 ;cur dir = [0,1] ret mntts6: jmp outran ; tssmfd label word ;expected TSS/8 MFD contents ;(N.B. all fit in a byte) db 3*2,10 ;first blockette points at second db 10*2,0001 ;[0,1] db 13*2,40 ;next dir entry db 17*2,20 ;retrieval list db 21*2,1 ;MFD starts with segment #1 for sure db 40*2,0002 ;[0,2] db 43*2,60 ;next dir entry db 47*2,50 ;retrieval list db 60*2,0003 ;[0,3] db 67*2,70 ;retrieval list db -1 ;end of list ; mntxx: ; mount new XXDP+ file system ;; mov ss:curdir[bp],101h ;UIC=[1,1] ret ;+ ; ; Set various things. ; ;- set: call getw ;get parameter jc set1 ;missing mov ax,offset setlst ;point at keyword list call tbluk ;look it up jc set2 ;not found callr ax ;call the routine set1: jmp misprm ;missing keyword set2: jmp synerr ;bad keyword ; setlst label byte kw ,setfd0 ;set floppy drive A: type kw ,setfd1 kw ,setfd2 kw ,stcopy ;set ASCII/BINARY default for COPY kw ,setfd3 kw ,setdsm ;set DISMOUNT /[NO]UNLOAD default kw ,setfdc ;set FDC type (CompatiCard or generic) kw ,stmore ;enable/disable **MORE** processing db 0 ; setfd0: ; set physical drive types for floppy drives ; (override CMOS defaults, in case user has hooked up some kind of ; reanimated corpse to their computer -- that's what we're here for!) xor bl,bl ;unit 0 jmp short setfd4 setfd1: mov bl,1 ;1 jmp short setfd4 setfd2: mov bl,2 ;2 jmp short setfd4 setfd3: mov bl,3 ;3 setfd4: xor bh,bh ;zero-extend unit # push bx ;save call getw ;get type pop ax ;[restore] jc setfd5 push ax ;(save) mov ax,offset medtab ;point at keyword list call tbluk ;look it up pop bx ;[restore unit #] jc setfd6 ;not found ; check to make sure it's a valid floppy drive type mov di,offset fdlist ;pt at list mov cx,fdlstl ;# entries repne scasw ;see if this is on it jne setfd6 mov ds:fdtype[bx],al ;we're OK, save it ret setfd5: jmp misprm ;missing keyword setfd6: jmp synerr ;bad keyword ; fdlist label byte dw rx01 ;250 KB 8" SS SD dw rx02 ;500 KB 8" SS DD dw rx03 ;1001 KB 8" DS DD dw rx31 ;(we don't support 360 KB drives, but still) dw rx50 ;real RX50 drive with FDADAP (or TM100-4 etc.) dw rx33 ;1.2 MB 5.25" dw rx24 ;720 KB 3.5" dw rx23 ;1.44 MB 3.5" dw rx26 ;2.88 MB 3.5" fdlstl= ($-fdlist)/2 ; stcopy: ; SET COPY call getw ;get parameter jc stcpy1 mov ax,offset scpytb ;pt at table call tbluk ;look up jc stcpy2 mov ds:bindef,ax ;save value callr confrm ;should be EOL stcpy1: jmp misprm ;missing parameter stcpy2: jmp synerr ;bad keyword ; setdsm: ; SET DISMOUNT call getw ;get parameter jc stdsm1 mov ax,offset dmtsws ;pt at table call tbluk ;look up jc stdsm2 mov ds:dsmunl,al ;save value callr confrm ;should be EOL stdsm1: jmp misprm ;missing parameter stdsm2: jmp synerr ;bad keyword ; setfdc: ; SET FDC call getw ;get parameter jc stfdc1 mov ax,offset sfdctb ;pt at table call tbluk ;look up jc stfdc2 mov byte ptr ds:cc4fdc,al ;save value callr confrm ;should be EOL stfdc1: jmp misprm ;missing parameter stfdc2: jmp synerr ;bad keyword ; stmore: ; SET MORE call getw ;get parameter jc stmor1 mov ax,offset onoff ;pt at table call tbluk ;look up jc stmor2 mov ds:more,al ;save value callr confrm ;should be EOL stmor1: jmp misprm ;missing parameter stmor2: jmp synerr ;bad keyword ; scpytb label byte kw ,text kw ,bin db 0 ; sfdctb label byte kw ,1 ;Micro Solutions CompatiCard IV kw ,0 ;anything else ;;; maybe should add an entry for 37C65 chips that use an unusual way to set ;;; vertical mode, for now I just write the port if the version code matches ;;; but that could lead to trouble with FDCs that return the same version code ;;; (a lot do) but don't like writes to 3F1h db 0 ; onoff label byte kw ,0 kw ,-1 db 0 ;+ ; ; Show dev parameters. ; ;- show: call skip ;blank line? jnc show4 ;no mov bp,ds:logdev ;get head of logical device list test bp,bp ;if any jz show3 show1: call show5 ;print next device mov bp,ss:next[bp] ;follow link show2: test bp,bp ;anything left? jz show3 ;no mov di,offset lbuf ;pt at buf call flush ;blank line between devs jmp short show1 show3: ret show4: ; non-blank command line call gdevn ;get dev name (or use default) call confrm ;check for EOL show5: ; print information for device at SS:BP mov di,offset lbuf ;point at buf call pdev ;print dev name mov al,' ' ;add a blank stosb cmp ss:dirinp[bp],offset dosdi ;is it a DOS disk? jne show6 ;no cram 'DOS' ;DOS device, no useful information callr flush show6: cmp ss:hwtype[bp],hwfile ;image file? jne show7 ;no test byte ptr ss:ilimg[bp],-1 ;yes, interleaved? jz show7 cram 'Interleaved ' ;yes show7: mov bx,ss:med[bp] ;get medium type mov si,offset devnam ;point at name list call pname ;print it mov ax," ," ;', ' stosw mov bx,ss:fsys[bp] ;get file system mov si,offset fsynam ;point at name list call pname ;print it mov ax," ," ;', ' stosw mov ax,ss:devsiz[bp] ;get # usable blocks mov bx,ss:devsiz+2[bp] mov cx,ss:devsiz+4[bp] mov dx,ss:devsiz+6[bp] push ax ;(save) push bx push cx push dx call pqnum ;print it cram ' total usable block' pop dx ;(restore) pop cx pop bx pop ax dec ax ;size=1? or ax,bx or ax,cx or ax,dx jz show8 ;yes mov al,'s' ;add 's' if not stosb show8: ; add current partition/LD: size for RT-11 cmp ss:fsys[bp],rt11 ;RT? jne show11 call flush ;flush line, start new one mov ax,ss:cursiz[bp] ;get # usable blocks mov dx,ss:cursiz+2[bp] push dx ;save push ax call pdnum ;print it cram ' block' pop dx ;(restore) pop ax dec ax ;size=1? (as above) jnz show9 cmp dx,ax ;(AX now =0) jz show10 show9: mov al,'s' ;add 's' if not stosb show10: cram ' in current volume' show11: callr flush ;+ ; ; TYPE {wildcard} ; ;- typ: ; get filespec call gdev ;parse device name, get dev rec in bp call ss:defset[bp] ;set defaults mov di,offset fname1 ;pt at buffer xor bl,bl ;no implied wildcards call ss:gfile[bp] ;parse dir/filename call confrm ;make sure EOL xor bx,bx ;input file call ss:savcwd[bp] ;save dir call ss:dsrini[bp] ;init DSR call ss:dirinp[bp] ;init dir input mov byte ptr ds:notfnd,1 ;nothing found yet ; init fake output device mov ds:binflg,text ;set text mode push bp ;save call getrec ;get a record (to be flushed in main loop) mov ss:dsrini[bp],offset cret ;no init needed mov ss:rstcwd[bp],offset cret ;no settings to restore mov ss:write[bp],offset wtty ;output routine mov ss:wlast[bp],offset wtty ;write last block (not called) mov ss:dirfin[bp],offset cret ;no dir finish mov ds:outdev,bp ;set output pseudo-device pop bp ;restore mov ds:indev,bp ;set input device typ1: ; check next file call ss:dirget[bp] ;get next entry jc typ4 ;no more jz typ1 ;skip empty areas and dirs mov si,offset fname1 ;pt at wildcard push di ;save call wild ;check for match pop di ;[restore] jne typ1 ;ignore ; it's a match, type filename call flush ;display filename xor al,al ;load 0 xchg al,ds:notfnd ;found one, see if first test al,al ;is it first? jnz typ2 ;yes, skip mov byte ptr ds:lnum,1 ;**MORE** on next line typ2: ; type the file mov ds:optr,offset lbuf ;init output ptr mov byte ptr ds:col,0 ;init col call ss:open[bp] ;open it jc typ5 call fcopy ;copy the file mov di,ds:optr ;get output ptr cmp di,offset lbuf ;anything on line? je typ3 ;no call flush ;yes, flush it typ3: mov bp,ds:indev ;get input device again call ss:dsrini[bp] ;init it xor bx,bx ;select input dir call ss:rstcwd[bp] call ss:reset[bp] ;close input file jmp short typ1 ;look for more typ4: ; no more call ss:dirfin[bp] ;finish up dir I/O cmp byte ptr ds:notfnd,0 ;find anything? jnz typ6 ret typ5: jmp dirio ;dir I/O err typ6: jmp fnf ;file not found ;+ ; ; Write to TTY for TYPE command. ; ; es:si string ; cx length (preserved on return) ; ;- wtty: jcxz wtty6 ;nothing (WLAST), skip push cx ;save length (we always succeed) mov di,ds:optr ;get output buf ptr mov dx,ds ;copy mov bx,es wtty1: mov ds,bx ;exchange for now mov es,dx wtty2: lodsb ;get a char cmp al,' ' ;ctrl char? jb wtty8 wtty3: cmp byte ptr cs:col,78d ;will this be penultimate col? je wtty7 ;yes wtty4: stosb ;save inc cs:col ;col+1 wtty5: loop wtty2 ;loop mov ds,dx ;restore mov es,bx mov ds:optr,di ;save pop cx ;restore # chars written wtty6: clc ;got it all ret wtty7: ; wrap while writing text char call wrap ;go handle wrap jmp short wtty4 ;continue wtty8: ; control character cmp al,cr ;ignore CR je wtty5 cmp al,lf ;EOL? je wtty12 cmp al,tab ;tab? je wtty10 push ax ;generic control char, save cmp byte ptr cs:col,78d ;should we wrap before "^"? jne wtty9 call wrap ;yes wtty9: mov al,'^' ;^ stosb inc cs:col ;col+1 pop ax ;restore char xor al,100 ;convert to printing char cmp al,'L' ;^L? jne wtty3 ; ^L causes **MORE** cmp byte ptr cs:col,78d ;will this be penultimate col? jne $+5 call wrap stosb ;save push bx ;save push cx push dx push si mov ds,dx ;restore seg call flush ;flush line pop si ;restore pop dx pop cx pop bx mov byte ptr ds:col,0 ;start over mov byte ptr ds:lnum,1 ;**MORE** on next line mov ds,bx jmp short wtty5 wtty10: ; tab push cx ;save xor ch,ch ;CH=0 mov al,' ' ;load blank mov ah,cs:col ;get col add ah,8d ;+8d and ah,not 7 ;back up to next tab stop cmp ah,80d ;off EOL? je wtty11 mov cl,ah ;copy col xchg ah,cs:col ;update sub cl,ah ;# cols to go rep stosb ;write them pop cx ;restore jmp wtty5 wtty11: mov ah,78d ;stop 2 before mov cl,ah ;copy xchg ah,cs:col ;update sub cl,ah ;# cols to go (may be 0) rep stosb pop cx ;restore call wrap ;wrap jmp wtty5 wtty12: ; LF push bx ;save push cx push dx push si mov ds,dx ;restore seg call flush ;flush line pop si ;restore pop dx pop cx pop bx mov byte ptr ds:col,0 ;start over mov ds,bx jmp wtty5 ; wrap: ; handle wrap in WTTY push ax ;save push bx push cx push dx push si mov al,'!' ;indicate wrap stosb mov ds,dx ;restore seg call flush ;flush line (resets DI) pop si ;restore pop dx pop cx pop bx pop ax mov byte ptr ds:col,0 ;back to 0 mov ds,bx ret ; if 0 costyp: ; type COS-310 file mov ax,ds:stblk ;starting block mov ds:blk,ax ;save mov ax,ds:fsize ;# blks mov ds:cnt,ax ;save xor cx,cx ;count=0 cost1: ; get next line call cosw ;get a word jc cost5 ;EOF mov bx,10000 ;abs(length) sub bx,ax and bx,7777 ;isolate (may have been 0) jz cost5 ;EOF (length must be gt 0) push bx ;save length ; print line # call cosw ;get line # ;; mov di,offset buf+4 ;pt at buf push cx ;save mov bx,10d ;base mov cx,4 ;loop count cost2: cwd ;DX=0 div bx ;/10 or dl,'0' ;cvt rem to ASCII dec di ;-1 mov [di],dl ;save char loop cost2 ;loop ; print the line itself pop cx ;restore count add di,4 ;advance again mov al,tab ;tab stosb ;save pop dx ;get WC back jmp short cost4 ;go count line #, return cost3: call cosw ;get next word mov bl,al ;save sal ax,1 ;left 2 sal ax,1 mov al,ah ;get left char call coschr ;save it mov al,bl ;restore call coschr ;save right char cost4: dec dx ;done all? jnz cost3 ;loop if not mov ax,cr+(lf*400) ;crlf stosw ;save it push cx ;save ;; mov dx,offset buf ;buf addr mov cx,di ;length sub cx,dx mov bx,1 ;STDOUT mov ah,40h ;func=write int 21h ;do it pop cx ;restore jmp short cost1 ;loop cost5: jz cost6 ;no i/o err clc ;no err cost6: ret ; cosw: ; get next word in COS-310 file, C=1 Z=1 if EOF, C=1 Z=0 if I/O err jcxz cosw2 ;skip cosw1: lodsw ;get a word dec cx ;-1 clc ;no prob ret cosw2: cmp ds:cnt,cx ;anything left to get? (CX=0) jz cosw3 ;no, fake it mov ax,ds:blk ;get blk # push dx ;save push di ;;; call rdsec ;read the sector pop di ;[restore] pop dx jc cosw4 ;bugged inc ds:blk ;block+1 dec ds:cnt ;count-1 jmp short cosw1 ;try again cosw3: xor ax,ax ;return 0 (Z=1) stc ;err ret cosw4: xor ax,ax ;return 0 or bh,1 ;Z=0 stc ;C=1 ret ; endif ; coschr: ; write COS-310 char in AL and al,77 ;isolate jz cosch1 ;NUL, don't even bother add al,' '-1 ;cvt to ASCII cmp al,'\' ;backslash? je cosch2 ;yes, special stosb ;save cosch1: ret cosch2: mov al,tab ;\ is tab stosb ret ;+ ; ; Print volume name. ; ;- vol: call gdevn ;parse device name, get dev rec in BP call ss:defset[bp] ;set defaults call ss:gdir[bp] ;get dir name (matters for RT-11) call confrm ;make sure confirmed call ss:dsrini[bp] ;init DSR call pvol ;print volume name (leave DI=LBUF) callr flush ;blank line ;+ ; ; Wipe out unused parts of disk (so Kermit, GZIP, PKZIP, etc. can win big). ; ;- wipe: call gdevn ;parse device name, get dev rec in bp callr ss:wipout[bp] ;wipe it out, return ; subttl device mount/init code ; comment _ Steps in preparing a volume for use: GMOUNT get name, switches OPNDEV open device (or CRTDEV for FORMAT) DEFMED iff medium type defaulted (guess from size or by reading floppy), not for FORMAT (which fills in medium type/size stuff itself) SETMED now that medium type is finalized (except possibly the ILIMG flag for RX50/52 image files), set all vectors and sizes for it DEFSYS iff OS defaulted, try to guess it, try interleaved and not for RX50/RX52 image files if guessing (MOUNT only) SETSYS now that OS type is finalized, set all vectors for it _ ;+ ; ; Parse MOUNT/FORMAT/INIT command args. ; ; [ldev:] [filespec] [/switches] ; ; bx 0 to parse logical dev name, -1 not to ; si,cx cmd line descriptor ; OTHSWT table of other switches (besides the medium/filesystem ones) ; ; Prints message and returns to MLOOP on error. ; Otherwise sets up TAREA and dev record as much as possible, returns ptr to ; dev record in SS:BP. ; ;- gmount: push cx ;save xor al,al ;load 0 mov di,offset tarea ;pt at temp area mov cx,ltarea ;length rep stosb ;clear it out pop cx ;restore mov ds:tdev,bx ;skip logical dev name for FORMAT/INIT cmds gmnt1: call skip ;skip blanks, tabs jc gmnt2 ;done, see if we have everything cmp al,ds:swchar ;switch? je gmnt3 ;handle it cmp ds:tdev,0 ;do we already have a logical device? jz gmnt10 ;no, this must be it cmp ds:tfnam,0 ;do we already have a filename? jnz gmnt9 ;yes, this is an error call getw ;get filename mov ds:tfnam,bx ;save ptr mov ds:tfnam+2,dx ;and length jmp short gmnt1 gmnt2: jmp gmnt11 gmnt3: ; handle switches inc si ;eat the / or whatever dec cx call getan ;get alphanumeric switch (stop at ':' etc.) jc gmnt9 mov ax,offset strtab ;pt at structure types call tbluk ;is it one of those? jc gmnt4 cmp ds:tfsys,0 ;was there already a file system type? jnz gmnt9 ;yes mov ds:tfsys,ax ;save this one jmp short gmnt1 gmnt4: mov ax,offset medtab ;pt at medium types call tbluk ;is it one of those? jc gmnt5 cmp ds:tmed,0 ;do we have one already? jnz gmnt9 ;yes mov ds:tmed,ax ;save this one jmp short gmnt1 gmnt5: mov ax,offset partab ;pt at partition stuff table call tbluk ;look it up jc gmnt6 call ax ;call the routine jmp short gmnt1 gmnt6: mov ax,offset mnttab ;point at other mount switches call tbluk ;look it up jc gmnt7 call ax ;call the routine jmp short gmnt1 gmnt7: mov ax,ds:othswt ;point at other switches test ax,ax ;anything? jz gmnt8 ;no call tbluk ;look it up jc gmnt8 call ax ;call the routine jmp short gmnt1 gmnt8: jmp badswt ;bad switch gmnt9: jmp synerr ;go complain gmnt10: xor ah,ah ;no colon needed call getlog ;get logical name jc gmnt9 mov ds:tdev,ax ;save mov ds:tdev+2,bx jmp gmnt1 gmnt11: ; end of line, check to see if this all made sense cmp ds:tdev,0 ;is there a logical name jnz gmnt12 ;yes cram '?Missing logical name',error gmnt12: mov ax,ds:tdev ;get dev name mov bx,ds:tdev+2 ;and flag,,unit ; if there's already a device with this name, dismount it mov bp,ds:logdev ;get head of list jmp short gmnt15 gmnt13: cmp ss:logd[bp],ax ;match? jne gmnt14 cmp ss:logu[bp],bx jne gmnt14 push ax ;save push bx mov al,ds:dsmunl ;get /[NO]UNLOAD default call dmnt ;dismount device pop bx ;restore pop ax jmp short gmnt16 ;go allocate it gmnt14: mov bp,ss:next[bp] ;get next gmnt15: test bp,bp ;is there one? jnz gmnt13 ;loop if so gmnt16: ; set up a record for it call getrec ;get a record mov ss:logd[bp],ax ;set dev name mov ss:logu[bp],bx ;and unit # mov ax,ds:tfsys ;get file system (if any) mov ss:fsys[bp],ax ;save mov ax,ds:tmed ;get medium type (if any) mov ss:med[bp],ax ;save ; interpret filename and/or switches cmp ds:tfnam,0 ;got image filename? jnz gmnt19 ;yes cmp byte ptr ds:tdrv+1,0 ;how about /DRIVE? jnz gmnt17 ;yes ; no filename or /DRIVE, use logical name if just one letter mov ax,ds:tdev ;get log dev name test al,al ;is log dev name just one letter? jnz gmnt18 ;no, bugged cmp byte ptr ds:tdev+3,0 ;was unit # given? jnz gmnt18 ;yes, bugged mov al,ah ;copy callr gdrvl ;handle drive letter gmnt17: callr gdrive ;handle drive gmnt18: cram '?Missing drive or file name',error gmnt19: callr image ;parse image filename (or drive or COM port) ; strtab label byte kw ,cos310 ;uses OS/8 format but text files are weird kw ,dosbat ;DOS/BATCH disk structure ;(DECtape is similar, but different) kw ,ods1 ;Files-11 ODS-1 kw ,frgn ;just blocks as far as we're concerned kw ,ods1 ;synonym for Files-11 kw ,ods1 ;synonym for Files-11 kw ,os8 ;OS/8, OS/78, OS/278 all use the same format kw ,os8 kw ,os8 kw ,ods1 ;synonym for Files-11 kw ,ps8 ;PS/8 kw ,putr ;TSS/8 PUTR format kw ,rsts ;RSTS Disk Structure kw ,rsts ;synonym kw ,ods1 ;synonym for Files-11 kw ,rt11 ;RT-11 format kw ,tss8 ;TSS/8.24 format kw ,xxdp ;XXDP new format db 0 ; medtab label byte kw <7-20KB>,rx24 ;here for backwards compatibility only kw ,mscp kw ,ra60 kw ,ra70 kw ,ra71 kw ,ra72 kw ,ra73 kw ,ra80 kw ,ra81 kw ,ra82 kw ,ra90 kw ,ra92 kw ,rd32 kw ,rd51 kw ,rd52 kw ,rd53 kw ,rd54 kw ,rk02 kw ,rk05 kw ,rk06 kw ,rk07 kw ,rl01 kw ,rl02 kw ,rm02 kw ,rm03 kw ,rm05 kw ,rm80 kw ,rp02 kw ,rp03 kw ,rp04 kw ,rp05 kw ,rp06 kw ,rp07 kw ,rs03 kw ,rs04 kw ,rs08 kw ,rx01 kw ,rx02 kw ,rx03 kw ,rx23 kw ,rx24 kw ,rx26 kw ,rx33 kw ,rx50 kw ,rx52 kw ,tu56 kw ,tu58 db 0 ; partab label byte kw ,mbpar ;BIOS partition #, 1-4 kw ,mdrv ;hard or floppy disk unit # kw ,mtype ;partition type (hex) db 0 ; mnttab label byte kw ,intlv ;interleaved image file kw ,noilv ;non-interleaved image file kw ,part ;RT-11 partition # kw ,ronly ;read-only access to device kw ,rdwrt ;read/write access to device db 0 ; mdrv: ; /DRIVE parm call eqnum ;get "=n" mov ds:tdrv,ax ;save ret ; mbpar: ; /BPARTITION parm call eqnum ;get "=n" test al,al ;make sure it's from 1 to 4 jz outra1 cmp al,4 ja outra1 mov ds:tbpart,ax ;save ret ; eqnum: ; parse "=n", return number +100h in AX (# must be .LE.255) call skip ;skip blanks, etc. jc mispr1 cmp al,':' ;: je eqnum1 cmp al,'=' ;or =, right? jne mispr1 eqnum1: inc si ;yes, eat it dec cx call getn ;get number test ah,ah ;not ridiculous? jnz outra1 inc ah ;=1 ret mispr1: jmp misprm ;missing parm outra1: jmp outran ;out of range ; mtype: ; /TYPE=hh call skip ;skip blanks, etc. jc mispr1 cmp al,'=' ;=, right? jne mispr1 inc si ;yes, eat it dec cx call geth ;get number test ah