.title Disassembler .enabl lc ;++ ; ; Saved image (.SAV) disassembler. ; ; By John Wilson . ; ; Copyright (C) 1998-2008 by D Bit. All rights reserved. ; Distribution, modification, and use for any purpose is allowed as long as ; source code is available which includes this notice. ; ; For RSTS/E V7.0-07 and later, or RT-11 V4.0 and later (probably V3C too). ; ; Makes two passes over the binary file, attempting to trace out the possible ; execution paths of the program, to help differentiate between code and data, ; and to put labels only on locations which are referenced elsewhere. ; ; 06/1984 JMBW Disassembler created (as part of RAID, DECUS #11-772). ; 08/1985 JMBW Converted to stand-alone disassembler with execution ; trace. V1.0. ; 07/10/1998 JMBW Added support for re-reading .MAC files from previous ; runs, to eliminate most of the manual cut/past work ; needed. /B switch for use on overlays. Cleaned up to ; run on all PDP-11 models (no EIS or SOB). V1.1. ; 10/17/1998 JMBW Added CIS, DCJ11, 11/60 instructions. /U switch for ; upper case output. V1.2. ; ; Command line: ; *outfile.MAC=infile.SAV[,infile.mac][/switches] ; ; Switches: ; /S:start Starting virtual addr of range to disassemble ; /E:end Ending virtual addr of range to disassemble ; /T:transfer Transfer address (beginning of an execution thread) ; /B:blk[:offset] Set base address (within .SAV file) of code to disassemble; ; virtual address 000000 is at the specified offset (default=0) ; of block "blk". ; /I Insert block of disassembled code here ; (used with /S and /E in .MAC input file) ; /U Upper case source output (default is lower case). ; ; The basic idea is, on the first run you use the command line to specify the ; range of the .SAV file to disassemble. Then you go into the resulting .MAC ; file with an editor, add comments and figure out the stuff that's too ; complicated for DISSAV to get on its own (like computed jumps and dispatch ; tables). You will probably find blocks of code to which DISSAV could find ; no execution path so it left them as .WORD statements. Once you figure out ; that a block of data is really code, chop it out with the editor and then ; put in a line starting with an asterisk, that looks like the above command ; line but contains only switches (no filenames). Re-running DISSAV with no ; switches (but with the existing .MAC file specified as the second input ; file) causes it to copy all lines of the file that don't start with "*" ; through to the output file verbatim, and lines that start with "*" are ; treated as command lines, similar to the CSI command line. For example, ; this could appear in the .MAC input file: ; ; ; insert disassembled code after the next line ; */s:1400/e:1477/t:1400/i ; ; The /I switch means that the block of code should be disassembled and ; nserted after the line starting with "*". The /S and /E switches are ; required when /I is used, and give the start and end address of the block ; to insert. /T may optionally be used to specify a transfer address within ; the block, in cases where DISSAV has found no execution route to the block ; on its own. ; ; DISSAV always copies "*" lines containing /T through to the output file ; (after removing /S, /E, and /I switches since they cause a one-time action), ; so that all the information about execution routes will be preserved for ; future runs. If the line contains /I (i.e. it's a line added by the user to ; force the insertion of a block of disassembled code), DISSAV encloses it in ; a .REM comment block in the output file, on the assumption that the user ; added that line by hand. Any other "*" line is assumed to already be inside ; a .REM block since it is assumed to have been generated by a previous DISSAV ; run, so it's copied without adding more .REM statements. ; ; "*" lines containing /B switches may be inserted anywhere in the .MAC file ; to change the base/offset used for reading data out of the .SAV file. ; ; "*" lines containing /T switches may also appear anywhere in the .MAC file; ; only one /T switch is recognized per line. This allows the user to tell ; DISSAV about JMP/JSR/BR targets that it was unable to find on its own, so ; that the execution trace performed on each successive run includes all ; previously found code blocks. As noted above, /T switches that appear on ; /I/S/E lines (that insert a block of disassembled code) are automatically ; passed through to the output file, so there's no need to specify /T twice ; for the same transfer address. ; ; Note: the parameters for the very first disassembly of the entire .SAV file ; are preserved in a .REM block at the beginning of the .MAC file. DISSAV ; will use this line to set the /B/E/S/T/U parameters for all future runs ; (rather than requiring the user to type the same switches again and again), ; so this automatically generated "*" line must be the first one in the file ; so that DISSAV can find it on the next run. ; ;-- rsts= 0 ;set to 0 for real RT-11 ;non-zero for RSTS-style version ID banner (needs .ERRPRT) ; .mcall .close,.csigen,.csispc,.print,.purge,.rctrlo,.wait ; macin= 2048. ;size of each of two .MAC input buffers (mult of 512.) macout= 2048. ;size of each of two .MAC output buffers (mult of 512.) ; .read= emt!375 ;SYSMAC forms of these expect to build EMT area for me .readw= emt!375 .write= emt!375 .writw= emt!375 ; .errprt=emt!364 ;RSTS RT11.RTS call to display err msg for err # in R0 ;(used only if RSTS symbol set NZ) ; op0= 0 ;0-operand instruction ; ht= 11 ;ASCII values lf= 12 ff= 14 cr= 15 ; jsw= 44 ;job status word ; ; Define instruction (with no byte form): .macro instr mask,result,name,oprnds .word mask,result .rad50 /name/ .word oprnds .endm ; ; Define instruction (add "b" to mnemonic if byte form): .macro instrb mask,result,name,oprnds .word mask,result .rad50 /name/ .word oprnds!1 ;odd => add "b" if opcode<15> set .endm ; .macro .out ptr ;write .ASCIZ string to output file mov #'ptr,r0 call out .endm ; ; Start of program. ; dissav: call progid ;print program ID .print #forhlp ;tell them how to get help ; csi: ; prompt for next command mov @#42,sp ;restore stack bis #40000,@#jsw ;enable LC input .rctrlo ;make sure RMON notices .csigen #devhnd,#defext,#0,#rbuf ;call CSI, save line mov #rbuf,r1 ;point at string 10$: movb (r1)+,r0 ;get a char beq 20$ ;end of string, null command cmpb r0,#40 ;space or ctrl char? blos 10$ ;yes, loop br 30$ ;no, significant char, cont 20$: tst (sp)+ ;purge stack call progid ;print program ID br csi ;reprompt 30$: clr gstart ;for now, so PEEK can read .SAV header mov #177777,gend ;(same as above) clr base ;reading root, file addr = memory addr clr offset mov #40,r5 ;get starting addr call peek mov r0,ppc ;point at starting loc mov r0,xfer ;(label='START:') mov #50,r5 ;get high limit call peek mov r0,end ;end at end mov #1000,start ;start at begn call switch ;handle switches parsed by CSI tstb eflg ;get /E value beq 40$ mov eval,end 40$: tstb sflg ;get /S value beq 50$ mov sval,start 50$: tstb tflg ;get /T value beq 60$ mov tval,ppc 60$: mov #symtab,r0 ;point at symbol table bitmap mov #14000,r1 ;length of SYMTAB+BITMAP+INSBND 70$: clr (r0)+ ;clear a word dec r1 ;loop bne 70$ bic #1,start ;start on an even address bic #1,xfer ;same for transfer address bic #1,ppc ;and pseudo-PC mov start,gstart ;save global file start/end mov end,gend ; init output file mov #wmbuf+macout,wmba ;init buffer addr mov #wmbuf,wmoth ;init "other" buffer mov #wmbuf,wptr ;init output ptr/ctr mov #macout,wcnt clr wmblk ;init blk # .wait #0 ;is channel #0 even open? mov #1,r0 ;[assume so] sbc r0 ;0 if not movb r0,wmflg ;NZ if open ;+ ; ; Okay, that takes care of initialization. What we's a gonna do, is: make ; two passes through the input file; on pass 1, do a recursive test ; disassembly to find out what gets executed and what doesn't (so we know ; what's data and what isn't). This will, of course, completely fuck up if ; the program is overlaid (although we will probably get some useful ; information), and if there are any JMPs or JSRs through registers or ; dispatch tables, we aren't going to be able to chase them down (well, I ; suppose we could if we actually single-stepped through the entire program, ; but I'm too lazy to do that and besides the program might be doing something ; nasty). Okay, so we'll start disassembling at the transfer address ; (contents of location 40 or value specified with /T switch), and each time ; we come to a conditional branch or a subroutine call, we'll try both ; branches (that's how this is recursive). Each time we read a word to ; disassemble with PEEK, a bit gets set in BITMAP to remember that this word ; gets executed. We will stop pursuing a branch if we reach either 1. a HALT ; (!) or EMT 350 instruction, [2. any undefined instruction,] 3. an RTS ; instruction, 4. a JMP or JSR which uses something other than relative or ; absolute mode, 5. anything outside of the range [START,END], or 6. any place ; we've been before, according to BITMAP (otherwise we'd hang on the first ; loop we came across). On the trace pass, a bit is set in INSBND for the ; first word of each instruction, so we know where labels are possible (not ; between words in a multi-word instruction). ; ;- call rewmac ;see if .MAC input file exists tstb rmflg ;does it? bne macinp ;yes, use it ; use supplied switches (or defaults) to do whole .SAV file mov xfer,tval ;set up /T switch if not already done movb #1,tflg clrb trash ;really write it movb #1,sflg ;always specify /S and /E movb #1,eflg mov start,sval ;copy values in case defaulted mov end,eval call comswt ;write cmd line switch(es) in a comment blk comb trash ;no output on first pass mov xfer,r5 ;point at transfer addr call bitnum ;get corresponding bit bis r0,symtab(r5) ;this gets a symbol (START) mov ppc,r5 ;get starting address call trace ;call recursive routine call disasm ;disassemble range .out t$end ;".endstart" done: ; finished with disassembly call wrmac ;flush last buffer to .MAC output file .close #0 ;.CSIGEN bug, no longer closes files in V5.X ;(in spite of V4.0 documentation) jmp csi ;reprompt ;+ ; ; If a .MAC input file was specified, things are different. Instead of ; disassembling the whole input file, we copy the .MAC file through to the ; output and only disassemble when we find a line starting with *, which ; contains another CSI command line with a similar syntax to our .CSIGEN line ; but no filenames. ; ; */T:transfer/S:start/E:end/B:base[:offset]/I/U ; ; /S and /E are only useful with /I, which means to insert a disassembled ; block of code in the output file in place of the '*' line. ; ;- macinp: mov base,-(sp) ;save initial base/offset values mov offset,-(sp) movb #377,trash ;no output on first pass call rdline ;find first "*" line, parse switches bcs 60$ ;haven't they heard of PIP?! tstb sflg ;this sets global start/end values beq 10$ mov sval,gstart 10$: tstb eflg beq 20$ mov eval,gend 20$: mov gstart,xfer ;assume we know nothing tstb tflg ;transfer addr specified? beq 30$ mov tval,xfer ;yes 30$: br 50$ ;go process, and pass the line through 40$: ; first pass, find and search all addresses specified with /T call rdline ;copy lines through until we find a "*" line bcs 60$ ;EOF 50$: tstb tflg ;/T flag given? beq 40$ ;no mov tval,r5 ;get starting address call bitnum ;get corresponding bit bis r0,symtab(r5) ;this gets a symbol mov tval,r5 ;get starting address again call trace ;trace range br 40$ 60$: call rewmac ;rewind .MAC file again clrb trash ;now write output for real mov (sp)+,offset ;restore initial base/offset values mov (sp)+,base 70$: ; second pass, copy input .MAC file, inserting disassembled blocks as ; required call rdline ;copy lines through until we find a "*" line bcs done ;done tstb iflg ;/I flag given? beq 80$ ;no, perhaps new base/offset, pass through tstb sflg ;they should have given both /S and /E beq 90$ tstb eflg beq 90$ mov sval,start ;fetch start/end values mov eval,end clrb sflg ;don't preserve /S/E clrb eflg call comswt ;pass switch(es) through, if any to save call disasm ;include block br 70$ ;around for more 80$: call outswt ;no /I, assume already in a comment block br 70$ ;around for more 90$: mov #ireqse,r0 ;/I requires /S and /E asterr: ; error with line starting with asterisk, R0=.ASCIZ message .print ;print message ;; really ought to give line number .purge #0 ;flush output file jmp csi ;outta here (purge I/O, reset stack) ;+ ; ; Tracer. See discussion above. ; ; r5 starting virtual address ; ;- trace: bic #1,r5 ;force even mov r5,r4 ;save R5 call bitnum ;get bit pattern bit r0,bitmap(r5) ;have we been here before? bne 20$ ;yes, don't loop bis r0,insbnd(r5) ;start of an instruction mov r4,r5 ;no, restore R5 call peek ;get an opcode bcs 20$ ;error, return tst r0 ;HALT instruction? beq 20$ ;yes, return cmp r0,#emt!350 ;.EXIT (RT-11)? beq 20$ ;yes, return ; This, of course, depends on the RTS (or OS) which the file is to run on, but ; it's a .SAV file, so it would be pretty useless anywhere but on RT-11. call dis1 ;disassemble the instruction tstb jmpflg ;is it a branch of some kind? beq trace ;no, get next instruction bmi 10$ ;unconditional, follow only one branch tstb adrflg ;where was it to? beq trace ;dunno, follow only other branch mov r5,-(sp) ;save r5 mov addr,r5 ;get destination call trace ;scope it out (recurse) mov (sp)+,r5 ;restore PC br trace ;loop 10$: mov addr,r5 ;get address to go to tstb adrflg ;is it valid? bne trace ;yes, loop 20$: rts pc ;no, return ;+ ; ; Disassemble the range from START to END and add it to the output file. ; ; BITMAP/SYMTAB assumed set up by TRACE. ; ;- disasm: mov start,r5 ;start at begn of range clrb trash ;for real this time mov r5,ppc ;save location counter 10$: call bitnum ;get corresponding bit bit r0,symtab(r5) ;does it get a symbol? beq 20$ ;no, skip mov ppc,r0 ;get addr call prlab ;print label .out colon ;print ":" 20$: .out tab ;print tab mov ppc,r5 ;restore ptr call bitnum ;get bit bit r0,bitmap(r5) ;is it ever executed? bne 30$ ;yes, skip .out t$word ;print ".word" mov ppc,r5 ;restore ptr call peek ;read a word call huh ;print its value br 40$ ;skip 30$: mov ppc,r5 ;restore ptr call dis ;do an instruction 40$: mov r5,ppc ;save cmp r5,end ;off end of range? blos 10$ ;no, loop rts pc ; dis: ; disassemble instruction at (R5) call peek ;get opcode dis1: clr jmpflg ;clear ADRFLG and JMPFLG mov r0,-(sp) ;save mov #inslst,r2 ;point at instruction list 10$: mov (sp),r1 ;get opcode back bic (r2)+,r1 ;clear operand bits cmp r1,(r2)+ ;compare to expected result beq 20$ ;this is it -- go print add #4+2,r2 ;skip name, CALL addr br 10$ ;loop until found (always finds .WORD) 20$: mov r1,-(sp) ;save mov r5,-(sp) mov #numbuf,r5 ;point at buf mov (r2)+,r0 ;get first half of string mov r2,-(sp) ;save call rad$ ;convert first half mov (sp)+,r2 ;restore mov (r2)+,r0 ;get second half of string mov r2,-(sp) ;save again call rad$ ;convert second half clrb (r5) ;mark end .out numbuf ;write the string mov (sp)+,r2 ;restore mov (sp)+,r5 mov (sp)+,r1 bit #1,(r2) ;byte flag set? beq 30$ ;no byte version of instr, skip tst (sp) ;byte bit set? bpl 30$ ;no, word .out b ;print 'b' bic #100000,(sp) ;chop off high bit 30$: mov (r2),r2 ;get operand types (addr of routine) bic #1,r2 ;clear low bit bne 40$ ;go print them tst (sp)+ ;no operands, clear stack jmp crlf ;print and return 40$: cmp r2,#chgflg ;SEx or CLx? beq 50$ ;yes, don't print tab .out tab ;print ' ' 50$: mov (sp)+,r0 ;get opcode back jmp (r2) ;jump to the appropriate routine ; op0.5: ; half operand (one register) cmp r1,#200 ;RTS instruction? bne 10$ ;no comb jmpflg ;yes, unconditional jump, no valid ADDR 10$: call prreg ;print the register jmp crlf ;print and return ; op1: ; single operand cmp r1,#100 ;JMP instruction? bne 10$ ;no comb jmpflg ;yes, unconditional jump incb adrflg ;valid address 10$: cmp r1,#4700 ;JSR PC ("CALL") instruction? bne 20$ ;no incb jmpflg ;yes, conditional jump (we'll be back later) incb adrflg ;valid address 20$: call proper ;print the operand jmp crlf ;print and return ; op1.5: ; 1-1/2 operand (reg,operand) cmp r1,#4000 ;JSR? bne 10$ ;no incb jmpflg ;yes, conditional jump (we'll be back later) incb adrflg ;valid address 10$: mov r0,-(sp) ;save opcode asl r0 ;left 2 asl r0 swab r0 ;and right 8, get register field into <2:0> call prreg ;print it .out comma ;print "," mov (sp)+,r0 ;get opcode back call proper ;print the operand jmp crlf ;print and return ; op2: ; two operand mov r0,-(sp) ;save opcode asl r0 ;left 2 asl r0 swab r0 ;and right 8, get source into <5:0> call proper ;print it .out comma ;print "," mov (sp)+,r0 ;get other operand call proper ;print it jmp crlf ;print and return ; branch: ; BRanch instruction cmp r1,#400 ;just BR (unconditional)? bne 10$ ;no, skip comb jmpflg ;remember this br 20$ ;skip 10$: incb jmpflg ;conditional branch 20$: movb r0,r0 ;sign extend asl r0 ;multiply by 2 (word) add r5,r0 ;add in current PC (instr addr +2) call prlab ;print resulting address incb adrflg ;valid address jmp crlf ;print and return ; muldiv: ; multiply or divide (or ASH[C]) mov r0,-(sp) ;save opcode call proper ;print source .out comma ;print "," mov (sp)+,r0 ;get opcode back asl r0 ;left 2 asl r0 swab r0 ;and right 8 to get register into <2:0> call prreg ;print destination jmp crlf ;print and return ; trpemt: ; TRAP or EMT bic #^C377,r0 ;isolate low 8 call prbyte ;print the low byte jmp crlf ;print and return ; decbra: ; SOB instruction mov r0,-(sp) ;save R0 asl r0 ;left 2 asl r0 swab r0 ;and right 8 to get reg into <2:0> call prreg ;print it .out comma ;print "," mov (sp)+,r1 ;restore bic #^C77,r1 ;isolate asl r1 ;*2 mov r5,r0 ;copy location counter sub r1,r0 ;calculate destination address call prlab ;print it incb adrflg ;valid address incb jmpflg ;conditional branch jmp crlf ; and return ; fop1: ; fdst call pfoper ;print the operand jmp crlf ; and return ; fop2a: ; fsrc,AC mov r0,-(sp) ;save call pfoper ;print source .out comma ;"," mov (sp)+,r0 ;restore call prac ;print AC jmp crlf ;, return ; fop2b: ; AC,fdst mov r0,-(sp) ;save call prac ;print AC .out comma ;"," mov (sp)+,r0 ;restore call pfoper ;fdst jmp crlf ;, return ; fop2c: ; src,AC mov r0,-(sp) ;save call proper ;src .out comma ;"," mov (sp)+,r0 ;restore call prac ;AC jmp crlf ;, return ; fop2d: ; AC,dst mov r0,-(sp) ;save call prac ;AC .out comma ;"," mov (sp)+,r0 ;restore call proper ;dst jmp crlf ;, return ; setpri: ; SPL instruction (which Digby doesn't have). oh well. bic #^C7,r0 ;isolate prio level add #'0,r0 ;convert to ASCII mov r0,-(sp) ;save on stack mov sp,r0 ;pt at it call out ;print it (check TRASH) tst (sp)+ ;clear stack jmp crlf ;, return ; chgflg: ; SEx, CLx mov #flgnam,r1 ;point at flags' names mov #4,r2 ;number of flags mov r3,-(sp) ;save R3 mov r5,-(sp) ;and R5 clr r3 ;nothing printed yet mov r0,r5 ;copy opcode into R5 10$: ror r5 ;rotate a bit into C bcc 30$ ;clear, continue tst r3 ;comma needed? beq 20$ ;no .out comma ;yes, print it 20$: movb (r1),r0 ;get flag name mov r0,-(sp) ;put on stack mov sp,r0 ;point at it call out ;print it tst (sp)+ ;clear stack inc r3 ;set comma flag 30$: inc r1 ;inc ptr dec r2 ;checked all flags? bne 10$ ;no mov (sp)+,r5 ;[restore R5] mov (sp)+,r3 ;[and R3] jmp crlf ;yes, , return ; ; Inline forms of CIS instructions: ; cis1.5: ; one descriptor, one immediate value call crlf ;EOL br cis2a ; cis2: ; two descriptors call crlf ;EOL br cis3a ; cis2.5: ; two descriptors, one immediate value call crlf ;EOL call cisd ;descriptor cis2a: call cisd ;descriptor br cisi ;immed value, return ; cis3: ; three descriptors call crlf ;EOL call cisd ;descriptor cis3a: call cisd ;descriptor br cisd ;descriptor, return ; cis4: ; MOVTCI ; two descriptors, immed value, table address call cis2.5 ;start it off ;br cisd ;treat table addr as descriptor, return ; cisd: ; display descriptor pointer mov r5,-(sp) ;save call bitnum ;get corresponding bit bit r0,symtab(r5) ;does it get a symbol? beq 10$ ;no, skip mov ppc,r0 ;get addr call prlab ;print label .out colon ;print ":" 10$: .out tab ;print tab .out space ;indent .out t$word ;print ".word" mov (sp)+,r5 ;restore call peek ;read a word call prlab ;print its value as a label jmp crlf ;CRLF, return ; cisi: ; display immediate value (fill char, etc.) mov r5,-(sp) ;save call bitnum ;get corresponding bit bit r0,symtab(r5) ;does it get a symbol? beq 10$ ;no, skip mov ppc,r0 ;get addr call prlab ;print label .out colon ;print ":" 10$: .out tab ;print tab .out space ;indent .out t$word ;print ".word" mov (sp)+,r5 ;restore call peek ;read a word ;br huh ;print as immed value, return ; huh: ; ??? undefined instruction call prnum ;".word nnnnnn" mov r0,-(sp) ;save value .out cmnt ;" ;" mov (sp),r0 ;get value clr r1 ;no quotes yet call huh1 ;do low byte mov (sp)+,r0 ;get again swab r0 ;byte swap call huh1 ;do high byte clr r2 ;not in quotes anymore call huh2 ;print closing quote jmp crlf ;see you around ; huh1: ; do a char movb r0,r0 ;SXT mov r0,-(sp) ;save cmp r0,#40 ;ctrl char? blo 10$ ;yes cmp r0,#176 ;no, rubout or negative? bhi 10$ ;yes cmp r0,#140 ;grave accent? beq 10$ ;yes, MACRO is afraid of them mov #1,r2 ;make sure we're between quotes call huh2 ;(print one if nessa) mov (sp)+,r0 ;restore movb r0,char ;no, put in buffer mov #char,r0 ;point at it jmp out ;print, return 10$: clr r2 ;make sure we aren't between quotes call huh2 ;(print a closing one) .out lt ;"<" mov (sp)+,r0 ;restore call prbyte ;print value mov #gt,r0 ;">" jmp out ;print it, return ; huh2: ; print a quote if R1<>R2 cmp r1,r2 ;equal? beq 10$ ;yes .out quote ;no, print a quote mov r2,r1 ;update flag 10$: rts pc ; proper: ; print operand in R0 <5:0> bic #^C77,r0 ;isolate mov r0,-(sp) ;save R0 bic #70,r0 ;mask out <5:3> cmp r0,#7 ;reg=PC? beq pcoper ;PC gets special treatment mov (sp),r0 ;get back operand bic #7,r0 ;mask out register prop1: tst r0 ;mode 0? bne 10$ ;no, continue mov (sp)+,r0 ;get register number jmp prreg ;print register and return 10$: cmp r0,#10 ;mode 1? bne 20$ ;no, continue mov (sp)+,r0 ;get reg number jmp prrgp ;print "(reg)" 20$: cmp r0,#20 ;mode 2? bne 40$ ;no, continue 30$: mov (sp)+,r0 ;get reg number call prrgp ;print "(reg)" mov #plus,r0 ;print "+" jmp out ;and return 40$: cmp r0,#30 ;mode 3? bne 50$ ;no, continue .out at ;print "@" br 30$ ;continue as if mode=2 50$: cmp r0,#40 ;mode 4? bne 70$ ;no, continue 60$: .out minus ;print "-" mov (sp)+,r0 ;get reg number jmp prrgp ;print "(reg)" and return 70$: cmp r0,#50 ;mode 5? bne 80$ ;no, continue .out at ;print "@" br 60$ ;continue as if mode=4 80$: cmp r0,#60 ;mode 6? bne 100$ ;no, must be mode 7 90$: call peek ;get index address call prlab ;print it mov (sp)+,r0 ;get reg number jmp prrgp ;print "(reg)" and return 100$: .out at ;print "@" br 90$ ;print "######(reg)" ; pcoper: ; handle special interpretations of modes 27, 37, 67, and 77 mov (sp),r0 ;get opcode bic #^C70,r0 ;isolate mode cmp r0,#20 ;immediate? bne 20$ ;no, continue 10$: tst (sp)+ ;clear stack .out number ;print "#" call peek ;get operand jmp prlab ;print label, return 20$: cmp r0,#30 ;absolute? bne 30$ ;no, continue tst (sp)+ ;clear stack movb r0,adrflg ;set flag in case of JMP or JSR .out at ;print "@#" .out number call peek ;get operand jmp prlab ;print it 30$: cmp r0,#60 ;relative? bne 50$ ;no, continue movb r0,adrflg ;set flag in case of JMP or JSR 40$: tst (sp)+ ;clear stack call peek ;get index add r5,r0 ;add on value of PC jmp prlab ;print resulting address and return 50$: cmp r0,#70 ;relative deferred? bne 60$ ;no, go treat it normally .out at ;print "@" br 40$ ;print index 60$: jmp prop1 ;go treat it like a normal register ; pfoper: ; print FP operand in R0 <5:0> bic #^C77,r0 ;isolate bit #70,r0 ;mode 0? beq prfpac ;yes, print "Fn" jmp proper ;no, print normal operand ; prrgp: ; print reg in R0 with parenthesis around it mov r0,-(sp) ;save R0 .out leftp ;print "(" mov (sp)+,r0 ;get number back call prreg ;print reg name .out rightp ;print ")" rts pc ;return ; prreg: ; print reg whose number is in R0 bic #^C7,r0 ;isolate cmp r0,#6 ;pc or sp? blo 20$ ;no, go print "r#" bne 10$ ;go print "pc" mov #sptext,r0 ;print "sp" jmp out ;and return 10$: mov #pctext,r0 ;print "pc" jmp out ;and return 20$: add #'0,r0 ;convert to ASCII movb r0,rtext+1 ;put in buffer mov #rtext,r0 ;print "r#" jmp out ;and return ; prfpac: ; print FP accumulator whose # is in R0 <2:0> bic #^C7,r0 ;isolate prac1: add #'0,r0 ;cvt to ASCII movb r0,actxt+1 ;put in buffer mov #actxt,r0 ;print jmp out ;and return ; prac: ; print FP accumulator whose # is in R0 <7:6> asl r0 ;left 2 asl r0 swab r0 ;and right 8 = net right 6 (get into <1:0>) bic #^C3,r0 ;isolate br prac1 ;jump into PRFPAC ;+ ; ; Print . ; ;- crlf: mov #crlf$,r0 ;print ;br out ;do it, return ;+ ; ; Write .ASCIZ string at (R0) to the output file if TRASH is not zero. ; ; R0 trashed, others preserved. ; ;- out: tstb trash ;are we trashing output? bne 40$ ;yes, return mov r1,-(sp) ;save regs mov r2,-(sp) 10$: mov wptr,r1 ;get ptr mov wcnt,r2 ;space remaining 20$: movb (r0)+,(r1)+ ;copy beq 30$ ;end of string dec r2 ;buffer full? bne 20$ ;loop if not mov r0,-(sp) ;save R0 mov r1,wptr ;update ptr call wrmac ;flush output buffer mov (sp)+,r0 ;restore br 10$ ;around for more 30$: dec r1 ;correct ptr mov r1,wptr ;update mov r2,wcnt mov (sp)+,r2 ;restore regs mov (sp)+,r1 40$: rts pc ; prlab: ; print R0 as a label, if its bit is set in INSBND; ; otherwise print in octal mov r0,addr ;save in case it's important mov r5,-(sp) ;save regs mov r0,-(sp) cmp r0,start ;in range? blo 30$ ;no cmp r0,end ;hm? bhi 30$ ;no mov r0,r5 ;yes, copy call bitnum ;get bit # tstb trash ;pass one? bne 10$ ;yes, set SYMTAB bit bit r0,bitmap(r5) ;is it an instruction? beq 10$ ;no, give it a label bit r0,insbnd(r5) ;yes, is it the begn of one? beq 30$ ;no, print it in octal 10$: bis r0,symtab(r5) ;make mark in table mov (sp)+,r0 ;restore R0 cmp r0,xfer ;START? beq 20$ ;yes, never mind mov #'L,r5 ;use L or M for high bit br prnum2 ;print the number, return 20$: .out strt ;print "START" mov (sp)+,r5 ;restore rts pc 30$: mov (sp)+,r0 ;restore br prnum1 ;print in octal ; prnum: ; print R0 in octal mov r5,-(sp) ;save R5 prnum1: mov #'0,r5 ;octal, not symbolic prnum2: call proct ;print it mov (sp)+,r5 ;restore rts pc ; proct: ; print R0 in octal, using char in R5 as char for high bit mov r0,addr ;save in case it's important mov r0,-(sp) ;save regs mov r1,-(sp) mov r2,-(sp) mov r3,-(sp) mov #numbuf+6,r1 ;point at end of buffer mov #5,r3 ;loop counter 10$: mov r0,r2 ;put # in R2 bic #^C7,r2 ;isolate low 3 bis #'0,r2 ;convert to ASCII movb r2,-(r1) ;put in buffer asr r0 ;right 3 asr r0 asr r0 dec r3 ;done all digits? bne 10$ ;loop if not asr r0 ;shift highest bit into C adc r5 ;R5+1 if 1 movb r5,-(r1) ;put in buffer .out numbuf ;print number mov (sp)+,r3 ;restore regs mov (sp)+,r2 mov (sp)+,r1 mov (sp)+,r0 rts pc ;return ; prbyte: ; print lower byte of R0 in octal mov r0,-(sp) ;save regs mov r1,-(sp) mov r2,-(sp) mov r0,r1 ;copy out of the way mov #numbuf+6,r0 ;point at end of buffer 10$: mov r1,r2 ;copy bic #^C7,r2 ;isolate bis #'0,r2 ;convert to ASCII, C=0 movb r2,-(r0) ;[save in buf] rorb r1 ;right a bit (C=0 from BIS above) rorb r1 rorb r1 cmp r0,#numbuf+3 ;done 3 digits? bhi 10$ ;loop if not call out ;print it mov (sp)+,r2 ;restore regs mov (sp)+,r1 mov (sp)+,r0 rts pc ;return ;+ ; ; Routine to convert a word from .RAD50 to .ASCII using a variation of the BCD ; method used by the TSS/8 System Interpreter to convert decimal numbers on ; the PDP-8 (with no DIV instruction). ; ; r0 number ; r5 buffer (updated on return) ; ;- rad$: mov #r50bit,r3 ;pt at table br 40$ ;jump into loop 10$: ; next digit clr r4 ;clear it mov #6,r2 ;bit count (each .RAD50 dig is 5.625 bits) 20$: asl r4 ;*2 cmp r1,r0 ;OK? bhi 30$ ;no (C=0) sub r1,r0 ;remove the bit (C=0) inc r4 ;[count it] 30$: ror r1 ;/2 (C=0 either way above) dec r2 ;done all bits? bne 20$ ;loop if not movb r50(r4),r4 ;convert beq 40$ ;blank, ignore movb r4,(r5)+ ;save 40$: mov (r3)+,r1 ;get next value bne 10$ ;loop if non-zero movb r50(r0),r4 ;convert last beq 50$ ;blank, ignore movb r4,(r5)+ ;save 50$: rts pc ;+ ; ; Routine to return word at virtual address in R5. Result in R0, R5 is ; incremented by 2. Returns with C set on error. ; ;- peek: mov r1,-(sp) ;save mov r2,-(sp) cmp r5,gstart ;before START? blo 60$ ;yes, error cmp r5,gend ;after end? bhi 60$ ;yes, error mov r5,r1 ;copy addr tstb trash ;on pass 2? beq 10$ ;yes, don't set bit call bitnum ;find bit in BITMAP bis r0,bitmap(r5) ;set it mov r1,r5 ;restore R5 10$: mov r1,r2 ;copy asr r2 ;right 9 bits swab r2 bic #^C177,r2 ;isolate block bic #^C777,r1 ;isolate offset within block add base,r2 ;add base block of memory image add offset,r1 ;and offset bit #^C777,r1 ;carry? beq 20$ inc r2 ;yes bic #^C777,r1 ;trim again 20$: cmp r2,rblk ;is this block already loaded? beq 30$ ;yes, great mov r2,rblk ;it will be in a second mov #rarea,r0 ;point at EMT arg blk .readw ;read a block bcs 50$ ;error, gack 30$: mov rbuf(r1),r0 ;get the word add #2,r5 ;inc ptr clc ;in case R5 was 177776 40$: mov (sp)+,r2 ;[restore] mov (sp)+,r1 rts pc 50$: mov #-1,rblk ;[don't get any ideas] br 40$ ;return, C is already set 60$: sec ;error, out range br 40$ ;return ;+ ; ; Find the bit in either bit map which corresponds to the address in R5. ; ; On return: ; r0 has the proper bit set ; r5 offset into bitmap of word to BIS/BIT ; Others preserved. ; ;- bitnum: mov r5,r0 ;copy byte addr asr r5 ;find word address asr r5 ;/16., *2 to get offset into table asr r5 asr r5 bic #^C7776,r5 ;isolate bic #^C<17*2>,r0 ;find bit number *2 mov bits(r0),r0 ;look up bit rts pc ;+ ; ; Interpret switches parsed by CSI. ; ; Stack: ; (stuff pushed by CSI) ; SP => return addr ; ; Saves values of /S/E/T/B switches in *VAL variables (BVAL is 2 words since ; /B can take two values), *FLG variables are set NZ if switch given. ; ;- switch: mov (sp)+,r5 ;catch return addr clrb bflg ;no switches yet clrb eflg clrb iflg clrb sflg clrb tflg clrb uflg mov (sp)+,r4 ;get switch count beq 110$ ;no switches, use defaults 10$: mov (sp)+,r0 ;get a switch bpl 20$ ;no value given mov (sp)+,r1 ;get value 20$: cmpb r0,#'? ;/? ? beq 50$ ;help bicb #40,r0 ;convert to UC if LC cmpb r0,#'B ;/B:block[:offset] ? beq 30$ ;set base block # (or offset) in file cmpb r0,#'E ;/E:addr ? beq 40$ ;set end of range cmpb r0,#'H ;/H ? beq 50$ ;help cmpb r0,#'I ;/I ? beq 60$ ;insert disassembled block cmpb r0,#'S ;/S:addr ? beq 70$ ;set start of range cmpb r0,#'T ;/T:addr ? beq 80$ ;set transfer addr cmpb r0,#'U ;/U ? beq 90$ ;upper case output .print #badsw ;none of the above, print message .purge #0 ;flush output file jmp csi ;outta here (purge I/O, reset stack) 30$: ; /B:block[:offset] tst r0 ;value given? bpl 100$ ;no mov bval,bval+2 ;any previous value was offset (/B:blk:offs) bic #1,bval+2 ;(must be even) mov r1,bval ;set starting block # movb #1,bflg ;remember that switch was given br 100$ 40$: ; /E:endaddr tst r0 ;value given? bpl 100$ ;no mov r1,eval ;set ending addr movb #1,eflg ;remember that switch was given br 100$ ;skip 50$: ; /Help call progid ;print program ID .print #help ;print msg (will seem odd if in .MAC file!) br 100$ ;skip 60$: ; /Insert (intended for use in input .MAC file only) movb #1,iflg ;remember that switch was given br 100$ ;skip 70$: ; /S:startaddr tst r0 ;value given? bpl 100$ ;no bic #1,r1 ;force even mov r1,sval ;set starting addr movb #1,sflg ;remember that switch was given br 100$ ;skip 80$: ; /T:xfraddr tst r0 ;value given? bpl 100$ ;no bic #1,r1 ;force even mov r1,tval ;set address to start bitmap at movb #1,tflg ;remember that switch was given br 100$ ;skip 90$: ; /Uppercase movb #1,uflg ;remember that switch was given 100$: dec r4 ;loop through all switches bne 10$ 110$: ; update base/offset if /B was given tstb bflg ;/B specified? beq 120$ mov bval,base ;get values mov bval+2,offset 120$: ; output source in upper case if /U was given tstb uflg ;/U specified? beq 150$ mov #ucbeg,r1 ;point at begn of range 130$: movb (r1)+,r0 ;get a char cmp r0,#'a ;lower case? blo 140$ cmp r0,#'z bhi 140$ bicb #40,-1(r1) ;yes, convert to upper 140$: cmp r1,#ucend ;done all? blo 130$ ;loop if not 150$: jmp (r5) ;return ; ; Write out the current set of switches, inside a comment block. ; ;- comswt: movb bflg,r0 ;see if anything specified bisb eflg,r0 bisb sflg,r0 bisb tflg,r0 bisb uflg,r0 beq 10$ ;no .out rem ;start off call outswt ;write switches .out remend ;end of string 10$: rts pc ;+ ; ; Write out the current set of switches (/B and/or /T). ; ; /S, /E, and /I are not passed through since we don't want to insert the same ; block next time through. ; ;- outswt: movb bflg,r0 ;see if anything specified bisb eflg,r0 bisb sflg,r0 bisb tflg,r0 bisb uflg,r0 beq 60$ ;no .out astrsk ;start off tstb bflg ;/B given? beq 10$ ;no .out slab ;/B: mov base,r0 ;get block # call prnum .out colon ;: mov offset,r0 ;get offset within block call prnum 10$: tstb sflg ;/S given? beq 20$ ;no .out slas ;/S: mov sval,r0 ;get starting addr call prnum 20$: tstb eflg ;/E given? beq 30$ ;no .out slae ;/E: mov eval,r0 ;get ending addr call prnum 30$: tstb tflg ;/T given? beq 40$ ;no .out slat ;/T: mov tval,r0 ;get transfer address call prnum 40$: tstb uflg ;/U given? beq 50$ ;no .out slau ;/U 50$: call crlf ; 60$: rts pc ;+ ; ; Print program ID. ; ; R0 destroyed, others preserved. ; ;- progid: .print #header ;print prog ID .if nz rsts ; display system ID as part of version banner (RSTS style) clr r0 ;print system ID .errprt .print #terpri ; .endc rts pc ;+ ; ; Rewind .MAC input file (if any) (start first read). ; ;- rewmac: clrb rmflg ;assume no .MAC file to read clrb rdeof ;no EOF yet clr line ;init line # .wait #4 ;make sure it exists bcs 10$ ;no clr rmblk ;init block # mov #rmbuf,rmba ;init buffer mov #rmbuf+macin,rmoth ;init "other" buffer mov #macin/2,rmwc ;word count mov #rmarea,r0 ;point at EMT area .read ;start first read bcs 20$ ;failed mov r0,rmewc ;save expected WC comb rmflg ;.MAC input file exists 10$: rts pc 20$: tst r0 ;EOF? bne rmerr incb rdeof ;yes, easy sec rts pc rmerr: .print #rerr ;error msg .purge #0 ;flush output file jmp csi ;bomb ;+ ; ; Read next buffer from .MAC input file. ; ; C=1 on EOF, otherwise: ; r5 addr of buffer ; r4 # bytes in buffer ; ;- rdmac: tstb rdeof ;EOF last time? bne 20$ .wait #4 ;wait for previous read to finish bcs rmerr ;error mov rmba,r5 ;get buf addr mov rmewc,r4 ;get expected word count asl r4 ;*2 => byte count add #macin/512.,rmblk ;update blk # for next read mov rmoth,rmba ;switch buffers mov r5,rmoth mov r5,rptr ;save mov r4,rcnt mov #rmarea,r0 ;point at EMT area .read ;read next bufferload bcs 10$ ;error mov r0,rmewc ;[save expected WC for next time] rts pc ;C=0 10$: tst r0 ;EOF? bne rmerr ;no, print msg incb rdeof ;report it next time rts pc ;C=0 from TST 20$: sec ;EOF rts pc ;+ ; ; Read next line from .MAC input file, copy to output if TRASH=0 and doesn't ; start with '*'. If the line *does* start with '*', parse it with .CSISPC ; and call SWITCH. ; ; On return, C=1 if reached EOF, otherwise SWITCH's variables are set up. ; ;- rdline: tst rcnt ;anything in buf? bne 10$ ;yes, skip call rdmac ;refill buf bcc rdline ;loop rts pc ;EOF 10$: inc line ;bump to new line # cmpb @rptr,#'* ;starts with '*'? beq 70$ ;yes ; doesn't start with '*', copy to output if TRASH=0 tstb trash ;well? bne 60$ ;just ignore the line 20$: ; copy input line directly to output movb @rptr,r0 ;get char beq 40$ ;NUL, ignore movb r0,@wptr ;save inc wptr ;count it dec wcnt bne 30$ call wrmac ;flush output buffer movb @rptr,r0 ;restore R0=char 30$: cmp r0,#lf ;EOL? (CR already copied if so) beq 50$ ;yes, skip cmp r0,#ff ;(FF works too) beq 50$ 40$: inc rptr ;eat the char dec rcnt bne 20$ ;loop call rdmac ;read more bcc 20$ ;loop on success rts pc ;EOF, C=1 50$: inc rptr ;eat the LF dec rcnt br rdline ;get next line 60$: ; skip rest of input line cmpb @rptr,#lf ;EOL? beq 50$ cmpb @rptr,#ff beq 50$ inc rptr ;eat the char if not dec rcnt bne 60$ call rdmac ;read more bcc 60$ ;loop on success rts pc ;EOF, C=1 70$: ; line starts with '*', copy to LBUF mov #lbuf,r5 ;point at it br 90$ ;eat the '*', copy line 80$: movb @rptr,r0 ;get a char cmp r0,#lf ;EOL? beq 100$ cmp r0,#ff beq 100$ cmp r0,#40 ;space or ctrl char? blos 90$ ;yes, ignore cmp r5,#lbuf+80. ;buf full? beq 90$ movb r0,(r5)+ ;store char if not 90$: inc rptr ;eat the char dec rcnt bne 80$ ;loop mov r5,-(sp) ;save ptr call rdmac ;read more mov (sp)+,r5 ;[restore] bcc 80$ ;loop br 110$ ;skip 100$: inc rptr ;eat the LF dec rcnt 110$: clrb (r5) ;mark EOL .csispc #sbuf,#defext,#lbuf ;parse switches (ignore any filenames) bcs 120$ ;error call switch ;parse switches (use CALL/RTS not CALLR, stack clc ;is set up from CSI) rts pc 120$: mov #synerr,r0 ;error msg jmp asterr ;+ ; ; Write next buffer to .MAC output file. ; ; WPTR char after last char written in current output buf ; ; R0, R1 destroyed, others preserved. ; ;- wrmac: tstb wmflg ;is file open? beq 20$ ;no, ignore output .wait #0 ;wait for previous write to finish bcs wmerr ;error mov wptr,r1 ;get curr addr bit #1,r1 ;odd? beq 10$ clrb (r1)+ ;yes, pad with NUL (last buffer only) 10$: mov wmoth,r0 ;get the buf we were using mov wmba,wmoth ;switch buffers mov r0,wmba sub r0,r1 ;find # bytes used beq 20$ ;whoops, nothing to do ror r1 ;/2 to get WC (C=0 from SUB) mov r1,wmwc ;set word count mov #wmarea,r0 ;point at EMT area .write ;start next write bcs wmerr ;failed clrb r1 ;(clear low order) swab r1 ;convert to block count add r1,wmblk ;update starting blk for next time 20$: mov wmoth,wptr ;reinit ptr/ctr mov #macout,wcnt rts pc wmerr: .print #werr ;error msg .purge #0 ;flush output file jmp csi ;bomb ; .sbttl pure data ; defext: .rad50 /SAVMACMACMAC/ ;3rd output file not used ; header: .ascii /DISSAV/ ;RSTS programs use a tab here .if nz rsts .ascii ;RSTS programs use a tab here .iff .ascii / / ;but RT-11 programs use a blank .endc .ascii /V1.2/ ;version # .iif nz rsts, .byte ht,200 ;tab, end of string terpri: .byte 0 ;must follow header ; forhlp: .asciz 'Type /H or /? for help' ; crlf$: .byte 15,12,0 synerr: .asciz /?Syntax error/ ireqse: .asciz '?/I requires /S and /E too' badsw: .asciz /?Bad switch/ werr: .asciz /?File write error/ rerr: .asciz /?File read error/ ; help: .ascii "By John Wilson " .ascii "Copyright (C) 1998-2008 by D Bit. " .ascii "All rights reserved." .ascii "Distribution, modification, and use for any purpose is" .ascii " allowed as long as" .ascii "source code is available which includes this notice." .ascii .ascii "Usage:" .ascii "*newsource[.MAC]=binary[.SAV][,oldsource.MAC]/switches" .ascii .ascii .ascii "Switches:" .ascii "/S:start Set starting virtual addr" .ascii "/E:end Set ending virtual addr" .ascii "/T:transfer Set virtual transfer addr" .ascii "/B:base[:offset] Set starting file address of memory image" .ascii .ascii " (BASE=starting block, OFFSET=offset in" .ascii " that block" .ascii "/I Insert block of code here (requires /S/E)," .ascii " useful only" .ascii " within oldsource.MAC" .ascii "/U Write source code in upper case" .ascii .ascii "output file: Disassembled MACRO source" .ascii "input file #1: Binary .SAV file" .ascii "input file #2: MACRO source (N.B. default ext=.SAV) from" .ascii " previous run," .ascii " lines starting with '*' are more switches" .byte 0 ; .even bits: ; table of bits (saves an ASH in BITNUM) .word 000001,000002,000004,000010,000020,000040,000100,000200 .word 000400,001000,002000,004000,010000,020000,040000,100000 ; r50bit: .word 40*50*50,40*50,0 ;bits for probing number in RAD$ ; inslst: ; instruction list instr 000000,000000,,op0 instr 000000,000001,,op0 instr 000000,000002,,op0 instr 000000,000003,,op0 instr 000000,000004,,op0 instr 000000,000005,,op0 instr 000000,000006,,op0 instr 000000,000007,,op0 instr 000077,000100,,op1 instr 000007,000200,,op0.5 instr 000007,000230,,setpri instr 000000,000240,,op0 instr 000000,000257,,op0 instr 000017,000240,,chgflg instr 000000,000277,,op0 instr 000017,000260,,chgflg instr 000077,000300,,op1 instr 000377,000400,
,branch instr 000377,001000,,branch instr 000377,001400,,branch instr 000377,002000,,branch instr 000377,002400,,branch instr 000377,003000,,branch instr 000377,003400,,branch instr 000077,004700,,op1 instr 000777,004000,,op1.5 instrb 100077,005000,,op1 instrb 100077,005100,,op1 instrb 100077,005200,,op1 instrb 100077,005300,,op1 instrb 100077,005400,,op1 instrb 100077,005500,,op1 instrb 100077,005600,,op1 instrb 100077,005700,,op1 instrb 100077,006000,,op1 instrb 100077,006100,,op1 instrb 100077,006200,,op1 instrb 100077,006300,,op1 instr 000077,006400,,op1 instr 000077,006500,,op1 instr 000077,006600,,op1 instr 000077,006700,,op1 instr 000077,007000,,op1 instr 000077,007200,,op1 instr 000077,007300,,op1 instrb 107777,010000,,op2 instrb 107777,020000,,op2 instrb 107777,030000,,op2 instrb 107777,040000,,op2 instrb 107777,050000,,op2 instr 007777,060000,,op2 instr 000777,070000,,muldiv instr 000777,071000,
,muldiv instr 000777,072000,,muldiv instr 000777,073000,,muldiv instr 000777,074000,,op1.5 ; FIS instr 000007,075000,,op0.5 instr 000007,075010,,op0.5 instr 000007,075020,,op0.5 instr 000007,075030,,op0.5 ; CIS instr 000000,076020,,op0 instr 000000,076021,,op0 instr 000000,076022,,op0 instr 000000,076023,,op0 instr 000000,076024,,op0 instr 000000,076025,,op0 instr 000000,076026,,op0 instr 000000,076027,,cis2 instr 000000,076030,,op0 instr 000000,076031,,op0 instr 000000,076032,,op0 instr 000000,076040,,op0 instr 000000,076041,,op0 instr 000000,076042,,op0 instr 000000,076043,,op0 instr 000000,076044,,op0 instr 000000,076045,,op0 instr 000000,076050,,op0 instr 000000,076051,,op0 instr 000000,076052,,op0 instr 000000,076053,,op0 instr 000000,076054,,op0 instr 000000,076055,,op0 instr 000000,076056,,op0 instr 000000,076057,,op0 instr 000000,076060,,op0 instr 000000,076061,,op0 instr 000000,076062,,op0 instr 000000,076063,,op0 instr 000000,076064,,op0 instr 000000,076065,,op0 instr 000000,076066,,op0 instr 000000,076067,,cis3 instr 000000,076070,,op0 instr 000000,076071,,op0 instr 000000,076072,,op0 instr 000000,076073,,op0 instr 000000,076074,,op0 instr 000000,076075,,op0 instr 000000,076076,,op0 instr 000000,076077,,op0 instr 000000,076130,,cis2.5 instr 000000,076131,,cis2.5 instr 000000,076132,,cis4 instr 000000,076140,,cis1.5 instr 000000,076141,,cis1.5 instr 000000,076142,,cis2 instr 000000,076143,,cis2 instr 000000,076144,,cis2.5 instr 000000,076145,,cis2 instr 000000,076150,,cis3 instr 000000,076151,,cis3 instr 000000,076152,,cis2 instr 000000,076153,,cis2 instr 000000,076154,,cis2 instr 000000,076155,,cis2 instr 000000,076156,,cis2.5 instr 000000,076157,,cis2 instr 000000,076170,,cis3 instr 000000,076171,,cis3 instr 000000,076172,,cis2 instr 000000,076173,,cis2 instr 000000,076174,,cis3 instr 000000,076175,,cis3 instr 000000,076176,,cis2.5 instr 000000,076177,,cis2 ; back to regular instructions instr 000000,076600,,op0 ;11/60 -- maint exam/depos instr 000077,076700,,op1 ;11/60 -- ext func code (WCS) instr 000777,077000,,decbra instr 000377,100000,,branch instr 000377,100400,,branch instr 000377,101000,,branch instr 000377,101400,,branch instr 000377,102000,,branch instr 000377,102400,,branch instr 000377,103000,,branch instr 000377,103400,,branch instr 000377,104000,,trpemt instr 000377,104400,,trpemt instr 000077,106400,,op1 instr 000077,106500,,op1 instr 000077,106600,,op1 instr 000077,106700,,op1 instr 007777,160000,,op2 ; FP11 floating point instr 000000,170000,,op0 instr 000000,170001,,op0 instr 000000,170002,,op0 instr 000000,170003,,op0 ;11/60 -- load ubreak reg instr 000000,170004,,op0 ;11/60 -- maint norm shift instr 000000,170005,,op0 ;11/60 -- maint partial prod instr 000000,170011,,op0 instr 000000,170012,,op0 instr 000077,170100,,op1 instr 000077,170200,,op1 instr 000077,170300,,op1 instr 000077,170400,,fop1 instr 000077,170500,,fop1 instr 000077,170600,,fop1 instr 000077,170700,,fop1 instr 000377,171000,,fop2a instr 000377,171400,,fop2a instr 000377,172000,,fop2a instr 000377,172400,,fop2a instr 000377,173000,,fop2a instr 000377,173400,,fop2a instr 000377,174000,,fop2b instr 000377,174400,,fop2a instr 000377,175000,,fop2d instr 000377,175400,,fop2d instr 000377,176000,,fop2b instr 000377,176400,,fop2c instr 000377,177000,,fop2c instr 000377,177400,,fop2a instr 177777,000000,<.WORD >,huh ; astrsk: .asciz /*/ ;start of CSI line slab: .asciz '/B:' ;switches slae: .asciz '/E:' slas: .asciz '/S:' slat: .asciz '/T:' slau: .asciz '/U' ; tab: .asciz space: .asciz / / comma: .asciz /,/ leftp: .asciz /(/ rightp: .asciz /)/ at: .asciz /@/ plus: .asciz /+/ minus: .asciz /-/ colon: .asciz /:/ cmnt: .asciz /;/ char: .asciz / / quote: .asciz /'/ lt: .asciz // ; .sbttl impure data (initialized but will change) ; ucbeg: ; beginning of data to convert to upper case on /U ; ;; .dsabl lc ;uncomment this to make /U the default ; t$word: .asciz /.word/ ;undefined or unexecuted instructions ; r50: .ascii <0>/abcdefghijklmnopqrstuvwxyz$.%0123456789/ ;lower case RAD50 ; rem: .ascii /.rem/ ;beginning of comment block ;(to hide CSI line from assembler) remend: .asciz /!/ ;(also end of comment block) ; t$end: .asciz /.end//start/ ;start at initial entry point b: .asciz /b/ ;suffix for byte instructions sptext: .asciz /sp/ pctext: .asciz /pc/ strt: .asciz /start/ flgnam: .ascii /cvzn/ ;names of flags ; rtext: .asciz /r / ;general register names actxt: .asciz /f / ;FP-11A registers ; ucend: ; end of data to convert to upper case on /U .enabl lc ;in case there was a .DSABL LC above ; .even rarea: .byte 3,10 ;.READW, channel 3 rblk: .word -1 ;block number .word rbuf ;buffer address .word 400 ;word count .word 0 ;no crtn; implicit .WAIT ; rmarea: .byte 4,10 ;.READ, channel 4 rmblk: .word ;block number rmba: .word ;buffer address rmwc: .word ;word count .word 1 ;no crtn; .WAIT for completion ; wmarea: .byte 0,11 ;.WRITE, channel 0 wmblk: .word ;block number wmba: .word ;buffer address wmwc: .word ;word count .word 1 ;no crtn; .WAIT for completion ; number: .asciz /#/ numbuf: .asciz /######/ ;buffer for numbers and .RAD50 symbols ; .sbttl pure storage ; .even ; start: .blkw ;start of range to disassemble end: .blkw ;end of range to disassemble xfer: .blkw ;STARTing address ppc: .blkw ;current pseudo program counter base: .blkw ;base block # in file (this blk contains addr 000000) offset: .blkw ;offset within block # BASE of addr 000000 ; gstart: .blkw ;global start of valid range of file gend: .blkw ;global end of valid range of file ; rcnt: .blkw ;count of unread bytes in current half of RMBUF rptr: .blkw ;current ptr into RMBUF ; wcnt: .blkw ;count of bytes free in current half of WMBUF wptr: .blkw ;current ptr into WMBUF ; .even jmpflg: .blkb ;non-zero if last instruction transferred ;control somewhere; ;negative if the transfer was unconditional adrflg: .blkb ;non-zero if last operand decoded was ;REL or ABS ;ADRFLG *must* follow JMPFLG (both CLRed at once) ; rmflg: .blkb ;NZ => .MAC input file is open wmflg: .blkb ;NZ => .MAC output file is open rdeof: .blkb ;NZ => reached EOF reading .MAC input file bflg: .blkb ;NZ => /B given eflg: .blkb ;NZ => /E given iflg: .blkb ;NZ => /I given sflg: .blkb ;NZ => /S given tflg: .blkb ;NZ => /T given uflg: .blkb ;NZ => /U given trash: .blkb ;OUT trashes output if this is non-zero ; lbuf: .blkb 81. ;line buffer for .CSISPC ; .even addr: .blkw ;target address if ADRFLG is set ; bval: .blkw 2 ;base, offset values eval: .blkw ;ending addr sval: .blkw ;starting addr tval: .blkw ;transfer addr ; sbuf: .blkw 39. ;scratch buf for .CSISPC ; line: .blkw ;current line # in .MAC input file rmewc: .blkw ;expected word count from outstanding .MAC input .READ rmoth: .blkw ;other buffer (besides the one in RMBA) wmoth: .blkw ;other buffer (besides the one in WMBA) ; rbuf: .blkw 400 ;.SAV read buf rmbuf: .blkb macin*2 ;.MAC read buf wmbuf: .blkb macout*2 ;.MAC write buf ; symtab: .blkw 4000 ;1 bit for each word that's a JMP/JSR/BR target bitmap: .blkw 4000 ;1 bit for each word executed insbnd: .blkw 4000 ;1 bit for each instruction's first word ;; INSBND isn't *that* useful, might be better to use the space for ;; bigger I/O buffers? ; devhnd= . ; device handlers go here .end dissav