8086 第三部分
8086 第三部分 汇编语言程序设计
目录
1 用汇编语言写的源程序
1.1 汇编的编译过程
//这是汇编固定的返回值
mov ax,4c00h
int 21h
1.2 汇编程序的结构
分号表示注释
2 由源程序到程序运行
2.1 编写汇编代码
- 编写源文件asm:随便你用什么编辑器(txt都行),反正写好一个asm文件就行
- 调用dos环境下的
masm
编译器对文件进行编译,编译后可能产生下述几个文件:
例如:masm hello
简化后续写法: masm hello;
*.obj
:目标文件,就是编译后的最终结果*.LST
:列表文件,是编译器将源程序编译为目标文件的过程中的中间结果*.CRF
:交叉引用文件,同列表文件一样,是比那一过程中的中间结果
- 链接 ----
link xx文件名
,会生成以下文件
*.exe
:可执行文件*.MAP
:映像文件,连接程序将目标文件连接为可执行过程中产生的中间结果*.LIB
:库文件,包含了一些可以调用的子程序,如果我们的程序中调用了某一个库文件的子程序,就需要在连接的时候,将这个库文件和我们的目标文件连接到一起,生成可执行程序。
有时候会提示你no stack segment
这个警告,说明没有栈段,对于简单程序可以不管
- 执行可执行程序
3 debug跟踪程序的执行
- DS:存程序在内存中的段地址,CS:存代码段的起始地址,那么DS-CS之间有个256字节的
PSP
(程序段前缀),DOS用来和程序进行通信。 - 程序加载进内存后,
CX
寄存器存的是代码的长度,单位为字节
补充debug使用参数
P:继续执行(proceed),类似T命令,单步执行指令,但是在遇到子程序or中断的时候,直接执行,然后显示结果
G(Go):运行命令,直接运行直到程序结束or遇到断点,也可以后面接个地址,说明从该地址开始run
4 [...]和(...)的约定
idata
----约定表示为一个常量
比如MOV AX,[idata]
可以代表mov ax,[1] mov ax,[2] mov ax,[3]
5 Loop指令
指令格式:Loop 标号
每次循环执行下述操作
- (CX)=(CX)-1
- 判断
CX
的值,如果不为0,则跳转到标号处执行程序,如果为0则向下执行
示例代码如下
;loop指令示例程序
assume cs:code
code segment
mov ax,2
mov cx,11
s: add ax,ax
loop s
mov ax,4c00h
int 21h
code ends
end
其中CX
中存循环次数,记得定义标号
在汇编程序中,数据不能以字母开头,比如我要给ax寄存器存ffff地址
要写成mov ax,0ffffh
6 段前缀的使用
6.1 一个“异常”的现象
;考虑如下异常代码
assume cs:code
code segment
mov ax,2000h
mov ds,ax
mov a1,[0]
mov b1,[1]
mov c1,[2]
mov d1,[3]
mov ax,4c00h
int 21h
code ends
end
在dos环境中运行debug,-u查看汇编代码,发现原本想让0地址的内容传进ax
,变成了传递数字00进ax
。
解决办法
引入段前缀:在访问内存单元的指令中,显式指明内存单元的段地址,比如DS: CS: SS: ES:
上述问题的对策,在[idata]
前显示的写上段寄存器,比如
mov ax,2000h
mov ds,ax
mov bx,0
mov al,ds:[bx]
mov al,ds:[0] ;注意看第二个参数写法
7 在代码段中使用数据
因为直接操作内存是非常危险的,所以程序中的数据是存在段上的,运行时由操作系统分配空间。
段有:代码段,数据段,栈段
每个段中都能有数据
;比如在定义在代码段上的数据
dw 0123H,0456H,0789H ;定义一个字
db ;定义一个字节
dd ;定义一个双字
标识程序代码开始的地方
8 在代码段中使用栈
要想在程序中使用栈,要通过定义空数据获得
比如下面一道题:
将程序中定义的数据逆序存放。
思路:开辟一个栈空间,将内存中的数据 入栈后再次出栈 存回去就完成了逆序操作
assume cs:codesg
codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h ;存了8个字的数据
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;开辟16个0的空间作为栈空间,因为上面是8个字
start:mov ax,cs
mov ss,ax ;设置栈基地址,和cs同一个地址
mov sp,30h ;设置栈顶指针
;入栈
mov bx,0
mov cx,8 ;8次循环
s: push cs:[bx]
add bx,2 ;每次加2表示 每次移动2字节,也就是一个字
loop s
;出栈
mov bx,0
mov cx,8
s0: pop cs:[bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
当然上述的做法是把数据、代码、栈 放在同一个段中的结果
注意程序默认拥有一个段,所以开始时可以不用给这个寄存器赋值
9 将数据、代码、栈 放入不同的段中
随着项目的增长,我们如果把所有内容都放一个段中,就会容易混乱。 所以我们要分段!!!
同样以上面那题为例
;开头就声明各个段寄存器的关联
assume sc:code,ds:data,ss:stack
;数据段
data segment
dw 0123H,0456H,0789H,0ABCH,0DEFH,OFEDH,0ABCH,0987H
data ends
;栈段
stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends
;代码段
code segment
start:
;初始化各段寄存器
mov ax,stack
mov ss,ax
mov sp,20h
mov ax,data
mov ds,ax
;入栈
;出栈
mov ax,4c00h
int 21h
code ends
end start