📄 asm.asm
字号:
;
; GRDB
;
; Copyright(c) LADsoft
;
; David Lindauer, camille@bluegrass.net
;
;
; ASM.ASM
;
; Function: Assembler parser
;
; not very efficient, but, fast enough!
;
;MASM MODE
.MODEL SMALL
.386
include iasm.inc
include iopcodes.inc
include eaoperan.inc
include eopcodes.inc
include eopcom.inc
include eprints.inc
include einput.inc
include emtrap.inc
include edispatc.inc
include eoperand.inc
include eoptions.inc
include ehistory.inc
PUBLIC asm
PUBLIC arg1
PUBLIC arg2
PUBLIC arg3
PUBLIC RepPfxBitmap
PUBLIC lastofs
PUBLIC lastbyte
PUBLIC PrefixBitmapWord
.data
mtoofew db "Not enough operands",0
lastbyte db 0 ;last char of valid mnemonic
lastofs dw 0 ;current disassembly offset
lastseg dw 0 ;and current segment
say_repne db "repne",0 ;prefix strings to look for
say_repe db "repe",0
say_rep db "rep",0
say_lock db "lock",0
say_word db "word",0 ;opcode size overrides
say_byte db "byte",0
say_ptr db "ptr"
arg1 asmop <> ;three args
arg2 asmop <>
arg3 asmop <>
arg4 asmop <> ;temp for base-mode register gathering
AsmbldInstrsBuf db 16 DUP (?) ;temporary to hold assembled instructions
OpSizeTable db 16 DUP (?) ;sizes returned by AOP routines
OpSizeTblIndex dw 0 ;pointer into OpSizeTable
RepPfxBitmap db ? ;bitmap of which rep prefix found
EnteredMnemonic db 16 DUP (?) ;bucket to hold mnemonic as typed
;DefaultSeg appears to hold an index into SegmentPfxBytes.
DefaultSeg db 0 ;current default seg (DS or SS)
SegmentPfxBytes db 26h,2eh,36h,3eh,64h,65h ;table of seg prefixes
SEGPFXLISTSIZE equ $-SegmentPfxBytes
OverridePfxList db "asosgsfsdssscses" ;list of prefixes with colon
PrefixBitmapWord dw ? ;bitmap of which prefix found
.CODE
;
; interpreter stub to get params
; INPUT: DS:SI points at the user input line, just past the command char.
; I am assuming that someone issued the A command. This can be by itself,
; or it can be followed by some addresses
; The syntax is A [[segment:]offset]
; where
; [segment:] can be any segment register, or it can be any hex
; number of up to 4 digits.
; [offset] can be any hex number of up to 8 digits
; OUTPUT: lastseg and lastofs set up to assemble at if user provided a legal
; address. Segment is also in FS
; CY if invalid address
; code address saved in case an unqualified 'A' command later
;
;
asm PROC
call WadeSpace ; see if address given
jnz readaddr ; yep, get it (not CR past spaces)
mov ax,lastseg ; else see if any last addr
or ax,lastofs
jz usehere ;if not, go from where we are
mov dx,lastseg ;else, get prior assemble address
movzx ebx,lastofs
jmp gotaddr ;no need to set up our own
usehere:
mov ebx,RegdumpEIP ;else load our CS:IP
mov dx,RegdumpCS
jmp gotaddr ;and use that
readaddr:
call ReadAddress ; read address from input line
jc aserrm ; out on err
call WadeSpace ; make sure nothing else
jnz aserrm ; NZ means we didn't hit a CR
call defCS ; default CS
gotaddr:
mov esi,ebx ; load address
mov fs,dx ;setup segreg
and esi,0ffffh ;force to 16-bit offset
mov lastseg,fs ;save current segment/selector
mov lastofs,si ;and current 16-bit offset
call histoff
call doasm ; do assembly
call histon
clc ; exit
ret
aserrm:
stc ; exit with err
ret
asm ENDP
;
; prompt for a line, parse and assemble
;
; main assembler
;
; OK, the user pressed A <CR>. Here we solicit each entry, one line
; at a time, and convert the valid ones to opcodes. Invalid entries are
; complained about, and we return to the top of this routine to get
; another attempt.
; INPUT: address to assemble at is in FS (and lastseg): lastofs
; OUTPUT: Assembled code placed at target memory location and code ptr
; incremented
; PROCESSING:
; 1) Display the segment:offset for the next line
; 2) Get an instruction from the user
; If CR only, we are done
; 3) If error, report and goto step 1, else assemble the instruction
; into a temporary assembly buffer
; 4) Add any prefixes or overrides to the buffer as required
; 5) Copy the buffer to the target memory location
; 6) Return to step 1 until done.
; NOTES:
; 1) GetInputLine places the user input into InputBuffer, an 80-char
; buffer. It handles backspacing. It returns with SI pointing
; to the beginning of the edited input, which may not make
; sense
; 2) getCode points DI at a 16-char bucket I call EnteredMnemonic,
; and returns the first delineated string
;
doasm PROC
call crlf ;move to new line
mov ax,fs ;get segment address
call printword ;paint that
mov dl,':' ;and a colon
call putchar
mov ax,lastofs ;get last offset
call printword ;print it
call printspace ;and a couple of spaces
call printspace
call GetInputLine ; get input line
call WadeSpace ; if nothing there we are done
jz doasx ;since we hit the CR
sub al,al ;clear out AL
call setsize ;put 0 into these 3 fields
mov PrefixBitmapWord,0 ;say no prefix found
mov DefaultSeg,3
call getcode ; get the opcode
jc badop
mov di,offset arg1 ; get first arg
call parsearg
jc badarg
mov di,offset arg2 ; get second arg
call parsearg
jc badarg
mov di,offset arg3 ; get third arg
call parsearg
jc badarg
jz assemble ;if no more, to assemble it
manyerr:
Call printAlignedErrorMsg ;else bitch about too many operands
db "Too many operands",0
jmp doasm
badarg:
call printAlignedErrorMsg ;complain about invalid operand
db "Unknown operand",0
jmp doasm
badop:
call printAlignedErrorMsg ;complain about invalid opcode
db "Unknown opcode",0
jmp doasm
assemble:
call validops ;size check and set
jc doasm ;size mismatch, ignore
mov si,offset mnemonicMatchAddrTable ;find table we built
mov cx,[mnemonicMatchCount] ;number of valid table entries
mov OpSizeTblIndex,0 ;init index to top of table
;mnemonicMatchAddrTable contains a list of up to 10h addresses. Each address
;points to a structure. Each structure contains (among other things) a
;pointer to a string for this mnemonic, the base opcode for the
;mnemonic, and an index to the routine used to assemble the code
; This loop is examining every valid element of the table we build to
;isolate those instances of this mnemonic that are valid in this case (that
;is, valid for size, addressing mode, etc).
assl:
push cx ;save count
push si ;save table address
mov si,[si] ;find structure address
mov di,offset AsmbldInstrsBuf ;where to put binary
call oneasm ;dispatch for this operand
; copies opcode for this instruction
; into the assembled code buffer
mov cl,0 ;assume we didn't do anything
jc assx2 ;and if true, cl is right
mov cx,di ;else get offset we ended up at
sub cx,offset AsmbldInstrsBuf ;minus starting offset
assx2:
mov bx,OpSizeTblIndex ;pointer into opsize table
mov [bx+OpSizeTable],cl ;save how many we did
inc OpSizeTblIndex ;bump to next location
pop si ;restore mnemonic ptr table address
pop cx ;and how many to examine
add si,2 ;point to next possibility
loop assl ;do them all
movzx ecx,byte ptr OpSizeTblIndex ;see if we did anything
jcxz nomatch ;nope, not found in table
sub bx,bx ;else init for next loop
;at this point we have at least one match between the disassembly
;strucutre and the mneominc/addressmode / size data we accrued earlier.
;We are going to search the matched entries for the one with the
;smallest possible byte sequence
szllp:
or bh,bh ;if bh is zero
jz szlg ;skip this stuff
cmp bh,[ecx+OpSizeTable-1] ;see if high byte of table value
;is greater than bh
jb sslc ;go here if it is
test byte ptr [ecx+OpSizeTable-1],0FFh ;see if table value is 0
jz sslc ;and if it is, same place
;OK, BH is keeping track of the length of the sequence, and BL is keeping
;track of the table offset to that sequence. We get here in case a) this
;is the only valid sequence we found, or b) this sequence is shorter than
;the prior shortest sequence we found.
szlg:
mov bh,[ecx+OpSizeTable-1] ;get high byte of tbl element->bh
mov bl,cl ;track the index to the shortest
sslc:
loop szllp ;do for as many valid match instances
;as we found
ssgot:
or bh,bh ;did we find anything?
jz nomatch ;no, we did not
sub bh,bh ;convert BL into a word index
shl bx,1 ;into the match table
mov si,[bx+mnemonicMatchAddrTable-2] ;get an address
mov di,offset AsmbldInstrsBuf ;point to buffer
call oneasm ;build our favorite sequence
mov cx,di ;DI is new buffer offset
mov si,offset AsmbldInstrsBuf ;switch top to SI
sub cx,si ;get buffer bytecount
mov di,lastofs ;point to last assemble offset
push es ;save ES
push fs ;mov FS to ES
pop es
push cx ;save count
;Now DI points to the actual location in memory where we want to put our
;assembled buffer. Since prefixes are not in the assembly buffer, we first
;stick in as many prefixes as we found, then paste the remainder of the
;buffer beyond them.
call InsertPrefixes
pop cx ;restore count
jc pfxerr ;if carry, too many prefixes
rep movsb ;copy to memory location for asm
mov lastofs,di ;update assemble in mem location
doasmn:
pop es ;restore old ES
jmp doasm ;and get next instruction
pfxerr:
pop es ;rectify stack
call printAlignedErrorMsg
db "Too many prefixes",0
jmp doasm
nomatch:
call printAlignedErrorMsg
db "Invalid opcode/operand combo",0
jmp doasm
doasx:
ret
doasm ENDP
;
;this routine is the shell which assembles an instruction based
;on the opcode/operand/size data
;
;INPUT: SI points to the opcode structure
; DI points to a temp buffer into which we do the assembly
;OUTPUT: BUFFER FILLED. This routine does the 0F prefix but none of the
; other prefixes
oneasm PROC
test [si+OPCODE.FLAGS],prefix0F ;0F prefix on this guy?
jz no386p ;nope
mov al,0fh ;else stash the 0F
stosb ;into the buffer
no386p:
;The syntax here works, but it can be clarified a little. opcode.operands
;was never explicitly written to. Instead, we build a table of pointers
;into a table of opcode structures. SI contains one of the pointers out
;of that table. Maybe it's just because I'm more used to it, but I prefer
;the MASM syntax to indicate this:
; mov al,(opcode ptr [si]).operands
mov al,[si+opcode.operands] ;get addressing mode
push 0 ;TableDispatch calls this subkey
call TableDispatch ;and dispatch it
dw 58 ;length of table
dw AOP0, AOP1, AOP2, AOP3, AOP4, AOP5, AOP6, AOP7
dw AOP8, AOP9, AOP10, AOP11, AOP12, AOP13, AOP14, AOP15
dw AOP16, AOP17, AOP18, AOP19, AOP20, AOP21, AOP22, AOP23
dw AOP24, AOP25, AOP26, AOP27, AOP28, AOP29, AOP30, AOP31
dw AOP32, AOP33, AOP34, AOP35, AOP36, AOP37, AOP38, AOP39
dw AOP40, AOP41, AOP42, AOP43, AOP44, AOP45, AOP46, AOP47
dw AOP48, aop49, aop50, AOP51, AOP52, AOP53, AOP54, AOP55
dw AOP56, AOP57, AOP58
ret
oneasm ENDP
;
; inserts prefixes into buffer
;
;INPUT: ES:DI points to buffer
;OUTPUT: all prefixes inserted and DI updated
;
;
; first comes th 386 prefixes
InsertPrefixes PROC
sub dx,dx
test [PrefixBitmapWord],AS_OPSIZE ;see if 66 override
jz nopsiz
mov al,66h
stosb
nopsiz:
test [PrefixBitmapWord],AS_ADDRSIZE ;see if 67 override
jz naddrsiz
mov al,67h
stosb
naddrsiz:
;
; now we do segment overrid prefixes by scanning the prefix bitmap
; word
sub dx,dx ;start with no override byte count
mov dh,byte ptr [PrefixBitmapWord] ;get prefix bitmap lo byte
mov bx,offset SegmentPfxBytes ;list of prefix bytes
mov cx,SEGPFXLISTSIZE ;there are 6 of these
sl2:
shr dh,1 ;see if this one required
jnc nsl2 ; no, skip
mov al,[bx] ; else load the prefix from the table
stosb ; save it
inc dx ; increment prefix count
nsl2:
inc bx ; point to next prefix
loop sl2 ; next prefix
cmp dl,2 ;count of segment prefixes added
jb lpnerr ;can't exceed 1
stc
ret
lpnerr:
;
; now we do the remainder of the 8086 repeate and lock prefixes
;
test RepPfxBitmap,AF_LOCK ;see if lock set
jz nlock ;nope, no lock
mov al,0f0h ;else stash lock prefix
stosb
nlock:
test RepPfxBitmap,AF_REPNE OR AF_REP ;see if REPNE set
jz nrepne ; nope, no REPNE
mov al,0f2h ;stick in repne prefix
stosb
nrepne:
test RepPfxBitmap,AF_REPE ; See if REPE
jz nrepe ; noe, no repe
mov al,0f3h ;stick in repe prefix
stosb
nrepe:
clc
ret
InsertPrefixes ENDP
;
; routine displays error if operands are mismatched
;
; INPUT: none
; OUTPUT: message displayed
operr PROC
call printAlignedErrorMsg
db "Unusable operand combination",0
stc
ret
operr ENDP
;
; check for size mismatches and get a size.
; INPUT: SI points to user's input buffer/
; OK, a little bit of clarification has happened on this one, thankfully.
; Turns out that opcodes fall into several categories with respect to this
; routine. In general, for any opcode with more than one operand, all
; subsequent operands must match one another in size except for the
; exceptions. The exceptions to be permitted are:
; 1) movzx and movsx, which by definition have mismatched operands
; 2) In and Out instructions, where DX holds the port, and we can
; read or write any size operand through that port
; 3) SHR,SAR,SHL,SAL,RCR,RCL CL, since any size operand can be shifted
; by CL bits
; 4) Immediate operands, which can be smaller but not larger than
; their targets
; 5) Memory, if sizes to any memory mode are given they must match
; the rest of the arguments.
;
validops PROC
cmp lastbyte,"x" ;if movzx or movsx
je vox ;then this is OK
;OK, the logic here is really hosed. What it is supposed to say is this:
;IF any operand is provided
; IF more than one operand
; IF all operands are not equal in size
; IF not an allowed exception
; THEN error
; ELSE OK
; ELSE OK
; ELSE OK
;ELSE OK
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -