读《汇编语言》II

Lxr-《Hb》

读《汇编语言》I。读《汇编语言》 -- 王爽  通读II。[ 2015.03.04 – 03.06 ]


3 练习

《汇编语言》 – 第三版 综合研究部分

[Hb-XVII] 计算机的抽象层次-简 使用寄存器 使用内存空间 C程序执行过程 使用main函数规定 不定参数函数机制 C


1 剪辑

(1) 汇编程序员的基础

[1] 汇编语言

[2] 硬件系统结构


(2) 为什么不同处理器的汇编指令集不同

每一种处理器,由于硬件设计和内部结构的不同,就需要不同的电平脉冲来控制,使它工作。所以每一种处理器都有自己的机器指令集,汇编指令与机器指令一一对应,故而不同处理器的汇编指令集不同。


(3) 提供给CPU控制设备的方式

CPU可以直接读写的三个部件是寄存器,内存,接口卡内的寄存器(端口)。


对于寄存器和内存,用户可以直接用机器码操作。对于接在接口卡上的设备,一般都有比较复杂的硬件细节,接口卡内一般都有ROM存储着对应的BIOS。这时可通过“int n”的方式调用BIOS内的程序来读写/控制在接口卡上的设备。BIOS是由主板和各类接口卡(显卡,网卡等)厂商提供的软件系统,可以通过它利用该硬件设备进行最基本的输入输出。


(4) 逻辑存储器

CPU在操控RAM,装有BIOS的ROM以及接口卡上RAM的时候,把它们都当作内存来对待,把它们总的看作一个由若干存储单元组成的逻辑存储器,这个逻辑存储器就是我们所说的内存地址空间


所有的物理存储器被看作由若干存储单元组成的逻辑存储器,没个物理存储器在这个逻辑存储器中占有一段地址空间。CPU在这段地址空间中读写数据,实际上就是在相对应的物理存储器中读写数据。


(5) 段

其实内存并没有分段,段的划分来自于CPU(基于8086CPU结构而得出的逻辑区域),由于8086 CPU用“基础地址(段地址x 16) + 偏移地址= 物理地址”的方式给出内存单元的物理地址,使得我们可以用分段的方式来管理内存。


(6) 8086 CPU“段地址:偏移地址”给出物理地址的设计

8086 CPU是16位结构:

  • 运算器一次最多可以处理16位的数据;
  • 寄存器的最大宽度为16位;
  • 寄存器和运算器的通路为16位。

8086 CPU有20根地址线。地址线上的地址由CPU内部给出,但CPU内部基本都是16位的宽度,怎么给出20位的数据?前辈们用两个16位的地址(16位就可以和CPU内的寄存器相连)来给出这20位的地址,在20位寄存器中将其中一个地址左移4位(乘以16)后再加上另一个地址。左移4位的地址被前辈们称之为段地址,没有被移位的地址被前辈们称之为偏移地址。


那么,8086CPU为什么不直接设计16根地址线呢?地址总线能传达多少个不同的信息,CPU就能够对多少个存储单元进行寻址。20根地址线有1M的寻址能力,16根地址线只有64KB的寻址能力。


(7) 在操作系统中可运行的可执行文件和机器码文件区别

在操作系统中一般要经过编译和链接两个阶段才会输出可执行文件。它将包含两部分内容:

  • 程序(从源程序中翻译过来的机器码)和数据(源程序中定义的数据)
  • 相关的描述信息(比如,程序有多大,要占用多少内存空间等。
  • 操作系统依照可执行文件中的描述信息,将可执行文件中的机器码和数据加载如内存,并进行相关的初始化(如设置CS:IP指向第一条要执行的指令),然后由CPU执行程序。

直接将二进制编辑器编写的程序保存为操作系统下可执行文件的形式(如后缀为.exe),它只包含程序和数据。操作系统将此程序载入内存中,并进行相关的初始化,然后由CPU执行程序。


(8) 汇编程序

汇编语言包含“汇编指令”,“伪指令”,“+,-等符号”;它们分别由机器,编译器,编译器执行/识别。


对于只包含“汇编指令”的汇编程序,经汇编器编译器(如masm.exe)和链接器(如Overlay Linker3.60)后形成可执行文件。操作系统(加载器)根据此文件内的描述信息,将此可执行文件的程序和数据加载到内存中,CS:IP指向汇编程序的第一条语句处(不管是指令还是数据)。

对于包含汇编语言所有元素的汇编程序,汇编编译器在将“汇编指令”处理为机器码外还将处理“汇编伪指令”和“符号”。像

段名 segment

       ..

段名 ends

中包含的伪指令对“segment.. ends”将会被编译器识别并执行其表示的含义:定义一个段。同时,“段名”会被编译,链接程序处理成一个段的段地址(汇编程序中的所有标号都被处理成为某汇编模块代码的首地址)。这就满足了8086用段来使用内存。汇编程序中每使用一对“segment ..ends”对就在内存地址空间中对应一个段,在“segment ..ends”对的数据或代码将会被分配到这段内存中。在程序被加载运行时正是根据程序中所定义的段而为程序分配内存,这些段用SS:SP指向则就可被当成栈来使用;若用CS:IP指向则就可被当成代码段来使用……(另一种获得内存空间的方法是向系统申请,即堆)。再如“end”伪指令表示整个汇编程序的结束;“end 标号”表示让CS:IP指向标号代表的地址处即程序从“标号”处开始执行


(9) 程序返回

将CPU的控制权交还给使得它运行的程序,将这个过程称为:程序返回。


(10) DOS,Windows,Unix操作系统管理内存的能力

在纯DOS方式(实模式)下,可以不理会DOS,直接用汇编语言去操作真实的硬件,因为运行在CPU实模式下的DOS,没有能力对硬件系统进行全面严格地管理。但在Windows2000,Unix这些运行于CPU保护模式下的操作系统中,不理会操作系统,用汇编语言去操作真实的硬件,是根本不可能的。硬件已经被这些操作系统利用CPU保护模式所提供的功能全面而严格地管理了。


(11) ASCII码文本显示

一个文本编辑器过程中,包含着按照ASCII编码规则进行编码和解码。在文本编辑过程中,我们按一下键盘的a键,就会在屏幕上看到“a”。这是怎样一个过程呢?


我们按下键盘的a键,这个按键的信息被送入计算机,计算机用ASCII码的规则对其进行编码,将其转化为61H(二进制对应电平状态)存储在内存指定的空间中;文本编辑软件从内存中取出61H,将其送到显卡的显存中;工作在文本模式下的显卡,用ASCII码的规则解释显存中的内容,61H被当作字符“a”,显卡驱动显示器,将字符“a”的图像画在屏幕上。


(12) 寻址方式的灵活应用

立即数(执行前)在CPU的指令缓冲器中。

读《汇编语言》II_第1张图片

Figure 1. 寻址方式(的灵活应用)

可以用汇编语言中的寻址方式实现C语言中的结构体,数组等数据结构。


(13) 位移的好处

转移指令都支持转移位移的方式,这个位移是编译器根据汇编指令中的“标号”计算出来的。这种设计,方便了程序段在内存中的浮动装配(这段程序装在内存中的不同位置都可正确执行)。


(14) 标志寄存器的标志位

准确理解标志寄存器的标志位。


在C语言中,可以按照“类型匹配符(%d,%u)”解释一个二进制串表示的有符号数还是无符号数。其实CPU在执行“add”,“sub”,“cmp”等汇编指令时将进行两种运算:有符号运算和无符号运算


有符号数和无符号数的区别是符号位。如果符号位发生变化就引起溢出(OF置位);如果寄存器的最高有效位产生进位或者最高有效位向更高位借位时则CF置位(进位和借位)。


cmp 比较两个数大小时,之所以不能单纯地用sf位来表示两个数的大小是因为可能发生了溢出(符号位发生变化)。


2 知识点能解决的问题

(1) 汇编代码目标代码信息 可执行文件信息反汇编代码 查看内存,寄存器

汇编代码可自己编写;也可以由C代码经过C编译器得来。用比较常用的编辑器(如记事本)即可查看汇编代码。


目标代码是编译器的终极产物。由于目标代码已经是二进制信息,所以需要二进制编辑器查看。要看目标代码组织的信息还需要特殊的工具,如Linux下的readelf工具。


可执行文件是链接器的终极信息。可执行文件也是二进制信息,需要用二进制编辑器查看。要查看可执行文件组织的信息,需要特殊的工具,如Linux下的readelf工具。


反汇编代码是将可执行文件的二进制翻译成为汇编代码(包含地址,引用等信息),这需要特殊的工具。如DOS下的debug调试器,Linux下的objdump工具。


在可执行程序运行时,对程序进行单步调试,并查看内存,寄存器的值由诸如debug调试器,gdb以及集成开发环境中的调试器来完成。


(2) 直接用机器指令或汇编指令访问内存,寄存器

内存和寄存器都是硬件。可以通过在C集成开发环境或者用汇编语言(汇编编译器和链接器)的方式访问内存和寄存器。也可以在debug调试器这样的程序(接口)中直接用机器指令或者汇编指令(提供的方式)去访问内存和寄存器等硬件。


(3) 灵活应用寻址方式去访问内存

灵活应用汇编语言中的寻址方式就可以像C语言中结构体,数组之类的数据结构那样访问内存。如果要用同一种方式连续的访问内存,则还需要用到loop循环指令。


(4) 用户程序获取内存空间的方式

[1] 在汇编程序中定义段,编译器或者编译器会给程序中的段分配对应的内存空间。

[2] 动态获取内存,在程序运行空间内去找一段没有被用得内存来用。如C语言中的malloc/free。


(5) 程序运行时修改程序

将某处的程序指令复制到程序的另一指令处(操作系统不允许的话,就复制到数据所在的内存处),再跳到被复制内存处执行,使被修改部分得以执行。


(6) 显示

要显示某些经编码的内容时,通过显存的手册,只需将这些内容送往显卡中的显存中(显存地址在逻辑存储器中)中的显示内容的字节单元中,再设置显示内容的属性(背景和前景)。


(7) 除法溢出问题

将结果保存在多个寄存器中(最好能利用自带的高位保存在寄存器A,低位保存在寄存器B的机制)。


(8) 数值显示

将数值利用除以10取余的方式得到每一位,再将每一位数字转换成对应的字符。再以正确的顺序送往显存中显示。


(9) 汇编程序中的子程序

汇编语言的CALL/RET或者CALLFAR/RETF以及标号可以实现子程序的编写。模块化产生。


(10) 编写中断处理程序

[1] 首先要能让CPU识别到中断号(如int0中的0能让CPU知道是0号中断)

[2] 然后将中断程序部分复制到内存中

[3] 最后要将中断程序所在的内存地址复制到中断号对应的中断向量表中


对于增加某中断程序的功能的,要先把此中断程序的内存地址保存起来(用户才能调用,也方便恢复)。

不同的处理器的中断机制有可能不一样,遵循其中断流程编写即可。


(11) 编写包含多个功能子程序的(中断)例程

[1] 先编写好各个子程序的代码

[2] 将各个子程序的代码的标号放在一起

[3] 根据参数(序号)来调用具体的子程序

[4]复制中断程序时要指定中断程序被复制内存的偏移地址(使用ORG指令

中断例程的编写遵循(10)中的原则。


(12) 读/写接口卡内的端口

CPU可以直接读/写接口卡内的端口(寄存器),每个端口都有对应的端口号,使用这个端口号就可以实现CPU对特定端口的读/写。从而与接口卡的上所连接的设备交互。


[2015.03.08-11:05]
R《Hb》Note Over.

你可能感兴趣的:(读《汇编语言》II)