8086 asm文件语法-1
Posted 不会写代码的丝丽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8086 asm文件语法-1相关的知识,希望对你有一定的参考价值。
前置的一些编译链接命令
code_seg segment
LABLE1:
mov ax,bx
LABLE2:
mov cx,dx
code_seg ends
end LABLE1
我们在vs插件的dosbox
会有内置编译器。
执行汇编:ml /c xxx.asm
执行链接: link xxxx.obj
多文件编译
第一个文件
;文件名myasm.asm
;导入的第二个文件的头文件,内部包含函数声明
include myasm2.inc
dseg segment
dseg ends
cseg segment
assume cs:cseg,ds:dseg,es:dseg
start:
mov ax,dseg
mov ds,ax
;调用其他文件的变量
mov ax,g_w3
;调用其他文件的函数
invoke MyFun
cseg ends
end start
第二个文件
;myasm2.asm
;必须加上这个关键字否则无法在其他文件使用
public g_w3
asm2_dseg segment
MyFun PROC far
ret
MyFun ENDP
g_w3 word 2h
asm2_dseg ends
end
头文件
;防止重复导入
ifndef MY_ASM2
MY_ASM2 equ 0
MyFun proto far
extern g_w3:word
extern ExitProcess MACRO
endif
编译语法
ml /c MYASM.ASM MYASM2.ASM
link MYASM.obj MYASM.obj
语法
段定义
段名字 segment 段定义开始
segment ends 结束段定义
code_seg segment
code_seg ends
重名段可以重复定义
如下面的代码:
myseg segment
mov ax,bx
myseg ends
myseg segment
mov bx,ax
myseg ends
上面的代码会把段代码合并如下代码:
myseg segment
mov ax,bx
mov bx,ax
myseg ends
入口
end 标签
code_seg segment
Start:
mov ax,1234
mov ax,1234d
mov ax,1234D
mov ax,1234h
mov al,1001b
mov al,23o ;
code_seg ends
end Start
常量定义
关键字 | 说明 | 示例 |
---|---|---|
无 | 十进制 | mov ax,1234 |
D | 十进制 | mov ax,1234d |
B | 二进制 | mov ax,1011b |
O | 八进制 | mov ax,76o |
H | 十六进制 | mov ax,76h |
H | 十六进制 | mov ax,0abh |
举例说明:
code_seg segment
mov ax,1234;十进制
mov ax,1234d;十进制
mov ax,1234D;十进制
mov ax,1234h;十六进制
mov al,1001b;二进制
mov al,23o ;八进制
code_seg ends
end LABLE1
变量定义
关键字 | 意义 |
---|---|
db | 字节 |
dw | 字 |
dd | 双字 |
dq | 8字节 |
dt | 10字节 |
来看一个小例子:
code_seg segment
val db 56h;在代码段定义了一个数据
Start:
mov ax,1234
code_seg ends
end Start
data_seg segment
g_db db 55h;
g_dw dw 6655h;
g_dd dd 77887788h
g_dq dq 1122334455667788h
g_dt dt 11223344556677889900h
g_dw0 dw ?
data_seg ends
code_seg segment
Start:
;获取data_seg地址,方便查看
mov ax,data_seg
code_seg ends
end Start
字符串
字符串可以以'
(单引号)或者"
(双引号)包裹
一般以$
作为字符串的结束
data_seg segment
g_db db "hello world$";
data_seg ends
code_seg segment
Start:
;获取data_seg地址,方便查看
mov ax,data_seg
code_seg ends
end Start
数组
数组定义方式1:
变量名 类型 [数值1][,数值2][,数值3]
data_seg segment
;定义多个变量
g_db db 1h,2h,3h,4h,5h
data_seg ends
code_seg segment
Start:
;获取data_seg地址,方便查看
mov ax,data_seg
code_seg ends
end Start
数组定义方式2:
名字 类型 数量 dup(初始值)[,数量dup(初始值)][,值]
示例代码:
g_ary db 256 dup(0),128 dup(11h)
重复256个0和128个11
另一个示例
data_seg segment
g_ary db 10 dup(0),128 dup(1)
data_seg ends
code_seg segment
Start:
;获取data_seg地址,方便查看
mov ax,data_seg
code_seg ends
end Start
伪指令
部分指令是原始cpu没有的,但是为了简便开发汇编器提供一些简便的封装指令我们称为伪指令
关键字 | 意义 |
---|---|
seg | 取段基址 |
offset | 取段偏移 |
type | 取元素类型大小 |
length | 取元素个数 |
size | 取数据大小(length*type) |
seg
data_seg segment
g_val_1 db 10
g_val_2 db 10
data_seg ends
code_seg segment
Start:
;获取g_val_1变量所在data_seg基地址
mov ax,seg g_val_1
;获取g_val_2变量所在data_seg基地址
mov ax,seg g_val_2
code_seg ends
end Start
offset
data_seg segment
g_val_1 db 10
g_val_2 db 10
data_seg ends
code_seg segment
Start:
mov ax,offset g_val_1
mov ax,offset g_val_2
code_seg ends
end Start
type
其本意返回类型变量的字节大小
data_seg segment
g_val_1 db 10
g_val_2 dw 10
g_val_3 dw 23,46,78
g_val_4 dw 10 dup(1)
data_seg ends
code_seg segment
Start:
mov ax,type g_val_1
mov ax,type g_val_2
mov ax,type g_val_3
mov ax,type g_val_4
code_seg ends
end Start
length
获取数组类型的长度
data_seg segment
g_val_1 db 10
g_val_2 dw 10
g_val_3 dw 23,46,78
g_val_4 dw 10 dup(1)
data_seg ends
code_seg segment
Start:
mov ax,length g_val_1
mov ax,length g_val_2
mov ax,length g_val_3
mov ax,length g_val_4
code_seg ends
end Start
size
assume
我们首先查看如下代码
data_seg segment
g_dw dw 10
data_seg ends
code_seg segment
Start:
mov ax,g_dw
code_seg ends
end Start
一切看起来没有问题,我们编译下
因为编译器不知道g_dw
在哪个段中,因此需要assume指令告诉编译器
data_seg segment
g_dw dw 10
data_seg ends
code_seg segment
Start:
;告诉编译器使用data_seg去寻找变量
assume ds:data_seg
mov ax,g_dw
code_seg ends
end Start
assume
不仅可以告诉编译器ds段基址
,也可以指明ss
段基址。
stack_seg segment
mystask db 256 dup(0cch)
g_dw2 dw 10
stack_seg ends
code_seg segment
Start:
;告诉编译器使用data_seg去寻找变量
assume ss:stack_seg
mov ax,g_dw2
code_seg ends
end Start
stack 标记
;自动根据stack_seg指定ss和sp数值
stack_seg segment stack
mystask db 256 dup(0cch)
stack_seg ends
code_seg segment
Start:
code_seg ends
end Start
DOS下的函数调用
操作系统提供了一些内置行为,通过中断实现,具体传给中断参数可通过上述表格查询。
比如我们需要像屏幕输出信息
上面的意思:传入AH=02h,dl传入输出到屏幕的字符
code_seg segment
Start:
;输出A到屏幕
mov dl,"A"
;调用输出到屏幕指令
mov ah,2
;陷入中断
int 21h
;此处传参数表示结束程序并返回0
mov ax,4c00h
int 21h
code_seg ends
end Start
movsb
将si
(使用ds
作为段基址)地址的内容拷贝到di
(使用es
作为段基址)地址,调用后si
和di
分别自增1
data_seg segment
g_szSrc db "hello world$"
g_szDst db 255 dup(00)
data_seg ends
code segment
START:
assume ds:data_seg
mov ax,data_seg
;放入ds到正确位置
mov ds,ax
mov ax,ds
;同上
mov es,ax
lea si,g_szSrc
lea di,g_szDst
;拷贝h字符到es:di
;拷贝后di+=1,si+=1
movsb
;拷贝e字符到es:di
;拷贝后di+=1,si+=1
movsb
;拷贝l字符到es:di
;拷贝后di+=1,si+=1
movsb
;拷贝l字符到es:di
;拷贝后di+=1,si+=1
movsb
;拷贝o字符到es:di
;拷贝后di+=1,si+=1
movsb
code ends
end START
首先我们运行第一个movsb
之前的内存状态图:
执行第一个movsb指令后我们查看对应的寄存器di
和si
后续步骤也是相同不在列出.
你可能看到上面的命令中si
和di
自增1,但如果你想每次调用movsb
后自动减一也是允许的。
si
和di
执行加一或者减一是由DF
标志位影响的.
在8086
中提供了两个相关指令让我们直接修改DF
标志位
指令 | 功能 |
---|---|
STD (set df) | 让si和di自动减一 |
CLD(clear df) | 让si和di自动加一 |
data_seg segment
g_szSrc db "hello world$"
g_szDst db 255 dup(00)
data_seg ends
code segment
START:
assume ds:data_seg
mov ax,data_seg
;放入ds到正确位置
mov ds,ax
mov ax,ds
;同上
mov es,ax
lea si,g_szSrc
lea di,g_szDst
;修改df标志位
std
;拷贝h字符到es:di
;拷贝后di+=1,si+=1
movsb
;拷贝e字符到es:di
;拷贝后di+=1,si+=1
movsb
;拷贝l字符到es:di
;拷贝后di+=1,si+=1
movsb
;拷贝l字符到es:di
;拷贝后di+=1,si+=1
movsb
;拷贝o字符到es:di
;拷贝后di+=1,si+=1
movsb
code ends
end START
movsw
与movsb 功能相似,但是每次拷贝一个字,si和di加2或者减2
movs
其实本质会转化为movsw或movsb
movs byte ptr [di], byte ptr [si] ; 实际转化为movsb
movs word ptr [di], word ptr [si] ; 实际转化为movsw
stosb
功能类似stosw,di自增1
stosw
将ax存储的数值拷贝ds:di ,之后di+=2
loadsb
类似loadsw ,但是si+=1
loadsw
将si地址的内容读入ax,然后si+=2
data_seg segment
g_szSrc db "hello world$"
g_szDst db 255 dup(00)
data_seg ends
code segment
START:
assume ds:data_seg
mov ax,data_seg
;放入ds到正确位置
mov ds,ax
lodsw
lodsw
lodsw
lodsw
code ends
end START
cmpsb
与cmpsw大致相同只不过比较的是一个字节 ,运行后si和第自增1
cmpsw
si 地址内从减去di地址内容,不存储结果 但影响标志位,运行后si和di自增2
scasb
与scasw大致相同
scasw
ax减去di地址内容,不存储结果,但影响标志位,运行后di+=2
data_seg segment
g_szSrc db "abcde$"
g_szDst db 255 dup(00)
data_seg ends
code segment
START:
assume ds:data_seg
mov ax,data_seg
;放入ds到正确位置
mov es,ax
xor ax,ax
;由于小端模式注意反着写
mov ax,'ba'
scasw
mov ax,'dc'
scasw
mov ax,'e$'
scasw
code ends
end START
重复前缀
一些指令前缀可以让指令无限的运行直到满足某一条件下才停止
rep
一般配合movs,stos,loads
指令一起使用,重复条件cx!=0
我们看下如下代码,拷贝ds中的字符串到es中,每次执行rep指令时cx都会减少1
data_seg segment
g_szSrc db "abcde$"
g_szDst db 255 dup(00)
data_seg ends
code segment
START:
assume ds:data_seg
mov ax,data_seg
;放入ds到正确位置
mov ds,ax
xor ax,ax
;计算出重复的次数
mov cx,g_szDst-g_szSrc
rep movsb
code ends
end START
指令执行前:
cx=6 因此会执行6次 movsb指令
目标内存:
原内存
第一次执行后:
执行6次后:
repz/repe
配合指令:cmps,scas
重复条件:cx!=0
且zf==0
我们看一个例子
data_seg segment
g_szSrc db "abcde$"
g_szDst db 255 dup(00)
data_seg ends
code segment
START:
assume ds:data_seg
mov ax,data_seg
mov ds,ax
mov es,ax
xor ax,ax
;得到字符串的长度
mov cx,g_szDst-g_szSrc
;
repz cmpsb
code ends
end START
repnz/repne
配合指令:cmps,scas
cx!=0
且 zf ==1
data_seg segment
g_szSrc db "abcde$"
g_szDst db 255 dup(00)
data_seg ends
code segment
START:
assume ds:data_seg
mov ax,data_seg
;放入ds到正确位置
mov ds,ax
mov es,ax
xor ax,ax
mov cx,g_szDst-g_szSrc
;由于字符串相等每次计算zf都会为0所以第一次比较就会结束
repnz cmpsb
code ends
end START
以上是关于8086 asm文件语法-1的主要内容,如果未能解决你的问题,请参考以下文章