.title dskpat - general disk block patcher .ident /003B/ .enabl lc ; ; Author: L. M. Fraser Apr, 80 V3 - Mar, 82 ; .mcall fhdof$,hmbof$ .mcall qiow$,dir$,alun$,exit$s,glun$,svtk$,spwn$,wtse$ .mcall istat$,state$,tran$ ; ;+ ; DSKPAT - Disk block patcher ; ;- ;BUILD+ ; ; mac dskpat,dskpat/-sp=[1,1]exemc/ml,[uic]dskpat ; tkb dskpat,dskpat/-sp=dskpat ; / ; asg=ti:1 ! command input lun ; asg=ti:2 ! dump lun #1 ; asg=lp:3 ! dump lun #2 ; asg=nl:3 ! disk lun (nl: for safty) ;BUILD- .nlist bex hmbof$ fhdof$ ; defines NSAVE = 4 ; number save buffers DMPROW = 10 ; number of rows per dump CR = 15 LF = 12 BEL = 7 SPC = 40 ; task build time stamp .psect $$dbts $dbts:: .blkw 10 ; date/time block from TKB ; documentation .psect string hlptxt: .ascii /DSKPAT - General DISK Block Patcher/ .ascii / Task built: / bldtxt: .ascii / / .ascii / Functions: V003B/ .ascii / get [ LBN ] / .ascii / put [ LBN ] / .ascii / set {LBN, READONLY, INDEXF, BAD, TERMINAL, PRINTER}/ .ascii / next/ .ascii / dump {o,b,a,r,h,f,d} [ loc ]/ .ascii / {octal, byte, ascii, rad50, home, file, directory}/ .ascii / modify loc val/ .ascii / display loc/ .ascii / status/ .ascii / home [ set,verify ]/ .ascii / block [ set,verify ]/ .ascii / save [ buf ]/ .ascii / restore [ buf ]/ .ascii / mcr command_line_text/ .ascii / done/ .ascii / help/ .ascii / Missing options default to NEXT, ZERO or VERIFY/ .ascii / LBNs can be #, NEXT, PREV, BOOT, HOME, INDEX, or LAST/ .byte CR, LF hlptxl = .-hlptxt .even ; macros .macro error string,pre,?aaa,?bbb br bbb aaa: .if nb pre .ascii /pre -- / .endc .ascii /string/ .even bbb: trap /2 .word aaa .endm ; .psect dpbs ; directive dpb's msg: qiow$ io.wvb,1,1,,iosb,, dev: qiow$ io.rpr,1,1,,iosb,, cmd: qiow$ io.rpr,1,1,,iosb,, dump: qiow$ io.wvb,2,2,,iosb,, disk: qiow$ io.rlb,4,4,,iosbdd,, att: qiow$ io.att,4,4,,iosbdd det: qiow$ io.det,4,4,,iosbdd alun: alun$ 4,NL,0 glun: glun$ 4,lunbuf svtk: svtk$ sstvec,10 spawn: spwn$ mcr...,,,,,1,,exstat,cmdbuf,80. wait: wtse$ 1 ; .psect string prmtd: .ascii /Device? / prmtdl = .-prmtd prmtc: .ascii /*Cmd? / prmtcl = .-prmtc .even ; asc: .rad50 /nulsohstxetxeotenqackbelbs ht lf vt / .rad50 /ff cr so si dledc1dc2dc3dc4naksynetb/ .rad50 /canem subescfs gs rs us sp / ; romsg: .asciz /READONLY/ null: .asciz / / ; func = disk + q.iofn blkl = disk + q.iopl+10 blkh = disk + q.iopl+6 ; .psect data ; should add oddadd to list in 0 & 1 sstvec: .word 0,0,0,0,0,0,trap,0 lunbuf: .blkw 8. ; get lun buffer exstat: .blkw 8. ; spawn exit status block ; cmdbuf: .blkb 80. ; command input buffer dmpbuf: .blkb 132. ; dump output buffer ; don't separate iosb and blkbuf **** iosb: .word 0,0 ; QIO status block iosbdd: .word 0,0 ; Disk QIO status block blkbuf: .blkb 512. ; disk bufer ; don't separate iosb and blkbuf **** savbuf: .rept nsave .blkb 512. ; save buffers .endr savlbn: .rept nsave .word 0,0 ; saved buffer LBN's .endr lastlb = lunbuf+g.lucw+2 ; last good LBN on volume idxlbn: .word 0,0 ; indexf file LBN from home block ; pflags: .word 0 ; permanent flags word ; low byte - latching per device ; high byte - latching is permanent ; defined bits for pflags ROFLG = 1 ; device is readonly flags: .word 0 ; flags word loc: .word 0 ; buffer offset val: .word 0 ; value buffer ; savblk: .word 0 ; save buffer used dswsav: .word 0 ; saved $DSW ; fmt: .word 0 ; format string address argblk: .blkw 40. ; argument block for EDMSG ; .psect format ; fmtoct: .asciz /%8P/ ; octal format fmtbyt: .asciz /%P- %16B/ ; byte format fmtasc: .asciz /%P- %16E/ ; extended ascii format fmtr50: .asciz /%8R/ ; radix 50 format fmtdir: .asciz /FID:%P:%P:%P %X/ ; directory structure ; status display fmtsta: .ascii / ** STATUS ** %2A%B: %I%N/ .ascii /Next LBN: %T%N/ .ascii /Saved LBN: %VT%N/ .ascii /MAX LBN: %T%N/ .asciz /$DSW = %P IOSB = %4B%N/ .even ; ; dispatch table for dump routines dispat: .word dmpoct,dmpbyt,dmpasc,dmpr50,dmphom,dmpfil,dmpdir,0,0,0 .page .psect code ; ; code .... ; entry point and initialization .. dskpat:: dir$ #svtk ; set up trap catcher mov #bldtxt,r0 ; point to tkb time loc mov #$dbts,r1 ; and to block set up by tkb call $dat ; load in date string movb #spc,(r0)+ ; space it out mov #4,r2 ; request tenths of seconds call $tim ; convert it br getdev ; ; deverr: mov $dsw,dswsav ; save $DSW for status error ; ; device loop .. getdev: dir$ #dev ; get device spec bcs 10$ ; abort on error cmpb iosb,#IS.SUC ; QIO OK? beq 20$ ; continue if good 10$: exit$s ; exit program ; 20$: mov iosb+2,r1 ; any thing there? beq getdev ; no, get another line mov #cmdbuf,r0 ; point to chars received mov #alun+a.luna,r1 ; and to out loc mov #2,r2 ; fixup only two call $cvtuc ; copy and make upper case mov #cmdbuf+2,r0 ; point to any digits call $cotb ; convert any unit number mov r1,alun+a.lunu ; set unit number cmpb r2,#': ; finished device right bne deverr ; report error dir$ #alun ; assign device bcs deverr ; something wrong on CS dir$ #glun ; OK, find about disk bcs deverr ; probably should do mountable, etc checks here, but screw it, let him think dir$ #att ; attach disk bcs deverr clrb lunbuf+g.lucw+3 ; flush drivers trash sub #1,lastlb+2 ; convert from size to LBN sbc lastlb ; and high part mov #-1,idxlbn ; trash any saved indexf LBN clrb pflags ; clear device specific flags ; ; command loop getcmd:: clr flags ; init some data clr loc clr val dir$ #cmd ; get a command bcs 10$ ; abort on error cmpb iosb,#IS.SUC ; QIO OK? beq 20$ ; continue if good 10$: exit$s ; exit program ; ; set up for .tpars 20$: mov iosb+2,r3 ; length of string beq getcmd ; can't be null mov #cmdbuf,r4 ; addr of string mov r4,r0 ; copy addr mov r0,r1 ; convert in place mov r3,r2 ; this many chars call $cvtuc mov #3*256.,r1 ; three char min, ignore blanks mov #cmdkey,r2 ; key tables mov #first,r5 ; transaction table addr .if df de$bug call .tpard ; state tracking entry .iff call .tpars .endc bcc 30$ ; cc = no error error 30$: tst flags ; check exit flag bpl getcmd ; do another command jmp getdev ; or another device .page ; ; tpars state tables .dsabl lc .if df de$bug istat$ cmdtab,cmdkey,$DEBUG .iff istat$ cmdtab,cmdkey .endc ; state$ first tran$ "get",st.get,ac.get tran$ "put",st.get,ac.put tran$ "set",st.get,,1,flags tran$ "next",$exit,ac.nxt tran$ "dump",st.dmp tran$ "dmp",st.dmp tran$ "modify",st.mod tran$ "display",st.dsp tran$ "dsp",st.dsp tran$ "status",$exit,ac.sta tran$ "home",st.chk,,1,flags tran$ "block",st.chk tran$ "file",st.chk tran$ "done",$exit,,-1,flags tran$ "save",st.sav,,1,flags tran$ "restore",st.sav tran$ "mcr",st.mcr tran$ "help",$exit,ac.hlp tran$ '?,$exit,ac.hlp ; state$ st.get tran$ "next",$exit,incblk tran$ "previous",$exit,decblk tran$ "boot",$exit,booblk tran$ "home",$exit,homblk tran$ "indexf",$exit,idxblk tran$ "last",$exit,lstblk tran$ $eos,$exit,chkio tran$ $numbr,$exit,setblk tran$ "readonly",$exit,,ROFLG,pflags tran$ "inspect",$exit,,ROFLG,pflags tran$ "bad",$exit,ac.bad tran$ "terminal",$exit,ac.ter tran$ "printer",$exit,ac.pri tran$ $lamda,$exit,syntax ; state$ st.dmp tran$ 'o,st.dm1,,0,flags tran$ 'b,st.dm1,,2,flags tran$ 'a,st.dm1,,4,flags tran$ 'r,st.dm1,,6,flags tran$ 'h,st.dm1,,10,flags tran$ 'f,st.dm1,,12,flags tran$ 'd,st.dm1,,14,flags tran$ $eos,st.dm1 tran$ $numbr,$exit,ac.dmp ; state$ st.dm1 tran$ $eos,$exit,ac.dm1 tran$ $numbr,$exit,ac.dmp tran$ $lamda,$exit,syntax ; state$ st.mod tran$ $numbr,st.mo1,ac.mod tran$ $lamda,$exit,syntax ; state$ st.mo1 tran$ $numbr,st.mo2,ac.mo1 tran$ $lamda,$exit,syntax ; state$ st.mo2 tran$ $eos,$exit tran$ $lamda,$exit,syntax ; state$ st.dsp tran$ $eos,$exit,ac.ds1 tran$ $numbr,$exit,ac.dsp tran$ $lamda,$exit,syntax ; state$ st.sav tran$ $eos,$exit,ac.sa1 tran$ $numbr,$exit,ac.sav tran$ $lamda,$exit,syntax ; state$ st.chk tran$ "set",st.ck1,,200,flags tran$ "verify",st.ck1 tran$ $eos,st.ck1 tran$ $lamda,$exit,syntax ; state$ st.ck1 tran$ $lamda,$exit,ac.chk ; state$ st.mcr tran$ $eos,$exit,syntax tran$ $lamda,$exit,ac.mcr ; ; end of tables state$ .page .enabl lc ; ; action routines .psect code ; ac.get: mov #io.rlb,func ; set QIO to read return ; ac.put: bit #ROFLG,pflags ; readonly access set? bne 10$ ; bit set -> no writes mov #io.wlb,func ; set QIO to write return 10$: error return ; incblk: add #1,blkl ; bump LBN low part adc blkh ; and high part br chkio ; and do it ; decblk: sub #1,blkl ; decrement LBN sbc blkh br chkio ; booblk: clr blkl ; set to boot block clr blkh ; i.e. LBN 0 br chkio ; homblk: mov #1,blkl ; i.e. LBN 1 clr blkh ; set to home block br chkio ; idxblk: tst flags ; do setup if != bne setidx ; go get mov idxlbn,blkh ; set to saved index file LBN bmi 10$ ; if mi idx not set mov idxlbn+2,blkl ; set up low part of LBN br doio 10$: error ; the -1 left in blkh here is invalid anywhere, must explicitly get next LBN return ; lstblk: mov lastlb,blkh ; set to last good block mov lastlb+2,blkl ; br chkio ; setidx: mov blkbuf+2,idxlbn ; save index file LBN mov blkbuf+4,idxlbn+2 ; and low part add blkbuf,idxlbn+2 ; skip over index bitmap adc idxlbn ; return ; setblk: mov .pnumb,blkl ; set low lbn mov .pnumh,blkh ; and high part ; chkio: tst flags ; do I/O only if = beq doio ; now do physical I/O return ; ; really should check block requested aginst MAX, but I'll let DRQIO do it doio: dir$ #disk ; do I/O to disk bcs 10$ ; any dir errors cmpb iosbdd,#IS.SUC ; or I/O errors bne 10$ ; yes report'em return ; nope, cont 10$: mov $dsw,dswsav ; error ; report problem return ; ac.ter: mov #2,dump+q.iolu ; set to terminal dumping return ac.pri: mov #3,dump+q.iolu ; or to printer dumps return ; ac.nxt: add #1,blkl ; increment LBN adc blkh ; (a 24 bit number) return ; ac.dmp: mov .pnumb,loc ; get starting loc ac.dm1: call dodump ; do dump return ; ac.dsp: mov .pnumb,loc ; pick up number ac.ds1: mov loc,r0 ; copy offset mov r0,r1 ; and again ror r1 ; byte address? bcs 10$ ; use movb mov blkbuf(r0),r1 ; get word needed br 20$ 10$: clr r1 ; init reg bisb blkbuf(r0),r1 ; pick up value 20$: mov #cmdbuf,r0 ; point to buffer ;conversion routine from M+ TDX cvt: ; enter w/ r0->buffer, r1=value mov r1,-(sp) ; save the word value mov r0,-(sp) ; save buffer addr mov #40,r2 ; clear buffer 10$: mov #" ,(r0)+ ; space fill sob r2,10$ ; loop mov (sp)+,r0 ; restore buffer addr movb #11,(r0)+ ; space over a tab clr r2 call $cbdmg ; put in decimal word add #2,r0 clr r1 bisb (sp),r1 ; low order byte clr r2 call $cbdmg ; decimal byte movb #',,(r0)+ clr r1 bisb 1(sp),r1 clr r2 call $cbdmg ; decimal high byte add #2,r0 ; some space movb #'#,(r0)+ mov (sp),r1 mov pc,r2 ; non-zero call $cbomg add #2,r0 movb #'#,(r0)+ clr r1 bisb (sp),r1 mov pc,r2 call $cbtmg movb #',,(r0)+ clr r1 bisb 1(sp),r1 mov pc,r2 call $cbtmg add #2,r0 movb #'$,(r0)+ mov (sp),r1 mov #8720.,r2 call $cbta add #2,r0 movb #'%,(r0)+ mov (sp),r1 call $c5ta add #2,r0 movb #'",(r0)+ cmpb (sp),#40 blos 20$ cmpb (sp),#177 beq 23$ bhi 25$ movb (sp),(r0)+ br 30$ 20$: movb #'<,(r0)+ clr r1 bisb (sp),r1 asl r1 mov asc(r1),r1 22$: call $c5ta movb #'>,(r0)+ br 30$ 23$: mov #^rdel,r1 br 22$ 25$: inc r0 30$: cmpb 1(sp),#40 blos 120$ cmpb 1(sp),#177 beq 123$ bhi 130$ movb 1(sp),(r0)+ br 130$ 120$: movb #'<,(r0)+ clr r1 bisb 1(sp),r1 asl r1 mov asc(r1),r1 122$: call $c5ta movb #'>,(r0)+ br 130$ 123$: mov #^rdel,r1 br 122$ 130$: tst (sp)+ mov #cmdbuf,r1 ; get buffer addr sub r1,r0 ; get length mov r1,msg+q.iopl ; set addr into DPB mov r0,msg+q.iopl+2 ; set len into DPB dir$ #msg ; do output return ; ac.mod: mov .pnumb,loc ; set loc to fix return ; ac.mo1: mov loc,r0 ; get offset to fix mov r0,r1 ; copy offset ror r1 ; low bit set? bcs 10$ ; yes, use byte manipulation mov .pnumb,blkbuf(r0) ; and do it return 10$: movb .pnumb,blkbuf(r0) ; set this high byte return ; ac.sta: mov #argblk,r0 ; get arg block mov r0,r2 ; copy for later mov #lunbuf+g.luna,(r0)+ ; point to device name mov #lunbuf+g.lunu,(r0)+ ; and to unit number bit #ROFLG,pflags ; is device readonly? bne 10$ ; NE means yes mov #null,(r0)+ ; point to null string br 20$ 10$: mov #romsg,(r0)+ ; or to "READONLY" string 20$: mov #blkh,(r0)+ ; point to next LBN field mov #NSAVE,(r0)+ ; repeat count for save buffers mov #savlbn,r1 ; first save LBN .rept NSAVE mov r1,(r0)+ ; and location add #4,r1 ; point to next .endr mov #lunbuf+g.lucw+2,(r0)+ ; size of device mov dswsav,(r0)+ ; $DSW mov #iosbdd,(r0)+ ; and I/O status block ; mov #cmdbuf,r0 ; get output buf mov #fmtsta,r1 ; get format string call $edmsg ; make the string mov #cmdbuf,msg+q.iopl ; set buffer address mov r1,msg+q.iopl+2 ; and length dir$ #msg ; do it!!!! return ; ac.sav: mov .pnumb,savblk ; set buffer to use ac.sa1: mov savblk,r0 ; get buffer to use cmp r0,#nsave ; valid buffer? bmi 1$ add #2,(sp) ; reject trans return 1$: mov r0,r2 ; save buffer number swab r0 ; mul by 512. asl r0 add #savbuf,r0 ; point to selected buffer ; tst flags ; ne => save bne 10$ ; restore mov #blkbuf,r1 ; set output to block buffer br 20$ ; use common copy code ; save 10$: mov r0,r1 ; copy output buffer addr asl r2 ; mul buf # by 4 asl r2 mov blkh,savlbn(r2) ; copy LBN mov blkl,savlbn+2(r2) mov #blkbuf,r0 ; set input to block buffer ; common copy 20$: mov #256.,r2 ; this many words 30$: mov (r0)+,(r1)+ ; move it sob r2,30$ ; loop return ; ac.chk: mov #blkbuf,r0 ; point to buffer clr r1 ; init for sum tstb flags ; mi -> verify only bpl ver.cs ; go verify for this block bit #1,flags ; eq ->block sum beq 20$ ; block only mov #H.CHK1/2,r2 ; location of first CS 10$: add (r0)+,r1 ; calc sum sob r2,10$ ; loop mov r1,(r0) ; load check sum 20$: mov #blkbuf,r0 ; buffer again clr r1 ; init sum again mov #255.,r2 ; do whole block now 30$: add (r0)+,r1 ; calc sum sob r2,30$ ; loop mov r1,(r0) ; load it return ; verify only ver.cs: bit #1,flags ; eq ->block sum beq 20$ ; block only mov #H.CHK1/2,r2 ; location of first CS 10$: add (r0)+,r1 ; calc sum sob r2,10$ ; loop cmp r1,(r0) ; check-sum right bne cserr ; report error error 20$: mov #blkbuf,r0 ; buffer again clr r1 ; init sum again mov #255.,r2 ; do whole block now 30$: add (r0)+,r1 ; calc sum sob r2,30$ ; loop cmp r1,(r0) ; this one right? bne cserr ; go report it csok: error return ; cserr: error return ; dodump: mov flags,r1 ; index into table mov r3,-(sp) ; save a register mov #DMPROW,r3 ; do this many rows jmp @dispat(r1) ; go to it... ; these following use the data itself as input for edmsg (r2 ->next data) ; octal dmpoct: mov #1,r0 ; mask odd bytes mov #fmtoct,fmt ; set format address br dmpcm1 ; rad50 dmpr50: mov #1,r0 ; mask odd bytes mov #fmtr50,fmt ; select rad50 format br dmpcm1 ; directory dmpdir: mov #17,r0 ; directories use mod 20 mov #fmtdir,fmt ; set format ; fall thru dmpcm1: mov #blkbuf,r2 ; data buffer addr bic r0,loc ; mask offset for valid data add loc,r2 ; point to word 10$: mov #dmpbuf,r0 ; place to put it all mov loc,r1 ; get current pointer mov r2,-(sp) ; protect r2 from cbomg call $cbomg ; convert mov (sp)+,r2 ; restore r2 movb #'-,(r0)+ ; insert a separator movb #11,(r0)+ ; insert a TAB mov fmt,r1 ; get correct format call $edmsg ; convert'em sub #dmpbuf,r0 ; calc length mov r0,dump+q.iopl+2 ; set length of buffer dir$ #dump ; send it out add #20,loc ; update pointer sob r3, 10$ ; loop mov (sp)+,r3 ; restore reg return ; these use a single address and repeat count for edmsg ; bytes dmpbyt: mov #fmtbyt,fmt ; select format string br dmpcom ; ascii dmpasc: mov #fmtasc,fmt ; select ascii format ;;; br dmpcom ; fall thru ; common code dmpcom: mov #blkbuf,r0 ; point to data bic #1,loc ; can't handle odd bytes add loc,r0 ; and to words mov r0,argblk+2 ; data buffer address ; 10$: mov #dmpbuf,r0 ; set output buffer address mov fmt,r1 ; and format address mov #argblk,r2 ; arg pointer mov loc,(r2) ; put in the location call $edmsg mov r1,dump+q.iopl+2 ; set up length dir$ #dump add #20,loc ; update pointer add #20,argblk+2 ; bump to next data sob r3,10$ ; loop mov (sp)+,r3 ; restore reg return ; these are one shot specials ; home block dmphom: ; file header dmpfil: error return ; ac.hlp: mov #hlptxt,msg+q.iopl ; set text address mov #hlptxl,msg+q.iopl+2 ; and length dir$ #msg ; dump it out return ; ac.mcr: mov r4,spawn+s.pwca ; set command line address mov r3,spawn+s.pwcl ; and length dir$ #det ; release disk device dir$ #spawn ; start sub task bcs 20$ ; error if CS dir$ #wait ; wait for it mov exstat,dswsav ; put exit status where user can get it dir$ #att ; grab disk again return ; 20$: mov $dsw,dswsav ; save dsw error return ; ac.bad: error return ; syntax: error return ; trap: mov (sp)+,msg+q.iopl+2 ; get msg length mov @(sp),msg+q.iopl ; and address add #2,(sp) ; skip address dir$ #msg ; send msg rtt ; return from trap ; .end dskpat