王爽《汇编语言》学习笔记(第三章、第四章)

《汇编语言》学习笔记(第一章、第二章)
《汇编语言》学习笔记(第三章、第四章)
《汇编语言》学习笔记(第五章、第六章)
《汇编语言》学习笔记(第七章、第八章)
《汇编语言》学习笔记(第九章、第十章)
《汇编语言》学习笔记(第十一章、第十二章)
《汇编语言》学习笔记(第十三章、第十四章)
《汇编语言》学习笔记(第十五章、第十六章)

第3章、寄存器(内存访问)

3.1 内存中字的存储

字单元:存放一个字型数据(16位)的内存单元,由两个连续的内存单元组成,高地址内存单元放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。

将起始地址为N的字单元称为N地址字单元。

3.2 DS和[address]

DS寄存器:通常用来存放要访问数据的段地址

[address]表示一个偏移地址为address的内存单元,段地址默认放在ds中

mov指令将一个内存单元中的内容送入另一个寄存器中:

mov 寄存器名 [内存单元的偏移地址]

指令执行时,8086CPU自动取ds寄存器中的数据为内存单元的段地址。

所以我们在将一个内存单元中的内容送入一个寄存器中时,需要提前将内存单元的段地址放入ds中。

8086CPU不支持将数据直接送入段寄存器,只能通过另一个寄存器中转,先将段地址送入一个一般的寄存器,如bx,再将bx中的内容送入ds。

3.4 mov、add、sub指令

mov 寄存器,数据 比如: mov ax,8

mov 寄存器,寄存器 比如: mov ax,bx

mov 寄存器,内存单元 比如: mov ax,[0]

mov 内存单元,寄存器 比如: mov [0],ax

mov 内存单元,段寄存器 比如: mov [0],cs

mov 段寄存器,内存单元 比如: mov cs,[0]

mov 段寄存器,寄存器 比如: mov ds,ax

mov 寄存器,段寄存器 比如:mov ax,ds

mov指令几乎都可以互相传递,唯一要注意的是:不能将数据直接送入段寄存器!

可以将内存单元的内容送入段寄存器,也可以将寄存器的内容送入段寄存器,但是唯独不能将数据直接送入段寄存器!

在这里插入图片描述

3.7 CPU提供的栈机制(ss和sp)

在基于8086CPU编程的时候,可以将一段内存当作栈来使用。

8086CPU的入栈和出栈操作都是以字为单位进行的。

push和pop指令执行时,CPU从SS和SP中得到栈顶的地址

段寄存器SS,存放栈顶的段地址,SP寄存器存放栈顶的偏移地址,任意时刻,SS:SP指向栈顶元素

8086CPU中,入栈时,栈顶从高地址向低地址方向增长。

push ax表示将寄存器ax中的数据送入栈中,由两步完成。

  1. SP=SP-2,SS:SP指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶;
  2. 将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶。

pop ax表示从栈顶取出数据送入ax,由以下两步完成。

  1. 将SS:SP指向的内存单元处的数据送入ax中;
  2. SP=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。

当栈为空的时候,不存在栈顶元素,此时SS:SP指向栈的最底部单元下面的单元。该单元的偏移地址为栈最底部的字单元的偏移地址+2!

一定要注意,栈最底部的字单元的地址并不是最底部的内存单元的地址!

由于栈从栈底到栈顶是从高地址到低地址的,而字单元的地址是两个字节单元地址中的较低的那一个,所以栈最底部的字单元的地址并不是最底部的内存单元的地址!

3.8 栈顶超界的问题

栈顶超界会导致其他内存空间的指令或数据被覆盖,产生无法想象的后果。

8086CPU不保证我们对栈的操作不会超界。

我们在编程时要自己操心栈顶超界的问题。

3.9 push、pop指令

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

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

实验

  1. 将10000H~1000FH这段空间当作栈,初始状态栈是空的;
  2. 设置AX=001AH,BX=001BH;
  3. 将AX、BX中的数据入栈;
  4. 然后将AX、BX清零;
  5. 从栈中恢复AX、BX原来的内容。
mov ax, 1000H 
mov ss, ax 
mov sp, 0010H    ;初始化栈顶
mov ax, 001AH
mov bx, 001BH 

push ax 
push bx    ;ax、bx入栈

sub ax, ax   ;将ax清零,也可以用mov ax,0,
             ;sub ax,ax的机器码为2个字节,
             ;mov ax,0的机器码为3个字节。
        
sub bx, bx 

pop bx  ;从栈中恢复ax、bx原来的数据
pop ax  ;

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

mov ax, 1000H
mov ss, ax
mov sp, 0010H

空栈时SP指向的内存单元的偏移地址为栈最底部的字单元的偏移地址+2

空栈时SP指向的内存单元的偏移地址为栈最底部的字单元的偏移地址+2

空栈时SP指向的内存单元的偏移地址为栈最底部的字单元的偏移地址+2

问题 3.12

因为push、pop在执行时只能修改SP,所以栈顶的变化范围是0~FFFFH,一个栈最大可以被设为64KB。

小于64KB的栈,栈顶超界时会覆盖别的内存单元;

而等于64KB的栈,栈顶超界时,SP会溢出,栈顶将环绕,覆盖原来栈中的内容。

段的综述

我们可以用一个段存放数据,将它定义为“数据段”;

我们可以用一个段存放代码,将它定义为“代码段”;

我们可以用一个段当作栈,将它定义为“栈段“。

对于数据段,将它的段地址放在DS中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当作数据来访问;

对于代码段,将它的段地址放在CS中,将段中第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令;

对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地址放在SP中,这样CPU在需要进行栈操作的时候,比如执行push、pop 指令等,就将我们定义的栈段当作栈空间来用。

CPU将内存中的某段内容当作代码,是因CS:IP指向了那里; CPU将某段内存当作栈,是因为SS:SP指向了那里。

一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么也不
是。关键在于CPU中寄存器的设置,即CS、IP、SS、SP、 DS的指向。

实验 2

我们可以用“d 段地址:偏移地址 ”的方式查看指定内存单元的内容,其中的段地址存储在DS段寄存器中。

我们既可以以数据的方式直接给出段地址,也可以以段寄存器的方式给出段地址。


第4章、第一个程序

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

  1. 使用文本编辑器用汇编语言写汇编程序,产生了一个存储源程序的文本文件
  2. 使用汇编语言编译程序对文件中的源程序进行编译,产生目标文件
  3. 使用连接程序对目标文件进行连接,产生可在操作系统中直接运行的可执行文件
  4. 操作系统执行可执行文件:
    1. OS将可执行文件中的机器码和数据加载入内存
    2. 进行相关的初始化(比如设置CS:IP指向第一条要执行的指令)
    3. 由CPU执行程序。

4.2 源程序

在汇编语言源程序中有两种指令:

  • 汇编指令:有对应的机器码,可以被编译为机器指令,最终被CPU执行
  • 伪指令:没有对应的机器码,由编译器来执行
1.asm
assume cs:codesg 将用作代码段的段codesg和段寄存器cs联系起来。

codesg segment 定义一个段,段的名称为“codesg”,这个段从此开始
			   codesg是一个标号,作为一个段的名称,最终被编译连接成一个段的段地址

	mov ax, 0123H
	mov bx, 0456H 
	add ax, bx
	add ax, ax 
	
	mov ax, 4c00H 
	int 21H 这两条指令实现程序的返回
	
codesg ends 名称为“codesg”的段到此结束

end 编译器在编译汇编程序的过程中,碰到了伪指令end,结束对源程序的编译

源程序:源程序文件中的所有内容称为源程序

程序:源程序中最终由CPU执行处理的指令或数据(即源程序中去掉伪指令的部分)

程序最先以汇编指令的形式存在源程序中,经过编译、连接后变成机器码,存在可执行文件中。

可执行文件是怎么得到运行的?

一个程序P2在可执行文件中,则必须有一个正在运行的程序P1,将P2从可执行文件中加载入内存后,将CPU的控制权交给P2,P2才能得以运行。P2开始运行后,P1暂停运行。而当P2运行完毕后,应该将CPU的控制权交还给使它得以运行的程序P1,此后P1继续运行。

程序返回:一个程序结束后,将CPU的控制权交还给使它得以运行的程序

所以需要在程序的末尾添加返回的程序段。

	mov ax, 4c00H 
	int 21H 这两条指令实现程序的返回

4.5 编译连接

首先在DOSBox Options.bat中对DOSBox中的虚拟目录和本地电脑中的真实目录挂载(mount)

如图,将DOSBox中的d:目录与本地路径的D:\DOSBox\masm挂载,即访问DOSBox中的d:目录相当于访问D:\DOSBox\masm


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iuu4LGbm-1612338581604)(%E3%80%8A%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E3%80%8B%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0.assets/20190321115301209.png)]

连接的作用:

  1. 当源程序很大时,可以将它分为多个源程序文件来编译,每个源程序编译成目标文件后,再用连接程序将它们连接在一起,生成一个可执行文件
  2. 程序中调用了某个库文件中的程序,需要将这个库文件和该程序生成的目标文件连接在一起,生成一个可执行文件
  3. 一个源程序编译后,得到了存有机器码的目标文件,目标文件中的有些内容还不能直接用来生成可执行文件,连接程序将这些内容处理为最终的可执行信息。所以,在只有一个源程序文件,也不需要调用某个库中的子程序的情况下,也必须用连接程序对目标文件进行处理,生成可执行文件。

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

在masm(link)后面加上被编译(连接)的源程序文件(目标文件)的路径、文件名,在结尾再加上分号,编译器就会在编译(连接)过程中自动忽略中间文件的生成。

4.7 exe的执行

在可执行文件的目录下输入可执行文件的名字即可执行(由于我将DOSBox中的d:目录与本地路径的D:\DOSBox\masm挂载,而可执行文件又在D:\DOSBox\masm下,所以此时就相当于在可执行文件所在的目录下)

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

程序P1要想运行,必须要有一个正在运行的程序P2,将P1从可执行文件中加载入内存,将CPU的控制权交给它,P1才能运行;P1运行完毕后,要将CPU的控制权还给使它得以运行的程序P2。

操作系统是由多个功能模块组成的庞大、复杂的软件系统,操作人员通过一个称为shell(外壳)的程序来操作计算机系统进行工作。

在DOS中有一个程序command.com,在DOS中被称为命令解释器,也就是DOS系统的shell

DOS启动时,先完成其他重要的初始化任务,然后运行command,由command执行用户输入的命令,比如:cd、dir、type。

在DOS中,command处理各种输入,命令或要执行的程序的文件名,我们就是通过command来进行工作的。

用户想要执行一个程序:

  1. 在command中输入该程序的可执行文件的名称
  2. command根据文件名找到可执行文件
  3. 将可执行文件中的程序加载入内存
  4. 设置CS:IP指向程序的入口
  5. command暂时停止运行,将CPU的控制权交给另一个程序
  6. 另一个程序运行完后,CPU控制权返回到command中,等待用户的输入。

4.9 程序执行过程的跟踪

command将程序加载入内存,CS:IP一指向程序的入口,command就放弃了CPU的控制权,直到程序结束,所以我们无法逐条指令看到程序的执行过程。

而Debug将程序加载入内存后并不放弃CPU的控制,单步执行程序。所以我们使用Debug来逐条执行指令,查看每一条指令的执行结果,跟踪执行过程。

debug a.exe,debug将程序从a.exe中加载入内存

程序被装入内存的位置:

(1) 程序加载后,ds中存放着程序所在内存区的段地址(SA),这个内存区的偏移地址为0,则程序所在的内存区的地址为ds:0;

(2) 这个内存区的前256个字节中存放的是PSP, DOS用来和程序进行通信。从256字节处向后的空间存放的是程序。

(3) 程序的物理地址是SAx16+0+256=(SA+16)x16+0,用段地址和偏移地址表示为:SA+10H:0。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值