[SingleSele]汇编语言(x86)学习笔记(未完)

第3章 寄存器

3.1 内存中字的存储

大端法——2字节存储一个字,高字节存储高8位,低字节存储低8位。
将起始地址位N的字单元称为N地址字单元
例:

地址 内容
0 20H
1 4EH
2 12H
3 00H
0地址存储的字节型数据为20H
2地址存储的字型数据为0012H

3.2 DS和[address]

DS寄存器存放要访问内存单元的段地址
[]用来表示内存单元的偏移量
如:

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

表示将1000:0的数据读到al中
8086CPU不支持将立即数送入寄存器ds,只能从其他寄存器送入ds

3.3 字的传送

只要在mov指令中给出16位的寄存器就可以进行16位数据的传送了

3.4 mov add sub指令

三者都可以对段寄存器进行读写操作

3.5 数据段

将一组长度位N<=64kB,地址连续,且起始地址为16倍数的内存单元当作专门存储数据的内存空间,即数据段
可以将ds设为数据段的段地址,再根据需要访问其中的具体单元
如:

;累加123B0H到123B2H的数据
mov ax,123BH
mov ds,as
mov al,0
add al,[0]
add al,[1]
add al,[2]

3.6 栈

3.7 CPU提供的栈机制

8086CPU提供入栈和出栈指令
栈是虚拟结构,CPU不记录栈底位置和栈的大小
SS:SP指向栈顶
需要程序员自行判定是否越界

3.8 栈顶超界的问题

3.9 push pop指令

访问内存段中数据时,系统自动从ds中获取段地址

3.10 栈段

指ss寄存器指向的地址,是逻辑结构,系统并不会记录栈的地址

第4章 第一个程序

4.1 一个源程序从写出到执行的过程

①编写汇编源程序

产生了一个存储源程序的文本文件

②对源程序进行编译链接

使用汇编语言编译程序对源程序文件中的源程序进行编译,产生目标文件,再用连接程序对目标文件进行链接,生成可执行文件
可执行文件包括

  • 程序和数据
  • 程序的描述信息

③执行可执行文件

4.2 源程序

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

下面对程序进行说明

(1)伪指令

没有对应的机器指令,最终不被CPU执行,而是被编译器执行,进行相关的编译指令

①segment

XXX segment
    ......
    ......
    XXX ends

定义一个段,XXX为段名
一个源程序中所有将被计算机处理的信息:指令、数据、栈,被划分到了不同的段中

②end

end是一个汇编程序的结束标记,编译器再编译汇编程序的过程中,如果碰到了end就结束对源程序的编译

③assume

假设某一段寄存器和程序中的某一个用segment定义的段相关联,通过assume说明这种关联
如 assume cs:codesg 表示cs寄存器与codesg代码段有关

(2)源程序中的“程序”

指源程序中最终由计算机执行,处理的指令或数据
最终被转换为二进制机器码,存储在可执行文件中

(3)标号

比如codesg,作为一个段的名称,指代了一个地址,该名称最终被编译、连接程序处理为一个段的段地址

(4)程序的结构

源程序是由一些段构成的,我们可以在这些段中存放代码、数据、或作为栈空间。

例:编程运算2^3

①定义一个段,名为abc

abc segment
abc ends

②在这个段中写入汇编指令

mov ax,2
add ax,ax
add ax,ax

③指出程序在何处结束

end

④将abc和cs联系起来

assume cs:abc
assume cs:abc

abc segment
    mov ax,2
    add ax,ax
    add ax,ax
abc ends

end

(5)程序返回

P1将P2加载入内存->CPU控制权交给P2->P2运行完毕->CPU控制权交还给P1

mov ax,4c00H
int 21H

两条程序代表程序返回

(6)语法错误和程序错误

程序在编译时被编译器发现的错误为语法错误,运行时发生的错误是逻辑错误

4.3 编辑源程序

使用任何文本编辑器均可,将结果保存为.asm格式

4.4 编译

进入c:\masm目录,运行masm.exe
若编译c:\masm\p1.asm

:p1
:p1.asm
:C:\masm\p1.asm

可以指定输出目录,默认绝对目录,默认.obj
可以指定生成列表文件的名称,也可不生成
可以指定生成交叉引用文件的名称,也可不生成
提示 0 Warnings,0 Errors
产生编译1.obj
两类错误无法编译

  • 程序中有"Severe Errors"
  • 找不到所给出的源程序文件

4.5 连接

需要对.o文件进行连接产生可执行文件
使用微软 Overlay Linker 3.60 连接器,文件名为link.exe
进入c:\masm目录,运行link.exe
若连接c:\masm\p1.obj

    :p1
    :p1.obj
    :C:\masm\p1.obj

可以指定输出目录,默认绝对目录,默认.exe
可以指定生成映像文件的名称,也可不生成
可以指定输入库文件的名称,若没有调用其他库中的子程序可不输入
提示no stack segment,忽略
连接的作用

  1. 当源程序很大时,可以分为多个源程序分开编译,再连接成一个可执行文件
  2. 程序调用了某个库中的子程序,需要将两个文件连接到一起
  3. 连接程序将目标文件处理为可执行文件

4.6 以简化的方式进行编译和连接

masm c:\1;
link c:\1;

自动忽略中间文件的生成

4.7 1.exe的执行

4.8 谁将可执行文件中的程序装载进入内存并使它运行?

操作系统的外壳
任何的操作系统都要提供shell程序,用户使用这个程序操作计算机系统进行工作
DOS中有一个程序command.com,称为命令解释器,即DOS的shell

4.9 程序执行过程的跟踪

debug 1.exe
源程序加载后寻找一段SA:0000的空闲内存区
创建一个大小为256字节的PSP区(程序前缀区)
程序起始点为SA+10H:0
用T指令单步执行,直到int 21H,用P执行

第5章 [BX]和loop指令

5.0.1 [bx]和内存单元的描述

要完整描述一个内存单元,需要两种信息

  1. 内存单元的地址
  2. 内存单元的长度(类型)

mov ax,[0]表示将一个长度为2的内存单元的内容送入ax,端地址在ds中,偏移量为0
mov ax,[bx]表示将一个长度为2的内存单元的内容送入ax,段地址在ds中,偏移量在bx中

5.0.2 loop

和循环有关

5.0.3 我们定义的描述性的符号:“()”

为了描述简洁,用(U)表示U中的内容
如:

  • (ax)表示ax中的内容,(al)表示al中的内容
  • ((ds)*16+(bx))表示ds中的address1乘以16加上bx中的address2作为新的address中的内容
  • 也可以理解为address1:address2中的内容

注:
()中的元素可以有三种类型

  1. 寄存器名 如(ax) (bl)
  2. 段寄存器名 如(ds)
  3. 内存单元的物理地址 如(20000H)

不可以出现(2000:0)((ds):1000H)等用法

5.0.4 约定符号idata表示常量

mov ax,[idata]代表 mov ax,[1] mov ax,[2] 等用法
mov bx,idata代表 mov bx,1 mov bx,2 等用法

5.1 [BX]

mov ax,[bx] 将ds:bx中的数据送入ax中,即(ax)=((ds)16+(bx))
mov [bx],ax 将ax中的数据送入ds:bx中,即((ds)
16+(bx))=(ax)

5.2 Loop指令

格式:loop 标号
执行时进行两步操作 ①(cx)=(cx)-1 ②若(cx)!=0 执行标号处程序
例:编程计算2^2,结果存在ax中

assume cs:code
code segment
    mov ax,2
    add ax,ax

    mov ax,4c00H
    int 21H
code ends
end

编程计算2^3

assume cs:code
code segment
    mov ax,2
    add ax,ax
    add ax,ax

    mov ax,4c00H
    int 21H
code ends
end 

编程计算2^12

assume cs:code 
code segment
    mov ax,2
    ;做11次add ax,ax
    mov cx,11
s:  add ax,ax 
    loop s

    mov ax,4c00H
    int 21h 
code ends
end

cx和loop指令相配合实现循环功能的3个要点:

  1. 在cx中存放循环次数
  2. loop指令中的标号所标识地址要在前面
  3. 要循环执行的程序段,要写在标号和loop指令的中间

用cx和loop指令相配合实现循环功能的程序框架如下

    mov cx,循环次数
s:
    循环执行的程序段
    loop s 

例:用加法计算123*236

assume cs:code
code segment 
    mov ax,123
    mov cx,235
s:  add ax,123
    loop s
    mov ax,4c00H
    int 21H 
code ends 
end 

注:将236加123次有助于提高运行速度

5.3 在Debug中跟踪用loop指令实现的循环程序

计算ffff:0006单元中的数乘以3,结果存储在dx中

(1)运算后结果是否会上溢出

ffff:0006是一个字节型数据,范围在0~255之间,用它和3相乘结果不会大于65535,可在dx中存放下

(2)用循环累加实现乘法,用哪个寄存器累加

ax存放ffff:6,dx累加ax

(3)ffff:6是一个字节单元,ax是一个16位寄存器,长度不同如何赋值

(ah)=0, (al)=ffff6H

程序如下:

    assume cs:code 
    code segment 
        mov ax,0ffffh
        mov ds,ax    ;ds不能直接改写
        mov bx,6H
        mov ah,0
        mov al,[bx]
        mov dx,0
        mov cx,3
    s:  add dx,ax 
        loop s 

        mov ax,4c00H
        int 21H 
    code ends 
    end 

注:在汇编源程序中,数据不能以字母开头,所以FFFF要写成0ffffh

循环指令的执行
-p 执行直到跳出循环
-g 0016 执行到CS:0016的指令

5.4 Debug和汇编编译器masm对指令的不同处理

对于mov ax,[idata],Debug将其视为mov ax,(idata),masm将其视为mov ax,idata
若需要在源程序中将内存单元中的数据送入寄存器,需要将偏移地址送入bx,再用[bx]访问内存
或者以mov ax,ds:[0]的形式给出段地址所在的段寄存器

5.5 loop和[bx]的联合应用

例:计算ffff:0~ffff:b单元中数据的和,结果存储在dx中

(1)运算后的结果是否会超出dx所能存储的范围

不会

(2)能否直接将ffff:0~ffff:b中的数据累加到dx中

不能,内存中数据是8位的,不能直接加到16位寄存器中

(3)能否直接将ffff:0~ffff:b中的数据累加到dl中

不能,dl只有8位,可能造成上溢出

(4)如何将ffff:0~ffff:b中的8位数据累加到16位寄存器中

使用一个16位寄存器做中介
程序如下:

assume cs:code 
code segment 

    mov ax,0ffffh
    mov ds,ax
    mov bx,0    ;将ds:bx指向ffff:0

    mov dx,0    ;存储结果
    mov cx,12    ;循环次数

s:  mov al,[bx]
    mov ah,0
    add dx,ax
    inc bx
    loop s

    mov ax,4c00h 
    int 21h 

code ends 
end 

5.6 段前缀

指令mov ax,[bx]中,内存单元的偏移地址由bx给出,而段地址默认在ds中,我们同样可以以其他段寄存器作为段地址访问内存
如:

mov ax,cs:[bx]
mov ax,es:[bx]
mov ax,ss:[0]

这些出现在访问内存单元的指令中,用于显式指明内存单元的段地址的“ds:”“cs:”在汇编语言中称为段前缀

5.7 一段安全的空间

在8086模式中,随意地向一段内存空间写入内容是非常危险的,因为这段空间中可能存放着重要的系统数据或代码。
例:

mov ax,1000h 
mov ds,ax 
mov al,0 
mov ds:[0],al 

如果1000:0中存放着重要的系统数据或代码,mov ds:[0],al将其改写,将引发错误

例:

assume cs:code
code segment 

    mov ax,0
    mov ds,ax 
    mov ds:[26h],ax 

    mov ax,4c00h 
    int 21h 

code ends 
end 

在Debug里执行mov [0026],ax时报错,在实模式下运行将会引起死机

可见,在不能确定一段内存空间中是否存放着重要的数据或代码的时候,不能随意向其中写入内容
DOS和其他合法的程序一般不会使用0:2000:2ff这段空间,如果在Debug模式下查看0:2000:2ff单元的内容都是0的话,则证明DOS和其他合法的程序没有使用这里

5.8 段前缀的使用

将内存ffff:0ffff:b中的数据复制到0:2000:20b单元中

assume cs:code 
code segment

    mov ax,0ffffh 
    mov ds,ax 
    mov ax,0020h
    mov es,ax 

    mov bx,0
    mov cx,12

s:  mov dl,cs:[bx]
    mov es:[bx],dl 
    inc bx 
    loop s
    
    mov ax,4c00h 
    int 21h 

code ends 
end 

第6章 包含多个段的程序

在操作系统的环境中,合法地通过操作系统取得的空间都是安全的。
程序取得所需空间的情况有两种

  1. 加载程序的时候为程序分配
  2. 程序在执行的过程中向系统申请

在本课程中只讨论第一种方法
若要一个程序在被加载的时候取得所需的空间,则要在源程序中定义段。
大多数有用的程序都要处理数据,使用栈空间,也都要有指令。为了程序设计清晰和方便,我们也都定义不同的段来存放他们。
讨论顺序:

  1. 在一个段中存放数据、代码、栈
  2. 将数据、代码、栈放入不同的段中

6.1 在代码段中使用数据

例:编程计算以下八个数据的和,结果存在ax寄存器中
在程序中定义我们希望处理的数据,这些数据就会被编译、连接程序作为程序的一部分写到可执行文件中。当可执行文件中的程序被加载入内存时。这些数据也同时被加载入内存中。与此同时,我们要处理的数据也就自然而然地获得了存储空间。

assume cs:code 
code segment 
    dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
    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的含义是定义字型数据 define word。
程序中的指令对8个数据进行累加,每个数据地址为指令地址+偏移量。若dw的地址为CS:0
则八个数据的地址分别为 CS:0 CS:2 CS:4 CS:6 CS:8 CS:A CS:C CS:E
然而这样做会使程序入口处不是我们想要执行的指令
可以在源程序中指明程序入口

assume cs:code 
code segment 
    dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
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:指明程序入口start

根据什么设置CPU的CS:IP指向程序的第一条要执行的指令?
由可执行文件中的描述信息指明。
有了这种方法,就可以这样来安排程序的框架

assume cs:code 
code segment 
        数据
        数据
        数据
start:  
        代码
        代码
        代码
        代码
code ends 
end start 

6.2 在代码段中使用栈

例:完成下面的程序,利用栈,将程序中定义的数据逆序存放

assume cs:codesg 
codesg segment
    dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

    ?
    ?

codesg ends 
end 

思路大致如下:
程序运行时,定义的数据存放在cs:0~cs:F单元中,依次将单元中的数据入栈,再依次出栈到8个单元中,从而实现逆序存放
问题是,需要一段可当作栈的内存空间
可以在程序中通过定义数据来取得一段空间,然后将这段空间当作栈空间来用

assume cs:codesg 
codesg segment 
    dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
    dw 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 

在描述dw的作用时,可以说用它定义数据,也可以说用它开辟内存空间

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

  1. 把他们放到一个段中使程序显得混乱
  2. 前面程序中处理的数据很少,用到的栈空间也小。8086处理器限制一个段大小为64kB

应该考虑用多个段来存放数据代码和栈

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

data segment 
    dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
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 
start:  mov ax,stack 
        mov ss,ax 
        mov sp,20h  ;设置ss:sp指向stack:20

        mov ax,data 
        mov ds,ax   ;设置ds:bx指向data段中第一个单元
        mov bx,0 

        mov cx,8
s:      push[bx]
        add bx,2
        loop s 

        mov bx,0

        mob cx,8 
s0:     pop [bx]
        add bx,2 
        loop s0 

        mov ax,4c00h 
        int 21h 
code ends 
end start 

(1)定义多个段的方法

与定义代码段的方法没有区别,对于不同的段需要有不同的段名

(2)对段地址的引用

段名即可指代段地址

(3)“代码段”“数据段”“栈段”完全是我们的安排

  1. 我们在源程序中为这三个段起了具有含义的名称
  2. 我们在源程序中使用伪指令assume将cs ds和ss分别与code data和stack段相连
  3. 段的区别体现在汇编指令中段的作用而非名称
posted @ 2020-03-26 18:20  答案唯一的四重选择题  阅读(347)  评论(0)    收藏  举报