title Peripheral Utility Transfer Routines ;++ ; ; File transfer program, knows many DEC formats. ; ; Copyright (C) 1995, 1996, 1997, 1998 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). ; ; This program has many loose ends. The RSTS support is read-only, and there's ; no trace of Files-11 support. I'd like to change the main loop (print a ; prompt) to handle an async .FORK queue during the KB wait loop so that ; support for Megan Gentry's RTEFTP protocol (the server side) can be added w/o ; skating on a lot of thin ice, but haven't yet. Also it would be nice to ; support ASPI tapes. ; ; TO DO LIST: ; ; Should handle bad block replacement with DL: and DM: RT-11 volumes. ; ; Several different cleanup concepts need to be sorted out: ; ; SQUEEZE should move empty blocks to end under RT-11 ; CLEAN should update SATT.SYS under RSTS ; something should combine contiguous empty blocks under RT-11 ; ; 09/18/90 JMBW Created (OS/278, COS310 read-only version). ; 03/22/94 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/94 JMBW Concept of "logged-in" device. ; 03/30/94 JMBW TYPE command works (DOS, OS/8, RT-11). ; 04/02/94 JMBW TU58/COM port driver added. ; 07/09/94 JMBW COPY command can write into RT-11 file system. ; 07/23/94 JMBW LD: disk files work like subdirectories. ; 07/30/94 JMBW Reads RSTS disks (RDS 0.0-1.2). ; 11/01/94 JMBW FORMAT creates image files, WIPEOUT added for RT-11. ; 01/10/95 JMBW V1.0 released. ; 01/29/95 JMBW Cached dir code for OS/8 (adapted from RT-11 code). ; 05/18/95 JMBW Writes and inits OS/8 disks. V1.1 released. ; 05/30/95 JMBW Exclude tracks 0, 78, 79 when initting OS/278 RX50s. ; 07/09/95 JMBW RX23, RX33 support (RX33 untested w/real media). ; 07/11/95 JMBW RX26, "RX52" support (RX26 untested). ; 08/05/96 JMBW Auto-sense image file type on MOUNT. ; 08/08/96 JMBW Indirect command files ("@ file"), PUTR.INI. ; 08/11/96 JMBW COPY/FILE/DEV, PC-style floppy disks and images. ; 08/24/96 JMBW RX26 support debugged (finally got a 2.88MB drive). ; 08/26/96 JMBW Maintains software TG43 signal for CompatiCard. V1.2. ; 10/24/96 JMBW Fixed problems with null OS/8 tentative files. V1.21. ; 07/24/97 JMBW Supports SCSI disks using ASPI driver. ; 07/27/97 JMBW INIT/SEGMENTS:n switch. ; 10/30/97 JMBW Fixed TU58 bug with COPY/FILE/DEV. V1.22. ; 11/24/97 JMBW MOUNT/PARTITION:n switch (for RT-11 DU:). V1.23. ; 12/01/97 JMBW Fixed TU58 baud rate parser. DISMOUNT/UNLOAD. V1.24. ; 12/02/97 JMBW SET DISMOUNT command. ; 04/11/98 JMBW Added RAxx series drive types. ; 04/12/98 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/98 JMBW Fixed math error in RTWDIR, got incorrect # of dir segs ; for disks with exactly 289. files. V1.26. ; 06/09/98 JMBW Fixed DXILV for OS/8 RX02s (never worked!). V1.27. ; 06/11/98 JMBW I'm an idiot. Now split into DXILV/DYILV. V1.28. ; ; FDCs that work in single density mode: ; SMC FDC37C65+ (non-+ version suspected to work too) ; Goldstar GM82C765B ; ;-- .radix 8 ;por supuesto ; ; include sym.inc ;(symbols for SYMDEB's benefit, this line not needed) ; 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) ; ; 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 &nam= byte ptr _ ifnb <&num> _= _+&num else _= _+1 endif endm ; ; Define a word field in a record (_ is current offset). ; Num is # words, default is 1. defw macro nam,num &nam= word ptr _ ifnb <&num> _= _+(&num*2) else _= _+2 endif 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) ostype cos310,twelv ;COS-310 ;;; ostype ods1,sixtn ;RSX-11, IAS, early VAX/VMS ostype os8,twelv ;OS/8, OS/78, OS/278 ;;; 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, COPY.SAV 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 rk02 ;RK11/RK02 (1.2 MB) token rk05 ;RK11/RK05 (2.5 MB) token rk06 ;RK611/RK06 (14 MB) token rk07 ;RK611/RK07 (28 MB) token rl01 ;RL11/RL01 (5 MB) token rl02 ;RL11/RL02 (10 MB) token rs03 ;RH11/RS03 (512 KB) token rs04 ;RH11/RS04 (1.0 MB) 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 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 ;floppy disk (DU:, DX:, DY:, DZ:) token hwpart ;hard disk partition token hwtu58 ;TU58 attached to serial port (DD:) token hwaspi ;ASPI SCSI device token hweftp ;Megan Gentry's RTEFTP protocol via packet driver ; ; 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 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 defw totsiz,4 ;total dev size in bytes (including trk 0 etc.) defw totblk,4 ;total dev size in blocks (including DEC166) defw devsiz,4 ;device size in usable 512-byte blocks defw actsiz,2 ;active device size (RT-11) defb initf ;init flag, cleared in all devs at each prompt defb ecache ;NZ => enable dir cache (RSTS) defb tapflg ;NZ => tape (otherwise disk) ; filesystem-dependent data defw pcs ;pack clustersize defw dcs ;dev clustersize defw curdir,2 ;.WORD current ppn (RSTS) ;or .LONG starting blk # (RT-11) defw cursiz,2 ;current LD: size (RT-11) defw actdir,2 ;active dir (during search, usually = CURDIR) defw mfddcn ;DCN of first cluster of MFD (RSTS) defw satptr ;DCN of last cluster allocated (RSTS) 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) ; 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.) reclen= _ ; ; 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 ; ; 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 buffer format: ; _=1000 ;512. bytes of data first defw rdsbkl ;abs 24-bit block number (LSW) (or 0 if not used yet) defb rdsbkh ; " " " (MSB) 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 ; start: cld ;DF=0 ; 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 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 $+3 inc bx ;yes, pretend there are two 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 numfd2 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 numfd2: ; 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 $+4 mov al,25d-1 ;no, all pre-EGA boards had 25d lines only 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 64KB 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 ; look for PUTR.INI mov dx,offset inifil ;point at init file name mov ax,3D00h ;func=open /RONLY int 21h jnc infl9 ;got it ; check executable's directory if DOS 3.0 or later mov ah,30h ;func=get DOS version int 21h cmp al,3 ;3.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 mov ax,3D00h ;func=open /RONLY int 21h 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 V1.28 Copyright (C) 1995-1998 by John Wilson ' mov byte ptr ds:lnum,10d ;don't **MORE** call flush cram 'All rights reserved' 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: ; print prompt mov di,offset lbuf ;pt at buffer mov al,'(' ;put in parentheses stosb test byte ptr ds:nprmpt,-1 ;no prompt? jnz mlp9 ;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 mlp10 ;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 mlp11 mlp9: mov si,offset badprm ;prompt mov cx,13d ;length rep movsb ;copy jmp short mlp12 ;continue mlp10: mov al,dl ;copy mov ah,':' ;add colon stosw xor cx,cx ;no trailing \ call dosddl ;display dir for drive in DL mlp11: dec byte ptr ds:nprmpt ;success, clear flag mlp12: 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 mlp13: 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 mlp13 ;whoops, we passed midnight pop cx ;restore call skip ;skip blanks etc. jc mlp14 ;blank line, reprompt cmp al,'@' ;open indirect file? je mlp15 cmp al,';' ;comment? je mlp14 ;ignore line if so cmp al,'!' ;either way je mlp14 call getw ;get a word jc mlp14 ;just got a separator, reprompt mov ax,offset cmdtab ;pt at command table call tbluk ;look up jc mlp19 call ax ;call the routine mlp14: jmp mloop ;loop mlp15: ; 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 mlp16 mov ah,3Eh ;yes, func=close int 21h mlp16: call getw ;get filename jc mlp17 ;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) mov ax,3D00h ;func=open /RONLY int 21h jc mlp18 mov ds:indhnd,ax ;save handle mov ds:indctr,0 ;buffer needs to be loaded jmp short mlp14 ;back into loop mlp17: jmp misprm mlp18: jmp fnf mlp19: ; 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 mlp26 ;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 mlp22 mlp20: cmp ss:logd[bp],ax ;is this it? jne mlp21 cmp ss:logu[bp],bx je mlp24 ;yes mlp21: mov dx,bp ;no, save ptr mov bp,ss:next[bp] ;follow link mlp22: test bp,bp ;is there more? jnz mlp20 ;loop if so ; not on list, see if it could be a DOS disk or bh,al ;2nd letter or unit specified? jnz mlp26 ;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 mlp23 ;nope dec dx ;numbers start at 0 for login mov ah,0Eh ;func=log in int 21h jc mlp23 ;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) mlp23: jmp baddrv ;no such drive mlp24: ; 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 mlp25 ;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 mlp25: mov byte ptr ds:nprmpt,0 ;assume we can prompt jmp mloop ;reprompt (flush stack) mlp26: ; 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 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 ;;; 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 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 mov byte ptr ds:binswt,0 ;no /BINARY or /ASCII yet mov byte ptr ds:devflg,0 ;no /DEV yet mov byte ptr ds:ovride,0 ;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 call ss:gdir[bp] ;get dir name mov di,offset fname1 ;pt at buffer xor bl,bl ;no implied wildcards call filnam ;parse 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 call ss:gdir[bp] ;get dir name mov di,offset fname3 ;pt at buffer xor bl,bl ;no implied wildcards call filnam ;parse 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] 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? jz $+5 ;yes jmp fnf ;file not found ret copy7: jmp dirio ;dir I/O err ;+ ; ; 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 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 ,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 ; copbin: ; /BINARY mov ds:binflg,bin ;set binary mode mov byte ptr ds:binswt,-1 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. ; ; 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 dx,ss:devsiz+2[bp] mov ds:isize,ax ;save mov ds:isize+2,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 ; 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 ; 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) 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 >64KB) ; 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 test ds:isize+2,-1 ;check high word jz ird1 ;zero, low word is right mov bx,-1 ;say 64K 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 pop di ;[restore] pop ax jc ird7 ;read error ird3: add ds:iblk,ax ;success, update block adc ds:iblk+2,0 sub ds:isize,ax ;update size sbb ds:isize+2,0 ;CF=0 (already checked) 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 dx,ds:iblk+2 call pdnum 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 mov ds:flpvec,offset secxrd ;point at read routine call secxfr ;read a track, or partial track if no space add ds:iblk,cx ;update block # adc ds:iblk+2,0 sub ds:isize,cx ;remove from count sbb ds:isize+2,0 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 64KB? jz $+5 mov cx,-1 ;stop just short les di,dword ptr ds:bigptr ;get ptr mov ax,cx ;copy add ax,di ;will this wrap around? jnc fcpy3 ;no mov cx,di ;copy not cx ;# bytes until EOB-1 (so ADD won't overflow) fcpy3: call ds:aread ;read as much as we can jc fcpy4 ;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 fcpy4: jnz fcpy5 ;skip inc ds:eof ;set EOF flag fcpy5: ; 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 fcpy6: ; do next write mov cx,ds:bigctr ;get # bytes left cmp ds:bigctr+2,0 ;is it more than 64KB? jz $+5 mov cx,-1 ;stop just short jcxz fcpy8 ;EOF les si,dword ptr ds:bigptr ;get ptr mov ax,cx ;copy add ax,si ;will this wrap around? jnc fcpy7 ;no mov cx,si ;copy not cx ;# bytes until EOB-1 fcpy7: call ds:awrite ;write as much as we can jcxz fcpy8 ;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 fcpy6 ;write more fcpy8: ; copy what's left back to begn of buffer so we can read more ; (assume all clustersizes<(64KB-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 fcpy9 ;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 fcpy9: ; 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 call ss:gdir[bp] ;get dir name mov di,offset fname1 ;pt at buffer xor bl,bl ;no implied wildcards call filnam ;parse 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? jz $+5 ;yes jmp fnf ;file not found ret del5: jmp dirio ;dir I/O err ;+ ; ; 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 call ss:gdir[bp] ;get dir name mov di,offset fname1 ;pt at buffer mov bl,1 ;implied wildcards call filnam ;parse 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 cx,1 ;filename follows directory name call ss:dirnam[bp] ;print dir name mov si,offset fname1 ;pt at buffer cmp byte ptr [si],0 ;is there a wildcard? jnz dir5 ;yes, good mov si,offset wldcrd ;no, use "*.*" dir5: lodsb ;get a byte test al,al ;end? jz dir6 stosb ;store jmp short dir5 dir6: 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 dir7: ; process next file call ss:dirget[bp] ;get next entry jc dir10 ;end of list jnz dir8 ;not .EMPTY. ; empty area cmp byte ptr ds:fname1,0 ;do we have a filename? jnz dir7 ;yes, skip empties call ss:diremp[bp] ;display it call flush ;flush it jmp short dir7 dir8: cmp byte ptr ds:fname1,0 ;do we have a wildcard? jz dir9 ;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 dir7 ;no, skip dir9: mov byte ptr ds:notfnd,0 ;found one call ss:dirdis[bp] ;display file ; end of line call flush ;flush it jmp short dir7 ;around for more dir10: call ss:dirfin[bp] ;finish up cmp byte ptr ds:notfnd,0 ;find anything? jz dir11 call fnf ;no dir11: call ss:dirsum[bp] ;print summary 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 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 ;;; cmp ss:fsys[bp],0 ;doesn't work for native devices ;;; je ... call getn ;get a number mov cx,1 ;read 1 block mov di,offset buf ;pt at temp buf call ss:rdblk[bp] ;get the block jnc dump1 jmp rderr dump1: xor ax,ax ;offset dump2: ; 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 mov cx,8d ;loop count dump3: ; dump bytes in octal push cx ;save lodsb ;get a word mov cx,3 ;digit count call poct ;write it mov al,' ' ;blank stosb pop cx ;restore loop dump3 ;loop stosb ;add another blank sub si,8d ;back to begn mov cl,8d ;reload counter dump4: ; dump ASCII bytes lodsb ;get a byte and al,177 ;trim to 7 bits cmp al,' ' ;printing char? jb dump5 cmp al,177 jb dump6 dump5: mov al,'.' ;no, change to dot dump6: stosb ;save loop dump4 ;loop push si ;save call flush ;flush line pop si ;restore pop ax add ax,10 ;skip to next 8. bytes cmp si,offset buf+512d ;done all of block? jne dump2 ;loop ret ;+ ; ; 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 getn ;read number and ax,not 777 ;trim to 512 bytes mov ss:totsiz[bp],ax ;save mov ss:totsiz+2[bp],dx 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? jne fmt6 test ss:med[bp],-1 ;yes, medium type specified? jnz fmt6 mov bl,ss:drive[bp] ;get drive # xor bh,bh mov al,ds:fdtype[bx] ;look up drive type test al,al ;known? jz fmt4 ;no, assume they have at least some plan cmp al,rx33 ;1.2MB drive? jne fmt5 ;no fmt4: mov al,rx50 ;otherwise default to RX50 fmt5: xor ah,ah ;AH=0 mov ss:med[bp],ax ;save fmt6: call setmed ;set medium-related stuff call setsys ;set file system related stuff fmt7: ; 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 fmt8 ;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 fmt8 call ax ;init fmt8: cmp ss:hwtype[bp],hwflop ;floppy drive? jne fmt10 fmt9: ; prompt to format more if floppy cram 'Format more disks (Y/N)? ',wrng call gtlin ;read a line call getw ;get their answer jc fmt9 ;if any cmp byte ptr [bx],'Y' ;does it start with 'Y'? je fmt7 fmt10: ret ; _=0 fmthw label word disp hwfile,fmtimg ;format image file disp hwflop,fmtflp ;format 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, >64KB 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 64KB-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 call ss:gdir[bp] ;get dir name mov di,offset fname1 ;pt at buffer xor bl,bl ;no implied wildcards call filnam ;parse 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, >64KB 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 ;<64KB 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 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 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) cmp cx,22d*512d ;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, 18-bit version is all ones rep stosw push ds ;save push es ;copy ES to DS pop ds mov cx,21d*512d/2 ;# words to copy (21. more blocks) rep movsw 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 or COS-310? 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 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 cmp ss:devsiz+2[bp],0 ;make sure size .lt. 64K blocks jnz zos7 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 xor dx,dx mov cx,1 ;write 1 block 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. doesn't match table below! ; RX02 DY 4 988 ; RF11 RF 4 ; RS03/4 DS 4 1024/2048 ; RK05 RK 16 4800 ; RL01 DL 16 10240/20480 ; RK07 DM 31 x/53790 ; RP03 DP 31 ; ; 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, /RSTS, default is /RT11) ; /MEDIUM is the medium type (for floppy drives, default is /RX50) ; ; 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 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 mov ax,ds:fremem ;make sure we don't flush the dev rec's RAM mov ds:kepmem,ax 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 os8,mntos ;OS/8 dw rsts,mntrs ;RSTS dw rt11,mntrt ;RT11 dw 0 ; mntos: ; mount OS/8 (or COS-310) file system ret ;no special processing ; mntrs: ; mount RSTS file system ; calculate device cluster size mov ax,1 ;assume DCS=1 mov cx,ss:devsiz+2[bp] ;get high order of dev size jcxz mntrs2 ;DCS is indeed 1 mntrs1: ; see how many times we have to divide the dev size by 2 to make ; the high order word of the size go away sal ax,1 ;left a bit shr cx,1 ;right a bit jnz mntrs1 ;loop mntrs2: cmp ax,64d ;<=64. right? jbe mntrs3 cram '?Invalid DCS',error mntrs3: 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 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 mntrs4 ;nope cmp ax,64d ;.LE.64? ja mntrs4 call pbyte ;yes, 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 mntrs5 ; RDS 0.0 xor ax,ax ;RDS 0.0 jmp short mntrs6 mntrs4: cram '?Invalid PCS',error mntrs5: ; RDS 1.X mov ax,[si+4] ;DCN of MFD mov ss:mfddcn[bp],ax ;save mov ax,[si+6] ;get RDS level mntrs6: ; 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 ss:curdir[bp],1 ;ppn=[0,1] mov ss:satptr[bp],1 ;init SATT.SYS cluster alloc ptr ;;; need to look up [0,1]SATT.SYS ;;; also save # of PC's on disk for GETCLU wraparound ret ; mntrt: ; mount RT11 file system call rtpart ;set up partition jnc mntrt1 ;success cram '?Nonexistent partition',error mntrt1: ret ; 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 ;+ ; ; 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 ,stcopy ;set ASCII/BINARY default for COPY kw ,setdsm ;set DISMOUNT /[NO]UNLOAD default kw ,setfdc ;set FDC type kw ,stmore ;enable/disable **MORE** processing db 0 ; 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 gdevn ;get dev name (or use default) call confrm ;check for EOL 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 show1 ;no cram 'DOS' ;DOS device, no useful information callr flush show1: cmp ss:hwtype[bp],hwfile ;image file? jne show2 ;no test byte ptr ss:ilimg[bp],-1 ;yes, interleaved? jz show2 cram 'Interleaved ' ;yes show2: 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 dx,ss:devsiz+2[bp] push ax ;(save) push dx call pdnum ;print it cram ' total usable block' pop dx ;(restore) pop ax dec ax ;size=1? jnz show3 cmp dx,ax ;(AX now =0) jz show4 show3: mov al,'s' ;add 's' if not stosb show4: ; add current partition/LD: size for RT-11 cmp ss:fsys[bp],rt11 ;RT? jne show7 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 show5 cmp dx,ax ;(AX now =0) jz show6 show5: mov al,'s' ;add 's' if not stosb show6: cram ' in current volume' show7: callr flush ;+ ; ; TYPE {wildcard} ; ;- typ: ; get filespec call gdev ;parse device name, get dev rec in bp call ss:defset[bp] ;set defaults call ss:gdir[bp] ;get dir name mov di,offset fname1 ;pt at buffer xor bl,bl ;no implied wildcards call filnam ;parse 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? jz $+5 ;yes jmp fnf ;file not found ret typ5: jmp dirio ;dir I/O err ;+ ; ; 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 $+3 ;no i/o err clc ;no err 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 ;null, 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, COMPRESS, etc. can win big). ; ;- wipe: call gdevn ;parse device name, get dev rec in bp callr ss:wipout[bp] ;wipe it out ; 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: kw ,cos310 ;uses OS/8 format but text files are weird kw ,frgn ;just blocks as far as we're concerned kw ,os8 ;OS/8, OS/78, OS/278 all use the same format kw ,os8 kw ,os8 kw ,rsts ;RSTS Disk Structure kw ,rsts ;synonym kw ,rt11 ;RT-11 format db 0 ; medtab: 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 ,rk02 kw ,rk05 kw ,rk06 kw ,rk07 kw ,rl01 kw ,rl02 kw ,rs03 kw ,rs04 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: kw ,mbpar ;BIOS partition #, 1-4 kw ,mdrv ;hard or floppy disk unit # kw ,mtype ;partition type (hex) db 0 ; mnttab: 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,ah ;out of range? jnz outra1 inc ah ;=1 mov ds:ttype,ax ;save ret ; intlv: ; /INTERLEAVE mov ds:tintlv,101h ;high byte set, low byte=flag ret ; noilv: ; /NOINTERLEAVE mov ds:tintlv,100h ;high byte set, low byte=flag ret ; part: ; /PARTITION=n call skip ;skip blanks, etc. jc part2 cmp al,':' ;: je part1 cmp al,'=' ;or =, right? jne part2 part1: inc si ;yes, eat it dec cx call getn ;get number mov ds:tpart,ax ;save ret part2: jmp misprm ;missing parm ; ronly: ; /RONLY mov ds:tronly,101h ;high byte set, low byte=RO flag ret ; rdwrt: ; /RW mov ds:tronly,100h ;high byte set, low byte=RO flag ret ;+ ; ; Decode image filename from GMOUNT. ; ; ss:bp log dev rec ; ;- image: ; filename given, see if it's just a drive name mov si,ds:tfnam ;get ptr to name mov bx,ds:tfnam+2 ;length of name mov ah,[si+bx-1] ;get last char cmp bx,2 ;right length for "x:"? jne imag1 cmp ah,':' ;ends in colon? jne imag1 mov al,[si] ;get drive letter cmp al,'A' ;yes, is it a letter? jb imag1 cmp al,'Z' ja imag1 ; drive letter mov ds:tfnam,0 ;forget filename jmp gdrvl ;go handle it imag1: ; not a drive letter, see if SCSIht_l: cmp bx,5 ;at least long enough for "SCSI:"? jb imag2 ;no cmp ah,':' ;ends in colon? jne imag2 cmp word ptr [si],"CS" ;SCSI? jne imag2 cmp word ptr [si+2],"IS" jne imag2 ;no jmp gscsi ;handle it imag2: ; not SCSI, maybe then it's a COM port cmp bx,5 ;long enough for "COMn:"? jb imag4 cmp byte ptr [si+4],':' ;colon in the right place? jne imag4 cmp word ptr [si],"OC" ;COM? jne imag4 cmp byte ptr [si+2],'M' jne imag4 ;no mov al,[si+3] ;get unit # sub al,'1' ;subtract base cmp al,4 ;valid? jae imag4 ;no jmp getcom ;get COM port imag3: jmp swtcon imag4: ; it's a file, make sure no /DRIVE, /BPART, /TYPE mov ss:hwtype[bp],hwfile ;set type mov al,byte ptr ds:tdrv+1 ;see if any set or al,byte ptr ds:tbpart+1 or al,byte ptr ds:ttype+1 jnz imag3 ;switch conflict if so ; set default ptrs to block I/O routines (for linear block image) mov ss:rdblk[bp],offset rdfil ;read from file mov ss:wrblk[bp],offset wrfil ;write to file imag5: mov ss:dsrini[bp],offset cret ;no DSR init ret ;+ ; ; Parse COM port parameters for actual TU58 drive. ; ; bp log dev rec ; al unit # (0-3) ; si pts at 'COMn:bbbbb' string ; ;- getcom: ; it's a COM port, see if it exists add al,al ;*2 push ds ;save xor bx,bx ;point at BIOS data mov ds,bx mov bh,4 ;RS232_BASE=400h mov bl,al ;index mov cx,[bx] ;get base port # pop ds ;restore jcxz gcom2 ;no such port mov ss:port[bp],cx ;save mov ax,tu58 ;default medium is TU58 cmp ax,ss:med[bp] ;is that it? je gcom5 cmp ss:med[bp],0 ;is it something else? jz gcom4 ;no, good gcom1: jmp swtcon gcom2: cram '?No such port',error gcom3: cram '?Invalid baud rate',error gcom4: mov ss:med[bp],ax ;set medium type if defaulted gcom5: ; it does, get baud rate if given mov bx,3 ;baud rate divisor for 38.4kbps (default) mov cx,ds:tfnam+2 ;get length sub cx,5 ;subtract what we ate jz gcom6 ;that's all there is add si,5 ;skip past colon call getn ;parse baud rate test cx,cx ;that's everything, right? jnz gcom3 mov bx,2 ;min baud rate cmp ax,2 ;will their baud rate cause overflow? jb $+4 mov bx,ax ;no, use it mov ax,0C200h ;divisor is 1C200h/(baud rate) mov dx,1 div bx mov bx,ax ;copy gcom6: ; BX=baud rate divisor, init port for programmed I/O mov dx,ss:port[bp] ;get base port # add dx,3 ;line ctrl reg mov al,203 ;DLAB=1 out dx,al sub dx,3 ;baud rate divisor mov al,bl ;write low byte out dx,al inc dx ;+1 mov al,bh ;write high byte out dx,al inc dx ;+2 inc dx mov al,3 ;8 data bits, 1 stop, no parity out dx,al dec dx ;-2 dec dx xor al,al ;disable all ints out dx,al add dx,3 ;modem ctrl reg mov al,3 ;set RTS, DTR out dx,al ; make sure switches are consistant for TU58 mov al,byte ptr ds:tdrv ;get drive # (default=0) cmp al,1 ;0 or 1, right? ja gcom7 mov ss:drive[bp],al ;save mov al,byte ptr ds:tbpart+1 ;make sure nothing extraneous set or al,byte ptr ds:ttype+1 jnz gcom8 ;switch conflict if so mov ss:dsrini[bp],offset ddinit mov ss:rdblk[bp],offset rddd mov ss:wrblk[bp],offset wrdd mov ss:devsiz[bp],512d ;# blks mov ss:totblk[bp],512d ;total is same mov ss:totsiz+2[bp],4 ;total in bytes=2^18. mov ss:hwtype[bp],hwtu58 ;it's a real TU58 drive ret gcom7: jmp baddrv ;invalid drive # gcom8: jmp swtcon ;switch conflict ;+ ; ; Decode hard drive/floppy information. ; ; ss:bp logical dev record ; al drive letter ; ;- gdrvl: cmp byte ptr ds:tdrv+1,0 ;/DRIVE already given? jnz gdrvl1 ;switch conflict if so mov bl,byte ptr ds:tbpart+1 ;see if /BPART or /TYPE given or bl,byte ptr ds:ttype+1 ;(required for hard drives) sub al,'A' ;convert to phys unit # cmp al,ds:firhd ;floppy or hard? jb gdrvl2 ;floppy if 0 jmp short gdrvl2 ;;;; %out temp fix for 8" C: endif sub al,ds:firhd ;hard, subtract base test bl,bl ;they gave /BPART and/or /TYPE right? jnz gdrvl3 ;yes cram '?Partition not specified',error gdrvl1: jmp swtcon ;switch conflict gdrvl2: test bl,bl ;floppy, partition must not be specified jnz gdrvl1 ;switch conflict gdrvl3: mov ah,1 ;set "valid" flag mov ds:tdrv,ax ;save gdrive: ; enter here with DS:TDRV set up mov bl,byte ptr ds:tbpart+1 ;check for /BPART or /TYPE (again) or bl,byte ptr ds:ttype+1 ;(to see if hard or floppy) jnz gdrv2 ;hard, skip ; floppy cmp al,ds:firhd ;floppy, in range? jae gdrv1 ;no if 0 %out temp fix for 8" C: ;;; (comment out the JAE GDRV1 above) endif mov ss:hwtype[bp],hwflop ;floppy drive mov ss:drive[bp],al ;save unit # ret gdrv1: jmp baddrv ;no such drive gdrv2: ; hard disk partition cmp al,ds:numhd ;in range? jae gdrv1 ;punt if not or al,80h ;set "HD" bit mov ss:drive[bp],al ;save ; read partition table mov cx,5 ;retry count gdrv3: push cx ;save mov dl,byte ptr ds:tdrv ;get drive # xor dh,dh ;head=0 mov cx,1 ;track 0, sector 1 mov bx,ds:dbuf ;pt at buffer mov ax,0201h ;func=read 1 sector int 13h jnc gdrv4 xor ah,ah ;func=reset int 13h pop cx ;restore retry count loop gdrv3 cram '?Error reading partition table',error gdrv4: ; search partition table for our partition pop cx ;flush count mov cl,4 ;loop count (CH=0 from above) xor dx,dx ;load 0 xchg dx,ds:tbpart ;get flag,,partition, zero it mov ax,ds:ttype ;and flag,,type add bx,1BEh ;pt at first table slot gdrv5: cmp byte ptr [bx+4],0 ;is this slot empty? jz gdrv7 ;yes, skip it inc byte ptr ds:tbpart ;actual partition +1 test dh,dh ;are we going by partition #? jz gdrv6 dec dl ;yes, see if we're there jz gdrv8 ;we are, check type if we have it jmp short gdrv7 ;go check next slot gdrv6: cmp al,[bx+4] ;is this the type byte we're looking for? je gdrv9 ;we're done if so gdrv7: add bx,10h ;skip to next slot loop gdrv5 ;loop through all cram '?No such partition',error gdrv8: test ah,ah ;found partition, can we double-check type? jz gdrv9 ;no, we're satisfied cmp al,[bx+4] ;yes, does it match? je gdrv9 ;yes cram '?Partition type mismatch',error gdrv9: ; DS:BX points to partition table entry mov dh,[bx+1] ;get head mov cx,[bx+2] ;cyl, sector mov al,[bx+4] ;type ;;; put them somewhere mov ss:hwtype[bp],hwpart ;type=H.D. partition ret ;+ ; ; Actually open the device or container file found by GMOUNT. ; ; ss:bp logical device record ; ;- opndev: mov bx,ss:hwtype[bp] ;get hardware type callr ds:opnhw[bx] ;dispatch ; _=0 opnhw label word disp hwfile,opnimg ;open image file disp hwflop,cret ;open floppy (already done) disp hwpart,opnhd ;open hard disk partition disp hwtu58,cret ;open TU58 (already done) disp hwaspi,cret ;open ASPI device (already done) ; opnimg: ; open image file mov ss:dsrini[bp],offset cret ;no DSR init mov ss:rdblk[bp],offset rdfil ;read from file mov ss:wrblk[bp],offset wrfil ;write to file mov si,ds:tfnam ;get ptr to filename mov cx,ds:tfnam+2 ;get length mov di,offset dotdsk ;default extension call defext ;apply it if needed mov ax,3D00h ;func=open /RO test byte ptr ds:tronly,-1 ;did they say /RO? jnz opni1 mov al,02h ;no, change it to open /RW opni1: int 21h jnc opni2 ;skip if OK jmp fnf ;file not found opni2: mov ss:handle[bp],ax ;save handle mov ds:outhnd,ax ;make sure it gets closed if we abort xor dx,dx ;set offset=0000:0000 xor cx,cx mov bx,ax ;copy handle mov ax,4202h ;func=lseek from EOF int 21h mov ss:totsiz[bp],ax ;save total size mov ss:totsiz+2[bp],dx if 1 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 endif ret ; opnhd: ; one of these days ;;; ret ;+ ; ; Same as above, but creates file for FORMAT. ; ; ss:bp log dev rec ; ;- crtdev: mov bx,ss:hwtype[bp] ;get hardware type callr ds:crthw[bx] ;dispatch ; _=0 crthw label word disp hwfile,crtimg ;create image file disp hwflop,cret ;create floppy (nothing to do) disp hwpart,crthd ;create hard disk partition disp hwtu58,cret ;create TU58 (nothing to do) ; crtimg: ; create image file test byte ptr ds:tronly,-1 ;did they say /RO? jnz crti2 ;pinhead call aysure ;make sure they're sure ;;; maybe should ask only if the file exists already? mov ss:dsrini[bp],offset cret ;no DSR init mov ss:rdblk[bp],offset rdfil ;read from file mov ss:wrblk[bp],offset wrfil ;write to file mov si,ds:tfnam ;get ptr to filename mov cx,ds:tfnam+2 ;get length mov di,offset dotdsk ;default extension call defext ;apply it if needed xor cx,cx ;mode=default mov ah,3Ch ;func=create int 21h jnc crti1 ;skip if OK jmp crerr ;file creation error crti1: mov ss:handle[bp],ax ;save handle mov ds:outhnd,ax ;make sure it gets closed if we abort ret crti2: jmp rodev ;can't format a read-only device ; crthd: ; one of these days ret ;+ ; ; Figure out default medium type (from size or by reading if floppy). ; ; Note that we can't tell an interleaved RX50 (or RX52) image file from a ; non-interleaved one by the size alone (it's 400KB either way), so we'll hold ; off setting ILIMG until DEFSYS for RX50/52s. ; ; ss:bp logical device record ; ;- defmed: cmp ss:hwtype[bp],hwfile ;image file? jne dmed8 ;no ; look up file type on size (take the biggest that fits) mov ax,ss:totsiz[bp] ;get total image file size mov dx,ss:totsiz+2[bp] mov bx,offset fsztab ;pt at table xor si,si ;nothing found yet dmed1: cmp dx,ds:flsiz+2[bx] ;too small? jb dmed4 ja dmed2 ;definitely fits cmp ax,ds:flsiz[bx] ;check low word jb dmed4 ;too small dmed2: ; if medium type already known, we're just checking for interleave ; (filter out other medium types because file may be bigger than ; necessary w/o hurting anything, that shouldn't confuse us) mov cx,ss:med[bp] ;get current medium type jcxz dmed3 ;none, great cmp ds:flmed[bx],cx ;yes, is this the right dev type? jne dmed4 ;skip it if not mov ax,ds:tintlv ;get /[NO]INTERLEAVE flag test ah,ah ;did they specify interleave or not? jz dmed3 ;no, just take it cmp al,ds:flilv[bx] ;does it match? jne dmed4 ;no dmed3: mov si,bx ;save a copy dmed4: add bx,fllen ;skip to next entry cmp bx,offset fszend ;off end? (including catch-all) jb dmed1 ;loop if not test si,si ;get anything? jz dmed6 ;no, file too small mov al,ds:flilv[si] ;get interleave flag mov ss:ilimg[bp],al ;save (may already match) mov ax,ds:flmed[si] ;get medium type mov ss:med[bp],ax ;set it, if not already set dmed5: ret dmed6: ; didn't find anything <= this dev's size cmp ss:med[bp],mscp ;did they already say /MSCP? je dmed5 ;yes test ss:med[bp],-1 ;did they say anything? jnz dmed7 ;yes, guess the size was just wrong mov ss:med[bp],mscp ;assume MSCP if nothing else fits dmed7: ret dmed8: ; not an image file, see if floppy cmp ss:hwtype[bp],hwflop ;floppy disk? jne dmed14 test ss:med[bp],-1 ;medium specified? jnz dmed13 ;yes ; try reading a sector a few different ways to guess disk type mov si,offset fdtyps ;point at table dmed9: lodsw ;get a word test ax,ax ;end of table? jz dmed18 ;too bad push si ;save call ax ;set up FDC for test pop si lodsw ;get table addr mov bx,ax ;copy test bx,bx ;does it exist? jnz dmed10 lodsw ;no, get type (this is a leaf on the tree) dmed10: push ax ;save both (AX is junk if BX is non-zero) push bx lodsb ;get cyl mov ch,al lodsb ;head mov dh,al lodsb ;sector mov cl,al push si ;save test cl,cl ;read specific sector, or just read header? js dmed11 ;header call fdrds ;try to read jmp short dmed12 dmed11: call fdrid ;read a header jc dmed12 ;failed mov al,[si+3] ;get N (SI points to C,H,R,N) cmp ds:fdlen,al ;match? je dmed12 ;yes stc ;say error dmed12: pop si ;[restore] pop bx pop ax jc dmed9 ;loop if failed mov si,bx ;follow branch test si,si ;or was it a leaf? jnz dmed9 ;branch, follow it mov ss:med[bp],ax ;leaf, we got it dmed13: ret dmed14: cmp ss:hwtype[bp],hwaspi ;ASPI disk? jne dmed15 ;no mov ax,mscp ;default=MSCP jmp short dmed16 dmed15: ; not a floppy, see if TU58 cmp ss:hwtype[bp],hwtu58 ;TU58? jne dmed17 mov ax,tu58 ;default=TU58 (the only sensible thing) dmed16: ; set default type from AX if none specified test ss:med[bp],-1 ;medium specified? jnz dmed17 ;yes mov ss:med[bp],ax ;use default dmed17: ret dmed18: cram '?Unable to detect disk type',error ; ; Table of file sizes and device geometry. Used by DEFMED to guess the medium ; type, and by SETMED to set up all the lengths once the medium type is known. ; This table is sorted in order of increasing image file size, so that DEFMED ; can use the latest entry <= the current image file to determine the image ; type. ; ; "2BSD" means geometry was supplied or supplemented by /etc/disktab entry ; from 2BSD UNIX (in cases where DEC docs were lacking or wrong). ; fsztab label byte filsiz rx01,252928d,252928d,,77d,1,26d,128d,1 ;252928. interleaved filsiz rx01,,252928d,,77d,1,26d,128d,0 ;256256. non-interleaved filsiz tu58,,,,1,1,512d,512d,0 ;TU58 256 KB filsiz tu56,,,,1,1,578d,512d,0 ;TU56 289 KB filsiz rx50,,,,80d,1,10d,512d,1 ;RX50 400 KB (interleaved or not) filsiz rx50,,,,80d,1,10d,512d,0 ;(2nd so ILIMG is cleared for DEFSYS) filsiz rx02,505856d,505856d,,77d,1,26d,256d,1 ;505856. interleaved filsiz rx02,,505856d,,77d,1,26d,256d,0 ;512512. non-interleaved filsiz rs03,,,,64d,1,64d,128d,0 ;RS03 512 KB filsiz rx24,,,,80d,2,9d,512d,0 ;RX24 720 KB filsiz rx52,,,,80d,2,10d,512d,1 ;RX52 800 KB (interleaved or not) filsiz rx52,,,,80d,2,10d,512d,0 filsiz rx03,1011712d,1011712d,,77d,2,26d,256d,1 ;1011712. interleaved filsiz rx03,,1011712d,,77d,2,26d,256d,0 ;1025024. non-interleaved filsiz rs04,,,,64d,1,64d,256d,0 ;RS04 1024 KB filsiz rk02,,,,200d,2,12d,256d,0 ;RK02 1.2 MB (ignore 3 spare tracks) filsiz rx33,,,,80d,2,15d,512d,0 ;RX33 1.2 MB filsiz rx23,,,,80d,2,18d,512d,0 ;RX23 1.44 MB filsiz rk05,,,,200d,2,12d,512d,0 ;RK05 2.5 MB (ignore 3 spare tracks) filsiz rx26,,,,80d,2,36d,512d,0 ;RX26 2.88 MB filsiz rl01,,,40d*256d,256d,2,40d,256d,0 ;RL01 5 MB filsiz rl02,,,40d*256d,512d,2,40d,256d,0 ;RL02 10 MB filsiz rk06,,,22d*512d,411d,3,22d,512d,0 ;RK06 14 MB filsiz rk07,,,22d*512d,815d,3,22d,512d,0 ;RK07 28 MB filsiz ra80,,,,546d,14d,31d,512d ;RA80 121 MB (560. cyls w/RCT) filsiz ra60,,,,1588d,6,42d,512d ;RA60 205 MB (1592. cyls w/RCT) filsiz ra70,,,,1507d,11d,33d,512d ;RA70 280 MB (no RCT 2BSD) filsiz ra81,,,,1248d,14d,51d,512d ;RA81 456 MB (1258. cyls w/RCT 2BSD) filsiz ra82,,,,1423d,15d,57d,512d ;RA82 622 MB (1427. cyls w/RCT 2BSD) filsiz ra71,,,,1915d,14d,51d,512d ;RA71 700 MB (no RCT 2BSD) filsiz ra72,,,,1915d,20d,51d,512d ;RA72 1.0 GB (no RCT 2BSD) filsiz ra90,,,,2649d,13d,69d,512d ;RA90 1.2 GB (2656. cyls w/RCT) filsiz ra92,,,,3099d,13d,73d,512d ;RA92 1.5 GB (3101. cyls w/RCT) ;(2BSD RA92 /etc/disktab entry differs wildly) filsiz ra73,,,,2667d,21d,70d,512d ;RA73 2.0 GB (no RCT 2BSD) ; fszend label byte ;end of actual device types ; ; Floppy disk autosizing tables. ; ; Each entry looks like this: ; ; dw addr of routine to set up FDC this type, 0 if end of list ; dw addr of table to skip to if successful, or 0 if just one type ; [ dw disk type if successful, if previous word was 0 ] ; db cyl,head,sec to try to read ; ; If SEC is negative then any sector will do (so use FDRID because it's ; faster). ; fdtyps dw inidz,dd512 ;512 b/s DD disks in HD drive db 0,0,-1 dw ini23,hd512 ;512 b/s HD disks db 0,0,-1 dw inidy,hd256 ;256 b/s HD disks (8" or workalikes) db 0,0,-1 dw inidx,0,rx01 ;RX01 if this works db 0,0,-1 dw ini24,0,rx24 ;RX24 (250 kHz vs. 300 kHz with DD512) db 0,0,-1 ;(this would see 360KB drives too) dw ini26,0,rx26 ;RX26 if this works db 0,0,-1 dw 0 ;end of list ; dd512 dw inidz,0,rx52 ;RX52 if DS and has 10 sectors db 0,1,10d dw inidz,0,rx50 ;RX50 if SS and has 10 sectors db 0,0,10d dw 0 ; hd256 dw inidy,0,rx03 ;RX03 if has side 1 db 0,1,-1 dw inidy,0,rx02 ;otherwise must be RX02 db 0,0,-1 dw 0 ; hd512 dw ini23,0,rx23 ;RX23 if has 18 sectors db 0,0,18d dw ini33,0,rx33 ;otherwise must be RX33 db 0,0,15d dw 0 ;+ ; ; Set all parameters that depend on medium type. ; ; bp log dev rec ; BADSEC is left set up for this device (with size of bad sector track) ; ;- setmed: mov ds:badsec,0 ;init size of bad sector track mov ds:badsec+2,0 mov bx,ss:med[bp] ;get disk type cmp bx,mscp ;MSCP (i.e. variable size) device? je smed4 ;yes, keep actual file size mov al,ss:ilimg[bp] ;get interleaved image file flag mov si,offset fsztab ;point at file size table smed1: cmp [si],bx ;is this it? .assume je smed3 ;yes smed2: add si,fllen ;bump to next cmp si,offset fszend ;done all? jb smed1 ;loop if not jmp short smed5 smed3: ; found matching device, copy its info if interleave flag matches cmp ds:flilv[si],al ;match? jne smed2 ;no mov ax,ds:flbsz[si] ;get size of bad block track in bytes mov dx,ds:flbsz+2[si] mov ds:badsec,ax ;save mov ds:badsec+2,dx mov ax,ds:flsiz[si] ;get file size in bytes mov dx,ds:flsiz+2[si] mov ss:totsiz[bp],ax ;save mov ss:totsiz+2[bp],dx ; assume a block is 512. bytes and init TOTBLK ; (can change it later if assumption is wrong) mov cl,9d ;shift count shr ax,cl ;divide by 512. ror dx,cl mov cx,dx ;copy and dl,not 177 ;isolate high 9 xor cx,dx ;isolate low 7 mov ss:totblk+2[bp],cx ;save high order or ax,dx ;compose low order mov ss:totblk[bp],ax ;save ; same for DEVSIZ mov ax,ds:flusz[si] ;get usable file size mov dx,ds:flusz+2[si] mov cl,9d ;shift count shr ax,cl ;divide by 512. ror dx,cl mov cx,dx ;copy and dl,not 177 ;isolate high 9 xor cx,dx ;isolate low 7 mov ss:devsiz+2[bp],cx ;save high order or ax,dx ;compose low order mov ss:devsiz[bp],ax ;save jmp short smed5 smed4: ; use actual file size for device size (MSCP or other variable size) mov ax,ss:totsiz[bp] ;get file size in bytes mov dx,ss:totsiz+2[bp] mov cl,9d ;shift count shr ax,cl ;divide by 512. ror dx,cl mov cx,dx ;copy and dl,not 177 ;isolate high 9 xor cx,dx ;isolate low 7 mov ss:totblk+2[bp],cx ;save high order mov ss:devsiz+2[bp],cx or ax,dx ;compose low order mov ss:totblk[bp],ax ;save mov ss:devsiz[bp],ax smed5: ; set up device geometry (used only for floppies at the moment) cmp ss:hwtype[bp],hwaspi ;SCSI? je smed6 mov ax,ds:flcyl[si] ;copy # cyls mov ss:ncyls[bp],al ;save low byte mov al,ds:flhd[si] ;# heads mov ss:nhds[bp],al mov ax,ds:flsec[si] ;# sectors mov ss:nsecs[bp],al ;save low byte mov ax,ds:flbs[si] ;# bytes/sector mov ss:secsz[bp],ax smed6: ; handle any needed special treatment mov si,offset smedty ;pt at table call wdluk ;lookup (BX still = med type) jc smed7 ;invalid callr ax ;init I/O routine ptrs, return smed7: ret ; smedty label word ;medium types ;cyls/heads/sectors, # bytes/sector dw rx01,sfddx ;77/1/26, 128 b/s dw rx02,sfddy ;77/1/26, 256 b/s dw rx03,sfdda ;77/2/26, 256 b/s dw rx23,sfd23 ;80/2/18, 512 b/s dw rx26,sfd26 ;80/2/36, 512 b/s dw rx33,sfd33 ;80/2/15, 512 b/s dw rx50,sfddz ;80/1/10, 512 b/s dw rx52,sfddz ;80/2/10, 512 b/s dw rx24,sfd24 ;80/2/9, 512 b/s dw 0 ; sfddx: ; RX01 floppy mov ax,offset inidx ;init mov bx,offset dxilv ;interleave routine jmp short sfddx1 ; sfddy: ; RX02(-like) floppy mov ax,offset inidy mov bx,offset dyilv ;interleave routine jmp short sfddx1 ; sfdda: ; RX03(-like) floppy mov ax,offset inidy mov bx,offset dyilv sfddx1: ; enter here to finish setting up RX01/02/03 floppy ; AX=init routine, BX=interleave routine mov ss:dsrini[bp],ax ;FDC init routine mov ss:fintlv[bp],bx ;interleave routine mov ss:rdblk[bp],offset rddx ;read from floppy mov ss:wrblk[bp],offset wrdx mov ax,offset fdrds ;assume actual hardware mov bx,offset fdwrs cmp ss:hwtype[bp],hwflop ;right? je sfddx2 mov ax,offset rddxf ;no, must be file otherwise mov bx,offset wrdxf sfddx2: mov ss:rdsec[bp],ax ;save mov ss:wrsec[bp],bx mov cl,2 ;shift count to convert 128 (sec) to 512 (blk) cmp ss:secsz[bp],256d ;double density? jne sfddx3 dec cx ;yes, fix shift count sfddx3: mov ss:blksec[bp],cl ;save it ret ; sfd24: ; RX24 floppy (720 KB) mov ax,offset ini24 jmp short sfddu ; sfd23: ; RX23 floppy (1.44 MB) mov ax,offset ini23 jmp short sfddu ; sfd26: ; RX26 floppy (2.88 MB) mov ax,offset ini26 jmp short sfddu ; sfd33: ; RX33 floppy (1.2 MB) mov ax,offset ini33 ;jmp short sfddu ; sfddu: ; set up RX23/RX24/RX26/RX33 disk or image file ; AX=DSRINI value mov ss:dsrini[bp],ax ;save mov ss:rdblk[bp],offset rddz mov ss:wrblk[bp],offset wrdz mov ss:fintlv[bp],offset duilv mov ax,offset fdrds ;assume actual hardware mov bx,offset fdwrs cmp ss:hwtype[bp],hwflop ;right? je sfddu1 mov ax,offset rdduf ;no, must be file otherwise mov bx,offset wrduf sfddu1: mov ss:rdsec[bp],ax ;save mov ss:wrsec[bp],bx ret ; sfddz: ; RX50 floppy mov ss:dsrini[bp],offset inidz mov ss:rdblk[bp],offset rddz mov ss:wrblk[bp],offset wrdz mov ss:fintlv[bp],offset dzilv mov ax,offset fdrds ;assume actual hardware mov bx,offset fdwrs cmp ss:hwtype[bp],hwflop ;right? je sfddz1 mov ax,offset rddzf ;no, must be file otherwise mov bx,offset wrdzf sfddz1: mov ss:rdsec[bp],ax ;save mov ss:wrsec[bp],bx ret ;+ ; ; Try to guess file system if defaulted. ; ;- defsys: test ss:fsys[bp],-1 ;did they tell us? jnz dsys6 ;yes xor cx,cx ;no idea yet dsys1: push cx ;save current file system guess push dx ;save ILIMG value that goes with CX call ss:dsrini[bp] ;init DSR pop dx ;restore pop cx mov si,offset chklst ;point at list of routines dsys2: lodsw ;get next addr test ax,ax ;end of table? jz dsys3 ;yes push si ;save push cx ;save current file system guess push dx ;save ILIMG of that guess call ax ;see if this could be it pop dx ;[restore] pop cx pop si jc dsys2 ;no, keep trying test cx,cx ;did we already have a guess? jnz dsys7 ;yes, ambiguous mov cx,ax ;save current guess mov dl,ss:ilimg[bp] ;remember whether interleaved jmp short dsys2 dsys3: ; finished trying all tests in table cmp ss:hwtype[bp],hwfile ;image file? jne dsys5 ;no cmp ss:med[bp],rx50 ;is it an RX50 image? je dsys4 cmp ss:med[bp],rx52 ;or "RX52" image? jne dsys5 dsys4: ; RX50 or RX52 image file, make second pass through table wit