汇编语言课程设计课程内容.md

分类:N08_C与汇编

标签:

### 汇编语言课程设计课程内容

#### 1. 绪论 

##### 1.1 我们要学汇编语言


> 汇编语言仍在发挥不可替代的作用:

>>

>> 效率

>>> 运行效率:开发软件的核心部件,快速执行和实时响应

>>> 开发效率:做适合的事,开发效率无敌

>>

>> 底层:计算机及外围设备的驱动程序

>>> 操作系统的内核

>>> 嵌入式系统:家用电器、仪器仪表、物联网等等

>

> 汇编语言在学习计算机中起到的独特作用-直击计算机系统的核心

>> 便于加深对计算机原理和操作的理解

>> 通过学习和使用汇编语言,能够感知,体会和理解及其的逻辑功能

>>> 向上为理解各种软件系统的原理,打下技术理论基础

>>> 向下为掌握硬件系统的原理,打下实践应用基础

>>

>> 学会底层的程序调试和错误分析方法


##### 1.2 由机器语言到汇编语言

###### 机器语言与机器指令

1. 机器语言是机器指令的集合

2. 机器指令是一台机器可以正确执行的命令

3. 机器指令是由一串二进制数表示,例:01010000

4. 电平脉冲:高低电平组成

5. 早期程序员门的工作状态

> 将0、1的数字编程的程序代码打在卡带或卡片上,1打孔,0不打孔,再将程序通过纸带机或卡片机输入计算机,进行运算。

###### 汇编语言与汇编指令

1. 汇编语言的主题是汇编指令

2. 汇编指令和机器指令的差别在于指令的表示方法上

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

> 汇编指令是机器指令的助记符

```

机器指令:1000100111011000

操作:将寄存器BX的内容送到AX中

汇编指令:MOV AX BX

```

3. 寄存器:CPU中可以存储数据的器件,一个CPU中有多个寄存器

4. 用汇编语言编写程序的工作过程

程序员->编写->汇编指令->经过编译器->生成->机器码

```

;汇编语言程序实例

assume cs:codesg

codesg segment

start:

mov ax, 0123H

mov bx, 0456H

add ax, bx

add ax, ax


mov ax, 4c00h

int 21h

codesg ends

end

```

> 以上代码说明:

>> 伪指令(由编译器执行)如:assume, codesg

>> 汇编指令(机器码的助记符)如:mov, add

>> 其他符号(由编译器识别)如:start, end


##### 1.3 计算机的组成

##### 1.4 内存的读写与地址空间

##### 1.5 汇编语言实践环境搭建




#### 2. 访问寄存器和内存

##### 2.0 阶段导学

##### 2.1 寄存器及数字存储

1. 8086CPU有14个寄存器

- 通用寄存器:AX、BX、CX、DX

- 变址寄存器:SI、DI

- 指针寄存器:SP、BP

- 指令指针寄存器:IP

- 段寄存器:CS、SS、DS、ES

- 标志寄存器:PSW

2. 共性

- 8086CPU所有的寄存器都是16位的,可以存放2个字节

##### 2.2 mov和add指令

1. 学习汇编指令 - 用中学

|汇编指令|控制CPU完成的操作|用高级语言的语法描述|

|---|---|---|

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

> 注:汇编指令不区分大小写


##### 2.3 确定物理地址的方法

1. 物理地址

- CPU访问内存单元时要给出内存单元的地址

- 所有的内存单元构成的存储空间室一个一维的线性空间

- 每一个聂村单元在这个空间中都有唯一的地址,这个唯一的地址称为物理地址

- 事实

* 8086有20位地址总线,可传递20位地址,寻址能力为1M

* 8086是16位结构的CPU

1. 运行器一次最多可以处理16位的数据,寄存器的最大宽度为16位

2. 在8086内部处理的、传输、暂存的地址也是16位,寻址能力也只有64KB

- 问题:8086如何处理在地址空间上的这个矛盾

- 8086CPU的解决办法:

* 用两个16位地址(段地址、偏移地址)合成一个20位的物理地址

- 地址加法器合成物理地址的方法

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

例如:

```

  段地址:1230

  + 偏移地址: 00C8

   ---------------

    物理地址:123C8

```

- 本质含义:

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


##### 2.4 内存的分段表示法

###### 用分段的方式管理内存

1. 8086CPU用“(段地址X16)+偏移地址= 物理地址”的方式给出内存单元的物理地址

2. 存层并没有分段,段的划分来自于CPU

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

1. 起始地址(基础地址)为10000H

2. 段地址位1000H,大小为100H

> 或者:

1. 起始地址(基础地址)为10000H和10080H,

2. 段地址为1000H和1008H,大小均为80H

###### 用不同的段地址和偏移地址形成同一个物理地址

|物理地址|段地址|偏移地址|

|-|-|-|

|21F60H|2000H|1F60H|

|21F60H|2100H|0F60H|

|21F60H|21F0H|0F60H|

|21F60H|21F6H|0000H|

|21F60H|1F00H|2F60H|


1. 在8086PC机种存储单元地址的表示方法


2. 例如:数据在21F60H内存单元中,段地址是2000H,说法

* (a)数据存在内存2000:1F60单元中;

* (b) 数据存在内存的2000H段中的1F60H单元中


3. 偏移地址16位,变化范围为0-FFFH,用偏移地址最多寻址64KB

4. 例:给定段地址2000H,用偏移地址寻址的范围:20000H-2FFFFFH,共64K

###### 段地址很重要! 用专门的寄存器存放段地址

> 4个段寄存器:

- CS-代码寄存器

- DS-数据段寄存器

- SS-栈段寄存器

- ES-附加段寄存器

> 偏移地址可以用多种方式提供- 8086丰富的取址方式

##### 2.5 Debug的使用

###### Debug是什么

1. Debug 是DOS系统中的著名的调试程序,也可以运行在windows系统模式下

2. 使用Debug程序,可以查看CPU各种寄存器中的内容、内存的情况,并且在机器指令级跟踪程序的运行

##### 2.6 CS、IP与代码段

###### 两个关键的寄存器

1. CS:代码段寄存器

2. IP:指令指针寄存器

3. CS:IP :CPU将内存中CS:IP 指向的内容当作指令执行

4. 示例:在CS和IP指示下代码的执行

* 8086CPU当前状态:CS中内容为2000H,IP中内容为0000H,通过地址加法器获得内存实际地址,并通过数据总线将指令输入到指令缓冲器,等待cup执行。

* 内存20000H-20009H处存放着可执行的机器代码

5. 8086PC工作过程的简要描述:

- 1.从CS:IP 指向内存单元读取指令,读取的指令进入指令缓冲器

- 2.IP = IP + 所读取指令的长度,从而指向下一条指令

- 3.执行指令,转到步骤1,重复这个过程


##### 2.7 jmp指令

###### 修改CS、IP的指令

1. 事实:执行何处的指令,取决于cs:ip

2. 应用:可以通过改变CS、IP中的内容,来控制CPU要执行的目标指令

3. 问题:如何修改CS、IP的值

4. 方法1:Debug中的R命令可以修改寄存器的值-rcs,rip

* DeBug 是调试手段,并非程序方法

5. 方法2:用指令修改(是禁止的,8086CPU不提供对CS和IP修改的指令)

* mov cs 2000H

* mov ip 0000H

6. 方法3:转移指令jmp

###### 转移指令jmp 

1. 同时修改CS、IP的内容

* jmp 段地址:偏移地址

jmp 2AE#:3

jmp 3:0B16

* 功能:用指令中给出的段地址修改CS,偏移地址修改IP。

2. 仅修改IP的内容

* jmp某一合法寄存器

jmp ax (类似与 mov IP,ax)

jmp bx (类似与 mov IP,bx)

##### 3.1 内存中字的存储

###### 内存中字的存储

1. 事实:对8086CPU,16位作为一个字

2. 问题:16位的字存储在一个16位的寄存器中,如何存储

3. 回答:高8位放高字节,低8位放低字节

4. 问题:16位的字在内存中需要2个连续字节存储,怎么存放?

5. 回答:低位字节存在低地址单元,高位字节存在高地址单元

* 20000D(4E20H)存放0,1两个单元,18D(0012H)存放在2,3两个单元

###### 字单元

1. 字单元:由2个地址连续的内存单元组成,存放一个字形数据(16位)

2. 原理:在一个字单元中,低地址单元存放低位字节,高地址单元存放高位字节

* 在起始位置为0的单元中,存放的是4E20H

* 在起始位置为2的单元中,存放的是0012H

3. 问题:

1. 0地址单元中存放的字节型数据是(20H)

2. 0地址字单元中存放的字型数据是(4E20H)

##### 3.2 用DS和[address]实现字的传送

###### 要解决的问题:CPU从内存单元中要读取数据

1. 要求:CPU要读取的一个内存单元的时候,必须先给出这个内存单元的地址;

2. 原理:在8086PC中,内存地址由段地址和偏移地址组成(段地址:偏移地址)

3. 解决方案:DS和[address]配合

1. 用DS寄存器存放要访问的数据的段地址

2. 偏移地址用[...]形式直接给出

```

例1:

mov bx, 1000H

mov ds, bx

mov al, [0]

```

> 将10000H(1000:0)中的数据读到al中

```

例2:

mov bx, 1000H

mov ds, bx

mov [0], al

```

> 将al中的数据写到10000H(1000:0)中

4. 将段地址送入DS的两种地址方法

* 法1 mov ds, 1000H

* 法2 mov bx, 1000H

  mov ds, bx

> 8086CPU 不支持将数据直接送入段寄存器(硬件设计的问题)

* 套路:数据->一般寄存器->段寄存器

```

内存:

10000H 23

10001H 11

10002H 22

10003H 66

指令

mov ax, 1000H

mov ds, ax

mov ax, [0]

mov bx, [2]

mov cx, [1]

add bx, [1]

add cx, [2]

```

##### 3.3 DS与数据段

###### 1.用mov 指令操作数据

|指令形式|示例|

|-|-|

|mov 寄存器,数据|mov ax,8|

|mov 寄存器,寄存器|mov ax,bx|

|mov 寄存器, 内存单元|mov ax, [0]|

|mov 内存单元,寄存器|mov [0], ax|

|mov 段寄存器,寄存器|mov ds, ax|

> 已知:mov 段寄存器, 寄存器

* 推测:mov 寄存器, 段寄存器

> 已知:mov 内存单元,寄存器

* 推测:mov 内存单元,段寄存器

* 推测:mov 段寄存器, 内存单元

> 已知:mov 寄存器, 数据

* 推测:mov 段寄存器, 数据(不行)

###### 2.加法add和减法sub指令

|add指令形式|示例|

|-|-|

|add 寄存器,数据|add ax, 8|

|add 寄存器,寄存器|add ax, bx|

|add 寄存器,内存单元|add ax, [0]|

|add 内存单元,寄存器|add [0], ax|

##### 3.4 栈与栈操作的实现

###### 1. 栈结构

1. 栈是一种只能在一端进行插入或者删除的数据结构

2. 栈有两个基本的操作:入栈和出栈

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

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

3. 栈顶的元素总是最最后入栈,需要出栈时,又最先被从栈中取出

4. 栈的操作规则:LIFO (Last In First Out, 后进先出)

5. CPU提供的栈机制

1. 现在的CPU中都有栈的设计

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

3. 基于8086CPU的编程,可以将一端内存当作栈来使用

6. PUSH(入栈)和POP(出栈)指令

1. push ax: 将ax中的数据送入栈中

2. pop ax: 将栈顶取出数据送入ax(以字为单位对栈进行操作)

3. 例:将100000H-1000FH内存当作栈来使用

```

mov ax, 0123H

push ax 

mov bx, 2266H

push bx

mov cx, 1122H

push cx

pop ax

pop bx

pop cx

```

7. 问题

1. CPU如何知道一段内存空间被当作栈使用?

2. 执行push和pop的时候,如何知道那个单元是栈顶单元

8. 回答:

> 8086 CPU中,有2个与栈相关的寄存器:

- 栈段寄存器:ss -存放栈顶的段地址

- 栈顶指针寄存器: sp -存放栈顶的偏移地址

> 任意时刻,SS:SP 指向栈顶元素

9. 栈的操作

```

mov ax, 1000H

mov ss, ax

mov sp, 0010H


mov ax, 001AH

mov bx, 001BH


push ax

push bx


pop ax 

pop bx

```

> 说明:

1. push ax

- 1 SP = SP -1;

- 2 将ax中的内容送入ss:sp 指向的内存单元处,ss:sp此时指向新栈顶

2. pop ax

- 1 将 ss:sp 指向的内存单元处的数据送入ax中

- 2 SP = SP+2, ss:sp 指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶

3. 栈顶超界问题

- 如何能够保证在入栈,出栈时,栈顶不会超出栈空间?

* 当栈满的时候再使用push指令入栈,将发生栈顶超界问题

* 当栈空的时候再使用pop指令出栈,将发生栈顶超界问题

4. 栈顶超界问题的解决

- 只能靠程序员自己解决

10. 栈的总结

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

2. 执行push和pop指令时,SP中的内存自动改变

3. 8086CPU提供的栈操作机制:

- 在SS,SP中存放栈顶的段地址和偏移地址,入栈和出栈指令根据SS:SP指示的地址,按照栈的方式访问内存单元

- push指令执行步骤:

1. sp=sp-2

2. 向ss:sp指向的字单元中送入数据

- pop指令的执行步骤

1. 从ss:sp指向的字单元中读取数据

2. sp=sp-2


##### 3.5 关于“段”的总结

###### 1. 各种段

1. 基础:物理地址 = 段地址X16+偏移地址

2. 做法

1. 编程时,可以根据需要将一组内存单元定义为一个段

2. 可以将起始地址为16的倍速,长度为N(N<=64K)的一组地址连续的内存单元,定义为一个段

3. 将一端内存定义为一个段,用一个段地址指示段,用偏移地址反问段内的单元,在程序中可以完全由程序员安排

3. 三种段

> 1.数据段

- 1. 将段地址放到DS中

- 2. 用mov,add,sub等访问单元的指令时,CPU将我们定义的数据段中的内容当作数据段来访问

> 2.代码段

- 1. 将段地址放在CS中,将段中第一条指令的偏移地址放在IP中

- 2. CPU将执行我们定义的代码段中的指令

> 3.栈段

- 1. 将段地址放在SS中,将栈顶单元的偏移地址放在SP中

- 2. CPU在需要进行栈操作(push,pop)时,就将我们定义的栈段当作栈空间来用

4. 综合示例:按要求设置段并执行代码

```

mov bx, 1000H

mov ds, bx

mov bx, 1001H

mov ss, bx

mov sp, 10H

mov ax, [0]

mov bx, [2]

push ax

push bx

pop ax

pop bx

mov [0], ax

mov [2], bx

```

#### 3. 汇编语言程序

##### 4.1 用汇编语言写的源程序

###### 1. 用汇编语言编写程序的工作过程

1. 汇编程序:包含汇编指令和伪指令的文本

2. 汇编指令:对应机器码的指令,可以被编译为机器指令,最终被CPU执行

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

4. 谁来执行伪指令呢?

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

5. 程序返回(套路):程序结束运行后,将CPU的控制权交给使他运行的程序(常为DOS系统)

###### 2. 程序中的三种伪指令

1. 段定义

1. 一个汇编程序是由多个段组成的,这些段被用来存放代码,数据或当作栈空间来使用

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

3. 定义程序中的段:每一个段都需要有段名

* 段名 segment --段的开始

* 段名 ends    --段的结束

2. end(不是ends)

> 汇编程序的结束标记。若程序结尾处不加end,编译器在编译程序时,无法知道程序在何处结束

3. assume(假设)

> 含义是假设每一段寄存器和程序中的某一个用segment...ends定义的段相关联--assume cs:codesg指CS寄存器与codesg关联,将定义的codesg当作程序的代码段使用

```

assume cs:codesg

codesg segment

start:

mov ax, 0123H

mov bx, 0456H

add ax, bx

add ax, ax


mov ax, 4c00h

int 21h

codesg ends

end

```

###### 3. 源程序经编译链接后变为机器码

###### 4. 汇编程序的结构

1. 在Debug中直接写入指令编写的汇编程序

1. 适用于功能简单,短小精悍的程序

2. 只需要包含汇编指令即可

```

debug

-a

mov ax, 27a1

add ax, 892C

```

2. 单独编写成为源程序后再编译为可执行文件的程序

1. 使用与编写大程序

2. 需要包含汇编指令,还要有知道编译器工作的伪指令

3. 源程序有一些段构成,这些段存放代码、数据,或将某个段当做栈空间。

```

assume cs:code, ds:data, ss:stack

data segment

dw 0123H,0456H,0789H,0abcH,0defH

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

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

  ...

code ends

end

```

##### 4.2 由源程序到程序运行

##### 4.3 用Debug跟踪程序的执行

##### 5.1 [...]和(...)

##### 5.2 Loop指令

##### 5.3 Loop指令使用再例

##### 5.4 段前缀的使用

##### 6.1 在代码段中使用数据

##### 6.2 在代码段中使用栈

##### 6.3 将数据、代码、栈放入不同段


#### 4. 内存寻址方式

#### 5. 流程转移和子程序

#### 6. 中断及其应用

#### 7. 高级汇编语言技术

修改内容