汇编语言学习(四)——汇编语言程序
目录
一、伪指令
二、程序返回
三、汇编语言结构
四、[...]的规定和(...)的约定
五、Loop指令
一、伪指令
在汇编语言源程序中,包含两种指令,一种时汇编指令,一种是伪指令。汇编指令是有对应的机器码的指令,可以被编译为机器指令,最终为cpu所执行。伪指令没有对应的机器指令,最终不被cpu所执行。
assume cs:codesgcodesg segmentmov ax, 0123Hmow bx, 0456Hadd ax,bxmov ax,4c00Hint 21H
codesg ends
end
(1) segment 和 ends
段名 segment
段体内容
段名 ends
segemt用于定义一个逻辑段的开始, ends用于定义一个逻辑段的结束。它告诉汇编器程序的某一部分将被组织为一个段,该段可以是代码段、数据段或堆栈段。
(2)end
end是一个汇编程序的结束标记。若程序结尾处不加end,编译器在编译程序时,无法知道程序在何处结束。
(3)assume
assume 段寄存器: 段名
assume 伪指令用于将特定用途的段与相关的段寄存器关联起来。它告诉汇编器在访问段内变量时,应该使用哪个段寄存器。
二、程序返回
mov ax, 4c00h
int 21h
这两条指令的作用是安全退出程序,并返回到 DOS 的控制台。这是大多数汇编语言程序的结尾部分,用于确保程序在执行完毕后不会继续运行或占用资源。
三、汇编语言结构
(1)在debug中直接编写汇编程序
- 可以直接在命令行中输入汇编指令,无需编写复杂的源文件格式。这种方式适合功能简单、短小精悍的程序;
- 在debug中直接执行、调试和修改程序,无需经过编译和链接过程,调试效率高;
(2)单独编写源文件后通过编译器生成可执行文件
- 源文件可以使用文本编辑器编写,便于组织和管理,适合编写复杂、大型程序;
- 编译器可以对源代码进行优化,生成更高效的机器码;
- 编译器可以识别并处理伪指令,帮助程序员组织程序结构;
- 通过编译器生成的可执行文件可以在不同平台上运行,只要目标平台支持相应的编译器和链接器;
特性 | 在DEBUG中直接编写 | 单独编写源文件后编译 |
---|---|---|
适用场景 | 简单、短小程序 | 复杂、大型程序 |
是否需要伪指令 | 否 | 是 |
调试方式 | 直接调试 | 需要编译、链接后调试 |
代码效率 | 低 | 高 |
环境依赖 | DOS环境 | 现代操作系统 |
学习成本 | 低 | 高 |
适合开发阶段 | 初期快速验证 | 中后期结构化开发 |
四、[...]的规定和(...)的约定
(1)[...]——表示一个内存单元
一个完整的内存单元,需要两种信息:a.内存单元的地址(段地址:偏移地址),b.内存单元的长度(或者字节);
内存单元的长度可以由具体指令中的其他操作对象(比如寄存器)指出;
[...]的默认地址为ds,偏移地址为“...”;
指令 | 段地址 | 偏移地址 | 内存单元长度 |
mov ax,[0] | DS | 0 | 字 |
mov al,[0] | DS | 0 | 字节 |
mov ax,[bx] | DS | bx寄存器中 | 字 |
mov al,[bx] | DS | bx寄存器中 | 字节 |
(2)(...)——表示一个内存单元或寄存器中的内容(为了方便学习做出的约定)
“()”中的元素可以有3种类型:寄存器名、段寄存器名、内存单元的物理地址(一个20位的数据)
描述对象 | 描述方法 |
ax中的内容为0010H | (ax)=0010H |
mov ax,[2]的功能 | (ax)=((ds)*16+2) |
add ax,2功能 | (ax)=(ax)+2 |
push ax功能 | (sp)=(sp)-2 ((ss)*16+(sp))=(ax) |
2000:1000处的内容为0010H | (210000H)=0010H |
mov [2],ax 功能 | ((ds)*16+2)=(ax) |
add ax,bx功能 | (ax)=(ax)+(bx) |
pop ax的功能 | (ax)=((ss)*16+(sp)) (sp)=(sp)+2 |
(3)约定符号idata表示常量
mov ax,[idata] 代表mov ax,[1] 、mov ax,[2] 、mov ax,[3] 等;
mov ax,idata 代表mov ax,1]、mov ax,2 、mov ax,3 等;
mov ds,idata 代表mov ds,1、mov ds,2 、mov ds,3 等,为非法指令;
(4)Debug和汇编编译器对指令的不同处理
Debug将[idata]解释为一个内存单元;编译器将[idata]解释为idata;
任务:将内存2000:0、2000:1、2000:2、2000:3单元中的数据送入al,bl,cl和dl中(1)在Debug中编程实现
mov ax,2000
mov ds,ax
mov al,[0]
mov bl,[1]
mov cl,[2]
mov dl,[3](2)汇编源程序实现
assume cs:code
code segmentmov ax,2000mov ds,axmov al,[0]mov bl,[1]mov cl,[2]mov dl,[3] mov ax,4c00hint 21hcode ends
end
a.在汇编源程序中,如果在“[]”里用一个常量idata直接给出内存单元的偏移地址,在“[]”的前面显式地给出段地址所在的段寄存器;
b.如果在“[]”里用寄存器,则段地址默认为ds,可以不显示的给出段地址所在的段寄存器;
五、Loop指令
(1)功能:实现循环(计数型循环)
loop 标号
作用:
递减
CX
寄存器:在执行loop
指令时,CPU 会自动将CX
寄存器的值减 1。判断循环条件:如果
CX
不为零,则跳转到指定的标号
处继续执行循环体;如果CX
为零,则不跳转,继续执行后续指令。
(2)CPU执行loop指令时要进行的操作
a. (cx)=(cx)- 1;
b.判断cx中的值
不为零则转至标号处执行程序;
如果为零则向下执行;
(3)实例程序
assume cs:codecode segmentmov ax,2mov cx,11
s:add ax,axloop smov ax,4c00hint 21h
code ends
end
(4)loop
指令与 [bx]
的结合使用
[bx]
是一种内存寻址方式,表示通过 BX
寄存器中的偏移地址访问内存单元。loop
指令可以与 [bx]
结合使用,实现动态访问内存中的数据。
在汇编语言中,当我们需要将内存地址 ffff:0
到 ffff:b
(共 12 个字节)中的 8 位数据累加到 16 位寄存器 dx
中时,会面临两个关键问题:
-
结果溢出:如果直接使用 8 位寄存器
dl
来存储累加结果,由于 12 个 8 位数据的总和最大为 12 × 255 = 3060,超过了dl
的最大值 255,因此会导致溢出。 -
类型不匹配:如果尝试将 8 位数据直接加到 16 位寄存器
dx
中,由于dx
是 16 位寄存器,而add
指令要求两个操作数类型一致,这种直接相加会导致类型不匹配的问题。
为了解决这两个问题,通常采用以下方法:
-
使用 16 位寄存器作为中介:将内存中的 8 位数据先加载到 16 位寄存器
ax
中,然后将ax
的内容加到dx
中。这样可以确保两个操作数的类型一致,同时避免溢出。 -
循环控制:使用
loop
指令配合[bx]
寄存器,实现对内存单元的逐个访问和累加,从而高效地完成整个累加过程。
实例演示l
assume cs:code
code aegmentmov ax,0ffffhmov ds,axmov bx,0 ;初始化ds:bx指向ffff:0mov dx,0 ;初始化累加寄存器dx,(dx)=0mov cx,12 ;初始化循环计数寄存器cx,(cx)=12s:mov al:[bx]mov ah,0add dx,ax ;间接向dx中加上((ds)*16+(bx))单元的数值inc bx ;ds:bx指向下一个单元loop smov ax,4c00hint 21h
code ends
end