Fork me on GitHub

8086汇编:组织程序

前面我们介绍了8086汇编中一些常用的、基本的、重要的概念,现在我们就要把这些东西组装起来。

定义变量

1
var_name db/dw/dd expression

其中的 var_name 就是一个变量,db/dw/dd是伪指令助记符,用于指定字节/字/双字,表达式可以为一个常量,一个字符串等。

1
2
3
4
5
data segment
num dw 0
msg db 'please input a num'
len db $-msg1
data ends

操作数'?'用于保留储存空间,但不存入数据。

1
2
adc db 0, 1, ?, ?, ?
len db ?

操作数字段还可以利用复制操作符 dup 来复制重复元素。

1
2
abc db 0, 1, 3 dup (?)
ssr db 5 dup (0, 1, 2)

程序的开始与结束

1. end 伪指令

1
end label

end 伪指令标识着整个程序的结束,end 后面的操作数指定程序执行的起始地址。所以,一般主程序模板必须 end 后面指定操作数,然后在操作数放到代码段的开头,如:

1
2
3
4
5
6
7
8
9
10
11
assume cs:code, ds:data
data segments
; ...
data ends
code segments
start: ; 程序执行的起始地址
; ...
code ends
end start ; 表明 start 为程序执行的起始地址

2. assume 伪指令

8086存储器是分段的,分了段之后,就必须指明段和段寄存器之间的关系,这由假设语句 assume 实现。

1
assume segment_reg : segment_name [,...]

其中,segment_reg 必须是 cs, ds, ss, es 中的一个,segment_name 则是定义的段名。

注意:assume 伪指令只是指定了某个段分配给某个段寄存器,但它并没有将段地址装入段寄存器,所以在代码段中,还要将段地址装入段寄存器。

3. 定义段

1
2
3
segment_name segment [align_type] [combine_type] [use_type] ['class']
...
segment_name ends

其中,[]代表是可选的,可以缺省的,但各项顺序不能错,一般不需要我们指定

4. 定义过程

1
2
3
4
5
process_name proc [near/far]
.
.
.
process_name endp

proc 后面可以指定 near(默认)—— 在段内被调用,far—— 可以被段外调用。

masm 中,过程含义跟子程序一样,可以通过 call 来调用,过程返回自动调用 ret

子程序

子程序跟过程段类似,通过 call 调用,ret 来返回,重点要注意现场保护参数传递等问题。

1
2
3
4
5
6
7
8
9
10
11
12
name:
; 要用到的寄存器压栈
push ax
push bx
; ...
; 子程序过程...
; 恢复寄存器
pop bx
pop ax
ret

简化段

简化段首先要定义程序的存储模型:

存储模型 说明 适用系统
tiny(微型) 所有数据和代码都放在一个段内,其访问都为NEAR型,整个程序≤64K,并会产生.COM文件 MS-DOS
small(小型) 所有代码在一个64KB的段内,所有数据在另一个64KB的段内(包括数据段,堆栈段和附加段) MS-DOS
Windows
medium(中型) 所有代码>64K时可放在多个代码段中,转移或调用可为FAR型。所有数据限在一个段内,DS可保持不变 MS-DOS
Windows
compact(紧凑型) 所有代码限在一个段内,转移或调用可为NEAR型。数据>64K时,可放在多个段中 MS-DOS
Windows
large(大型) 允许代码段和数据段都可超过64K,被放置在有多个段内,所以数据和代码都是远访问 MS-DOS
Windows
huge(巨型) 单个数据项可以超过64K,其它同large模型 MS-DOS
Windows

说明:small 模型是一般应用程序最常用的一种模型,因为只有一个代码段和一个数据段,所以数据和代码都是近访问的。所以一般将 .model 指定为 small 就可以了。

简化段伪指令:

伪指令 功能 说明
.code [段名] 创建一个代码段 隐含段名为 @code
.data 创建一个数据段 隐含段名为 @data
.stack [大小] 创建一个堆栈段并指定大小 隐含段名为 @stack,并形成sssp的初值

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
.model small
.stack 100h
.data ; 定义数据段
;...
.code ; 定义代码段
main:
mov ax, @data ; 数据段地址
mov ds, ax
; ...
mov ax, 4c00h
int 21h
end main

其他伪操作

1. 基数控制

汇编程序默认的数为十进制数,还可以由以下方式指定基数:

  1. 二进制: 01001000b
  2. 八进制: 123q 或 123o
  3. 十进制: 1234d
  4. 十六进制: 1234h / 0ffffh (第一个为字母时前面加0)

2. '$' 地址计数器

'$'保存了当前汇编指令的偏移地址。

常用用途:

  • 计算字符串长度

    1
    2
    3
    4
    5
    6
    data segment
    str1 db 'hello'
    len1 db $-str1 ; 5
    str2 db 'world hahaha'
    len2 db $-str2 ; 12
    data ends
  • 跳转

    1
    jne $+6 ; 跳转到 jne 指令所在地址 + 6
-------------------------------- 全文完 感谢您的阅读 --------------------------------
「写的那么辛苦,连一块钱都不打赏吗/(ㄒoㄒ)/~~」