从0创建一个OS (四) 电脑存储的组织形式


本节将学习boot sector以及其所在内存的相关知识
学习完本节后,要明确一个概念: 代码的存储区域和运行区域有可能不同

关键字:memory offsets; pointers

目标:学习电脑存储的组织形式


在进行学习之前,先看一下这幅图
在这里插入图片描述
这幅图为16bit实模式下电脑启动后的内存存储结构,读者请尤其注意粉色椭圆勾选的区域.


实验一

我们想要通过boot_sector的代码将大写字母"X"打印在屏幕上,为此设计了一组实验,实验设计见源码.

源码

; ===============================================================
; 文件名: boot_sect_memory.asm
; 本程序尝试打印一个"X",使用的方法为向AH传入0x0E,向AL传入待打印内容,即"X"
; 待打印内容定义为标签"the_secret"的形式
; ===============================================================
mov ah, 0x0e

; 尝试一
; 将标签赋值给AL,试图打印内容
; 结果:当然会失败,稍有汇编知识的人都知道,the_secret只是
; 一个地址,无法用地址代替内容

mov al, "1"
int 0x10
mov al, the_secret
int 0x10

; 尝试二
; 将标签指向的内容赋值给AL,试图打印内容
; 结果:失败
; 失败原因:BIOS将我们的boot_sector(本程序)编译出的二进制文件
; 放置在0x7C00的位置上,而[the_secret]只表示该标签指向的内容相对于地址0的
; 地址

mov al, "2"
int 0x10
mov al, [the_secret]
int 0x10

; 尝试三
; 在尝试二的基础上,获得the_secret指向的内容的绝对地址
; 绝对地址的形式为: 0x7c00 + 相对于地址0的地址(即[the_secret])
; 结果:成功
mov al, "3"
int 0x10
mov bx, the_secret
add bx, 0x7c00
mov al, [bx]
int 0x10

; 尝试四
; 在已知"X"存储在本程序编译完成的二进制文件的第0x2d个字节上(怎么知道的呢?数的呗)的情况下,直接
; 使用数值地址代替变量地址
; 译者曰:这个尝试四非常蠢,无法应用到实际当中
; 当然原作者只是为了展示the_secret的实际位置,给出这个示例无可厚非,但是实际中不应该这样使用

mov al, "4"
int 0x10
mov al, [0x7c2d]
int 0x10


jmp $

the_secret:
    db "X"

times 510 - ($ - $$) db 0
dw 0xAA55

 

编译并Boot

具体编译并Boot方法参见从0创建一个OS (二) boot_sector的"裸骨架"的编译、Boot部分

试验结果

在这里插入图片描述
可以看到尝试三和尝试四取得了成功,因为通过不同的方式,都取得了"X"的绝对地址.


新知识

如果每次对变量进行寻址都需要加上0x7C00,那岂不是太麻烦了,因此人们设计了汇编语法

org  段地址

使用该语法,使后续的寻址基地址都变为段地址.


实验二

接下来我们将"org 段地址"写入我们的源码第一行中,看看会有什么样的结果.

; ================================================================
; 文件名: boot_sect_memory_org.asm
; 本文件除开头的[org 0x7C00]外,其余与boot_sect_memory.asm一样
; 
; 本程序尝试打印一个"X",使用的方法为向AH传入0x0E,向AL传入待打印内容,即"X"
; 待打印内容定义为标签"the_secret"的形式
; ================================================================
[org 0x7C00]

mov ah, 0x0e

; 尝试一
; 将标签赋值给AL,试图打印内容
; 结果:当然会失败,稍有汇编知识的人都知道,the_secret只是
;      一个地址,无法用地址代替内容

mov al, "1"
int 0x10
mov al, the_secret
int 0x10

; 尝试二
; 将标签指向的内容赋值给AL,试图打印内容
; 结果:成功
; 结果改变原因:BIOS将我们的boot_sector(本程序)编译出的二进制文件
; 放置在0x7C00的位置上,但这一次[the_secret]表示该标签指向的内容相对于地址0x7C00的
; 地址

mov al, "2"
int 0x10
mov al, [the_secret]
int 0x10

; 尝试三
; 在尝试二的基础上,获得the_secret指向的内容的绝对地址
; 绝对地址的形式为: 0x7c00 + 相对于地址0x7C00的地址(即[the_secret])
; 结果:失败
; 结果改变原因:这里的绝对地址中多加了一个0x7C00,导致目标地址从正确变为错误
mov al, "3"
int 0x10
mov bx, the_secret
add bx, 0x7c00
mov al, [bx]
int 0x10

; 尝试四
; 在已知"X"存储在本程序编译完成的二进制文件的第0x2d个字节上(怎么知道的呢?数的呗)的情况下,直接
; 使用数值地址代替变量地址
; 结果: 成功
; 译者曰:这个尝试四仍旧非常蠢,无法应用到实际当中
; 当然原作者只是为了展示the_secret的实际位置,给出这个示例无可厚非,但是实际中不应该这样使用

mov al, "4"
int 0x10
mov al, [0x7c2d]
int 0x10


jmp $

the_secret:
    db "X"

times 510 - ($ - $$) db 0
dw 0xAA55

 

编译并Boot

具体编译并Boot方法参见从0创建一个OS (二) boot_sector的"裸骨架"的编译、Boot部分

实验结果

在这里插入图片描述
尝试二从失败变为成功,尝试三从成功变为失败,尝试四仍旧成功,具体原因参见源码中的注释.


总结

本节进行了诸多尝试,解释了boot sector中的变量存储方式,以后对boot sector的程序编写,首行一定要加上 org 0x7C00.

boot sector虽然存储在磁盘的第一个512个字节范围,但是运行时被load到了0x7C00进行运行.


番外. 查看二进制文件内容

一定有读者想要问上面源码中的0x2d 是怎么数出来的,在这里给大家演示以下,以证明我在尝试四中写的所谓的"愚蠢"是有道理的.

我们由上面的两个汇编源文件boot_sect_memory.asmboot_sect_memory_org.asm编译链接生成了boot_sect_memory.binboot_sect_memory_org.bin两个二进制文件,使用二进制文件查看工具可以看到其中的内容,本文这里使用的二进制文件查看工具为: vscode的插件"hexdump for vscode".

两个源码之间的区别只有首行的"org 0x7C00", 让我们来看一下这两个二进制文件的异同.

在这里插入图片描述
在这里插入图片描述
每个文件中标黄部分,相同的是16进制58,本质为"X"的ASCII码,不同的是,加上org 0x7C00后,第二行第一列的00变为了7C.

那么地址怎么看呢,请看下图
在这里插入图片描述
在**vscode的插件"hexdump for vscode"**中将鼠标放置需要查看的字节上,将自动显示该字节在该文件中的地址,由上图可以看出,"X"的地址为0x2d


 

posted @ 2020-11-18 17:01  EwanHai  阅读(143)  评论(0编辑  收藏  举报