chapter1-chapter6

发布时间 2023-11-03 22:46:16作者: scrutiny-span

chapter1:基础知识

机器语言

机器语言就是机器指令的集合。电子计算机的机器指令就是一列二进制数字。

汇编语言

  1. 汇编指令:汇编语言的主题就是汇编指令,汇编指令是机器指令便于记忆的书写格式,同机器指令一一对应。
  2. 汇编编译器(汇编器):将汇编源程序(由汇编指令组成)转换成机器指令。
  3. 汇编语言的组成
    1. 汇编指令:机器码的助记符,有对应的机器码
    2. 伪指令:没有对应的机器码,由汇编编译器执行,计算机并不执行
    3. 其他符号:比如+,-,*,/等,由汇编编译器识别,没有对应的机器码

chapter2:寄存器

不同的CPU,寄存器的个数、结构是不同的。

1.通用寄存器

  1. 8086CPU的所有寄存器都是16位的,可以存放两个字节。AX,BX,CX,DX这四个寄存器通常用来存放一般性的数据,被称为通用寄存器。
  2. 8086CPU的上一代CPU中的寄存器都是8位的,为了保证兼容,8086CPU的AX,BX,CX,DX这四个寄存器可以分为两个独立使用的8位寄存器。比如说AX分为AH,AL两个八位的寄存器。

2.字在寄存器中的存储

出于兼容性的考虑,8086CPU可以一次性处理以下两种尺寸的数据:

  1. 字节:一个字节可以存放在一个8位寄存器中
  2. 字:一个字由两个字节组成,一个字可以存放在一个16位寄存器中。

3.一条汇编指令示例

  1. 示例
// 执行该指令前ax这个16位寄存器的数据为00C5H
// al这个八位寄存器的数据为C5H
// 问题:执行该指令后AX中的数据为多少?
add al, 93H

// ax的数据为0058H,al与93H相加得到的一位进位值不能在存放高8位的AH寄存器中保存。
  1. 注意事项:在进行数据传送(比如mov指令)或者运算(比如add指令)时,要注意两个指令的两个操作对象的位数一致
// 错误的指令
mov ax, bl
add al, 100H

4.段的概念

  1. 其实内存并没有分段,段的概念来自CPU。如下图所示:
  2. 在8086CPU中,存储单元的物理地址用两个元素表示,即基础地址和偏移地址。段地址 * 16(即段地址左移四位得到基础地址) + 偏移地址 = 物理地址
8086CPU有20位地址线,可以传送20位地址。
而8086CPU又是16位结构,在内部一次性处理,传输,暂时存储的地址为16位。
所以8086CPU采用一种在内部用两个16位地址合成的方法形成一个20位的物理地址。
  1. CPU可以使用不同的段地址和偏移地址形成同一个物理地址。
  2. 可以根据需要将地址连续、起始地址为16的倍数的一组内存单元定义为一个段。

5.段寄存器

8086CPU在访问内存时要由相关部件提供内存单元的段地址和偏移地址,送入地址加法器合成物理地址。其中段地址在8086CPU中的段寄存器存放,8086CPU有四个段寄存器:CS,DS,SS,ES。

1.CS和IP

CS为代码段寄存器,存放指令的段地址,IP为指令指针寄存器。这两个关键的寄存器指示了CPU当前要读取指令的地址。

设CS中的内容为M,IP中的内容为N。
8086CPU将从内存的第M*16 + N单元开始读取一条指令并执行。
取指结束后更新IP的值,IP的值为IP的旧值 + 指令的长度

问题:在内存中指令和数据都是以二进制信息存放,CPU根据什么来区分指令和数据?

在取指令阶段,CPU将CS和IP中的内容分别当作指令的段地址和偏移地址,
用他们合成指令的物理地址,到内存中读取指令码并执行。
2.修改CS、IP的指令

CPU从何处执行指令是由CS,IP中的内容决定的,程序员可以通过改变CS、IP中的内容来控制CPU执行目标指令。

  1. 一个简单的可以修改CS,IP寄存器的指令:jmp指令,jmp指令属于转移指令之一。
jmp 段地址:偏移地址:该指令的功能表示用指令给出的段地址修改CS,偏移地址修改IP
比如jmp 3:01B6:含义类似于mov CS,3 mov IP,01B6
jmp 某一合法寄存器:该指令功能表示用寄存器中的值修改IP
比如jmp ax:含义上类似于mov IP, ax(实际并不存在该指令)

6.代码段

要让CPU执行代码段中的指令,必须要让CS:IP指向所定义的代码段中的第一条指令的首地址。

chapter3:寄存器(内存访问)

1.内存中字的存储

字单元:存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。内存单元的类型分为字单元和字节单元,字单元和字节单元长度不同,注意区分。

2.DS和[address]

  1. DS:DS即数据段寄存器,访问数据段中的数据使用数据段的段地址+偏移地址的形式。
    1. 数据段的段地址:8086CPU中有一个DS寄存器,通常用来存放要访问数据的段地址。(数据段的段地址)
    2. 偏移地址:[address]表示一个偏移地址为address的内存单元
  2. 8086CPU不支持直接将数据直接送入段寄存器。
  3. 示例:寄存器和内存之间数据的传送
// 将内存中10000H中的数据读入al寄存器中
mov bx, 1000h   #段地址保存在bx寄存器中
# 8086CPU中不支持将数据直接送入段寄存器,因此需要通用寄存器进行中转
mov ds, bx
mov al, [0]
#这条指令表示将一个内存单元的内容送入al这个寄存器中。
#其中0表示内存单元的偏移地址,
#8086CPu自动取DS寄存器中的数据作为内存单元的段地址

// 将寄存器al中的数据送入内存单元10000H中
mov bx, 1000H # 段地址
mov ds, bx
mov [0], al

3.mov指令,sub指令,add指令

  1. mov指令的格式:mov des,source,形式有如下几种
    1. mov 寄存器,数据
    2. mov 寄存器,寄存器
    3. mov 寄存器,内存单元
    4. mov 内存单元,寄存器
    5. mov 段寄存器,寄存器
    6. mov 寄存器,段寄存器
    7. mov 段寄存器,内存单元
  2. add指令示例
// 将123B0H~123B9H的内存单元定义为数据段
// 123BH作为数据段的段地址
// 累加数据段的前三个单元的数据
mov ax, 123BH
mov ds, ax  ;ds保存数据段的段地址
mov al, 0   ;al存放累加结果
add al, ds:[0]
add al, ds:[1]
add al, ds:[2]

4.栈

1.8086CPU提供的栈机制
  1. 8086CPU提供入栈和出栈指令,入栈指令push,出栈指令pop.入栈和出栈操作都是以字为单位进行的。
push ax ;表示将寄存器ax的数据送入栈中
pop ax ;表示将从栈顶取出数据送入ax

push和pop实质上是内存传送指令,可以在寄存器和内存之间传送数据,与mov指令不同的是,push和pop指令访问的内存单元地址不是在指令中给出,而是由ss:sp指出的,同时push和pop指令还需要改变sp的内容
2. CPU如何知道栈顶的位置?任意时刻,ss:sp指向栈顶元素,CPU从SS和SP中得到栈顶的地址。
1. 段寄存器SS,堆栈寄存器sp(stack pointer)ss存放栈的段地址
2. sp存放栈顶的偏移地址
3. 栈顶超界的问题
1. 解决方案:比如说CPU有记录栈顶上限和栈底的寄存器(只是设想,8086CPU并没有这样的寄存器)
4. push,pop指令的格式

push 寄存器 ;将一个寄存器中的数据入栈
push 段寄存器 ;将一个段寄存器中的数据入栈
push 内存单元 ;将一个内存字单元处的字入栈
pop 寄存器  ;用一个寄存器接受出栈的数据
pop 段寄存器 ;用一个段寄存器接受出栈的数据
pop 内存单元 ;用一个内存字单元接收出栈的数据

栈操作以字为单位

2.push,pop指令
  1. push指令:比如说push ax这条指令,其含义如下
    1. sp = sp - 2(前提栈从高地址向低地址增长)
    2. 将寄存器ax的内容送入ss:sp指向的内存单元。
  2. pop指令:比如说pop bx这条指令,其含义如下
    1. 将SS:SP指向的内存单元的数据送入bx中
    2. sp = sp + 2
  3. 示例:利用栈交换寄存器AX和寄存器BX中的数据
// 假设10000H~1000FH作为栈的空间,初始状态栈为空
// 设置AX=001AH,BX=001BH
// 栈顶由高地址向低地址增长

mov ax, 1000H
mov ss, ax ;设置栈的段地址,不能直接向段寄存器送入数据
mov sp, 0010H ;初始化栈顶
mov AX,001AH
mov bx,001BH
push ax
push bx
pop ax
pop bx
3.栈段
  1. 栈段:可以根据需要,将一组内存单元定义为一个段,当作栈空间来使用,此外还需要设置SS和SP寄存器的值,从而定义了一个栈段。

总结:

  1. 对于数据段,将他的段地址存放在DS寄存器中,使用mov、sub等访问内存单元中的指令时,CPU就将定义在数据段中的内容当作数据来访问
  2. 对于代码段,将他的段地址存放在CS寄存器中,将段中第一条指令的偏移地址存放在IP中,这样CPU就将执行定义在代码段中的指令
  3. 对于栈段,将栈的段地址存放在SS寄存器中,将栈顶的偏移地址存放在SP寄存器中,这样CPU再执行pop、push等栈操作的指令时,将我们定义的栈段当作栈空间使用。

chapter4:第一个程序

1.生成可以在OS中直接运行的可执行文件的步骤:
  1. 使用汇编语言编写汇编源程序
  2. 对汇编源程序进行编译和链接:使用到两个工具,汇编语言编译程序和链接程序
2.第一个简单的汇编语言源程序
;将用作代码段的段codesg和CPU中的段寄存器CS关联起来
assume cs:codesg
;一个汇编程序由多个段组成,下面定义一个代码段
codesg segment
    ;求2的三次方(2+2+2+2)
    mov ax,2
    add ax,ax
    add ax,ax
codesg ends
end
  1. 伪指令:在汇编语言源程序中,包含两种指令,一种是汇编指令,另一种是伪指令。汇编指令有对应的机器码的指令,可以被编译成机器指令。伪指令没有对应的机器指令,不被CPU执行,它是由编译器执行的指令。
  2. 上述程序定义了三种伪指令
    1. 段名 segment ...段名 ends:segment和ends是一对成对使用的伪指令,它们的功能是定义一个段,segment说明一个段的开始,ends说明一个段的结束
    2. end:end是一个汇编程序的结束标记,标记整个程序的结束,汇编器遇到了这个伪指令就停止对汇编源程序的编译
    3. assume:用来假设某一个段寄存器和程序中的某一个用segment...end定义的段相关联。
3.与结束相关的概念(段结束,程序结束,程序返回)

image.png

chapter5:[BX]和loop指令

1.[bx]

  1. [bx]:表示偏移地址存放在bx寄存器中
  2. 指令示例
mov ax,[bx]
功能:bx存放的数据作为一个偏移地址EA,段地址SA默认在ds寄存器中
将SA:EA处的一个字单元的数据送入ax中,即(ax) = ((ds) * 16 + (bx))

mov [bx],ax
功能:将ax中的数据送入内存SA:EA处
((ds) * 16 + (bx)) = (ax)

2.loop指令

  1. loop指令的格式:loop 标号,在汇编语言中,标号代表一个地址。经过汇编编译器翻译后,标号就是一个地址。当执行到loop指令的时候,判断cx的值不为0,则更改IP寄存器的值为标号的值。
  2. loop指令的执行:CPU执行loop指令的时候,进行两步操作。
    1. (cx) = (cx) - 1 ;cx寄存器用来存放循环次数
    2. 判断cx的值,如果不为0则转至标号处执行程序,如果为0则向下执行
  3. loop指令一般用于实现循环,cx寄存器用来存放循环的次数
  4. loop指令示例
// 使用loop指令完成计算2^12次方
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

3.[bx]和loop指令的联合使用

1.段前缀

可以在访问内存单元的指令中显示的给出内存单元的段地址所在的段寄存器。例如mov ax,ds:[bx]表示将一个字单元送入ax寄存器中,偏移地址在bx中,段地址在ds中。其中ds:这种叫做段前缀

chapter6:包含多个段的程序

1.在代码段中使用数据

  1. dw即define word,表示定义字型数据。例如dw 0123H
  2. start用于指明程序的入口所在。
assume cs:code
code segment
    ...(数据)
    start:mov ax,0 ;表示这是程序执行的第一条指令
    ...(代码)
code ends
end start   ;end指令指明程序的入口在标号start处,这样CPU加载程序才知道程序的入口地址

3. 如何知道哪一条指令是程序的第一条要执行的指令?

由可执行文件的描述信息指明,描述信息主要是编译、
连接程序对源程序中相关伪指令进行处理得到的信息。
伪指令end描述了程序的结束和入口。

注:可执行文件由描述信息和程序组成,程序来自于
源程序中定义的汇编指令和数据

2.在代码段中使用栈

  1. 使用dw定义数据来取得空间,然后将这段空间作为栈空间使用,核心语句如下:
mov ax, cs ;将代码段的段地址作为栈段寄存器的值
mov ss, ax
mov sp, xxx;初始化栈顶偏移量

3.将数据、代码、栈放入不同的段

  1. 定义多个段:和定义一个段的方法没有区别,只是各个段名称不同
  2. 对段地址的引用:在程序中,段名就相当于一个标号,它代表了段地址
  3. 示例:
assume cs:code,ds:data,ss:stack
;定义数据段
data segment
    ...
data ends
;定义存放栈的段
stack segment
    ...
stack ends
;定义代码段
code segment
    ...
    start:...  ;start后的第一条指令为程序执行的第一条指令
code ends
end start ;end指令指明程序的入口