name dasm ;++ ; ; Disassembler for absolute images of Z-80, 8080, 8051, or 8086 code. ; ; Traces code before disassembling to determine what's code and what's data, ; and where there should be labels. ; ; Copyright (C) 1994-1999 by John Wilson. All rights reserved. ; ; This program may be distributed freely provided that no charge is made for ; distribution and proper credit is given to John Wilson in all versions. ; ; 01/31/92 JMBW Created. ; 02/02/92 JMBW Added Intel mnemonics. ; 02/04/92 JMBW 8086 .COM files. ; 03/10/94 JMBW Fixed names of undoc'ed IX/IY halves to common usage. ; 12/13/97 JMBW 8051 mode. V1.1. ; 06/12/99 JMBW Improved support for 80386 and later. ; 08/21/99 JMBW Preliminary PDP-8 support. V1.2. ; ;-- .radix 8 ; tab= 11 lf= 12 cr= 15 ; op macro name,opcode,mask,dispat local foo,bar db mask,opcode ifidn ,<''> db 0 ;;no output else ifidn ,<""> db 200 ;;same as previous mnemonic else foo db bar,name bar= $-foo-1 endif endif dw dispat ;;addr called to display operands ;;dispat-2 is called during trace (DRY) endm ; opt macro name,opcode,mask,dispat local foo,bar db mask,opcode foo db bar,name,tab ;;same thing with a tab after it bar= $-foo-1 dw dispat endm ; ; Word (16-bit) versions of above: opw macro name,opcode,mask,dispat local foo,bar dw mask,opcode ifidn ,<''> db 0 ;;no output else ifidn ,<""> db 200 ;;same as previous mnemonic else foo db bar,name bar= $-foo-1 endif endif dw dispat ;;addr called to display operands ;;dispat-2 is called during trace (DRY) endm ; opwt macro name,opcode,mask,dispat local foo,bar dw mask,opcode foo db bar,name,tab ;;same thing with a tab after it bar= $-foo-1 dw dispat endm ; ; Stuff that precedes the beginning of a mnemonic table porg= word ptr -5 ;routine to print "ORG" statement plabel= word ptr -3 ;routine to print a label ocsize= byte ptr -1 ;opcode size in bytes (1 or 2) ; symtab macro nam,org,lab,siz ;start a symbol table dw &org ;routine to print "ORG" statement dw &lab ;routine to print a label db &siz ;opcode size (1 or 2 bytes) &nam label byte ;beginning of table endm ; ; Symbol table is in the first 16 KB of OBUF, and contains two bits for each of ; the possible 64 K addresses of the Z-80. Of each pair, the even-numbered bit ; is set if that address is jumped to, and the odd bit is set if that address ; is referred to as data. ; inst= 125 ;even bits mean it's an instruction (jump dest) data= 252 ;odd bits mean it's data (can be both) stsize= 40000 ;size of symbol table in bytes (16 KB) ; code segment assume cs:code ; ; Assumed registers: ; ; In general, DF=0 (always), ; BP=Z-80 PC (while disassembling), ; and DS=IBUF, ES=OBUF (after init). ; start: ; start here cld ;move forwards push cs ;toss CS to ES pop es ; parse command line mov ax,3700h ;func=get SWITCHAR int 21h ;into DL (throughout startup code) mov si,80h ;pt at JCL lodsb ;get length cbw ;AH=0 mov bx,ax ;copy mov [bx+si],ah ;.ASCIZ nxtatm: ; parse next atom from command line call skip ;skip blanks jz eol ;eol cmp al,dl ;SWITCHAR? je swit1 ;yes jmp fn ;no, must be filename swit1: ; handle switch inc si ;eat it lodsw ;get the switch, colon and al,not 40 ;upper case cmp al,'I' ;Intel mnemonics? je switi cmp al,'N' ;native mnemonics? je switn cmp ah,':' ;others must have colon jne badjcl cmp al,'S' ;disassembly starting addr je swits cmp al,'E' ;disassembly ending addr je swite cmp al,'T' ;transfer address (or @file (contains list)) je switt cmp al,'F' ;file starting addr (if not 100h) je switf cmp al,'C' ;CPU type je switc badjcl: ; invalid switch, punt with help message mov di,offset help+2 ;point at help text mov cx,lhelp ;length mov al,'~' ;char to replace with SWITCHAR push cs ;copy CS to ES pop es bjcl1: repne scasb ;look for next ~ jne bjcl2 ;none left mov es:[di-1],dl ;replace with SWITCHAR jmp short bjcl1 ;keep looking bjcl2: mov si,offset help ;pt at msg jmp errout ;punt eol: jmp init ;init and go swits: ; /S:starting disassembly address (default=0100) call gethex ;get # jc badjcl ;error mov cs:dstart,ax ;save jmp short nxtatm ;loop swite: ; /E:ending disassembly address (default=FFFF) call gethex ;get # jc badjcl ;error mov cs:dend,ax ;save jmp short nxtatm ;loop switf: ; /F:file load address (default=0100) call gethex ;get # jc badjcl ;error mov cs:faddr,ax ;save jmp short nxtatm ;loop switc: ; /C:cpu CPU type call getwd ;get keyword jc badjcl ;error if none mov ax,offset cpus ;point a table call tbluk ;look up this CPU jc badjcl ;error if not found mov cs:mnem,ax ;save starting addr of mnem table jmp nxtatm ;loop switi: ; /I Intel 8080/8085 mnemonics (default is Zilog) dec si ;unget next char (don't want colon) mov cs:mnem,offset intel ;set ptr jmp nxtatm ;loop switn: ; /N native (80x86) mnemonics dec si ;unget next char (don't want colon) mov cs:mnem,offset iapx86 ;set ptr jmp nxtatm ;loop switt: ; /T:transfer address (or @file -- list of addresses) call gethex ;get # jc tradr1 ;nope mov cs:xfer,ax ;save jmp nxtatm tradr1: lodsb ;get char cmp al,'@' ;@? jne badjcl tradr2: lodsb ;skip blanks/tabs or al,al ;EOL? jz badjcl cmp al,' ' ;blank or CC? jbe tradr2 ;yes, ignore dec si ;back up mov di,si ;save tradr3: lodsb ;get a char cmp al,' ' ;blank/ctrl char? jbe tradr4 ;yes, end of filename cmp al,dl ;SWITCHAR? jne tradr3 ;loop if not tradr4: dec si ;unget xor dh,dh ;get a 0 xchg dh,[si] ;save, mark end xchg di,dx ;save dx, get start of string mov ax,3D00h ;func=open, /RONLY int 21h ;do it jc tradr5 ;oops mov cs:xhand,ax ;save mov dx,di ;restore mov [si],dh ;replace terminating char jmp nxtatm ;loop tradr5: mov si,offset xfrerr ;msg jmp errout fn: ; must be a filename mov di,offset fname ;pt at buffer xor bx,bx ;no extension yet fn1: lodsb ;get a char cmp al,' ' ;end of filename? jbe fn5 cmp al,dl ;hm? (SWITCHAR) je fn5 cmp al,'/' ;pathname separator? je fn4 cmp al,'\' ;either is OK with me je fn4 cmp al,'.' ;OK, then could it be an extension? je fn3 fn2: stosb ;bo-ring jmp short fn1 ;loop fn3: ; . seen, this is the extension unless / or \ follows mov bx,di ;copy ptr jmp short fn2 ;save the . fn4: ; / or \, if we saw a . it wasn't the extension xor bx,bx ;zap jmp short fn2 ;save fn5: ; end of filename dec si ;unget terminator test bx,bx ;do we have an extension? jnz fn6 ;yes ; add .COM extension mov bx,di ;ext will be here mov ax,"C." ;.C stosw mov ax,"MO" ;OM stosw fn6: xor al,al ;mark end stosb ; open the input file push ds ;save push dx push bx xor bx,bx ;first close old STDIN mov ah,3Eh ;func=close int 21h push cs ;copy cs pop ds ;to ds mov dx,offset fname ;pt at filename mov ax,3D00h ;func=open, /RONLY int 21h ;should appear on STDIN jc inoper ;punt ; now create output file pop bx ;restore ptr to extension inc bx ;+1 mov di,bx ;copy mov ax,[bx] ;get 1st 2 chars of ext and ax,not 20040 ;cvt to upper cmp ax,"SA" ;AS jne notasm mov al,[bx+2] ;get 3rd char and al,not 40 cmp al,'M' ;ASM? je opokay ;sigh, just write on STDOUT notasm: mov ax,"SA" ;AS stosw mov ax,'M' ;M<0> stosw mov bx,1 ;STDOUT mov ah,3Eh ;func=close old STDOUT int 21h mov dx,offset fname ;pt at filename xor cx,cx ;mode=0 mov ah,3Ch ;func=create int 21h jnc opokay ;opened OK ; open error, print msg (on STDERR!) mov si,offset enterr ;error entering file jmp short errout inoper: ; input file open error mov si,offset lukerr ;error looking up file errout: ; dump msg, CS:SI='message' push cs ;toss CS to DS pop ds lodsw ;get length mov dx,si ;addr mov cx,ax ;len mov bx,2 ;STDERR mov ah,40h ;func=write int 21h mov ax,4C01h ;func=punt int 21h opokay: ; (gasp) everying is OK pop dx ;restore pop ds jmp nxtatm ;around for more ; init: ; initialize memory to HALTs ; (so we won't get lost if they JMP into space) mov ax,seg ibuf ;pt at bufs mov ds,ax mov es,ax ;for now xor di,di ;pt at begn mov cx,8000h ;32KW mov ax,166+(400*166) ;fill with HALT instructions rep stosw ;leaving di at 0 again ; load the file mov dx,cs:faddr ;get file load addr xor bx,bx ;STDIN mov ah,3Fh ;func=read sub cx,dx ;get length (CX=0 from REP STOSW above) jnz load1 ;not reading 64 K, skip ; all because DOS can't read more than 64 K -1 at a time (groan) mov ch,80h ;count=8000h (32 KB) int 21h ;do the read jc lderr ;err add dx,ax ;update load addr mov ah,3Fh ;func=read load1: int 21h ;read (either way we got here) jc lderr ;err add ax,dx ;update load addr dec ax ;-1=last addr mov cs:last,ax ;save it cmp cs:dend,ax ;is end off end of file? jbe $+6 ;no mov cs:dend,ax ;yes, fix mov ax,seg obuf ;pt at output buf mov es,ax ; erase symbol table (di still 0 from above) mov cx,stsize/2 ;zap symbol table xor ax,ax ;0's rep stosw ;yep (leave di pointing at free buf space) ; trace from xfer address(es) mov bx,cs:xhand ;get handle, if any or bx,bx jnz xa1 ;file is open, read it ; easy case, just one transfer address mov bp,cs:xfer ;get transfer address call dry ;dry run (trace execution) jmp short xa6 lderr: mov si,offset lderrm ;error msg jmp errout ;go dump it xa1: push ds ;save push es ;toss ES to DS pop ds mov dx,di ;pt at free space (=STSIZE) mov cx,177000-stsize-1 ;get # free bytes -1 mov ah,3Fh ;func=read int 21h ;do it jc xa3 push ax ;save mov ah,3Eh ;func=close int 21h pop bx ;restore mov [bx+di],dl ;mark EOF with 0 (low byte of STSIZE) mov si,dx ;point at begn xa2: call skip ;skip blanks jz xa5 ;eof push ax ;save start char cmp al,'#' ;#? jne $+3 inc si ;yes, skip call gethex ;get an address (DL=0 from STSIZE above) pop bx ;[restore char] jc xa5 ;no more cmp bl,'#' ;#? je xa4 ;yes mov bp,ax ;set PC pop ds ;restore push si ;save call dry ;dry trace pop si ;save push ds ;save again mov ax,es ;copy ES to DS mov ds,ax jmp short xa2 ;loop for next addr xa3: mov si,offset xrderr ;read error jmp errout xa4: ; #HHHH, enter HHHH in data S.T. mov bl,data ;flag call addsym ;add to table jmp short xa2 ;next xa5: pop ds ;restore xa6: ; do the initial ORG statement mov ax,cs:dstart ;starting addr mov bp,ax ;set PC mov si,cs:mnem ;pt at table call word ptr cs:porg[si] ;print ORG call crlf ;crlf mov ax,cs:dend ;get ending addr mov cs:last,ax jmp newblk ;actually disassemble ;+ ; ; "Dry run" entry. ; ; Enter with BP set to instruction to start at. ; ; Instructions are disassembled from this point on until we reach an ; instruction at which we must stop (HLT, RST 0, unconditional RET, JP (HL), or ; any undefined opcode). ; ; We follow unconditional branches; when we encounter CALLs and conditional ; branches we recursively take both paths. ; ; Once we have entered the program at every possible entry point (initially the ; only entry is at 100h (by default), but after looking at our listing the user ; may find jump tables, interrupt service routines, etc. that we couldn't ; interpret, and so they may tell us about other entries to get a better ; disassembly). ; ;- dry: mov ax,bp ;get PC call chksym ;defined? test ah,inst ;as an instruction? jnz dry3 ;yes, no point in continuing mov ax,bp ;no, define it mov bl,inst ;as a jump destination call addsym xdry: ; (enter here if above already done) cmp bp,cs:last ;off EOF? ja dry3 ; init prefixes in case 8086 mov byte ptr cs:opsize,0 mov byte ptr cs:adsize,0 mov si,cs:mnem ;pt at table cmp byte ptr cs:ocsize[si],2 ;2-byte opcodes? je dry4 drypfx: ; reenter here to search for prefixed opcodes call getb ;get byte mov dl,al ;copy dry1: lods word ptr cs:[si] ;get opcode,,mask and al,dl ;AND this opcode with mask cmp al,ah ;does this give the opcode we want? je dry2 lods byte ptr cs:[si] ;no, get length and ax,177 ;remove "ditto" bit, AH=0 add si,ax ;skip inc si ;skip vector inc si jmp short dry1 ;loop dry2: ; got it lods byte ptr cs:[si] ;get length and ax,177 ;remove "ditto" bit, AH=0 add si,ax ;skip lods word ptr cs:[si] ;get vector dec ax ;-2 (JMP SHORT before display routine) dec ax call ax ;call it mov ax,bp ;get PC call chksym ;defined? test ah,inst ;as a jump dest? jz xdry ;no, around for more dry3: ; we've been here before, don't get caught in a loop ret dry4: ; disassembling code with 2-byte opcodes call getw ;get word mov dx,ax ;copy dry5: lods word ptr cs:[si] ;get mask and ax,dx ;AND this opcode with mask cmp ax,cs:[si] ;match? lea si,[si+2] ;[advance] je dry2 lods byte ptr cs:[si] ;no, get length and ax,177 ;remove "ditto" bit, AH=0 add si,ax ;skip inc si ;skip vector inc si jmp short dry5 ;loop ;+ ; ; Actually disassemble. ; ; Come back to NEWBLK each time we hit a new symbol that might not be code, to ; see whether it's referenced code, referenced data, or dead data. ; ;- newblk: ; start a new block of code or data mov ax,bp ;get PC cmp ax,cs:last ;EOF? ja eof1 call chksym ;on symbol table? jz newbk0 ;yes test ah,inst ;code? jz newbk1 ;no jmp next ;yes, handle it newbk0: ; it's a dead location, see if more than one (might be half of DW) mov ax,bp ;get PC again inc ax ;+1 call chksym ;next loc too? jnz newbk2 ;yes, treat this one as data (no label) ; dead code, say so and dump it until next symbol (drop through) newbk1: ; loc has a label, print it push ax ;save flag mov ax,bp ;addr mov si,cs:mnem ;pt at table call word ptr cs:plabel[si] ;print label pop ax ;restore test ah,inst ;is it a jump target? jnz next1 ;yes test ah,data ;it was data right? jz newbk3 ;must have been dead (from above) newbk2: ; it's data mov ax,tab+(400*'d') ;d stosw mov ax,'b'+(400*tab) ;b stosw call imm8 ;display data jmp short newblk ;try next eof1: jmp eof2 ;can't reach from here next1: jmp next2 newbk3: ; dead code, dump as DB statements until next label or EOF mov si,offset dead ;pt at msg mov cx,9d ;LEN('; dead') rep movs [di],byte ptr cs:[si] ;copy newbk4: ; dump next line xor bx,bx ;offset (0-7) push bx ;save mov ax,tab+(400*'d') ;d stosw mov ax,'b'+(400*tab) ;b stosw jmp short newbk6 newbk5: ; dump next byte mov ax,bp ;get PC cmp ax,cs:last ;eof? ja newbk7 call chksym ;symbol defined? jnz newbk7 ;yes, stop dumping mov al,',' ;, stosb newbk6: call getb ;get a byte mov dl,al call hex2 ;display cmp dl,' ' ;printable? jb $+7 cmp dl,'~' jbe $+4 mov dl,'.' ;replace with . if not pop bx ;restore mov cs:fname[bx],dl ;save for ASCII dump inc bx ;count it push bx cmp bl,8d ;line full? jb newbk5 ;loop if not newbk7: ; finished line pop cx ;restore count mov ax,tab+(400*';') ;comment stosw mov si,offset fname ;pt at buffer rep movs [di],byte ptr cs:[si] ;copy call crlf ;flush it mov ax,bp ;get next addr cmp ax,cs:last ;eof? ja eof1 call chksym ;on ST? jz newbk4 ;loop if not jmp newblk ;restart if so ; next: ; process next instruction mov ax,bp ;get PC cmp ax,cs:last ;EOF? ja eof1 call chksym ;see if symbol jz next2 ; display a label mov ax,bp ;addr mov si,cs:mnem ;pt at table call word ptr cs:plabel[si] ;print label next2: ; we're definitely going to go for it mov al,tab ;write a tab stosb mov si,cs:mnem ;pt at table cmp byte ptr cs:ocsize[si],2 ;2-byte opcodes? je ins4 ; init in case 8086 mode (seg prefixes, etc.) mov byte ptr cs:segpfx,0 ;none yet mov byte ptr cs:reppfx,0 ;no rept pfx either mov byte ptr cs:opsize,0 ;should have switch for seg default mov byte ptr cs:adsize,0 inspfx: ; reenter here to search for prefixed opcodes call getb ;get byte mov dl,al ;copy ins1: ; enter here with opcode in DL already lods word ptr cs:[si] ;get mask and al,dl ;mask cmp al,ah ;is this it? je ins2 lods byte ptr cs:[si] ;no, get length inc si ;(+2 to skip vector) inc si test al,al ;ditto bit? js ins1 ;yes, loop mov cx,si ;copy ptr+3 cbw ;AH=0 add si,ax ;skip jmp short ins1 ;loop ins2: ; found it lods byte ptr cs:[si] ;get length test al,al ;ditto bit? js ins3 ;yes cbw ;AH=0 mov cx,ax ;copy rep movs byte ptr [di],byte ptr cs:[si] ;copy name lods word ptr cs:[si] ;get dispatch addr call ax ;call it jmp short next ;loop ins3: ; ditto flag, use previous valid addr lods word ptr cs:[si] ;get dispatch addr mov si,cx ;get prev addr dec si ;correct dec si mov cl,cs:[si-1] ;get length xor ch,ch ;CH=0 rep movs byte ptr [di],byte ptr cs:[si] ;copy name call ax ;dispatch jmp next ;loop ins4: ; disassembling code with 2-byte opcodes call getw ;get word mov dx,ax ;copy ins5: lods word ptr cs:[si] ;get mask and ax,dx ;AND this opcode with mask cmp ax,cs:[si] ;match? lea si,[si+2] ;[advance] je ins2 lods byte ptr cs:[si] ;no, get length and ax,177 ;remove "ditto" bit, AH=0 add si,ax ;skip inc si ;skip vector inc si jmp short ins5 ;loop eof2: call flush ;flush output buf mov ax,4C00h ;func=exit, no error int 21h ; jmp short cbpfx1 cbpfx: ; CB instruction prefix mov si,offset cbtab ;pt at table pop ax ;flush R.A. jmp short inspfx ;jump back in cbpfx1: mov si,offset cbtab ;trace entry pop ax ;flush R.A. jmp drypfx ;back in ; jmp short ddpfx1 ddpfx: ; DD instruction prefix mov dh,'x' ;IX mov si,offset ixtab ;pt at table pop ax ;flush R.A. jmp short inspfx ;jump back in ddpfx1: mov si,offset ixtab ;trace entry pop ax ;flush R.A. jmp drypfx ;back in ; jmp short edpfx1 edpfx: ; ED instruction prefix mov si,offset edtab ;pt at table pop ax ;flush R.A. jmp inspfx ;jump back in edpfx1: mov si,offset edtab ;trace entry pop ax ;flush R.A. jmp drypfx ; jmp short ddpfx1 ;use IX code fdpfx: ; FD instruction prefix mov dh,'y' ;IY mov si,offset ixtab ;pt at table pop ax ;flush R.A. jmp inspfx ;jump back in ; jmp short pfx0Fa pfx0F: ; 0F instruction prefix mov si,offset tab0F ;pt at table pop ax ;flush R.A. jmp inspfx ;jump back in pfx0Fa: mov si,offset tab0F ;trace entry pop ax ;flush R.A. jmp drypfx ;back in ; porgh: ; print hex "ORG" mov si,offset org1 ;pt at it mov cx,5 ;org rep movs [di],byte ptr cs:[si] ;copy jmp hexw ;display it, return ; plabh: ; print hex label call labl ;print L plus four hex digits mov al,':' ;: stosb ret ; crlf: ; write , maybe flush buffer mov ax,cr+(400*lf) ; stosw ;write it cmp di,-2000 ;in last block of buf? jae flush ;yes, flush buf ret ; flush: ; flush output buffer push ds ;save regs push es push es ;copy ES to DS pop ds mov dx,stsize ;pt at buffer mov cx,di ;length sub cx,dx mov bx,0001h ;stdout mov ah,40h ;func=write int 21h ;flush buffer pop es ;restore pop ds mov di,dx ;reset ptr ret ; subttl operand decoding routines ;+ ; ; No operands. ; ;- ret nop no: jmp short crlf ;+ ; ; Same as above, but stop if tracing (end of this branch -- RET etc.). ; ;- pop ax ;flush R.A. ret ;return to caller's caller nostop: call crlf ; stop: ; jump here on HALT, RST 0, RET, etc. pop ax ;lose return addr jmp newblk ;start a new block (may be dead) ;+ ; ; Two register operands. ; ;- ret nop rr: call reg1 ;display 1st stosb ;, call reg ;2nd jmp short crlf ;+ ; ; Register, immediate byte. ; ;- inc bp ;eat the byte ret rn: call reg1 ;display it stosb ;, jmp short imm8 ;immediate byte, return ; inc bp ;eat 1 byte ret imm8: call getb ;get another byte dbyte: ; enter here with data byte in AL push ax ;save call hexb ;do the hex pop dx ;restore dchar: ; enter here with data byte in DL, hex already displayed cmp dl,' ' ;printing char? jb crlf ;no cmp dl,'~' ;hm? ja crlf mov ax,tab+(400*';') ;comment stosw mov al,"'" ;' mov ah,dl ;char stosw mov al,"'" ;' stosb jmp crlf ;+ ; ; r (in bits 3-5 of opcode). ; ;- ret nop r1: call reg1 ;display jmp crlf ;+ ; ; a,(mm) ("a," already displayed). ; ;- jmp short mm1 amm: dec di ;unput tab call mm ;(mm) jmp crlf mm1: ; trace entry call getw ;get address mov bl,data ;it's data jmp addsym ;add to symbol table ;+ ; ; (mm),a. ; ;- jmp short mm1 mma: call mm ;(mm) stosb ;, mov al,'a' ;a stosb jmp crlf ;+ ; ; dd,nn. ; ;- jmp short ddnn1 ddnn: call regpr ;dd mov al,',' ;, stosb call getw ;get a word call hexw ;display jmp crlf ddnn1: ; trace entry inc bp ;eat a word inc bp ret ;+ ; ; dd,(mm). ; ;- jmp short mm1 ddmm: call regpr mov al,',' ;, stosb call mm ;(mm) jmp crlf ; mm: ; 16-bit address mov al,'(' ;( stosb call getw ;mm call labl mov al,')' ;) stosb mov al,',' ;often needed ret ;+ ; ; (mm),dd. ; ;- jmp short mm1 mmdd: call mm ;(mm) stosb ;, call regpr ;dd jmp crlf ;+ ; ; rp. ; ;- ret nop rp: call regpr ;dd jmp crlf ;+ ; ; r. ; ;- ret nop r: call reg ;display reg jmp crlf ;+ ; ; b,r. ; ;- ret nop br: mov al,dl ;get opcode mov cl,3 ;bit count shr al,cl ;right 3 and ax,7 ;isolate or ax,",0" ;convert to ascii, add a comma stosw ;b, jmp short r ;later ;+ ; ; JP cc,nn. ; ;- jmp short cndjmp ccnn: mov bl,dl ;get opcode shr bl,1 ;right 2 bits shr bl,1 and bx,16 ;isolate mov ax,word ptr cs:cond[bx] ;look up condition stosw ;save mov al,',' ;, cmp al,ah ;already written? je $+3 stosb call getw ;get addr call labl ;display jmp crlf ;+ ; ; JP nn. (won't come back) ; ;- jmp short undjmp nn: call getw ;get addr call labl ;display call crlf ;crlf jmp stop ;execution stops (gone somewhere else) ;+ ; ; CALL nn. ; ; Like JP nn only we will return. ; ;- jmp short cndjmp callnn: call getw ;get addr call labl ;display jmp crlf ;crlf, return ; ; The next few instructions are the whole beauty of the disassembler! cndjmp: ; trace entry for conditional jump and CALL call getw ;get a word cndjm1: ; enter with address in AX push bp ;save where we are mov bp,ax ;trace the called code call dry ;recurse pop bp ;restore ret ;continue where we left off ; undjmp: ; trace entry for unconditional jump call getw ;get a word undjm1: ; enter with address in AX mov bp,ax ;transfer to there pop ax ;flush R.A. jmp dry ;enter in sym tab w/o stopping on it ;+ ; ; JR e. ; ;- jmp short undjr ujr: ; unconditional jump-relative mov dh,dl ;set "don't continue" flag jmp short cjr1 ; jmp short cndjr cjr: ; conditional jump-relative xor dh,dh ;clear the "don't continue" flag cjr1: ; enter here with DH<>0 if unconditional jump rel8 call getb ;get offset cbw ;sxt push ax ;save add ax,bp ;add PC address call labl ;display as a label mov ax,tab+(400*';') ;comment stosw mov al,'$' ;$ stosb pop ax ;get offset inc ax ;+2 to compensate for (opcode,displacement) inc ax jz $+5 ;no offset (sit 'n spin) call offst ;display it push dx ;save flag call crlf ;crlf pop dx ;restore ; start a new blk (with a label) if jump was unconditional test dh,dh ;continue? jnz $+3 ;yes ret jmp stop ; undjr: ; trace entry for JR call getb ;get offset cbw ;SXT add ax,bp ;add offset jmp short undjm1 ;continue ; cndjr: ; trace entry for JR cc, and DJNZ call getb ;get offset cbw ;SXT add ax,bp ;add offset jmp short cndjm1 ;continue ;+ ; ; RET cc. ; ;- ret ;doesn't end trace since it's conditional nop cc: mov bl,dl ;get opcode shr bl,1 ;right 2 shr bl,1 and bx,16 ;isolate mov ax,word ptr cs:cond[bx] ;look up condition stosb ;save 1st byte cmp ah,',' ;just 1 char? je cc1 ;yes mov al,ah ;copy stosb ;write 2nd char cc1: jmp crlf ;+ ; ; RST n. ; ;- jmp short rst1 rst: mov al,dl ;copy and al,70 ;isolate call hex2 ;display mov al,'h' ;h stosb cmp dl,307 ;RST 0? je $+5 ;yes jmp crlf call crlf ;CRLF jmp stop ;stop rst1: ; trace entry cmp dl,307 ;RST 0? jne $+3 ;no pop ax ;lose return address ret ;drop through to caller's caller if RST 0 ;+ ; ; [IN A,](n). ; ;- inc bp ;eat n ret upn: dec di ;unput tab call pn ;(n) jmp crlf ;+ ; ; (n),a. ; ;- inc bp ;eat n ret pna: call pn ;(n) mov ax,"a," ;,a stosw jmp crlf ; pn: ; print next byte in paranthesis mov al,'(' ;( stosb call getb ;get n call hexb ;print in hex mov al,')' ;) stosb ret ;+ ; ; r,(c). ; ;- ret nop rpc: call reg1 ;reg mov ah,'(' ;,( stosw ;(, in al already) mov ax,")c" ;c) stosw jmp crlf ;+ ; ; Anything else is a DB pseudo-op. ; ; Since we're pretty definitely screwing up (unless they knew about some ; undocumented instructions that we don't), we'll treat this as a HALT and stop ; tracing/disassembling. ; ;- pop ax ;lose return addr ret ;return to caller's caller ubite: dec di ;unput tab jmp short bite ; pop ax ;lose r.a. ret ;return to caller's caller bite: mov al,dl ;opcode call dbyte ;display jmp stop ;execution stops ; subttl annoying IX/IY instructions ;+ ; ; In these routines, when I say "IX" I mean IX or IY, either x or y was put in ; DH by DDPFX and FDPFX. ; ; r,(ix+d). ; ;- inc bp ;eat D ret rixm: call reg1 ;dest reg stosb ;, call ixp ;(ix+d) jmp crlf ;+ ; ; (ix+d),r. ; ;- inc bp ;eat D ret ixmr: call ixp ;(ix+d) stosb ;, jmp r ;+ ; ; (ix+d),n. ; ;- jmp short ixeat2 ixmn: call ixp ;(ix+d) stosb ;, jmp imm8 ;n ;+ ; ; ix,nn. ; ;- jmp short ixeat2 ixnn: call ix ;ix stosb ;, call getw ;nn call hexw jmp crlf ;+ ; ; ix,(mm). ; ;- jmp short ixeat2 ixmm: call ix ;ix stosb ;, call mm ;(mm) jmp crlf ;+ ; ; (mm),ix. ; ;- jmp short ixeat2 mmix: call mm ;(mm) stosb ;, call ix ;ix jmp crlf ;+ ; ; ix. ; ;- ixeat2: ; trace to eat 2 bytes inc bp inc bp ;steal a ret from UIXX trace entry ret ret uixx: dec di ;unput tab ixx: call ix ;ix jmp crlf ;+ ; ; (ix+d). ; ;- inc bp ;eat d ret uixm: dec di ;unput tab call ixp ;(ix+d) jmp crlf ; inc bp ;eat d ret ixm: call ixp ;(ix+d) jmp crlf ;+ ; ; ix,rp. (if rp is hl, use the "opposite" index reg instead) ; ;- ret nop ixpp: call ix ;ix stosb ;, mov al,dl ;get opcode and al,60 ;isolate rp bits cmp al,40 ;hl? je ixpp1 ;yes call rp ;rp jmp crlf ixpp1: xor dh,('x' xor 'y') ;use opposite of 1st operand if hl call ix ;iy jmp crlf ;+ ; ; JP (ix). ; ; Unconditional jump, something new starts immediately after it. ; ;- pop ax ;lose R.A. ret ;return to caller's caller ixxp: mov al,'(' ;( stosb call ix ;ix mov al,')' ;) stosb call crlf jmp stop ;+ ; ; Undefined opcode with DD or FD prefix. ; ;- pop ax ;lose R.A. ret ;return to caller's caller ixbite: mov ax,"D0" ;0D cmp dh,'x' ;ix, right? je $+4 mov ah,'F' ;no, change to 0F stosw mov ax,"hD" ;Dh stosw mov al,',' ;, stosb mov al,dl ;get opcode call dbyte ;go dump it jmp stop ;+ ; ; Instructions prefixed by DD CB or FD CB come here. ; These are new rot/shifts and bit instructions which reference ix or iy. ; Note that the displacement comes between the second and third opcode bytes, ; for your convenience. ; ;- ;;;;;;;; should parse so we stop on invalid opcodes jmp short ixeat2 ;no point in parsing, always 2 more bytes cbix: call getb ;get displacement mov bl,al ;save (NEXT won't hurt it) mov si,offset cbixtb ;pt at new table jmp inspfx ;continue scanning ;+ ; ; b,(ix+d) (d was already read & saved in bl). ; ;- ; no trace entry, handled in CBIX bixm: mov al,dl ;get opcode mov cl,3 ;bit count shr al,cl ;right 3 and ax,7 ;isolate or ax,",0" ;0, (convert to ASCII, add comma) stosw ;jmp short cbixm ;(ix+d) ;+ ; ; (ix+d) (d already read and saved in bl). ; ;- ; no trace entry cbixm: mov al,'(' ;( stosb call ix ;ix mov al,bl ;d call ixpd jmp crlf ;+ ; ; Undefined opcode, but we already read DD/FD, CB, d, opcode. ; Dump the whole mess. Sorry about no ASCII for d. ; ;- ; no trace entry cbixbt: mov al,'D' ;assume IX (DD) cmp dh,'x' ;right? je $+4 mov al,'F' ;iy, use FD mov byte ptr cs:ddcb+1,al ;patch mov si,offset ddcb ;pt at string mov cx,12/2 ;len('0DDh,0CBh,') rep movs word ptr [di],word ptr cs:[si] ;copy mov al,bl ;get d cmp al,0A0h ;need leading 0? jb $+4 movs [di],byte ptr cs:[si] ;copy a 0 call hex2 ;nn mov ax,",h" ;h, stosw mov al,dl ;get final opcode byte call dbyte ;gasp! jmp stop ;+ ; ; ix or iy. ; ;- ix: mov al,'i' ;i mov ah,dh ;x or y stosw mov al,',' ;may be needed ret ;+ ; ; lx or ly. ; ;- lx: mov al,'l' ;l mov ah,dh ;x or y stosw mov al,',' ;may be needed ret ;+ ; ; hx or hy. ; ;- hx: mov al,'h' ;h mov ah,dh ;x or y stosw mov al,',' ;may be needed ret ;+ ; ; (ix+d) or (iy+d). ; ;- ixp: mov ax,"i(" ;(i stosw mov al,dh ;x or y stosb call getb ;get offset ixpd: ; enter here with d in al call offst ;show the offset mov al,')' ;) stosb mov al,',' ;may be needed ret ; offst: ; display AL as +/-n mov ah,'+' ;assume positive or al,al ;negative? jns offst1 ;no mov ah,'-' ;make '-' neg al ;and take abs. val offst1: mov es:[di],ah ;sign inc di cmp al,9d ;can use 1 decimal digit? jbe $+5 ;yep jmp hexb ;offset or al,'0' ;convert stosb ret ; subttl really annoying undocumented IX/IY instructions ;+ ; ; The basic principal is that the DD or FD prefix tells the Z-80 to substitute ; IX [IY] for the next reference to HL, or (IX+d) [(IY+d)] for (HL). The ; Lifeboat Model II BIOS uses some of these. I haven't tried them for ugly ; cases like DD; LD H,H (which operand is really H?). ; ; It seems risky to assume that just because these work on your Z-80 means they ; work on all Z-80's by all manufacturers, but that's not my prob. ; ; r,lx. ; ;- ret nop rixl: call reg1 ;r stosb ;, call lx ;lx jmp crlf ;+ ; ; r,lx. ; ;- ret nop rixh: call reg1 ;r stosb ;, call hx ;hx jmp crlf ;+ ; ; lx,r. ; ;- ret nop ixlr: call lx ;lx stosb ;, call reg ;r jmp crlf ;+ ; ; hx,r. ; ;- ret nop ixhr: call hx ;hx stosb ;, call reg ;r jmp crlf ;+ ; ; lx. ; ;- ret ret uixl: dec di ;unput tab ixl: call lx ;ix jmp crlf ;+ ; ; hx. ; ;- ret ret uixh: dec di ;unput tab ixh: call hx ;hx jmp crlf ; subttl Intel mnemonics for the same thing ;+ ; ; MOV r,r. ; ;- ret nop irr: call ireg1 ;r1 mov al,',' ;, stosb mov al,dl ;r2 call ireg jmp crlf ;+ ; ; r,n. ; ;- inc bp ret irn: call ireg1 ;r mov al,',' ;, stosb jmp imm8 ;immediate byte ;+ ; ; rp,nn. ; ;- jmp short ieat2 irpnn: call iregpr ;rp mov al,',' ;, stosb call getw ;nn call hexw jmp crlf ; ieat2: ; trace entry -- eat nn inc bp ;yep inc bp ret ;+ ; ; mm. ; ;- jmp short imm1 imm: call getw ;get address call labl ;make a label jmp crlf imm1: ; trace entry call getw ;get address mov bl,data ;data reference jmp addsym ;add symbol, return ;+ ; ; rp. ; ;- ret nop irpp: call iregpr ;rp jmp crlf ;+ ; ; RST n. ; ; Intel uses the value of the 3-bit field (0::7), not the value that you get ; when you isolate it (0,8,10h,18h, etc.). ; ;- jmp short irst1 irst: mov al,dl ;get value mov cl,3 ;bit count shr al,cl ;right 3 and al,7 ;isolate or al,'0' ;cvt to ASCII stosb ;save push dx ;save value call crlf ;crlf pop dx ;restore cmp dl,307 ;RST 0? je $+3 ret jmp stop ;yes, code stops here irst1: ; trace entry cmp dl,307 ;RST 0? jne $+3 pop ax ;flush r.a. if so ret ;+ ; ; IN/OUT n. ; ; Like iimm except there's no point in saying if it's an ASCII char. ; ;- inc bp ret iio: call getb ;get a byte call hexb ;display jmp crlf ;+ ; ; r. (in bits 5:3 of opcode) ; ;- ret nop ir1: call ireg1 ;print it jmp crlf ;+ ; ; r. (in bits 2:0 of opcode) ; ;- ret nop ir: call ireg ;print it jmp crlf ;+ ; ; n. Display in ASCII too if printing char. ; ;- inc bp ret iimm: jmp imm8 ;yep ;+ ; ; Non-8080/8085 instruction, try it as a Z-80 instruction. ; If that's wrong too then they'll get a comment as well as a DB, but tough! ; ;- jmp short non85a non85: push dx ;save opcode call crlf ;crlf pop dx ;restore mov al,tab ; stosb mov si,offset z80 ;pt at Z-80 table dec bp ;unget the opcode jmp inspfx ;go non85a: ; trace entry mov si,offset z80 ;pt at Z-80 table dec bp ;unget the opcode jmp drypfx ;go ; subttl Intel 8051 routines ;+ ; ; ACALL dest ; ;- jmp short acall1 acall: call abs11 ;get addr call labl ;display jmp crlf ;, return acall1: call abs11 ;get addr jmp cndjm1 ;trace both paths ; abs11: ; fetch 11-bit abs addr for ACALL/AJMP ; low 11 bits are from opcode, high 5 are from PC after fetch call getb ;get low bits mov ah,dl ;copy high bits mov cl,5 ;shift count shr ah,cl ;right-justify, pad with zeros mov dx,bp ;fetch addr and dh,not 7 ;trim low 3 bits or ah,dh ;compose ret ;+ ; ; Inst "a,xxx" ; ;- jmp short tsop51 asrc51: mov ax,",a" ;'a,' stosw call opn51 ;handle operand crlf51: mov dx,cs:immop ;get immediate operand test dx,dx ;if any js crlf52 ;no jmp dchar ;display it, CRLF, return crlf52: jmp crlf ;+ ; ; Single operand instructions. ; ;- jmp short tsop51 sop51: call opn51 ;display jmp crlf tsop51: ; trace entry and dl,0Fh ;isolate cmp dl,4 ;immed? je tsop52 ;;; will this ever happen? if it's a single operand, 0100 means A not imm8 ;;; (which is handled separately in MCS51 symbol table) cmp dl,5 ;direct? jne tsop53 tsop52: inc bp ;eat immed/direct byte tsop53: ret ;+ ; ; Handle generic 8051 operand. ; ; xxxx0100 #imm8 ; xxxx0101 direct ; xxxx011n @Rn ; xxxx1nnn Rn ; ;- opn51: mov cs:immop,-1 ;assume not immediate and dl,0Fh ;isolate test dl,10 ;xxxx1rrr? jz opn52 ;no ; a,rn mov al,'r' ;r mov ah,dl ;copy reg # add ah,'0'-10 ;0-7 stosw mov al,',' ;often needed ret opn52: cmp dl,04h ;immed? jne opn53 ;no ; a,#n mov al,'#' ;# stosb call getb ;get data byte xor ah,ah ;>=0 mov cs:immop,ax ;save for display call hexb ;do the hex mov al,',' ;often needed ret opn53: cmp dl,05h ;direct? jne opn54 ;no ; a,n call getb ;get the byte call hexb ;write it out mov al,',' ;often needed ret opn54: ; a,@rn mov ax,"r@" ;'@r' stosw mov al,dl ;copy add al,'0'-6 ;convert to 0-1 stosb mov al,',' ;often needed ret ;+ ; ; Inst direct,A ; ;- inc bp ;eat direct addr ret dira51: call getb ;get byte call hexb ;display mov ax,"a," ;',a' stosw jmp crlf ;+ ; ; Inst [dest,]bit ; ;- inc bp ;eat it ret bit51: call getb ;get byte call bitnam ;display it jmp crlf ;+ ; ; Display name of 8051 bit in AL. ; ; Well OK for now we just give the hex bit #, but we could use a table for the ; ones that do have names. ; ; al returns ',' ; ;- bitnam: jmp hexb ;display, return ;; mov al,',' ;(if we have our own code some day) ;; ret ;+ ; ; Inst direct,#imm8 ; ;- jmp short diri52 diri51: call getb ;get byte call hexb ;display stosb ;',' jmp imm8 ;immediate value diri52: call getw ;get imm8,,addr xor ah,ah ;AH=0 mov bl,data ;data symbol jmp addsym ;add to symbol table ;+ ; ; AJMP dest ; ;- jmp short ajmp1 ajmp: jmp acall ;looks just like ACALL ajmp1: call abs11 ;get addr jmp undjm1 ;change direction ;+ ; ; CJNE A,xxx ; ;- jmp short cjnea1 cjnea: call opn51 ;sort out 2nd operand cjne: stosb ;, call getb ;get a byte cbw ;SXT push ax ;save add ax,bp ;add PC address call labl ;display as a label mov ax,tab+(400*';') ;comment stosw mov dx,cs:immop ;get immediate operand test dx,dx ;if any js cjne1 ;no cmp dl,' ' ;printing char? jb cjne1 ;no cmp dl,'~' ;hm? ja cjne1 mov al,"'" ;' mov ah,dl ;char stosw mov ax," '" ;' stosw cjne1: mov al,'$' ;$ stosb pop ax ;get offset add ax,3 ;+3 to compensate for opcode bytes jz cjne2 ;no offset (sit 'n spin) call offst ;display it cjne2: jmp crlf ;, return cjnea1: inc bp ;skip direct/immed operand jmp cndjr ;handle conditional rel8 jump ;+ ; ; CJNE A,xxx ; ;- jmp short cjnea1 ;imm8/rel8 bytes as usual cjnei: call opn51 ;sort out 1st operand stosb ;, mov dl,04h ;pretend inst had an imm8 arg call opn51 ;handle that jmp short cjne ;pick it up from there ;+ ; ; DJNZ direct,rel8 ; ;- jmp short cjnea1 ;skip operand, handle conditional rel8 jump djnzd: call opn51 ;handle operand djnzd1: ; (entry from JB51) stosb ;, call getb ;get a byte cbw ;SXT push ax ;save add ax,bp ;add PC address call labl ;display as a label mov ax,tab+(400*';') ;comment stosw mov al,'$' ;$ stosb pop ax ;get offset add ax,3 ;+3 to compensate for opcode bytes jz djnzd2 ;no offset (sit 'n spin) call offst ;display it djnzd2: jmp crlf ;, return ;+ ; ; DJNZ Rn,rel8 ; ;- jmp short djnz52 djnz51: call opn51 ;handle operand stosb ;, jmp cjr ;go finish up (2-byte opcode) djnz52: jmp cndjr ;+ ; ; Jx bit,rel8 ; ;- jmp short jb52 jb51: call getb ;get byte call bitnam ;display jmp short djnzd1 ;the rest is like DJNZ jb52: inc bp ;eat bit address jmp cndjr ;handle conditional jump ;+ ; ; LCALL addr16 ; ;- jmp short lcall1 lcall: call getw ;get next 2 bytes xchg al,ah ;MSB first call labl ;display jmp crlf ;, return lcall1: call getw ;get next 2 bytes xchg al,ah ;MSB first jmp cndjm1 ;trace both paths ;+ ; ; LJMP dest ; ;- jmp short ljmp1 ljmp: jmp short lcall ;looks just like LCALL ljmp1: call getw ;get next 2 bytes xchg al,ah ;MSB first jmp undjm1 ;change direction ;+ ; ; MOV Rn,A ; ;- ret nop movrna: call opn51 ;Rn stosb ;, mov al,'a' ;a stosb jmp crlf ;+ ; ; MOV Rn,direct ; ;- inc bp ;eat direct addr ret movrnd: call opn51 ;Rn stosb ;, mov dl,05h ;direct call opn51 jmp crlf ;+ ; ; MOV Rn,#imm8 ; ;- inc bp ;eat immed value ret movrni: call opn51 ;Rn stosb ;, mov dl,04h ;#imm8 call opn51 jmp crlf ;+ ; ; MOV direct,A ; ;- inc bp ;skip direct addr ret movda: call opn51 ;direct stosb ;, mov al,'a' ;a stosb jmp crlf ;+ ; ; MOV direct,xxx (xxx is Rn or @Ri) ; ;- inc bp ;skip direct adr ret movdx: push dx ;save mov dl,05h ;direct call opn51 stosb ;, pop dx ;restore call opn51 ;Rn or @Ri jmp crlf ;+ ; ; MOV direct,direct ; ;- jmp short movdd1 movdd: inc bp ;skip source addr call opn51 ;dest stosb ;, dec bp ;back to dest addr dec bp mov dl,05h ;(restore DL=direct) call opn51 ;source inc bp ;skip dest again jmp crlf movdd1: inc bp ;skip both addrs inc bp ret ;+ ; ; MOV direct,#imm8 ; ;- jmp short movdd1 ;skip both args movdi: call opn51 ;direct stosb ;, mov dl,04h ;#imm8 call opn51 jmp crlf ;+ ; ; MOV @Ri,A ; ;- ret nop movira: call opn51 ;@Ri stosb ;, mov al,'a' ;a stosb jmp crlf ;+ ; ; MOV @Ri,direct ; ;- inc bp ;skip direct addr ret movird: call opn51 ;@Ri stosb ;, mov dl,05h ;direct call opn51 jmp crlf ;+ ; ; MOV @Ri,#imm8 ; ;- inc bp ;eat immed operand ret moviri: call opn51 ;@Ri stosb ;, mov dl,04h ;#imm8 call opn51 jmp crlf ;+ ; ; MOV bit,C ; ;- inc bp ;skip bit addr ret movbc: call bitnam ;bit stosb ;, mov al,'c' ;c stosb jmp crlf ;+ ; ; MOV DPTR,#imm16 ; ;- jmp short mdptr1 mdptr: call getw ;get value xchg al,ah ;>< jmp imm16 ;value, mdptr1: inc bp ;skip imm16 value inc bp ret ;+ ; ; MOVX A,@Ri ; ;- ret nop movxar: or dl,06h ;make sure OPN51 recognizes it call opn51 ;@Ri jmp crlf ;+ ; ; MOVX @Ri,A ; ;- ret nop movxra: or dl,06h ;make sure OPN51 recognizes it call opn51 ;@Ri stosb ;, mov al,'a' ;a stosb jmp crlf ;+ ; ; PUSH/POP direct ; ;- inc bp ;eat direct addr ret stk51: mov dl,05h ;direct call opn51 jmp crlf ; subttl Intel 8086 routines ;+ ; ; Segment & size prefixes. ; ;- ret nop es86: mov byte ptr cs:segpfx,'e' ;es: jmp short pfx1 ; ret nop cs86: mov byte ptr cs:segpfx,'c' ;cs: jmp short pfx1 ; ret nop ss86: mov byte ptr cs:segpfx,'s' ;ss: jmp short pfx1 ; ret nop ds86: mov byte ptr cs:segpfx,'d' ;ds: jmp short pfx1 ; ret nop fs86: mov byte ptr cs:segpfx,'f' ;fs: jmp short pfx1 ; ret nop gs86: mov byte ptr cs:segpfx,'g' ;gs: jmp short pfx1 ; ret nop repp86: mov cs:reppfx,dl ;save opcode jmp short pfx1 ; jmp short ops86a opsz86: not cs:opsize ;invert operand size jmp short pfx1 ops86a: ; trace entry not cs:opsize ;same as above jmp short dpfx1 ; jmp short ads86a adsz86: not cs:adsize ;invert address size pfx1: pop ax ;lose return addr mov si,offset iapx86 ;pt at instructions again jmp inspfx ads86a: ; trace entry not cs:adsize ;same as above dpfx1: pop ax mov si,offset iapx86 jmp drypfx ;but dry ;+ ; ; r/m,r. ; ;- jmp short eatrm1 rmr86: call modrmg ;r/m stosb ;, call reg86a ;r jmp crlf eatrm1: jmp eatrm ;eat mod-r/m byte ;+ ; ; r,r/m. ; ;- jmp short eatrm1 rrm86: call reg86g ;r stosb ;, call modrm ;r/m jmp crlf ;+ ; ; MOVSX, MOVZX instructions. ; ;- jmp short eatrm1 zx86: inc dx ;set "word" bit (was 0) call reg86g ;r stosb ;, dec dx ;byte source call ptr86 call modrm ;mod-r/m jmp crlf ; jmp short eatrm1 zx86d: ; 32-bit dest, 16-bit source mov byte ptr cs:opsize,-1 ;always 32-bit dest call reg86g ;r (DL<0> already set) stosb ;, inc byte ptr cs:opsize ;back to 0 call ptr86 ;word source call modrm ;mod-r/m jmp crlf ; jmp short eatrm1 les86: ; LES instruction, has even opcode (W bit=0) inc dx ;opcode is even, make odd (word) jmp short rrm86 ;+ ; ; a,imm. ; ;- jmp short eatai ai86: xor bl,bl ;al/ax test dl,1 ;byte? jz ri86 ;yes cmp byte ptr [bp+1],4Ch ;could it be an exit? jne ri86 ;no mov ax,bp ;copy inc ax ;skip the word inc ax mov cs:addr4C,ax ;save addr of next inst (not if 32-bit!) ri86: ; reg,imm call reg86 ;display imm86: stosb ;, test dl,1 ;byte or word? jnz $+5 ;word jmp imm8 ;yep call getw ;get a word test byte ptr cs:opsize,-1 ;32 bits? jnz imm32 ;yes jmp imm16 ;just 16 imm32: push ax ;save word call getw ;get a word cmp ah,0A0h ;need leading 0? jb imm32a ;no mov byte ptr es:[di],'0' ;yes inc di imm32a: call hex4 ;write high 16 bits pop ax ;restore call hex4 ;low 16 mov al,'h' ;h stosb jmp crlf ;crlf eatai: ; trace entry test dl,1 ;byte or word? jz eati86 ;byte cmp byte ptr [bp+1],4Ch ;exit command? jne eati86 ;no inc bp ;+2 inc bp ;;; +2 more if 32 bits mov cs:addr4C,bp ;save ret eati86: ; trace entry for general immediates inc bp ;eat a byte test dl,1 ;word flag set? jnz $+3 ;yes ret inc bp ;word anyway test byte ptr cs:opsize,-1 ;32 bits? jz $+4 ;no inc bp ;+2 more inc bp ret ;+ ; ; MOV mod-r/m,seg. ; ;- jmp short eatrm2 rmsg86: inc dx ;opcode is 8C, make it odd (word) call modrmg ;mod-r/m stosb ;, call seg86a ;seg jmp crlf ;+ ; ; MOV seg,mod-r/m. ; ;- jmp short eatrm2 sgrm86: call getb ;get a byte mov bl,al ;copy call seg86a ;seg mov al,',' ;, stosb inc dx ;opcode is 8E, make it odd (word) call modrm ;mod-r/m jmp crlf eatrm2: jmp eatrm1 ;+ ; ; r/m,imm. ; r/m,+imm8. ; ; Last 3 bits of opcode are in mod-r/m byte. ; ;- jmp short eatrmi rmi86: call getb ;get mod-r/m, opcode mov bl,al ;save shr bl,1 ;right 2 shr bl,1 and bx,7*2 ;isolate last 3 bits of opcode cmp dl,0F6h ;which prefix is it? jb $+5 ;ADD, etc. add bl,10*2 ;TEST, etc. mov si,cs:add86[bx] ;get ptr mov bl,cs:[si] ;get length inc si mov cx,bx ;copy rep movs [di],byte ptr cs:[si] ;copy inst name rmi86a: ; enter here with mod-r/m in AL if name already displayed mov bl,al ;get mod-r/m byte call ptr86 ;ptr message call modrm ;mod-r/m cmp dl,83h ;+imm8? jne rmi86b ;no, immediate value stosb ;save comma call getb ;get byte call offst ;offset jmp crlf rmi86b: cmp dl,0F6h ;which prefix? jb rmi86c ;ADD, etc. and bl,70 ;isolate opcode cmp bl,10 ;TEST or ??? ? ja rmi86d ;no rmi86c: jmp imm86 ;not sign-extended 8-bit # rmi86d: jmp crlf ;no immediate op eatrmi: ; trace entry call eatrm ;eat mod-r/m cmp dl,83h ;always imm8? je ermi2 ;yes cmp dl,366 ;which pfx? jb ermi1 ;ADD etc. and bl,70 ;isolate opcode cmp bl,10 ;TEST or ??? ? ja ermi3 ;no, no immediate val ermi1: jmp eati86 ;eat whatever ermi2: inc bp ermi3: ret ;+ ; ; MOV mod-r/m,imm. ; ; Apparently bits 5:3 of mod-r/m are don't cares. ; ;- jmp short eatrmi mrmi86: call getb ;get mod-r/m jmp short rmi86a ;go display (DL is not 83h so no +imm8) ;+ ; ; Conditional near jumps. ; ;- jmp short cndnj cnjmp: call getw ;get offset cwd ;SXT test byte ptr cs:adsize,-1 ;32-bit addr? jz cnjmp1 ;no push ax ;save call getw ;get high word ;; mov dx,ax ;copy pop ax ;restore cnjmp1: add ax,bp ;add PC address ;; adc dx,... call labl ;display as a label jmp crlf ;CRLF, return ; cndnj: ; trace entry for conditional near jump call getw ;get offset cwd ;SXT test byte ptr cs:adsize,-1 ;32-bit addr? jz cndnj1 ;no push ax ;save call getw ;get high word ;; mov dx,ax ;copy pop ax ;restore cndnj1: add ax,bp ;add PC address ;; adc dx,... jmp cndjm1 ;continue ;+ ; ; JCXZ -- might be JECXZ if 67h prefix. ; ;- jmp short jcxz3 jcxz1: test byte ptr cs:adsize,-1 ;JECXZ? jz jcxz2 ;no sub di,4 ;un-put "cxz" mov ax,"ce" ;"ec" stosw mov ax,"zx" ;"xz" stosw mov al,tab ;tab stosb jcxz2: jmp cjr jcxz3: jmp cndjr ; add86 dw tadd,tor,tadc,tsbb,tand,tsub,txor,tcmp dw ttest,ttstq,tnot,tneg,tmul,timul,tdiv,tidiv ; tadd db 4,'add',tab ;prefixed by 200, 201, 203 tor db 3,'or',tab tadc db 4,'adc',tab tsbb db 4,'sbb',tab tand db 4,'and',tab tsub db 4,'sub',tab txor db 4,'xor',tab tcmp db 4,'cmp',tab ttest db 5,'test',tab ;prefixed by 366 ttstq db 4,'???',tab ;;; seems to take an immediate value tnot db 4,'not',tab tneg db 4,'neg',tab tmul db 4,'mul',tab timul db 5,'imul',tab tdiv db 4,'div',tab tidiv db 5,'idiv',tab ;+ ; ; IMUL r16,r/m16 (or 32,32) 386+ only ; ;- imulaf: if 0 !@#$ opt 'imul',257,377,imulaf endif ;;; ;+ ; ; mod-r/m. ; ; Last 3 bits of opcode are in mod-r/m byte. ; ;- jmp short eatrmb rm86: call getb ;get a byte mov bl,al ;copy shr bl,1 ;convert to opcode*2 shr bl,1 and bx,7*2 ;isolate mov si,cs:inc86[bx] ;get ptr cmp dl,8Fh ;POP instruction? jne $+5 mov si,offset tpop ;yes mov bl,cs:[si] ;get length inc si mov cx,bx ;copy rep movs [di],byte ptr cs:[si] ;copy mov bl,al ;copy opcode call ptr86 ;PTR msg, if needed push ax ;save call modrm ;display operand call crlf ;CRLF pop ax ;restore and al,70 ;isolate cmp al,40 ;JMP or JMP FAR? jb rm86a ;no cmp al,50 ja rm86a jmp stop rm86a: ret eatrmb: ; trace entry call eatrm ;eat it (preserve DL) cmp dl,8Fh ;POP? je ermb1 ;yes, skip and bl,70 ;isolate cmp bl,40 ;JMP or JMP FAR? jb ermb1 cmp bl,50 ja ermb1 pop ax ;flush R.A. if JMP ermb1: ret ; inc86 dw tinc,tdec,tcall,tcallf dw tjmp,tjmpf,tpush,tpush ;7 undef'ed (works as PUSH on V20) ; tinc db 4,'inc',tab tdec db 4,'dec',tab tcall db 5,'call',tab tcallf db 6,'call',tab,'d' tjmp db 4,'jmp',tab tjmpf db 5,'jmp',tab,'d' tpush db 5,'push',tab tpop db 4,'pop',tab ;bits 5:3 of mod-r/m seem to be don't cares ;+ ; ; ROL etc. mod-r/m,1/cl/imm8. ; ; Last 3 bits of opcode are in mod-r/m byte. ; ;- jmp short trot86 rot86: call getb ;get a byte mov si,ax ;copy shr si,1 ;convert to opcode*4 and si,34 ;isolate 3 bits add si,offset rol86 ;get ptr mov cx,4 ;length rep movs [di],byte ptr cs:[si] ;copy mnemonic mov bl,al ;copy opcode call ptr86 ;PTR msg if needed call modrm ;1st operand stosb ;, test dl,10h ;imm8? jz rot86b ;yes test dl,2 ;cl? jnz rot86a ;yes mov al,'1' ;1 stosb jmp crlf rot86a: mov ax,"lc" ;cl stosw jmp crlf rot86b: jmp iio ;byte, crlf ; trot86: ; trace entry call eatrm ;eat r/m test dl,10h ;imm8? jnz $+3 inc bp ;yes, eat it ret ; rol86 db 'rol',tab db 'ror',tab db 'rcl',tab db 'rcr',tab db 'sal',tab ;;I bet this is really SHL db 'shr',tab db 'sal',tab ;;not really (but works!) db 'sar',tab ;+ ; ; a,moffs. ; ;- jmp short eata86 amof86: xor bl,bl ;al/ax/eax call reg86 ;yep stosb ;, call moff86 ;mem offset jmp crlf eata86: ; trace entry call getw ;get offset mov bl,data ;add to symbol table jmp addsym ;and return ;+ ; ; moffs,a. ; ;- jmp short eata86 mofa86: call moff86 ;mem offset stosb ;, xor bl,bl ;al/ax/eax call reg86 ;yep jmp crlf ;+ ; ; byte reg,imm. ; ;- inc bp ret bi86: mov bl,dl ;copy reg # xor dl,dl ;byte instruction jmp ri86 ;reg,imm ;+ ; ; word reg,imm. ; ;- jmp short eatw86 wi86: mov bl,dl ;copy reg # mov dl,1 ;word instruction jmp ri86 ;reg,imm eatw86: ; trace entry inc bp inc bp ret ;+ ; ; word reg. ; ;- ret nop r86: mov bl,dl ;copy reg # mov dl,1 ;word instruction call reg86 ;reg name jmp crlf ;+ ; ; ax/eax,reg. ; ;- ret nop xchg86: mov dh,dl ;save mov dl,1 ;word xor bl,bl ;ax/eax call reg86 stosb ;, mov bl,dh ;reg (dl=1 for word) call reg86 jmp crlf ;+ ; ; Short branches. ; ;- jmp short undjr1 jsh86: ; unconditional short branch mov si,offset tshort ;pt at string mov cx,6/2 ;LEN('short ') rep movs word ptr [di],word ptr cs:[si] ;copy mov dh,dl ;set "don't continue" flag jmp cjr1 ;finish up with Z-80 code undjr1: jmp undjr ;trace tshort db 'short ' ;+ ; ; JMP near. ; ;- jmp short undj86 jmpn86: call getw ;get offset cwd ;sign-extend test byte ptr cs:adsize,-1 ;32-bit offset? jz jn86a push ax ;save call getw ;get offset ;; mov dx,ax ;copy pop ax jn86a: add ax,bp ;compute addr ;; adc dx,... call labl call crlf ;crlf jmp stop ;start new block undj86: ; trace entry call getw ;get a word test byte ptr cs:adsize,-1 ;32-bit offset? jz uj86a push ax ;save call getw ;get offset ;; mov dx,ax ;copy pop ax uj86a: add ax,bp ;compute addr ;; adc dx,... jmp undjm1 ;go follow the jump ;+ ; ; JMP far. ; ;- pop ax ;just lose ret jmpf86: call calf86 ;display address jmp stop ;+ ; ; CALL near. ; ;- jmp short undc86 caln86: call getw ;get offset add ax,bp ;compute addr call labl ;label jmp crlf ;crlf, return undc86: ; trace entry call getw ;get a word add ax,bp ;find addr jmp cndjm1 ;recurse ;+ ; ; CALL far. ; ;- jmp short unfc86 calf86: call getw ;get offset push ax call getw ;seg call hex4 mov al,':' ;: stosb pop ax ;offset call hex4 jmp crlf unfc86: add bp,4 ;skip seg:offset ret ;+ ; ; PUSH/POP seg reg ; ;- ret nop pseg86: mov bl,dl ;copy call seg86a ;reg name jmp crlf ;+ ; ; PUSH immediate value. ; ;- jmp short pim86a pimm86: call getw ;get a word test byte ptr cs:opsize,-1 ;32 bits? jnz pimm32 ;yes jmp imm16 ;just 16 pimm32: jmp imm32 pim86a: call getw ;get a word test byte ptr cs:opsize,-1 ;32 bits? jz pim86b call getw ;get another word if so pim86b: ret ;+ ; ; PUSH sign-extended 8-bit immediate value. ; ;- inc bp ;eat imm val ret pimm8: call getb ;get byte call offst ;offset jmp crlf ;+ ; ; INT n. ; ; Stop on INT 20h, or INT 21h if prev instruction was MOV AX,4CXX. ; ;- jmp short tint86 int86: call getb ;get a byte push ax ;save call hexb ;display call crlf ;crlf pop ax ;restore cmp al,20h ;exit? je int86a ;yes cmp al,21h ;DOS call? jne int86b ;no mov ax,bp ;copy dec ax ;-2 dec ax cmp ax,cs:addr4C ;prev instruction=MOV AX,4CXX? jne int86b ;no int86a: jmp stop ;new block int86b: ret tint86: ; trace entry call getb ;get a byte cmp al,20h ;exit? je ti86a ;yes cmp al,21h ;DOS call? jne ti86b ;no mov ax,bp ;copy dec ax ;-2 dec ax cmp ax,cs:addr4C ;prev inst=MOV AX,4CXX? jne ti86b ;no ti86a: pop ax ;flush r.a. ti86b: ret ;+ ; ; RET[F] nnnn ; ;- jmp short ret86a ret86: call getw ;get parm call hexw ;display call crlf jmp stop ;stop ret86a: ; trace entry pop ax ;flush (forget about parm) ret ;+ ; ; ENTER imm8,imm16. ; ;- jmp short ent86a ent86: call getb ;imm8 call hexb stosb ;, call getw ;imm16 call hexw jmp crlf ent86a: add bp,3 ;skip 3 ret ;+ ; ; IN a,port. ; ;- inc bp ret in86: xor bl,bl ;zap reg # call reg86 ;al/ax/eax stosb ;, call getb ;port # call hexb jmp crlf ;+ ; ; IN a,DX. ; ;- ret nop indx86: xor bl,bl ;zap reg # call reg86 ;al/ax/eax stosb ;, mov ax,"xd" ;DX stosw jmp crlf ;+ ; ; OUT port,a. ; ;- inc bp ret out86: call getb ;get a byte call hexb ;byte stosb ;, xor bl,bl ;al/ax/eax call reg86 jmp crlf ;+ ; ; OUT DX,a. ; ;- ret nop oudx86: xor bl,bl ;al/ax/eax call reg86 jmp crlf ;+ ; ; String instructions. ; Give the simple form unless we have to show it all. ; ; This is an ugly mess. ; ;- ret nop cmps86: call rep86 ;REP prefix mov si,offset tcmps ;string mov cx,4 ;length test byte ptr cs:adsize,-1 ;32-bit address? jnz cmps1 test byte ptr cs:segpfx,-1 ;prefix? jz cmps2 ;no cmps1: inc cx ;add tab rep movs [di],byte ptr cs:[si] ;copy mov bl,2 ;2 and bl,cs:adsize ;or 0 if 16-bit or bl,4 ;4=[SI], 6=[ESI] call ptr86 ;PTR text call modrm ;mod-r/m stosb ;, call bdi86 ;[di] jmp crlf cmps2: jmp stsz86 ;go show size tcmps db 'cmps',tab ret nop ins86: ; INS mov si,offset tins jmp stor86 tins db 3,'ins' ; ret nop lods86: ; LODS mov si,offset tlods ;string load86: call rep86 ;repeat prefix mov cx,4 ;length rep movs [di],byte ptr cs:[si] ;copy test byte ptr cs:adsize,-1 ;32-bit addressing? jnz src86 ;yes, hard case test byte ptr cs:segpfx,-1 ;prefix? jnz src86 ;yes jmp stsz86 ;no, just show size src86: mov al,tab ;tab src86a: stosb mov bl,2 ;[SI] and bl,cs:adsize ;or 0 or bl,4 ;4=[SI], 6=[ESI] call ptr86 ;PTR msg call modrm ;show it jmp crlf ;crlf, return tlods db 'lods' ; ret nop movs86: ; MOVS call rep86 ;REP prefix mov si,offset tmovs ;pt at string mov cx,4 ;length test byte ptr cs:adsize,-1 ;32-bit addressing? jnz dsrc86 ;show dest and source if so test byte ptr cs:segpfx,-1 ;seg prefix? jz stsz86 ;no, just show size dsrc86: inc cx ;add tab rep movs [di],byte ptr cs:[si] ;copy call bdi86 ;[di] mov al,',' ;, jmp short src86a ;go show source tmovs db 'movs',tab ; ret nop outs86: ; OUTS mov si,offset touts ;string jmp short load86 ;go deal touts db 'outs' ; ret nop scas86: ; SCAS mov si,offset tscas jmp short stor86 tscas db 4,'scas' ; ret nop stos86: ; STOS mov si,offset tstos stor86: call rep86 ;prefix lods byte ptr cs:[si] ;get length cbw mov cx,ax ;in cx test byte ptr cs:opsize,-1 ;normal addressing? jz stsz86 ;yes mov al,tab ;tab stosb xor bl,bl ;mod-r/m=memory call ptr86 ;PTR call bdi86 ;[di] jmp crlf stsz86: ; just show size on string instruction ; si=ptr, cx=length rep movs [di],byte ptr cs:[si] ;copy mov al,'b' ;assume byte test dl,1 ;byte or word? jz str86a ;byte mov al,'w' ;word then test byte ptr cs:opsize,-1 ;word or dword? jz $+4 ;word mov al,'d' ;dword str86a: stosb ;write size byte jmp crlf ;crlf, return ; tstos db 4,'stos' ;+ ; ; Display "[(E)DI]". ; ;- bdi86: mov al,'[' ;[ stosb test byte ptr cs:adsize,-1 ;32 bits? jz $+5 mov al,'e' ;e stosb mov ax,"id" ;di stosw mov al,']' ;] stosb ret ;+ ; ; XLAT (BYTE PTR xS:[(E)BX]). ; ;- ret nop xlat86: mov bl,3 ;mod-r/m=[EBX] test byte ptr ds:adsize,-1 ;[EBX]? jnz xlat1 ;yes mov bl,7 ;mod-r/m=[BX] test byte ptr ds:segpfx,-1 ;DS:? jnz xlat1 ;no jmp crlf xlat1: mov al,tab ; stosb mov si,offset bytptr ;pt at msg mov cx,11 ;length rep movs [di],byte ptr cs:[si] ;copy xor dl,dl ;byte instruction call modrm ;display seg:[(E)BX] jmp crlf ;+ ; ; Display a "BYTE PTR" message or whatever, if needed. ; ; bl mod-r/m byte ; dl opcode (b0=word) ; ;- ptr86: cmp bl,300 ;register? jae ptr86b ;yes, no msg mov cx,11 ;length mov si,offset bytptr ;pt at msg test dl,1 ;byte? jz ptr86a ;yes add si,cx ;skip test byte ptr cs:opsize,-1 ;32 bits? jz ptr86a ;skip add si,cx ;skip again inc cx ;count the "d" ptr86a: rep movs [di],byte ptr cs:[si] ;copy text ptr86b: ret ; bytptr db 'byte ptr ' db 'word ptr ' db 'dword ptr ' ;+ ; ; Display operand shown by "mod r/m" byte. ; ; bl mod r/m byte ; dl<0> 0 => byte, 1 => word ; ; Returns BL preserved, comma in AL. ; ;- modrmg: ; getb the byte first call getb ;get byte mov bl,al ;save modrm: ; byte in bl cmp bl,300 ;mod=3? jae modrm4 ;; see if 32-bit mov al,bl ;copy and al,307 ;lose 3 bits in the middle cmp al,6 ;[DISP16]? je moff86 mov cl,al ;save call pfx86 ;do prefix, if any mov al,cl ;restore mov cx,3 ;bit/byte count sal al,cl ;*8 test al,40 ;r/m <3? jnz $+4 sal cl,1 ;*2=6 and ax,70 ;isolate bits 5:3 add ax,offset ea86 ;index mov si,ax ;point at name rep movs [di],byte ptr cs:[si] ;copy all but ']' cmp bl,100 ;mod=0? jb modrm1 cmp bl,200 ;mod=2? jae modrm3 ; [reg(s)+HH] (mod=1) push bx ;save call getb ;get byte call offst ;do offset pop bx modrm1: ; [reg(s)] (mod=0) inc si ;skip + movs [di],byte ptr cs:[si] ;] mov al,',' ;may be needed ret moff86: ; [HHHH] (mod=0, r/m=6) cmp byte ptr cs:segpfx,0 ;prefix defined? jnz $+8d mov byte ptr cs:segpfx,'d' ;use ds: to show it's a mem ref push bx ;save call pfx86 ;do prefix call getw ;get word call labl ;label pop bx ;restore mov al,',' ;may be needed ret modrm3: ; [reg(s)+HHHH] (mod=2) movs [di],byte ptr cs:[si] ;+ push bx ;save call getw ;get word call labl ;label pop bx ;restore movs [di],byte ptr cs:[si] ;copy ] mov al,',' ;may be needed ret modrm4: ; register jmp short reg86 ;go ; eatrm: ; same as above, but eat it (mod r/m byte returned in BL, DL preserved) call getb ;get mod r/m push ax ;save and al,307 ;isolate mode stuff cmp al,6 ;[DISP16]? je eatr1 cmp al,100 ;mod=0? jb eatr3 cmp al,200 ;mod=1? jb eatr2 cmp al,300 ;mod=2? jae eatr3 eatr1: call getw ;get 16-bit offset mov bl,data ;enter in symbol table call addsym ;and return dec bp ;undo next instruction eatr2: inc bp ;eat byte offset eatr3: pop bx ;restore mod-r/m into BL ret ;+ ; ; Display register. ; ; bl reg # ; dl<0> 1 => word, else byte ; ; Returns ',' in AL, BL preserved. ; ;- reg86g: call getb ;get the byte mov bl,al ;copy reg86a: mov al,bl ;get from old mod r/m byte mov cl,3 ;bit count shr al,cl ;right 3 jmp short $+4 reg86: mov al,bl ;copy and ax,7 ;reg # sal al,1 ;left 1 test dl,1 ;word? jz r86a ;no add al,20 ;yes, skip to 16-bit regs cmp byte ptr cs:opsize,0 ;really? jz r86a ;yes mov byte ptr es:[di],'e' ;no, 32-bit regs inc di r86a: add ax,offset gr86 ;add base mov si,ax ;point movs word ptr [di],word ptr cs:[si] ;copy mov al,',' ;may be needed ret ;+ ; ; Display segment register in BL. ; ;- seg86a: mov al,bl ;copy mov cl,3 ;bit count shr al,cl ;right 3 jmp short $+4 seg86: mov al,bl ;copy sal al,1 ;*2 and ax,7*2 ;isolate add ax,offset s86 ;add offset mov si,ax ;copy movs word ptr [di],word ptr cs:[si] ;copy the name ret ;+ ; ; Display a repeat prefix, as appropriate. ; ; dl prefixed opcode ; ;- rep86: xor al,al ;load 0 xchg al,cs:reppfx ;zap prefix, get it or al,al ;anything? jz rep86d ;no push si ;save mov si,offset trepn ;pt at REPN mov cx,3 ;count for now cmp al,145 ;REPC or REPNC? jbe rep86b ;yes, just show 'em cmp dl,246 ;REPE/REPNE, check opcode je rep86a ;CMPS, show E/NE cmp dl,256 ;SCAS? jne rep86c ;no rep86a: ; show E/NE mov ah,'e' ;load it jmp short $+4 ;show all rep86b: mov ah,'c' ;REPC/REPNC (NEC V20 only) test al,1 ;N? jnz $+3 inc cx ;yes rep movs [di],byte ptr cs:[si] ;copy mov al,ah ;copy letter mov ah,tab stosw ;save letter pop si ret rep86c: rep movs [di],byte ptr cs:[si] ;copy REP mov al,tab ;tab stosb pop si ;restore rep86d: ret ; trepn db 'repn' ;+ ; ; Display segment prefix, if one has been seen. ; ;- pfx86: xor al,al ;load 0 xchg al,cs:segpfx ;set prefix test al,al ;is there one? jz pfx86a ;no stosb ;x mov ax,":s" ;s: stosw pfx86a: ret ;+ ; ; Display 16-bit immediate value, CRLF. ; ; ax value ; ;- imm16: call hexw ;display it jmp crlf ; ea86 db '[bx+si+][bx+di+][bp+si+][bx+si+]' ;all 8 bytes db '[si+] [di+] [bp+] [bx+]' gr86 db 'alcldlblahchdhbhaxcxdxbxspbpsidi' ;byte/word regs s86 db 'escsssdsfsgss6s7' ;seg regs, s6/s7 are undefined segpfx db ? ;NZ => seg prefix reppfx db ? ;NZ => repeat prefix opcode opsize db ? ;NZ => 32-bit operands adsize db ? ;NZ => 32-bit addresses addr4C dw -1 ;addr of last MOV AX,4CXX instruction ;;; this would be better, not implemented yet: ahval dw -1 ;GE => known value of AH alval dw -1 ;GE => known value of AL ; subttl PDP-8 routines ;+ ; ; Memory reference instructions. ; ;- ret nop mri: mov al,' ' ;blank stosb test dh,400/400 ;indirect? jz mri1 ;no mov ax," I" ;'I ' stosw mri1: call addr8 ;compute address call labl8 ;print label name or number jmp crlf ;+ ; ; ISZ -- MRI with conditional skip ; ;- jmp short isz1 ;tracing is special isz: jmp short mri ;but looks like any other MRI isz1: ; trace conditional skip push bp ;save where we are inc bp ;skip following opcode inc bp call dry ;recurse pop bp ;restore ret ;continue where we left off ;+ ; ; JMS (jump to subroutine). ; ;- jmp short jms9 jms8: jmp short mri ;looks like any other MRI jms9: test dh,400/400 ;indirect? jnz jms11 ;yes, don't know where to go call addr8 ;sort out address add ax,ax ;byte addr push bp ;save where we are mov bp,ax ;trace the called code call chksym ;defined? test ah,inst ;as an instruction? jnz jms10 ;yes, no point in continuing mov ax,bp ;no, define it mov bl,inst ;as a jump destination call addsym inc bp ;but actually trace from the following word inc bp call xdry jms10: pop bp ;restore jms11: ret ;continue where we left off ;+ ; ; JMP ; ;- jmp short jmp9 jmp8: jmp short mri ;looks like any other MRI jmp9: test dh,400/400 ;indirect? jnz jmp10 ;yes, don't know where to go call addr8 ;sort out address add ax,ax ;byte addr jmp undjm1 ;unconditional jump jmp10: pop ax ;flush R.A. ret ;return to caller's caller ; addr8: ; figure out PDP-8 addr from MRI opcode in DX, return in AX mov cx,dx ;copy mov ax,bp ;copy addr shr ax,1 ;word addr dec ax ;back to addr of opcode word and ax,7600 ;isolate page addr test dl,200 ;this page? jnz addr9 xor ax,ax ;no, page 0 addr9: and dl,177 ;isolate offset within page or al,dl ;combine ret ;+ ; ; IOT nnn ; ;- ret nop iot: mov al,' ' ;blank stosb mov ax,dx ;get opcode mov cx,3 ;3 digits call octn jmp crlf ;+ ; ; Group 1 operate instructions. ; ;- ret ;nothing to it nop opra: mov si,offset oprg1 ;point at table jmp short opr ;print, return ;+ ; ; Group 2 operate instructions (including skips). ; ;- jmp short oprb1 oprb: mov si,offset oprg2 ;point at table jmp short opr ;print, return oprb1: test dl,170 ;any "skip" bits set? jz oprb2 ;no push bp ;save where we are inc bp ;skip following opcode inc bp call dry ;recurse pop bp ;restore oprb2: ret ;continue where we left off ;+ ; ; Group 3 operate instructions. ; ;- ret nop oprc: mov si,offset oprg3 ;point at table jmp short opr ;print, return ;+ ; ; Compose "operate" instruction mnemonics. ; ; cs:si pointer to list of bits to check and strings to add ; dl low 8 bits of opcode word ; ;- opr: xor bl,bl ;nothing written yet (so no blank) opr1: lods word ptr cs:[si] ;fetch expected value,,mask test al,al ;end? je opr4 and al,dl ;isolate cmp al,ah ;matches expected value? lods byte ptr cs:[si] ;[fetch string length] cbw ;[AH=0] jz opr2 ;yes add si,ax ;skip string jmp short opr1 ;back for more opr2: mov cx,ax ;copy string length test bl,bl ;first time through? jz opr3 mov al,' ' ;no, add blank stosb opr3: rep movs [di],byte ptr cs:[si] ;copy string mov bl,1 ;definitely add a blank next time jmp short opr1 opr4: jmp crlf ;CRLF, return ; oprg1 label byte ;group 1 operate instructions ; sequence 1 db 0200,0200,3,'CLA' db 0100,0100,3,'CLL' ; sequence 2 db 0040,0040,3,'CMA' db 0020,0020,3,'CML' ; sequence 3 db 0001,0001,3,'IAC' ; sequence 4 db 0016,0010,3,'RAR' db 0016,0004,3,'RAL' db 0016,0012,3,'RTR' db 0016,0006,3,'RTL' db 0016,0002,3,'BSW' db 0016,0014,7,'RAR RAL' ;(officially illegal) db 0016,0016,7,'RTR RTL' ;(officially illegal) dw 0 ;end of table ; oprg2 label byte ;group 2 operate instructions ; sequence 1 db 0110,0100,3,'SMA' db 0110,0110,3,'SPA' db 0050,0040,3,'SZA' db 0050,0050,3,'SNA' db 0030,0020,3,'SNL' db 0030,0030,3,'SZL' db 0170,0010,3,'SKP' ; sequence 2 db 0200,0200,3,'CLA' ; sequence 3 db 0004,0004,3,'OSR' db 0002,0002,3,'HLT' dw 0 ;end of table ; oprg3 label byte ;group 3 operate instructions ; sequence 1 db 0200,0200,3,'CLA' ; sequence 2 db 0100,0100,3,'MQA' db 0020,0020,3,'MQL' ;;; don't have the other KE8/E EAE instructions yet dw 0 ;end of table ; porg8: ; print PDP-8 style ORG statement push ax ;save mov al,'*' ;asterisk stosb pop ax ;restore shr ax,1 ;word addr jmp oct4 ;print it, return ; plab8: ; print PDP-8 style label shr ax,1 ;word addr call labl8 ;print L plus four octal digits mov al,',' ;, stosb ret ; subttl utility routines ;+ ; ; Display register in AL. ; ; REG: al<2:0> ; REG1: al<5:3> ; ;- reg1: mov al,dl ;get opcode mov cl,3 ;bit count shr al,cl ;right 3 jmp short $+4 reg: mov al,dl ;get opcode and ax,7 ;isolate cmp al,6 ;(hl)? je regm ;skip mov bx,ax ;copy mov al,byte ptr cs:regs[bx] ;look up stosb ;save mov al,',' ;often needed ret regm: ; (hl) mov ax,"h(" ;write it stosw mov ax,")l" stosw mov al,',' ;often needed ret ;+ ; ; Same as above for Intel mnemonics, reg in DL. ; Same deal, only "(hl)" is shown as "m". ; ;- ireg: mov bl,dl ;copy ireg2: and bx,7 ;isolate mov al,byte ptr cs:regs[bx] ;look up stosb ;save ret ; ireg1: ; same thing with reg in DL<5:3> mov bl,dl ;copy mov cl,3 ;bit count shr bl,cl ;shift into place jmp short ireg2 ;+ ; ; Display register pair in DL<5:4>. ; ;- regpr: mov bl,dl ;copy opcode mov cl,3 ;bit count shr bl,cl ;move and bx,6 ;isolate mov ax,word ptr cs:regprs[bx] ;look up stosw ;save ret ;+ ; ; Same as above for Intel mnemonics, opcode in DL. ; Only the first letter of bc, de, or hl is shown (sp is OK though). ; ;- iregpr: mov bl,dl ;copy mov cl,3 ;bit count shr bl,cl ;move and bx,6 ;isolate mov ax,word ptr cs:regprs[bx] ;look up cmp al,'s' ;sp? je $+4 stosb ;no, write only 1st byte ret stosw ;sp ret ;+ ; ; Display a byte in hex with a leading 0 if needed, and trailing h. ; ; Byte comes in AL. ; ;- hexb: cmp al,0A0h ;starts with a letter? jb hexb1 ;no mov byte ptr es:[di],'0' ;add leading 0 inc di ;+1 hexb1: call hex2 ;show it mov al,'h' ;h stosb mov al,',' ;often needed ret ;+ ; ; Same as above for a word (in AX). ; ;- hexw: cmp ah,0A0h ;starts with a letter? jb hexw1 ;no mov byte ptr es:[di],'0' ;add leading 0 inc di hexw1: call hex4 ;display mov al,'h' ;h stosb mov al,',' ret ;+ ; ; Display address in AX as a label if it is within the range of the ; disassembly. ; ;- labl: cmp ax,cs:dstart ;hm? jb hexw cmp ax,cs:dend ja hexw mov byte ptr es:[di],'L' ;prefix an L inc di jmp short hex4 ;yep ;+ ; ; Print hex # in AL/AX. ; ;- hex2: ; 2 digits mov bx,2 ;length jmp short hex hex4: ; 4 digits mov bx,4 ;length hex: ; print BX-digit hex # push si ;save push bx add di,bx ;advance ptr mov ch,bl ;copy mov bx,ax ;copy mov cl,4 ;bit count hex1: mov si,bx ;copy and si,17 ;isolate mov al,cs:hextab[si] ;look up dec di ;back 1 mov es:[di],al shr bx,cl ;shift dec ch ;done? jnz hex1 ;loop if not pop ax ;restore add di,ax ;advance pop si ;restore ret ;+ ; ; Display address in AX as an octal PDP-8 label if it is within the range of ; the disassembly. ; ;- labl8: mov cx,ax ;copy add cx,cx ;byte addr cmp cx,cs:dstart ;hm? jb oct4 cmp cx,cs:dend ja oct4 mov byte ptr es:[di],'L' ;prefix an L inc di ;jmp short oct4 ;yep ;+ ; ; Print 4-digit octal number in AX. ; ;- oct4: mov cx,4 ;digit count octn: ; enter with # digits in CX add di,cx ;skip to end push di ;save octn1: mov bl,al ;copy and bl,7 ;isolate or bl,'0' ;convert to digit dec di ;back up mov es:[di],bl ;save digit shr ax,1 ;right 3 bits shr ax,1 shr ax,1 loop octn1 ;do all digits pop di ;restore ptr past end ret ;+ ; ; Skip blanks. ; ; ds:si pointer ; ; AL returns the char we stopped on, ZF=1 if it's 0. ; ;- skip: lodsb ;get a char or al,al ;end? jz skip1 cmp al,' ' ;blank or ctrl? jbe skip ;loop if so skip1: dec si ;unget or al,al ;set ZF ret ;+ ; ; Get a hex number. ; ; ds:si pointer ; ; Returns number in AX, DS:SI stopped at first non-whitespace, non-hex ; character. CF=1 if there were no good hex characters. ; ;- gethex: lodsb ;get a char or al,al ;eol? jz ghex5 cmp al,' ' ;blank or cc? jbe gethex ;yes, ignore dec si ;unget xor ah,ah ;zap good-digit-seen flag xor bx,bx ;zap # mov cl,4 ;bit count ghex1: lodsb ;get a char cmp al,dl ;SWITCHAR? je ghex4 sub al,'0' ;see if # cmp al,9d ;0-9? jbe ghex3 sub al,'A'-'0' ;try for letter cmp al,5 ;A-F? jbe ghex2 sub al,'a'-'A' ;lower case cmp al,5 ;a-f? ja ghex4 ghex2: add al,10d ;A-F => 10.-15. ghex3: sal bx,cl ;left 4 or bl,al ;OR in new digit inc ah ;set flag jmp short ghex1 ;continue ghex4: ; invalid hex char (including null) dec si ;unget sub ah,1 ;set CF if nothing gotten mov ax,bx ;[copy] ret ghex5: stc ;error return ret ;+ ; ; Parse a word from the input line. ; ; ds:si current position ; dl SWITCHAR (preserved) ; ; On return: ; si points at posn after last char of word ; bx points at begn of word if CF=0 ; cx length of word ; ;- getwd: mov bx,si ;save ptr getwd1: lodsb ;get a char cmp al,' ' ;blank/ctrl (or NUL at end of string)? jbe getwd2 ;yes, end cmp al,dl ;SWITCHAR? je getwd2 ;yes, end cmp al,'a' ;lower case? jb getwd1 ;loop if not cmp al,'z' ja getwd1 and byte ptr [si-1],not 40 ;convert to UC if so jmp short getwd1 ;and loop getwd2: dec si ;unget char we stopped on mov cx,si ;compute length sub cx,bx ;find length cmp cx,1 ;CF=1 if null ret ;+ ; ; Look up a keyword in a table. ; ; ds:bx keyword } from GETWD ; cx length } ; cs:ax table ; ; Returns CF=1 if not found, otherwise AX=number from table. ; ; This routine doesn't require that DS=CS, so it may be used to parse ; environment strings. ; ; si,dl preserved either way (presumably line ptr and SWITCHAR value). ; ;- tbluk: push si ;save push es push ds ;copy DS to ES pop es push cs ;copy CS to DS pop ds mov si,ax ;point at table tbluk1: lodsb ;get length of next string and ax,377 ;AH=0, see if 0 jz tbluk3 ;end of table if so xchg ax,cx ;CX=len of this string, AX=len of test string cmp ax,cx ;does length match? jne tbluk2 ;no mov di,bx ;point at test string repe cmpsb ;match? je tbluk4 ;yes tbluk2: add si,cx ;skip rest of string mov cx,ax ;restore length of test string lodsw ;skip value jmp short tbluk1 ;keep looking tbluk3: push es ;restore DS pop ds pop es ;and ES/SI pop si stc ;failed ret tbluk4: lodsw ;get value push es ;restore DS pop ds pop es ;and ES/SI pop si clc ;happy ret ;+ ; ; Add an address to symbol table. ; ; ax address ; bl bit mask to set (INST or DATA) ; dl preserved (required by EATRM for EATRMB to detect POP) ; ; Returns NZ if it was already defined. ; ;- addsym: mov cl,al ;copy low bits shr ax,1 ;right 2 shr ax,1 and cl,3 ;isolate low 2 sal cl,1 ;*2 mov bh,3 ;load 2 1's sal bh,cl ;shift into place and bl,bh ;isolate mask xchg ax,bx ;swap and ah,es:[bx] ;get old bits or es:[bx],al ;set bit or ah,ah ;ZF=0 if it was already defined ret ;+ ; ; Same thing again but just checks to see if defined. ; ;- chksym: mov cl,al ;copy low bits shr ax,1 ;right 2 shr ax,1 and cl,3 ;isolate low 2 sal cl,1 ;*2 mov bh,3 ;load 2 1's sal bh,cl ;shift into place xchg ax,bx ;swap and ah,es:[bx] ;get bits ret ;+ ; ; Get next byte into AL, update BP, preserve DX. ; ; (BP contains the Z-80 PC value throughout this program). ; ;- getb: mov al,ds:[bp] ;fetch byte inc bp ;advance pc ret ;+ ; ; Get a word. ; ;- getw: mov ax,ds:[bp] ;fetch word inc bp ;advance inc bp ret ; cpus label byte ;TBLUK table for CPU types db 4,'8051' dw mcs51 db 4,'8080' dw intel db 4,'8086' dw iapx86 db 3,'Z80' dw z80 db 4,'PDP8' dw pdp8 db 0 ;end of table ; mnem dw z80 ;starting posn in symbol table ; faddr dw 100h ;/F:file load addr dstart dw 100h ;/S:disassembly starting addr dend dw 177777 ;/E:disassembly ending addr xfer dw 100h ;/T:transfer address ; xhand dw 0 ;NZ => handle of /T:@file ; last dw 177777 ;actual last addr of file ; lukerr db 33,0,'?Error opening input file',cr,lf enterr db 35,0,'?Error creating output file',cr,lf xfrerr db 46,0,'?Error opening transfer address file',cr,lf xrderr db 46,0,'?Error reading transfer address file',cr,lf lderrm db 30,0,'?Input file read error',cr,lf ; help dw lhelp db 'DASM V1.2 Tracing disassembler ',cr,lf db 'Copyright (C) 1994-1999 by John Wilson.' db ' All rights reserved.',cr,lf db 'command line: filename[.COM] (out=.ASM, or STDIN/OUT)',cr,lf db ' ~S:hhhh starting addr for disassembly',cr,lf db ' ~E:hhhh ending addr for disassembly',cr,lf db ' ~T:hhhh transfer addr (def=0100), or:',cr,lf db ' ~T:@file blank- or CRLF-separated list of xfer addrs in' db ' file',cr,lf db ' ~F:hhhh load addr of file (def=0100)',cr,lf db ' ~C:xxxx give mnemonics for specified CPU',cr,lf db ' ~C:Z80 Zilog Z80 (default)',cr,lf db ' ~C:8051 Intel 8051/8052/8751',cr,lf db ' ~C:8080 Intel 8080 (syn. ~I)',cr,lf db ' ~C:8086 Intel 80x86 (syn. ~N)',cr,lf lhelp= $-help-2 ; ddcb db '0DDh,0CBh,0' ;prefix for pfx'ed invalid opcodes org1 db tab,'org',tab ;initial ORG statement dead db tab,'; dead',cr,lf ;comment for dead data hextab db '0123456789ABCDEF' ;hex digits regs db 'bcdehlma' ;register names regprs db 'bcdehlsp' ;register pair names (2 chars each) cond db 'nzz,ncc,popep,m,' ;conditions (2 chars each) ; ; Intel mnemonics for 8080/8085 instruction set. ; symtab intel,porgh,plabh,1 ;hex orgs/labels, 1-byte opcodes op 'halt',166,377,nostop ;=MOV M,M (must come before MOV) ; move, load, store opt 'mov',100,300,irr opt 'mvi',006,307,irn opt 'lxi',001,317,irpnn opt 'stax',002,357,irpp opt 'ldax',012,357,irpp opt 'sta',062,377,imm opt 'lda',072,377,imm opt 'shld',042,377,imm opt 'lhld',052,377,imm op 'xchg',353,377,no ; stack ops op 'push psw',365,377,no opt 'push',305,317,irpp op 'pop psw',361,377,no opt 'pop',301,317,irpp op 'xthl',343,377,no op 'sphl',371,377,no ; jumps opt 'jmp',303,377,nn opt 'jc',332,377,callnn opt 'jnc',322,377,callnn opt 'jz',312,377,callnn opt 'jnz',302,377,callnn opt 'jp',362,377,callnn opt 'jm',372,377,callnn opt 'jpe',352,377,callnn opt 'jpo',342,377,callnn op 'pchl',351,377,nostop ; calls opt 'call',315,377,callnn opt 'cc',334,377,callnn opt 'cnc',324,377,callnn opt 'cz',314,377,callnn opt 'cnz',304,377,callnn opt 'cp',364,377,callnn opt 'cm',374,377,callnn opt 'cpe',354,377,callnn opt 'cpo',344,377,callnn ; returns op 'ret',311,377,nostop op 'rc',330,377,no op 'rnc',320,377,no op 'rz',310,377,no op 'rnz',300,377,no op 'rp',360,377,no op 'rm',370,377,no op 'rpe',350,377,no op 'rpo',340,377,no ; restart opt 'rst',307,307,irst ; I/O opt 'in',333,377,iio ;like iimm but don't give ASCII opt 'out',323,377,iio ; inc and dec opt 'inr',004,307,ir1 opt 'dcr',005,307,ir1 opt 'inx',003,317,irpp opt 'dcx',013,317,irpp ; add opt 'add',200,370,ir opt 'adc',210,370,ir opt 'adi',306,377,iimm opt 'aci',316,377,iimm opt 'dad',011,317,irpp ; subtract opt 'sub',220,370,ir opt 'sbb',230,370,ir opt 'sui',326,377,iimm opt 'sbi',336,377,iimm ; logical opt 'ana',240,370,ir opt 'xra',250,370,ir opt 'ora',260,370,ir opt 'cmp',270,370,ir opt 'ani',346,377,iimm opt 'xri',356,377,iimm opt 'ori',366,377,iimm opt 'cpi',376,377,iimm ; rotate op 'rlc',007,377,no op 'rrc',017,377,no op 'ral',027,377,no op 'rar',037,377,no ; specials op 'cma',057,377,no op 'stc',067,377,no op 'cmc',077,377,no op 'daa',047,377,no ; control op 'ei',373,377,no op 'di',363,377,no op 'nop',000,377,no ; 8085A-specific instructions (opcodes interfere with Z-80) op 'rim',040,377,no ;read interrupt mask (opcode=JR NZ,) op 'sim',060,377,no ;set interrupt mask (opcode=JR NC,) ; not valid MCS-80/85 instruction opt '; non-8080/85 instruction',000,000,non85 ; ; General Z-80 instructions. ; symtab z80,porgh,plabh,1 ;hex orgs/labels, 1-byte opcodes op '',313,377,cbpfx ;CB prefix op '',335,377,ddpfx ;DD prefix (IX replaces HL) op '',355,377,edpfx ;ED prefix op '',375,377,fdpfx ;FD prefix (IY replaces HL) ; (from G.P. arith/CPU control group) op 'halt',166,377,nostop ;=LD (HL),(HL) (must come before LD) ; 8-bit load group opt 'ld',100,300,rr opt 'ld',006,307,rn op 'ld a,(bc)',012,377,no op 'ld a,(de)',032,377,no opt 'ld a,',072,377,amm op 'ld (bc),a',002,377,no op 'ld (de),a',022,377,no opt 'ld',062,377,mma ; 16-bit load group opt 'ld',001,317,ddnn opt 'ld',052,377,ddmm ;dd bits happen to show HL opt 'ld',042,377,mmdd ;dd bits happen to show HL op 'ld sp,hl',371,377,no op 'push af',365,377,no ;special case of dd opt 'push',305,317,rp op 'pop af',361,377,no ;special case of dd opt 'pop',301,317,rp ; exch, blt, blk srch group op 'ex de,hl',353,377,no op "ex af,af'",010,377,no op 'exx',331,377,no op 'ex (sp),hl',343,377,no ; 8-bit arith/logical group op 'add a,',200,370,r op 'add a,',306,377,imm8 op 'adc a,',210,370,r op 'adc a,',316,377,imm8 opt 'sub',220,370,r opt 'sub',326,377,imm8 opt 'sbc',230,370,r opt 'sbc',336,377,imm8 opt 'and',240,370,r opt 'and',346,377,imm8 opt 'xor',250,370,r opt 'xor',356,377,imm8 opt 'or',260,370,r opt 'or',366,377,imm8 opt 'cp',270,370,r opt 'cp',376,377,imm8 opt 'inc',004,307,r1 opt 'dec',005,307,r1 ; G.P. arith/CPU control group op 'daa',047,377,no op 'cpl',057,377,no op 'ccf',077,377,no op 'scf',067,377,no op 'nop',000,377,no op 'di',363,377,no op 'ei',373,377,no ; 16-bit arithmetic group op 'add hl,',011,317,rp opt 'inc',003,317,rp opt 'dec',013,317,rp ; rotate/shift group op 'rlca',007,377,no op 'rla',027,377,no op 'rrca',017,377,no op 'rra',037,377,no ; jump group opt 'jp',303,377,nn opt 'jp',302,307,ccnn opt 'jr',030,377,ujr op 'jr c,',070,377,cjr op 'jr nc,',060,377,cjr op 'jr z,',050,377,cjr op 'jr nz,',040,377,cjr op 'jp (hl)',351,377,nostop opt 'djnz',020,377,cjr ; call/return group opt 'call',315,377,callnn opt 'call',304,307,ccnn op 'ret',311,377,nostop opt 'ret',300,307,cc opt 'rst',307,307,rst ; I/O group opt 'in a,',333,377,upn opt 'out',323,377,pna ; anything else is undefined opt 'db',000,000,bite ;matches everything else ; ; Instructions prefixed by DD or FD, in which accesses to HL (or (HL)) are ; replaced by IX (or (IX+d)) or IY (or (IY+d)), respectively. ixtab label byte op '',313,377,cbix ;xD CB prefix (argh!) ; 8-bit load group opt 'ld',106,307,rixm ;not 105 -- Z-80 Prod. Spec. is WRONG opt 'ld',105,307,rixl ;undoc'ed (0DDh; LD r,L) opt 'ld',104,307,rixh ;undoc'ed (0DDh; LD r,H) opt 'ld',160,370,ixmr opt 'ld',150,370,ixlr ;same deal (0DDh; LD L,r) opt 'ld',140,370,ixhr opt 'ld',066,377,ixmn ; 16-bit load group opt 'ld',041,377,ixnn opt 'ld',052,377,ixmm opt 'ld',042,377,mmix opt 'ld sp,',371,377,uixx opt 'push',345,377,ixx opt 'pop',341,377,ixx ; exch, blt, blk srch group opt 'ex (sp),',343,377,uixx ; 8-bit arith/logical group opt 'add a,',206,377,uixm opt 'add a,',205,377,uixl ;undoc'ed opt 'add a,',204,377,uixh ;undoc'ed opt 'adc a,',216,377,uixm opt 'adc a,',215,377,uixl ;undoc'ed opt 'adc a,',214,377,uixh ;undoc'ed opt 'sub',226,377,ixm opt 'sub',225,377,ixl ;undoc'ed opt 'sub',224,377,ixh ;undoc'ed opt 'sbc',236,377,ixm opt 'sbc',235,377,ixl ;undoc'ed opt 'sbc',234,377,ixh ;undoc'ed opt 'and',246,377,ixm opt 'and',245,377,ixl ;undoc'ed opt 'and',244,377,ixh ;undoc'ed opt 'xor',256,377,ixm opt 'xor',255,377,ixl ;undoc'ed opt 'xor',254,377,ixh ;undoc'ed opt 'or',266,377,ixm opt 'or',265,377,ixl ;undoc'ed opt 'or',264,377,ixh ;undoc'ed opt 'cp',276,377,ixm opt 'cp',275,377,ixl ;undoc'ed opt 'cp',274,377,ixh ;undoc'ed opt 'inc',064,377,ixm opt 'inc',054,377,ixl ;undoc'ed opt 'inc',044,377,ixh ;undoc'ed opt 'dec',065,377,ixm opt 'dec',055,377,ixl ;undoc'ed opt 'dec',045,377,ixh ;undoc'ed ; 16-bit arithmetic group opt 'add',011,317,ixpp ;not 111 (Z-80 prod. spec. is wrong) opt 'inc',043,377,ixx opt 'dec',053,377,ixx ; jump group opt 'jp',351,377,ixxp ;just (ix) or (iy) ; anything else is undefined opt 'db',000,000,ixbite ; ; Instructions prefixed by CB. cbtab label byte ; rotate/shift group opt 'rlc',000,370,r opt 'rrc',010,370,r opt 'rl',020,370,r opt 'rr',030,370,r opt 'sla',040,370,r opt 'sra',050,370,r opt '?sll?',060,370,r ;would be consistent (works? = SLA?) opt 'srl',070,370,r ; bit set/reset/test group opt 'bit',100,300,br opt 'set',300,300,br opt 'res',200,300,br ; anything else is undefined opt 'db 0CBh,',000,000,ubite ; ; Instructions prefixed by DD CB or FC CB (CB w/HL replaced by IX/IY). ; The really gross thing is that the displacement comes before the final ; opcode byte. Yech. cbixtb label byte ; rotate/shift group opt 'rlc',006,377,cbixm opt 'rrc',016,377,cbixm opt 'rl',026,377,cbixm opt 'rr',036,377,cbixm opt 'sla',046,377,cbixm opt 'sra',056,377,cbixm opt '?sll?',066,377,cbixm ;would make sense, but no opt 'srl',076,377,cbixm ; bit set/reset/test group opt 'bit',106,307,bixm opt 'set',306,307,bixm opt 'res',206,307,bixm ; anything else is undefined op 'db',000,000,cbixbt ; ; Instructions prefixed by ED. edtab label byte ; 8-bit load group op 'ld a,i',127,377,no op 'ld a,r',137,377,no op 'ld i,a',107,377,no op 'ld r,a',117,377,no ; 16-bit load group opt 'ld',113,317,ddmm opt 'ld',103,317,mmdd ; exch, blt, blk srch group op 'ldi',240,377,no op 'ldir',260,377,no op 'ldd',250,377,no op 'lddr',270,377,no op 'cpi',241,377,no op 'cpir',261,377,no op 'cpd',251,377,no op 'cpdr',271,377,no ; G.P. arith/CPU control group op 'neg',104,377,no op 'im 0',106,377,no op 'im 1',126,377,no op 'im 2',136,377,no ; 16-bit arithmetic group op 'adc hl,',112,317,rp op 'sbc hl,',102,317,rp ; rotate/shift group op 'rld',157,377,no op 'rrd',147,377,no ; call/return group op 'reti',115,377,nostop op 'retn',105,377,nostop ; I/O group opt 'in',100,307,rpc op 'ini',242,377,no op 'inir',262,377,no op 'ind',252,377,no op 'indr',272,377,no op 'out (c),',101,307,r1 op 'outi',243,377,no op 'otir',263,377,no op 'outd',253,377,no op 'otdr',273,377,no ; anything else is undefined opt 'db 0EDh,',000,000,ubite ; ; Intel 8051/8052/8751 instructions. ; symtab mcs51,porgh,plabh,1 ;hex orgs/labels, 1-byte opcodes opt 'acall',021,037,acall opt 'add',044,374,asrc51 op "",050,370,asrc51 opt 'addc',064,374,asrc51 op "",070,370,asrc51 opt 'ajmp',001,037,ajmp opt 'anl',124,374,asrc51 op "",130,370,asrc51 op "",122,377,dira51 op "",123,377,diri51 op 'anl c,',202,377,bit51 op 'anl c,/',260,377,bit51 op 'cjne a,',264,376,cjnea opt 'cjne',266,376,cjnei op "",270,370,cjnei op 'clr a',344,377,no op 'clr c',303,377,no opt 'clr',302,377,bit51 op 'cpl a',364,377,no op 'cpl c',263,377,no opt 'cpl',262,377,bit51 op 'da a',324,377,no op 'dec a',024,377,no opt 'dec',024,374,sop51 op "",030,370,sop51 op 'div ab',204,377,no opt 'djnz',325,377,djnzd op "",330,370,djnz51 op 'inc a',010,377,no opt 'inc',004,374,sop51 op "",010,370,sop51 op 'inc dptr',243,377,no opt 'jb',040,377,jb51 opt 'jbc',020,377,jb51 opt 'jc',100,377,cjr op 'jmp @a+dptr',163,377,no opt 'jnb',060,377,jb51 opt 'jnc',120,377,cjr opt 'jnz',160,377,cjr opt 'jz',140,377,cjr opt 'lcall',022,377,lcall opt 'ljmp',002,377,ljmp opt 'mov',350,370,asrc51 op "",345,377,asrc51 op "",346,376,asrc51 op "",164,377,asrc51 op "",370,370,movrna op "",250,370,movrnd op "",170,370,movrni op "",365,377,movda op "",210,370,movdx op "",205,377,movdd op "",206,376,movdx op "",165,377,movdi op "",366,376,movira op "",246,376,movird op "",166,376,moviri op 'mov c,',242,377,bit51 opt 'mov',222,377,movbc op 'mov dptr,#',220,377,mdptr op 'movc a,@a+dptr',223,377,no op 'movc a,@a+pc',203,377,no op 'movx a,',342,376,movxar op 'movx a,@dptr',340,377,no opt 'movx',362,376,movxra op 'movx @dptr,a',360,376,no op 'mul ab',244,376,no op 'nop',000,377,no opt 'orl',104,374,asrc51 op "",110,370,asrc51 op "",102,377,dira51 op "",103,377,diri51 op 'orl c,',162,377,bit51 op 'orl c,/',240,377,bit51 opt 'pop',320,377,stk51 opt 'push',300,377,stk51 op 'ret',042,377,nostop op 'reti',062,377,nostop op 'rl a',043,377,no op 'rlc a',063,377,no op 'rr a',003,377,no op 'rrc a',023,377,no op 'setb c',323,377,no opt 'setb',322,377,bit51 opt 'sjmp',200,377,ujr opt 'subb',224,374,asrc51 op "",230,370,asrc51 op 'swap a',304,377,no opt 'xch',305,377,asrc51 op "",306,376,asrc51 op "",310,370,asrc51 opt 'xchd',326,376,asrc51 opt 'xrl',144,374,asrc51 op "",150,370,asrc51 op "",142,377,dira51 op "",143,377,diri51 opt 'db',000,000,bite ; ; This is definitely way out of hand. ; symtab iapx86,porgh,plabh,1 ;hex orgs/labels, 1-byte opcodes op 'aaa',067,377,no ; op '',0D5h,377,i86pD5 ;AAD=0A ; op '',0D4h,377,i86pD4 ;AAM=0A op '',017,377,pfx0F ;80386-specific prefix op '',046,377,es86 ;ES: op '',056,377,cs86 ;CS: op '',066,377,ss86 ;SS: op '',076,377,ds86 ;DS: op '',144,377,fs86 ;FS: op '',145,377,gs86 ;GS: op '',146,377,opsz86 ;complement default operand size op '',147,377,adsz86 ;complement default address size op '',200,376,rmi86 ;mod-r/m,imm ;;; are next 2 two separate cases??? (haven't looked 'em up) op '',203,377,rmi86 ;mod-r/m,+imm8 ;;;; op '',202,376,rmi86 ;mod-r/m,+imm8 ;;;; right? op '',217,377,rm86 ;POP mod-r/m op '',300,376,rot86 op '',320,374,rot86 op '',366,376,rmi86 ;mod-r/m,imm op '',376,376,rm86 ;mod-r/m op 'aas',077,377,no opt 'adc',020,376,rmr86 op "",022,376,rrm86 op "",024,376,ai86 opt 'add',000,376,rmr86 op "",002,376,rrm86 op "",004,376,ai86 opt 'and',040,376,rmr86 op "",042,376,rrm86 op "",044,376,ai86 opt 'arpl',143,377,rmr86 opt 'bound',142,377,rmr86 opt 'call',232,377,calf86 op "",350,377,caln86 op 'cbw',230,377,no ;CWDE with size pfx op 'clc',370,377,no op 'cld',374,377,no op 'cli',372,377,no op 'cmc',365,377,no opt 'cmp',070,376,rmr86 op "",072,376,rrm86 op "",074,376,ai86 op '',246,376,cmps86 ;CMPS op 'cwd',231,377,no ;CDQ with size prefix op 'daa',047,377,no op 'das',057,377,no opt 'dec',110,370,r86 opt 'enter',310,377,ent86 op 'hlt',364,377,no opt 'inc',100,370,r86 opt 'in',344,376,in86 op "",354,376,indx86 op '',154,376,ins86 ;INS op 'int 3',314,377,no opt 'int',315,377,int86 op 'into',316,377,no op 'iret',317,377,nostop ;;;IRETD with prefix opt 'ja',167,377,cjr opt 'jb',162,377,cjr opt 'jbe',166,377,cjr opt 'jcxz',343,377,jcxz1 ;special case (67h pfx => JECXZ) opt 'je',164,377,cjr opt 'jg',177,377,cjr opt 'jge',175,377,cjr opt 'jl',174,377,cjr opt 'jle',176,377,cjr opt 'jmp',351,377,jmpn86 op "",352,377,jmpf86 op "",353,377,jsh86 opt 'jnb',163,377,cjr opt 'jne',165,377,cjr opt 'jno',161,377,cjr opt 'jns',171,377,cjr opt 'jo',160,377,cjr opt 'jpe',172,377,cjr opt 'jpo',173,377,cjr opt 'js',170,377,cjr op 'lahf',237,377,no opt 'lds',305,377,rrm86 opt 'lea',215,377,rrm86 op 'leave',311,377,no opt 'les',304,377,les86 op 'lock',360,377,no op '',254,376,lods86 opt 'loop',342,377,cjr opt 'loope',341,377,cjr opt 'loopne',340,377,cjr opt 'mov',210,376,rmr86 op "",212,376,rrm86 op "",214,377,rmsg86 op "",216,377,sgrm86 op "",240,376,amof86 op "",242,376,mofa86 op "",260,370,bi86 op "",270,370,wi86 op "",306,376,mrmi86 op '',244,376,movs86 ;MOVS op 'nop',220,377,no opt 'or',010,376,rmr86 op "",012,376,rrm86 op "",014,376,ai86 opt 'out',346,376,out86 op 'out dx,',356,376,oudx86 op '',156,376,outs86 ;OUTS opt 'pop',130,370,r86 op "",007,347,pseg86 op 'popa',141,377,no ;;;POPAD w/pfx op 'popf',235,377,no ;;;POPFD w/pfx opt 'push',120,370,r86 op "",006,347,pseg86 op "",150,377,pimm86 ;PUSH imm16/imm32 op "",152,377,pimm8 ;PUSH +imm8 (16/32-bit looks same!) op 'pusha',140,377,no ;;;PUSHAD w/pfx op 'pushf',234,377,no ;;;PUSHFD w/pfx op '',144,377,repp86 ;REPNC - NEC V-series only op '',145,377,repp86 ;REPC - V-series op '',362,377,repp86 ;REPNE op '',363,377,repp86 ;REPE opt 'ret',302,377,ret86 op 'ret',303,377,nostop opt 'retf',312,377,ret86 op 'retf',313,377,nostop op 'sahf',236,377,no opt 'sbb',030,376,rmr86 op "",032,376,rrm86 op "",034,376,ai86 op '',256,376,scas86 ;SCAS op 'stc',371,377,no op 'std',375,377,no op 'sti',373,377,no op '',252,376,stos86 ;STOS opt 'sub',050,376,rmr86 op "",052,376,rrm86 op "",054,376,ai86 opt 'test',250,376,ai86 op "",204,376,rmr86 op 'wait',233,377,no opt 'xchg',206,376,rmr86 op "",220,370,xchg86 op 'xlat',327,377,xlat86 opt 'xor',060,376,rmr86 op "",062,376,rrm86 op "",064,376,ai86 opt 'db',000,000,bite ; ; Instructions prefixed by 0F. tab0F label byte op 'clts',006,377,no op 'cpuid',242,377,no ;; opt 'imul',257,377,imulaf opt 'ja',207,377,cnjmp opt 'jae',203,377,cnjmp opt 'jb',202,377,cnjmp opt 'jbe',206,377,cnjmp ;opt 'jc',202,377,cnjmp opt 'je',204,377,cnjmp ;opt 'jz',204,377,cnjmp opt 'jg',217,377,cnjmp opt 'jge',215,377,cnjmp opt 'jl',214,377,cnjmp opt 'jle',216,377,cnjmp ;opt 'jna',206,377,cnjmp ;opt 'jnae',202,377,cnjmp opt 'jnb',203,377,cnjmp ;opt 'jnbe',207,377,cnjmp ;opt 'jnc',203,377,cnjmp opt 'jne',205,377,cnjmp ;opt 'jng',216,377,cnjmp ;opt 'jnge',214,377,cnjmp ;opt 'jnl',215,377,cnjmp ;opt 'jnle',217,377,cnjmp opt 'jno',201,377,cnjmp ;opt 'jnp',213,377,cnjmp opt 'jns',211,377,cnjmp ;opt 'jnz',205,377,cnjmp opt 'jo',200,377,cnjmp ;opt 'jp',212,377,cnjmp opt 'jpe',212,377,cnjmp opt 'jpo',213,377,cnjmp opt 'js',210,377,cnjmp ;opt 'jz',204,377,cnjmp opt 'movsx',276,377,zx86 op "",277,377,zx86d opt 'movzx',266,377,zx86 op "",267,377,zx86d op 'pop fs',241,377,no op 'pop gs',251,377,no op 'push fs',240,377,no op 'push gs',250,377,no ; anything else is undefined opt 'db 0Fh,',000,000,ubite ; ; PDP-8 instructions. ; symtab pdp8,porg8,plab8,2 ;octal orgs/labels, 2-byte opcodes opw 'AND',0000,7000,mri opw 'TAD',1000,7000,mri opw 'ISZ',2000,7000,isz opw 'DCA',3000,7000,mri opw 'JMS',4000,7000,jms8 opw 'JMP',5000,7000,jmp8 opw 'IOT',6000,7000,iot ; special operate instruction opcodes opw 'NOP',7000,7777,no ;no op opw 'NOP',7400,7776,no ;(group 2/3 equiv) opw 'STA',7240,7777,no ;set AC to all ones (CLA CMA) opw 'CIA',7041,7777,no ;complement/increment AC (CMA IAC) opw 'LSR',7604,7777,no ;load switch register (CLA OSR) opw 'CAM',7621,7777,no ;clear AC and MQ (CLA MQL) opw 'SWP',7521,7777,no ;swap AC and MQ (MQA MQL) ; generic operate instructions (these will pick up all the rest) opw '',7000,7400,opra ;group 1 operate instructions opw '',7400,7401,oprb ;group 2 operate instructions opw '',7401,7401,oprc ;group 3 operate instructions ; immop dw 1 dup(?) ;8-bit immediate operand if >=0 ; fname db 80h dup(?) ;filename buffer ; code ends ; ibuf segment db 177777 dup(?) ;(filled out to 200000 by PARA alignment) ibuf ends ; obuf segment db 177000 dup(?) obuf ends ; ifdef ??version nowarn res ;goddamn TASM! endif stack segment stack 'stack' ;goddamn TASM needs class dw 400h dup(?) stack ends ; end start