;IMSDSM.A - Transputer software image disassembler ; Author John Otis Lene-Comeau, Industrial Control Technology ; Public Domain for what it's worth ; #include ; first hacked together 12/20/93 ; known bug: calculates prefixed jumps wrong, can't figure out why... ; Email to jcomeau@world.std.com dos equ 21h ; setop macro ;set opcode in output line ;specify opcode as a string or as a register which points to a string push cx ;save count register in any case push si ;save source and dest regs ##if !#s1-2 ;if register mov cl,dl ;get length of string xor ch,ch ;clear high byte mov si,#1 ;get register mov di,offset outline+8 ;destination rep movsb ;store the mnemonic ##else mov cl,#s1-2 ;get length of string xor ch,ch ;clear high byte mov si,offset >m1 ;point to where we'll store the string mov di,offset outline+8 ;destination rep movsb ;store in output line jmp >m2 m1: db #1 m2: ;end of string ##endif pop si ;restore registers, leave DI where it points pop cx #em ; posit macro ;position to field in output string cmp di,offset outline+#1 ;already there? jb >m1 ;if not, use specified offset inc di ;else point up one space from current loc jmp >m2 m1: mov di,offset outline+#1 ;use offset provided m2: ;end of macro #em ; sethex macro ;translate CX:DX into Hexadecimal ##if !#s1 ;if no parameter specified mov di,offset outline+16 ;point to arg field ##else cmp di,offset outline+#1 ;see if DI already there jb >m1 ;if not, use specified offset inc di ;else point up one space from current loc jmp >m2 m1: mov di,offset outline+#1 ;use offset provided m2: ##endif call sethexr #em ; setdec macro ;set arg in CX:DX to decimal call setdecr #em ; opcode macro ;build opcode table dw #2 ;first the value db #s1-2 ;then count byte of chars in mnemonic string db #1 ;then the string itself #em ; putline macro ;send line to output file call putliner #em ; nextbyte macro call nextbyter #em ; hexoutb macro ;output byte as hexadecimal push ax mov al,byte #1 ;get it call hexout_byte pop ax #em ; hexoutw macro ;output word as hexadecimal push ax mov ax,word #1 ;get arg push ax xchg ah,al ;put high byte first call hexout_byte pop ax call hexout_byte pop ax #em ; putchar macro ;output char at current DI pointer push ax mov al,#1 stosb pop ax #em ; disasm: xor cx,cx ;clean out storage registers mov dx,cx i9: nextbyte ;get next byte jc >i1 ;quit if no more aam 10h ;split into nybbles mov bl,ah ;get function code shl bx,1 ;make word offset shl cx,1 ;now shift CX:DX up a nybble rcl dx,1 shl cx,1 rcl dx,1 shl cx,1 rcl dx,1 shl cx,1 rcl dx,1 add cl,al ;store low nybble of input byte call ftable[bx] ;and go to its routine jmp i9 ;loop back until done i1: mov ax,4c00h ;exit int dos ftable: dw ims_j,ims_ldlp,ims_pfix,ims_ldnl dw ims_ldc,ims_ldnlp,ims_nfix,ims_ldl dw ims_adc,ims_call,ims_cj,ims_ajw dw ims_eqc,ims_stl,ims_stnl,ims_opr ims_j: mov ax,cx ;get low word of jump destination or ax,dx ;see if it's 0 je >i1 ;if so, special routine setop 'j' ;otherwise mnemonic is 'j' sethex ;convert arg to hex (AX:DX) putline ;and send line to output ret ;return i1: setop 'jump 0 ;used for debugging' putline ;send to output ret ims_ldlp: setop 'ldlp' ;set mnemonic for "load local pointer" setdec ;translate arg to signed decimal putline ;send to output ret ims_pfix: ret ;do nothing for "prefix" ims_ldnl: setop 'ldnl' ;"load non-local" setdec ;signed decimal putline ;send to output ret ims_ldc: setop 'ldc' ;"load constant" setdec ;signed decimal putline ;send to output ret ims_ldnlp: setop 'ldnlp' ;"load non-local pointer" sethex ;hexadecimal putline ;send to output ret ims_nfix: or dx,dx ;see if number is already negative jl >i1 ;continue if so mov dx,0ffffh ;else start off with -1 or cx,0fff0h ;... i1: mov al,cl ;get low byte of arg word not al ;get ones' complement and al,0fh ;and clear high nybble and cl,0f0h ;clear low nybble of CL or cl,al ;and merge complemented nybble ret ims_ldl: setop 'ldl' ;"load local" setdec ;signed decimal putline ;send to output ret ims_adc: setop 'adc' ;"add constant" setdec ;signed decimal putline ;send to output ret ims_call: setop 'call' ;"call" sethex ;hexadecimal putline ;send to output ret ims_cj: setop 'cj' ;"conditional jump" sethex ;hex for address putline ;send to output ret ims_ajw: setop 'ajw' ;"adjust workspace" sethex ;hexadecimal putline ;send to output ret ims_eqc: setop 'eqc' ;"equals constant" setdec ;signed decimal putline ;send to output ret ims_stl: setop 'stl' ;"store local" setdec ;signed decimal putline ;send to output ret ims_stnl: setop 'stnl' ;"store non-local" setdec ;signed decimal putline ;send to output ret ims_opr: ;this instruction effects all the "opcodes", so it's handled differently mov bx,offset optable ;start at beginning of opcode table i1: mov ax,[bx] ;get opcode at current table address add bx,2 ;point past opcode word or ax,ax ;see if end of table jge >i8 ;continue if non-negative, or... jmp >i3 ;skip ahead if so i8: mov dl,[bx] ;get length byte inc bx ;and point to first char of mnemonic cmp ax,cx ;see if this opcode is the one from program jne >i2 ;skip if not setop bx ;else load mnemonic into output line putline ;and send to output ret ;return to main routine i2: xor dh,dh ;clear high byte of length word add bx,dx ;point past mnemonic jmp i1 ;and loop around i3: setop 'data' ;treat it as data if not valid opcode sethex ;show as hexadecimal putline ;send to output ret ; sethexr: push ax push cx mov al,dh call >s1 ;convert nybbles to hex mov al,dl ;ditto on down the line call >s1 mov al,ch call >s1 mov al,cl call >s1 mov al,'h' ;mark it as hex stosb ;... pop cx ;restore registers pop ax ret s1: aam 10h ;split nybbles add ax,3030h ;make both into digits cmp ah,3ah ;or is it A-F? jl >s2 ;skip if so add ah,27h ;else make it lowercase letter s2: cmp al,3ah ;perform same check on AL jl >s3 add al,27h s3: xchg al,ah ;swap bytes stosw ;store the word ret ; hexout_byte: ;output byte in AL as hexadecimal string at current DI ; update DI as we go push ax aam 10h ;split nybbles add ax,3030h ;make both into digits cmp ah,3ah ;or is it A-F? jl >s2 ;skip if so add ah,27h ;else make it lowercase letter s2: cmp al,3ah ;perform same check on AL jl >s3 add al,27h s3: xchg al,ah ;swap bytes stosw ;store the word pop ax ret ; setdecr: push ax ;save registers push bx mov di,outline+16 ;point DI to arg output field test dx ;see if DX is negative jge >s2 ;skip ahead if not not dx ;else get two's complement of doubleword not cx sub cx,1 sbb dx,0 ;...that does it mov al,'-' ;show as negative stosb ;... s2: mov bx,offset dectable ;point to table of decimal numbers xor ah,ah ;clear to indicate "no output yet" s9: mov al,'0' ;start AL off with ASCII 0 s3: cmp dx,[bx+2] ;compare high word to what's in the table ja >s4 ;skip low word compare if already higher jb >s5 ;skip also if less cmp cx,[bx] ;else compare low word jae >s4 ;go and subtract if higher or equal s5: test ah ;have we already started output? jne >s6 ;skip if so, output this cmp al,'0' ;have something worth showing? jg >s6 ;go show it jmp >s7 ;any other case, skip it s4: sub cx,[bx] ;longword subtract sbb dx,[bx+2] inc al ;inc decimal digit in AL jmp s3 ;loop back around s6: stosb ;send byte to output mov ah,al ;show that we've output something s7: add bx,4 ;point to following number test word [bx] ;end of table? jne s9 ;loop if not test ah ;have we output anything yet? if z stosb ;store a zero if not pop bx ;restore registers pop ax ret dectable: dd 10000000000 ;10 billion on down dd 1000000000 dd 100000000 dd 10000000 dd 1000000 dd 100000 dd 10000 dd 1000 dd 100 dd 10 dd 1 dd 0 ; putliner: ;modified to show instruction pointer as comment posit 30 ;place in comment field putchar ';' ;mark as comment hexoutw [last_ims_ip] ;output IP as hexadecimal word mov dx,offset outline mov ah,40h ;dos WRITE function mov al,13 ;store CRLF at end of current line stosb mov al,10 stosb mov cx,di ;point to end of current line sub cx,offset outline ;subtract address of line start mov bx,1 ;handle of STDOUT, redirectable from command line int dos ;call DOS for service mov di,offset outline ;now clean the line for next go-round mov cx,79 mov si,offset freshline rep movsb xor cx,cx ;clear CX:DX for next go-round mov dx,cx mov ax,word [ims_ip] ;make current IP "last" IP mov word [last_ims_ip],ax mov ax,word [ims_ip+2] mov word [last_ims_ip+2],ax ret ; nextbyter: push cx ;save some registers push dx push bx xor bx,bx ;point to STDIN push ax ;just to make room on stack mov ah,3fh ;dos READ function mov cx,1 ;just get one byte mov dx,sp ;location of byte "buffer" int dos jc >i2 ;quit if error or ax,ax ;see if we got anything jnz >i1 ;skip if so stc ;else set carry jmp >i2 ;and return i1: add word [ims_ip],1 ;inc doubleword instruction pointer adc word [ims_ip+2],0 ;... i2: pop ax ;get byte just read pop bx ;restore registers pop dx pop cx ret ; ims_ip: dd 0 ;IMS instruction pointer, updated each fetch last_ims_ip: dd 0 ;pointer to current instruction ; freshline: db " " db " ",13,10 outline: db " " db " ",13,10 optable: ;arithmetic/logical opcodes opcode 'and',46h ;logical AND opcode 'or',4bh ;logical OR opcode 'xor',33h ;exclusive OR opcode 'not',32h ;bitwise NOT opcode 'shl',41h ;shift left opcode 'shr',40h ;shift right opcode 'add',5 ;add opcode 'sub',0ch ;subtract opcode 'mul',53h ;multiply opcode 'fmul',72h ;fractional multiply opcode 'div',2ch ;divide opcode 'rem',1fh ;remainder opcode 'gt',9 ;greater than opcode 'diff',4 ;difference opcode 'sum',52h ;sum opcode 'prod',8 ;product for register A ;long arithmetic opcodes opcode 'ladd',16h ;long add opcode 'lsub',38h ;long subtract opcode 'lsum',37h ;long sum opcode 'ldiff',4fh ;long difference opcode 'lmul',31h ;long multiply opcode 'ldiv',1ah ;long divide opcode 'lshl',36h ;long shift left opcode 'lshr',35h ;long shift right opcode 'norm',19h ;normalize ;general opcodes opcode 'rev',0 ;reverse opcode 'xword',3ah ;extend to word opcode 'cword',56h ;check word opcode 'xdble',1dh ;extend to doubleword opcode 'csngl',4ch ;check single opcode 'mint',42h ;minimum integer opcode 'dup',5ah ;duplicate top of stack opcode 'pop',79h ;pop processor stack ;floating point support opcodes opcode 'cflerr',73h ;check floating point error opcode 'fptesterr',9ch ;load value true (FPU not present) opcode 'unpacksn',63h ;unpack single length fp number opcode 'roundsn',6dh ;round single length fp number opcode 'postnormsn',6ch ;post-normalize correction of ;single-length fp number opcode 'ldinf',71h ;load single length infinity ;block move opcodes opcode 'move2dinit',5bh ;initialize data for 2d block move opcode 'move2dall',5ch ;2d block copy opcode 'move2dnonzero',5dh ;2d block copy non-zero bytes opcode 'move2dzero',5eh ;2d block copy zero bytes ;CRC and bit opcodes opcode 'crcword',74h ;calculate CRC on word opcode 'crcbyte',75h ;calculate CRC on byte opcode 'bitcnt',76h ;count bits set in word opcode 'bitrevword',77h ;reverse bits in word opcode 'bitrevnbits',78h ;reverse bottom n bits in word ;indexing/array opcodes opcode 'bsub',2 ;byte subscript opcode 'wsub',0ah ;word subscript opcode 'wsubdb',81h ;form double word subscript opcode 'bcnt',34h ;byte count opcode 'wcnt',3fh ;word count opcode 'lb',1 ;load byte opcode 'sb',3bh ;store byte opcode 'move',4ah ;move message ;timer handling opcodes opcode 'ldtimer',22h ;load timer opcode 'tin',2bh ;timer input opcode 'talt',4eh ;timer alt start opcode 'taltwt',51h ;timer alt wait opcode 'enbt',47h ;enable timer opcode 'dist',2eh ;disable timer ;input/output opcodes opcode 'in',7 ;input message opcode 'out',0bh ;output message opcode 'outword',0fh ;output word opcode 'outbyte',0eh ;output byte opcode 'alt',43h ;alt start opcode 'altwt',44h ;alt wait opcode 'altend',45h ;alt end opcode 'enbs',49h ;enable skip opcode 'diss',30h ;disable skip opcode 'resetch',12h ;reset channel opcode 'enbc',48h ;enable channel opcode 'disc',2fh ;disable channel ;control opcodes opcode 'ret',20h ;return opcode 'ldpi',1bh ;load pointer to instruction opcode 'gajw',3ch ;general adjust workspace opcode 'gcall',6 ;general call opcode 'lend',21h ;loop end ;scheduling opcodes opcode 'startp',0dh ;start process opcode 'endp',3 ;end process opcode 'runp',39h ;run process opcode 'stopp',15h ;stop process opcode 'ldpri',1eh ;load current priority ;error handling opcodes opcode 'csub0',13h ;check subscript from 0 opcode 'ccnt1',4dh ;check count from 1 opcode 'testerr',29h ;test error false and clear opcode 'seterr',10h ;set error opcode 'stoperr',55h ;stop on error opcode 'clrhalterr',57h ;clear halt-on-error opcode 'sethalterr',58h ;set halt-on-error opcode 'testhalterr',59h ;test halt-on-error ;processor initialization opcodes opcode 'testpranal',2ah ;test processor analyzing opcode 'saveh',3eh ;save high priority queue registers opcode 'savel',3dh ;save low priority queue registers opcode 'sthf',18h ;store high priority front pointer opcode 'sthb',50h ;store high priority back pointer opcode 'stlf',1ch ;store low priority front pointer opcode 'stlb',17h ;store low priority back pointer opcode 'sttimer',54h ;store timer opcode 'lddevid',17ch ;load device identity opcode 'ldmemstartval',7eh ;load value of memstart address ;debugger support codes opcode 'break',0b1h ;break opcode 'clrj0break',0b2h ;clear jump 0 break enable flag opcode 'setj0break',0b3h ;set jump 0 break enable flag opcode 'testj0break',0b4h ;test jump 0 break enable flag set opcode 'timerdisableh',7ah ;disable high priority timer interrupt opcode 'timerdisablel',7bh ;disable low priority timer interrupt opcode 'timerenableh',7ch ;enable high priority timer interrupt opcode 'timerenablel',7dh ;enable low priority timer interrupt ;end of opcode table ; dw -1