汇编从零到精通

目录

第1章 基础知识

1.1 存取器

1.2 指令与数据

1.3 存储单元

1.4 cpu对存储器的读写

1.5 地址总线

1.9 数据总线

1.10 控制总线

第2章 寄存器

2.1通用寄存器

2.2基本汇编指令

2.3 物理地址

 2.4 8086cpu给出2物理地址的方法

2.5 段

2.6 段寄存器

        CS和IP

        修改CS和IP地址的指令


第1章 基础知识

1.1 存取器

        cpu是计算机的核心部件,它控制着整个计算机的运作进行计算。要想让一个cpu工作,就必须向他提供指令和数据。指令和数据在存储器中存放,就是内存。

1.2 指令与数据

        在内存所磁盘中,指令和数据没有任何区别,都是二进制信息。

        列如:内存中的二进制信息1000100111011000,计算机可以把它看作大小为89D8H的数据来处理,也可以将其看作指令mov ax,bx来执行

1000100111011000->89D8H(数据)
1000100111011000->mov ax,bx (程序)

1.3 存储单元

        存储器被划分成若干个存储单元,每个存储单元从0开始顺序编号,列如一个存储器有128个存储单元,编号0~127
        

        

计算机最小信息为bit,也就是一个二进制位
8 bit=1 Byte(一个字节)
一个存储器有128个存储单元,可存放128个Byte

1KB=1024B   1MB=1024KB   1GB=1024MB   1TB=1024G

1.4 cpu对存储器的读写

        存储器被划分成多个存储单元,存储单元从零开始顺序编号。这些编号可以看作存储单元在存储器中的地址。

        cpu想要从内存中读取数据,首先要指定存储单元的地址。所以cpu在读写数据时还要指明,它要对那个器件进行操作,进行那种操作,是从中读取数据,还是写入

 cpu想要进行数据的读写,必须和外部器件(芯片)进行信息交互

  • 存储单元的地址(地址信息)
  • 器件的选择,读或写的命令(控制信息)
  • 读或写的数据(数据信息)

cpu从3号单元读取数据过程:

        

(1)cpu通过地址线将地址信息3发出。

(2)cpu通过控制线发出内存读命令,选中存储器芯片,并通知它,将要从中读取数据。

(3)存储器将3号单元的数据08通过数据线送入cpu

对应汇编指令:mov ax,[3]

写入数据也类似的

1.5 地址总线

        cpu通过地址总线来指定存储器单元的,所以地址总线上能传送多少个不同的信息,cpu就可以对多少个存储单元进行寻址

        例如:一个cpu有10根地址总线,那么10根导线可以传送10个二进制数据,那么10个二进制数表示多少数据呢?2的10次方个。最小为0,最大为1023.

        一个cpu有N根地址总线,则可以说这个cpu的地址总线宽度为N。这样的cpu最多可以寻2的N次方给单元

 

1.9 数据总线

         数据总线的宽度决定了cpu和外界的数据传送速度。8根数据总线一次可以传送一个8位二进制数(1个字节)。16根则为2个字节

1.10 控制总线

        其宽度决定了cpu对系统中其他器件的控制能力

第2章 寄存器

        寄存器对信息进行存储,不同cpu寄存器个数,结构都不相同。

        8086cpu有14个寄存器

寄存器:
AX,BX,CX,DX,SI,DI,SP,BP,IP,CS,SS,DS,ES,PSW

2.1通用寄存器

        AX,BX,CX,DX这四个寄存器通常用来存放一般性数据

        以AX为例:

一个16位寄存器可以存放16位数据

在8086cpu中寄存器可以分为两个独立使用的8位寄存器来用:

  • AX可分为:AH和AL
  • BX可分为:BH和BL
  • CX可分为:CH和CL
  • DX可分为:DH和DL

例如:

数据:18
二进制表示:10010

在AX中的存储:

 

AX的低8位构成了AL寄存器,高8位构成了AH寄存器,两者可以独立使用

 

十六进制数的一位相当于二进制的四位,
如0100111000100000可以表示为:4(0100),E(1110),2(0010),0(0000)
                                          4E20H(H表示为这数为十六进制数)

2.2基本汇编指令

汇编指令
mov ax,18     将18送入ax中                         ax=18
mov ah,78     将78送入ah中                         ah=78
add ax,8      ax加上8                              ax=ax+8
mov ax,bx     将bx数据送入ax中                     ax=bx
add ax,bx    将ax和bx中的值相加,结果存入ax中        ax=ax+bx        

mov ax,4E20H        ax=4E20H        bx=0000H
add ax,1406H        ax=6226H        bx=0000H
mov bx,2000H        ax=6226H        bx=2000H
add bx,ax           ax=8226H        bx=2000H
mov bx,ax           ax=8226H        bx=8226H

若add ax,bx        ax为5412H,bx为6123H相加是什么结果呢

相加后ax数据为11535H,但ax寄存器只能存放16位,所以要将高位舍去(并不是真正丢弃了),所以ax中数据位1535H

2.3 物理地址

        cpu访问访问内存空间时,要给出内存单元地址。每个内存单元在这个空间中都有唯一的地址,我们称为物理地址。

 2.4 8086cpu给出2物理地址的方法

        8086cpu有20位地址总线,可以传送20位地址,达到1MB寻址能力。其结构为16位结构,在内部一次性处理,传输,暂时存储的地址为16位。

        所以cpu采用了在内部用;两个16位地址合成的方法来形成一个20位的物理地址

  • cpu提供了两个16位的地址,一个称为段地址,另一个被称为偏移地址
  • 段地址和偏移地址一起送入称为地址加法器的部件
  • 将两个16位地址合成一个20位的物理地址

地址加法器:

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

 

段地址*16的意思:

        常用说法左移4位。

数据为2H,十进制为10B

左移位数            二进制            十六进制            十进制
0                    10B                2H                2
1                   100B                4H                4
2                  1000B                8H                8
3                 10000B               10H                16 
4                100000B               20H                32

十六进制数2H左移4位为2*10=20H

段地址*16+偏移地址=物理地址的本质含义:

        cpu在访问内存时,用一个基础地址(段地址*16)和一个相对于基础地址的偏移地址相加,给出内存单元的物理地址

        例如:段地址为200H,偏移地址为826H,则物理地址为2826H

2.5 段

        

        内存并没有分段,段的划分来源于cpu,用“段地址*16+偏移地址=物理地址”的方法给出内存单元的物理地址,可以使得我们用分段的方式管理内存

        图上地址定义为10000H~100FFH组成一个段,该段的段地址为1000H,或者分成两个段一个段地址为1000H,另一个为1008H

         注意:用段地址*16定位段的起始地址2,用偏移地址定位段中的内存单元

        因为偏移地址为16位,16位地址的寻址能力位64KB,所以一个段的长度最大位64KB

2.6 段寄存器

        CS,DS,SS,ES

  • cs代码段
  • ds数据段
  • ss堆栈段
  • es附加段

        CS和IP

CS可以看作为段地址,IP看作为偏移地址 ,CS*16+IP,访问对应CS段中的IP位置的内存数据

        修改CS和IP地址的指令

jmp 段地址:偏移地址

jmp 2AE3:3,执行后cs=2AE3H,ip=0003H,cpu将从2AE33H处读取数据

若只想修改ip的内容

jmp 寄存器

jmp ax   执行前ax=1000H,ip=0003H,执行后ip=1000H

第3章 debug的使用

        Debug是一个调试工具,通常是操作系统的一部分,用于帮助程序员找出并修复代码中的问题。它提供了一组功能,允许程序员逐步执行程序、观察寄存器和内存中的数据,以及检查代码的执行路径。Debug通常包括命令行界面,允许程序员输入各种命令以控制程序的执行
https://pan.baidu.com/s/1P76bEvHup1QjZoHs34OMsg?pwd=ac12 提取码: ac12

本章节要多动手实践操作

需要的软件:debug软件和DOSBox,建议在虚拟机中安装

进入DOSBox,输入指令

mount c c:\masm的文件目录
c:

进入debug

  •  R命令查看,改变cpu寄存器内容
  • D命令查看内存中的内容
  • E命令改写内存中的内容
  • U命令将内存中的机器指令翻译成汇编指令
  • T命令执行一条机器指令
  • A命令以汇编指令的格式在内存中写入一条机器指令

3.1 R命令

内容均是以16进制来表示的

查看寄存器的内容

修改寄存器内容,指定寄存器ax改为1000H

 

3.2 D命令

        查看内存中的内容

d 段地址:偏移地址

 查看cs:ip中的内容

 

可指定D命令查看的内容范围

d 段地址:偏移地址 结尾偏移地址

3.3 E命令

        可以逐个修改某一地址开始的内存单元中的内容,以1000:10单元开始为例

输入后光标将会停在00.处提示输入想要写入的数据,有两种选择

  • 输入数据,然后空格,即用输入的数据改写当前的内存单元
  • 不输入数据,直接按空格键,则不对当前内存单元进行改写

可以使用 E命令向内存中写入字符“a”,数值2,字符“b”,数值3,字符“d”

 字符a,b,c是以ASCII码形式写入内存单元中

也可以写入字符串,字符串“a+b”等

3.4 U指令 与 T指令

        用e命令向地址1000:0开始写入数据,并用d命令查看1000:0到1000:000f的数据

如何查看写入的或内存中原有的机器码所对应的汇编指令?

T执行一条汇编指令

先使用r命令改变cs:ip的地址,使它指向对应的1000:0地址处

注意:cs为代码段

 使用t命令执行汇编语句

注意ax的变化

 B90200为汇编指令mov cx,0002的机器代码

3.5 A命令

        以汇编指令的形式在内存中写入机器指令

在查看1000:0处的代码,并用t命令执行,注意多观察寄存器变化

第4章 寄存器(内存访问)

        4.1 内存中字的存储

        cpu中,用16位寄存器来存储一个字。高8位存放高位字节,低8位存放低位字节。在内存中存储时,由于内存单元时字节单元(一个单元存放一个字节),则要用两个地址连续的内存单元来存放,这个字的低位字节存放在低地址单元中,高字节在高地址中,例如有个数据为4E20H,高位为4E,低位为20,所以4E存放在偏移地址为0处,20存放在偏移地址为1处,构成了一个字单元

  • 0地址单元中存放的字节型数据为20H
  • 0地址字单元中存放的字节型数据为4E20H

4.2 DS和[address]

        CPU要读写一个内存单元时,必须先给出这个内存单元的地址,在8080CPU中,内存地址由段地址和偏移地址组成的。8086CPU中有一个DS寄存器通常用来存放数据的段地址。

mov ax,1000H
mov ds,ax
mov bx,[0]

上面代码中将1000H送入ax寄存器中,并将再将ax的数据送入ds段地址中,此时ds的段地址为1000H,[0]为内存单元的偏移地址,将1000:0处的数据输入到bx中

注意:ds:1000H 写法是错误的,不能直接写入,需要通过通用寄存器间接传入

将ax的数据0023H送到ds段中偏移地址为0(1000:0)处

mov ax,1000H
mov ds,ax
mov ax,0023H
mov [0],ax

我们直接进入debug中验证

 

最后成功查找到ds段中数据

4.3 字的传送

        使用mov指令在寄存器和内存之间进行字节型数据的传送。因为8086CPU是16位结构,有16根数据线,所以一次性传送16位的数据,即一次性传送一个字

mov bx,1000H
mov ds,bx
mov ax,[0]   # 1000:0处的字型数据送入ax
mov [0],cx   # cx中的16位数据送到1000:0处

 例如:右图为内存情况

  指令             执行后寄存器的内容                    说明
mov ax,1000H            ax=1000H                  前两条命令目的是将ds设置为1000H
mov ds,ax               ds=1000H

mov ax,[0]              ax=1123H                  1000:0处存放的字型数据送入ax
                                                  1000:1单元存放字型数据的高8位: 11H
                                                  1000:0单元存放字型数据的低8位: 23H
                                                  所以1000:0处存放字型数据为1123H
                                                  指令执行时,字型数据的高8位送入 ah,
                                                  低8位送入 al 中  


mov bx,[2]              bx=6622H                        原理同上
mov cx,[1]              cx=2211H
add bx,[1]              bx=8833H
add cx,[2]              cx=8833H

 4.4 栈

        在这里,我们对栈的研究仅限于这个角度:栈是一种具有特殊的访问方式的存储空间,特殊在于,最后进入这个空间的数据,最先进入这个空间的数据,最先出去

       我们可以用一个盒子和3本书来描述这个栈的基本操作方式:

       将三本书以高等数学,c语言,软件工程的顺序放入

         那么如何取出呢?显然,如果要取出数据,那么就要从最上面开始依次取出

 

以程序化的角度来看,应该有一个标记这个标记一直指示着盒子最边上的书

如果说上面的例子中盒子就是一个栈,我们可以看出,栈有两个基本操作:入栈和出栈,入栈就是将一个新的元素放到栈顶,出栈就是从栈顶取出这一个元素(先进后出)

4.5 CPU 提供的栈机制

        在8086cpu编程时候,可以将一段内存当作栈使用

        它提供了入栈和出栈的指令,最基本的是PUSH(入栈)POP(出栈)

push ax         表示将寄存器ax中的数据送入栈中
pop  ax         表示从栈顶取出数据送入ax

出栈和入栈操作都是以字为基本单位进行的

      下面举例说明,我们可以将10000H~1000FH这段内存当作栈来使用

 其指令是:      (注意:字型数据用两个单元存放,高地址单元存高8位,低地址单元存放低8位)

mov ax,0123H
push ax
mov bx,2266H
push bx
mov cx,1122H
push cx
pop ax
pop bx
pop cx

那么CPU如何知道当前要执行的指令所在的位置呢?

答案是:CS:IP 中存放着段地址和偏移地址

问题是CPU如何知道栈顶的位置呢,显然也应当有相应的寄存器来存放栈顶的地址

段寄存器SS 和 寄存器 SP,栈顶的段地址存放在SS中,偏移地址存放在SP中

在任意时刻,SS:SP指向栈顶元素。push指令和pop指令执行时,CPU从SS和SP中得到栈顶地址

 push ax的执行,由以下两步完成。

(1) SP=SP-2,SS:SP 指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶

(2) 将ax中的内容送入 SS:SP 指向的内存单元处,SS:SP 此时指向新栈顶

        从图中我们可以看出,入栈时,栈顶从高地址向低地址方向增长

        如果将10000H~1000FH这段空间当作栈,初始状态栈是空的,此时,

        SS=1000H,SP=0010H

将10000H~10000F这段空间当作栈段,SS=1000H,栈空间的大小为16字节,栈最底部的字单元地址为1000:000E(注意:这里是字单元,一个字单元存放两个字节)任意时刻,SP:SP指向栈顶,当栈中只有一个元素时候,SS=1000H,SP=000E。栈为空,相当于栈中唯一的元素出栈,出栈后,SP=SP+2,SP原来为000EH,加 2 后 SP=10H,当栈为空时,SS=1000H,SP=0010H

        换角度看,任意时刻,SS:SP指向栈顶元素,当栈为空时候,栈中没有元素,也就不存在栈顶元素,所以SS:SP 只能指向栈的最底部单元下面的单元,该单元的偏移地址为栈最底部的字单元的偏移地址 + 2 ,栈最底部字单元的地址为1000:00)E,所以栈空时,SP=0010H

接下来我们来描述pop指令的功能,例如pop ax

pop ax 与 push ax执行过程恰好相反

(1) 将SS:SP指向的内存单元处的数据送入ax中

(2) SP=SP+2, SS:SP 指向当前栈顶下面的单元,以当前栈顶下面的单元为新栈顶

4.6 push,pop指令

push 寄存器            ;将一个寄存器中的数据送入栈
pop 寄存器             ;出栈,用一个寄存器接收出栈的数据

push 段寄存器            ;将一个段寄存器中的数据送入栈
pop 段寄存器             ;出栈,用一个段寄存器接收出栈的数据

push 内存单元            ;将一个内存字单元处的字送入栈
pop 内存单元             ;出栈,用一个内存字单元接受出栈的数据
比如:
mov ax,1000H            
mov ds,ax               ; 内存单元的段地址要存放在ds中
push [0]                ; 将1000:0处的字压入栈中
pop [2]                 ; 出栈,出栈的数据送入1000:2处

第5章 第一个程序

 5.1 基础:

    我们来分析此段代码:

assume cs:code

code segment
    mov ax,0123H
    mov bx,0456H
    add ax,bx
    add ax,ax

    mov ax,4c00H
    int 21H
code ends

end

 1,伪指令

(1)

        在汇编语言源程序中,包含两种指令,一种是汇编指令,一种是伪指令。

        汇编指令前面我们了解过了,那么伪指令是什么呢?

        XXX segment

        ...........

        XXX ends

        segment 和 ends是一对成对使用的伪指令,这是在写可被编译器编译的汇编程序时必须要用到的一对伪指令。segment 和 ends 的功能是定义一个段,segment说明一个段的开始,ends 说明一个段的结束。一个段必须有一个名称来标识格式为(段的名称可以自己命名):

        段名 segment

        ...........

        段名 ends

在上面的程序中我们定义了一个段,段名称为" code" ,从这个段开始,到code ends 结束

 (2)end

        end是一个汇编程序的结束标记,编译器在编译汇编程序时,如果碰到了伪指令end,就结束了对源程序的编译,注意不要混淆了end 和 ends

 

(3)assume

        含义为“假设”,它假设某一段寄存器和程序中的某一个用segment.....ends 定义的段相关联,使编译器可以将段寄存器和某一个具体的段相关联

  (4)  程序的返回

        回过头看上面程序中的两条指令

mov ax,4c00H
int 21H

在目前阶段,不必去理解int 21H指令的含义,和为什么要在这条指令前面加上指令mov ax,4c00H,只需要知道,在程序的末尾使用这两条指令可以实现程序的返回

5.2 编译源程序

        我们需要用到 masm 汇编编译器,在第3章提供了本章节所用的工具

8088 汇编跳转 一、状态寄存器 PSW(Program Flag)程序状态字寄存器,是一个16位寄存器,由条件码标志(flag)和控制标志构成,如下所示: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0         OF DF IF TF SF ZF   AF   PF   CF 条件码: ①OF(Overflow Flag)溢出标志。溢出时为1,否则置0。 ②SF(Sign Flag)符号标志。结果为负时置1,否则置0. ③ZF(Zero Flag)标志,运算结果为0时ZF位置1,否则置0. ④CF(Carry Flag)进位标志,进位时置1,否则置0. ⑤AF(Auxiliary carry Flag)辅助进位标志,记录运算时第3位(半个字节)产生的进位置。有进位时1,否则置0. ⑥PF(Parity Flag)奇偶标志。结果操作数中1的个数为偶数时置1,否则置0. 控制标志位: ⑦DF(Direction Flag)方向标志,在串处理指令中控制信息的方向。 ⑧IF(Interrupt Flag)中断标志。 ⑨TF(Trap Flag)陷井标志。 二、 直接标志转移(8位寻址) 指令格式 机器码 测试条件 如...则转移     指令格式 机器码 测试条件 如...则转移 JC 72 C=1 有进位 JNS 79 S=0 正号 JNC 73 C=0 无进位 JO 70 O=1 有溢出 JZ/JE 74 Z=1 /等于 JNO 71 O=0 无溢出 JNZ/JNE 75 Z=0 不为/不等于 JP/JPE 7A P=1 奇偶位为偶 JS 78 S=1 负号 JNP/IPO 7B P=0 奇偶位为奇 三、间接标志转移(8位寻址) 指令格式 机器码 测试格式 如...则转移 JA/JNBE(比较无符号数) 77 C或Z=0 >  高于/不低于或等于 JAE/JNB(比较无符号数) 73 C=0 >=  高于或等于/不低于 JB/JNAE(比较无符号数) 72 C=1 <  低于/不高于或等于 JBE/JNA(比较无符号数) 76 C或Z=1  大于/不小于或等于 JGE/JNL(比较带符号数) 7D S异或O=0 >=  大于或等于/不小于 JL/JNGE(比较带符号数) 7C S异或O=1 <  小于/不大于或等于 JLE/JNG(比较带符号数) 7E (S异或O)或Z=1 <=  小于或等于/不大于
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值