学习笔记

《汇编语言》的一些小结

今天汇编终于到了,马买皮,早知道这么慢就去京东上买了。

我为什么要学汇编

关于汇编,以前看《深入理解计算机系统》的时候就大致看了一蛤,当然是远远不够的,而且这本书我从大二看到现在也还没看完……
而我稍微浏览了一下我们这学期的教科书,跟那个煞笔老师一样让我感到很遗憾的是,书上也完全没有汇编。
要是真的学得这么浅的话,就不止是让人遗憾,更是让人觉得失望了。
是的,我已经很自然的认为学习操作系统就需要学并用到汇编,比如MBR还想用什么写,那是直接接触硬件的部分,除了汇编没有其他选择。emmmmm,据说汇编还有其他在debug等追根溯源上作为终极武器有很强的功能,据说啦,据说。不过看了一点之后发现汇编和操作系统在一些部分是很通用的。
另外,在《操作系统真象还原》其实最基础的假设就是读者都已经学了汇编。
顺便吐槽一句,王爽的《汇编语言》居然平台居然是Windows的,醉了。

正文

2017-10-28 00:37:44 星期六

  1. 对于一串01字符串,既可以表示命令,也可以表示数据,CPU区分的方式是通过寄存器分割开来。比如说固定让这个寄存器访问命令,让那个寄存器访问数据。
  2. 8086通过“段+偏移”的寻址方式,而使段只偏移4位的结果有二,一是CPU可以通过不同的“段+偏移”来形成同一个物理地址。二是若仅改变偏移地址,最多寻址64KB个内存单元。
  3. 8086有4个段寄存器 CS,DS,SS,ES。
  4. 8086 CPU的第一条指令固定是 FFFFH:0000H
  5. cs:ip 只能通过 jmp命令更改,单独更改 cs 段寄存器则不需要。
  6. CPU与内存交互主要通过 DS 段存储器,ds存储内存单元的段地址,再通过 [偏移地址] 访问具体内存单元。
  7. 8086 不支持直接将数据送入段存储器的操作。
  8. 1字 = 2字节。字节类型数据在8086中需要用到两个内存单元存储,高地址单元存放高8位,低地址单元存放低8位
  9. 任意时刻,SS : SP只想栈顶元素。8086 CPU只知道栈顶元素在哪,而无法知道我们安排的栈有多大。
  10. 8086 CPU在入栈时,栈顶从高地址向低地址方向增长。入栈顺序是先移动栈顶指针,再存储数据。出栈则反之。
  11. 段。段简单来说就是一大部分连续的内存单元。但是这个内存单元的起始位置必须是16的倍数。原因在于8086的寻址方式。根据在段中存放的内容,可分为“数据段”,“代码段”,“栈段”。

顺便附上我写的第一个汇编程序

assume cs:codesg

codesg segment

    mv ax,2
    add ax,ax
    add ax,ax

    mov ax,4c00h
    int 21h

codesg ends

end

虽然连结果都打印不出来,但还是挺兴奋的。

2017-10-30 11:40:50 星期一

终于可以做书上的实验了!!!
总结一下 DOS 中debug的命令。

  • r : 查看,修改CPU寄存器的内容
  • d : 查看内存的内容
  • e : 修改内存的内容
  • u : 将内存中的内容解释为机器指令和对应的汇编指令
  • t : 执行当前指令,当前指令有cs:ip 所指向,可通过改变这两个寄存器改变指令
  • a : 以汇编形式写入内存
  • p : 执行中断指令

2017-10-30 22:45:57 星期一

  1. 修改了SS寄存器后,会紧接着执行下一条指令
  2. shell——操作系统的外壳。对于一个可执行程序,都是由shell执行,将该程序加载到内存,设置CS:IP 指向程序的入口,此后程序运行,运行结束后,返回到shell。最后shell再去执行其他程序
  3. dos运行程序时,都需要利用PSP(Program Segment Prefix)来和被加载程序进行通信。PSP占据256(100H)个字节。所以程序的物理地址为 SA+10H:0
  4. SA(Segment Adress),段地址。EA(Effective Adress),偏移(有效)地址。
  5. 通常使用loop指令来实现循环功能,cx中存放循环次数。
  6. 在汇编程序中,数据不能以字母开头。比如A000H,就必须写成0A000H。
  7. 在汇编源程序中,如果用指令访问一个内存单元,则在指令中必须用“[...]”来表示内存单元。但如果“[]”内是数值常量,就必须在前面加上段前缀。所谓段前缀就是 “段寄存器:” 的形式。
  8. dos 方式下,一般情况下,0:200 ~ 0:2ff 这段空间是安全的。
  9. end 除了通知编译程序以外,还可以通知编译器程序的入口在哪。
  10. 取得程序内存的两种方法:在程序加载时系统自动为其分配,或者在程序运行时动态申请。
  11. 这里再次强调。伪指令不会被编译器所执行,只能作为程序员理清代码使用。比如最开始的assume,不能确保段的功能与assume相对应。
  12. 用'...'的形式指明数据是由字符形式给出的。编译器会将其转化成对应的ASCII码。
  13. 内存单元中的偏移地址表达形式有:(其中,idata表示数值常量)
    1. [ax+bx+idata]
    2. idata[ax][bx]
    3. [ax].idata[bx]
    4. [ax][bx].idata

至此,全书已看完一半了……

第八章 数据处理的两个基本问题

  1. 8086 CPU中,只有bx,si,di,dp四个寄存器才能用于内存寻址。并且,对于两个两个寄存器两两组合的情况,只有bx,bp 与 si,di 分别两两组合形成的4种组合方案。
  2. 内存寻址方式在 本书 P164 中有很好的总结,这里不再赘述。
  3. 指定处理的数据长度的方法有二:
    1. 通过寄存器名指明数据的尺寸。 因为位数不同的寄存器无法相互操作。
    2. 通过 操作符 X ptr 的方式指定。其中 byte ptr 指的是一个字节单位;word ptr指的是一个字单位; dword ptr 指的是一个双字单位。
  4. 在C语言中,我们经常可以看到,如:dec.cp[i]代表的是名为 dec的结构体中首地址为cp的变量中的第i个单元。而我们在汇编中做法存在bx.10h[si]。仔细一想的话,C语言中包含了很多与汇编相通的语法。只不过包了一层外衣。
  5. div指令。语法是div 除数(reg/内存单元),而我们的被除数一般优先放在 ax 中,其次放在 dx中。其商也是优先放在 ax 中,其次放在 dx 中。
  6. 伪指令 db,dw,dd。以下是三个伪指令的缩写,看了英文就会明白它的意思。
    • db: define byte
    • dw: define word
    • dd: define dword
  7. 指令操作符 dup。表示重复定义元素。语法为 db/dw/dd n dup (元素) 其中n为重复次数。在()为初值。比如 db 4 dup (0) 等同于 db 0,0,0,0

第九章 指令转移的原理

  1. 指令操作符 offset 。 offset 是偏移的意思。它的指令含义是获得标号的偏移地址。语法:offset 标号
  2. 指令操作符 nop 。 申请一个什么都没有的字节单位的机器码。
  3. cpu在执行jmp指令的时候无须知道目标地址。jmp指令只考虑与当前指令地址的偏移量。、
  4. jmp 语法小节:
    • jmp short 标号,偏移量在有符号字节单元内的段内转移。范围即[-128,127]
    • jmp near ptr 标号,偏移量在有符号字单元内的段内转移。范围即[-32768,32767]
    • jmp far ptr 标号,段间转移。用标号处的SA,EA来修改CS,IP。
    • jmp 16位 reg,用reg中的值来改变 ip 。
    • jmp word ptr 内存单元,段内转移。内存单元中存的是EA。
    • jmp dword ptr 内存单元,段间转移。内存单元中高位字存SA,低位字存EA。
  5. jcxz 标号。含义为 “jmp if cx equal to zero”。等同于C语句if((cx)==0) jmp short 标号;
  6. 补充,loop本质上也是有jmp指令而来,loop指令跳转的也是短转移。即其jmp范围为[-128,127]

第十章 call与ret指令

  1. ret 指令: 等同于 pop ip
  2. retf 指令: 等同于 pop ip pop cs指令。
  3. call 指令: 指令将进行两步操作,先将当前 ip 或者是 cs和ip 压入栈中,再是转移。其语法有
    1. call 标号:段内转移,等同于
      1. push ip
      2. jmp near ptr 标号
    2. call far ptr 标号:段间转移,等同于
      1. push cs
      2. push ip
      3. jmp far ptr 标号
    3. call 16位reg:段内转移,用寄存器中的数据进行转移,等同于
      1. push ip
      2. jmp 16位寄存器
    4. call word ptr 内存单元:段内转移,用内存数据进行转移,等同于
      1. push ip
      2. jmp word ptr 内存单元地址
    5. call dword ptr 内存单元:段间转移,用内存数据进行转移,等同于
      1. push cs
      2. push ip
      3. jmp dword ptr 内存单元
  4. 仔细观察一下,将call与ret结合起来使用就是一个函数的功能。
  5. 批量参数数据的传递可以通过申请内存并保存首地址的方式来实现。
  6. 指令操作符 mul:两个相乘的数,要么是8位,要么是16位,默认都放在ax寄存器中。另一个数字放在其他16位寄存器或者内存单元中。而其结果优先放在 ax 中,其次放在 dx 中。
  7. 寄存器是有限的,在使用call之后应首先把当前使用的寄存器的值预先放在栈中,在ret之前把栈的数据还原。

第十一章 标志寄存器

标志寄存器是8086的最后一个寄存器。也称为flag寄存器。flag寄存器是按位起作用的。其有效位如下

1514131211109876543210
OFDFIFTFSFZFAFPFCF

从低位到高位其各自英文全称与作用如下

  • CF: Carry Flag 。进位标志。在无符号计算时如果存在进位则为 1 。否则为 0 。
  • PF: Parity Flag 。奇偶标志。指令计算结果为偶数时为 1, 否则为 0 。
  • AF: Auxiliar Carry Flag。辅助进位标志。 意义不明。
  • ZF: Zero Flag。零标志。 指令结果为 0 则为 1。否则为 0。
  • SF: Sigh Flag。符号标志。 指令结果为 负数 则为 1。否则为 0 。
  • TF: Trace Flag。轨迹标志。 当TF为 1 时,执行完当前指令后将引起单步中断。
  • IF: Interrupt Flag。中断标志。 功能与 TF 一致。
  • DF: Direction Flag。方向标志。 DF为0时,每次指令操作后si,di递增,否则递减。
  • OF: Overflow Flag。溢出标志。在有符号计算时如果发生溢出,则为 1 ,否则为 0。

其他:

  1. adc 指令。带进位加法指令。adc ax,bx等同于 (ax)=(ax)+(bx)+CF,一般用于大数操作。
  2. sbb 指令。带借位减法指令。与adc指令类似。
  3. cmp 指令。cmp指令的功能类似于 减法指令。cmp指令虽然不会保存结果,但是会影响flag寄存器。通过flag寄存器的值,来判断两个数之间的大小关系。
  4. 以ax,bx为例,以下为无符号数大小比较之法:
    • ZF = 1 \rightarrow (ax) = (bx)
    • ZF = 0 \rightarrow (ax) \neq (bx)
    • CF = 1 \rightarrow (ax) < (bx)
    • CF = 0 \rightarrow (ax) \geq (bx)
    • CF = 0 \&\& ZF = 0 \rightarrow (ax) > (bx)
    • CF = 1 \&\& ZF = 1 \rightarrow (ax) \leq (bx)
  5. 以ax,bx为例,以下为有符号数大小比较之法:
    • ZF = 1 \rightarrow (ax) = (bx)
    • ZF = 0 \rightarrow (ax) \neq (bx)
    • SF ^ OF = 1 \rightarrow (ax) < (bx)
    • SF = 0 \&\& OF = 0 \rightarrow (ax) \geq (bx)
    • SF = 1 \&\& OF = 1 \rightarrow (ax) > (bx)
  6. pushf和popf。将flag寄存器压入栈中或者从栈中弹出数据存到flag寄存器中。
  7. movsb / movsw 指令。将 ds:si 指向的内存单元的数据(字节/字)送入 es :di中,然后根据flag寄存器中的df位的值,将si,di递增或递减(1位/2位)。
  8. cld 指令将flag寄存器中的df位置变为 0,std指令则为置1 。
  9. 基于flag寄存器的条件转移,见表1。
  10. dos的debug中对于flag寄存器的表示,见表2。

表1

指令含义检测的相关标志位
je等于则转移ZF = 1
jne不等于则转移ZF = 0
jb低于则转移CF =1
jnb不低于则转移CF = 0
ja高于则转移CF = 0 \&\& ZF = 0
jna不高于则转移CF = 1 || ZF = 1

表2

标志值为 1 的标记值为 0 的标志
OFOVNV
SFNGPL
ZFZRNZ
PFPEPO
CFCYNC
DFDNUP

第十二章 内中断

  1. 内存段有4种产生方式
    • 除法错误。中断类型码: 0
    • 单步执行。中断类型码: 1
    • 执行 into 指令。中断类型码: 4
    • 执行 int 指令。中断类型码: 其他

发表评论

电子邮件地址不会被公开。 必填项已用*标注