“实模式汇编真的很简单!”——————鲁迅

我们在上一篇文章中已经初步学习了实模式汇编,读者们应该对于实模式汇编的一些基础语法有了了解。接下来,我们将进一步深入讲解实模式汇编,学完本篇,读者们就可以写出自己的汇编程序了!

1.分段

我们前边介绍了实模式下的地址寻找方法,即段寄存器:偏移地址,这样做的目的是为了适应20位的物理地址。但是另一个方面,这样分段地址与偏移地址的方式是为了程序分段

我们发现,在使用一个段地址时最多寻找2^16字节(即64KB的内存空间),但是这样就出现了一个问题:如果程序大于64KB呢?

另一方面,如果我们把数据与程序写到一起,会造成程序结构混乱

还有一点,汇编中很多指令需要分段的支持(如需要ES段的串传输指令)

综上,分段的设计是必要的

使用section 段名称伪指令的方式进行段定义,直到遇到下一个此伪指令或者到程序结束为一个段。

section myData vstart=0 aligh=16
value1 DW 1,1,2,0x100
value2 DB 'A','B','C'
dataSec DW section.myData.start
section myCode vstart=0 aligh=16
mov AX,word [dataSec]
mov DS,AX
mov AX,[value1]

对于以上这段程序 我们定义了两个段 分别来存放代码和数据(myData与myCode)

当我们定义好了分段以后 我们只需要得到段首所在的物理地址(20位)并且将其除以16就可以作为段地址放入段寄存器中。但是x86规定:不能够手动mov的方式来修改CS寄存器,而其他的段寄存器的修改只能够使用寄存器寻址的方式来修改,例如mov DS,AX

这个时候我们就遇到一个问题,既然不可以手动填充CS寄存器,那么我怎么才可以让CS寄存器指向用户程序的代码段呢?

我们下边两节(汇编地址与引导程序)就围绕这个问题来探讨。

2.程序加载器

我们使用一个程序来加载另一个程序到物理内存中,我们称之为程序加载器(loader)。而对于实模式下的加载器主要要做的事情是保证程序的符号正确性。因为程序中的符号是编译器计算出来的,并且具有确定性。需要注意的是程序在加载到内存中时,我们不知道程序具体加载的物理地址,所以对于一个符号地址的访问,我们就遇到了困难,由于符号编译后是一个确定的值,所以我们要做的就是使用loader计算出段寄存器地址。所以程序中的地址使用相对于程序的第一条指令偏移量的汇编地址来表示。这样就可以使用:程序首地址+程序内偏移的方式来得到每一个符号所表示的物理地址

head:
指令1
指令2
指令3
position:
指令4

假设每一条指令长度都为一字节,那么对于以上的程序来说:指令1的汇编地址为0,指令4的汇编地址为3。
再另一个方面,在没有分段时,汇编器在编译时会将符号翻译为按照程序首地址的偏移量,所以position符号表示偏移量为3

对于一个分段程序,我们可以使用vstart属性来使段内的符号按照段首为标准来偏移

SECTION code vstart=0
指令1
指令2
指令3
position:
指令4

假设指令长度都为1,那么由于使用了vstart=0属性,position的值就要从start来计数,并且从0开始计数,同理可以使用vstart=0x100来使position从0x100计数,那么position=0x100+3=0x103

我们以上只是获取的符号的值,当vstart=0时获取时,符号的值为相对于段的偏移。

我们可以使用section.段名.start伪指令来获取某个段相对于程序首地址的偏移地址,当然,为了保存这个值,我们一般在数据段中分配一个变量来保存

codeHeader DD section.code.start    ;将code段的偏移地址存放在codeHeader变量中

并且由于程序加载的不确定性,所以我们还需要一个程序加载的首物理地址信息。

综合上边,我们就可以使用程序首物理地址+段偏移地址+段内符号偏移地址来获取某个符号的物理地址。

但是,我们前边说过,访问一个符号的内存空间,我们使用的是段寄存器+符号地址的形式

所以有等式:段寄存器*16+符号地址=程序首物理地址+段偏移地址+符号地址(这个等式需要vstart=0时才成立,但是我们一般为了方便,都是用vstart=0)

所以段寄存器*16=程序首物理地址+段偏移地址

程序记载器要做的就是:(1)将程序加载到某个空闲物理地址中(2)使用上边的等式计算出段寄存器的地址,并且填充段寄存器(3)使用jmp指令跳转到加载的程序的首地址,这样程序就开始执行了。

3.段内对齐

除了start属性,我们还可以使用align属性定义段内字节对齐。

SECTION data align=16 ;16字节对齐

段对齐属性即是指段的第一条指令或者数据必须是对齐值的倍数,比如上边指令的align=16,表示段中第一条指令地址必须是16的倍数(并且inter规定,至少是align=16)。

由等式段寄存器*16=程序首物理地址+段偏移地址。当程序首地址为16倍数时,段地址必须为16的倍数才可以计算出段寄存器的值,所以16字节对齐是必须的。

但是还有另一个原因:8086数据总线一次能够传输一个字的信息。即内存中使用字为单位。当我们需要使用0x00与0x01物理地址的总共一个字的数据时,总线传送第一个字就行了。但是当我们需要0x01与0x02这个字时,我们需要使用传输第一个字,并且取高字节,传输第二个字,并且取低字节,最后将这两个字节合为一个字。可以看出,如果使用按照字对齐的方式来存放这个字就可以减少内存访问次数来提高性能。

所以,我们得到一个规律,对于大于计算机字长的信息,最好按照字长对齐来存放,这样可以提高程序性能。

所以16字节对齐时一定可以保证实模式下的字对齐的。

另一个比较好的例子是C语言中的结构体,结构体中的元素都是按照一定的对齐方式来存放的,比如对于32位机器的long long型(8个字节),是按照字长对齐方式存放(首地址必须为8的倍数 )。如果结构体只用一个long long型,若结构体的首地址为0x10,实际上结构体的内存大小为10字节 (使用sizeof可以看出),因为longlong型存放在0x12中(32位机器4字节对齐)。

4.MOV指令

格式:MOV 目的操作数,源操作数

作用:将源操作数数据复制后传入到目的操作数中(会改变目的操作数)

注意事项:操作数可以是立即数 寄存器 内存寻址,但是不可以在内存与内存之间传输 不可以将立即数作为目的操作数,对于两个操作数数据长度不相符的可以使用前一章介绍的数据长度限定位指令

以下为错误示范:

MOV 0x123,AX ;将立即数作为目的操作数,错误
MOV [DS:0x11],[CS,0x12] ;两个操作数都在内存中,错误

5.ALU支持的指令

前边介绍过,AUL作为计算机的逻辑处理单元,用于进行指令执行中的执行阶段,ALU只能进行加法 与或非以及移位操作运算。

ADD指令

格式 :ADD 目的操作数,源操作数

作用:将源操作数数据加上目的操作数后传入到目的操作数中(会改变目的操作数)

AND指令

格式 :AND 目的操作数,源操作数

作用:将源操作数数据按位与目的操作数作与运算后传入到目的操作数中(会改变目的操作数)

OR指令

格式 :OR 目的操作数,源操作数

作用:将源操作数数据按位与目的操作数作或运算后传入到目的操作数中(会改变目的操作数)

NOT指令

格式 :NOT 操作数

作用:将操作数数据按位取反后更新到操作数中

6.另一些运算指令

对于减法 乘法 除法虽然不能使用ALU的硬件直接执行,但是可以在ALU的基本功能上进行组合运算的到。

首先,读者应该了解计算机的数据编码方式——补码

电子开发圈:原码、反码、补码?这样理解很简单​zhuanlan.zhihu.com

SUB指令

格式 :SUB 目的操作数,源操作数

作用:将源操作数数据减去目的操作数后传入到目的操作数中(会改变目的操作数)

在补码编码中,可以使用操作数取反加一的方式来的到此操作数的负数,所以,对于一个SUB指令,我们可以拆分为(1)操作数取反加一(2)取负数后的操作数加上被减数。我们可以通过ALU基础指令得到SUB指令。

MUL指令

格式 :MUL 操作数

作用:当操作数为8位时,操作数与AL相乘并且存放在AX中,当操作数为16位时,操作数与AX相乘,高16位存放在DX中,低16位存放在AX中

DIV指令

格式 :DIV 操作数

作用:当操作数为8位时,AX处以操作数并且商存放在AL中,余数存放在AH中,当操作数为16位时,被除数高16位存放在DX中,低16位存放在AX中,与操作数相除,余数存放在DX中,商存放在AX中。

当然,MUL与DIV指令同样可以通过ALU的基础指令得到(只要知道,数的乘除法是通过移位操作得到的即可)

汇编 align_从零开始自制操作系统(5):实模式汇编(二)相关推荐

  1. 《操作系统真象还原》从零开始自制操作系统 全流程记录

    文章目录 前引 章节博客链接 实现源码链接 前引 这本<操作系统真象还原>里面一共有十五个章节 大约760页 这些系列博客也是我在做完哈工大操作系统Lab之后 觉得还是有些朦朦胧胧 毅然决 ...

  2. 《操作系统真象还原》从零开始自制操作系统 自写源码实现 (fs相关文件)

    文章目录 专栏博客链接 fs相关文件 编写完的dir.c 编写完的dir.h 编写完的file.c 编写完的file.h 编写完的fs.c 编写完的fs.h 编写完的inode.c 编写完的inode ...

  3. 一步步编写操作系统 11 实模式下程序分段的原因

    cpu中本来是没有实模式这一称呼的,是因为有了保护模式后,为了将老的模式区别开来,所以称老的模式为实模式.这情况就像所有同学坐在同一个教室里,本来没有老同学这一概念,但某天老师领着一个陌生人进入教室并 ...

  4. 多线程操作时操作系统时间片_从零开始自制操作系统(15):内核多线程

    1.多线程原理: (1)概述: 多线程是指CPU可以在一段时间中并行执行多个程序,比如我们可以一边听音乐.一边写代码(这两个程序可以"同时进行",我们称之为多进程,而多进程实现的本 ...

  5. 操作系统引导--从实模式到保护模式

    从开始到保护--系统开机引导 ------没有一个文档能写的通俗易懂,我希望写出来. 开机引导和实模式: 两个星期加上假期吐血整理,所述为计算机的开机引导,其中包括一系列计算机内存设置等等,由于没有老 ...

  6. x86从实模式到保护模式 pdf_【自制操作系统04】从实模式到保护模式

    通过前三章的努力,我们成功将控制权转交给了 loader.asm 这个程序.具体说就是 bios 通过加载并跳转到 0x7c00(IMB大叔们定的) 把控制权转交给了我们操作系统的第一个汇编程序 mb ...

  7. 手写一个X86操作系统实战:从零开始构建一个U盘启动的自制操作系统(一)

    这个标题可能有点大了:-) 一个操作系统至少应该有自己的文件系统和进程机制,不过我们的最终目标应该是这个~ 无论如何,看完本文,你应该可以手写一段通过U盘启动的在PC上运行的不需要其他软件来协助的自启 ...

  8. 操作系统学习:启动进入实模式

    本文参考书籍 操作系统真相还原 Linux内核完全剖析:基于0.12内核 x86汇编语言 从实模式到保护模式 ps:基于x86硬件的pc系统 实模式相关介绍 实模式在上文已经做了简要的介绍,实模式的寄 ...

  9. 3.操作系统——CPU的实模式、保护模式和长模式

    有实模式.保护模式.长模式 实模式16(实地址模式) 真实分为两个方面: 运行真实指令.不区分指令动作,只是直接执行指令的真实功能 发往内存的地址是真实.不加限制的. 总结来说就是,这个模式下直接往物 ...

最新文章

  1. node.js(一)
  2. [SDOI2017]数字表格
  3. 《MATLAB R2012a超级学习手册》一2.5 本章小结
  4. 光纤传感器实验模块_飞秒激光制备异质光纤光栅的温度应变双参数传感器
  5. 日志-周报-月报(2019年2月)
  6. java 拼图_我最喜欢的Java拼图2 + 1 = 4
  7. win主机上搭建php网站运行环境,Windows server 2008搭建php运行环境图文详解(php5.3)
  8. 基础知识—表达式与语句-运算符
  9. 求多个数的最小公倍数
  10. 基于SSD的存储IO优化解决方案
  11. “互联网+”时代,漫谈影响用户体验的X因素
  12. win10 安装VB6详细安装教程及其中的坑
  13. GitHub Android 最火开源项目Top20
  14. 双系统,主系统损坏,如何启动另一个系统
  15. 方形图片使用QLabel显示成圆形
  16. OUC暑期培训(深度学习)——第四周学习记录:MobileNetV1,V2,V3
  17. CAS-认证流程详解
  18. 【成神之路】Http网络相关面试题
  19. No2. 图像几何变换
  20. Linux命令+shell脚本大全:处理目录

热门文章

  1. 工业物联网再起云涌,大咖共叙破圈之道
  2. 打破“打工人”魔咒,RPA 来狙击!
  3. 光刻机的“崛起秘密”,第一本ASML的企业成长传记来了!
  4. 盘一盘 Spring 核心技术之依赖注入 | 原力计划
  5. 开源公司 HashiCorp 国内险遭禁,阿里、华为也要做好 B 计划?
  6. 一文读懂约瑟夫环算法 | 原力计划
  7. 详解 Ops 智能运维机器人,故障处理又快又准!
  8. 35 万行代码,旷视重磅开源天元深度学习框架 ,四大特性实现简单开发
  9. 十一款游戏教你学会 CSS!
  10. 程序员如何才算真正的高效编程?