汇编语言(王爽)第三版-读书笔记

文章目录

Central Processing Unit 中央处理单元 简称CPU

汇编语言的组成
  • 汇编指令:机器码助记符 有对应机器码

  • 伪指令:没有对应机器码 由编译器执行 计算机不执行

  • 其他符号:如+ - * / 等 又编译器识别 计算机不执行

  • 存储器 = 128存储单元 = 128Byte

  • Byte = 8bit =一个内存单元

  • 1bit = 1个二进制位 一个bit只能表示一个开关量,例如 l代表“开关闭合”,0代表“开关断开”。

Cpu对存储器的读写
  • 存储单元信息(地址信息)
  • 器件选择 读或写的命令(控制信息)
  • 读或写的数据(数据信息)
CPU与其他存储芯片的导线-总线
  • 地址总线 发出地址信息

  • 控制总线 发出内存读命令 选中存储芯片 通知他从中读取数据

  • 数据总线 将读取到的数据送入CPU

  • 地址总线 一根线表示一位 N根表示地址总线宽度为N,寻址范围为0-2^N。

  • 数据总线 总线宽度决定了传输的速度,8根表示一个1Byte,16根表示2Byte.

  • 控制总线 总线宽度决定了CUP对外部器件的控制能力

存储器
  • 随机存储器
  • 只读存储器
  • BOIS(Basic Input?output System,基本输入/输出系统)
  • 接口卡上的RAM
内存地址空间 书中11页 作者对其作出了很好的解释

CUP通过总线于各个物理存储器相连,将他们看作一个个存储器单元,他们分别占据一个地址段,CUP在这段地址空间读写数据 实际上就是在相应的物理存储器上读写数据。而汇编语言就是面对的内存地址空间。

寄存器

CPU内部

  • 运算器:进行信息计算
  • 寄存器:进行信息存储
  • 控制器:控制各器件进行工作
  • 内部总线:连接各个器件 在他们工作时进行数据传递
通用寄存器 兼容8位寄存器
  • AX : 分AH和AL 书中16页介绍了 AX 20000(4E20) AH 78(4E) AL 32(20)
  • BX : 分BH和BL
  • CX : 分CH和CL
  • DX : 分DH和DL
  • ADD AX ,BX AX为16位 最多存4位16进制 最高位去掉 即0000H
  • ADD AL ,BL AL为8位 最多存2位16进制 最高位去掉 00H
注意:
  • MOV AX,BL
  • 8位寄存器与16位寄存器之间传递数据
  • MOV BH,AX
  • 16位寄存器与8位寄存器之间传递数据
  • MOV AL,2000
  • 8位最多存储255的数值
  • MOV AL,100H
  • 不要将一个高于8位的数加到8位寄存器
物理地址

内存单元位线性空间 拥有唯一地址 即为物理地址

16位寄存器
  • 运算器一次处理16位数据
  • 寄存器最大宽度16位
  • 寄存器和运算器通路位16位
8086CPU的物理地址
  • 提供两个16位地址,段地址 偏移地址
  • 通过内部总线送入地址假发器
  • 地址加法器合成为一个20位物理地址
  • 地址加法器送入BIOS
  • BOIS将20位物理地址送入地址总线
  • 地址总线送到储存器

物理地址 = 段地址 * 16 + 偏移地址

下图对物理地址左移做了很详细的总结

物理地址的计算

那么 16位地址 * 16 其实是乘10H,也可以说是16位地址转成10进制成16。最简单的方式就是16位地址左移1位

本质:基础地址+偏移地址  = 物理地址
      基础地址 = 段地址 * 16

书中22对其本质做出了生动的解释

段的概念 书中24页
  • 段地址 大小一定为16的倍数
  • 偏移地址最大为16位 则最大寻址能力为64kb
段寄存器 书中25页
  • 8086CPU提供四个段寄存器CS DS SS ES
  • CS 代码段寄存器
  • IP 指令指针寄存器
书中28页将CS配合IP寻址 查询 的具体图解

当进入指令缓冲器 即为读取一条指令 IP 值自动增加并读取下一条指令根据第一条指令的大小增加值

复位或者加电启动 CS=FFFFH IP=0000H FFFF0H即为8086PC启动执行的第一条指令

转移指令 修改CS IP 的值
  • jmp段地址:偏移地址
  • jmp 3:0B16 CS= 0003H IP= 0B16H 即从00B46H读取指令
  • jmp 2AE3:3 CS= 2AE3 IP= 0003H 即从2AE33H读取指令
  • jmp bx 等同于 mov IP,ax CS不变 BX赋值给IP
书中34对jmp做了图文并茂的解释

jmp 1000:3 就是跳转到1000*16+0003H = 10003H处读取指令。

实验一:CPU 的debug使用

先找个引路篇-安转篇https://blog.youkuaiyun.com/try2find/article/details/39619999

以及debug的命令行使用 https://blog.youkuaiyun.com/liuer2004_82/article/details/52638516#t2

第三章

内存中字的存储
  • 字单元:存放一个字型数据(16位)的内存单元,两个连续的内存单元
  • 高地址存字型数据的高位字节 低地址存字型数据的地位字节
  • 地址单元和地址子单元:每个地址单元包含一个高位字节单元和一个低位字节单元
DS和[address]书中48页
  • mov al,[0] 表示: mov 寄存器名,内存单元地址
  • […] 表示内存单元 […]中的0表示内存单元的偏移地址。
  • mov ds,bx ds会被默认取值 作为段地址 所以在调用mov al,[0]前必须给值ds.
  • mov ds,1000H 为非法操作 数据不能直接送入段寄存器 即ds 需要先mov bx,1000H
字的传送

书中51页对字的传送做了图文并茂的介绍 表3.2

mov add sub
  • mov 寄存器,数据 mov ax,8
  • mov 寄存器,寄存器 mov ax,bx
  • mov 寄存器,内存单元 mov ax,[0]
  • mov 内存单元,寄存器 mov [],ax
  • mov 段寄存器,寄存器 mov ds,ax
书中52验证了mov [0] cs

我学会debug指令 r,t,d查看 a修改

debug

数据段

数据段:将一组长度为N(N<=64KB),地址连续,起始地址为16的倍数的内存单元当做专门存储数据的内存空间,即为数据段。

各寄存器初始值:CS = 2000H,IP = 0,DS = 1000H,AX = 0,BX = 0;

栈 后进先出
CPU 栈机制
  • 如何确定栈空间?
  • 如何确定栈顶?
  • 段寄存器SS 存放栈顶栈地址
  • 寄存器SP 存放偏移地址
  • 任意时刻 SS:SP指向栈顶 SS 为段寄存器 SP为 偏移地址
栈超界

栈满push 栈空pop都会造成栈超界问题
8086CPU并没有给出解决方法 需自己注意

push 和 pop 指令 书中64页
//基本操作
mov ax,1000H
mov ss,ax   ;设置栈的段地址 值必须由ax传递
push [0]    ;1000:0处的字压入栈中 
pop [2]     ;出栈 出栈的数据送入1000:2
书中65页问题3.8将栈操作写的很完善

push时:CPU操作:先改变SP:SP-2 后向SS:SP处传送

pop时: CPU操作:先读取SS:SP处的数据,后改变SP :SP+2

栈段
  • DS 内存区间 段地址
  • CS 代码段 段地址
  • SS 栈 段地址
-r ds
:1000
-d ds:0 ;查看1000:0开始的内存区间 128字节
 
-r ds 
:1000
-d ds:10 18 ;查看10000~1000:18 中的内容

-d cs:0     ;查看当前代码段中的指令代码

-d ss:0     ;查看当前栈中的内容

E 直接修改内存
U 显示代码 以汇编的形式
A 汇编指令修改内存

-r ds 
:1000
-e ds:0 11 12 33 11 44 ;在从1000:0开始的内存区间写入数据

-u cs:0  ;以汇编指令的形式,显示代码段中的代码,0是段地址

-r ds 
:1000
-a ds:0     ;以汇编指令的形式,向1000:0开始的内存单元写入指令

代码练习 书中78页
assume cs:codesg;‘假设’编译程序将段寄存器和某个具体的段相关联
codesg segment  ;与codesg ends 成对使用 定义一个代码段,名称为‘codesg’
		mov ax,0123H
		mov bx,0456H
		add ax,bx
		add ax,ax
		
		mov ax,4c00H
		int 21H
codesg ends

end ;汇编程序结束的标志
		
段结束 程序结束 程序返回
目的相关指令指令性质指令执行者
通知编译器一个段结束段名 ends伪指令编译时 编译器
通知编译器程序结束end伪指令编译时 编译器
程序返回mov ax,4c00H int 21H汇编指令编译时 由CPU行
编译asm文件过程 书中 第84页

首先需要一套工具 这边提供下载链接 https://blog.youkuaiyun.com/wybliw/article/details/53259602

设置完环境后打开dosbox 输入Masm 输入文件路径 一路enter会得到 .OBJ后缀文件 而实际上会得到三个文件

  • 目标文件 obj
  • 列表文件 lst
  • 交叉引用文件 crf
连接 obj -> exe

输入Link 然后输入文件名 一路enter 生成 exe

简化编译连接 注意加上 ‘;’。
  • masm newone.asm;
  • link newone.obj;
cmd命令行是怎么执行程序的 书中90页

cmd命令行是怎么运行的

关于DOSBOX的快捷键操作

https://blog.youkuaiyun.com/lovefengruoqing/article/details/79009456

DEBUG追踪exe执行过程
  • debug newone.exe 可跟踪执行
  • CX 指定机器码字节数
  • PSP 为DOS创建程序前缀 与被加载程序通信 256字节
  • 可用段地址为 PSP 段地址+偏移地址为 SA+10H:0
  • U命令查看汇编命令
  • int 21H 使用 P命令结束

第五章 [BX]和loop命令

[bx]和内存单元
  • [bx]同样表示一个内存单元 偏移地址为bx
  • loop 循环指令
  • ‘()’表示一个寄存器或者一个内存单元中的内容
  • ‘()’可用元素的种类型 1 寄存器 2 段寄存器 3 内存单元物理地址(20位)
  • ‘()’表示两种数据类型 1 字型 2字节型 (al) = (20000H)取字节型 (ax)=(20000H) 20000H取字型
    -idata 表示常量
Loop循环
mov cx ,循环次数
s: 
    循环执行程序块
    loop s
通过编译连接的asm文件 对[]偏移地址的解释错误

使用[bx]间接传递偏移地址

mov ax , 2000H
mov ds , ax         ;段地址2000H送入ds
mov bx , 0          ;偏移地址0送入bx
mov al , [bx]       ;ds:bx的送入al

或者

mov al , ds:[0]
段前缀
  • ds 默认段地址
  • cs 代码段地址
  • ss 栈段地址
  • es 显存段地址
书中120页给出了loop循环很好的实例

第六章 包含多个段的程序

在代码中使用数据
assume cs:code   ;与系统cs相关联 固定写法
code segment    ;代码段 名称 'code' 
    dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h  ;声明字型数据
    mov bx,0                ;赋值语句
    mov ax,0                ;赋值语句
    mov cx,8                ;循环8次
s:add ax,cs:[bx]            ;(ax) =((cs*16)+bx)
    add bx,2                ;偏移地址+2
    loop s                  
    
    mov ax,4c00h            ;类似于return 固定写法
    int 21h
code ends

end                         ; 代码段结束标记  固定写法
标记程序开始部分 类似于smali语言中的 .prologue 书中126页
assume cs:code   ;与系统cs相关联 固定写法
code segment    ;代码段 名称 'code' 
      dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h  ;声明字型数据
      start:  mov bx,0                ;赋值语句
              mov ax,0                ;赋值语句
              mov cx,8                ;循环8次
          s:add ax,cs:[bx]            ;(ax) =((cs*16)+bx)
              add bx,2                ;偏移地址+2
              loop s                  
            
              mov ax,4c00h            ;类似于return 固定写法
              int 21h
          code ends
end start                    ; 代码段结束标记  固定写法
自定义段 书中130页
  • 一个段的数据段可以由段名表示,如:data:6表示’0cbah’
  • 这也是JAVA思想的雏形啊
  • CPU 将 cs指向code ds指向data ss指向stack(可以这样理解)
assume cs:code,ds:data,ss:stack			;关联系统段地址 :固定写法

data segment		;自定义段 data
	dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h			;16字节内存单元
data ends			;段结束

stack segment			;	自定义栈 stack
	dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0					;16字节内存单元
stack ends			;段结束

code segment		;自定义代码段  code

start:mov ax,stack							;start 开始标记  stack段地址给寄存器ax
	  mov ss,ax								;ax赋给栈段地址	
	  mov sp,20h							;设置栈顶ss:sp 指向stack:20
	  
	  mov ax,data							;data段地址给寄存器ax
	  mov ds,ax								;ds 指向 data 
	  
	  mov cx,8								;设置循环次数8
	s:push [bx]								;将ds:[bx]的值入栈,也就是data:0~1516个字型数据
	  add bx,2								;bx 自加 2
	  loop 									;循环标志 固定写法
	  
	  mov bx,0								;将bx置空
	  
	  mov cx,8								;循环标志 固定写法
	s0:pop [bx]								;将栈stack依次出栈 赋值给 ds:[bx]
	  add bx,2								;bx 自加 2
	  loop s0								;循环标志 固定写法
	  
	  mov ax,4c00h							;结束标志 固定写法
	  int 21h
code ends								;代码段结束标记

end start								;程序结束标记

第七章

or and 指令

and: 与指令 同为1 则为1
or : 只要有1 则为1

mov al 1100100B
add al 1000000B
//执行和结果为  10000000B
mov al 1000111B
or  al 1110111B
//执行结果为 1110111B
书中139页介绍了ASCII从键盘到显示器过程

ASCII码表 http://ascii.911cha.com/

字符串数据

这边先介绍一下 db dw dd

  • db定义字节类型变量,一个字节数据占1个字节单元,读完一个,偏移量加1
  • dw定义字类型变量,一个字数据占2个字节单元,读完一个,偏移量加2
  • dd定义双字类型变量,一个双字数据占4个字节单元,读完一个,偏移量加4
转换大小写案例

这里有意思的是

  • 大写转小写 即 and 11011111B 也就是-20H 也就是第五位置0 大写转大写无变化
  • 小写转大写 即 and 00100000B 也就是+20H 也就是第五位置1 小写转小写无变化
assume cs:codesg,ds:datasg

datasg segment
	db 'BaSiC'
	db 'iNfOrMaTiOn'
datasg ends

codesg segment
	start:mov ax,datasg
		  mov ds,ax
		  mov bx,0
		  mov cx,5
		s:mov al,[bx]			;取出ds:bx所指的第一个字母
			 and al,11011111B   ;将第五位置0 就是-20H 
			 mov [bx],al		;将值返还给ds:[bx]
			 inc bx
		 loop s			 
		  mov bx,5				;从ds:5开始指向information的第一个字母	
		  mov cx,11
		s0:mov al,[bx]			
			   or al,00100000B  ;将第五位置1 就是+20H 
			   mov [bx],al
			   inc bx
	    loop s0			  
		  mov ax,4c00h
		  int 21h
		  
codesg ends
end start
[bx+idata]进行数组处理 简化上面的程序
mov al,[5+bx]
mov [5+bx],al
  • C语言:a[i],b[i]
  • 汇编语言:0[bx],5[bx] 那这里的 0 5 代表的就是 数组的首地址了 而c语言用数组名表示
SI DI 与bx功能相似 不能分成两个8位寄存器 书中147页
mov ax ,[bx][si] 
//等同于
mov ax,[bx=si]
//描述为
(ax) = (ds*16 + bx + si)
寻址方式的灵活运用 书中152页
  • [idata]
  • [bx]
  • [bx+idata]
  • [bx+si]
  • [bx+si+idata]
为了培养我们汇编中数组的概念 书中153页

定义如下数据段 6个字符串 等同于下图 的一个

datasg segment;注意每个字符被扩展为16位
    db  '0. file        '
    db  '1. edit        '
    db  '2. search      '
    db  '3. veiw        '
    db  '4. options     '
    db  '5. helps       '
datasg ends

数组

循环嵌套

最终:是将datasg数组中的数据全部大写

datasg segment;注意每个字符被扩展为16位
    db  'fil              '
    db  'edi              '
    db  'sea              '
    db  'vei              '
datasg ends

mov ax,datasg
mov ds,ax
mov bx,0        ;ds:bx 指向datasg
mov cx,4        ;循环次数4行

s0:mov si,0     ;外层循环 第一行 当si=3退出循环
   mov cx,3     ;循环次数3列
  
   s: mov al,[bx+si]    ;内层循环 ds:[bx][si]  理解为 第bx行 第si列
    and al,11011111b    ;第五位置0 也就是-20H 也就是 小写 -> 大写
    mov [bx+si],al      ;
    inc si              si加1
    loop s
    
add bx,16               ;外层循环 +16+10H 即换行
loop s0
改进之-栈存储数据(节省寄存器的使用数量)

用栈的方式暂时保存cx的值

datasg segment;注意每个字符被扩展为16位
    db  'fil              '
    db  'edi              '
    db  'sea              '
    db  'vei              '
datasg ends

stacksg segment
	dw 0,0,0,0,0,0,0,0
stacksg segment

codesg segment
	start:mov ax ,stacksg		
		  mov ss,ax
		  mov sp,16				;ss:sp 指向 stacksg:10H
		  mov ax,datasg			
		  mov ds,ax
		  mov bx,0				;ds:bx 指向 datasg:0
		  mov cx,4					
	s0:	  push cx				;入栈 cx 
		  mov si,0				
		  mov cx,3
	
	s:	  mov al,[bx+si]		;(al) = (ds*16+bx+si) 也就是[bx][si] bx行si列
		  and al,11011111b		;改成大写
		  mov [bx+si],al		;(ds*16+bx+si) = (al)
		  inc si				;si自加1
		  loop s
		  
		  add bx,16				;换行
		  pop cx				;出栈 给cx
		  loop s0
		  
		  mov ax,4c00h
		  int 21h

codesg ends

end start

第八章 数据处理

  • 处理数据在什么地方?

  • 要处理的数据有多长?

  • reg:寄存器 ax bx cx dx al ah bl bh cl ch dl dh cl ch sp bp si di

  • sreg:段寄存器 ds cs ss es

使用规则 mov ax […]
  • bx si bp di 可用于内存单元寻址
  • bx si bp di 只能单独出现,且 bx与bp,si与di不能组合使用
  • []中使用bp 默认使用段寄存器 ss 即 ss*16+bp
机器指令处理的数据在什么地方
机器码汇编指令指令执行前数据的位置
8E1E0000mov bx,[0]内存,ds:0单元
89c3mov bx,axCPU内部,ax寄存器
BB0100mov bx,1CPU内部,指令缓冲器
数据位置的表达
  • 立即数(idata)
  • 寄存器
  • 段地址(SA)和偏移地址(EA)
  • 存放段地址的寄存器 如:mov ax ds:[bx]
寻址方式

寻址方式

指令处理的数据长度
  • ax 字单元
  • al 字节单元
  • mov byte ptr ds:[0],1 字节单元
  • mov word ptr ds:[0],1 字单元
  • [bx].idata [bx].idata[si] bx定位结构体 idata定位数据项 si定位数组元素 (书中169页)
div指令 除法指令
  • 除数:8位和16位 在一个reg或内存单元中
  • 除号前面位被除数,后面为除数
  • 被除数:默认放在AX或者DX和AX中 除数8位则被除数16位 除数16位则被除数32位,由DX(高16位)和AX存放(底16位)
  • 结果:除数8位 AL存商 AH存余数 除数16位 AX存商 DX存余数

实例

div word ptr es:[0]
(ax) = [(dx)*10000H+(ax)]/((es)*16+0)的商   //注意这里是10000H
(dx) = [(dx)*10000H+(ax)]/((es)*16+0)的余数

div byte ptr ds:[0]
(al) = (ax)/((ds)*16+0)的商
(ah) = (ax)/((ds)*16+0)的余数

实例计算 100001/100

mov dx,1
mov ax,86a1h
mov bx,100
div bx

根据公式一:
100 = 64H

  • (ax) = [1*10000H+ax]/100 = 186a1H/64H = 03e8h
  • (dx) = [1*10000H+ax]%100 = 186a1H%64H = 1
伪指令dd ((dword)double word 双字型 即四个字节 32位)
  • db 00H 2个8位
  • dw 0000H 2个byte
  • dd 00000000H 2个word
dup(两份) 配合dd 进行数据重复 书中172页
  • db 3 dup (0) == db 0,0,0
试验七 书中173页 请认真完成!!

答案地址 https://blog.youkuaiyun.com/love_jing_forever/article/details/53204818

第九章 转移指令原理

定义:可以修改IP,或同事修改CS和IP的指令统称为转移指令。

转移行为:

  • 段内转移 只修改IP jmp ax
  • 段间转移 同事修改 CS IP jmp 1000:0

对IP修改范围不同

  • 短转移 对IP修改范围 -128~127
  • 近转移 对IP修改范围 -32768~32767

指令分类

  • 无条件转移指令 jmp
  • 条件转移指令
  • 循环指令 loop
  • 过程
  • 中断
操作符 offset 获取标号的偏移地址
start:mov ax ,offset start  ;相当于mov ax ,0
    s:mov ax ,offset s      ;相当于 mov ax s

jmp指令并不需要转移的目的地址 只需要转移的位移

assume cs:codesg
codesg segment

	start:mov ax,0      
		  jmp short s ;short 表示范围在 -128~127之间 即‘段内短转移’ 
		  add ax,1  
		 s:inc ax 
codesg ends

end start
我们先复习一下补码 书中329页
  • 8位无符号数 00000000B~11111111F 共255个数

  • 8位有符号数

  • 10000000B~11111111F -127~127 共254个数 注意:10000000B不算-0没有意义

  • 最高位为1 表示负数

  • 整数补码取反加1或,为其对应负数的补码,负数补码取反后加1,位其绝对值

  • 1的补码:11111111B,表示-1

  • -1的补码:00000001B,其绝对值为1.

  • 补码计算法定义:非负数的补码是其原码本身;负数的补码是其绝对值的原码最高位符号位不变,其它位取反,再加1。

  • 模定义:一个负整数与其补码的和。

整理下对补码的知识

求0-9的补码
0000H-0009H
因为指定jmp short 转成二进制:
00000000B-00001001B 
应为没有减法器 所以理解为加法
00000000B+(-00001001B) 
也就是求-9(10001001B)的补码
10001001B 取反且首位不变(11110111B)
即F7H
  • jmp near ptr 标号(IP) = (IP)+16位位移
  • jmp far ptr 标号 段间转移 远转移 (CS)=标号所在段的段地址;(IP)=标号在段中的偏移地址
转移地址在内存中的jmp指令
  • jmp word ptr ds:[0] 将内存单元中的存放的赋给 偏移地址 (IP) = (ds:[0])
  • jmp word ptr [bx]将内存单元中的存放的赋给 偏移地址 (IP) = ([bx])
  • jump dword ptr 内存单元地址(段地址) (CS) = (内存单元地址+2) (IP) = (内存单元地址)
mov ax ,0123H
mov ds:[0],ax
mov word ptr ds:[2],0
jmp dword ptr ds:[0]
//(CS)=0,(IP)=0123H,CS:IP指向0000:0123  
jcxz指令 条件转移指令(短指令)

JCXZ CX: 为零时转移

格式:jcxz 标号
用高级语言描述:if (cx == 0) 
                 jmp short 标号     ;------> 当cx= 0 转移到标号处
            else
                next                ;------>当cx != 0 程序什么也不做,继续向下执行
(短转移,通过位移进行转移,IP修改范围-128127
loop指令前面讲过了
书中189页 试验8.9部分以后再看

第十章 Call和Ret指令

  • call和ret都是转移指令 都可修改IP 或同时修改CS和IP
  • ret 修改IP 实现近转移 相当于 pop IP
  • retf 修改SS和IP 实现远转移 相当于 POP IP POP CS

实例:

assume cs:codesg
stack segment
db 16 dup (0)  ;申请一个16字节的空间
stack ends

code segment
	mov ax,4c00h
	int 12h
start: mov ax,stack     ;代码开始部分
	   mov ss,ax        
	   mov sp,16
	   mov ax,0
	   push cs      
	   push ax
	   mov bx,0
	   retf        cs:IP指向 cs:ax即 cs:0000H;代码第一行mov ax,4c00h
code ends

end start
Call指令
  • 将当前IP或者CS和IP压入栈中
  • 转移
  • 不能实现短转移 范围2个有符号字节

call 标号

汇编表示: 入栈IP后

(SP) = (SP)-2
((ss)*16+(sp))=(IP)  将IP对应的值存入ss:sp中  sp偏移向上2个字节
(IP)=(IP)+16
push IP     
jmp near ptr 标号    ;对应(IP)=(IP)+16  就是跳过一行
```c
cal far ptr 标号 段间转移
汇编表示:入栈CS 和 IP后

push CS
push IP
jmp far ptr 标号 cs:ip指向 标号处


call 16位reg
汇编表示:

```c
push IP
jmp 16位 reg   ; 直接跳转到ss:reg位置

call word ptr
汇编表示: 跳转到 标号处 SP+2

push IP
jmp word ptr 内存单元地址

call dword ptr
汇编表示: 跳转到 标号处 SP+4

push CS
push IP
jmp word ptr 内存单元地址
call 和 ret 配合使用
  • 子程序:具有一定功能的程序。
  • call:调用子程序 并将IP压入栈中
    ret:返回子程序 POP IP弹栈后 调用cs:ip处的代码 即 call的下一行。
mul指令 乘法指令
  • 想成两数 必须同时为16位或8位
  • 8位时 一个默认放在AL中,另外一个放在8位reg或者内存单元中
  • 16位 一个默认放在AX中,另一个放在16位reg或者内存单元中

对于结果:

  • 8位乘法 结果默认放在AX中
  • 16位乘法 结果高位放在DX,低位放在AX中
mul byte ptr ds:[0]
;含义: (ax) = (al)*((ds)*16+0;   (ax) = (al)*(ds:bx)

mul word ptr [bx+si+8]
;(ax) = (ax)*((ds)*16+(bx)+si+8)结果的底16;(dx) = (ax)*((ds)*16+(bx)+si+8)结果的高16位

计算100*10
分析:10010 都小于255  可以做8位乘法
 mov al ,100
 mov bl ,10
 mul bl
 结果:(ax) = al*bl = 1000 = 0e38H
 
 计算10000*100
 100小于25510000大于255,所以做16位乘法
 
 mov ax,100
 mov bx,10000
 mul bx
 
 结果: ax*bx = F4240 = 11110100001001000000 =
 1111 0100 0010 0100 0000
 (ax) = 4240H dx = 0000 0000 0000 1111 = 0000FH
参数和结果的传递

子程序:也就java中的Util方法 或者普通方法

assume cs:code
	data segment
	 dw 1,2,3,4,5,6,7,8       ;这边申请的是word 也就是一个字16位
	 dd 0,0,0,0,0,0,0,0		  ;这边申请的是dword 也就是一个双字32位
	data ends

code segment

	start:mov ax,data
		  mov ds,ax
		  mov si,0				;ds:si 指向第一组word单元
		  mov di,16				;ds:di 指向第二组的dword单元
								;si di等同于bx  不能分成两个8位
		  mov cx 8				;循环8次
		s:mov bx,[si] 			;(bx) =[ds:si]=([ds:0])=1
		  call cube				;假设bx = 8 
		  mov [di],ax			;([di]) = 0040H  	低16位
		  mov [di].2,ax			;向后移动2个字节  高16位	
		  add si,2				;ds:si 指向下一个word单元
		  add di,4				;ds:di 指向下一个dword单元
		  loop s
		  
		  mov ax,4c00h
		  int 21h
	
	cube:mov ax,bx			;该方法被调用 因为bx小于255(FF) 用8位
		 mul bx				;(ax) = (al)*(bx) = 0*H*0008H =8 * 8 = 64 =0040H
		 mul bx				;(ax) = (al)*(bx) = 04H*0040H =4 * 64 = 64 =0100H
		 ret				;返回调用处的下一行,即:mov [di],ax

code ends

end start
批量数据传递

作者告诉我们为什么定义字符串使用 db https://blog.youkuaiyun.com/weixin_42553435/article/details/80919722

assume cs:code
	data segment
	  db 'conversation'  
	  ;存的byte字节 是顺序的 
	  ;可以理解成 db 'c','o','n','v','e','r','s','a','t','i','o','n' 总共12个字符
	data ends

code segment
	start:mov ax,data
	mov ds,ax
	mov si,0
	mov cx,12
	call capital
	mov ax,4c00h
	int 21h
	
	capital:and byte ptr [si],11011111B
			inc si
			loop capital
			ret
	
code ends

end start

编写子程序的标准

子程序开始: 子程序中使用的寄存器入栈
            子程序内容
            子程序中使用的寄存器出栈
            返回(ret,retf)
试验10-编写子程序 书中206页

第十一章 标志寄存器

  • 存储相关指令的执行结果
  • 位CPU执行指令提供行为依据
  • 控制CUP的工作方式

标志寄存器

ZF标志 零标志位 zero flag
  • 记录指令执行的结果是否为0
  • 1则zf = 0 , 0则 zf = 1
  • 影响zf指令:add,sub,mul,div,inc,or,and
  • 不影响zf指令:mov push pop
mov ax,1
sub ax,1
//ax=0,zf =1
PF标志 奇偶标志 parity flag
  • 记录指令执行的结果 的bit位中1的个数是否为偶数,偶数则pf=1 奇数则pf=0
mov ax,1
add al,10
//ax=00001011B,zf =0
SF标志 符号标志位 symbol flag
  • 记录指令执行的结果 是否为负,为负数则sf=1 非负则sf=0
  • 当做无符号数计算 SF无意义
  • 只记录实际结果的正负 不记录逻辑结果的正负 (考虑溢出)
mov al,10000001B
add al,1
//结果:(al) = 10000010B
//SF=1
mov al,10000001B
add al,01111111B
//结果:(al) = 100000000B //溢出则 去掉最高位1 取00000000B = 00H 
//SF=0 结果非负数
CF标志 进位标志 carry flag
  • 无符号位的计算中
  • 记录运算结果的: 1.最高有效位 2.更高位进位值 3.从更高位的借位值
  • 比如一个8位数 第7位位最高有效位 第8位即相对于最高有效位的更高位
  • 8位溢出后 由CF记录这个值
  • 97H-98H 将产生借位
mov al,98H
add al,al       ;98H*2 = 0130H执行后:(al) = 30H, CF = 1,CF记录最高有效位的更高位的进位值
add al,al       ;30H*2 = 60H ,CF = 0
mov al,97H
sub al,98H      ;借位后即: (al) =  197H-98H = FFH ,CF=1,记录向更高位的借位值
sub al,al       ;(al) = FFH-FFH = 0H ,CF = 0 
OF标志 溢出标志位 overflow Flag
  • OF只对有符号数有意义 计算出理想值进行比较 如8位范围-128~127
  • 8位有效范围:-128~127
  • 溢出OF =1
  • CF只对无符号数有意义
  • CPU 在执行指令时 会对数据分别做有符号和无符号的计算 并对CF OF分别赋值
mov al,98 
add al,97  
    ;结果为::98+97 = 195
    ;实际结果位 195 = C3H=11000011B
    //11000011B 为有符号数 
    //10111100B+1
    //10111101B  =-61
  • 对于无符号数:没有产生进位 CF = 0
  • 对于有符号数:产生溢出 OF = 1
mov al,0F0H     
            ; FOH 为有符号数    -16 
                //11110000
                //10001111+1
                //10010000
              
add al,088H     
            ; 088H 为有符号数   -120
                //10001000
                //11110111+1
                //11111000 
                
            ;0F0H+ 088H 结果为 101111000
               //101111000  
               //110000111+1
               //110001000  -136 溢出
            ;但是实际结果为
            //01111000  超界则去掉最高位
            //78H
  • 对于无符号数:没有产生进位 CF = 1
  • 对于有符号数:产生溢出 OF = 1
adc指令 add +carry 带进位的加法指令
adc ax,bx 实现功能:(ax) = (ax)+(bx)+CF 

mov ax,2
mov bx,1
sub bx,ax    ;(bx)<(ax) 产生借位:CF=1
adc ax,1     ;(ax)+1+CF = 2+1+1=4 
sbb指令 subtraction+borrow 带进位的减法指令
cmp指令 compare 比较指令
  • 于减法指令,只是不保存结果
  • p指令执行后,将对标志寄存器产生影响
mov ax,8
mov bx,3
cmp ax,bx
//执行后:(ax)-(bx) =5, zf = 0,pf = 1,sf = 0,cf = 0,of =0;
书中225页进行了一通cmp分析
  • zf = 1 说明:ax = bx
  • zf = 0 说明:ax != bx
  • cf = 1 说明:ax < bx
  • cf = 0 说明:ax >= bx
  • cf = 0 并且 zf = 0 说明:ax > bx
  • cf = 1 或 zf =1 说明:ax <= bx
  • sf = 1 并且 of = 1 (ah) > (bh)
  • sf = 0 并且 of = 1 (ah) < (bh)
  • sf = 0 并且 0f = 0 (ah) >=(bh)
检测比较结果的条件转移指令
指令含义检测相关标志位
je(equal)等于则转移zf=1
jne不等于zf!=0
jb(below)低于cf=1
jnb不低于cf=0
ja(above)高于cf = 0 并且 zf = 0
jna不高于cf = 1 后者 zf = 1
cmp ah,bh
je s            ;如果(ah)= (bh) 其实只要ZF = 1 就执行 :跳转到 s  否则向下执行 
add ah,bh
jmp short ok
s:add ah,ah
ok:...
DF标志 和 串传送指令
  • DF标志:direction flag 方向标志
  • 串处理指令:控制每次操作后 si di 的增减
  • df = 0 每次操作后 si di 递增
  • df = 1 每次操作后 si di 递减
  • movsb(byte) 描述:mov es:[di],byte ptr ds:[si] df=0: di si 加1 ,df=0: di si 减1
  • movsw(word) 描述:mov es:[di],byte ptr ds:[si] df=0: di si 加2 ,df=0: di si 减2
  • cld指令 将df置1
  • std指令 将df置0
  • rep movsb 配合movsb 和 movsw 使用 实现循环 汇编描述:
s:movsb
  loop s
实现内存的传送
data segment
	db 'Welcome to masm!'
	db 16 dup (0)
data ends

code segment
	mov ax ,data
	mov ds,ax
	mov si,0		;ds:si 指向 data:0
	mov es,ax	
	mov di:16		;es:di 指向 data:0010H
	mov cx,16		
	cld   			;df=0 正向传送
	rep movsb		
code ends
pushf 和 popf 针对标志寄存器压弹栈 书中234页

检测点11.4 记得这是存的flag 是一个16位的数

下面指令执行后,(ax)=0045h

mov ax,0            ;(ax)=0

push ax             ;将(ax)入栈

popf                ;将flag寄存器的所有为都初始化为0.因为它们使用的是同一个栈结构。

mov ax,0fff0h       ;(ax)=0fff0H

add ax,0010h        ;执行add后,结果是:(ax)=0000H(作为无符号数,产生进位cf=1),

                    ;作为有符号数没有溢出,of=0;其它zf=1,sf=0,pf=1.我们把这些标                           ; 位按照顺序组合起来(不能确定的我们不管了):00000xxx010x0101                                     ;我们不能确定的都用X表示。没有使用的用0表示吧             

pushf               ;将flag的值入栈。

pop ax              ;弹栈,(ax)= 00000xxx010x0101B

and al,11000101B    ;ax低8位按位与,    010x0101 and 11000101=01000101B=45H

and ah,00001000B    ;ax高8位按位与,00000xxx and 00001000=0000000B=00H
Debug中的flag 标记

flag

试验11 书中234页

第十二章 内中断

  • CPU不再接着(刚执行完的的指令)向下执行,而是转去处理这种中断信息
  • 中断信息由CPU的内部和外部产生。
内中断的产生
  • 除法错误 执行div产生除法溢出
  • 单步执行
  • 执行into指令
  • 执行int指令
中断类型
  • 中断类型码:一个字节型数据 256中种中断信息
  • 中断信息来源:中断源
  • 除法错误:0
  • 单步执行:1
  • 执行into指令:4
  • 执行int指令:格式为 int n ,n为字节型立即数
中断向量表:中断处理程序入口地址列表
  • 中断向量表:通过中断类型码找到中断入口地址的的先决条件
  • 8086CPU中 0000:0000~003F 共1024个单元 存放中断向量表
  • 一个表存放一个地址 = 段地址+偏移地址 = 2个字大小
中断过程
  • 中断过程:通过中断类型码找到中断入口地址 设置CS:IP CPU执行中断程序
  • (中断信息中)取得中断类型
  • 标志寄存器入栈
  • 设置标志寄存器 第八位TF 和第九位IF 为0
  • CS 内容入栈
  • IP 内容入栈
  • 从内存地址为中断类型码4 和中断类型码4+2 的两个字节单元中读取中断处理程序的入口地址设置IP和CS(*4是因为每个向量地址有4个字节)
iret指令和中断处理程序

CPU随时可能执行中断程序,所以中断程序必须一直存储在内存的某段中

  • 保存用到的寄存器
  • 处理中断
  • 恢复用到的寄存器
  • 用iret指令返回

iret指令功能用汇编描述:

pop IP
pop CS
popf
除法溢出错误
mov ax,1000h
mov bh,1
div bh

计算:
(al) = (ax)/((ds)*16+0)的商 = 1000h/0001H = 1000H (溢出 al范围为00H~FFH)
(ah) = (ax)/((ds)*16+0)的余数  = 0H
do0 修改 除法溢出的错误提示
  • 编写显示“overflow!”的中断程序:do0
  • 将do0送入内存0000:0200处
  • 将do0的入口地址0000:0200存储在中断向量表0号表项中

实现代码:

assume cs:code
code segment
	start:do0 安装程序 
		  设置中断向量表
		  mov ax,4c00h
		  int 21h
		  
	do0:  显示字符串“overflow!”
		  mov ax,4c00h
		  int 21h
		 
code ends

end start
安装 do0
assume cs:code
code segment
	start:mov ax,cs
		  mov ds,ax 
		  mov si,offset do0 				;设置ds:si指向源地址
		  
		  mov ax,0
		  mov es,ax							
		  mov di,200h						;设置es:di指向目的地址
		  
		  mov cx,offset do0end-offset do0   ;do0 部分代码的长度 		;设置cx为传输长度
		  cld 								;设置传输方向
		  rep movb
		  
		  设置中环向量表
		  
		  mov ax,4c00h
		  int 21h
		  
	do0:  显示字符串“overflow!”
		  mov ax,4c00h
		  int 21h
		 
code ends

end start
编写do0程序

这里存在一个问题 当code segment执行玩之后data
segment内存随时可能被释放。所以需将data实时生成。

do0代码不执行 只是装载到200H处

assume cs:code

data segment
 db "overflow!"
data ends

code segment
	start:mov ax,cs
		  mov ds,ax 
		  mov si,offset do0 				;设置ds:si指向源地址
		  
		  mov ax,0
		  mov es,ax							
		  mov di,200h						;设置es:di指向目的地址
		  
		  mov cx,offset do0end-offset do0   ;do0 部分代码的长度 		;设置cx为传输长度
		  cld 								;设置传输方向
		  rep movsb
		  
		  ;设置中环向量表
		  mov ax,4c00h
		  int 21h
		  
	do0:  mov ax,data						
		  mov ds,ax
		  mov si,0							;设置ds:si指向字符串
		  
		  mov ax,0b800H			
		  mov es,ax
		  mov di,12*160+36*2				;设置es:di指向显存空间的中间位置
		  
		  mov cx 9							;设置cx字符串长度
	s:	  mov al,[si]
		  mov es:[di],al
		  inc si
		  add di,2
		  loop s
		  
		  mov ax,4c00h
		  int 21h
	do0end:nop
   	
code ends

end start
改进do0
assume cs:code
code segment
	start:mov ax,cs
		  mov ds,ax 
		  mov si,offset do0 				;设置ds:si指向源地址
		  
		  mov ax,0
		  mov es,ax							
		  mov di,200h						;设置es:di指向目的地址
		  
		  mov cx,offset do0end-offset do0   ;do0 部分代码的长度 		;设置cx为传输长度
		  cld 								;设置传输方向
		  rep movsb
		  
		  ;设置中环向量表
		  mov ax,4c00h
		  int 21h
		  
	do0: jmp short do0start
		 db "overflow!"
		 
	do0start:  mov ax,cs					
		  mov ds,ax
		  mov si,0							;设置ds:si指向字符串
		  
		  mov ax,0b800H			
		  mov es,ax
		  mov di,12*160+36*2				;设置es:di指向显存空间的中间位置
		  
		  mov cx, 9							;设置cx字符串长度
	s:	  mov al,[si]
		  mov es:[di],al
		  inc si
		  add di,2
		  loop s
		  
		  mov ax,4c00h
		  int 21h
	do0end:nop
   	
code ends

end start
设置中断向量

do0 入口地址0:200 0:0存偏移地址 0:200子单元存段地址.

单步中断 类似Debug的t命令单步调试
  • TF为1 则(Step Flag)
  • 去除中断类型码1 (提前置0 防止无限循环)
  • 标志寄存器入栈,TF,IF 设置为0
  • CS,IP入栈
  • (IP)=(14),(cs)= (14+2)
响应中断的特殊情况

中断后 并不会设置 ss:SP 且当执行栈操作时 不会去响应中断,此时我们应该将SS和SP连续存放

mov ax,1000h
mov ss,ax
mov sp,0
试验12 编写0号中断处理程序 书中251页

第十三章 int指令:内中断之一

格式:int n,n为中断类型码 引发中断
执行过程:

  • 去除中断类型码1 (提前置0 防止无限循环)
  • 标志寄存器入栈,TF,IF 设置为0
  • CS,IP入栈
  • (IP)=(14),(cs)= (14+2)
书中253页 编写一个中断程序

问题 编写安装中断7ch中断例程
求2*3456^2
第一步:

assume cs:code

 code segment
 
	start :mov ax,3456
		   int 7ch
		   add ax,ax
		   adc dx,dx
		   mov ax,4c00h
		   int 21h
		   
 code ends
 
end start

第二步:

assume cs:code

 code segment
 
	start :mov ax,cs
		   mov ds,ax
		   mov si,offset sqr
		   mov ax,0
		   mov di,200h
		   mov cx,offset sqrend-offset sqr  ;安装子程序到 es:di 也就是 0:200H处
		   cld  ;df=0 正向传送  di si 加1
		   rep movsb;	循环复制 (mov es:[di],byte ptr ds:[si] df=0)
		   
		   mov ax,0
		   mov es,ax
		   mov word ptr es:[7ch*4],200h			;es:[7ch*4]中断向量表 偏移地址
		   mov word ptr es:[7ch*4+2],0			;es:[7ch*4+2]中断向量表 段地址
		   
		   mov ax,4c00h
		   int 21h
		   
	sqr:   mul ax
		   iret
		   
	sqrend:nop
	
 code ends
 
end start
```c

其中iret为 恢复数 CS IP 和 flag

pop IP
pop CS
popf

######  int和iret和栈深入理解  书中256页已经变得很复杂了。可能是前面基础没打好。




**先休息一下 本文共17章  且 课后试验部分未做,可谓是任重而道远,未完待续**

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值