第七章:更灵活的定位内存地址的方法
and 和 or 指令:
指令 功能 例 and 将操作对象的相应位设为0,其它位不变 将al的第0位设为0:and al,11111110B or 逻辑或指令,按位进行或运算 将al的第6位设为1:or al,01000000B 使用这两个命令可以帮助我们对内存中的数据按位置 0 或 1.
指明内存单元的另一个方法:[bx + idata]来访问一个内存单元(一个变量一个常量),其代表的是一个偏移地址;这种内存访问方式为高级语言实现数组提供了便利机制。
命令 mov ax,[200 + bx] 的含义为: (ax) = ((ds) * 16 + (bx) + 200)
通用写法:
- mov ax,[bx + 200]
- mov ax,200[bx]
- mov ax,[bx].200
si 和 di 是8086CPU中两个和 bx 功能相近的寄存器, 但不能分为两个 8 为寄存器来使用。
mov bx,0 mov ax,[bx] mov si,0 mov ax,[si] mov di,0 mov ax,[di] mov bx,0 mov ax,[bx + 123] mov si,0 mov ax,[si + 123] mov di,0 mov ax,[di + 123]
上面的代码说明了si 与 di 同 bx 使用上的相同之处。
我们可以使用 [bx (si 或 di)](一个变量) 、[bx (si 或 di) + idata] (一个变量和一个常量)来指明一个内存单元,还有 [bx + si] 、[bx + di] (两个变量)
mov ax,[bx + si] ;(ax) = ((ds) * 16 + (bx) + (si))
常用形式: mov ax,[bx] [si]
更多的内存访问方式:[bx + si + idata] 、[bx + di + idata](两个变量,一个常量)
mov ax,[bx + si + idata] ;(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
将datasg中的每个单词改为大写字母:
;版本1 ;使用 dx 寄存器临时保存 cx 中的值 assume cs:codesg,ds:datasg datasg segment db 'ibm ' db 'dec ' db 'dos ' db 'vax ' datasg ends codesg segment start: mov ax,datasg mov ds,ax mov bx,0 ;此时指向第一行第一个字节的字母 mov cx,4 s0: mov dx,cx ;将 cx 的值暂存到 dx 中 mov si,0 mov cx,3 s: mov al,[bx + si] ;(al) = ((ds) * 16 + (bx) + (si)) and al,11011111b ;将小写字母改为大写字母 mov [bx + si],al ;((ds) * 16 +(bx) + (si)) = (al) inc si ;si + 1 loop s add bx,16 ;00 → 10 → 20 → 30 ... mov cx,dx ;将暂存的 cx 返回 loop s0 ;cx - 1 codesg ends end start
;版本2 ;使用栈空间来暂存数据,前面我们使用的是寄存器,但有些时候寄存器是不足的,不是可取的办法 assume cs:codesg,ds:datasg,ss:stacksg datasg segment db 'ibm ' db 'dec ' db 'dos ' db 'vax ' datasg ends stacksg segnebt dw 0,0,0,0,0,0,0,0,0 ;定义一个段用作栈段,容量为16字节 stacksg ends codesg segment start: mov ax,stacksg mov ss,ax mov sp,16 mov ax,datasg mov ds,ax mov bx,0 ;此时指向第一行第一个字节的字母 mov cx,4 s0: push cx ;将外循环 cx 的值压栈 mov si,0 mov cx,3 ;内循环 cx 的值 s: mov al,[bx + si] ;(al) = ((ds) * 16 + (bx) + (si)) and al,11011111b ;将小写字母改为大写字母 mov [bx + si],al ;((ds) * 16 +(bx) + (si)) = (al) inc si ;si + 1 loop s add bx,16 ;00 → 10 → 20 → 30 ... pop cx ;将暂存的 cx 返回 loop s0 ;cx - 1 mov ax,4c00H int 21H codesg ends end start
在需要暂存数据的时候,一般使用栈来操作
将 datasg 段中每个单词前4个字母改为大写:
assume cs:codesg,ds:datasg,ss:stacksg datasg segment db '1. display ' db '2. brows ' db '3. replace ' db '4. modify ' datasg ends stacksg segnebt dw 0,0,0,0,0,0,0,0,0 ;定义一个段用作栈段,容量为16字节 stacksg ends codesg segment start: mov ax,stacksg mov ss,ax mov sp,16 mov ax,datasg mov ds,ax mov bx,0 ;此时指向第一行第一个字节的字母 mov cx,4 s0: push cx ;将外循环 cx 的值压栈 mov si,0 mov cx,4 ;内循环 cx 的值 s: mov al,[bx + 3 + si] ;(al) = ((ds) * 16 + (bx) + (si) + 3) and al,11011111b ;将小写字母改为大写字母 mov [bx + 3 + si],al ;((ds) * 16 +(bx) + (si) + 3) = (al) inc si ;si + 1 loop s add bx,16 ;00 → 10 → 20 → 30 ... pop cx ;将暂存的 cx 返回 loop s0 ;cx - 1 mov ax,4c00H int 21H codesg ends end start
第八章:数据处理的两个基本问题
描述性符号:
符号 功能 集合 reg 代表一个寄存器 ax、bx、cx、dx、ah、al、bh、bl、ch、cl、dh、dl、sp、bp、si、di sreg 代表一个段寄存器 ds、ss、cs、es 在8086CPU中,只有bx 、si、di、bp四个寄存器可以用在 […] 中来进行内存单元的寻址。
在 […] 中,这四个寄存器可以单个出现,或者以4种组合出现:bx和si、bx和di、bp和si、bp和di
mov ax,[bx] mov ax,[si] mov ax,[di] mov ax,[bp] 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]
使用寄存器 bp 进行寻址,如果没有显性的给出段地址,那么段地址默认在 ss 段寄存器中
mov ax,[bp] ;(ax) = ((ss) * 16 + (bp)) mov ax,[bp + si + idata] ;(ax) = ((ss) * 16 + (bp) + (si) + idata)
处理指令大致分为3类:读取、写入、运算;指令执行前,数据一般在3个地方:CPU内部、内存、端口
数据的位置:
立即数(idata):在汇编指令中字节给出
mov ax,1 add bx,2000H
寄存器:要处理的数据在寄存器中,在汇编指令中给出相应的寄存器名就可以。
段地址(SA)和偏移地址(EA):指令要处理的数据在内存中,使用段地址+偏移地址定位内存单元。
寻址方式总结:
寻址方式 含义 名称 常用格式 [idata] EA = idata;SA = (ds) 直接寻址 [idata] [bx] EA = (bx);SA = (ds) 寄存器间接寻址 [bx] [si] EA = (si) ;SA = (ds) [di] EA = (di);SA = (ds) [bp] EA = (bp);SA = (ds) [bx + idata] EA = (bx) + idata;SA = (ds) 寄存器相对寻址 用于结构体:[bx].idata [si + idata] EA = (si) + idata;SA = (ds) 用于数组:idata[si],idata[di] [di + idata] EA = (di) + idata;SA = (ds) 用于二维数组:[bx] [idata] [bp + idata] EA = (bp) + idata;SA = (ss) [bx + si] EA = (bx) + (si);SA = (ds) 基址变址寻址 用于二维数组:[bx] [si] [bx + di] EA = (bx) + (di);SA = (ds) [bp + si] EA = (bp) + (si);SA = (ss) [bp + di] EA = (bp) + (di);SA = (ss) [bx + si + idata] EA = (bx) + (si) + idata;SA = (ds) 相对基址变址寻址 用于表格中的数组:[bx].idata[si] [bx + di + idata] EA = (bx) + (di) + idata;SA = (ds) [bp + si + idata] EA = (bp) + (si) + idata;SA = (ss) 用于二维数组:idata[bx] [si] [bp + di + idata] EA = (bp) + (di) + idata;SA = (ss) 8086CPU 可以处理 byte 和 word 两种尺寸的数据。
通过寄存器名指明要处理的数据的尺寸:
;字操作 mov ax,1 add ax,1000 inc ax
;字节操作 mov al add al000 inc al
没有寄存器名存在得情况下,用操作符 X ptr 指明内存单元的长度,X 可以为 byte 或 word:
;word ptr 表示访问一个字单元 mov word ptr ds:[0],1 inc word ptr [bx] inc word ptr ds:[0] add word ptr [bx],2
;byte ptr 表示访问一个字节单元 mov byte ptr ds:[0],1 inc byte ptr [bx] inc byte ptr ds:[0] add byte ptr [bx],2
在机器指令中指明要访问的是字单元还是字节单元是非常重要的,8086CPU中,一个byte为8bit,而一个字为2byte,如果不指明,那么在访问的时候是有区别的。
使用 push 和 pop 指令就代表了是进行字操作,无需指明
div 除法指令:
(1)除数:8位和16位两种,在一个reg或内存单元中
(2)被除数:默认在AX或AX和DX中,除数位8位,被除数位16位,默认在AX中存放;除数位16位,则被除数位32位,DX存放高16位,AX存放低16位。
(3)结果:如果除数位8位,则AL存储除法操作的商,AH存放余数;如果除数位16位,AX存储除法操作的商,DX存放余数
;格式 div reg div 内存单元
;示例 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]的余数 div byte ptr [bx + si + 8] ;(al) = (ax) / ((ds) * 16 + (bx) + (si) + 8)的商 ;(ah) = (ax) / ((ds) * 16 + (bx) + (si) + 8)的余数 div word ptr [bx + si + 8] ;(ax) = [(dx) * 10000H + (ax) / (ds) * 16 + (si) + 8]的商 ;(dx) = [(dx) * 10000H + (ax) / (ds) * 16 + (si) + 8]的余数js
计算:100001 / 100
;100001 > 65535,所以除数得是32位,转为16进制为 186A1H,所以高16位在DX中,低16位在AX中 mov dx,1 mov ax,86A1H mov bx,100 div bx
伪指令 dd 、dup
指令 功能 示例 db 定义字节型数据 db 1 dw 定义字型数据 dw 100 dd 定义dword双字型数据 dd 100001 dup 与上面3个指令配合使用,用来进行数据的重复 db(dw、dd) 重复次数 dup (重复的数据)
汇编语言实验七:
;题目太长,只贴代码,先思考写代码,而后体会别人的代码 assume cs:code data segment db '1975','1976','1977','1978','1979','1980','1981','1982','1983','1984','1985' db '1986','1987','1988','1989','1990','1991','1992','1993','1994','1995' dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514,345980 dd 590827,803530,1183000,1843000,2758000,3753000,4649000,5937000 dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226 dw 11542,14430,15257,17800 data ends stack segment dw 0,0,0,0,0,0,0 stack ends table segment db 21 dup ('year summ ne ?? ') table ends code segment start: mov ax,data mov es,ax mov si,0 mov ax,table mov ds,ax mov di,0 mov ax,stack mov ss,ax mov sp,16 mov bx,0 mov cx,21 s0: mov ax,es:[si] mov ds:[di+bx],ax add si,2 add di,2 mov ax,es:[si] mov ds:[di+bx],ax mov ax,es:[si+82] mov ds:[bx+di+3],ax mov ax,es:[si+84] mov ds:[bx+di+5],ax add si,2 mov di,0 add bx,16 loop s0 mov bx,0 mov si,168 mov di,5 mov cx,21 s1: mov ax,es:[si] mov ds:[bx+di+5],ax mov ax,ds:[bx+di] mov dx,ds:[bx+di+2] div word ptr ds:[bx+di+5] mov ds:[bx+di+8],ax add si,2 add bx,16 loop s1 mov ax,4c00h int 21h code ends end start
第九章:转移指令的原理
可以修改 IP ,或者同时修改 CS 和 IP 得指令统称为转移指令。
- 段内转移:jmp ax
- 短转移IP修改范围:-128~127
- 近转移IP修改范围:-32768~32767
- 段间转移:jmp 1000:0
- 段内转移:jmp ax
转移指令分为无条件转移指令(jmp)和条件转移指令、循环指令、过程、中断等等
操作符 offset ,能够取得标号的偏移地址:
start: mov ax,offset start ;取得start的偏移地址,相当于 mov ax,0