汇编学习笔记
汇编学习笔记
参考《汇编语言(第三版)》王爽著
1.基础知识
1.1 机器语言
二进制编码
1.2 汇编语言
1.3 汇编语言的组成
1.4进制表示符
二进制(B),十六进制(H)
2.寄存器
2.1通用寄存器
8086CPU的所有寄存器都是16位的,可以存放两个字节。AX、BX、CX、DX这4个寄存器通常用来存放一般性的数据,被称为通用寄存器。
8086CPU上一代CPU中的寄存器是8位的,为保证兼容,上述四个寄存器可以分为两个可独立使用的8位寄存器使用:
例如:AX可分为AH和AL
8个高位构成AH,8个低位构成AL
例如:
2.2字在寄存器中的存储
字节:byte,1 byte=8 bit,刚好存在8位寄存器中
字:word,1 word=2 byte,两个字节分别占用字的高位和低位,一个字刚好存在16位寄存器中
2.3几条汇编指令
例题运算:
ax=ax+bx=8226H+8226H=1044CH,保留044CH
al=C5H+93H=158H,保留58H,故ax=0058H
此处有规律,比如ax=2640H,则ah=26H,al=40H,在做运算时,可以将ax拆成ah
和al进行运算。
2.4物理地址
我们知道,CPU访问内存单元时,要给出内存单元的地址。所有的内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间中都有唯一的地址,我们将这个唯一的地址称为物理地址。
内存单元的存储空间的线性空间地址就是物理地址
2.5 16位结构的CPU
2.6 8086CPU给出物理地址的方法
补充:一个X进制的数据左移1位,相当于乘以X
2.7 “段地址x16+偏移地址=物理地址”的本质含义
CPU最大为16位,不能直接传出20位的数字信息,利用地址偏移将20位数字拆分为两个16位的数字信息进行传输
2.8 段的概念
CPU对内存进行分段处理(内存本身没有分段)
2.9 段寄存器
段地址需要存放在8086CPU的段寄存器中。8086CPU有4个段寄存器:CS、DS、SS、ES。8086CPU访问内存是,从这四个段寄存器中获取内存单元段地址
CS 代码寄存器 CS:IP指定CPU处理的地址
DS 数据段寄存器 DS:[···]指定内存段的位置
SS 堆栈段寄存器 SS:SP指定栈顶的位置
ES 附加段寄存器
2.10CS和IP
CS为代码段寄存器,IP为指令指针寄存器。其中设CS中内容为M,IP中内容为N,8086CPU将从内存Mx16+N单元开始,读取一条指令执行。
运行示例:
初始状态获取CS:2000H,IP:0000H
CS、IP放入地址加法器运算2000Hx16+0000H=20000H
数据传入输入输出控制电路、再传入地址总线
CPU通过数据总线从内存20000H
开始读取数据
输入输出控制电路将机器指令送入指令缓冲器
读取一条指令后,IP自动增加(当前读入B82301
字节大小为3,故增加3字节)。也就是CPU控制内存从哪里开始读取
执行控制器执行指令,则AX被赋值为0123
。这里可以看出CPU并非在结果出现后才进行控制下一次运算的,而是进入指令缓冲器后就开始执行下一次。
8086CPU工作简要概括为:
8086CPU刚开始工作(或复位时),CS=FFFFH,IP=0000H,故FFFF0H单元中的指令是8086PC机开机执行的第一条指令
2.11 修改CS、IP的指令
mov被称为传送指令
可以修改CS、IP内容的指令被称为转移指令
jmp指令可以修改CS、IP的内容
格式jmp 段地址:偏移地址
即jmp CS:IP
jmp ax
含以上类似mov IP,ax
,作用是只修改IP的值
例题:
该题注意要点jmp只改变IP的值
,这题最后会陷入循环中
2.12 代码段
代码段是人为定义的,想要让CUP执行这段代码,就必须指定CS=123BH,IP=0000H
实验debug
debug参数:
R查看、改变CPU寄存器的内容
D查看内存中的内容
E改写内存中的内容
U将机器指令翻译成汇编指令
T执行一条机器指令
A以汇编指令的格式在内存中写入一条机器指令
3.寄存器(内存访问)
3.1 内存中字的储存
CPU中用16位寄存器存储一个字,8个高位存放在高位字节,8个低位存放在低位字节。而在内存中存储,则是一个单元存放一个字节,一个字要用两个地址连续的内存单元来存放,低位字节放在低地址单元中,高位字节放在高地址单元中。
在上图中,若存放一个字,由0、1两个字节单元组成,对于字单元来说,0号单元是低地址单元,1号单元是高地址单元。以此类推,起始地址相对后续地址单元为低地址值单元,后续单元以此类推
字单元:即存放一个字型数据(16位)的内存单元,有两个地址连续的内存单元组成。高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。
例题:对于图3.1
3.2 DS和[address]
ds 为内存单元的段地址
[···]表示一个内存单元,[0]表示内存单元偏移地址为0
mov al,[0] //8086CPU中将(ds:0-ds:1)的数据读到al中
mov [0],al //将数据从寄存器送入内存单元,al的16进制值赋值给10000H的内存地址
ds属于段寄存器,在8086CPU中不能直接将数据送入ds,需要使用一个寄存器进行中转
3.3 字的传送
8086CPU是16位结构,有16根数据线,可以一次性传送16位的数据。
sub ax,bx //ax的值减去bx
ds的值设定后,后续内存单元[···]自动认为在ds段地址中,这里看ds有点像全局变量
3.4 mov、add、sub指令
mov指令的形式
mov 寄存器,数据 mov ax,8
mov 寄存器,寄存器 mov ax,bx
mov 寄存器,内存单元 mov ax,[0]
mov 内存单元,寄存器 mov [0],ax
mov 段寄存器,寄存器 mov ds,ax
mov 寄存器,段寄存器 mov ax,ds
add指令的形式
add 寄存器,数据 add ax,8
add 寄存器,寄存器 add ax,bx
add 寄存器,内存单元 add ax,[0]
add 内存单元,寄存器 add [0],ax
sub指令的形式
sub 寄存器,数据 sub ax,8
sub 寄存器,寄存器 sub ax,bx
sub 寄存器,内存单元 sub ax,[0]
sub 内存单元,寄存器 sub [0],ax
3.5 数据段
一组长度为N(N<=64KB)、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,定义为一个数据段。
检测点3.1
(1)
mov ax,1 //ax=0001H
mov ds,ax //ds=0001H
mov ax,[0000] //ax=2662H (ax等于0001:0000-0001:0001地址位的值,0001:0000-0001:0001地址位等于00010H-00011H,同时可以发现0000:0010-0000:0011)
mov bx,[0001] //bx=E626 (同上)
mov ax,bx //ax=bx=E626
mov ax,[0000] //ax=2662H
mov bx,[0002] //bx=D6E6H
add ax,bx //ax=ax+bx=FD48H
add ax,[0004] //ax=ax+2ECCH=2C14H
mov ax,0 //ax=0000H
mov al,[0002] //al=E6H,故ax=00E6H
mov bx,0 //bx=0000H
mov bl,[000c] //bl=26,故bx=0026H
add al,bl //al=al+bl=E6+26=0CH,故ax=000CH
通过检测点3.1发现一个有趣的现象:0000:0010
=0001:0000
(2)
①②
初始值为CS=2000H,IP=0,DS=1000H,AX=0,BX=0,程序从CS:IP开始运行
mov ax,6622H //ax=6622H
jmp 0ff0:0100 //CS:IP=0ff0:0100,跳转内存地址10000H
mov ax,2000H //ax=2000H
mov ds,ax //ds=2000H
mov ax,[0008] //ax=C389
mov ax,[0002] //ax=EA66
3.6 栈
栈的特点是:先进后出,后进先出
3.7 CPU提供的栈机制
PUSH(入栈)和POP(出栈)
push ax
表示将寄存器ax中的数据送入栈
pop ax
表示从栈顶取出数据送入ax
段寄存器SS和寄存器SP存放栈顶的位置,其中栈顶的段地址存放在SS中,偏移地址存放在SP中,任意时刻,SS:SP指向栈顶元素,在执行push和pop指令时,CPU从SS和SP中得到栈顶的地址。
在8086CPU中,入栈时,栈顶从高地址向低地址方向增长。
注意,在pop指令中,虽然在SP=SP+2时,1000C-1000D中的值已经赋值给ax了,但它依然存在内存单元中,只是已经不在栈中,在下一次入栈时,将会被覆盖
问题3.6
当栈为空时,SS=1000H,SP=10H
3.8 栈顶超界的问题
在8086CPU中,没有单独的寄存器负责栈是否超界的问题,需要开发者自行规范代码
超出栈的后果:pop在栈超界后,会导致大于10020H的地址从其他栈中释放出来,后续再用push时,将会出现数据覆盖的现象。
3.9 push、pop指令
push 寄存器 ;将一个寄存器中的数据入栈
push 段寄存器 ;将一个寄存器中的数据入栈
push 内存单元 ;将一个内存字单元处的字入栈(栈操作以字为单位)
pop 寄存器 ;出栈,用一个寄存器接收出栈的数据
pop 段寄存器 ;出栈,用一个段寄存器接收出栈的数据
pop 内存单元 ;出栈,用一个内存单元接收出栈的数据
问题3.7
mov ax,1000
mov ss,ax ;设置栈的段地址,不能直接向ss中传数据
mov sp,0010 ;设置栈顶的偏移地址,sp=0010H
push ax
push bx
push cx
问题3.8
mov ax,1000
mov ss,ax
mov sp,0010
mov ax,001a
mov bx,001b
push ax
push bx
sub ax,ax ;将ax清零
;sub ax,ax的机器码为2个字节
;mov ax,0的机器码为3个字节
sub bx,bx
pop bx
pop ax
问题3.9
mov ax,1000
mov ss,ax
mov sp,1010
mov ax,001a
mov bx,001b
push ax
push bx
pop ax
pop bx
问题3.10
# 补充代码
mov ax,1000
mov ss,ax
mov sp,2 ;根据sp-2写入原则,将初始地址定义到10002H上,则写入时,可以从10000H开始写入
3.10 栈段
在8086CPU中将一组内存单元定义为一个段。例如,将长度为N(N<=64KB)的一组地址连续、起始地址为16的倍数的内存单元,当做栈空间,从而定义一个栈段。
比如,将10010H~1001FH这段长度为16字节的内存空间作为栈来用,以栈的方式访问,则这个空间就成称为一个栈段,段地址为1001H,大小为16字节
问题3.11
解释一下栈底部地址为什么是1000:FFFE,因为sp+2
问题3.12
根据问题3.11的结论,可以明显看出出,一个栈的大小,取决于SP的容量(相对于SP)
检测点3.2
(1)
mov ax,2000
mov ss,ax
mov sp,0010
(2)
mov ax,1000
mov ss,ax
mov sp,0000
实验2 用机器指令和汇编指令编程
1、预备知识:Debug的使用
d 段寄存器:偏移地址
-r ds
:1000
-d ds:0 ;查看从1000:0开始的内存区间中的内容
-d ds:10 18
-d cs:0 ;查看当前代码段中的指令代码
-d ss:0 ;查看当前栈段中的内容
-e ds:0 11 22 33 44 55 66 ;在从1000:0开始的内存区间中写入数据
-u cs:0 ;以汇编指令的形式,显示当前代码段中的代码
-a ds:0 ;以汇编指令的形式,向从1000:0开始的内存单元中写入指令
mov ax,2000
mov ss,ax
mov sp,10 ;设定2000:0000~2000:000F为栈空间,初始化栈顶
#在栈中压入两个数据
mov ax,3123
push ax
mov ax,3366
push ax
debug的T命令在执行mov ss,ax
命令后,紧接着mov sp,10
自动执行了,这又被称为中断机制
(2)
原因是mov ss,ax mov sp,10两个指令时使用了中断机制,执行中断例程时,cpu会将一些中断例程使用的变量自动压栈到栈中。
4、第一个程序
4.1一个源程序从写出到执行的过程
第一步:编写汇编源程序
第二步:对源程序进行编译连接
第三步:执行可执行文件中的程序
4.2源程序
程序4.1
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
1、伪指令
汇编指令有对应的机器码,可以编译成机器指令,从而由CPU执行。伪指令没有对应的机器指令,最终不被CPU所执行。
(1)
段名 segment //说明一个段开始
段名 ends //一个段结束
一个有意义的汇编程序至少要有一个段,这个段用来存放代码。
(2)
end //一个汇编程序的结束标记
segment···ends
标记一个段的结束
end
标记整个程序结束
(3)
assume //将代码段codesg与段寄存器cs联系起来
假设某一段寄存器和程序中的某一个用segment...ends定义的段相关联
2、源程序中的“程序”
3、标号
一个标号指代了一个地址。比如codesg
4、程序的结构
编程运算2^3
assume cs:abc ;(可选)abc当做代码段使用
;定义一个段,名称abc
abc segment
mov ax,2 ;写入汇编指令
add ax,ax
add ax,ax
abc ends
end
5、程序返回
#在程序4.1中的命令返回代码
mov ax,4c00H
int 21H
6、语法错误和逻辑错误
在上述4、程序的结构
中,代码运行会引发一些问题,因为程序没有返回。这个错误在编译时不会表现出来。
修改后:
assume cs:abc ;(可选)abc当做代码段使用
;定义一个段,名称abc
abc segment
mov ax,2 ;写入汇编指令
add ax,ax
add ax,ax
mov ax,4c00H
int 21H
abc ends
end
4.3 编辑源程序
edit 1.asm
assume cs:codesg
mov ax,0123h
mov bx,0456h
add ax,bx
add ax,ax
mov ax,4c00h
int 21h
codesg ends
end
4.4 编译
该笔记采用微软masm汇编编译器
目标文件(.obj)、列表文件(.lst)、交叉引用文件(.crf)
其中目标文件是我们最终要得到的结果
4.5 连接
上一节中,将1.asm编译得到了1.obj,现在将1.obj连接为1.exe
利用link.exe进行连接,在bin目录下。
nul.map
提示输入映象文件的名称,该文件是连接程序将目标文件连接为执行文件过程中产生的中间结果,直接回车可以不生成这个文件
.lib
提示输入库文件的名称。库文件里包含了一些可以调用的子程序,如果程序中调用了某一个库文件中的子程序,就需要在连接的时候,将库文件和目标文件连接到一起,生成可执行文件。
nul.def
定义文件
连接的作用
4.6 以简化的方式进行编译和连接
masm 1.asm;
link 1.obj;
4.7 1.exe的执行
这个程序只做了将数据送入寄存器和加法的操作。
4.8 谁将可执行文件中的程序装载进入内存并使它运行
4.9 程序执行过程的跟踪
进入debug流程
debug 1.exe
在DOS系统中,exe文件程序的加载过程
(1)程序加载后,ds中存放这程序所在内存区的段地址(DS=SA),这个内存区的偏移地址为0,则程序所在的内存区的地址为ds:0;
(2)内存区前256个字节中存放的是PSP,DOS用来和程序进行通信。从256字节处向后的空间存放的是程序。
ds可得到PSP的段地址SA,PSP的偏移地址为0,则物理地址为SAx16+0
因为PSP占256(100H)字节,所以程序的物理地址是:
SAx16+0+256=SAx16+16x16+0=(SA+16)x16+0
用段地址和偏移地址表示为:CS:IP=SA+10H:0
继续上面用debug调试1.exe
通过r和u可以查看寄存器状态和内存内占用情况
利用t可以一步一步执行,同时在执行到INT 21
时,使用p
命令,可以得到Program terminated normally(程序正常终止)
的提示消息
实验3 编译、汇编、连接、跟踪
程序最初运算的起点
之后的每一步
-u ss:0
ss:sp=2000H:000AH
pop ax
ss:sp=2000H:000AH
pop bx
ss:sp=2000H:000CH
push ax
ss:sp=2000:000EH
push bx
ss:sp=2000:000CH
pop ax
ss:sp=2000:000AH
pop bx
ss:sp=2000:000CH
这里有一个现象,pop取走空栈的内容,栈溢出了,导致栈的内容也改变了
assume cs:codesg ;将代码段codesg与cs联系起来
codesg segment ;代表一个段开始
mov ax,2000H ;ax=2000H
mov ss,ax ;ss=2000H,栈的段地址
mov sp,0 ;sp=0,栈的偏移地址
add sp,10 ;sp=0+10=10,栈顶为ss:sp,栈桥大小为sp=10
pop ax ;取ss:sp位置的值,ax=0
pop bx ;取ss:sp+2
push ax ;取ss:sp+2+2-2
push bx ;ss:sp+2+2-2-2
pop ax
pop bx
mov ax,4c00H
int 21H
codesg ends ;代表一个段结束
在源程序中,若不加H,则代表十进制
PSP的内容应该是在CS-10H:IP
的位置
5、[BX]和loop指令
1、[bx]和内存单元的描述
[bx]也表示一个内存单元,偏移地址在bx中,段地址在ds中
mov ax,[bx]
2、loop
循环
5.1 [BX]
mov ax,[bx] ;段地址SA默认在ds中,将SA:EA处的数据送入ax中。即:(ax)=((ds)*16+(bx))
mov [bx],ax ;将ax中的数据送入内存SA:EA处。即((ds)*16+(bx)=(ax))
问题5.1
inc bx表示bx中的内容加1
mov ax,2000H ;ax=2000H
mov ds,ax ;ds=2000H
mov bx,1000H ;bx=1000H
mov ax,[bx] ;ax=00BE
inc bx ;bx=bx+1=1001H
inc bx ;bx=bx+1=1002H
mov [bx],ax ;21002H的内存地址是BE00(由小到大)
inc bx ;bx=bx+1=1003H
inc bx ;bx=bx+1=1004H
mov [bx],ax ;21004H的内存地址是BE00(由小到大)
inc bx ;bx=bx+1=1005H
mov [bx],al ;21005H的内存地址是BE(由小到大)
inc bx ;bx=bx+1=1006H
mov [bx],al ;21006H的内存地址是BE(由小到大)
5.2 Loop指令
loop指令的格式是:loop标号,CPU执行loop指令的时候,要进行两步操作,①(cx)=(cx)-1;②判断cx中的值,不为零则转至标号处执行程序,如果为零则向下执行。
loop指令实现循环功能,cx中存放循环次数
程序 5.1
编程计算2^12,结果存在ax中
assume cs:codesg
codesg segment
mov ax,2
mov cx,11
s: add ax,ax ;标号s,标识了一处指令
loop s ;执行loop两步指令
mov ax,4c00h
int 21h
codesg ends
循环功能的程序框架
mov cx,循环次数
s:
循环执行的程序段
loop s
程序5.2
编程计算123*236,结果存在ax中
assume cs:code
code segemt
mov ax,0
mov cx,123
s: add ax,236
loop s
mov ax,4c00h
int 21h
code ends
end
5.3 debug跟踪loop指令实现的循环程序
计算ffff:0006
单元中的数乘以3,结果存储在dx中
程序5.3
assume cs:code
code segemt
mov ax,0ffffh ;数值不能以字母开头
mov ds,ax
mov bx,6
mov al,[bx] ;将ah为0,al为bx,是因为ax存储ffff:6-7单元的值
mov ah,0
mov cx,3
mov dx,0
s: add dx,ax
loop s
mov ax,4c00h
int 21h
code ends
end
在汇编程序中,数值不能以字母开头
s等同于0012,loop s
在源程序执行后,变成了loop 0012
,在执行后,把IP设置为0012h。
5.4 Debug和汇编编译器masm对指令的不同处理
masm编译器会将"mov ax,[0]"当做"mov ax,0"处理
解决:可以写成"mov ax,ds:[0]",或者"mov ax,[bx]"
5.5 loop和[bx]的联合应用
问题:计算ffff:0~ffff:b单元中的数据的和,结果存储在dx中
因为ffff:0~ffff:b中的数据是8位的,不能直接加到16位的寄存器dx中
程序5.6
利用loop与[bx]的配合使用,实现上述问题
assume cs:codesg
codesg segment
mov ax,0ffffh
mov ds,ax
mov bx,0
mov dx,0
mov cx,12
s:
mov al,[bx]
mov ah,0
add dx,ax
inc bx ;ds:bx指向下一个单元
loop s
mov ax,4c00h
int 21h
codesg ends
end
5.6 段前缀
用于显示地指向内存单元的段地址的"ds:" "cs:" "ss:" "es:",在汇编语言中称为段前缀。
mov ax,ds:[bx]
mov ax,cs:[bx]
mov ax,ss:[bx]
mov ax,es:[bx]
mov ax,ss:[0]
mov ax,cs:[0]
5.7 一段安全的空间
在8086模式中,随意向一段内存空间写入内容是很危险的,因为这段空间中可能存放着重要的系统数据或代码。
在dos方式下,一般情况,0:200~0:2ff空间中没有系统或其他程序的数据或代码
5.8 段前缀的使用
将内存ffff:0~ffff:b
单元中的数据复制到0:200~0:20b
单元中
分析:0:200~0:20b
assume cs:codesg
codesg segment
mov bx,0 ;(bx)=0,偏移地址从0开始
mov cx,12 ;(cx)=12,循环12次
s: mov ax,offffh
mov ds,ax ;(ds)=0ffffh
mov dl,[bx] ;(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入dl
mov ax,0020h
mov ds,ax ;(ds)=0020h
mov [bx],dl ;((ds)*16+(bx))=(dl),将dl中的数据送入0020:bx
inc bx ;(bx)=(bx)+1
loop s
mov ax,4c00h
int 21h
code ends
end
改进:
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax ;(ds)=0ffffh
mov ax,0020h
mov es,ax ;(es)=0020h
mov bx,0 ;(bx)=0,此时ds:bx指向ffff:0,es:bx指向0020:0
mov cx,12
s: mov dl,[bx] ;(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入dl
mov es:[bx],dl ;((es)*16+(bx))=(dl),将dl中的数据送入0020:bx
inc bx ;(bx)=(bx)+1
loop s
mov ax,4c00h
int 21h
code ends
end
实验4 [bx]和loop的使用
assume cs:code
code segment
mov ax,0020H
mov ds,ax
mov bx,0
mov dx,0
mov cx,64
s: mov [bx],dx
inc bx
inc dx
loop s
mov ax,4c00h
int 21h
code ends
end
assume cs:code
code segment
mov ax,0020H
mov ds,ax
mov bx,0
mov cx,64
s: mov [bx],bx
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end
(3)
assume cs:code
code segment
mov ax,cs
mov ds,ax
mov ax,0020h
mov es,ax
mov bx,0
mov cx,17H
s: mov al,[bx]
mov es:[bx],al
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end
6、包含多个段的程序
程序取得所需空间的方法有两种,一是在加载程序的时候为程序分配,再就是程序在执行的过程中向系统申请
若要一个程序在被加载的时候取得所需的空间,则必须要在源程序中做出说明。在源程序中定义段来进行内存空间的获取。
6.1 在代码段汇中使用数据
dw定义字型数据,即define word
程序6.1
assume cs:code
code segment
dw 0123h,0456h,0789h,0abch,0fedh,0cbah,0987h
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定义的数据处于代码段的最开始,所以偏移地址为0,这8个数据就在代码段的偏移0、2、4、6、8、A、C、E处。程序运行时,它们的地址就是CS:0、CS:2、CS:4、CS6、CS:8、CS:A、CS:C、CS:E。
在代码段中,前面的16个字节是用dw
定义的数据,从第16个字节开始才是汇编指令对应的机器码。
程序6.2
assume cs:code
code segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
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作用,通知编译器程序结束,通知也通知编译器程序入口在哪(end start)
start是标号
程序框架
assume cs:code
code segment
:
:
数据
:
:
start:
:
:
代码
:
:
code ends
end start
6.2 在代码段中使用栈
程序6.3
assume cs:codesg
codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;用dw定义16个字型数据,在程序加载后,将去得16个字的内存空间,
;存放这个16个数据。在后面的程序中将这段空间当做栈来使用
start: mov ax,cs
mov ss,ax
mov sp,30h
*简述上述知识点
8086CPU的所有寄存器都是16位的,可以存放两个字节。AX、BX、CX、DX这4个寄存器通常用来存放一般性的数据,被称为通用寄存器,其中寄存器可拆分为AH、AL等
# 段寄存器
CS 代码寄存器 CS:IP指定CPU处理的地址
DS 数据段寄存器 DS:[···]指定内存段的位置
SS 堆栈段寄存器 SS:SP指定栈顶的位置
ES 附加段寄存器 ES:[···]指定内存段的位置
mov 赋值、add 增加、sub 减少、jmp 跳跃、inc 加1、lea加载内存地址到寄存器、call 调用子程序
push 入栈、pop出栈
masm 1.asm;
link 1.obj;
编程(Edit)—>1.asm—>编译(masm)—>1.obj—>连接(link)—>1.exe—>加载(command)—>内存中的程序—>运行(CPU)
debug参数:
R查看、改变CPU寄存器的内容
D查看内存中的内容
E改写内存中的内容
U将机器指令翻译成汇编指令
T执行一条机器指令
A以汇编指令的格式在内存中写入一条机器指令
P自动重复执行循环中的指令,知道(cx)=0为止
g直接执行到执行位置,例如:'g 0016'=CS:0016
assume cs:codesg ;将代码段codesg与cs联系起来
codesg segment ;代表一个段开始
codesg ends ;代表一个段结束
start ends start ;代表程序入口,start是标号
#在程序中的命令返回代码
mov ax,4c00H
int 21H
循环功能的程序框架
mov cx,循环次数
s:
循环执行的程序段
loop s
"ds:" "cs:" "ss:" "es:",在汇编语言中称为段前缀。
dw定义字型数据,即define word
本书约定规章
()表示一个寄存器或一个内存单元中的内容
(ax)表示ax中的内容、(al)表示al中的内容
比如:(ax)表示ax中的内容、(al)表示al中的内容
"()"中的元素有三种类型:1、寄存器名 2、段寄存器名 3、内存单元的物理地址(一个20位数据)
idata表示常量
mov ax,[idata]代表 mov ax,[2]、mov ax,[3]等
mov bx,idata代表 mov bx,1、mov bx,2、mov bx,3等