DOB - Macro-11 Disassembler =========================== User's Guide ============ DOB will disassemble a MACRO-11 object file, which may be a standalone file or part of an object library. While the output code will occasionally contain odd areas where the disassembler has tried to interpret data as code, it is usually clear enough to form an idea of what is going on. In addaition, most standard DEC programs or subroutines use separate PSECTs for data and code, which makes life much easier. Normally the output is in a format akin to a MACRO-11 assembly listing, that is it starts with the current PC, and the octal values of the (up to three) words that comprise the instruction, followed by the instruction itself, followed by a comment. Obviously in disassembled code, meaningful comments cannot be produced, so the comment field is used to hold values for the word(s) making up the instruction converted as ASCII and as RAD50 (useful for data areas). In addition, the /MA switch will be specified. The main effect of this is to omit the output of the current PC and the octal values of the instruction word(s), and also to align the other output records accordingly. In addition it omits a couple of thins the assembler does not like, so that a dissambled file in this form can be fed right back into the Assembler, thus making it easy to change code and replace a module. Note however that care must be taken of branch instructions if this is done. The format of the command to DOB is:- DOB output-filespec=input-filespec The output-filespec contains the disassembled code and defaults to TI: if nothing is specified, and to SY:.DOB if a name is given. The input-filespec defines the object file, and defaults to SY:DISOBJ.OBJ. To disassemble a file from an object library type DOB output-filespec=library-name/LB:module-name where the library-name defaults to LB:[1,1]SYSLIB.OLB if nothing is given,and to SY:.OLB if a name is specified, and the module-name represents the name of the module within the library. /EX may be used as an alternative to /LB. DOB may report the following error messages:- 1. DOB - SST Trap = n at PC nnnnnn ... The program encountered an SST trap during execution. This should never occur during normal operation, and probably indicates a program bug. 2. DOB - Invalid Indirect Command File An indirect command file was specified as input to the program, but it could not be read successfully. Check that you specified the right file name. 3. DOB - Command Read Error = nnnnn An error was reported when the program tried to read its command line. This should never happen and probably indicates a program bug. 4. DOB - Command Line Parse Failure at Offset nn A syntax error was detected in the input command-line at offset nn (decimal) from the start. Correct the line and resubmit it. 5. DOB - Invalid Syntax in Output Filespec The file specification you gave for the output file is invalid. Correct it and resubmit the line. 6. DOB - Invalid Syntax in Input Filespec The file specification you gave for the input file is invalid. Correct it and resubmit the line. 7. DOB - Open Error = nnnnnn on Output File An error occurred when the program tried to open the output file that you specified - this could be anything from a hardware error to a typing error in the file specification. Check the error and the line you typed and act accordingly. 8. DOB - Open Error = nnnnnn on Input File An error occurred when the program tried to open the input file that you specified - this could be anything from a hardware error to a typing error in the file specification. Check the error and the line you typed and act accordingly. 9. DOB - Too Many Output Files Specified DOB only processes one output file at a time, and you specified more than one. Correct your input line and resubmit it. 10. DOB - Too Many Input Files Specified DOB can only process one input file at a time, and you specified more than one. Correct your input line and resubmit it. 11. DOB - Error = nnnnnn Reading Input File An error was encountered reading the input file - probably the file or disk is corrupt. Check the error code and act accordingly. 12. DOB - Invalid Record Type = %D The program encountered a record that was not of a valid type for an object file - probably the input file is corrupt. 13. DOB - Too Many PSECTs in Module The program needs to keep an internal table of PSECTs encountered, and this table has overflowed. Rebuild DOB specifying a larger table. 14. DOB - Unexpected RLD Type = %D The program encountered a record that appeared to be an RLD record, but did not have a valid type field - probably the input file is corrupt. 15. DOB - RLD Type Error = %D in Finishing The program encountered a record that appeared to be an RLD record that required some associated text, but no text was available - probably the input file is corrupt. 16. DOB - RLD ASCII Buffer Mismatch The program encountered a record that appeared to be an RLD record that required some associated text, but the associated text does not match the RLD requirements - probably the input file is corrupt. 17. DOB - Requested Module not in Library The user requested that the module to be disassembled be extracted from a library, but the given library contained no such module. Correct the input line and resubmit it. DOB - Macro-11 Disassembler =========================== System Guide ============ Unfortunately the Disassembler is a rather badly structured program (apart for the disassembly subroutine itself) and I have not had the time/energy to restructure the whole creature. Hopefully the following notes will help anybody rash enough to want to do further modifications. 1. Macros ====== The program makes use of several macros designed to make a lazy programmer's life easier, and making the code far more complicated until you are used to it. The macros defined in various modules are:- FERR This is part of the standard error reporting interface as described in BOILER.DOC DIAG Ditto QIOEXE This macro accepts a buffer address and length as parameters and outputs the given amount to the output file (OUTFDB) and calls TOFC (in PRTOUT) to see if it is time for a new page. BLNKLN This macro accepts a buffer address and length as parameters, stuffs the first into R0, the second into R1, and calls BLNKIT (in DISOBJ) to blank out the buffer. R5ASCL This macro accepts a buffer address and a RAD50 word as parameters, stuffs the first in R0, the second in R1 and calls $C5TA to convert it. CBASCL This macro accepts a buffer address, a binary number, a flag word and a routine name as parameters, stuffs the first in R0, the second in R1, the third in R2 and calls the fourth. BUFTRA This macro accepts a length and address of an output buffer, a length and address of an input buffer and an offset as parameters, and sets up R0 from the output address, R1 from the input address plus the offset, R2 from the input length minus the offset (and stuffs this result in the output length field) and calls BUFTRN (in RECFIL) to copy the input buffer to the output buffer. BLNKCH This macro accepts a buffer address and length as parameters, stuffs the first into R0, the second into R1, and calls BLKCHK (in PRTOUT) to see if the buffer is blank. TRNCBL This macro accepts a buffer address and length as parameters, stuffs the first into R0, the second into R1, and calls TRCBLK (in PRTOUT) to truncate any trailing blanks in the buffer. WORDIN This macro accepts the address of a word field, and inserts a word into it from the location pointed at by R3, allowing for the possibility that R3 is currently odd. SETUP This macro accepts a buffer address as parameter, stuffs it into R0 and R5, does a WORDIN into R1 and clears R2. FINISH This macro takes no arguments, but moves R0 to R4, subtracts R5 from R4 and does a RETURN. It would probably be advantageous for future maintenance to remove all of these macros apart for DIAG, FERR and WORDIN. 2. Flag bits ========= The program uses several flag bits in various places, most of which are well-commented. The main exception are the bits used in the flag word FLWORD, which are very important, and can be very confusing. These are used to control the parallel reading of the RLD and TXT records - which is necessary as the former modify the latter. The bits used are:- DS.NRL = 100 = File buffer (RECBF2) has RLD record DS.NTX = 40 = File buffer (RECBF2) has TXT record DS.RLD = 10 = RLD buffer (RLDBUF) has RLD record DS.TXT = 2 = TXT buffer (TXTBUF) has TXT record DS.RCN = 20 = RLD continuation buffer (RLDBF1) has RLD data DS.TCN = 4 = TXT continuation buffer (TXTBF1) has TXT data 3. DISOBJ ====== DISOBJ is the main module of the program, and is invoked initially. It contains the following major routines: DISOBJ Get command line, and parse it. Set input file to object module or library and open it. If library, search it to find relevant module and position to it. RECSET Read the next record. Check it is a valid type and jump to GSDDEC (in GSDDEC) for GSD record ENDGSD for End of GSD record EOMPRC for End of Module record back to RECSET for all others This just reads through all GSD records until the end is found. ENDGSD Rewinds the file. Calculates the number of PSECTs stored. Calls RECINP to get the next record. If an RLD record sets flag (DS.NRL) and jumps to TXTSET (in TXTSET). If End of Module record jumps to EOMPRC. Else loops back for next record. RECINP Reads next record from input file into RECBF2. BLNKIT Sets R1 characters to blank starting at address in R0. ENDPRC Closes files and loops back for next command. EOMPRC Outputs .END line (possibly with transfer address) and branches to ENDPRC. STRPSC Stores a 2-word PSECT name (in RAD50) in the PSECT table. 4. GSDDEC ====== This module reads all the GSD records, outputting lines to define any PSECTs or global symbols used in the program, and setting up the PSECT table. GSDDEC If first time in set up variables & output heading. GSD001 Initialize the output buffer and branch to a GSD-type-specific routine to set: BFNAM to address of ASCIZ string to output before record contents AFNAM to address of ASCIZ string to output after record contents AFPROC to address of routine for processing after record contents output and then branch to GSDCOM. GSDCOM Copies string from address in BFNAM to output buffer via TXTRAN (in PRTOUT). Converts record name from RAD50 to ASCII and stuffs in buffer. Copies string from address in AFNAM to output buffer via TXTRAN (in PRTOUT). If a routine is specified in AFPROC jumps to it, and if not passes over next two words and branches to FINISH. VALFIN Used for types 1 (CSECT), 2 (Internal Symbol) and 4 (Global symbols defined here) and converts next word in record as octal, stuffs it into the buffer and branches to FINISH. XFSTOR Used for type 3 (Transfer Address) and stores the transfer address and branches to VALFIN. PSECPR Used for type 5 (PSECT). Calls STRPSC (in DISOBJ) to store the PSECT name, decodes the flag byte to ASCII codes and stuffs them in the buffer, and branches to VALFIN to store the length. FINISH Outputs the buffer to the output file. If the record was a type 5 (.PSECT) - and not for . ABS. - it also creates and outputs a PSECT-specific location counter (see later), a .BLKB for the length of the PSECT, and then resets the PC to the start of the PSECT (in case there are any globals). If the record was a type 1 (.TITLE) and /MA was specified it outputs the Floating Point Processor register definitions (e.g. AC0=%0). If there are more GSD records in the buffer it then branches to GSD001, else it branches to RECSET. 5. TXTSET ====== This is the main disassembly control routine and manages the parallel reading of the TXT and RLD records. TXTSET Calls RECFIL (in RECFIL) to read a record. If no text to process (DS.TXT and DS.TCN both clear) branches to STRLD1, else, if not continuation text, sets current PC from PC stored in record. DISPRC If current PC is odd calls WRDPRC (in PRTOUT) and branches to EVEN02, else gets RLD pointer and calls TYPRLD (in PRTOUT) to see what it is, and if it matches the current PC. If match on PC+1, calls WRDPRC (in PRTOUT) and branches to EVEN02. If match on PC, and not TRAP/EMT, calls WRDPRC (in PRTOUT) and branches to EVEN02. Else sets up parameter list and calls DISASS (in DISASS) to disassemble the next bit of text. Updates pointers. If now at end of text buffer branches to EVEN. If past end of buffer branches to UNEVEN. If we are doing text continuation (DS.TCN set) branches to TXTCON, else calls DISOUT (in PRTOUT) and branches back to DISPRC. TXTCON If next record PC is not the one we want, or no text in the TXT buffer, branch to TXTCO1, else call DISOUT (in PRTOUT) and branch to TXTCO2. TXTCO1 Call WRDFIN (in PRTOUT), reset PC and branch to TXTCO2. TXTCO2 Call RLDCON and clear flag (DS.TCN). If text in buffer branch back to DISPRC, else branch to TXTSET. EVEN If doing continuation text (DS.TCN set) and we haven't found what we were looking for, branch to TXTCO1; else call DISOUT (in PRTOUT) and branch to EVEN02. EVEN01 Clear continuation flag (DS.TCN) and branch to STRLD1. EVEN02 Update pointers, and if more text to do branch to DISPRC, else branch to EVEN01 STRLD1 If any RLD to finish (DS.RLD set) call RLDFIN, else clear flags (DS.RLD and DS.RCN) and branch to FINOUT. FINOUT Clear text flag (DS.TXT) and branch to TXTSET. UNEVEN If odd number of bytes over, output .WORDs (via WRDOUT in PRTOUT) and branch to EVEN01; else copy buffer to continuation buffer and set flag (DS.TCN). If any RLD left copy them to continuation as well and set flag (DS.RCN ) and branch to FINOUT. RLDFIN For each remaining RLD record call RLDCN1, and when all finished return. RLDCON For each remaining RLD record call RLDCN1, clear continuation flag (DS.RCN) and return. RLDCN1 If independent type call INDRLD (in PRTOUT) to output it and return, else log an error. 6. DISASS ====== This is the module that does the actual disassembly, and it may be called as a 'standalone' subroutine from other programs. The calling sequence is:- CALL DISASS(IARR,OPT,OPND1,OPND2,ICNT,PC) where IARR = Address of (up to) 3-word code to be disassembled OPT = Address of byte array to store operator code in OPND1 = Address of byte array to store first operand in (if it exists) OPND2 = Address of byte array to store second operand in ( " " ) ICNT = Returned word giving number of words decoded IPC = Current PC (updated & returned) The module contains the standard form of disassembly code, and is fairly straightforward. 7. PRTOUT ====== This module contains the formatting and output of the disassembled code. SKPNUM Advance buffer pointer R4 to end of numeric string. BLKCHK Check to see if R1 characters starting at address in R0 are all blank, and if so set the Z-bit before returning. TRCBLK Given address and length of a string in R0 and R1, modify R1 to be the length of the string without trailing blanks. NMBCHK Check to see if the buffer whose address is in R0 starts with n, #n, @n or @#n (where n is a number) and if so positions R0 to point to n and clears the Z-bit before returning. TYPRLD If still doing an RLD continuation branch to 4$, else, if we have just finished a continuation clear the flag (DS.RCN). If we have an RLD record and have not finished it branch to 4$, else clear the flag (DS.RLD) and see if there is an RLD in the buffer (DS.NRL). If not exit with the Z-bit set, else call RECFIL (in RECFIL), reset the buffer pointer, and branch back to TYPRLD. (4$) Get RLD type and displacement. Modify displacement to real address. Check address against current address and return with the following condition codes set:- C-bit if the addresses match V-bit if the current PC is even, and the RLD address matches the current PC+1 N-bit if the RLD is for a byte INDRLD Call DISRLD (in DISRLD) and output the string so created preceded, if /MA is not specified, by the current address and 4 tabs. BUFCNT Checks the buffers output from DISASS and counts how many are non-blank. TXTRAN Moves R1 characters from R0 to R5, terminating prematurely on a null byte. DISOUT Outputs disassembled instruction, using SETRLD and BYTRLD to handle any RLD records associated with it. WRDOUT Outputs words as .WORD directives. 8. DISRLD ====== This module is responsible for converting any RLD records. DISRLD Get RLD and jump to routine to handle it: RLCNST Type 1 Format .$xxxx+/-constant. GLOBAL Types 2/4 Convert variable from RAD50 to ASCII. CNSTNT Type 3 Convert number to octal. GBLADD Types 5/6 Call GLOBAL and append +/-constant. DPSECT Type 7 Format .PSECT xxxxxx and store .$xxxx. PCMODF Type 10 Format .=.$xxxx+constant. DLIMIT Type 11 Format .LIMIT. RPSECT Types 12/14 Formats .$xxxx. ADPPSC Types 15/16 Calls RPSECT and appends +/- constant. COMPLX Type 17 All the clever stuff for complex types. PUSH/POP Used for stack handling by COMPLX. C5TASC Call $C5TA and move R0 back over any trailing blanks. 9. RECFIL ====== This module reads the input file, and sets up the relevant buffers. RECFIL If RLD records (DS.RLD set) with no TXT records (DS.TXT set) call RLDFIN (in TXTSET) to finish them off and clear flags (DS.RLD and DS.RCN). If buffer has unused RLD records (DS.NRL set) branch to RLDFIL. If buffer has unused TXT records (DS.NTX set) branch to TXTFIL. If end record has not been seen (DS.END clear) branch to READIN. If end record seen and nothing left to do jump to EOMPRC (in DISOBJ); else return. RLDFIL If RLD buffer full (DS.RLD set) return, else copy record buffer to RLD buffer, set DS.RLD, clear DS.NRL, and branch to RECFIL setting RLD pointer if not in continuation (DS.RCN clear). TXTFIL Analagous to RLDFIL but for TXT, and also sets current PC from text record. READIN Call RECINP (in DISOBJ) to read in a record. If it is a TXT record, set flag (DS.NTX) and branch to RECFIL. If it is a RLD record, set flag (DS.NRL) and branch to RECFIL. If it is a EOM record, set flag (DS.END) and branch to RECFIL. Ignore all other legal record types and branch back to READIN. 10. ERRMSG ====== This module defines all the error message strings, and contains the routine FERR to log all errors. 11. RCDATA ====== This module defines various data areas. 12. Technical points on output. =========================== Because of the requirement to produce an output that can be input to the macro assembler (with the /MA switch) it was necessary to find a way to output relocatable references (either across PSECTs or within a PSECT) in such a way as to generate the correct object code when assembled. The method adopted was as follows. Each PSECT has associated with it a PSECT 'location counter' which is labelled .$cdef if the PSECT was called abcdef - this format was chosen because most standard DEC PSECTs differ in the last four characters, but often not in the first two, and because it was thus unlikely to clash with an existing symbol (the straight PSECT name would be dangerous as much of RMS-11 uses the same PSECT name as the module name). These symbols are defined when the PSECT is initially defined (in the GSD records) and are then used in all references. For example, the code:- .PSECT FRED A: .WORD .PSECT GEORGE B: .WORD MOV A,B would be disassembled (approximately) as:- : : .PSECT FRED... .$ED=. : : .PSECT GEORGE... .$ORGE=. : : .PSECT GEORGE .=.$ORGE MOV .$ED+0,.$ORGE+0 which would assemble into the same object code as the first. There are two problems in this approach. Firstly it means that all branches are code in 'absolute' form so that code cannot easily be inserted into or deleted from the disassembled module without having to change all the branches. The alternative was considered of labelling all the statements (in the form annnnn: where a was an initial letter dependent on the PSECT number, and nnnnn was the current PC) but this leads to difficulties when 'data' converts into out-of-range branches - MACRO-11 will accept this in the format we have adopted, but not in the other format. If somebody can think of a way around this problem it would be a good thing to implement. The second problem is more obscure, more rare - and, unfortunately, more awkward. If the 'data' area of a program being disassembled contains something like:- .WORD 377 .WORD FRED then this will be disassembled as something like SWAB @.$xxxx+n where n is the offset of FRED inside PSECT xxxxxx. Unfortunately this is by no means the same as the first format and will assemble differently. I have not yet had time to find a solution for this one, and can only suggest that if you want to be absolutelu sure your disassembly is right, re-assemble and re-disassemble the output and compare the two disassemblies. 13. Building DOB ============ DOB is assembled and built by the command file DOB.CMD. If just @DOB is typed, all the modules will be assembled and the task will be task-built. If @DOB modulename is typed, just the module specified is assembled, and the task is task built. DOB is a non-privileged task, with a taskname of ...DOB and using 6 units.