汇编语言设计

汇编语言设计


机器语言是机器指令集合

早期:打纸带:1打孔,0不打孔

汇编语言与汇编指令

汇编语言主体是汇编指令

  • 汇编指令是机器指令便于记忆书写格式

  • 汇编指令是机器指令助记符

  • 机器指令100110001100001100000

    汇编MOV,AX,BX

汇编语言编写程序工作过程

汇编指令--编译器--机器码

伪指令--编译器执行

汇编指令--机器码助记符

其他符号--编译器识别

计算机组成

cpu,总线,内存,扩展槽

1646403344878

指令和数据存放在内存或磁盘上,都是二进制

数据如何表示:二进制B,八进制O,十进制D,十六进制H

计算机中的存储单元

一个存储器有128单元

8086有20条数据总线,那有2的20次方的,1MB的空间

总线:链接cpu和其他芯片的导线,

逻辑上总线分:数据总线,地址总线,控制总线

地址总线

地址总线宽度,决定了可寻址总线的存储单元大小

数据总线

cou内存和其他器件之间的数据传送是通过数据总线进行的,数据总线宽度决定了cou的外界数据传输速度

8088 8位,8086,16位

控制总线

cpu对外部设备器件进行控制

内存的读写和地址空间

存储单元地址(地址信息)

器件的选择,读或写的命令(控制信息)

读或写的数据(数据信息)

1646436605038

什么是内存地址空间

RAM:能读能写,没电就没了,

ROM:只读,系统的BIOS

1646436787301

所有物理存储器看做一个由若干存储单元组成的逻辑存储器

每个物理存储器在逻辑存储器中占有一个地址段,即一段地址空间

1646437045397

汇编语言实践语言的环境搭建

目标理解计算机底层工作原理

DOS环境
链接:https://pan.baidu.com/s/1SMBlRQQXeJ4EUMmVhPfCxw?pwd=kaip
提取码:kaip
--来自百度网盘超级会员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个寄存器

1646441308461

通用寄存器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 内容相加

汇编指令不区分大小写

1646443847015

1646443872449

确定物理地址方法

川普访问内存单元都要给出内存单元地址

每一个内存单元在这个空间构成的空间都有唯一地址,这个地址叫物理地址

8086有20位地址总线,寻址能力是1mb

8086是16位cpu,寻址能力是64KB

如何解决寻址空间的矛盾

用两个16位地址和段地址,偏移地址合成一个20位的物理地址

物理地址 = 段地址X16+偏移地址

例子:

段地址: 1230 偏移1位

偏移地址 00C8

12300
+00C8
= 123C8

物理地址 = 123C8

1646444470670

1646446640660

本质含义

用两个16位地址相加得到20位物理地址

本质含义:cpu在访问内存时,用一个基础地址和一个相对于基础地址的偏移地址相加,给出一个内存单元的物理地址

内存的分段表示法

内存并没有分段,段的划分来自于cpu

同一段内存,多种分段方案

1646446985145

根据段地址计算偏移地址

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

1646450843825

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

1646454622633

内存中字的存储

8086 16位作为一个字

高8位,低8位

低位字节低地址单元,高位字节高地址单元

0是低地址单元,1是高地址单元

1646467465306

字单元:由两个连续地址连续内存单元组成,存放一个字型数据

原理在一个字单元中,低地址单元存放低位字节,搞地质单元存放高位字节

用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处

1646469392049

[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指令操作数据

1646474009953

add指令操作

1646474356960

1646474435628

栈结构

1646474464836

栈的基本操作

入栈和出栈

入栈:将一个新的元素放到栈顶

出栈:从栈顶取出一个元素

栈顶元素总是在最后入栈,需要出栈,又最先从栈中取出

cpu提供栈机制

8086cpu提供相关指令,支持用栈的方式访问内存空间

基于8086cpu编程,可以将一段内存当栈来使用

PUSH入栈和POP出栈指令
push ax:将ax从中的数据送入栈中
pop ax:从栈顶取出数据送入ax

12 栈及栈操作的实现【 www.52download.cn】.mp4_20220305_185510

问题: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

!1646480515984

1646483898364

执行入栈,栈顶超出栈空间1646484002676

1646483992029

总结

push,pop实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据,与mov指令不同的是,push指令访问的内存单元地址不是在指令中给出的,而是有SS:SP指出的

1646484167922

段总结

物理地址 = 段地址x16 +偏移地址

数据段

  • 将段地址放在DS中
  • 用mov,add,sub等访问内存单元指令时,cpu将我们定义的数据段中内容当做数据段访问

代码段

  • 将段地址放在CS中,将段中第一条指令的偏移地址放在IP
  • CPU将执行我么定义四的代码段中指令

栈段

  • 将段地址放在SS,将栈顶单元偏移的放在SP中
  • CPU在需要我们进行栈操作push,pop,就将我们定义的栈段当做栈空间来使用

1646484931399

1646484901712

1646484869494

汇编语言程序


1646488894470

汇编语言编程序的工作过程

程序员--汇编程序--编译器--机器码--计算机

伪指令:没有对应的机器码指令,最终不被cpu所执行

伪指令是编译器执行,编译器根据伪指令进行相关的编译工作

汇编程序:汇编程序好伪指令的文本

程序返回:程序结束后将cpu的控制权交还给使运行的程序系统

1646489110038

程序的三种伪指令

段定义:一个会变程序是多个段组成的,这些段用来被存放代码,数据或当做栈空间来使用

一个有意义的汇编程序中至少有一个段,这个段用来存放代码

段名 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



1646492996125

1646493051228

提示语法错误

severe errirs

找不到所给出的源文件

连接

可执行文件是对一个程序进行连接要得到的最终结果

映象文件:是连接程序将目标文件连接为可执行文件过程中产生中间结果

库文件包含了一些可以调用的子程序,如果我们的程序中调用了某一个库文件中的子程序,就需要连接的时候将这个库文件和我们的目标文件连接到一起,生成可执行文件、

no stack segment,一个 没有栈段的错误警告,可以不去理会

执行程序;直接输入文件名

1646494719088

程序的运行和跟踪


DEBUG装在程序EXE

程序加载后,DS中存放着程序所在内存区的段地址,这个内存区的偏移地址是0,程序内存区地址是DS:0

这个内存区前256字节存PSP,DOS用来程序进行通信

从256字节向后空间存放的是程序,CS值是DS+10H

程序加载后,CX存放代码长度

继续命令P

类似于T命令,逐条执行指令,显示结果,但遇到子程序,中断等,直接执行,然后显示结果

运行命令G

从指定地址处开始运行程序,知道断点结束或者程序正常结束

在DOS中执行

程序执行的常态

一个内存单元的描述:

  • ​ 内存单元地址
  • ​ 内存单元长度

[...]表示一个内存单元

1646532956256

(...)表示一个内存单元或寄存器中内容

1646532945376

1646535510578

1646536404008

LOOP指令

功能:实现循环

cpu执行loop指令时进行的操作

  1. (cx)=(cx)-1

  2. 判断cx的值

    1. 不为0则转到标号处执行程序

    2. 如果是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

例子

1646539789037

编译:

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




引入段前缀,异常现象的对策

1646540784700

访问内存单元--loop和[bx]

问题:计算ffff:0-ffff:b字节单元中的数据的和,结果存储在dx中

分析:略

对策:取出8位数据,加到16位寄存器

mov al,ds:[addr]  取出8位数据
mov ah,0
add dx,ax

1646552657288

段前缀的使用

1646552867145

代码段中数据的使用

将内存ffff:0中数据拷贝到0:200-0:20b

问题,程序中直接写地址,很危险

对策

在程序中段中存放数据,运行时由操作系统分配空间

段的类别:数据段,代码段,栈段

各种段中均可以有数据

可以在单个段中安置,也可以将数据代码,栈放入不同的段中

实际应用

问题编程计算以下8个数据的和,结果存放到ax寄存器中

1646553321286

解决方案

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 通知编译器程序结束外,还可以通知编译器入口在什么地方

1646556572849

在代码段中使用栈

程序运行时候。定义存放的数据cs:0--cs:f,一共8个单元

依次将八个单元中数据入栈,然后一次出栈找到8个字单元,从而实现数据的逆序存放

栈需要内存空间,在程序中通过定义:空数据来获得

数据逆序存放程序

1646557089008

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

将数据代码栈放入不同段

1646558248311



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码

1646561244105

大小写字符问题

大写+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

1646573578602

[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

用数组方法解决大小写转换问题

image-20220306224746667

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

  1. mov s,0
  2. mov di 0

例子:用寄存器SI,SI实现将字符串‘welcome to masm’复制到特后面的数据区

image-20220306225901544

源数据起始地址: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]


image-20220306233212634

[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]

内存寻址方式的灵活应用

image-20220307220104101

例子

image-20220307222058107

例子

image-20220307222603897

二重循环用了同一个cx,解决办法

image-20220307222826549

但是存在问题是,cx占用寄存器资源,比较浪费

image-20220307223232783

可以利用固定的储存单元,也可以用栈来存数据

直接寻址过程
image

寄存器间间接寻址过程
8 不同寻址方式演示_2

寄存器相对寻址过程
8 不同寻址方式演示_3
基址变址寻址过程
8 不同寻址方式演示_4
相对基址变址寻址过程
8 不同寻址方式演示_5

哪些寄存器用于寻址

用于内存寻址的寄存器用法

只有bx,bp,si,di可以用在[…]对内存单元寻址

bx以外的通用寄存器,段寄存器不可以用[…]

bx,bp区别:bx默认指ds段

bp默认指ss段

正确的指令image-20220307231448620

image-20220307231621104

image-20220307231628105

错误的指令

image-20220307231642678 image-20220307231649968

汇编语言中数据位置的表达

立即数

对于直接包含在机器指令的数据称为立即数

寄存器

指令要处理数据在寄存器中

内存:段地址和偏移地址

指令要处理的数据在内存中,由SA:EA确定内存单元

image-20220307232116244

指令要处理的数据有多长

字word操作

-----------------
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
mov al,ds:[0]
mov ds:[0],al
inc al
add al,100

--------------------
mov word ptr ds:[0],1
inc word ptr [bx]
inc word ptr ds:[0]
add word ptr [bx],2
-----------------
mov byte ptr ds:[0],1
inc byte ptr [bx]
inc byte ptr ds:[0]
add byte ptr [bx],2





字节操作

用word ptr 或 byte ptr指明

在没有寄存器参与的内存单元访问指令中,用word ptr 或 byte ptr 显性指明所要访问的内存单元长度是很有必要的,否则cpu将无法得知所要访问是字单元,还是字节单元

DIV指令

div是除法指令,使用div做除法时候

被除数:默认放在放在AX或DX和AX中

除数:8位或16位,在寄存器或内存单元中

div指令格式

​ div 寄存器

​ div 内存单元

image-20220308082336485

例子

image-20220308083430604

双字型数据定义

	data segment 

​	db 1:定义字节型数据 在,data:0 占一个字节

dw 1:定义字型数据0001H,在data:1处,占2个字节

dd 1 定义双字型数据00000001H在data:3处,占2个字(4个字节)

data ends

data segment

​	dd 100001

​	dw 100

​	dw 0 

data ends

mov ax,data

mov ds,ax

mov ax,ds:[0]

mov dx,ds:[2]

div word ptr ds:[4]

mov ds:[6],ax

dup 功能

dup 和db,dw,dd等数据定义伪指令配合使用,用来进行数据的重复

image-20220308090631078

db 重复的次数dup(重复字节型数据)

dw 重复的次数dup(重复字节型数据)

dd 重复双字数据

assume cs:code,ds:data
data segment
	db 3 dup(0)
	db 3 dup(0,1,2)
	db 80 dup(0)
	db 3 dup('abc','ABC')
data ends

code segment
	mov ax,,data
	mov ds,ax
	mov ax,4c00h
code ends
end

流程转移和子程序


北京:一般情况下指令是顺序逐条执行的,实际中,需要改变执行流程

可以控制cpu的内存中某处代码指令

可以修改ip,或同时修改cs和ip指令

转移指令分类

  • 按转移行为
    • 段内转移:只修改ip 如imp ax
    • 段间转移: 同时修改cs和ip ,imp 1000。
  • 根据指令对ip修改的范围不同
    • 段内短转移:ip修改范围128-127
    • 段内近转移:ip修改范围32768-32767
  • 按转移指令
    • 无条件转移指令(imp)
    • 条件转移指令 jcxz
    • 循环指令 loop
  • 过程
  • 中断

操作符 offset

用操作符 offset 取得标号的偏移地址

offse 标号
assume cs:codeseg
start: mov ax,offset start ;相当于mov ax,0
s:mov ax,offset s   ;相当于 mov ax,3
codeseg ends
end start

有如下程序段,添加2条指令,使改程序运行汇总s指令复制到s0处


assume cs:codesg
codesg segment
	s:mov ax,bx
	mov si,offset s
	mov di,offset s0
	mov ax,cs:[si]
	mov cs:[di],ax
	s0:nop
		nop ; nop机器码 占一个字节,起到一个占位的作用
	codesg ends
	end
	

s 和s0处指令所在的内存单元是多少

cs:offset 和cs:offset s0

将 s处的指令复制到s0处就是将cs:offset s处的数据复制到cs:offset s0处

地址如何表示

段地址已知在cs中,偏移地址已经送到si和di中

要复制的数据有多长

mov,ax,bx指令长度是两个字节,1个字

jmp指令--无条件转移

jmp指令的功能

无条件转移,可以只修改ip,也可以同时修改cs和ip

jmp指令要给出两种信息

转移的目的地址

转移的距离

image-20220308174642698

依据位移进行转移

jmp指令:依据位移进行转移

assume cs:codesg
codesg segment
	start: 
		mov ax,0
		jmp short s
		add ax,1
		s:inc ax
codesg ends
end start

1.ip = 0003 cs:ip 指向E8 05

读取指令码 E8 05 进入指令缓冲器

ip = ip +所读指令长度 = ip +2=0005 cs:ip 指向 add ax,0001

cpu指令缓冲器中的指令E805

指令E805执行后,ip=ip+05=000AH cs:ip 指向inc ax

assume cs:codesg
codesg segment
	start: mov ax,0
		jmp short s
		add ax,1
		nop
		nop
		s: inc ax
codesg ends
end start

俩种段内转移

短转移

jmp short 标号
功能 ip = ip+8位位移
原理 
8位位移 = 标号 处的地址-jmp指令后的第一个字节的地址
short 指明此处的位移是9位位移

8位位移的范围是-128~127用补码表示,不能超出范围

8位位移编译程序在编译时候算出

进转移

jmp near ptr 标号
功能 ip = ip +16
原理 
16位位移 = 标号处的地址 imp 指令后的第一个、字节的地址
near ptr 指明此处的位移是16位位移,进行段内转移
16位移范围 -32769~32767,用补码表示

远转移 jmp far ptr 标号

段间转移:

far ptr 指明了跳转目的地址,即包含了标号段地址cs和偏移地址ip

段内转移

near ptr 指明了相对于当前ip转移位移,而不是转移的目的地址

转移地址在寄存器中jmp指令

指令格式 jmp 16位寄存器

ip = 16位寄存器

jmp ax jmp bx

assume cs:codesg
codesg segement
start:mov ax,0
	mov bx,ax
	jmp bx
	mov ax,0123H
codesg ends
end start 

转移地址在内存中的jmp指令

jwp word ptr 内存单元地址

功能:从内存单元地址处开始存放一个字,是转移的目的偏移地址

image-20220309092929090

jmp dword ptr 内存单元的地址

段间转移

功能:从内存单元地址开始存放两个字,高地址处的字是转移的目的段地址,低地址是转移的目的偏移地址

image-20220309093145876

小结

jmp 标号

段间转移(远转移) : jmp far ptr 标号

段内短转移 jmp short 标号 8位位移

段内近转移: jmp near ptr 标号 16位位移

jmp 寄存器 jmp bx 16位移

jmp内存单元(表示跳转到地址)

段内转移 jmp word ptr 内存单元地址 jmp word ptr [bx]

段间转移 jmp dword ptr 内存单元地址 jmp dword ptr [bx]

注意

源程序中,不允许使用“jmp 2000:0100”的转移指令实现段间转移 • 这是在 Debug 中使用的汇编指令,汇编编译器并不认识 • 如果在源程序中使用,编译时也会报错。

其他转移指令


jcxz指令

格式 jcxz 标号

功能 如果 cx=0 则转移到标号执行

  • cx 不等于 0 什么也不做
  • cx = 0 时ip=ip +8位位移
  • 9位位移 = 标号 处的地址-jxcz指令后的第一个字节的地址
  • 8位位移的范围是-128~127用补码表示
  • 8位位移由编译程序在编译时算出

jxcz是有条件转移指令

  • 所有的有条件转移指令
  • 所有有条件转移指令都是短转移
  • 对ip修改范围是-128~127
  • 在对应的机器码中包含转移的位移,而不是目的的地址

loop指令

指令操作

cx=cx-1

当cx不等于0时则转移到标号处执行

cx=0,程序向下执行

loop s在执行时只涉及到s的位移

-4前移4个字节,补码表示FCH

  • 所有的有条件转移指令
  • 所有有条件转移指令都是短转移
  • 对ip修改范围是-128~127
  • 在对应的机器码中包含转移的位移,而不是目的的地址

根据位移进行相对转移的意义

对ip的修改时根据转移的目的地址和转移起始地址之间的位移进行

jmp short

jmp near ptr

jxcz 标号

loop标号

如果 loop s 的机器码中包含是s地址,则就对程序段的内存偏移地址有了严格的限制,易引发错误

当机器码中包含的是转移的位移无论s处的指令的实际地址是多少,loop指令的相对位移是不变的

call指令 ret指令

mov ax,0
call s
mov ax,4c00h
int 21h
s:add ax,1
ret
实质就是流程转移指令,它们都是修改ip同时修改cs,ip

call 指令

就是调用子程序

实质就是流程转移,和jmp原理相似

两步操作

当前ip或cs:ip压入栈

转移到标号处执行指令

image-20220309095635633
call far ptr 标号实现的是段间转移

sp = sp -2

ssx 16+sp = cs

sp = sp -2

ssx16+sp = ip

相当于

push cs

push ip

jmp far ptr

转移地址在寄存器中的call指令

格式

call 16 寄存器

sp = sp-2

ss*16 +sp = ip

ip=16位寄存器

相当于

push ip

jmp 16位寄存器

转移地址在内存中call指令

call word ptr 内存单元地址

mov sp,10h
mov ax,0123h
mov ds:[0],ax
call word ptr ds:[0]
执行后 ip = 0123H ,sp=0EH

call word ptr

push cs

push ip

jmp word ptr 内存单元地址

mov sp,10h
mov ax,0123H
mov ds:[0],ax
mov word ptr ds:[2],0
call word ptr ds:[0]
执行cs = 0 ip = 0123H sp = 0CH
低地址放偏移地址
高地址放段地址

image-20220310222549596

call 和ret配合使用

计算2的N次方,N由cx提供

assume cs:code
code segment
start: 
	mov ax,1
	mov cx,3
	call s 
	mov bx,ax
	mov ax,4c00h
	int 21h
	ret
code ends
end start

assume cs:code,ss:stack
stack segment
	db 8 dup (0)
	db 8 dup (0)
stack ends
code segment
start: 
	mov ax,stack
	mov ss,ax
	mov sp,16
	mov ax,1000
	call s
	mov ax,4c00h
	int 21h
	s:add ax,ax
	ret
code ends
end start


除法div指令

div是除法

格式 div 寄存器

div 内存单元

使用 div做除法时候

被除数 放在AX DX和AX中

除数8位16位,在寄存器或内存单元

mull指令做乘法

image-20220311174734466

image-20220311175017913

计算 100*10

mov al,100

mov bl,10

mul bl

ax = 1000

计算 100*10000

分析100小于255 课10000大于255,所以必须做61乘法

mov ax,100

mov bx,10000

mul bx

结果 dx =000FH

ax = 4240h

F4240H = 1000000

模块化程序设计

调用子程序:call指令

返回:ret指令

子程序:根据提供的参数处理一定事务处理后,将返回值提供给调用者

参数和结果传递

方案

用寄存器

用内存单元

用栈

用寄存器传递参数

参数放到dx,bx = N

子程序中多个mul指令计算N^#

汇编子程序

cube: mov ax,bx

mul bx

mul bx

ret

计算data段一组数据3次方

结果保存在后面dword中

assume cs:code
data segment
	dw 1,2,3,4,5,6,7,8
	dd 0,0,0,0,0,0,0,0
data ends
code segment
start:mov ax,data
	mov ds,ax
	mov si,0
	mov di,16
	
	
	mov cx,8
	S:mov bx,[si]
	call cube 
	mov [di],ax
	mov [di].2,dx
	add si,2
	add di,4
	loop s
	
	mov ax,4c00h
	int 21h
	code ends
end start

用内存单元批量出阿迪数据

将批量数据放到内存汇总,然后将它们所在内存空间的首地址放在寄存器中,首地址放到寄存器中,传递哦给需要的子程序中

用栈传递参数

由调用者将需要传递给子程序的参数

压入栈中,子程序从栈中取得参数

进入子程序前,参数 a,b入栈

调用子程序,将使栈顶存放IP

结果 dx:ax = (a-b)^3

mov ax,1

push ax

mov ax,3

push ax

call difcube


diffcube: push bp
	mov bp,sp
	mov ax,[bp+4]
	sub ax,[bp+6]
	mov bp,ax
	mul bp
	mul bp
	pop bp
	ret 4
	
code ends

image-20220311192056711

j寄存器冲突方案解决

在编写调用子程序冲突时有没有会产生冲突的寄存器

如果有,调用者会使用别的寄存器

在编写子程序时候,不要使用会产生冲突的寄存器

但是这个方案不好

子程序标准框架:子程序使用的寄存器入栈

子程序使用的寄存器出栈

返回(ret,retf)

在子程序开始,将要用到的所寄存器内容都保存起来,在子程序返回前再恢复

标志寄存器

标志寄存器结构

flag寄存器是按位起作用,每一位都有专门的含义,记录特定信息

8086没有使用flag 1,3,5,12,13,14 这些位不具有任何含义

作用

用来存储相关指令某些结果

用来为cpu执行相关指令提供行为依据

用来控制CPU相关工作方式

image-20220311200340508

直接访问标志寄存器

pushf:将标志寄存器值压入栈

popf:从栈中弹出数据,送入标志寄存器

ZF零标志

ZF标记相关指令计算结果是否为0

ZF=1 表示结果是0 1表示逻辑真

ZF=0 结果不是0,0表示逻辑假

8086指令集中,有的指令执行是影响标志寄存器,多数是逻辑算数运算

有的指令执行对标志寄存器没有影响,比如mov,push,pop,多数是传送指令

使用一条指令时候,要注意这条指令的全部功能,包括执行结果对标记寄存的哪些标志位影响

image-20220311204346058

PF奇偶标志

PF记录指令执行,结果所有二进制位1个数

1的个数是偶数 PF=1

1的个数是奇数 PF=0

image-20220312081449343

SF符号标志

结果是负 SF=1

不是负数 SF=0

image-20220312081503686

CF进位标志

在进行无符号数运算,CF记录了运算届法国最高有效位和更高位进位值

对于位数为N的无符号的数来说,对应的二进制信息最高位就是第N-1位,是最高的有效位

假想存在第N位,就是相对最高有效位的更高位

CF记录执行执行后CF=1

无进位或借位 CF=0

image-20220312081519487

OF溢出标志

在进行有符号数运算时候,如果超过了机器所能表示的范围叫溢出

CF和OF区别

CF是对无符号数运算有意义的进位标志位

OF是对有符号运算有意义的溢出标志位

mov al,0F0H

add al,88H

CF=1,OF=1 当无符号数运算有进位,当有符号运算溢出

image-20220312082454641

adc带进位加法指令

格式 adc操作对象,操作对象2

功能操作对象1 = 操作对象1+操作对象2+CF

例 adc ax,bx 实现功能ax = ax+bx+CF

image-20220312084451068

8086指令提供add指令,完成8位或16位加法

例子:计算·11EF000H +201000H 结果放在ax 高16位和 bx低16位

先将低16位相加,然后将16位进位值相加

mov ax,001EH

mov bx,0F000H

add bx,1000H

adc ax,0020H

例子 计算1E F000 1000H+20 1000 1EF0H

结果放在 ax 高16位 bx 次高16位 cx低16位

mov ax,001EH

mov bx,0F000H

mov cx,1000H

add cx,1EF0H

add bx,1000H

adc ax,0020H

128位数据相加

对128位数据相加

add128

两个逆序存放128数据相加

image-20220312090054203

数据是128位,需要8个单元,由低地址单元到高地址单元,依次存放由低到高各个字

ds:si指向存储第一个数的内存空间

ds:di指向存储存储的第二个数内存空间

运算结果存储的第一个数的存储空间

sbb指令

带借位减法指令

格式 sbb操作对象,操作对象2

功能操作对象1 = 操作对象1-操作对象2-CF

与sub区别,利用CF位上记录的借位值

比如 sbb ax bx

实现功能 ax = ax-bx-CF

应用 对任意大的数据进行减法运算

计算

结果放在 ax,bx

mov bx,1000H

mov ax,003EH

sub bx,2000H

sbb ax,0020H

cmp指令

格式 cmp操作对象1,操作对象2

功能计算操作对象-操作对象2

cmp是比较指令功能相当于减法指令,知识不保存结果

cmp指令执行后,将对标志寄存器产生影响

image-20220312095211172

无符号数比较标志位取值

通过cmp指令执行后相关标志位的值,可以看出结果

image-20220312095526098

比较指令设计思路,通过做减法运算影响标志寄存器,标志寄存器相关位取值,体现比较好的结果

有符号数比较与标志位取值

用cmp进行有符号数比较时,cpu哪些标志比较时

cmp ah,bh

image-20220312095734657

仅凭结果正负SF无法得出结论,需要配合1是否溢出 得到结论

条件转移指令

根据单个标志位转移指令

image-20220312105603189

调价转移指令使用

j新系列指令cmp指令配合,构造条件转移指令

不必再考虑cmp指令对相关标志影响

可以直接考虑cmp指令配合

例子

image-20220312110408708

条件转移指令

可以根据某种条件,决定是否转移,程序执行流程

转移 = 修改ip

通过检测标志位,由标志体现调价

条件转移指令通常都和cmp相配合使用、

cmp指令改变标志位

双分支结结构实现

例子

image-20220312113356073

初始设置ax=0,然后用循环依次比较每个字节的值,找到一个和8相等的数就将ax+1

image-20220312113454097

初始设置ax=0,然后循环依次比较每个字节的值,找到一个大于8的数就将ax+1

image-20220312113546645

初始设置ax=0,然后循环依次比较每个字节,找到一个小于8的数就将ax+1

DF标志和串传送指令

在串处理指令中华,控制每次、操作后si,di增减

DF=0每次操作后si,di递增

DF=1,每次操作后si,di递减

对DF进行设置指令

cld指令 将标志旗寄存器DF为设为0

std指令,将标志寄存器DF设为1

image-20220312120620841

rep指令

rep指令常和串传送指令搭配使用

根据cx值,重复执行后面的指令

image-20220312121015298

assume csd:code,ds:data
data segment
	db ''
	db ''
data ends
code segment
start:
	mov ax,data
	mov ds,ax
	mov si,0
	mov es,ax
	mov di,16
	cld
	mov cx,8
	rep movsw
	
	mov ax,4c00h
	int 21h
code ends
end start

	

例子,用串传送指令,将F000H中最后16个字符复制到data中

assume cs:code,ds:data
data segment
	db 16 dup (0)

data ends
code segment
start:
	mov ax,0f000h
	mov ds,ax 
	mov si,0ffffh
	mov ax,data
	mov es,ax
	mov di,15
	mov cx,16
	std
	rep movsb
	
	mov ax,4c00h
	int 21h
	
code ends
end start

中断和外部设备操作


移位指令 SHL OPR CNT

image-20220312125839410

逻辑右移 SHR OPR CNT

image-20220312125852583

算数左移 SAL OPR CNT

image-20220312125859814

带进位循环左移 RCL OPR CNT

image-20220312125916909

循环左移ROL OPR CNT

image-20220312125925417

循环右移 ROR OPR CNT

image-20220312125935704

算数右移 SAR OPR CNT

image-20220312125945985

带进位循环右移 RCR OPR CNT

image-20220312125954316

逻辑移位指令shl shr

SHL OPR CNT

将寄存器或内存单元中数据向左一维

将最后移出一位写入CF

移动位数大于1时必须用cl

mov al,010100001b

mov cl,3

shl al,cl

操作显存数据

image-20220312131332507

显示信息一种直接方式


assume cs:codeseg,ds:datasg
datasg segment
	db 'welcome'
datasg ends

codeseg segment
start:
	mov ax,datasg
	mov ds,ax
	mov ax,0B800H
	mov es,ax
	mov si,0
	mov di,160*12+80-16
	mov cx,16
	w:mov al,[si]
	mov es:[di],al
	inc di
	mov al,71H
	mov es:[di],al
	inc si
	inc di
	loop w
	
	mov ax,4c00h
	int 21h
codeseg ends
end start
	
	

描述内存单元的标号

代码段中标号可以来标记指令,段的起始地址

代码段中也可以用标号

在代码段中使用标号a,b后面没有“:”,它们描述内存地址和单元标号

标号a,地址code:0

以后内存单元都是字节

标号b

地址 code:8

以后内存单元都是字

数据标号

数据标号标记了存储数据的单元的地址和长度

数据标号不同于仅仅表示地址的地址标号

数据的直接定址表

以十六进制形式在屏幕中间显示给定的byte数据

先将一个byte高4位和低4位分开,显示对应的数码字符

如果数值显示是0,则显示“0”

如果数值是1,则显示1

数值是11 显示B

image-20220312161839854

建立一张表,表中依次存储字符 0F,通过数值0 ~15直接查找对应的字符

image-20220312171249322

assume cd:code
code segment
start:
	mov al,2Bh
	call showbyte
	mov ax,4c00h
	int 21h
	showbyte:
		jmp short show
		table db '0123456AVCDEF'
	show: 
	push bx
	push es
	push cx
	mov ah,al
	mov cl,4
	shr ah,cl 
	and al,00001111b
	
	mov bl,ah
	mov bh,0
	mov ah,table[bx]
	mov bx,0b800h
	mov es,bx
	mov es:[160*12+40*2],ah
	
	mov bl,al
	mov bh,0
	mov al,table[bx]
	mov es:[160*12+40*2+2],al
	
	
	pop cx
	pop cs
	pop bx
	ret
	
code ends
end start

利用表在两个数据几个之间简历哦一种映射关系,用查表方法根据得到在另一个集合中对应的数据

posted @ 2022-03-06 22:18  #卧龙先生#  阅读(349)  评论(0编辑  收藏  举报