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

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

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

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
c00b17341bc4dab5a5c6ec45ccccd1f2.png

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的基础指令得到(只要知道,数的乘除法是通过移位操作得到的即可)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值