-
段的定义,假设与引用
-
段的定义
-
格式
-
segment_name segment [align] [combine] ['class'] statements segment_name ends ;关键字segment表示段定义的开始,关键字ends表示段定义的结束(end of segment),segment_name表示段名,任何一个段都必须有段名,statements表示汇编语言的语句 ;align表示对齐方式,它是以下5个关键字的其中一个:byte word dword para page ;这些关键字用来规定所定义的段以字节,字,双字,节,页为边界,节是16字节,页为256字节,如果在段定义时省略对齐方式,默认为节,一般情况下不必指定对齐方式 ;combine表示合并类型,它是指以下5个关键词的其中一个 ;public stack common memory at ;public一般用于代码段或数据段的定义,凡是段名相同且合并类型为public的段在连接时合并成一个段,stack用于堆栈段定义,凡是段名相同且合并类型为stack的段在连接时合并为一个堆栈段,并在程序装入内存准备运行时,ss自动初始化为该堆栈段的段地址,sp自动初始化为该段末尾字节的偏移地址+1,一般情况下不需要写,如果在程序中定义了堆栈段,则一般应指定该段的合并类型为stack ;'class'表示类别名,它的内容固定不变,可以随便取,作用是可以使连接程序在连接时把该段与其他具有相同类别名的段放在一起,一般情况下不用指定 ;一般情况下就这样定义段就好 segment_name segment statements segment_name ends ;堆栈段一般这样定义 stk segment stack db 100h dup(0);定义100h个字节,每个字节的值都为0 stk ends
-
-
-
段的假设
-
汇编指令assume可用来建立段寄存器与段的对应关系
-
格式
-
assume segreg:segment_name ;segreg表示段寄存器(cs,ds,es,ss)其中一个
-
-
注意,assume建立起的段寄存器与段的对应关系仅是帮助汇编编译程序把段地址替换成段寄存器,但并不会对段寄存器赋值即不能保证段寄存器等于对应的段地址
-
在程序开始运行时,除了cs与ss这两个寄存器会被操作系统自动赋值外,ds与es并不等于assume指定的段地址
-
-
段的引用
-
用段名引用段地址
-
可以用段名来代替段地址
-
用seg运算符引用段地址
-
格式
-
seg variablename;变量名,表示变量variablename所在段的段地址 seg labelname;标号名,标号所在段的地址
-
-
-
-
-
-
程序的结束
-
源程序的结束
-
用汇编指令end结束
-
格式:
-
end labelname;end表示源程序到此结束,labelname是代码段中某个标号的名字,它指令程序从该标号开始运行,如果在end后省略labelname,则程序从代码段的第一条指令开始执行
-
-
-
-
程序段前缀
-
PSP(program segment prefix),为一段长度为100h字节的内存
-
DOS在运行任何一个可执行程序时,首先在内存中为该程序分配一个PSP,接下去DOS读取该程序文件的内容并装入到PSP之后的内存中,最后DOS把DS与ES都设置成PSP段址,把SS与SP设置成程序的堆栈段地址与堆栈段最后一个字节偏移地址+1,CS设置为该程序代码段地址,IP设置为源程序中end指定的标号的偏移地址,程序中CS:IP开始运行
-
偏移 长度(byte) 内容 0000h 2 int 20h(0cdh,20h) 0016h 2 父程序PSP段地址 002ch 2 环境块段地址 0080h 1 命令行参数长度 0081h 不定(最多7Fh字节) 命令行参数
-
-
程序的终止
-
汇编源程序中的end只是表示源程序的结束,它只是一个汇编指示指令。在程序编译之后,end将会消失,不会转化成任何代码
-
真正要想让程序终止通常使用DOS的4ch号功能调用
-
mov ah,4ch mov al,return_code int 21h ;其中al中的返回码用来传递本程序的返回信息给父程序(当前正在运行的程序的调用者)
-
除了DOS的4c号功能外,int 20h中断调用以及DOS的00h号功能也可以终止程序运行,但是这两个调用要求代码段寄存器cs的值等于PSP段地址,如果在调用这两个功能时CS不等于PSP段地址,则会造成死机
-
还有一种终止方法
-
code segments assume cs:code push ds;在程序一开始就把ds压入堆栈,实际上保存了PSP的段地址 mov ax,0 push ax .... retf;从堆栈中弹出0,ds,这个时候会把IP赋值为0,cs赋值为ds也就是PSP段地址,相当于执行了jmp psp:0000,那里存放着一个int 20h指令,于是程序终止执行 code ends
-
-
-
-
汇编语言的语句
-
汇编语句的格式
-
name mnemonic operand ;comment ;name称为名字项,主要指变量名和标号名,也可以是段名,过程名等,名字项在汇编语言中并不是必须的,大多数语句不需要 ;mnemonic为助记,主要指8086指令(如mov,add,jmp),也可以是汇编指示指令(assume,end,segment)和伪指令(db,dw) ;operand为操作数,为助记的参数 ;comment为注释,总是以分号开始
-
-
常数与常数表达式
-
汇编支持的常数包括整型常数,字符常数,字符串常数
-
整型常数
-
8位,16位,32位整型常数,可以是正数负数非符号数,可以用十进制,二进制,八进制,十六进制表示
-
10,-10;十进制,无后缀 1011B;二进制,B为后缀 177Q;八进制,Q为后缀 3Fh,0FFh;十六进制,h为后缀
-
-
字符常数
- 用单引号或双引号括起来的单个字符, ‘A’,“A”,在数值上等于该字符的ASCII码
-
字符串常数
- 用单引号或双引号括起来的一串字符,‘ABC’,"ABC
- 注意,汇编中的字符串常数包含的字符是引号中的字符,没有\0来结束字符串
-
常数表达式
-
汇编支持的运算符
-
运算符 格式 含义 + +表达式 正 - -表达式 负 + 表达式+表达式 加 - 表达式-表达式 减 * 表达式*表达式 乘 / 表达式/表达式 除 MOD 表达式MOD 表达式 求余 SHR 表达式1 SHR 表达式2 右移表达式2位 SHL 表达式1 SHL 表达式2 左移 NOT NOT 表达式 求反 AND 表达式AND 表达式 与 OR 表达式 OR表达式 或 XOR 类似上 异或 SEG SEG 变量名或标号名 段地址 OFFSET 同上 偏移地址
-
-
符号常数
-
符号常数是指以符号形式表示的常数,用EQU ,=定义
-
symbol equ expression symbol = expression;symbol为符号名,expression为表达式 ;=的操作数只能是数值类型或字符类型的常数或常数表达式,同一个符号允许用=重新定义 ;equ的操作数除了数值类型或字符类型的常熟或常数表达式外,还允许是字符串,甚至可以是一个汇编语句,但不可以重新定义同一个符号 char = 'A' exitfun equ <mov ah,4ch> dosint equ <int 21h> code segment assume cs:code main: mov ah,2 mov dl,char;相当于 mov dl,'A' dosint ;int 21h char = 'B';重新定义 mov ah,2 mov dl,char; mov dl,'B' dosint exit fun ;mov ah,4ch dosint code ends end main
-
-
-
变量与标号
-
变量名与标号名
- 变量名不能以数字开头,$与?不能单独用作变量名,变量名包含的字符个数最多为31个,在缺省情况下不区分变量名的大小写,不能重复定义,不能是关键字,不可以含有空格
-
变量的定义
-
变量名 db|dw|dd 初始值 ;db(define byte) ;dw (define word) ;dd(define double word) ;ex: x db 3Fh y db 1,2,3 ;相当于定义了y[3] z db 'ABC',0Dh,0Ah,'$';z[6] abc dw 1234h,5678h;abc(2) ;dup指令,重复相同的初始值 abc db 100 dup(0) ;abc(100),每个初始化为0 ;dup括号后的值可以有多项 x db 3 dup(1,2) ;定义一个字节类型数组x,共3*2个元素,分别为(1,2,1,2,1,2) ;还可以嵌套 y db 2 dup('A',3 dup('B'),'C') ;相当于'ABBBCABBBC'
-
-
标号的定义
-
标号的作用是用作跳转(jmp类)或调用(call)的目标
-
标号名:;最简单的定义方式,相当于下面的near 标号名 label near|far|byte|word|dword ;near只包含品偏移地址,far包含段地址和偏移地址 ;如果后面跟的是byte,word,dword实际上定义的是一个变量 abc label byte db 7Fh ;第一句定义了abc为字节类型变量,但没有分配内存空间,这种情况下,abc的地址就等于后面那个变量的地址 相当于 abc db 7Fh ;这样作用是可以同时定义字节或字变量 www label word abc db 12h,34h ;www相当于就是3412h bbb label byte xyz dw 5678h ;bbb就是78h
-
-
变量引用
- 代码段中用var或[var]都可作为8086指令的操作数表示变量的值
-
变量强制类型转换
-
word ptr [var] byte ptr dword ptr near ptr far ptr
-
-
位置计数器
-
汇编编译程序在编译源程序时,会使用称为位置计数器的变量来记录当前段内的偏移地址,当遇到一个段定义开始时,位置计数器的值就被赋值为0,然后每编译到一个语句时,位置计数器的值就会自动加上所占的字节数,在源程序中,可以用$取得当前位置计数器的值,利用位置计数器的值,就可以得到一个字符串的长度
-
data segment poem db "abcsdefsdq" len db $-offset poem;这样就获得了在这个指令之前也就是poem的长度 data ends code segment assume cs :code main: mov ah,4ch int 21h code ends end main
-
-
-
- 可以看到len果然是字符的长度10个 - 设定或修改位置计数器的值 - org - ```assembly data segment org 1000h;设定$为1000h,这样abc的偏移地址就是1000h而不是0000h abc db 12h,34h org $+100h xyz dw 5678h data ends code segment assume cs :code main: mov ah,4ch int 21h code ends end main ```
-
可以看到,本来应该紧跟ss:0的数据内容不见了,被0填充了
可以看到,果然在1000h处出现了12,34,说明$其实有点像堆栈的指针,她指向了下一个内容放置的偏移地址,如果我们修改了,则不会按照既定的连续放置,那我们也可以估计,xyz的偏移地址应该是1000+100+2=1102
所以如果不是特别需要,还请别随便修改$的地址,同时也可以看到org指令是不占内存空间的,应该是在编译预处理的时候起了作用
- 标号的引用
- 如果lab是标号名,则 lab或offset lab均可表示该标号的偏移地址
内容不见了,被0填充了
- 如果lab是标号名,则 lab或offset lab均可表示该标号的偏移地址
[外链图片转存中…(img-SCj8ntcD-1619056079924)]
可以看到,果然在1000h处出现了12,34,说明$其实有点像堆栈的指针,她指向了下一个内容放置的偏移地址,如果我们修改了,则不会按照既定的连续放置,那我们也可以估计,xyz的偏移地址应该是1000+100+2=1102
[外链图片转存中…(img-5xj3BBHD-1619056079925)]
所以如果不是特别需要,还请别随便修改$的地址,同时也可以看到org指令是不占内存空间的,应该是在编译预处理的时候起了作用
- 标号的引用
- 如果lab是标号名,则 lab或offset lab均可表示该标号的偏移地址