汇编语言(王爽)
第一章 基础知识
汇编语言的三类指令
(1)汇编指令:机器码的助记符,有对应的机器码
(2)伪指令:没有对应的机器码,由编译器执行,计算机不执行
(3)其它符号,如+,-,*,/等,由编译器识别,没有对应的机器码
指令和数据
在内存或磁盘上,指令和数据没有区别,都是二进制信息
总线
地址总线:一个CPU有N根地址总线,可以说、CPU的地址总线宽度是N,最多可以寻找2的N次方个内存单元
数据总线:宽度决定cpu与外界数据传送速度
控制总线:宽度决定cpu对外部器件的控制能力
第二章 寄存器
CPU的组成
控制器:控制各种器间进行工作
运算器:信息处理
寄存器:进行信息存储
内部总线:连接各种器件,在它们之间进行数据传送
通用寄存器
AX,BX,CX,DX存放一般性的数据,8086cpu寄存器都是16位的,可以存放两个字节(1个字)
AX可以分为AH,AL
8086给出物理地址的方法
(1)在内部用两个16位地址合成的方法形成20位的物理地址
一个称为段地址,一个称为偏移地址
(2)段地址和偏移地址通过内部总线送入地址加法器
(3)地址加法器将两个16位地址合成一个20位的物理地址
物理地址 = 段地址x16+偏移地址
地址加法器将二进制地址左移4位,完成段地址x16
(4)地址加法器通过内部总线将20位物理地址送入 输入输出控制电路
(5)输入输出控制电路将20位物理地址送上地址总线
(6)20位物理地址被地址总线送到存储器
补充:一个x进制的数据左移1位,相当于乘以x
段地址
一个段的起始地址一定是16的倍数,偏移地址16位,代表寻址能力为64KB,故一个段最大长度为64kb
可以用不同的段地址和偏移地址形成同一个物理地址
可以根据需要,将地址连续,起始地址为16的倍数的一组内存单元定义为一个段
CS IP
cs为代码段寄存器,IP为指令指针寄存器
CPU将cs ip指向的内存单元中的内容看作指令,如果说内存中一段信息曾被CPU执行过,
那么,它所在的内存单元必然被cs ip指向过
修改cs ip的指令
mov:传送指令
jump: 用寄存器中的值修改ip
第三章 寄存器(内部访问)
小端模式
高地址内存单元中存放数据高位,低地址存放数据低位
用0,1两个内存单元存放数据4E20H
将起始地址为N的字单元称为N地址字单元,如0地址字单元存放4E20H
1地址字单元存放124EH
段地址和[address]
DS用来存放要访问数据的段地址
mov bx, 1000H
mov ds, bx
mov al, [0]
以上三条指令将10000H(1000:0)中的数据读到al中
[]表示一个内存单元,0表示内存单元的偏移地址,mov ds,1000H是非法的
需要有一个寄存器进行中转
mov bx,1000H
mov ds,bx
mov[0], al
将al中的数据送入内存单元10000H中
mov ax,123BH
mov ds, ax
mov al, 0
add al,[0]
add al,[1]
add al,[2]
累加数据段的前三个单元中的数据
栈
段寄存器SS、寄存器SP存放栈顶位置
push ax指令
sp=sp-2,将ax中内容(2266H)送入ss:sp 指向的内存单元处
栈空时,sp指向栈空间最高地址的下一个单元
pop ax后
pop执行后,2266H任然存在,但是已经不在栈中
push 寄存器 ;将一个寄存器中的数据入栈
pop 寄存器 ;出栈,用一个寄存器接收出栈的数据
在10000H处写入字型数据2266H,不允许 mov 内存单元,寄存器的指令
mov ax, 1000H
mov ss, ax
mov sp, 2
mov ax, 2266H
push ax
第一个汇编程序
;ex1.asm
assume cs:code
code segment
dw 0241h,0242h,0243h,0244h,0245h
start:
mov ax, 0b800h
mov ds, ax
mov bx, 0
mov cx, 5
s: mov ax,cs:[bx]
mov [bx], ax
add bx ,2
loop s
mov ah, 4ch
int 21h
code ends
end start
masm ex1;-->编译源程序
link ex1;-->链接
ex1 -->执行
执行结果
第五章 [bx]和loop指令
[bx]
mov ax, [bx]
将一个内存单元内容送入ax,内存单元长度为2字节,
偏移地址在bx中,段地址在ds中,相当于 (ax)=((ds)*16+(bx))
mov al, [bx]与之类似
mov [bx], ax
将bx中存放的数据作为一个偏移地址EA,段地址SA默认在ds中,将ax中数据送入内存SA:EA处
((ds)*16+(bx))=(ax)
inc
inc bx含义是bx中的内容加1
mov bx, 1
inc bx
执行后bx=2
()
()用于表示一个寄存器或者一个内存单元里的内容
(ax)表示ax中的内容
(20000H)表示内存20000H单元的内容,()中的内存单元地址为物理地址
((ds)*16+(bx))表示:ds中的地址作为段地址add1,bx中的地址为偏移地址add2,
内存add1:add2单元的内容
()中元素可以有三种类型:
- 寄存器名
- 段寄存器名
- 内存单元的物理地址(20位)
idata
用idata表示常量
mov ax, [idata] ;代表mov ax, [1] mov ax, [2]等
mov ax, idata ;代表mov bx, 1 mov bx, 2 等
但是,在汇编源程序中,想在[]里用idata给出内存单元的偏移地址,就要在[]前面显示
给出段地址所在的段寄存器,否则编译器masm将[idata]视为idata
mov al, ds:[0]
mov al, [0] ;等效与mov al, 0
loop指令
loop指令格式是 :loop 标号,CPU执行loop指令时,需要两步操作
- (cx)=(cx)-1
- 判断cx值是否为0,不为0则转至标号处执行,为0则顺序执行
注意:
-
标号代表一个地址,标号所标识的地址要在前面
-
cx中保存循环的次数
example
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
段前缀
用于显式指明内存单元的段地址的 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]
结合idata理解
;example
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 ;(cx)=12,循环12次
s:
mov dl, [bx]
mov es:[bx], dl
inc bx
loop s
mov ax, 4c00h
int 21h
code ends
end
第六章 包含多个段的程序
程序取得空间的方法有两种
- 在加载程序时候为程序分配
- 在执行过程中向系统申请
重点讨论第一种
将数据,代码,栈放入不同的段
原因:
- 将数据,栈,代码放入一个段使程序混乱
- 数据,栈,代码需要的空间可能超过64KB
;example
assume cs:code, ds:data, ss:stack
data segment
dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h
data ends
stack segment
dw 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 ;设置栈顶ss:sp指向stack:20
mov ax, data
mov ds, ax ;ds指向data段
mov bx, 0 ;ds:bx指向data段的第一个单元
mov cx, 8
s:
push [bx]
add bx, 2
loop s ;将data段0-15单元中8个字型数据依次入栈
mov bx, 0
mov cx, 8
s0:
pop [bx]
add bx, 2
loop s0 ;依次出栈8个字型数据到data段的0-15单元中
mov ax, 4c00h
int 21h
code ends
end
(1)定义多个段的方法:对于不同的段,要有不同的段名
(2)对段地址的引用:
在程序中,段名相当于一个标号,代表了段地址
mov ax, data 含义是将名称为data段的段地址送入ax
(3)代码段,数据段,栈段等的命名,是程序员的安排
CPU并不清楚各个段名的意义,data, stack, code 段名只是使程序便于阅读
第七章 更灵活的定位内存地址的方法
and与or指令
通过or指令可以将操作对象的相应位设为0,其它位不变
mov al, 01100011B
;将al第6位设为0:and al, 10111111B
;将al第7位设为0:and al, 01111111B
;将al第0位设为0:and al, 11111110B
通过or指令可以将操作对象相应位设为1
mov al, 01100011B
;将al第6位设为1:or al, 01000000B
;将al第7位设为1:or al, 10000000B
;将al第0位设为1:or al, 00000001B
以字符形式给出的数据
data segment
db 'unix'
db 'foRK'
data ends
上面的源程序中
db 'unix' 相当于 db 75H, 6EH, 49H, 58H
db 'foRK' 相当于 db 66H, 6FH, 52H, 4BH
mov al, 'a' 相当于mov al, 61H,a的ASCII码为61H
大小写转换问题
大写 十六进制 二进制 小写 十六进制 二进制
A 41 01000001 a 61 01100001
B 42 01000010 b 62 01100010
观察发现,小写字母的Ascii码比大写字母的ASCII码值大20H
不能通过加减20H的方法改变大小写,因为这种方法需要判断字符是大写还是小写
观察大小写字母的二进制形式,只有第五位不同,
大写字母第五位为0,小写字母第五位为1,可以用and和or实现
and al, 11011111B ;将第五位置为0,小写字母->大写字母
or al, 00100000B ;将第五位置为1,大写字母->小写字母
assume cs:codesg, ds:datasg
datasg segment
db 'BaSiC'
db 'iNfOrMaTion'
datasg ends
codesg segment
start:
mov ax, datasg
mov ds, ax ;设置ds指向datasg段
mov bx, 0 ;设置(bx)=0,ds:bx指向'BaSiC'的第一个字母
mov cx, 5 ;设置循环次数5,因为'BaSiC'有五个字母
s:
mov al, [bx] ;将ASCII码从ds:bx所指的单元取出
and al, 11011111B ;将al中ASCII码的第五位置位0,变为大写字母
mov [bx], al ;将转变后的ASCII码写回原单元
inc bx
loop s
mov bx, 5 ;设置(bx)=5,ds:bx指向'iNforMaTion'的第一个字母
mov cx, 11 ;'iNforMaTion有11个字母,需要循环11次'
s0:
mov al, [bx]
or al, 00100000B ;将al中ASCII码第五位置位1,变为小写字母
Mov [bx], al
inc bx
loop s0
mov ax, 4c00H
int 21h
codesg ends
end start
[bx+idata]
[bx+idata]表示一个内存单元,它的偏移地址是(bx)+idata (bx中的数值加上idata)
mov ax, [bx+200]含义:将一个内存单元中的内容送入ax,这个内存单元的长度为2个字节,
存放一个字,偏移地址为bx中的数值加上200,段地址在ds中
即:(ax)=((ds)*16+(bx)+200)
该指令也可以写成 mov ax, [200+bx] mov ax, 200[bx] mov ax, [bx].200
[bx+idata]的应用:进行数组的处理
;example
通过比较发现,[bx+idata]的方式为高级语言实现数组提供了便利机制
SI和DI
si和di是8086CPU中和bx功能相近的寄存器,si和di不能分成两个8位寄存器使用
mov bx, 0
mov ax, [bx]
mov si, 0
mov ax, [si]
mov di, 0
mov ax, [di]
以上三组指令实现的功能相同
;用si和di将字符串'welcome to masm'复制到他后面的区域
assume cs:code, ds:data
data segment
db 'welcome to masm!'
db '............... '
data ends
code segment
start
mov ax, data
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
code ends
end start
;使用[bx+idata]方式优化
code segment
start:
mov ax, data
mov ds, ax
mov si, 0
mov cx, 8
s:
mov ax, 0[si]
mov 16[si], ax
add si, 2
loop s
mov ax, 4c00h
int 21h
code ends
end start
[bx+si]和[bx+di]
mov ax, [bx+si]含义如下:
将一个内存单元中的内容送入ax, 这个内存单元偏移地址位bx中的数值加上si中的数值
段地址在ds中
数学表示:(ax)=((ds)*16+(bx)+(si))
也可以写成 mov ax, [bx] [si]
[bx+si+idata]和[bx+di+idata]
mov ax, [bx+si+idata]含义:将一个内存单元的内容送入ax,
偏移地址为(bx)+(si)+idata,段地址在ds中
数学表示:(ax) = ((ds*16)+(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
不同寻址方式的灵活应用
- [idata]用一个常量表示地址,可以用于直接定位一个内存单元
- [bx]用一个变量表示内存地址,可以用于间接定位一个内存单元
- [bx+idata]用一个变量和常量表示地址,可以在一个起始地址的基础上用变量间接定位一个内存单元
- [bx+si]用两个变量表示地址
- [bx+si+idata]用两个变量和一个常量表示地址
;
第八章 数据处理的两个基本问题
基本问题
- 处理的数据在什么地方
- 要处理的数据有多长
定义描述性符号reg和sreg表示寄存器和段寄存器
reg:ax, bx, cx, dx, ah, al, bh, bl, ch, cl, dh, dl, sp, bp, si, di
sreg: ds, ss, cs, es
bx,si, di和bp
(1)8086cpu中,只有这4个寄存器可以用在[]中进行内存单元的寻址
mov ax, [bx]
mov ax, [bx+si]
mov ax, [bx+di]
mov ax, [bp]
mov ax, [bp+si]
mov ax, [bp+di]
(2)在[]中,这4个寄存器只能单个出现或者以4中组合出现
mov ax, [bx+si]
mov ax, [bx+di]
mov ax, [bp+si]
mov ax, [bp+di]
mov ax, [bx+si+idata]
mov ax, [bx+di+idata]
mov ax, [bp+si+idata]
mov ax, [bp+di+idata]
如mov ax, [bx+bp] mov ax, [si+di]是错误的
(3)[bp]的默认段地址在ss中
mov ax, [bp+si+idata] ;含义:(ax)=((ss)*16+(bp)+(si)+idata)
机器指令处理的数据在什么地方
指令执行前,所要处理的数据可以放在三个地方
- CPU内部
- 内存
- 端口
数据位置的表达
(1)立即数idata执行前放在CPU指令缓存器中
mov ax, 1
add bx, 2000h
(2)寄存器,指令要处理的数据在寄存器中
mov ax, bx
mov ds, ax
push bx
mov ds:[0], bx
push ds
mov ss,ax
mov sp, ax
(3)段地址(SA)和偏移地址(EA)
段地址寄存器可以是默认的
;默认在ds中
mov ax, [0]
mov ax, [di]
mov ax, [bx+8]
mov ax, [bx+si]
mov ax, [bx+si+8]
;默认在ss中
mov ax, [bp]
mov ax, [bp+8]
mov ax, [bp+si]
mov ax, [bp+si+8]
也可以显性给出
mov ax, ds:[bp]
mov ax, es:[bx]
mov ax, ss:[bx+si]
mov ax, cs:[bx+si+8]
寻址方式
指令要处理的数据有多长
(1)通过寄存器名指明要处理数据的尺寸
;字操作
mov ax, 1
mov bx, ds:[0]
mov ds, ax
mov ds:[0], ax
inc ax
add ax, 1000
;字节操作
mov al, 1
mov al, bl
----
(2)用x ptr指明内存单元的长度,x可以为word或者byte
mov word ptr ds:[0], 1
mov byte ptr ds:[0], 1
寻址方式的综合应用
div指令
(1)除数:有8位和16位两种,在一个reg或内存单元中
(2)被除数:默认放在AX或者 DX和AX中,如果除数为8位,被除数则位16位,默认在AX中存放
如果除数为16位,被除数则为32位,在DX和AX中存放,DX存高16位,AX存低16位
(3)结果:如果除数为8位,则AL存储除法操作的商,AH存储余数,(AH是AX的高位)
如果除数为16位,AX存放商,DX存储余数
div byte ptr ds:[0]
含义:(al)=(ax)/((ds)*16+0)的商
(ah)=(ax)/((ds)*16+0)的余数
div word ptr es:[0]
含义:(ax)=[(dx)*10000H+(ax)]/((es)*16+0)的商
(dx)=[(dx)*10000H+(ax)]/((es)*16+0)的余数
伪指令dd
db:定义字节型数据 define byte
dw:定义字型数据 define word
dd:定义双字型数据 define dword
dup
dup是一个操作符,如同db,dw,dd等一样,是由编译器识别处理的,用来进行数据的重复
db 3 dup(0)
;定义了3个字节,它们的值都是0,相当于定义了db 0,0,0
db 2 dup(0,1,2)
;定义了9个字节,相当于db 0,1,2,0,1,2,0,1,2
实验1
用汇编指令编码和调试
实验任务2
给出使用d命令查看生产时期的截图给出使用e命令修改生产日期所在内存单元的截图,以及,修改后,再次使用d命令查看的截图结论:能否修改,以及,原因分析
时间并未被修改,因为生产日期在C0000~FFFFF地址范围内,在其中写入数据是无效的,相当于只改写只读存储器中的内容
实验任务3
给出在debug中使用e命令修改内存单元的命令,及,效果截图给出在debug中使用f命令批量填充内存单元的命令,及,效果截图尝试修改内存单元,或,数据后的效果截图
实验任务4
根据在debug环境中调试观察结果,填空并回答问题。填空注*: 以下这段汇编指令代码,是在debug环境中使用a命令输入的汇编指令。debug环境中,默认十六进制。回答问题
[](javascript:void(0)
文章来源: 博客园
- 还没有人评论,欢迎说说您的想法!