汇编语言设计----汇编基础第一节
汇编语言设计
机器语言是机器指令集合
早期:打纸带:1打孔,0不打孔
汇编语言与汇编指令
汇编语言主体是汇编指令
-
汇编指令是机器指令便于记忆书写格式
-
汇编指令是机器指令助记符
-
机器指令100110001100001100000
汇编MOV,AX,BX
汇编语言编写程序工作过程
汇编指令--编译器--机器码
伪指令--编译器执行
汇编指令--机器码助记符
其他符号--编译器识别
计算机组成
cpu,总线,内存,扩展槽
指令和数据存放在内存或磁盘上,都是二进制
数据如何表示:二进制B,八进制O,十进制D,十六进制H
计算机中的存储单元
一个存储器有128单元
8086有20条数据总线,那有2的20次方的,1MB的空间
总线:链接cpu和其他芯片的导线,
逻辑上总线分:数据总线,地址总线,控制总线
地址总线
地址总线宽度,决定了可寻址总线的存储单元大小
数据总线
cou内存和其他器件之间的数据传送是通过数据总线进行的,数据总线宽度决定了cou的外界数据传输速度
8088 8位,8086,16位
控制总线
cpu对外部设备器件进行控制
内存的读写和地址空间
存储单元地址(地址信息)
器件的选择,读或写的命令(控制信息)
读或写的数据(数据信息)
什么是内存地址空间
RAM:能读能写,没电就没了,
ROM:只读,系统的BIOS
所有物理存储器看做一个由若干存储单元组成的逻辑存储器
每个物理存储器在逻辑存储器中占有一个地址段,即一段地址空间
汇编语言实践语言的环境搭建
目标:理解计算机底层工作原理
DOS环境
链接:https://pan.baidu.com/s/1P62ldT7uXqKBAl3v4tXUOA?pwd=6fr9
提取码:6fr9
--来自百度网盘超级会员V5的分享
解压后的文件夹如下:
其中:
DOSBox0.74-win32-installer.exe是DOS模拟器的安装文件;
MASM文件夹中是汇编程序设计中用到的命令;
EX文件夹中提供了几个汇编程序作为示例。
照下面的提示完成环境的配置。
1. 将MASM文件夹拷贝到工作盘
MASM中包含了8086汇编程序设计中要用到的几个必需的工具(masm.exe汇编、link.exe连接、Debug.exe调试,edit.com和edlin.com是两个编辑程序。),如下:
将这个文件夹拷贝到你习惯的工作盘(我用D盘作工作盘,拷贝后,这些文件在D:\MASM中)。
2. 安装DOSBox
双击DOSBox0.74-win32-installer.exe,照提示安装,最后,桌面上有快捷方式:
3. 启动DOSBox
双击上面的图标,启动DOSBox0.74。启动后的界面如下:
4.挂接MASM文件夹
在Z:>提示符后输入命令mount c d:\masm。这个命令的意思是,将本机d:\masm文件夹作为模拟器的C盘。
运行命令后的界面如下:
注意:d:\masm是第1步拷贝到的工作目录,若你在第1步设置的是其他工作,请按你的实际设置改动。
下面,将工作盘转换到C盘(输入C:并回车),再查看目录(dir再回车),可以看到类似下面的界面,标志着工作环境设置好了。
在以后工作时,只需要第3、4步即可。
寄存器和数据存储
运算器进行信息处理
寄存器进行信息存储
控制器协调各种器件
内存总线实现CPU各器件之间的联系
14个寄存器
通用寄存器AX例子
在AX中存储18D
18D --十进制
12H --十六进制
10010 --二进制
问:8088 如何8086兼容?
分高低位
01001110 00100000
高位 低位
AH AL AX
BH BL BX
CH CL CX
DH DL DX
字:一个字可以存在一个16位的寄存器中
高位字节在高位寄存器高8位,低位字节在低8位寄存器
MOV add指令
mov ax.18 AX = 18 相当于赋值
mov ah.78 AH = 78 把78放到了高 8位
add ax.8 AX = AX +8 在AX数据加8
mov.ax.bx AX = BX BX数据送入AX中
add ax.bx AX = AX+BX 把AX,BX 内容相加
汇编指令不区分大小写
确定物理地址方法
川普访问内存单元都要给出内存单元地址
每一个内存单元在这个空间构成的空间都有唯一地址,这个地址叫物理地址
8086有20位地址总线,寻址能力是1mb
8086是16位cpu,寻址能力是64KB
如何解决寻址空间的矛盾
用两个16位地址和段地址,偏移地址合成一个20位的物理地址
物理地址 = 段地址X16+偏移地址
例子:
段地址: 1230 偏移1位
偏移地址 00C8
12300
+00C8
= 123C8
物理地址 = 123C8
本质含义
用两个16位地址相加得到20位物理地址
本质含义:cpu在访问内存时,用一个基础地址和一个相对于基础地址的偏移地址相加,给出一个内存单元的物理地址
内存的分段表示法
内存并没有分段,段的划分来自于cpu
同一段内存,多种分段方案
根据段地址计算偏移地址
4个段寄存器
CS-代码段DS数据段-SS栈段-ES段加
debug的使用
R查看寄存器的内容
R 寄存器名 改变指定寄存器内容(128字节)
RCS,RIP
D命令:查看内存中的内容
D段地址:偏移地址-列出内存中指定地址处的内容
D 1000:0
D段地址:偏移地址 结尾偏移地址-;列出内存中指定地址范围内容
E命令
E段地址:偏移地址 数据1
EE段地址:偏移地址
E 1000:0
逐个询问式修改
空格接收,继续
回车,结束
U命令将内存中机器指令翻译成汇编指令
有汇编指令
mov ax,0123H
mov bx,0003H
mov ax,bx
add ax,bx
对应机器码
B8 23 01
BB 03 00
89 D8
01 D8
e 地址- 数据写入
d 地址查看
u地址-查看代码
A命令
有汇编指令
mov ax,0123H
mov bx,0003H
mav ax,bx
add ax,bx
对应机器码
B8 23 01
BB 03 00
89 D8
01 D8
a 地址-写入汇编指令
d 地址-查看数据
u 地址 -查看代码
T指令(执行机器命令)
t 执行CS:IP处的指令
mov ax,123H
两个关键的寄存器
CS:代码寄存器
IP:指令指针寄存器
CS:IP :cpu将内存中CS:IP指向的内容当做指令执行
CS内容是2000H,IP中内容是0000H
8086PC工作过程简要概述
1.CS:IP指向内存单元读取指令,读取的指令进入指令缓存器
2.IP = IP+ 所读取的指令的长度,从而指向下一条指令
3.执行指令,转到步骤1
jmp指令
事实:执行何处的指令,去取决于CS:IP
应用;可以通过改变CS IP的内容,来控制执行目标的指令
转移指令jmp
同时修改cs,ip内容
jmp段地址:偏移地址
jmp:2AE3:3
jmp:30B16
用指令中给出的段地址修改cs,偏移地址修改ip、
仅修改ip内容
jmp 某一合法寄存器
? jmp ax (类似于mov IP,ax)
? jmp bx
内存中字的存储
8086 16位作为一个字
高8位,低8位
低位字节低地址单元,高位字节高地址单元
0是低地址单元,1是高地址单元
字单元:由两个连续地址连续内存单元组成,存放一个字型数据
原理在一个字单元中,低地址单元存放低位字节,搞地质单元存放高位字节
用DS和[address]实现字的传送
要解决的问题:cpu从内存单元中要读取数据
在8086中,内存地址有段地址和偏移地址组成(段地址:偏移地址)
DS寄存器存放要访问的数据和段地址
偏移地址用[]形式
将段地址送入DS的方式
mov bx,1000H
mov bs, bx
套路是: 数据---一般寄存器---段寄存器
字的传送
8086可以一次性传送一个字
例子:
mov bx,1000H
mov ds ,bx
mov ax, [0] # 1000:0处的字型数据送入ax
mov[0],cx # cx中的16位数据送到1000:0处
[0] -- 1123
[1] -- 2211
[2] -- 1122
DS与数据段
对内存单元中数据的访问
对于8086 可以根据需要将一组内存单元定义为一个段
将一组长度N,地址连续,起始地址16的倍数内存单元当做专门存储数据内存空间,从而定义的一个数据段
段地址 123BH 起始偏移地址0000H 长度10字节
处理方法 DS([address])
用DS存放数据段的段地址
用相关指令访问数据段中的具体单元,单元地址由[address]
将123B0H--123BAH定义为数据段
累加数据段中前3个单元的数据
mov ax,123BH
mov ds,ax
mov al,[0]
add al,[0]
add,al,[1]
add al,[2]
用mov指令操作数据
add指令操作
栈结构
栈的基本操作
入栈和出栈
入栈:将一个新的元素放到栈顶
出栈:从栈顶取出一个元素
栈顶元素总是在最后入栈,需要出栈,又最先从栈中取出
cpu提供栈机制
8086cpu提供相关指令,支持用栈的方式访问内存空间
基于8086cpu编程,可以将一段内存当栈来使用
PUSH入栈和POP出栈指令
push ax:将ax从中的数据送入栈中
pop ax:从栈顶取出数据送入ax
问题:cpu如何知道一段内存空间被当做栈使用?
执行push和pop的时候,如何知道哪个单元是栈顶单元?
答案
8086中有两个与栈相关的寄存器
栈段寄存器SS--存放栈顶的段地址
栈顶指针寄存器SP--存放栈顶的偏移地址
栈的操作
mov ax,1000H
mov ss,ax
mov sp,0010H
mov ax,001AH
mov bx,001BH
push ax
push bx
pop ax
pop bx
执行入栈,栈顶超出栈空间
总结
push,pop实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据,与mov指令不同的是,push指令访问的内存单元地址不是在指令中给出的,而是有SS:SP指出的
段总结
物理地址 = 段地址x16 +偏移地址
数据段
- 将段地址放在DS中
- 用mov,add,sub等访问内存单元指令时,cpu将我们定义的数据段中内容当做数据段访问
代码段
- 将段地址放在CS中,将段中第一条指令的偏移地址放在IP
- CPU将执行我么定义四的代码段中指令
栈段
- 将段地址放在SS,将栈顶单元偏移的放在SP中
- CPU在需要我们进行栈操作push,pop,就将我们定义的栈段当做栈空间来使用
汇编语言程序
汇编语言编程序的工作过程
程序员--汇编程序--编译器--机器码--计算机
伪指令:没有对应的机器码指令,最终不被cpu所执行
伪指令是编译器执行,编译器根据伪指令进行相关的编译工作
汇编程序:汇编程序好伪指令的文本
程序返回:程序结束后将cpu的控制权交还给使运行的程序系统
程序的三种伪指令
段定义:一个会变程序是多个段组成的,这些段用来被存放代码,数据或当做栈空间来使用
一个有意义的汇编程序中至少有一个段,这个段用来存放代码
段名 segment --- 段的开始
段名 ends --- 段的结束
assume cs:codesg
codesg segment
mov ax,0123H
mov bx,0456H
add ax,bx
add ax,ax
mov ax,4c00h
int 21h
codesg ends
end
'''
end
汇编程序结束标志
assume
含义是假设某一段寄存器中某一个使用segment,ends定义段相关联--assume cs:cdoesg指的是cs寄存器与codesg关联,将定义的codesg当做程序的代码来使用
'''
源程序经过编译连接后变为机器码
源程序文件 .asm ---可执行 .exe
汇编程序结构
在debug中直接写入指令编写汇编程序
适用于简单的短小精悍的程序
单独编写成源文件在编译可执行文件--编写大程序
注释 ;
编写一个汇编程序
步骤
-
定义一个段
-
实现处理任务
-
指出程序在何时结束
-
段与段寄存器关联
-
加上程序返回的代码
-
assume cs:abc abc segment abc segment mov ax,2 add ax,ax add ax,ax mov ax,4c00h int 21h abc ends end程序中出现的错误
语法错误
写错了。。。
逻辑错误
脑子抽了。。。
写出源程序到可执行文件过程
编辑源程序
编译
? 对目标文件.OB对一个源程序进行编译要得到的结果
? 列表文件.LST是编译器将源程序编译为目标文件的过程中产生的中间结果
? 交叉引用文件。CRF和列表文件一样,是编译器将源程序编译为目标文件过程中产生的中间结果
对源程序编译结束,编译器输出最后两行告诉错误必须改正的错误
assume cs:codesg
codesg segment
mov ax,0123H
mov bx,0456H
add ax,bx
add ax,ax
mov ax,4c00h
int 21h
codesg ends
end
提示语法错误
severe errirs
找不到所给出的源文件
连接
可执行文件是对一个程序进行连接要得到的最终结果
映象文件:是连接程序将目标文件连接为可执行文件过程中产生中间结果
库文件包含了一些可以调用的子程序,如果我们的程序中调用了某一个库文件中的子程序,就需要连接的时候将这个库文件和我们的目标文件连接到一起,生成可执行文件、
no stack segment,一个 没有栈段的错误警告,可以不去理会
执行程序;直接输入文件名
程序的运行和跟踪
DEBUG装在程序EXE
程序加载后,DS中存放着程序所在内存区的段地址,这个内存区的偏移地址是0,程序内存区地址是DS:0
这个内存区前256字节存PSP,DOS用来程序进行通信
从256字节向后空间存放的是程序,CS值是DS+10H
程序加载后,CX存放代码长度
继续命令P
类似于T命令,逐条执行指令,显示结果,但遇到子程序,中断等,直接执行,然后显示结果
运行命令G
从指定地址处开始运行程序,知道断点结束或者程序正常结束
在DOS中执行
程序执行的常态
一个内存单元的描述:
- ? 内存单元地址
- ? 内存单元长度
[...]表示一个内存单元
(...)表示一个内存单元或寄存器中内容
LOOP指令
功能:实现循环
cpu执行loop指令时进行的操作
-
(cx)=(cx)-1
-
判断cx的值
-
不为0则转到标号处执行程序
-
如果是0就向下执行
-
要求
cx要提前存放循环次数,(cx)影响着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
例子:编程计算 2^2 , 2^3
cx和loop指令想配合实现循环功能的三个要点
? 1.在cx中存放循环次数
? 2.用标号指定循环开始的位置
? 3.在标号和loop指令的中间,写上要循环执行的程序段(循环体)
? 例子:计算 123x236
assume cs:code
code segment
mov ax,0
mov cx,236
s: add ax,123
loop s
mov ax,4c00h
code ends
end
例子
编译:
masm p5-1.asm
link p5-1
debug p5-1.exe
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov bx,6
mov al,[bx]
mov ah,0
mov dx,0
mov cx,3
s: add dx,ax
loop s
mov ax,4c00h
int 21h
code ends
end
引入段前缀,异常现象的对策
访问内存单元--loop和[bx]
问题:计算ffff:0-ffff:b字节单元中的数据的和,结果存储在dx中
分析:略
对策:取出8位数据,加到16位寄存器
mov al,ds:[addr] 取出8位数据
mov ah,0
add dx,ax
段前缀的使用
代码段中数据的使用
将内存ffff:0中数据拷贝到0:200-0:20b
问题,程序中直接写地址,很危险
对策
在程序中段中存放数据,运行时由操作系统分配空间
段的类别:数据段,代码段,栈段
各种段中均可以有数据
可以在单个段中安置,也可以将数据代码,栈放入不同的段中
实际应用
问题编程计算以下8个数据的和,结果存放到ax寄存器中
解决方案
assume cd:code
code segment
dw 0123H,0456H,0789H,0ABCH,0DEFH,0CBAH,0987 ;在代码段中定义数据,只要求数据本身,并未指定在哪些单元中
mov bx,0
mov ax,0
mov cx,8
s: add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end
dw:定义一个字
db:定义一个字节
dd:定义一个双字
代码有问题
改进
assume cs:code
code segment
dw 0123H,0456H,0789H,0ABCH,0DEFH,0CBAH,0987 ;在代码段中定义数据,只要求数据本身,并未指定在哪些单元中
start: ; 定义一个标号,指示代码开始的位置
mov bx,0
mov ax,0
mov cx,8
s: add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end start ;end 通知编译器程序结束外,还可以通知编译器入口在什么地方
在代码段中使用栈
程序运行时候。定义存放的数据cs:0--cs:f,一共8个单元
依次将八个单元中数据入栈,然后一次出栈找到8个字单元,从而实现数据的逆序存放
栈需要内存空间,在程序中通过定义:空数据来获得
数据逆序存放程序
assume cs:codesg
codesg segment
dw 0123H,0456H,0789H,0ABCH,0DEFH,0CBAH,0987 ;在代码段中定义数据,只要求数据本身,并未指定在哪些单元中
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
start:
mov ax,cs
mov ss,ax
mov sp,30h
mov bx,0
mov cx,8
s:push cs:[bx]
add bx,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
将数据代码栈放入不同段
assume cs:code,ds:data,ss:stack
data segment
dw 0123H,0456H,0789H,0ABCH,0DEFH,0CBAH,0987
data ends
stack segment
dw 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 bx,0
mov cx,8
;---------------------------入栈
s:push [bx]
add bx,2
loop s
mov bx,0
mov cx,8
; --------------------------出栈
s0:pop [bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
code ends
end
内存寻址方式
处理字符问题
汇编中,用‘..................'的方式指明数据是字符形式给出的,编译器把他们转化为响应的ASCII码
大小写字符问题
大写+20H --->小写 小写-20H --->大写
大小写转换
第一个字符串:小--大
第二个 大--小
assume cs:codesg,ds:datasg
datasg segment
db 'BsSic'
db 'INforMAtion'
datasg ends
codesg:segment
start:
mov ax,datasg
mov ds,ax
;第一个字符串:
mov bx,0
mov cx,5
s:mov al,[bx]
and al,11011111b
mov [bx],al
inc bx
loop s
;第二个字符串
mov bx,5
mov,cx 11
s0:mov al,[bx]
or al,00100000b
mov [bx],al
inc bx
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
[bx+idata]含义
表示一个内存单元,它的偏移地址为(bx)+idata (bx中数值加上idata)
mov ax,[bx+200]/mov ax,[200+bx]
将一个内存单元的内容送入ax
这个内存单元的段地址在ds中,偏移地址在ds中,偏移地址为200加上bx的数值,数学化的描述是(ax) = ((ds)*16+200+(bx))
指令mov,ax,[bx+200]其他用法
mov ax,[200+bx]
mov ax,200[bx]
mov ax,[bx].200
用数组方法解决大小写转换问题
assume cs:codesg,ds:datasg
datasg segment
db 'BasiC'
db 'MinIX'
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,5
s:mov al,[bx]
and al,11011111b
mov [bx],al
mov al,[5+bx]
or al,00100000b
mov [5+bx],al
inc bx
loop s
mov ax,4c00h
int 21h
codesg ends
end start
SI和DI寄存器
8086cpu有十四个寄存器
通用寄存器 AX,BX,CX,DX
变址寄存器 SI,DI
指针寄存器 SP,DP
指令指针寄存器 IP
段寄存器 CS,SS,DS,ES
标志寄存器 PSW
执行与地址有关操作
SI,DI和BX相近的寄存器
SI:源变址寄存器
DI目标变址寄存器
区别:SI,DI不能够分成两个8位寄存器来使用
1.mov bx,0
- mov s,0
- mov di 0
例子:用寄存器SI,SI实现将字符串‘welcome to masm’复制到特后面的数据区
源数据起始地址:datasg:0
目标数据起始地址:datasg:16
用ds:si指向要复制的原始字符串
用ds:di指向目的空间
然后用一个循环来完成复制
原地址:si
目标地址:di
assume cs:codesg,ds:datasg
datasg segment
db 'welcome to masm'
db '...............'
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov si,0
mov di,16
mov cx,8
s: mov ax,[si]
mov [di],ax
add si,2
add di,2
loop s
mov ax,4c00h
int 21h
codesg ends
end start
[bx+si] [bx+di]指定地址
- [bx+si] 表示一个内存单元
- 偏移地址 bx +si 即是数值上加si中的数值
指令mov ax, bx+si 含义
- 将一个内存单元的内容送入ax
- 这个内存单元长度是2字节(字单元),存放一个字
- 偏移地址是bx中数值加上si中的数值
- 段地址 在ds中
指令mov ax,[bx+si]的数学化描述
- (ax) = ((ds)*16+(bx)+(si))
- mov ax,[bx+si]其他用法
- mov ax,[bx] [si]
例子
mov ax,2000H
mov ds,ax
mov bx,1000H
mov si,0
mov ax,[bx+si]
inc si
mov cx,[bx+si]
inc si
mov di,si
mov ax,[bx+di]
[bx+si+idata] 和 [bx+di+idata]
偏移地址是 (bx) +(si)+idata 就是bx中的数值加上si中数值再加上idata
move ax,[bx+si+idata]含义
将一个内存单元的内容送入ax
这个内存单元长度是2字节,存放一个字
偏移地址中bx中数值加上idata,段地址在ds中
数字化描述
(ax=(ds)*16)+(bx)+(si)+idata
指令mov,ax,[bx+si+idata] 其他写法
- mov ax,[bx+200+si]
- mov ax,[200+bx+si]
- mov ax,200[bx] [si]
- mov ax,[bx].200[si]
- mov ax,[bx] [si].200
- mov ax,[bx] [si]
例子
内存中数据 2000:1000 BE 00 06 00 6A 22
程序执行后
mov ax,2000H
mov ds,ax
mov bx,1000H
mov si,0
mov ax,[bx+2+si]
inc si
mov cx,[bx+2+si]
inc si
mov di,si
mov ax,[bx+2+di]
内存寻址方式的灵活应用
例子
例子
二重循环用了同一个cx,解决办法
但是存在问题是,cx占用寄存器资源,比较浪费
可以利用固定的储存单元,也可以用栈来存数据
直接寻址过程
寄存器间间接寻址过程
寄存器相对寻址过程
基址变址寻址过程
相对基址变址寻址过程