.Title DACM - Internally-Queueing DU Handler for RT/TSX .enabl lc .dsabl gbl .nlist bex .mcall .module,.drdef,.br,.assume,.addr,.fork,.mtps,.mfps,.print,.inten,.ttyou .module da,release=V05,version=206,audit=yes .psect dadvr .psect junk .psect daboot .psect program .psect mesage con Vermes: .asciz /DACM Internally-Queueing Multi-Unit DU Handler V5d 18-Jul-92/ da.ver=206 ;Version number accessible from status table. Change when ; format of SPFUN's alters. ;(Gets high-order bit set if TSX-plus) ;Author: Chester Wilson, 71 Galatea Street, Charleville 4470 ;For documentation, see DUCM.DOC .Sbttl Check for Attempted Multi-Port Assembly .if df DU$PORTS .iif ne du$ports-1, .error; DACM provides Single-Port DU Handler Only .endc .Sbttl Variables Which the User may Re-define ; TSX$P ; ***** .iif ndf TSX$P, TSX$P=0 ;This handler runs under TSX+, as a handler mapped into high memory, if ; the variable ; TSX$P ; is defined. .if ne TSX$P .iif eq mmg$t, .error ;Must have extended memory for TSX! da.ver = da.ver ! 100000 ;TSX - add extra bit to version. .endc ; $XLINK ; ****** $xlink=0 ;DU device is already using this! ; TS$JOB ; ****** .iif ndf ts$job, ts$job=24. ;For TSX-plus later than 6.01, may have >31 jobs. Define the maximum ; allowable, and prang him if he tries to use more. ; CSR & VECTOR ; ************ .iif ndf dacsr, dacsr=160334 .iif ndf davec, davec=150 ; RETRY ; ***** .iif ndf retry, retry=3 ;Who should need more with MSCP drives? ; TIMSIZ ; ****** .iif ndf timsiz, timsiz=10. ;Size of disc time-out error recording block. .iif gt timsiz-30., .error ;Keep timsiz to <=30 for space allocation ; in programme portion or for users. ; SPLIT ; ***** .iif ndf split, split=0 ;Default is no internal splitting of discs ; into partitions .Sbttl .Sbttl UPDATE HISTORY .Sbttl -------------- .Sbttl V5d 18-Jul-92 ; Create "DA" version, renaming as req'd. Billy Y.. .Sbttl DEFINITIONS .Sbttl ----------- .Sbttl TSX Queue Element Definitions ; For use within monitor q.link ==: 0 ;Link to next queue entry q.csw ==: 2 ;Address of CSW for channel making request q.blkn ==: 4 ;Physical block number of request q.func ==: 6 ;Special function code q.unit ==: 7 ;Device unit number (bits 0 through 2) q.jnum ==: 7 ;Job number issuing request (bits 3 through 7) q.buff ==: 10 ;User buffer address relative to Q.PAR q.wcnt ==: 12 ;Word count (+ = read, 0 = seek, - = write) q.comp ==: 14 ;Address of completion routine for request q.par ==: 16 ;PAR relocation bias for buffer address q.pa5 ==: 20 ;Mapping value for kernel PAR 5 q.umrx ==: 22 ;Address of UMR block assigned for I/O q.chan ==: 24 ;User channel # associated with I/O request q.devx ==: 26 ;Device index number q.flag ==: 27 ;Device control flags q.job ==: 30 ;Number of job that is making request q.umvb ==: 31 ;Unibus UMR base register number q.umpb ==: 32 ;Original value of Q.BUFF when I/O was initiated q.umpp ==: 34 ;Original value of Q.PAR when I/O was initiated q.pa6 ==: 36 ;Mapping value for kernel PAR 6 q.ucsw ==: 40 ;Virtual address of user's channel block q.icsw ==: 42 ;Copy of user's channel block (12 bytes) ;For use within handlers q$link ==: -4 ;Link to next queue entry q$csw ==: -2 ;Address of CSW for channel making request q$blkn ==: 0 ;Physical block number of request q$func ==: 2 ;Special function code q$unit ==: 3 ;Device unit number (bits 0 through 2) q$jnum ==: 3 ;Job number issuing request (bits 3 through 7) q$buff ==: 4 ;User buffer address relative to Q$PAR q$wcnt ==: 6 ;Word count (+ = read, 0 = seek, - = write) q$comp ==: 10 ;Address of completion routine for request q$par ==: 12 ;PAR relocation bias for buffer address q$pa5 ==: 14 ;Mapping value for kernel PAR 5 q$umrx ==: 16 ;Address of UMR block assigned for I/O q$chan ==: 20 ;User channel # associated with I/O request q$devx ==: 22 ;Device index number q$flag ==: 23 ;Device control flags q$job ==: 24 ;Number of job that is making request q$umvb ==: 25 ;Unibus UMR base register number q$umpb ==: 26 ;Original value of Q$BUFF when I/O was initiated q$umpp ==: 30 ;Original value of Q$PAR when I/O was initiated q$pa6 ==: 32 ;Mapping value for kernel PAR 6 q$ucsw ==: 34 ;Virtual address of user's channel block q$icsw ==: 36 ;Copy of user's channel block (12 bytes) .Sbttl Priority Setting/Clearing Macros psw=177776 ;All good XM processors have one. p1=172342 ;PAR1 (KISAR1) - for mapping user buffer p6=172354 ;PAR6 (KISAR6) - ibid, TSX p5=172352 ;PAR5 (KISAR5) For getting handler addresses .if ne tsx$p px=p6 .iff px=p1 .endc .macro nointerrupts .if ne MMG$T bis #340,@#psw .iff .mtps #340 .endc .endm .macro interrupts .if ne MMG$T bic #340,@#psw .iff .mtps #0 .endc .endm .Sbttl SAVPSW Macro .macro savpsw arg .if ne mmg$t mov @#psw,arg .iff clr arg .endc .endm savpsw .Sbttl DEBUG Macro .macro debug n,?x tst @#500 bne x mov #n,@#502 halt x: .endm debug .Sbttl DEBUGT Macro .macro debugt n,?x,?y x: bit #200,@#177564 beq x movb n,@#177566 y: bit #200,@#177564 beq y .endm debugt .Sbttl JUMP Macros .macro jmpcs loc,?x bcc x jmp loc x: .endm jmpcs .macro jmpcc loc,?x bcs x jmp loc x: .endm jmpcc .macro jmpeq loc,?x bne x jmp loc x: .endm jmpeq .macro jmpne loc,?x beq x jmp loc x: .endm jmpne .macro jmpgt loc,?x ble x jmp loc x: .endm jmpgt .macro jmple loc,?x bgt x jmp loc x: .endm jmple .macro jmpge loc,?x blt x jmp loc x: .endm jmpge .macro jmplt loc,?x bge x jmp loc x: .endm jmplt .macro jmphi loc,?x blos x jmp loc x: .endm jmphi .macro jmplos loc,?x bhi x jmp loc x: .endm jmplos .macro jmphis loc,?x blo x jmp loc x: .endm jmphis .macro jmplo loc,?x bhis x jmp loc x: .endm jmplo .macro jmppl loc,?x bmi x jmp loc x: .endm jmppl .macro jmpmi loc,?x bpl x jmp loc x: .endm jmpmi .Sbttl SPFUN Definitions ;Standard DEC DU SPFUNs SP.RIO=:377 ;Read, returning status word SP.WIO=:376 ;Write, returning status word SP.PSZ=:373 ;Return partition size SP.DAT=:372 ;Return RT-type partition table SP.BYO=:371 ;Bypass handler for MSCP access (old format) SP.BYP=:360 ;Ibid, new format. ;Additional SPFUNs SP.USZ=:300 ;Return unit size (2 words, high-order first) SP.PIO=:301 ;Physical I/O SP.CMP=:302 ;Compare data SP.PIC=:303 ;Compare data, physical I/O SP.STS=:363 ;Return statistics table (8 words) SP.FAK=:200 ;Fake a disc timeout (requires BLOCK=100000) .Sbttl Special I/O Flag Values si.ok=: 100000 ;Error-free return si.bbr=:100200 ;Bad block replacement flag found set on I/O si.rty=:100002 ;Error but recovered on retry si.err=:177777 ;Error which did not recover .Sbttl Definitions of Q$LINK bits x.done=: 200 ;Set after message returned for this element x.error=: 100 ;Set if error occurred x.avail=: 40 ;Set if the error was AVAILABLE x.bbr=: 20 ;Set if error was BBR x.gus=: 4 ;Set if GET UNIT STATUS required x.ondone=: 2 ;Set if an ONLINE has already been performed for this x.online=: 1 ;Set if an ONLINE command required .Sbttl Definitions of Q$FUNC byte values (the order is actually used!) x.io=:0 ;Normal I/O x.cmp=:x.io +1 ;Compare Data (spfun 302) x.pio=:x.cmp+1 ;Physical I/O (spfun 301) x.pic=:x.pio+1 ;Compare Data, physical I/O (spfun 303) x.sio=:x.pic+1 ;Special I/O (spfuns 376,377) x.byp=:x.sio+1 ;Bypass (spfuns 360,371) x.bys=:x.byp+1 ;Special bypass, with checking x.psz=:x.bys+1 ;Partition Size (spfun 373) x.usz=:x.psz+1 ;Unit Size (spfun 300) x.xsz=:x.usz+1 ;Unit Size, with non-zero block number .Sbttl Other Definitions SYSPTR=:54 ;Pointer to start of RMON DRFIN$=:270 ;Offset from start of RMON to queue element returning ; subroutine. SYSVER=:276 ;Monitor version SYSUPD=:277 ;Monitor release SYSGEN=:372 ;Sysgen features word TSX$=:100000 ;Set if running under TSX-plus P1EXT=: 432 ;PAR1 "Externalization" Routines for XM CVAPHY=:-16 ;HAVEN'T ADJUSTED HANDLER FOR AT: HANDLER INTERFACE ; YET ... SORRY! P.CSIZ==:60 ;Minimum size for command buffer. P.MSIZ==:60 ;ibid, message buffer. .Sbttl .Sbttl SET & INSTALLATION CODE .Sbttl ----------------------- .Sbttl Installation Code .drdef da,50,filst$!spfun$!varsz$,0,dacsr,davec .drptr load=once,unload=once,fetch=once,release=once .drins da nop tst @inscsr return noport: .asciz /?DA-E-Single Port Only/ .even .Sbttl EMT Area for SET Code barea: .byte 17,11 ;Channel 17; code for WRITE .blkw 2 .word 256. ;Word count .word 0 ;Wait for I/O completion. .Sbttl Read & Write SET Blocks ;These routines expect the block number in T1, and ; return R2 pointing to the second block in the USR buffer. ; Readb1 & Writb1 put 1 into R1. Otherwise registers are preserved. .Enabl lsb Readb1: mov #1,r1 Readb: movb #10,barea+1 ;Make it a READ br 10$ Writb1: mov #1,r1 Writb: movb #11,barea+1 ;Make it a WRITE 10$: mov r0,-(sp) .addr #1000,r2 .addr #barea+4,r0 mov r2,(r0) ;Buffer Address mov r1,-(r0) ;Block Number tst -(r0) ;Point R0 to start of area emt 375 ;Read or write bcc 20$ mov (sp)+,r0 ;Scrub r0, as actually want to scrub return 20$: mov (sp)+,r0 ; address [only call this 1 layer down!!!] return .Dsabl lsb .iif gt <.-360> .error .Sbttl SET Code .drset VECTOR,1,o.vec,oct .drset CSR,1,o.csr,oct .drset PART,1,o.part,num .drset UNIT,1,o.unit,num .drset PORT,1,o.port,num .drset BYPASS,1,o.byp,no .if ne tsx$p ;If TSX, no SETs allowed at all o.vec: o.csr: o.part: o.unit: o.port: o.byp: .addr #notsx,r0 .print sec return notsx: .asciz /?DA-E-No SETs allowed under TSX/ .even .iff ;If not TSX, give him all the sets. o.unit: call gettab ;Get address of part/unit table mov r0,(r1) ;Put into unit word call writb1 ;Write out this block mov #bpartb/1000,r1 ;and get boot block with partition table call readb add r3,r2 ;Offset within partition table mov r0,(r2) ;Insert unit br o.w o.part: call gettab ;Get address of part/unit table mov r0,2(r1) ;Put into partition word call writb1 ;Write out this block mov #bpartb/1000,r1 ;and get boot block with partition table call readb add r3,r2 ;Offset within partition table mov r0,+2(r2) ;Insert partition br o.w o.port: .addr #noport,r0 .print br o.bad ;Give error return (in case in command file) o.csr: cmp r0,#160000 blo o.bad bit #3,r0 bne o.bad mov r0,inscsr mov r0,ip mov r0,r1 inc r1 inc r1 mov r1,sa call writb1 mov #bdacsr/1000,r1 call readb mov r0,(r2) o.w: call writb call readb1 br o.good o.vec: cmp r0,#400 bhis o.bad bit #3,r0 bne o.bad mov r0,dastrt .if ne tsx$p mov r0,tsxvec .endc asr r0 ;Set up in form required for init list asr r0 add #,r0 mov r0,vec br o.good o.byp: dec r3 ;Value of 1 - to allow bypass, zero it. nop no.byp: mov r3,bypok .br o.good o.good: tst (pc)+ o.bad: sec return gettab: ;Get address of partition table offset. asl r1 ;Device number (DA0,1,2,3,4,5,6,7) asl r1 ;4 bytes per unit. mov r1,r3 ;Need this value to calculate table offset .addr #partab,r1,add return .endc .iif gt <.-1000>, .error .Sbttl .Sbttl HANDLER PROPER .Sbttl -------------- .Sbttl Entry Point of Handler .drbeg da br x statu$: 0 ;Status word for last operation performed. ; [WHY didn't they use an SPFUN?] x: jmp begin .Sbttl DATA AREA .Sbttl . MCSP Buffers ;Word needed by TSX+ .if ne TSX$P MPOINT: 0 ;If TSX, need separate word for virtual MBUFF address .iff MPOINT=MRING ;Otherwise MBUFF address is in MRING. .endc ;Headers: CMDINT: 0 ;Zeroed by host; set if interrupt caused by command RSPINT: 0 ;ibid, response. MRING: .blkw 2 ;Pointer to MBUFF, with flags in second word CRING: .blkw 2 ;Pointer to CBUFF, with flags in second word ;Buffers: COMAND: LN.CMD: 0 ;Command length VC.CMD: 0 ;Connection ID, message type, and credits CBUFF: .blkb P.CSIZ ;Actual buffer MESAGE: LN.RSP: 0 ;Response length VC.RSP: 0 ;Connection ID, message type, and credits MBUFF: .blkb P.MSIZ ;Actual buffer .Sbttl . Data Modifiable by SET Commands IP: dacsr ;Initialization/Polling register address SA: dacsr+2 ;Status/Address register VEC: + + step ;Vector as required for init list BYPOK: 0 ;Zero to allow BYPASS SPFUN, non-zero to disallow it. SC03: 0 ;Make it x.gus to GET STATUS with SC03 - 0 for others .if ne tsx$p TSXVEC: da$vec ;Vector for TSX checking .endc .Sbttl . Data Settable/Receivable by SPFUNs DATAB: .rad50 /DA / ;Allow for RT-11 5.4 et seq .word 8. PARTAB: .word 0,0, 1,0, 2,0, 3,0, 4,0, 5,0 ;Unit,partition JOBTAB: .word 6,0, 7,0 ;Alterable units. .if ne TSX$P .if eq SPLIT .rept ts$job .word 6,0, 7,0 .endr .endc .endc taboff = .-partab ;Partition table offsets of.unit=0 ; unit of.part=2 ; partition of.offs=taboff ; offset of.size=taboff+2 ; size .if ne SPLIT ;If a split disc, put in offset and size values. .word 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 .endc .iif gt <.-2000-dastrt> .error ;Must be in first 2 blocks for /d STATS: ;STATISTICS TABLE s.ver: da.ver ;Version number, for making sure we know format s.ini: 0 ;# of INITs performed successfully s.inir: 0 ;# of INIT retries s.init: 0 ;# of INIT timeouts s.bldy: 0 ;# of bloody disasters (ie SA negative) s.avl: 0 ;# of AVAIL errors s.ior: 0 ;# of I/O retries s.ioer: 0 ;# of I/O errors s.linx: 0 ;# of double-linked requests s.stat: 0 ;STATU$ word .if ne tim$it timcnt: 0 ;# of timeout errors s.extra:.blkw timsiz*2 ;IP/SA pairs when timeout error occurs .endc ln.st=.-stats ;Length of stats table .Sbttl . Initialization Data iretry: -1 ;Retry count for initialization ; Used as flag - set to -1 once initialization ; completed, so timeout errors can check for ; initializations which fail to complete. inmask: 0 ;Receives mask as goes from ISTEP1 to ISTEP4 inpntr: 0 ;Pointer within INILST inilst: 0 ;Values required for initialization (set up later) ringad: 0 ;Receives ring address before init 0 GO .Sbttl . Other Data ;[Note - fork blocks put at end of code section to make sure that we don't ; cause any trouble with TSX fork priority determination.] ;Internal Partition Table (set by call to GETPRT): g.unit=0 g.part=2 g.offs=4 g.size=6 GPTAB: ;GETPRT Table - gets 0 ; physical unit 0 ; partition .if ne SPLIT 0 ; offset 0 ; size .endc WCQE: 0 ;Headers for WAITING queue WLQE: 0 CREDNM: 0 ;Credit available from this disc CREDIT: 0 ;Credit available and allowed to be used (1 if serious ; error situation being dealt with). CMDCNT: 0 ;Number of commands in progress BLOCKF: -1 ;>=0 if serious error handling in progress QFLAG: -1 ; -1 if not processing queues at fork level ; 0 if checking queues ; >0 if need to re-check queues (eg after interrupt) ;QTAB Table: This contains the elements currently being processed. The ; address of each element is held in QTAB, with the least significant bit ; (=1) set if it is necessary to send an MSCP command for this element. ; The table is terminated by the value -1. ; The QPOINT pointer is used by the MSCP command dispatcher as a pointer to ; the last accessed location in QTAB. This allows progressive dealing with ; elements in QTAB, as otherwise were one to always start at the bottom, some ; queue elements may never get handled! .iif ndf qnum, qnum=32. QTAB: .blkw qnum ;Allow for a maximum of 32 queued elements. .word -1 ;Terminator for list. QPOINT: 0 ;Pointer within QTAB for command dispatching to DA .Sbttl Driver Code Proper .Enabl lsb ;This code only puts the elements from the header across to the waiting ; queue. All other processing is done at FORK level. Begin: .if ne tsx$p .assume .-begin eq 0 ; Must be here, as self-bloody-modifying code! call tsetup ; Set for TSX special interrupt code .endc nointerrupts mov dacqe,r4 ;;; Get element(s) pointer mov wlqe,r1 ;;; Last element on WAITING queue bne 10$ ;;; WAITING queue has something in it mov r4,wcqe ;;; WAITING queue empty - this becomes current br 20$ ;;; element 10$: mov r4,q$link(r1) ;;; Link to last element 20$: mov r4,r0 ;;; Hold pointer to possibly last element .if ne tim$it ;;; Remember how many requests, if TIM$IT inc (pc)+ ;;; reqno: 0 ;;; .endc ;;; mov q$link(r4),r4 ;;; See if any more beq 30$ ;;; If so, determine which is actually last. inc s.linx ;;; Count double-linked requests br 20$ ;;; 30$: mov r0,wlqe ;;; This is the last one clr dacqe ;;; clr dalqe ;;; interrupts .if ne tim$it mov sp,tflag ;A new request -> tflag non-zero tst tcrt ;See if timout in action beq 40$ ;Isn't savpsw -(sp) .addr #40$,-(sp) .if ne tsx$p tst introu ;May need extra code for TSX beq 35$ ;Don't. mov @#p5,-(sp) ;Do. Stack SP mov #340,-(sp) ; priority mov introu,-(sp) ; and special return address. 35$: .endc jsr r5,@$inptr .word ^c340 .fork fblkt jmp retime ;Get it running. 40$: .endc .br loopn ;Go and check out what's running. .Dsabl lsb .Sbttl Initial Processing at Fork Level .Enabl lsb ;Loop Entry if Not at Fork Level Loopn: inc qflag ;See if already forked beq 20$ ;No - process queues 10$: return ;Yes - he'll test the flag at the end. 20$: savpsw -(sp) ;Simulate an interrupt to get to FORK .addr #10$,-(sp) ; level. .if ne tsx$p tst introu ;May need extra code for TSX beq 25$ ;Don't. mov @#p5,-(sp) ;Do. Stack SP mov #340,-(sp) ; priority mov introu,-(sp) ; and special return address. 25$: .endc jsr r5,@$inptr .word ^c340 br loopf ;Then FORK to continue work. ;Loop Entry from interrupt without QFLAG checking Loopi: inc qflag beq 30$ return 30$: .br loopf ;Loop Entry if Only Require .FORK and have checked (and incremented) QFLAG Loopf: .fork fblk .br loop Loop: bit #isteps,@sa ;Check for bus RESET bne init ;(or can't reboot in RT-11, & SJ dies) tst inmask ;Have we already INITed? beq init ;No. jmp check .Dsabl lsb .Sbttl Initialization of Controller .Enabl lsb INIT:: mov #3,iretry ;3 Initialization Retries allowed. ini: ;(come here after initialization timeout) clr qflag ;Make it appear we're busy in the queue system clr cmdcnt ;No commands running at the moment. .if ne tim$it call retime ;Make sure start with fresh timer .endc .addr #qtab,r0 ;Address of table of elements being processed mov r0,qpoint mov vec,inilst ;Set up vector in initialization sequence ini0: mov #0,@IP ;Commence initialization ;(Can't use MOV SP or PC as low order byte may ;invoke boot or Wombat; can't CLR as on LSI-11 ;causes a READ which -> poll first!) .if eq TSX$P .addr #mring,-(sp) ;Ring address in initialization list mov (sp)+,ringad .iff call mapadr .word map-mring ;Get physical ring address mov r5,ringad mov r4,ringad+2 .addr #mbuff,-(sp) ;Get virtual message buffer address mov (sp)+,mpoint .endc .addr #inilst-2,-(sp) ;Pointer within init list mov (sp)+,inpntr mov #istep1,inmask ;Masks go from 4000 to 40000 mov #inint-next-2,next ;Set up for interrupts 10$: mov @sa,r5 ;Get status beq 10$ ;Give it time to get a value (should be quick) .br inint ININT:: ;Come here on initialization interrupts ;(remove .FORK - V5b - ? timing problem - ; this should be quick code - put it back at ; INI2) mov @sa,r5 bmi inierr ;Whoops - error bic #^c,r5 ;Clear all but STEP bits cmp r5,inmask ; and see that only the one is set bne inierr asl inmask ;For next mask add #2,inpntr ;And next data word mov @inpntr,@sa tst inmask ;If hit the end of the list, bmi ini2 ; go to next exciting episode. return ;Else just continue init .Dsabl lsb Inierr:: inc s.inir ;Counter of INIT retries dec iretry bne ini0 10$: ;In real trouble here: can't init. mov wcqe,r4 ;Get an element from waiting queue beq 30$ ;If none left, fine. nointerrupts ;;; mov q$link(r4),wcqe ;;; bne 20$ ;;; clr wlqe ;;; 20$: interrupts ;;; mov #si.err,r0 ;Return full-scale error to special I/O call spioex call delinb ;De-link back to monitor, bad one. br 10$ 30$: .addr #qtab,r5 ;De-link all elements currently in list 40$: mov (r5),r4 ; of those undergoing processing. cmp r4,#-1 ;End of table beq 60$ bic #1,r4 ;Clear "to be processed" bit beq 50$ call delinb ;Return them with error 50$: clr (r5)+ ;Clear table entry br 40$ 60$: clr inmask ;In case he tries again! return .Sbttl Second Initialization Phase ;The second initialization phase sets up the communications addresses, ; then uses SET CONTROLLER CHARACTERISTICS to ; remove any timeout and get the command credit count. INI2:: .fork fblk ;Put .FORK here (V5b) .if eq TSX$P .addr #mbuff,-(sp) ;Set up buffer ring addresses mov (sp)+,mring clr mring+2 call mset .addr #cbuff,-(sp) mov (sp)+,cring clr cring+2 .iff call mapadr .word map-mbuff ;Set up buffer ring addresses mov r5,mring mov r4,mring+2 call mset call mapadr .word map-cbuff mov r5,cring mov r4,cring+2 .endc clr credit ;Zero count of credits from controller mov #ini3-next-2,next ;Appropriate interrupt address ini2a: call cset movb #op.scc,p.opcd(r3) ;SET CONTROLLER CHARACTERISTICS .if ne to.min mov #to.min*60.,p.htmo(r3) ;(Be surprised if it's ever non-0) .endc $$own=< & 77777 > / 200 ;Want OWN in low-order byte ; (unsigned divide-by-400) movb #$$own,cring+2+1 ;Command ready, don't interrupt bis #own!flag,mring+2 ;Ready for message, do interrupt tst @ip ;Poll return .Sbttl Third Initialization Phase INI3:: .fork fblk bit #st.msk,mbuff+p.sts ;Check for errors in OP.SCC use bne inierr movb vc.rsp,r0 bic #^c377,r0 ;"Message" field should be zero, dec r0 ; so if controller returns it all at ble ini3b ini3a: add r0,credit ; once, use it (Websters, bugger 'em). br ini2a ;[Fixed in later firmware] ini3b: cmp credit,#qnum ;See if have table space for available blos 10$ ; credit. mov #qnum,credit ;If not, use our maximum. 10$: tst crednm ;Has allowed credit been set up? bne 20$ ;Yes: may be processing serious error mov credit,crednm ; otherwise set it up (it shouldn't 20$: ; alter between INITs). mov #rwint-next-2,next ;Set up for R/W interrupts call mset ;Set up for message interrupts bis #own!flag,mring+2 inc s.ini ;Counter of successful initializations mov #-1,iretry ;End of Init - flag. .br check ;Initialization Complete. .Sbttl Check the Elements Currently Undergoing Processing (QTAB) .If eq 1 Check the list of elements entered in the QTAB table: - if completed, return them; - if vacant slot, get new one from waiting queue; - if any is capable of being sent off as an MSCP command, send it. .endc .Enabl lsb check: .addr #qtab,r1 ;Link-offset pointer to qcqe mov #qnum,r2 ;Maximum number of elements allowed nxtele: mov (r1)+,r4 ;Get next element address jmpne tstmsg ;Not unused table entry - see if ; message received for this one. ;Here, have an empty table entry. Queue next one from the WAITING queue. 10$: tst blockf ;See if processing bad error jmppl nxtque ;Yes - add no more to queue. nxtwat: mov wcqe,r4 ;Address of next waiting element jmpeq nxtque ;None. nointerrupts ;;; mov q$link(r4),wcqe ;;;Remove it from WAITING queue bne 20$ ;;;Another element waiting. clr wlqe ;;;No more - clear LAST element also 20$: interrupts clr q$link(r4) ;Make sure no inadvertent links left. .br newone .Dsabl lsb .Sbttl Process a New Element from the Waiting Queue ;At this point ; R1 / QTAB address ; R2 / count within QTAB (QNUM counter) ; R4 / address of queue element .Enabl lsb Newone: .assume x.io eq 0 ;Normal I/O - Q$FUNC code zero movb q$func(r4),r5 ;Check SPFUNs as need this byte beq 40$ ; for coding function of element .if ne tim$it ;Special spfun to fake a disc crash .if ne tsx$p cmpb q$job(r4),#1 ;(for safety under TSX, restrict to bne 10$ ; job 1 - usually CTY) .endc cmpb r5,#sp.fak bne 10$ cmp (r4),#100000 ;Check for this block number. bne 10$ ;(just for safety) call delink jmp timout 10$: .endc ; Check for SPFUN's which can be dealt with without disc access cmpb r5,#sp.sts ;Read statistics table? jmpeq sp$sts cmpb r5,#sp.dat ;DEC partition table? jmpeq sp$dat ; Check for SPFUN's which will require queueing to the disc cmpb r5,#sp.byp ;Bypass? beq .byp cmpb r5,#sp.byo ; - both flavours. beq .byp cmpb r5,#sp.psz ;Partition size? beq .psz cmpb r5,#sp.usz ;Unit size? beq .usz cmpb r5,#sp.pio ;Physical I/O? beq .pio cmpb r5,#sp.pic ;Physical Compare? beq .pic cmpb r5,#sp.cmp ;Compare Data? beq .cmp cmpb r5,#sp.rio ;Special read? beq .rio cmpb r5,#sp.wio ;Special write? beq .wio jmp daerr ;Unknown SPFUN - return it in disgrace ; and look at next waiting element. ; Prepare for non-immediate SPFUN's .wio: neg q$wcnt(r4) ;Special write - negate word count .rio: movb #x.sio,q$func(r4) ; and treat as special read. br 40$ ;Still check for partition overflow. .pio: movb #x.pio,q$func(r4) ;Code for physical I/O br 60$ .pic: movb #x.pic,q$func(r4) ;Physical Data Compare br 60$ .psz: movb #x.psz,q$func(r4) ;Code for partition size br 60$ .usz: movb #x.usz,q$func(r4) ;Code for unit size .assume q$blkn eq 0 tst (r4) ;If block number non-zero, beq 60$ ;special flavour to get .assume x.xsz eq ;size of physical unit. incb q$func(r4) br 60$ .cmp: movb #x.cmp,q$func(r4) ;Code for compare br 40$ ; BYPASS request: various checks are required: ; - are we allowing bypasses? ; - if block number = 1, wants retries etc ; - need to check that we don't exceed out command/response buffer ; sizes .byp: movb #x.byp,q$func(r4) ;Code for bypass tstb bypok ; Allowing bypasses? bne daerr ; No. .assume q$blkn eq 0 ; Check block number: if cmpb (r4),#1 ; 1, means he wants retries etc bne 20$ .assume x.bys eq x.byp+1 incb q$func(r4) 20$: .if ne mmg$t mov @#px,-(sp) ;Set up PAR1/6 to point to user buffer mov q$par(r4),@#px .endc mov q$buff(r4),r0 mov (r0),r3 ;User's response ring size mov 4+p.msiz(r0),r0 ;User's command ring size .if ne mmg$t mov (sp)+,@#px .endc cmp r3,#p.msiz bne daerr cmp r0,#p.csiz bne daerr br 60$ ; Normal or special I/O: check for extending partition boundaries .if eq SPLIT 40$: mov q$wcnt(r4),r3 ;See if he's extending partition bpl 50$ ; boundaries. neg r3 50$: add #377,r3 ;Part blocks -> whole blocks bic #377,r3 swab r3 ;Convert to blocks .assume q$blkn eq 0 add (r4),r3 bcc 60$ bne daerr ;Allow "hidden" block to be read. 60$: br insnew ;Valid element, ready for sending. .iff 40$: call getprt ;If splitting, get size of this mov g.size(r5),r5 ; partition. mov q$wcnt(r4),r3 ;See if he's extending partition bpl 50$ ; boundaries. neg r3 50$: add #377,r3 ;Part blocks -> whole blocks bic #377,r3 swab r3 ;Convert to blocks .assume q$blkn eq 0 add (r4),r3 ;Get ending block for I/O bcc 60$ ;If CC, then not over end. bne daerr ;Allow "hidden" block to be read tst r5 ; iff size = 0. bne daerr 60$: tst r5 ;Check size. beq insnew ;Full partition size - no problem. cmp r3,r5 ;See if excessive bhi daerr br insnew ;Valid element, ready for sending. .endc .Dsabl lsb ;Error in element - send it home and check out the next one. daerr: mov #-1,r0 ;Error code for special I/O call spioex ;If special I/O, return bad vibes call delinb ;Send element home in disgrace jmp nxtwat ;Look at next one in waiting queue. .Sbttl SPFUN's requiring no Disc Access ; Spfun SP.DAT (372) - DEC's Partition Table tabsiz=<2*8.>+2 ;Table size in RT-11 V5.4 (words) .if ne tsx$p tabsiz=tabsiz-4 ;Table size less units 6 & 7 for TSX .endc sp$dat: .addr #datab,r3 ;Address of partition table mov #tabsiz,r5 ;Number of words in partition table mov @#sysptr,r0 ;Check version of RT/TSX add #sysver,r0 cmpb (r0)+,#5 ;If later than version 5 use 5.4 table bhi 20$ ;Later than version 5 blo 10$ ;Earlier than version 5 cmpb (r0),#4 ;Version 5 - see if 5.4 or later bhis 20$ 10$: add #4,r3 ;Earlier than version 5: don't include sub #2,r5 ; ^rDA or count of units. 20$: tst q$wcnt(r4) ;Negative=write, positive=read bpl rpt .if eq TSX$P ;Write partition table - RT-11 30$: call getwrd mov r0,(r3)+ dec r5 bne 30$ jmp nxthom ;Read partition table - RT-11 ;Just head off to DAPUT to send the table .iff ;Write partition table - TSX-plus call jobnum ;If job 1, allow changing of all partitions mov r0,-(sp) ;(also need it later) cmp r0,#1*8. bne 50$ ;Not job 1. 30$: call getwrd mov r0,(r3)+ ;Change units 0 through 5 dec r5 bne 30$ cmp q$wcnt(r4),#100000 ;If 100000, change ALL job units bne 60$ ;R3 now points to unit 6 for job 0. mov @#px,-(sp) ;Set up PAR1/6 to point to user buffer mov q$par(r4),@#px mov #ts$job,r5 40$: mov q$buff(r4),r1 ;Pointer to start of 6/7 values mov (r1)+,(r3)+ ;Unit 6 unit mov (r1)+,(r3)+ ;Unit 6 partition mov (r1)+,(r3)+ ;Unit 7 unit mov (r1)+,(r3)+ ;Unit 7 partition dec r5 bne 40$ mov (sp)+,@#px ;Reset PAR 6 tst (sp)+ ;Didn't use job # again anyway. br 80$ 50$: call getwrd ;Not job 1 - Waste all except units 6 & 7 dec r5 bne 50$ 60$: mov (sp)+,r3 ;Address of table for 6 & 7 for this job .addr #jobtab,r3,add mov #4,r5 70$: call getwrd mov r0,(r3)+ dec r5 bne 70$ 80$: jmp nxthom ;Read partition table - TSX-plus rpt: call putwrd ;Transfer words up to partitions 6 & 7 call jobnum ;Returns in r0 mov r0,r3 ;Address of this job's 6 & 7 table .addr #jobtab,r3,add mov #4,r5 ;4 words left br daput .endc ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Spfun SP.STS (363) - Read Statistics Table sp$sts: mov statu$,s.stat ;Put STATU$ word into error table .addr #stats,r3 mov #ln.st/2,r5 .iif eq tsx$p, rpt: daput: call putwrd jmp nxthom .Sbttl Insert New Element into QTAB Table .Enabl lsb Insnew: mov r4,-(r1) ;Insert into table ; Set its internal parameters ready for action: mov #retry,q$link(r4) ;Retries in link byte. Clear flags. cmpb q$func(r4),#x.psz ;See if a SIZE request of any type blo 10$ ;Yes - require GET STATUS bisb #x.gus,q$link+1(r4) 10$: bisb sc03,q$link+1(r4) ;If set for an SC03 controller, ; always do a GUS or old SC03's can ; utterly (strangely!) crash system. inc (r1)+ ;Set LSB in QTAB so will be sent ; as command. br nxtque ;Check out next table entry ;Table entry non-zero. See if a message has been returned concerning it ; (ie see if q$link+1 has high-order bit set) tstmsg: bit #1,r4 ;If still requires queueing, bne nxtque ; don't try to return it. .assume x.done eq 200 movb q$link+1(r4),r5 ;See what the flags byte says bmi 20$ ;If positive, still waiting for disc nxtque: dec r2 ;Move on to the next queue slot. jmpgt nxtele jmp chkc ;None left here - try for command ; required. ;Negative status byte in q$link+1. Some action has occurred concerning this ; element. Check whether error or not. 20$: cmpb q$func(r4),#x.byp ;Check for bypass BEFORE error check beq 30$ bit #x.gus,r5 ;Check for completed GET UNIT STATUS beq 25$ ; Wasn't. bicb #x.gus,q$link+1(r4) ; Was. Clear that bit, bisb #x.online,q$link+1(r4) ; set the ONLINE REQUIRED bit, bit #x.avail,r5 ; and see if AVAILABLE error. bne 80$ ;AVAILABLE - fine to continue. bit #x.error,r5 ;See if any other error. bne 90$ ; (anything else is not acceptable) .assume x.io lt x.psz .assume x.cmp lt x.psz .assume x.pio lt x.psz ;Oh gawd. But the order is necessary! .assume x.pic lt x.psz .assume x.sio lt x.psz .assume x.byp lt x.psz .assume x.bys lt x.psz .assume x.usz gt x.psz .assume x.xsz gt x.psz cmpb q$func(r4),#x.psz ;If size SPFUN, do ONLINE. Otherwise bhis 80$ ; try I/O directly. bicb #x.online,q$link+1(r4) br 80$ 25$: bitb #x.error,r5 bne 50$ ;Error condition occurred. ;No error: either completed I/O or SPFUN, or ONLINE completed. cmpb q$func(r4),#x.psz ;Partition size SPFUN beq sp$psz .assume x.xsz gt x.usz cmpb q$func(r4),#x.usz ;Unit size SPFUN bhis sp$usz bitb #x.online,r5 ;See if ONLINE or actual I/O beq 30$ ;Not ONLINE - must be I/O bicb #x.online,q$link+1(r4) ;Was ONLINE - clear ONLINE bit bisb #x.ondone,q$link+1(r4) ;Remember have done an ONLINE incb q$link(r4) ;ONLINE error doesn't count: reset br 70$ ; & requeue ;Return after actual I/O 30$: mov #si.ok,r0 ;Hopefully good I/O cmpb q$link(r4),#retry ;See if any retries beq 40$ ;No. mov #si.rty,r0 ;Were. 40$: call spioex ;Set up special first buffer word prn br nxthom ;Return this element OK ;Remove it from table ; and put in another if available. ;ERROR occurred: ; If ONLINE request, prior to V1h, kill this element - do so no longer. ; If I/O request, see if any retries left. If none, kill him. If some, ; then check whether error was AVAILABLE. If so, convert to ONLINE ; request and retry. If not, retry anyway. 50$: tstb q$link(r4) ;Any retries left? beq 90$ ;No - die you dog. decb q$link(r4) ;Decrease his retries. bitb #x.bbr,r5 ;Was error BBR? beq 60$ ;No. mov #si.bbr,r0 ;Yes - tell him with no further ado. br 100$ 60$: bitb #x.avail,r5 ;Was it AVAILABLE? beq 70$ ;No. bitb #x.ondone,r5 ;Previous AVAILABLE performed? bne 90$ ;Yes: shoot the piano player. bisb #x.online,q$link+1(r4) ;Try to get unit on line. inc s.avl ;Count of AVAIL errors br 80$ 70$: inc s.ior ;Count of I/O retries bisb #x.gus,q$link+1(r4) ;Force a GET STATUS on an error. 80$: bicb #,q$link+1(r4) ;Clear error bits inc -2(r1) ;Set so command dispatcher sees it. br nxtque 90$: mov #si.err,r0 100$: call spioex ;Return error word for special I/O call delinb br nxthm1 ;Clear table entry and look for next .Sbttl SPFUNs which require ONLINE command (Size SPFUNs) ; Spun 373 - Return Partition Size sp$psz: call getprt ;Get unit/partition pointer in r5 .if ne SPLIT mov g.size(r5),-(sp) ;Get size from table bne 140$ ;Specified - use it. mov q$wcnt(r4),(sp) .iff mov q$wcnt(r4),-(sp);If low order volume size 0, reduce size by 1 .endc bne 110$ dec (sp) .assume q$blkn eq 0 dec (r4) bmi 120$ ;If -> -1, zero size disc. 110$: .assume q$blkn eq 0 cmpb 2(r5),(r4) ;Check whether this partition exists beq 140$ ;If top partition, use low order size only. blo 130$ 120$: bis #hderr$,@q$csw(r4) ;Error if doesn't exist 130$: mov #177777,(sp) ;If not end partition, 177777 blocks. 140$: .if ne mmg$t call @$ptwrd .iff mov (sp)+,@q$buff(r4) .endc br nxthom ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Spfun SP.USZ (300) - Return Unit Size (2 words) ; (if the block number were non-zero, use one less than that as the physical ; unit to get the size of) sp$usz: cmpb q$func(r4),#x.xsz ;Physical unit only? beq 150$ call getprt ;Get starting partition number .assume q$blkn eq 0 .if ne SPLIT ;If split disc, subtract offset also sub g.offs(r5),q$wcnt(r4) sbc (r4) bcs 145$ .endc sub g.part(r5),(r4) ;Subtract from high-order word bcc 150$ ; effectively ignoring preceding parts 145$: bis #hderr$,@q$csw(r4) ;If none left, non-extant partition. 150$: .if ne mmg$t .assume q$blkn eq 0 mov (r4),-(sp) ;High order word first call @$ptwrd mov q$wcnt(r4),-(sp) ; then low order. (Put here at call @$ptwrd ; interrupt level). .iff mov q$buff(r4),r5 ;Buffer address .assume q$blkn eq 0 mov (r4),(r5)+ mov q$wcnt(r4),(r5) .endc .br nxthom ;------------------------------------------------------------------------- ;Send an element home OK, then check out the next one in the waiting queue. nxthom: call delink ;Release the element nxthm1: clr -2(r1) ;Clear its position in QTAB jmp nxtwat ; and check out the next one waiting. .Dsabl lsb .Sbttl Check for Elements Requiring an MSCP Command to be Sent chkc: tst cring+2 ;See if command ring busy bmi 40$ ;Yes - don't crowd the controller cmp cmdcnt,credit ;Any credit left? bhis 40$ ;No - patience is a virtue. mov #qnum,r1 ;In case none -> loop 10$: cmp @qpoint,#-1 ;See if pointer points to end of table bne 20$ .addr #qtab,r0 ;If so, reset to start. mov r0,qpoint tst blockf ;If serious error, now completed it. bmi 20$ ;(wasn't) mov crednm,credit ;Set back to normal inc qflag ;(so we queue waiting elements) mov #-1,blockf 20$: mov @qpoint,r4 bit #1,r4 ;See if requires command. bne 50$ 30$: add #2,qpoint dec r1 bne 10$ 40$: jmp 200$ ;End of all we can do here. ;Here, we have something requiring a command to be sent 50$: dec @qpoint ;Clear command queueing bit. dec r4 call getprt ;Get unit/partition pointer in R5 call cset ;Set up the command buffer .assume g.unit eq 0 mov (r5),p.unit(r3) ;Always insert unit number. cmpb q$func(r4),#x.xsz ;See if unit size for different unit bne 60$ .assume q$blkn eq 0 mov (r4),r0 ;(checked for non-zero previously) dec r0 ;If block number non-zero, use 1 less mov r0,p.unit(r3) ; as the unit to get the size of. 60$: bitb #x.online!x.gus,q$link+1(r4) ;See if need ONLINE or GUS beq 80$ ;No. ; ONLINE only required mov #op.onl,p.opcd(r3) ;Set up for ONLINE bitb #x.gus,q$link+1(r4) ;See if really GET UNIT STATUS beq 70$ mov #op.gus,p.opcd(r3) ;Get Unit Status. 70$: jmp 190$ 80$: cmpb q$func(r4),#x.byp ;Bypass? beq 90$ cmpb q$func(r4),#x.bys ; or Special Bypass? bne 120$ 90$: ; BYPASS Function .if ne mmg$t mov @#px,-(sp) ;Set up PAR1/6 to point to user buffer mov q$par(r4),@#px .endc mov q$buff(r4),r1 ;User's buffers are RESPONSE then CMD cmp (r1)+,(r1)+ ;Skip p.csiz & flags words mov p.crf+4+p.csiz(r1),p.crf(r1) ;Copy reference word across mov p.crf+2+4+p.csiz(r1),p.crf+2(r1); as we will use our own. add #p.csiz+4,r1 ;Skip to point to his CMD buffer mov #p.csiz/2-2,r2 mov r3,-(sp) 100$: mov (r1)+,(r3)+ dec r2 bne 100$ mov (sp)+,r3 mov q$wcnt(r4),r0 ;Did he specify a buffer in the wcnt? .if eq mmg$t beq 190$ ;No - go and send this command. mov r0,p.buff(r3) ;Yes - put it in the command buffer .iff beq 110$ ;No - restore PAR then send. .if ne tsx$p mov r4,-(sp) call @$rlptr mov (sp)+,r4 bic #160000,r2 ;Offset - clear 3 PAR bits clr r0 ashc #6,r0 ;Shift PAR six places left add r1,r2 ;Double precision add to get adc r0 ; physical address. mov r0,p.buff+2(r3) mov r2,p.buff(r3) .iff mov @#sysptr,r1 ;XM MONITOR: Sorry: this code mov p1ext(r1),r1 ; has not yet been rewritten to call cvaphy(r1) ; take advantage of the AT: mov r2,p.buff(r3) ; handler: use with care!!!! mov r1,p.buff+2(r3) .endc 110$: mov (sp)+,@#px .endc br 190$ ;Non-Bypass I/O 120$: mov q$wcnt(r4),r2 ;Get word count word ready. mov g.part(r5),p.part(r3) ;Insert partition cmpb q$func(r4),#x.cmp ;Compare Data SPFUN? bne 130$ ;No. movb #op.cmp,p.opcd(r3) ;Yes - put in MSCP Compare command br 170$ 130$: cmpb q$func(r4),#x.pic ;Physical Data Compare? beq 140$ cmpb q$func(r4),#x.pio ;Normal I/O or physical I/O? beq 150$ br 160$ ;Not physical. ; PHYSICAL I/O 140$: movb #op.cmp,p.opcd(r3) ;Physical, Data Compare 150$: ;Physical I/O movb r2,p.part(r3) ;Use low-order word count for bic #377,r2 ; high order block number. add g.part(r5),p.part(r3) ;Add in partition value. 160$: ; Normal I/O movb #op.rd,p.opcd(r3) ;Assume read till proved otherwise. tst r2 ;Write? bpl 170$ ;No. neg r2 ;Yes - convert word count to positive .assume op.wr eq op.rd+1 incb p.opcd(r3) ; and insert WRITE opcode. 170$: .if eq mmg$t ;Buffer address mov q$buff(r4),p.buff(r3) .iff .if ne $xlink mov q$umrx(r4),-(sp) ;Don't confuse the issue clr q$umrx(r4) ; with unwanted UMR's. .endc mov r4,r5 add #q$buff,r5 call @$mpptr mov (sp)+,p.buff(r3) mov (sp)+,r0 ash #-4,r0 movb r0,p.buff+2(r3) .if ne $xlink mov (sp)+,q$umrx(r4) .endc .endc cmpb q$func(r4),#x.sio ;If special I/O, skip initial status bne 180$ ; word in buffer. add #2,p.buff(r3) .if ne mmg$t adc p.buff+2(r3) .endc 180$: asl r2 mov r2,p.bcnt(r3) ;Byte count .assume q$blkn eq 0 mov (r4),p.lbn(r3) ;Block number .if ne SPLIT add gptab+g.offs,p.lbn(r3) ;Add in starting offset adc p.lbn+2(r3) ;(may jump partitions) .endc 190$: mov r4,p.crf(r3) ;Element address for Message Interrupt inc cmdcnt ;Another ship launched. bis #own!flag,cring+2 tst @ip 200$: ;(Originally tested @SA here; but ; this can cause Emulex SC03 to die!) dec qflag ;See if due for any re-runs (eg after jmppl loop ; an interrupt). return .Sbttl Move Data to & from User Buffer ; PUTWRD expects an address in R3 and a word count in R5. ; R3 is returned unchanged. putwrd: ;Put word into user's buffer mov r3,-(sp) 10$: .if ne mmg$t mov (r3)+,-(sp) call @$ptwrd .iff mov (r3)+,@q$buff(r4) add #2,q$buff(r4) .endc dec r5 bne 10$ mov (sp)+,r3 return ;GETWRD returns a word in R0. getwrd: ;Get word from user's buffer clr r0 .if ne mmg$t call @$gtbyt bisb (sp)+,r0 call @$gtbyt swab (sp) bisb (sp)+,r0 .iff mov @q$buff(r4),r0 add #2,q$buff(r4) .endc return .Sbttl Return Special I/O Word ;Called by mov #value,r0 ; mov
,r4 ; call spioex ;checks for special I/O on this queue element, and if in use puts the ;value in R0 into the first word of the buffer spioex: cmpb q$func(r4),#x.sio bne 10$ ;Not special I/O .if ne mmg$t mov r0,-(sp) call @$ptwrd .iff mov r0,@q$buff(r4) .endc 10$: return .Sbttl Interrupts ;It appears to be essential to get messages cleared from the message buffer ; as fast as possible to avoid a device time-out problem. ;This problem appears to have been overcome with later controllers. Ignore: return ;(Used for ignoring interrupts) .if ne $xlink Hook:: .word .-dalqe+4 ;Hook for WA handler to communicate .endc ; with DA handler in case DA not ; mapped under TSX. .DRAST da,7,ignore ;Abort entry point not used. ;; No point in checking @sa here: ;; if it can interrupt, it ain't ;; dead! jmp . ;; NEXT = .-2 ;; Plugged with relevant offset for ;; interrupt (LOC-NEXT-2) RWINT:: .assume own eq 100000 ;; tst mring+2 ;; bpl rwmsg ;; Message Interrupt .br rwcmd ;; If not message, assume command. RWCMD:: ;; COMMAND INTERRUPT jmp loopi ;; Check out processing queue prn .Sbttl Message Interrupts .Enabl lsb RWMSG:: ;; MESSAGE INTERRUPT mov mpoint,r4 ;; mov p.crf(r4),r5 ;; Address of element mov p.sts(r4),statu$ ;; Save status of last operation. cmpb q$func(r5),#x.byp ;; Bypass? beq bypmsg ;; bit #st.msk,p.sts(r4) ;; Check for error bne 10$ ;; ; No error on this element. If SIZE SPFUN need to put size across .assume x.psz lt x.usz ;; cmpb q$func(r5),#x.psz ;; blo 50$ ;; Not a size spfun. cmpb p.opcd(r4),#op.end+op.onl ;; If ONLINE command, bne 50$ ;; put size across .assume q$blkn eq 0 ;; (if do it on GUS, foul up Q!) mov p.unsz+2(r4),(r5) ;; High order size mov p.unsz+0(r4),q$wcnt(r5) ;; Low order size br 50$ ; Error on this element. See whether AVAILABLE or not 10$: bisb #x.error,q$link+1(r5) ;; bic #^c,p.sts(r4) ;; Remove subcodes cmpb p.sts(r4),#st.avl ;; See if (basically) available bne 20$ ;; Not. bisb #x.avail,q$link+1(r5) ;; br 50$ ;; 20$: cmp p.sts(r4),#st.cmd ;; See if CMD error - KLUDGE FOR ERROR bne 40$ ;; IN SRQD11-A BOARDS clrb q$link(r5) ;; - IF SO, CAUSE A HORRIBLE ERROR jmp urg ;; WHICH WILL RESET EVERYTHING ;; (and cream his retries!) 40$: bit #ef.bbr,p.sts(r4) ;; See if BBR error beq 45$ ;; bisb #x.bbr,q$link+1(r5) ;; Is BBR - set bit to remember it by br 50$ 45$: cmpb p.sts(r4),#st.ofl ;; Offline? beq 50$ ;; Yes - retry for GUS (? removable). cmpb p.sts(r4),#st.cmp ;; Comparison error? bne 50$ ;; 47$: clrb q$link(r5) ;; Yes - don't retry on comparisons .br 50$ ;; or offline. ; Final common pathway for message elements 50$: cmpb q$func(r5),#x.bys ;; Special Bypass? beq bypmsg ;; Yes - copy MSCP Buffer mesdon: bisb #x.done,q$link+1(r5) ;; Set DONE bit dec cmdcnt ;; Room for another command bis #own!flag,mring+2 ;; tst @ip ;; Poll to return message element jmp loopi ;; .Dsabl lsb .Sbttl Bypass - Copy Buffer ;Copy response ring: bypmsg: .if ne mmg$t mov @#px,-(sp) mov q$par(r5),@#px .endc mov r2,-(sp) mov r3,-(sp) mov q$buff(r5),r3 ;User's buffer: RESPONSE then CMD add #10,r3 ;Skip header & reference words cmp (r4)+,(r4)+ ;Skip reference words mov #p.msiz/2-2,r2 ;Number of words, less reference words 10$: mov (r4)+,(r3)+ dec r2 bne 10$ mov (sp)+,r3 mov (sp)+,r2 .if ne mmg$t mov (sp)+,@#px .endc br mesdon .Sbttl Timeout Code .if ne tim$it tblk: .word 0,0,0,0,0 ;Lo-ord & hi-ord time, link, jnum, seq .word -1 tcrt: .word 0 ;Completion routine address tflag: .word 0 ;Flag word: set to non-zero on new request. tcode: tst tflag ;Any new requests? bne retime ;Yes - just reset flag & timer. tst reqno ;No. Anyone waiting? beq retime ;No - must just have timed out br timout ;Timed out waiting for action! retime: clr tflag tst tcrt beq 10$ .ctimio tblk ;Just in case ... 10$: .addr #tcode,-(sp) ;Address of timeout completion routine mov (sp)+,tcrt .timio tblk,0,750. ;Up to 15 seconds (V5) return timout: cmp blockf,#1 ;See if handling serious errors already blt 10$ call retime ;Yes - don't add to his headaches! jmp check 10$: nointerrupts cmp timcnt,#timsiz ;;; Don't overshoot. bhis 20$ ;;; mov timcnt,-(sp) ;;; Pointer within that table asl (sp) ;;; 4 bytes/entry asl (sp) ;;; .addr #s.extra,(sp),add ;;; Table of errors mov @ip,@(sp) ;;; add #2,(sp) ;;; mov @sa,@(sp) ;;; tst (sp)+ ;;; 20$: inc timcnt ;;; mov #0,@ip ;;; Reset controller now, before tidy-up mov #ignore-next-2,next ;;; and ignore any interrupts mov (sp),-(sp) ;;; Simulate interrupt clr 2(sp) ;;; (from priority zero) ;;; [(SP) contains call to routine which ;;; jumped to timout. .if ne tsx$p tst introu ;;;May need extra code for TSX beq 25$ ;;;Don't. mov @#p5,-(sp) ;;;Do. Stack SP mov #340,-(sp) ;;; priority mov introu,-(sp) ;;; and special return address. 25$: .endc jsr r5,@$inptr ;;; .inten ^c340 ;;; Maximum priority .fork fblk ;;; Messy, ain't it? call retime ; Reset timer tst iretry ; Init timeout? bmi urgher ; No. Handle normal queued elements. ;Timeout during init phase: queue element to return is still on the ; WAITING queue. dec iretry ;Give him the odd retry ... beq 30$ jmp ini 30$: inc s.init mov wcqe,r4 beq 50$ ;Nothing on waiting queue. Urk! mov q$link(r4),wcqe bne 40$ clr wlqe 40$: call delinb ;Kill him if INIT times out. 50$: return .endc .Sbttl Handling of Extremely Nasty Errors Urg: .fork fblk Urgher: inc s.bldy ;Count of bloody errors. mov #ignore-next-2,next ;Ignore any interrupts here mov #0,@ip mov #1,credit ;Allow only a single command at a time clr cmdcnt ;None active currently inc blockf ;Set flag, and see whether already beq 20$ ; handling serious error. ;Here, serious error encountered whilst already handling serious error mov @qpoint,r4 ;Return in error the current command cmp r4,#-1 ; element (the -1 & 0 shouldn't occur, beq 10$ ; but logic ain't the essence ...) bic #1,r4 beq 10$ bic #1,qpoint ;Belt & braces ... clr @qpoint ;Clear table entry mov #si.err,r0 ;Error code if special I/O call spioex call delinb ;Send it home in disgrace 10$: br 50$ ;Here, serious error, first encounter of the close kind 20$: .addr #qtab,r5 ;Address of QUEUED list mov r5,qpoint ;Reset so work sequentially through Q mov #qnum,r2 30$: mov (r5)+,r4 ;Check this slot beq 40$ ;Empty - ignore. bic #1,r4 ;Clear "command" bit .assume x.done eq 200 bicb #x.ondone,q$link+1(r4) ;Give him another chance to init. tstb q$link+1(r4) ;See if was already processed. bmi 40$ ;Was - leave to normal channels bis #1,-2(r5) ;Wasn't - reset "command" bit 40$: dec r2 bgt 30$ 50$: clr qflag jmp init ;No more - re-init .Sbttl .Sbttl Subroutines .Sbttl . DELINK - Return an Element to the Monitor ;Expects: R4 / pointer to element to be delinked ;Returns: R4 & R5 / altered ;(the monitor does not alter r0 - r3) .word 0,0,0 dalqx: .word 0 dacqx: .word 0 Delinb: inc s.ioer ;Count of I/O errors bis #hderr$,@q$csw(r4) ;Bad return - returns an error Delink:: .addr #dalqx,r5 mov r4,(r5)+ mov r4,(r5) clr q$link(r4) ;Clear flags, retries in link word clrb q$func(r4) ;Clear FUNC or monitor can alter error byte .if ne tsx$p .if ne $xlink clr q$umrx(r4) ;Clear this or TSX gets very muddled! .endc .endc mov r5,r4 mov @#sysptr,r5 .if ne tim$it dec reqno ;Another one gone bpl 10$ ;(Shouldn't happen ... clr reqno ; must track this one down.) 10$: .endc jmp @drfin$(r5) ;Return the element .Sbttl . GETPRT - Return Partition Number ;Expects R4 / pointer to queue element ;Returns R5 / pointer to table with unit number, partition number ; and (if SPLIT) offset and size. Getprt:: mov r3,-(sp) .addr #gptab,r3 mov r3,-(sp) .if ne TSX$P .if ne $xlink tst q$umrx(r4) ;Special unit/partition? beq 5$ ;No - normal request. mov r4,r5 add #q$umrx,r5 ;Yes - point to q$umrx .assume g.unit eq 0 movb (r5)+,(r3)+ ;Get unit clrb (r3)+ .assume g.part eq 2 movb (r5)+,(r3)+ ; partition, but clear .if ne SPLIT clr (r3)+ ; offset & size. clr (r3) .endc br 15$ 5$: .endc movb q$unit(r4),r5 bic #^c<7>,r5 ;Unit number only asl r5 ;Unit * 2 asl r5 ;Unit * 4 (2 words per table entry) .if eq SPLIT cmp r5,#6*4 ;6 or 7? blo 10$ ;No - simple mapping from partab mov r0,-(sp) call jobnum ;Returns in r1 add r0,r5 mov (sp)+,r0 .endc .iff movb q$unit(r4),r5 bic #^c<7>,r5 asl r5 asl r5 ;Unit number * 4 .endc 10$: .addr #partab,r5,add ;Add table base address .assume g.unit eq 0 mov (r5)+,(r3)+ .assume g.part eq 2 mov (r5)+,(r3)+ .if ne SPLIT add #taboff-4,r5 .assume g.offs eq 4 mov (r5)+,(r3)+ .assume g.size eq 6 mov (r5),(r3) .endc 15$: mov (sp)+,r5 ;Return GPTAB address in R5 mov (sp)+,r3 return .Sbttl . TSX High Memory Mapping .If eq 1 Called by call mapadr .word map-address returns r4 / high order physical address r5 / low order physical address .Endc .if ne TSX$P Mapadr: clr r4 ;Clear high order physical address mov pc,r5 map: sub @(sp),r5 ;Get virtual address of desired loc add #2,(sp) ;So we return properly. cmp r5,#120000 ;Mapped? blo 10$ ;No - no problems. mov r5,-(sp) ;Yes. Hold virtual address mov @#p5,r5 ;Get PAR5 value ashc #6,r4 ;Convert to physical address bic #160000,(sp) ;Remove offset from virtual address add (sp)+,r5 ;Add to physical address offset adc r4 ;(Need high order bits right-justified) 10$: return .Sbttl . TSX Job Number Determination ;Expects CQE pointer in R4 ;Returns job number in R0, * 8. ; CS if > ts$job Jobnum:: movb q$jnum(r4),r0 ;Job & unit numbers bic #^c370,r0 ;Job number * 8 cmp r0,#8.*31. bne 10$ ;If not 31., no problems. clr r0 bisb q$job(r4),r0 ;Otherwise get it from other job location cmp r0,#ts$job bhi 20$ asl r0 asl r0 asl r0 ;Want it * 8. 10$: tst (pc)+ 20$: sec return .Sbttl TSX Initialization tsetup: clr introu ;TSX has extra code at interrupt for mapped mov @tsxvec,r4 ; handlers, where it stacks p6, #340, and cmp 16(r4),#12746 ; what appears to be a special return address. bne 10$ ; This return address is in a "MOV #xxx,-(sp)" mov 20(r4),introu ; instruction at where the vector points + 16 10$: mov #401,begin ;So we don't have to do this again. return introu: 0 .endc .Sbttl . Buffer Initialization .enabl lsb ;These routines return the buffer address in R3, suitable for use with ; the MSCP offsets defined at the end of this file. Mset:: .addr #mesage,r3 ;Address of text portion of message buffer mov #p.msiz,(r3)+ ;Length available for messages clr (r3)+ ;Zero for connection ID, message type etc return ;Buffer gets cleared by device. Cset:: mov r5,-(sp) .addr #comand,r3 ;Address of text portion of command buffer mov #p.csiz/2,r5 ;Number of words in text portion of buffer mov #p.csiz,(r3)+ ;Length of command clr (r3)+ ;Zero for connection ID, message type etc mov r3,-(sp) ;Actually points to text area of buffer now 20$: clr (r3)+ ;Zero out buffer dec r5 bne 20$ mov (sp)+,r3 ;Return buffer address in r3. mov (sp)+,r5 return .dsabl lsb .Sbttl .Sbttl Fork Block - Keep at End of Code to Stop TSX Priority Confusion. fblk: .blkw 4 .if ne tim$it fblkt: .blkw 4 .endc .Sbttl .Sbttl ------------------------------- .Sbttl .Sbttl Bootstrap Driver .psect dadvr .psect junk ;Used to force an even block boundary for boot linking .psect daboot ; (necessary for SET UNIT & PART to work properly) .Drbot da,boot1,read,control= . = daboot+40 boot1: mov #10000,sp ;Hard boot code - gets wiped out early. mov r0,-(sp) ;Boot device unit mov r0,@#b$devu ;Put here now for READ, but dies on 1st read mov #2,r0 ;Read from block 0 mov #<4*400>,r1 ;4 blocks mov #1000,r2 ;into loc 1000 call read ;Read the rest of the boot in mov (sp)+,@#b$devu ;Now replace the boot device mov #b$dnam,@#b$devn ;Put in the device name jmp @#boot-daboot . = daboot+120 read: asl r1 ;Words to bytes. asr #1 bcc read1 binit: dec (pc)+ ;Out of retries? bretry: .word 3 ;Hard-wire the retries. blt bioelk ;Um - looks like it. bi: mov bdacsr,r5 mov r4,(r5)+ mov #istep1,r3 mov #binlst-daboot,r4 1$: tst @r5 bmi binit bit @r5,r3 beq 1$ mov (r4)+,@r5 asl r3 bpl 1$ cmp (r4)+,(r4)+ mov r4,bcring mov r4,bmring jsr r0,bgtbuf .word op.onl call bdoio .br read1 READ1: jsr r0,bgtbuf .word op.rd .if ne SPLIT add botoff-4(r5),r0 .endc mov r0,p.lbn(r4) mov r1,p.bcnt(r4) mov r2,p.buff(r4) .br bdoio .Enabl lsb bdoio: mov (pc)+,r3 bdacsr: .word da$csr 1$: mov #bcring+2-daboot,r4 mov #own,@r4 mov (r3)+,r5 2$: tst @r3 bne 4$ tst @r4 bmi 2$ tst -(r4) mov #own,-(r4) 3$: tst @r3 bne 4$ tst @r4 bmi 3$ tstb bbuff+p.sts bioelk: bne bioerr return 4$: tst (sp)+ br binit .Dsabl lsb Bgtbuf: mov #bbuff-daboot+p.msiz,r4 1$: clr -(r4) cmp r4,#bbuff-daboot bhi 1$ mov #p.msiz,-4(r4) mov @#b$devu,r5 ;Translate logical unit asl r5 ; to physical unit asl r5 ; and partition. add #bpartb-daboot,r5 ;Table address + <4 * offset> mov (r5)+,p.unit(r4) ;Unit cmp (r0),#op.rd ;Only put in partition if bne 2$ ; a READ command (not for ONLINE) mov (r5)+,p.part(r4) ;(Goes in high-order block number) 2$: mov (r0)+,p.opcd(r4) rts r0 .Sbttl Bootstrap Impure Area Binlst: .byte 0 .byte 0*10+0+step .word bmring-daboot .word 0 .word go Bln: .word 0 Bvc: .word 0 Bbuff: .blkb p.msiz .word 0,0 Bmring: .word 0,0 Bcring: .word 0,0 .Sbttl Bootstrap Partition Table bpartb: .word 0,0, 1,0, 2,0, 3,0, 4,0, 5,0, 6,0, 7,0 .Sbttl Fetch/Release/Load/Unload Code ;Put here for easy access to BDACSR once: mov bdacsr,r0 mov #0,(r0)+ 10$: tst (r0) beq 10$ return .assume . le daboot+576 .Sbttl Bootstrap Primary Routine . = daboot+576 Boot: mov #read-daboot,@#b$read;Change the address of the boot read code mov #b$dnam,@#b$devn ;Insert boot device name bootnm = .-4 clr r0 ;Look in the boot code for this one! jmp @#b$boot .if ne SPLIT botoff=.-bpartb .word 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 .endc .Sbttl Handler Termination .drend da .Sbttl Force boot code to be linked at start of disc block for SETs .save .psect dadvr .if ne < <.-dastrt> & 777 > $$$ = 1000 - < <.-dastrt> & 777 > .psect junk .blkb $$$ .endc .restore .Sbttl .Sbttl ---------------------------- .Sbttl .Sbttl PROGRAMME PORTION .Sbttl ***************** .nlist bex .psect program .mcall .sreset,.spfun,.print,.exit,.savestatus,.reopen,.purge,.csigen,.wait .mcall .csispc,.dstatus,.gval,.rctrlo,.gtlin,.writw,.settop,.readw,.rctrlo .mcall .gtjb,.serr ;Asect Definitions userrb=53 ;User Error Byte error$=4 ;Value for normal error jsw=44 ;Job Status Word ttlc$=40000 ;Allow Lower Case ovly$=1000 ;Programme is overlayed .asect .=jsw ovly$ ;Make it overlayed for /D switch .psect program ;Other Definitions cr=15 lf=12 tab=11 tabsiz=<2*8.>+2 ;Words in SPFUN 372 (partition) table .Sbttl Macro Definitions .macro type string ;Convenient form of .PRINT request .save .psect mesage con $$$=. .ascii string .restore mov r0,-(sp) .print #$$$ mov (sp)+,r0 .endm type .macro rtype string ;ibid, with .RCTRLO .save .psect mesage con $$$=. .ascii string .restore mov r0,-(sp) .rctrlo .print #$$$ mov (sp)+,r0 .endm rtype .macro typex string ;Form of TYPE which does not save r0 .save .psect mesage con $$$=. .ascii string .restore .print #$$$ .endm typex .macro rtypex string ;ibid, with .RCTRLO .save .psect mesage con $$$=. .ascii string .restore .rctrlo .print #$$$ .endm rtypex .macro tx string ;To extend TYPE strings .save .psect mesage con .ascii string .restore .endm tx .macro movs string,register ;Get address of string .save .psect mesage con $$$=. .ascii string .restore mov #$$$,register .endm movs .macro x string ;To make asciz text readable! .ascii #'string'# .endm x .macro .error routine,?tag ;For jumping to error routines if carry set bcc tag jmp routine tag: .endm .error .macro gtlin prompt,buffer .save .psect mesage con $$$=. .ascii prompt .restore .gtlin buffer,#$$$ .endm gtlin .Sbttl Data Area stack: 0 ;To save it in. area: .blkw 7 ;General EMT Area defext: .rad50 / / ;For CSI requests limit: .limit hlimit: 0 ;For new high limit datblk: .blkw 5 ;Up to 5 values for /D response tsxloc: 0 ;Negative if running under TSX-plus rttyp2: 0 ;Non-zero if running under >= 5.4 RT ptab: 0 ;Pointer to start of data in PTABLE ntab: 0 ;Pointer to start of data in NTABLE ptable: .blkw tabsiz+<30.*2> ;Partition table + error table ntable: .blkw tabsiz ;New partition table line: .blkb 100. ;Input line saves: .blkw 5 ;Savestatus area s.csw=0 ; Channel status s.sblk=2 ; Starting block s.leng=4 ; Length s.hibl=6 ; Highest block written s.reqs=10 ; Pending requests (byte) s.devu=11 ; Device unit (byte) size: .blkw 2 ;For unit size jobloc: .blkw 8. ;For job number ;Data area for /A switch hvstab: .blkb 8. ;Non-zero if have done logical unit asize: .blkw 2 ;For unit size aleft: .blkw 2 ;For number of blocks left on unit anext: .blkw 2 ;For starting block for next logical u swt.a: 0 ;Non-zero if /A specified swt.b: 0 ;Non-zero if /B specified (gets value) swt.c: 0 ;Non-zero if /C specified swt.d: 0 ;Non-zero if /D specified swt.e: 0 ;Non-zero if /E specified swt.g: 0 ;Gets value of /G swt.l: 0 ;Non-zero if /L specified swt.p: 0 ;Non-zero if /P specified swt.s: 0 ;Non-zero if /S specified swt.u: 0 ;Non-zero if /U specified swt.v: 0 ;Non-zero if /V specified swt.x: 0 ;Non-zero if /X specified swt.w: 0 ;Non-zero if /W specified swt.2: 0 ;Non-zero if /2 specified val.c: 0 ;Value if /C specified val.p: 0 ;Value if /P specified val.u: 0 ;Value if /U specified unit.s: 0 ;Unit to size if value on /S unit.u: 0 ;Unit in use during /A cycle outfil: .blkw 3*5 ;CSISPC block, output files infil: .blkw 6*4 ;ibid, input files .Sbttl Get Command .Enabl lsb Badrun:: bisb #error$,@#userrb ;Error which will show on command file Rerun:: mov stack,sp tst swt.x ;See if /X specified. If so, die. beq start .exit Start:: .serr .rctrlo ;(No .sreset as need overlay channel) mov sp,stack ;Save for reruns bis #ttlc$,@#jsw ;Who wants upper case anyhow? .gval #area,#sysgen mov r0,tsxloc .gtjb #area,#jobloc ;Get job number also clr rttyp2 ;Zero unless > RT 5.4 mov #ptable,ptab ;If < 5.4, no additional table words mov #ntable,ntab .gval #area,#sysver ;System version number cmpb r0,#5 blo 20$ bhi 10$ .assume sysupd eq sysver+1 ;Don't give RT11 a GVAL with a byte swab r0 ; address, or get trap to 4! cmpb r0,#4 blo 20$ 10$: dec rttyp2 ;Under RT 5.4 or greater mov #ptable+4,ptab ;Data starts after additional words mov #ntable+4,ntab 20$: .csigen limit+2,#defext,,#line mov limit+2,hlimit tstb line bne 40$ .print #vermes .if ne SPLIT .print #splmes .endc br start 40$: .csispc #outfil,#defext,#line ;Get rad50 names .Sbttl Process Switches clr swt.a ;Clear switch locations clr swt.b clr swt.c clr swt.d clr swt.e clr swt.g clr swt.l clr swt.p clr swt.s clr swt.u clr swt.v clr swt.w clr swt.x clr swt.2 clr val.c clr val.p clr val.u mov (sp)+,r5 ;# of switches jmpeq endswt ;None specified Nexswt: dec r5 ;Any left? jmpmi endswt ;No. clr r2 ;In case no value. mov (sp)+,r1 ;b15 set if value specified, b14-8 file on ; which switch specified, b7-0 ascii value bpl 50$ ; of switch. mov (sp)+,r2 ;Value 50$: cmpb r1,#'z bhi 55$ cmpb r1,#'a blo 55$ bic #40,r1 ;Lower case = upper case 55$: .if ne SPLIT cmpb r1,#'A ;/A - automatic adjustment of unit offsets bne 60$ inc swt.a br nexswt .endc 60$: cmpb r1,#'B ;/B:ON:OFF - Allow/disallow bypass bne 90$ inc swt.b ;In case /B alone tstb r1 bpl 80$ cmp r2,#^rON beq 70$ cmp r2,#^rOFF jmpne illval 70$: mov r2,swt.b 80$: br nexswt 90$: cmpb r1,#'C ;/C - change unit (6 or 7) jmpne 130$ .if ne SPLIT rtype <#?DA-E-/C cannot be used on handler built for split discs#<0>> jmp badrun .iff tst r1 bmi 100$ rtype <#?DA-E-/C requires a value (6 or 7 only if TSX unless job 1)#<0>> jmp badrun .endc 100$: cmp limit+2,hlimit bne 105$ rtype <#?DA-E-/C requires handler to be LOADed#<0>> jmp badrun 105$: cmp r2,#7 blos 110$ rtype <#?DA-E-/C out of range 0 through 7#<0>> jmp badrun 110$: cmp r2,#6 bhis 120$ tst tsxloc bpl 120$ ;RT may change anything cmp jobloc,#1 ;So may job 1 under TSX beq 120$ rtype <#?DA-E-/C may only change units 6 or 7 under TSX-plus#<0>> jmp badrun 120$: tst swt.c jmpne only1 inc swt.c mov r2,val.c br nexswt 130$: cmpb r1,#'D ;/D Use disc file, not handler in memory bne 140$ inc swt.d jmp nexswt 140$: cmpb r1,#'E ;/E - list error table bne 150$ inc swt.e jmp nexswt 150$: cmpb r1,#'H ;/H Help bne 160$ .print #vermes .print #hmes jmp rerun 160$: cmpb r1,#'G ;/G - /G:ON:OFF for GUS with every request bne 165$ ; (ON for SC03 controllers) tst r1 bpl 162$ cmp r2,#^rON beq 161$ cmp r2,#^rOFF bne 162$ 161$: mov r2,swt.g jmp nexswt 162$: rtype <#?DA-E-/G must be /G:ON or /G:OFF#<0>> jmp badrun 165$: cmpb r1,#'L ;/L - list partition table bne 170$ inc swt.l jmp nexswt 170$: cmpb r1,#'P ;/P:n Partition for /C bne 190$ tst r1 bmi 180$ rtype <#?DA-E-/P must have a value specified#<0>> jmp badrun 180$: tst swt.p jmpne only1 inc swt.p mov r2,val.p jmp nexswt 190$: cmpb r1,#'S ;/S - size the device bne 200$ clr unit.s inc swt.s tst r1 ;Value? jmppl nexswt ;No. inc r2 ;Yes - use it as the block number mov r2,unit.s jmp nexswt 200$: cmpb r1,#'U ;/U:n Unit for /C bne 220$ tst r1 bmi 210$ rtype <#?DA-E-/U must have a value specified#<0>> jmp badrun 210$: tst swt.u bne only1 inc swt.u mov r2,val.u jmp nexswt 220$: cmpb r1,#'V ;/V Modify vector & CSR in disc file bne 230$ inc swt.v jmp nexswt 230$: cmpb r1,#'W ;/W - change whole system's unit 6 & 7 bne 240$ ; if using /C inc swt.w jmp nexswt 240$: cmpb r1,#'X ;/X - exit after this command bne 250$ inc swt.x jmp nexswt 250$: cmpb r1,#'Z ;/Z Cause a pseudo disc crash bne 260$ jmp diudog 260$: .if ne SPLIT cmpb r1,#'2 ;/2 - use double-precision partition/offset bne 270$ inc swt.2 jmp nexswt 270$: .endc illswt:: rtype <#?DA-E-Illegal switch /#<200>> .ttyou r1 type <#. For help type /H#<0>> jmp badrun only1: rtype <#?DA-E-Only one of any of /C, /P, or /U on any 1 line#<0>> jmp badrun illval: rtype <#?DA-E-/B must be either /B:ON or /B:OFF#> jmp badrun .Dsabl lsb .Enabl lsb .Sbttl Act on Switch Settings Endswt: .if ne SPLIT tst swt.a ;Autoset? bne 5$ ; Yes. .endc tst swt.d ;Does he want to use the disc file itself? beq 10$ ;No. 5$: jmp disset ;Yes - separate portion. 10$: tst swt.v ;/V only legal with /D jmpne bad.v tst swt.s ;Does he want the size? beq 30$ call telsiz 30$: tst swt.e ;Was /E specified? beq 40$ call typerr ;Yes - type the error table 40$: tst swt.c ;Was /C specified? beq 50$ call chgtab ;Yes - go and change the table clr swt.p ;Have used these! clr swt.u 50$: tst swt.g beq 56$ clr sc03 cmp swt.g,#^rON bne 55$ mov #x.gus,sc03 55$: .writw #area,#17,#1000,#1000,#1 ;2 blocks starting at location 1000 bcc 56$ rtype <#?DA-E-I/O Error on Handler Block 1 or 2#<0>> jmp badrun 56$: tst swt.l ;Was /L specified? beq 60$ call typtab ;Yes - go and type the table 60$: tst swt.p ;See if naked /P or /U beq 70$ rtype <#?DA-E-/P requires /C to be of any use#<0>> br 100$ 70$: tst swt.u beq 80$ rtype <#?DA-E-/U requires /C to be of any use#<0>> br 100$ 80$: mov swt.b,r0 ;Was /B specified? beq 100$ call bypset bcc 110$ ;If carry clear, no problems. 100$: jmp badrun 110$: jmp rerun .Dsabl lsb .Sbttl HELP Text .psect mesage con hmes: .ascii x .if ne SPLIT x .endc x x x x x x < or OFF: only as necessary (later controllers, and faster!)> .if eq SPLIT x x < TSX unless job 1) used with one or both of the following two> x < switches:> x x .ascii .endc x x x < (may only be used with /L, /V and /X -> x < if used with /V allows alteration of CSR & vector.> x .if eq SPLIT x .if ne tim$it .endc x .if ne SPLIT x .endc .endc .byte 0 .if ne SPLIT splmes: .ascii /(built to cope with splitting of disc partitions)/<0> .endc .psect program .Sbttl Diudog - Cause a false disc timeout. Diudog: .spfun #area,#3,#sp.fak,#0,#0,#100000 jmp start .Sbttl Bypass Allowing/Disallowing .Enabl lsb bypset: tst swt.d ;Check - must have /D or /A present. bne 5$ tst swt.a beq 35$ 5$: cmp r0,#^rON ;/B:ON? bne 10$ clr bypok br 20$ 10$: cmp r0,#^rOFF ;/B:OFF? bne 30$ ;(Neither off nor on) mov #1,bypok 20$: .assume bypok-dastrt lt 1000 .writw #area,#17,#1000,#400,#1 bcc 50$ rtype <#?DA-E-I/O Error on handler block 1#<0>> br 40$ 30$: rtype <#?DA-E-/B must be either /B:ON or /B:OFF#<0>> br 40$ 35$: rtype <#?DA-E-/B must be associated with /D#> .if ne SPLIT tx <# or /A#> .endc tx <<0>> 40$: sec 50$: return .Dsabl lsb .Sbttl Get & Check Details of DA: .Enabl lsb Chkda:: .wait #3 bcs 10$ .dstatus #area,#infil ;Gets status thereof. bcs 10$ bic #^c177,area cmp area,#50 ;Make sure it's a DU beq 20$ 10$: rtype <#?DA-E-DU class device not specified#<0>> jmp badrun 20$: .spfun #area,#3,#sp.sts,#ptable,#0,#0 .error er.sts cmp ptable,#da.ver beq 40$ rtype > mov ptable,r0 call w8t type > mov #da.ver,r0 call w8t type > jmp badrun 40$: .spfun #area,#3,#sp.dat,#ptable,#0,#0 ;Read partition table .error er.rpt .savest #area,#3,#saves .error er.sav .reopen #area,#3,#saves ;If this prangs, HELP! .error er.reo mov #ptable,r0 ;Take a copy of the partition mov #ntable,r1 ; table to play with. mov #tabsiz,r2 80$: mov (r0)+,(r1)+ dec r2 bne 80$ return .Dsabl lsb .Sbttl List Current Partition Table .Enabl lsb Typtab: call chkda ;Check bona fides rtype <0>> mov ptab,r1 br 10$ Typtad: mov #partab,r1 10$: clr r2 call typt1 return Typt1: rtype > type > 20$: type > mov r2,r0 call w8t type > mov (r1)+,r0 call wdt type > mov (r1)+,r0 call wdt call tcrlf inc r2 cmp r2,#7 blos 20$ return .Dsabl lsb .Sbttl Type Partition Table (SPLIT version) .if ne SPLIT .Enabl lsb Typtds: mov #partab,r1 clr r2 tst swt.2 bne 10$ rtype tx > type > br 20$ 10$: rtype tx > type > 20$: type > mov r2,r0 ;Logical Unit call w8t type > mov (r1)+,r0 ;Physical Unit call wdt type > tst swt.2 beq 30$ mov r2,-(sp) ;If /2, will need double-precision mov r3,-(sp) ; output. mov (r1)+,r2 mov taboff-4(r1),r3 call wd210t mov (sp)+,r3 mov (sp)+,r2 br 40$ 30$: mov (r1)+,r0 ;Partition call wdt type > mov taboff-4(r1),r0 ;Offset call wd6t 40$: type > mov taboff-2(r1),r0 ;Size call wd6t call tcrlf inc r2 cmp r2,#7 blos 20$ return .Dsabl lsb .endc .Sbttl List Error Table .Enabl lsb Typerr:: call chkda ;Check bona fides .spfun #area,#3,#sp.sts,#ptable,#0,#0 bcc 10$ rtype > jmp badrun 10$: mov #ptable,r1 rtype > mov (r1)+,r0 call w8t call tcrlf type > call type1 type > call type1 type > call type1 type > call type1 type > call type1 type <#Number of I/O retries: #<200>> call type1 type <#Number of I/O errors: #<200>> call type1 type <|# double-linked requests: |<200>> call type1 type <#Value of STATU$ word: #<200>> mov (r1)+,r0 call w8t call tcrlf mov (r1),r5 bmi 30$ type <#Number of Timeout errors: #<200>> call type1 cmp r5,#timsiz blos 20$ ;Limit reporting to number of locations mov #timsiz,r5 ; allocated for table. 20$: tst r5 beq 30$ type <#IP = #<200>> mov (r1)+,r0 call w8t type <#, SA = #<200>> mov (r1)+,r0 call w8t call tcrlf dec r5 br 20$ 30$: return type1: mov (r1)+,r0 call wdt jmp tcrlf .Dsabl lsb .Sbttl Size the Device .Enabl lsb Telsiz:: call chkda .spfun #area,#3,#sp.usz,#size,#0,unit.s ;Get its size. If prangs, .error er.siz ; assume invalid unit. type > tst unit.s bne 5$ type > 5$: type > mov size,r2 mov size+2,r3 call wd2t type <<0>> mov size,r0 ;Number of partitions = 65535 blocks beq 10$ ;None - so don't tell him anything. call wdt type > 10$: mov size+2,r1 ;Remainder beq 20$ type > mov r1,r0 call wdt type > 20$: jmp tcrlf .Dsabl lsb .Sbttl Change Partition Table .Enabl lsb Chgtab:: mov swt.u,r0 ;See that at least /U or /P specified bis swt.p,r0 bne 10$ rtype <#?DA-E-/C requires /P &/or /U to effect a change#<0>> jmp badrun 10$: call chkda mov val.c,r2 ;Get pointer into ptable asl r2 asl r2 mov r2,r3 add ptab,r2 ;Partition table add ntab,r3 ;Table for checking tst swt.u bne 20$ mov (r2),val.u 20$: tst swt.p bne 30$ mov 2(r2),val.p 30$: mov val.u,(r3)+ mov val.p,(r3)+ .purge #1 ;Get a spare channel movb val.c,saves+s.devu ; accessible to DA6 or DA7 .reopen #area,#1,#saves .error er.reo mov swt.w,r1 ;See if wants to change all 6 & 7 beq 35$ mov #100001,r1 35$: dec r1 ;0 becomes -1 = standard write. .spfun #area,#3,#sp.dat,#ntable,r1,#0 ;Re-write partition table .error er.wpt .spfun #area,#1,#sp.psz,#size,#0,#0 ;Get its size. If prangs, bcc 40$ ; invalid. .spfun #area,#3,#sp.dat,#ptable,#-1,#0 ;Go back to original for the .error er.wpt ; moment, as have pranged. rtype <#?DA-E-Non-existent unit/partition specified: unit #<200>> mov val.u,r0 call wdt type > mov val.p,r0 call wdt call tcrlf jmp badrun 40$: return .Dsabl lsb .Sbttl List/Modify Disc Handler .Enabl lsb Disset:: .if ne SPLIT tst swt.a ;See if wants auto-partition bne 10$ ;If so, may specify a file. .endc .wait #3 ;Make sure no file specified bcs 10$ rtype <#?DA-E-/D must not have a device specified,#> tx <# as it works on the actual handler being#> tx <# run as a programme.#<0>> jmp badrun 10$: mov swt.e,r0 ;Check for switch incompatibilities bis swt.c,r0 bis swt.p,r0 bis swt.u,r0 bis swt.s,r0 beq 20$ rtype <#?DA-E-/E, /C, /P, /U, & /S may not be used with /D or /A#<0>> jmp badrun 20$: tst swt.a ;If autochange, do it first bne 40$ tst swt.l ;See if he wants a list of present settings beq 40$ ;No - he wants to change them. Tellhim: .rctrlo .print #vermes .if ne SPLIT .print #splmes .endc type </Details of /<200>> mov bootnm,r0 mov #tloub,r1 mov r1,r2 call w50 call rtrim movb #200,(r1)+ .print r2 type > .if ne tsx$p tx <#Built for TSX-plus#> .iff .if ne mmg$t tx <#Built for RT-11 XM Monitor#> .iff tx <#Built for RT-11 FB/SJ Monitor#> .endc .endc .iif ne tim$it, tx > .iif ne erl$g, tx > tx </Vector: /<200>> mov dastrt,r0 call w8t type > mov ip,r0 call w8t type > tst bypok ;See if BYPASS ON or OFF beq 30$ type > 30$: type <0>> tst sc03 bne 31$ type tx <0>> br 32$ 31$: type tx <#(gets unit status on every I/O request).#<0>> 32$: .if eq SPLIT call typtad .iff call typtds .endc jmp end.d ;He wants to change the table. Tell him how. .psect mesage chvmes: x x .byte 0 chgmes: .ascii .if eq SPLIT x x x x .byte 0 .iff x x x x x .byte 0 chgm2: .ascii x x x x x .byte 0 .endc bypmes: .ascii x x .byte 0 prompt: .ascii /> /<200> ;Prompt for changes. prompb: .ascii /ON or OFF? /<200> ;Prompt for Bypass. .psect program .Sbttl . Change Vector, CSR &/or name 40$: .if ne SPLIT tst swt.d ;Only Autoset? jmpeq autdis ;Yes - don't ask for changes. .endc tst swt.v ;If /V, allow CSR & Vector alteration jmpeq 90$ .print #chvmes mov #tloub,r1 ;Set up in a line so GTLIN works nicely. movs >,r0 call copys mov dastrt,r0 call w8 movs <0>>,r0 call copys mov #line,r1 .gtlin r1,#tloub call r8 tst r0 beq 50$ bit #^c374,r0 ;See if legal vector jmpne badvec mov r0,dastrt ;Put vector in place, .if ne tsx$p mov r0,tsxvec ;Special for TSX also. .endc asr r0 ; and also where required asr r0 ; for soft setting of vector add #<+step>,r0 ; in initialization code. mov r0,vec 50$: mov #tloub,r1 movs >,r0 call copys mov ip,r0 call w8 movs <0>>,r0 call copys mov #line,r1 .gtlin r1,#tloub call r8 tst r0 beq 70$ cmp r0,#160000 jmplo badcsr bit #1,r0 jmpne badcsr mov r0,ip ;Working CSR mov r0,sa ; & SA also add #2,sa mov r0,bdacsr ;Boot CSR mov hlimit,r0 ;Get room to read & write block 0 add #1000,r0 ; as installation CSR is in an mov r0,r1 ; awkward place. Don't just rewrite .settop r0 ; block zero from memory image, as cmp r0,r1 ; it gets rather mucked up once this jmpne noroom ; programme is run. mov hlimit,r1 .readw #area,#17,r1,#400,#0 bcs 60$ mov ip,(r1) ;Installation CSR .writw #area,#17,r1,#400,#0 bcc 70$ 60$: jmp 340$ 70$: type <<0>> mov #tloub,r1 movs >,r0 call copys mov bootnm,r0 call w50 call rtrim movs <0>>,r0 call copys mov #line,r1 .gtlin r1,#tloub call r50 tst r0 beq 80$ mov r0,bootnm 80$: .Sbttl . Change Physical Units & Partitions 90$: .if eq SPLIT .print #chgmes .iff mov #chgmes,r0 tst swt.2 beq 100$ mov #chgm2,r0 100$: .print .endc read.d: mov #line,r1 .gtlin r1,#prompt mov #datblk,r2 ;Temporary logical unit/unit/part/offset/size mov #5,r3 ;Up to 5 values 110$: .if ne SPLIT tst swt.2 beq 120$ cmp r2,#datblk+4 ;Are we up to partition/offset? bne 120$ mov r3,-(sp) mov r2,-(sp) call rd2 mov (sp)+,r0 mov r2,(r0)+ mov r3,(r0)+ mov r0,r2 mov (sp)+,r3 dec r3 br 130$ 120$: .endc call rd ;Read a number (decimal) tstb line jmpeq autdis mov r0,(r2)+ ;Hold it 130$: tstb (r1) ;If at end of line, bne 140$ ; don't skip the delimiter - just read 0. inc r1 140$: dec r3 bne 110$ mov #datblk,r2 ;Get address of data again. tst 10(r2) ;See if size specified. bne 150$ ;Yes - use as is. tst 6(r2) ;Offset? beq 150$ ;No. No problems. mov #-1,r1 ;Yes. Size=0 implies size = 65535-offset sub 6(r2),r1 mov r1,6(r2) 150$: cmp (r2),#7 ;See that logical unit is in range 0-7 jmphi illlun mov (r2)+,r5 asl r5 ;Get logical unit * 4, which is asl r5 ; offset to particular physical unit/partition mov r5,r1 ;Save in case TSX DA6 or DA7 change add #partab,r5 ;Pointer within physical unit/partition table mov (r2)+,(r5)+ ;Physical unit mov (r2)+,(r5)+ ;Partition .if ne SPLIT mov (r2)+,taboff-4(r5) ;Offset mov (r2), taboff-2(r5) ;Size .endc mov r1,r5 ;Now for the boot unit/partition table. add #bpartb,r5 mov #datblk+2,r2 mov (r2)+,(r5)+ ;Physical unit mov (r2)+,(r5)+ ;Partition .if ne SPLIT mov (r2)+,botoff-4(r5) ;Offset mov (r2), botoff-2(r5) ;Size .endc .if ne tsx$p .if eq SPLIT mov #datblk+2,r2 sub #6*4,r1 ;See if unit 6 or 7 being changed. bmi read.d ;No - nothing more to do. mov #ts$job+1,r5 ;Yes - get number of job slots allowed + 1. add #jobtab,r1 ;Point to job table for units 6 & 7. 160$: mov (r2),(r1)+ ;Physical unit mov 2(r2),(r1)+ ;Partition cmp (r1)+,(r1)+ ;Skip other unit (6 or 7, whichever not used) dec r5 bne 160$ .endc .endc br read.d .Sbttl . Automatic Partition Offset Setting Autdis: .if eq SPLIT .br set.b .iff tstb swt.a ;Automatic partition adjustment? jmpeq set.b ;No. ;Clear table of "have done" units mov #7.,r5 170$: clrb hvstab(r5) dec r5 bpl 170$ clr r5 ;Index within PARTAB/HVSTAB br 180$ ;Get next logical unit from PARTAB. nxtunit: inc r5 ;Next unit - cmp r5,#7 ; 7 is maximum. jmphi alcdon tstb hvstab(r5) ;Has it already been allocated? bne nxtunit ;Yes - try for next. 180$: mov r5,r4 ;4 bytes per PARTAB entry .assume ne 4 asl r4 asl r4 add #partab,r4 .assume of.unit eq 0 mov (r4),unit.u ;Unit number for this cycle. ;Get size of unit. If device specified, get it from that; if not, ask him. .wait #3 bcs 190$ .assume of.unit eq 0 .spfun #area,#3,#sp.usz,#size,#0,(r4) bcc 200$ rtype <#Cannot determine size of unit from handler.#<0>> 190$: .assume of.unit eq 0 mov (r4),r0 ;Get size manually from user add #'0,r0 movb r0,sizunt ;Put it into message for .GTLIN mov #line,r1 .gtlin r1,#sizmes call rd2 mov r2,size ;High order mov r3,size+2 ;Low order br 210$ .save .psect mesage con sizmes: .ascii #Size of unit # sizunt: .ascii #x: #<200> .restore 200$: rtype <#Size of unit #<200>> ;Automatic size: tell him. .assume of.unit eq 0 mov (r4),r0 call wdt type <# is #<200>> mov size,r2 ;High order mov size+2,r3 ;Low order call wd2t call tcrlf 210$: clr anext ;Starting block for first one on this unit clr anext+2 mov size,aleft ;Number of blocks left on this unit mov size+2,aleft+2 mov r4,r3 ;Take a copy of PARTAB address mov r5,r2 ; and of HVSTAB index. br 230$ ;Set up and insert values for this logical unit 220$: add #4,r3 ;Move to next PARTAB entry inc r2 ; and next HVSTAB entry cmp r2,#7 ;Last unit is #7 bhi nxtunit cmp (r3),unit.u ;Same as previous unit? bne 220$ 230$: tst aleft ;See if any left in this unit bne 240$ tst aleft+2 bne 240$ mov size,of.part(r3) ;None left - put in impossible inc of.part(r3) ; value so get errors. br 270$ 240$: mov aleft,r0 ;Hold amount left in case exceed it mov aleft+2,r1 mov anext,of.part(r3) ;Starting partition mov anext+2,of.offs(r3) ;Starting offset tst of.size(r3) ;Check size; zero implies 65536 bne 250$ inc anext dec aleft br 260$ 250$: add of.size(r3),anext+2 adc anext sub of.size(r3),aleft+2 sbc aleft 260$: bpl 270$ mov r1,of.size(r3) ;Exceeded available - adjust clr aleft clr aleft+2 270$: incb hvstab(r2) ;Mark this one as DONE. mov r2,r1 asl r1 ;4 bytes per boot table entry. asl r1 add #bpartb,r1 mov of.part(r3),of.part(r1) mov of.offs(r3),botoff(r1) mov of.size(r3),botoff+2(r1) br 220$ Alcdon: .br set.b .endc .Sbttl . Allow/Disallow Bypass set.b: tstb swt.b ;See if /B specified. If not, don't ask. jmpeq writ.d .print #bypmes 280$: type > tst bypok bne 290$ type > br 300$ 290$: type > 300$: mov #line,r1 .gtlin r1,#prompb tstb (r1) beq writ.d ;Null line. movb (r1)+,r0 bic #40,r0 cmpb r0,#'O bne 310$ movb (r1)+,r0 bic #40,r0 cmpb r0,#'N beq 320$ ;ON cmpb r0,#'F beq 330$ ;OF(f) 310$: rtype > br 280$ 320$: clr bypok br writ.d 330$: mov #1,bypok writ.d: ;Re-write portion of handler modified ; - boot block first. .writw #area,#17,#>,#400,# bcs 340$ .writw #area,#17,#1000,#1000,#1 ;2 blocks starting at location 1000 bcc 350$ 340$: rtype <#?DA-E-I/O Error on Handler Block#<0>> jmp badrun 350$: tst swt.l ;Does he want a listing also? jmpne tellhim ;Yes. end.d: jmp rerun .Dsabl lsb .Sbttl Error Routines illlun: rtypex > br .badr badvec: rtypex > br .badr badcsr: rtypex > br .badr bad.v: rtypex <#?DA-E-/V may only be used with /D#<0>> br .badr noroom: rtypex <#?DA-E-Insufficient memory for Handler Block I/O#<0>> br .badr er.sav: rtypex > br .badr er.reo: rtypex > br .badr er.sts: rtypex > br .badr er.rpt: rtypex > br .badr er.wpt: rtypex > br .badr er.siz: rtypex > .badr:: call tcrlf jmp badrun .Sbttl .Sbttl Subroutines for Programme Portion - Taken from CVLLIB .Sbttl --------------------------------- .Sbttl WDT & WD2T - Write a decimal (single or double-precis) number .if eq 1 WDT: Types a single-precision unsigned decimal number from R0. WD2T: Types a double-precision integer in R2 (hi-ord) and R3 (lo-ord). .endc WDT:: mov r2,-(sp) mov r3,-(sp) mov r0,r3 clr r2 call wd2t mov (sp)+,r3 mov (sp)+,r2 return WD2T:: mov r0,-(sp) mov r4,-(sp) clr r4 mov r3,-(sp) mov r2,-(sp) bge 10$ neg r2 ;If negative, make positive! neg r3 sbc r2 bic #100000,r2 ;In case 100000 .ttyou #'- 10$: inc r4 ;Digit counter mov #10.,r0 ;Divisor for DIV21 call div21 mov r0,-(sp) ;Remainder tst r2 bne 10$ ;Stop when zero tst r3 bne 10$ 20$: mov (sp)+,r0 add #'0,r0 .ttyou dec r4 bgt 20$ mov (sp)+,r2 mov (sp)+,r3 mov (sp)+,r4 mov (sp)+,r0 return .Sbttl WD6T - Decimal Number taking 6 positions .if ne SPLIT .if eq 1 WD6T: Types a single-precision unsigned decimal number from R0, in a field 6 characters wide. WD210T: Types a double-precision integer in R2 (hi-ord) and R3 (lo-ord) in a field 10 places wide. WD2PT: Types a double-precision integer in R2 (hi-ord) and R3 (lo-ord), in a field R5 places wide. .endc WD6T:: mov r2,-(sp) mov r3,-(sp) mov r5,-(sp) mov #6,r5 mov r0,r3 clr r2 call wd2pt mov (sp)+,r5 mov (sp)+,r3 mov (sp)+,r2 return WD210T:: mov r5,-(sp) mov #10.,r5 call wd2pt mov (sp)+,r5 return WD2PT:: mov r0,-(sp) mov r1,-(sp) clr r1 ;Leading sign. mov r4,-(sp) clr r4 mov r3,-(sp) mov r2,-(sp) bge 10$ neg r2 ;If negative, make positive! neg r3 sbc r2 bic #100000,r2 ;In case 100000 mov #'-,r1 inc r4 10$: inc r4 ;Digit counter mov #10.,r0 ;Divisor for DIV21 call div21 mov r0,-(sp) ;Remainder tst r2 bne 10$ ;Stop when zero tst r3 bne 10$ 20$: mov r4,r2 25$: cmp r4,r5 ;Make sure at least r5 places taken bhis 30$ inc r4 .ttyou #40 br 25$ 30$: mov r1,r0 ;Was there a sign? beq 40$ .ttyou ;Yes. dec r2 40$: mov (sp)+,r0 add #'0,r0 .ttyou dec r2 bgt 40$ mov (sp)+,r2 mov (sp)+,r3 mov (sp)+,r4 mov (sp)+,r1 mov (sp)+,r0 return .endc .Sbttl RD2 - Read unsigned double-precis decimal number .if eq 1 Reads a double-precision decimal integer from the string pointed to by R1. Overflow is counted as 32-bit overflow, not 31+sign, the result being counted as an unsigned integer. If negative, there has been signed overflow. Overflow is indicated by return with the carry bit set. Should overflow occur, the scan of the input string will be continued to the end of the number. The number is returned in R2 (hi-ord) and R3 (lo-ord). R1 is reset to point to the delimiting character. No other registers are altered. .endc RD2:: MOV R0,-(SP) MOV R5,-(SP) clr r5 ;To track carry. CALL LTRIM ;Ignore leading tabs/spaces CLR R2 CLR R3 10$: MOVB (R1)+,R0 BIC #^C177,R0 SUB #'0,R0 ;Convert next character to numeric value CMP R0,#9. ; and check range BHI 20$ ASL R3 ROL R2 ; * 2 adc r5 mov r2,-(sp) mov r3,-(sp) ASL R3 ROL R2 ; * 4 adc r5 ASL R3 ROL R2 ; * 8 adc r5 add (sp)+,r3 ; * 10 adc r2 adc r5 add (sp)+,r2 adc r5 ADD R0,R3 ADC R2 ;Plus this digit adc r5 beq 10$ mov #1,r5 ;In case he's REALLY keen! br 10$ 20$: DEC R1 ;Point back to delimiting character tst r5 ;Overflow? beq 30$ ;No. sec ;Yes. 30$: MOV (SP)+,R5 MOV (SP)+,R0 RETURN .Sbttl DIV21 - Signed / integer divide .if eq 1 Called by MOV ,R2 MOV ,R3 MOV ,R0 CALL DIV21 Returns R2/hi-order quotient R3/lo-order quotient R0/remainder If divide-by-zero is attempted, the arguments are unaltered, and both C (carry) and V (overflow) are set. Normally carry is clear on return. .endc DIV21:: mov r0,-(sp) ;Quotient gets XOR of signs beq 70$ ;Whoops - don't like this! bgt 10$ neg r0 ;But work internally with positive numbers 10$: bic #77777,(sp) add r2,(sp) mov r2,-(sp) bge 20$ neg r2 neg r3 sbc r2 20$: mov #32.,-(sp) mov r0,-(sp) ;Save on stack for subtractions clr r0 30$: asl r3 rol r2 rol r0 cmp r0,(sp) blo 40$ sub (sp),r0 ;Subtract out as shift numbers across inc r3 ; and mark subtraction in quotient 40$: dec 2(sp) bgt 30$ cmp (sp)+,(sp)+ tst (sp)+ bge 50$ neg r0 ;Remainder gets sign of dividend 50$: tst (sp)+ bge 60$ neg r2 neg r3 sbc r2 60$: .word clc!clv return 70$: tst (sp)+ .word sec!sev ;Set both error flags return .Sbttl TCRLF Teletype routine for VT100 esc=33 TCRLF: ;Print a carriage-return line-feed mov r0,-(sp) .print #nul mov (sp)+,r0 return .psect mesage con nul: .byte 0 .psect program .Sbttl TLOUB - Output Buffer for TT output TLOUB: .blkb 140. ;Allow for a printer line .Sbttl COPYS - Copy String copys: movb (r0)+,(r1)+ bne copys dec r1 return .Sbttl RD - Read an unsigned single-precision decimal number .if eq 1 Called by MOV #string,R1 CALL RD Returns the number in R0, and R1 points to the delimiter of the number. Overflow is counted as 16-bit overflow, not 15+sign, the result being counted as an unsigned integer. If negative, there has been signed overflow. Overflow is indicated by return with the carry bit set. Should overflow occur, the scan of the input string will have been continued to the end of the number. .endc RD:: mov r2,-(sp) mov r3,-(sp) clr r2 ;For overflow detection. clr -(sp) call ltrim clr r0 ;For result 10$: movb (r1)+,r3 ;Next character bic #^c177,r3 sub #'0,r3 cmp r3,#9. bhi 20$ asl r0 ; * 2 adc r2 mov r0,(sp) asl r0 adc r2 ; * 4 asl r0 ; * 8 adc r2 add (sp),r0 ; * 10 adc r2 add r3,r0 adc r2 beq 10$ mov #1,r2 br 10$ 20$: dec r1 ;Point at delimiter tst (sp)+ mov (sp)+,r3 tst r2 ;Any overflow? beq 30$ ;No. sec ;Yes. 30$: mov (sp)+,r2 return ltrim: tstb (r1) ;Trim off all leading spaces and tabs beq 20$ cmpb (r1),#40 beq 10$ cmpb (r1),#tab bne 20$ 10$: inc r1 br ltrim 20$: return .Sbttl W8T & W8 - Write an unsigned octal number W8T:: ;Write number in R0 to terminal mov r1,-(sp) mov #tloub,r1 call w8 movb #200,(r1) .print #tloub mov (sp)+,r1 return W8:: ;Write number in R0 to string mov r0,-(sp) bne 10$ movb #'0,(r1)+ ;Send a zero if it is. br 40$ 10$: clr -(sp) ;Flag for when finished with stack 20$: mov r0,-(sp) clc ;In case negative ror r0 asr r0 asr r0 bne 20$ 30$: mov (sp)+,r0 beq 40$ bic #177770,r0 bis #60,r0 movb r0,(r1)+ br 30$ 40$: mov (sp)+,r0 return .Sbttl R8 - Read an unsigned single-precision octal number R8:: mov r2,-(sp) ;For overflow indication clr r2 call ltrim clr r0 10$: movb (r1)+,-(sp) bic #^c177,(sp) sub #'0,(sp) ;Convert to number cmp (sp),#7 bhi 20$ asl r0 adc r2 asl r0 adc r2 asl r0 adc r2 add (sp)+,r0 adc r2 br 10$ 20$: tst (sp)+ tst r2 beq 30$ sec 30$: mov (sp)+,r2 dec r1 return .Sbttl R50 - Read up to 3 Radix50 Characters into R0 .If eq 1 Reads up to 3 ascii characters from the string pointed to in R1, converting them to radix50 and returning them in R0. Returns CS if NEXT character is not a valid radix50 character (even if three characters have been read), and CC otherwise. R1 is left pointing to the next character. There are three routines here: R50S, R50F and R50. R50 - all radix50 characters including full stop and space. R50S - symbol set, dot OK, space acts as a delimiter. R50F - filename set, dot and space act as delimiters. .Endc .Enabl lsb R50S:: mov r2,-(sp) mov #-1,-(sp) ;Flag for dot/space recognition. br 10$ R50F:: mov r2,-(sp) ;Save 1 ac first as need scrap heap at end mov #1,-(sp) br 10$ R50:: mov r2,-(sp) clr -(sp) 10$: mov r3,-(sp) clr -(sp) ;For result call rnc ;Read next character from R1, converted to R50 bcs 20$ ;Hit a delimiter mov #50*50,r2 call mul11 ;Multiplier in R0, multiplicand in R2, returns mov r3,(sp) ; double-length result in R2,R3 call rnc ;Second character bcs 20$ mov #50,r2 call mul11 add r3,(sp) call rnc ;Third character bcs 20$ add r0,(sp) call rnc ;And an extra just to check whether valid R50 20$: ; coming up next (but don't skip it) dec r1 mov (sp)+,r0 ;Result mov (sp)+,r3 mov (sp)+,r2 ;Scrap flag without affecting carry mov (sp)+,r2 return rnc: movb (r1)+,r0 bic #^c177,r0 call ca50 ;Converts ascii to rad50, returning CS if bcs 30$ ; invalid, and ZS if . or space. bne 30$ tst 6(sp) ;See if he wants dot and space returned OK beq 30$ ;Yes tst r0 ;Otherwise see if just "." is OK beq 25$ ;Space always sets carry here tst 6(sp) blt 30$ ;But get away with dot in R50S 25$: sec ;No - mark them as delimiters 30$: return .dsabl lsb .Sbttl W50 - Write a RADIX50 word to a string .globl div21,c50a .If eq 1 Writes to the string pointed to in R1 the Radix50 word contained in R0. No registers other than R1 are altered. The unused Radix50 code, 35, is typed as an "*". .Endc W50:: mov r0,-(sp) mov r2,-(sp) mov r3,-(sp) clr r2 ;Force unsigned division for first step. mov r0,r3 ;DIV21 expects dividend in R2,R3 divisor in R0 mov #50,r0 call div21 call c50a ; and returns remainder in R0 mov r0,-(sp) mov #50,r0 call div21 ; and quotient in R2 ready for next time. call c50a mov r0,-(sp) mov r3,r0 call c50a movb r0,(r1)+ movb (sp)+,(r1)+ movb (sp)+,(r1)+ mov (sp)+,r3 mov (sp)+,r2 mov (sp)+,r0 return .Sbttl MUL11 - single * single and double * single signed multiply .if eq 1 Called by MOV ,R0 MOV ,R2 CALL MUL11 Returns R2/hi-ord 2/precis result R3/lo-ord 2/precis result CS if result cannot be expressed in a single-precision no. Multiplies two single-precision integers (in R0 and R2) returning a double-precision result in R2 (hi-ord) and R3 (lo-ord). R0 is unaltered. .endc MUL11:: mov r1,-(sp) ;Used to hold overflow from single-precis clr r1 mov r0,-(sp) ;Hold multiplier, and use positive form bge 10$ neg r0 10$: mov r4,-(sp) ;Put double-precis multiplicand in R4,R5 for mov r5,-(sp) ; left-shifting. clr r4 mov r2,r5 bge 20$ dec r4 20$: clr r2 clr r3 br 50$ 30$: asl r5 ;Shift multiplicand to left for next bvc 40$ ; (potential) addition. If overflow, remember inc r1 40$: rol r4 50$: clc ror r0 ;Right-shift multiplier bcc 70$ add r5,r3 bvc 60$ inc r1 ;Again, keep track of overflow on addition 60$: adc r2 add r4,r2 70$: tst r0 bne 30$ mov (sp)+,r5 mov (sp)+,r4 mov (sp)+,r0 bge 80$ neg r2 ;If negative multiplier, negate result neg r3 sbc r2 80$: tst r1 ;Check for overflow into high-order word beq 90$ sec 90$: mov (sp)+,r1 return .Sbttl CA50 - Convert Ascii to Radix50 .If eq 1 Converts a character in R0 from ascii to radix50. The unused radix50 character, 35, is assigned to the character "*". The wildcard "%" character becomes the same as the character ".". Illegal characters cause a CS return. In addition, the characters space and full-stop (invalid within filenames) cause a CC but ZS return. .Endc CA50:: CMP R0,#'z ;Convert lower case to upper case BHI 20$ ;At this end, out of bounds anyway. CMP R0,#'a BLO 10$ SUB #40,R0 10$: CMP R0,#'Z BHI 20$ ;Illegal CMP R0,#'A BHIS 80$ ;Alpha CMP R0,#'9 BHI 20$ ;Illegal CMP R0,#'0 BHIS 70$ ;Numeric CMP R0,#40 ;Space BEQ 40$ CMP R0,#'% ;Wildcard % BEQ 35$ CMP R0,#'* ;Asterisk BEQ 50$ CMP R0,#'$ BEQ 60$ CMP R0,#'. BEQ 30$ 20$: SEC ;Illegal Character - CS return RETURN 30$: MOV #34,R0 ;Full stop CLC SEZ RETURN 35$: SUB #11-40,R0 ;% becomes . 40$: SUB #40-15,R0 ;Space 50$: SUB #15-11,R0 ;* 60$: SUB #11-22,R0 ;$ 70$: SUB #22-100,R0 ;Numeric 80$: SUB #100,R0 ;Alpha CLC ;Note space returns ZS from the subtraction RETURN .Sbttl C50A - Convert Radix50 char to Ascii .If eq 1 Expects a Radix50 character in R0. Returns the corresponding Ascii value in R0, destroying no other registers. The unassigned Radix50 character, 35, is returned as "*", and any illegal value is returned as "?", with CS. .Endc C50A:: cmp #47,r0 ;See that it's within range. bcc 10$ ;==bhis mov #50,r0 ;Return this for error char. with carry set. 10$: movb r50tab(r0),r0 return r50tab: .ascii / ABCDEFGHIJKLMNOPQRSTUVWXYZ$.*0123456789?/ .even .Sbttl RTRIM - Delete trailing spaces & tabs .if eq 1 Expects a pointer to a string in R1. Backs up this pointer to point to before any spaces or tabs. .endc space=40 tab=11 rtr: dec r1 RTRIM:: cmpb -1(r1),#space beq rtr cmpb -1(r1),#tab beq rtr return .Sbttl ------------------------------- .Sbttl .Sbttl MSCP Protocol Definitions .Sbttl . Control Message Opcodes OP.ABO ==: 1 ;ABORT OP.ACC ==: 20 ;ACCESS OP.AVL ==: 10 ;AVAILABLE OP.CMP ==: 40 ;COMPARE HOST DATA OP.DAP ==: 13 ;DETERMINE ACCESS PATHS OP.ERS ==: 22 ;ERASE OP.GCS ==: 2 ;GET COMMAND STATUS OP.GUS ==: 3 ;GET UNIT STATUS OP.ONL ==: 11 ;ONLINE OP.RD ==: 41 ;READ OP.RPL ==: 24 ;REPLACE OP.SCC ==: 4 ;SET CONTROLLER CHARACTERISTICS OP.SUC ==: 12 ;SET UNIT CHARACTERISTICS OP.WR ==: 42 ;WRITE OP.END ==: 200 ;End message flag (added to opcode & returned) OP.SEX ==: 7 ;Serious Exception end message OP.AVA ==: 100 ;AVAILABLE Attention Message OP.DUP ==: 101 ;DUPLICATE UNIT NUMBER Attention Message OP.ACP ==: 102 ;ACCESS PATH Attention Message .Sbttl . Command Modifiers ;Generic Command Modifiers ------------- MD.CMP ==: 40000 ;Compare MD.EXP ==: 100000 ;Express Request MD.ERR ==: 10000 ;Force Error MD.SEC ==: 1000 ;Suppress Error Correction MD.SER ==: 400 ;Suppress Error Recovery ;AVAILABLE Command Modifiers ------------- MD.ALL ==: 2 ;All Class Drivers MD.SPD ==: 1 ;Spin down ;GET UNIT STATUS Command Modifier ------------- MD.NXU ==: 2000 ;Next Unit ;ONLINE Command Modifiers ------------- MD.RIP ==: 1 ;Allow Self-Destruction MD.IMP ==: 2 ;Ignore Media Format Error ;ONLINE and SET UNIT CHARACTERISTICS Command Modifier ------------- MD.SWP ==: 4 ;Enable Set Write Protect ;REPLACE Command Modifier ------------- MD.PRI ==: 1 ;Primary Replacement Block .Sbttl . End Message Flags EF.BBR ==: 200 ;Bad Block Reported EF.BBU ==: 100 ;Bad Block Unreported EF.LOG ==: 40 ;Error Log Generated .Sbttl . Unit Flags UF.CMR ==: 1 ;Compare Reads UF.CMW ==: 2 ;Compare Writes UF.RMV ==: 200 ;Removable Media UF.WPH ==: 20000 ;Write Protect (hardware) UF.WPS ==: 10000 ;Write Protect (software) UF.576 ==: 4 ;576 byte sectors .Sbttl . Controller Flags CF.ATN ==: 200 ;Enable Attention Messages CF.MSC ==: 100 ;Enable Miscellaneous Error Log Messages CF.OTH ==: 40 ;Enable Other Host's Error Log Messages CF.THS ==: 20 ;Enable This Host's Error Log Messages CF.576 ==: 1 ;576 byte sectors .Sbttl . Status and Event Codes ST.MSK ==: 37 ;Status / Event Code Mask ST.SUB ==: 40 ;Sub-Code Multiplier ST.SUC ==: 0 ;Success ST.CMD ==: 1 ;Invalid Command ST.ABO ==: 2 ;Command Aborted ST.OFL ==: 3 ;Unit Offline ST.AVL ==: 4 ;Unit Available ST.MFE ==: 5 ;Media Format Error ST.WPR ==: 6 ;Write Protected ST.CMP ==: 7 ;Compare Error ST.DAT ==: 10 ;Data Error ST.HST ==: 11 ;Host Buffer Access Error ST.CNT ==: 12 ;Controller Error ST.DRV ==: 13 ;Drive Error ST.DIA ==: 37 ;Message from an Internal Diagnostic .Sbttl . Command Message Offsets P.CSIZ ==: 60 ;Minimum size of Command Buffer ;(Size) (Description) P.CRF ==: 0 ;4 Command reference number P.UNIT ==: 4 ;2 Unit number ;2 (6=reserved) P.OPCD ==: 10 ;1 Opcode ;1 (11=reserved) P.MOD ==: 12 ;2 Modifiers P.BCNT ==: 14 ;4 Byte count P.BUFF ==: 20 ;12 Buffer descriptor (here 4 byte addr lo first) P.LBN ==: 34 ;4 Logical Block Number P.PART ==: 36 ;(hi-ord word of logical block number) ;ABORT and GET COMMAND STATUS Command Message Offset: ------------- P.OTRF ==: 14 ;4 Outstanding Reference Number ;ONLINE and SET UNIT CHARACTERISTICS Command Message Offsets: ------------- ;2 (14=reserved) P.UNFL ==: 16 ;2 Unit Flags ;12 (20=reserved) P.DVPM ==: 34 ;4 Device dependent parameters ;REPLACE Command Message Offset: ------------- P.RBN ==: 14 ;4 Replacement block number ;SET CONTROLLER CHARACTERISTICS Command Message Offsets: ------------- P.VRSN ==: 14 ;2 MSCP version (must be zero?) P.CNTF ==: 16 ;2 Controller flags P.HTMO ==: 20 ;2 Host timeout TO.MIN == 0 ; Minutes to time out - zero for us. ;2 (22=reserved) P.TIME ==: 24 ;8 Quad-word time & date .Sbttl . End & Attention Message Offsets P.MSIZ ==: 60 ;Minimum Message Buffer Size ;(Size) (Description) P.CRF ==: 0 ;4 Command Reference Number P.UNIT ==: 4 ;2 Unit Number ;2 (6=reserved) P.OPCD ==: 10 ;1 Opcode/Endcode P.FLGS ==: 11 ;1 End Message Flags P.STS ==: 12 ;2 Status P.BCNT ==: 14 ;4 Byte Count ;12 (20=reserved) P.FBBK ==: 34 ;4 First Bad Block ;ABORT and GET COMMAND STATUS Message Offset: ------------- P.OTRF ==: 14 ;4 Outstanding Reference Number ;GET COMMAND STATUS End Message Offset: ------------- P.CMST ==: 20 ;4 Command Status ;GET UNIT STATUS End Message Offsets: ------------- P.MLUN ==: 14 ;2 Multi-Unit Code P.UNFL ==: 16 ;2 Unit Flags ;4 (20=reserved) P.UNTI ==: 24 ;8 Unit Identifier P.MEDI ==: 34 ;4 Media Type Identifier P.SHUN ==: 40 ;2 Shadow Unit P.TRCK ==: 44 ;2 Track Size P.GRP ==: 46 ;2 Group Size P.CYL ==: 50 ;2 Cylinder Size ;2 (52=reserved) P.RCTS ==: 54 ;2 RCT Table Size P.RBNS ==: 56 ;1 RBN's per Track P.RCTC ==: 57 ;1 RCT Copies ;ONLINE and SET UNIT CHARACTERISTICS End Message and AVAILABLE ; Attention Message Offsets: ------------- P.MLUN ==: 14 ;2 Multi-Unit Code P.UNFL ==: 16 ;2 Unit Flags ;4 (20=reserved) P.UNTI ==: 24 ;8 Unit Identifier P.MEDI ==: 34 ;4 Media Type Identifier P.UNSZ ==: 44 ;4 Unit Size P.VSER ==: 50 ;4 Volume Serial Number ;SET CONTROLLER CHARACTERISTICS End Message Offsets: ------------- P.VRSN ==: 14 ;2 MSCP Version P.CNTF ==: 16 ;2 Controller Flags P.CTMO ==: 20 ;2 Controller Timeout ;2 (22=reserved) P.CNTI ==: 24 ;8 Controller ID .Sbttl . Error Log Message Offsets ;(Size) (Description) L.CRF ==: 0 ;4 Command Reference Number L.UNIT ==: 4 ;2 Unit Number L.SEQ ==: 6 ;2 Sequence Number L.FMT ==: 10 ;1 Format L.FLGS ==: 11 ;1 Error Log Message Flags L.EVNT ==: 12 ;2 Event Code L.CNTI ==: 14 ;8 Controller ID L.CSVR ==: 24 ;1 Controller Software Version L.CHVR ==: 25 ;1 Controller Hardware Version L.MLUN ==: 26 ;2 Multi-Unit Code L.UNTI ==: 30 ;8 Unit ID L.USVR ==: 40 ;1 Unit Software Version L.UHVR ==: 41 ;1 Unit Hardware Version ;2 (Format dependent) L.VSER ==: 44 ;4 Volume Serial Number ;Host Memory Access Errors with Bus Address Error Log ; Message Offset: ------------- L.BADR ==: 30 ;4 Bus Address ;Disc Transfer Errors Error Log Message Offsets: ------------- L.LVL ==: 42 ;1 Level L.RTRY ==: 43 ;1 Retry L.VSER ==: 44 ;4 Volume Serial Number L.HDCD ==: 50 ;4 Header Code ;SDI Errors Error Log Message Offsets: ------------- L.HDCD ==: 50 ;4 Header Code L.SDI ==: 54 ;12 SDI Information .Sbttl . Error Log Message Format Codes FM.CNT ==: 0 ;Controller Errors FM.BAD ==: 1 ;Host Memory Access Errors with Bus Address FM.DSK ==: 2 ;Disc Transfer Errors FM.SDI ==: 3 ;SDI Errors .Sbttl . Error Log Message Flags LF.SUC ==: 200 ;Operation Successful LF.CON ==: 100 ;Operation Continuing LF.SNR ==: 1 ;Sequence Number Reset .Sbttl UDA Port Definitions OWN ==: 100000 ;Set in header if owned by controller FLAG ==: 40000 ;Set on return from controller; host to clear if don't ; want interrupts from action being requested. .Sbttl . Initialization Values & Parameters ;During initialization: flags for SA register: ;Step 1 - returned in SA after any value inserted into IP ISTEP1 ==: 4000 ;Initialization step 1 NV ==: 2000 ;Set if port does not support host-settable vector QB ==: 1000 ;Set if Q-bus DI ==: 400 ;Set if port implements enhanced diagnostics ;Step 2 - sent to SA STEP ==: 200 ;Gawd knows why this is defined with this name! ; 100000 - Used as the high-order bit, high byte, always set WR ==: 40000 ;Set if want DIAGNOSTIC WRAP MODE (& DI set) ; 34000 Mask for command ring length - # slots, as power of 2 ; 3400 Mask for response ring, ibid IE ==: 200 ;Allow interrupts during initialization ; 177 Vector address/4 ;Step 2 - returned in SA ISTEP2 ==: 10000 ;Initialization step 2 ; 3400 Port Type No ( = 0 ) ; 200 Set ; 100 Set if WR set ; 70 Mask of command ring length ; 7 Mask of response ring length ;Step 3 - sent to SA ; 177776 Low-order address of base of "ring" PI ==: 1 ;Set if host needs Unibus adapter purge interrupts ;Step 3 - returned in SA ISTEP3 ==: 20000 ;Initialization step 3 ; 200 If IE set ; 177 Interrupt vector/4 ;Step 4 - sent to SA PP ==: 100000 ;Set if requesting execution of purge & poll tests ; (only valid if DI=1) ; 77777 Ringbase high address ;Step 4 - returned in SA ISTEP4 ==: 40000 ;Initialization step 4 ; 377 Control Code Version ;Step 5 - sent to SA ; 177400 (reserved) BURST ==: 374 ;1 less than max # long words host is willing to allow ; per DMA transfer. .LF ==: 2 ;Send LAST FAIL response packet at end of init ; (define with "." as lf usu means ) GO ==: 1 ;Enter functional controller microcode when init done ;(If IE set, interrupts may occur after steps 2, 3, and 4) ISTEPS ==: 74000 ;Mask of all init steps .end start