问题描述
我一直在x86-16装配瞎搞和VirtualBox的运行它。出于某种原因,当我从内存中读取并尝试打印它作为一个人物,我得到了我的期待完全不同的结果。然而,当我硬code中的字符作为指令的一部分,它工作正常。
这里的code:
0 ORG
位16推字0xB800;在彩色显示器实模式文本屏幕的视频内存地址
推CS
流行DS; DS = CS
流行ES; ES = 0xB800
JMP启动;输入=二(位置* 2),斧头(字符和属性)
的putchar:
STOSW
RET;输入= SI(NUL结尾的字符串)
打印:
CLI
CLD
.nextChar:
LODSB; MOV等,[DS:SI]; SI + = 1
测试人,人
JZ .finish
打电话的putchar
JMP .nextChar
。完:
STI
RET开始:
MOV啊,为0x0E
MOV DI,8 ;应打印P
MOV人,字节[味精]
打电话的putchar ;应打印
MOV人,字节[味精+ 1]
打电话的putchar ;应打印Ø
MOV人,字节[味精+ 2]
打电话的putchar ;应打印!
MOV人,字节[味精+ 3]
打电话的putchar ;应打印点¯x
MOV人,X
打电话的putchar ;应打印ÿ
MOV人,Y
打电话的putchar CLI
HLT味精:DB'PAO!',0;填充字节的其余部分与高达0字节510
次510 - ($ - $$)0分贝;头
将0x55分贝
DB和0xAA
打印标签和说明书中可因为我还没有使用它,因为我一直在尝试打印存储在内存中的字符的问题被忽略。我既FASM和NASM组装,并有同样的问题这意味着它显然是我的错。
它打印是这样的:
ORG指令
当您指定的 ORG 的如 ORG 0×0000
指令在你的汇编程序的顶部,并使用位16
你通知的 NASM 的,解决标签code和数据时,也将将根据起点在指定的偏移量生成的绝对偏差的 ORG 的(16位code将被限制在一个偏移量是一个的字的/ 2字节)。
如果您有 ORG 0×0000
的开始和放置标签启动:
在$ C的开始$ C,启动
将有绝对的为0x0000抵消。如果你使用 ORG 0x7C00
然后标签启动
将有绝对的0x7c00抵消。这将适用于任何数据标签和code标签。
我们可以简化您的例子,看到一个数据变量和硬codeD人物打交道时,发生了什么事在生成的code。虽然这code不完全执行相同的操作你的code,它是足够接近展现什么可行,什么不可行。
使用示例 ORG 0×0000
位16
ORG为0x0000开始:
推CS
流行DS; DS = CS
推0xb800
流行ES; ES = 0xB800(显存)
MOV啊,0x0E的; AH =属性(黑底黄字) MOV人,字节[味精]
MOV [ES:0×00],斧头;这应该打印字母'P'
MOV人,字节[味精+ 1]
MOV [ES:0×02],斧头;这应该打印字母'A'
MOV人,'O'
MOV [ES:0×04],斧头;这应该打印字母O
MOV人,!
MOV [ES:0×06],斧头;这应该打印字母'! CLI
HLT味精:DBPA;启动扇区填充
次510 - ($ - $$)0分贝
DW 0xAA55将
如果你在的的VirtualBox 的第2个字符是垃圾运行此而 0!
应正确显示。我将使用通过这个答案的其余部分这个例子。
的VirtualBox / CS:IP /段:偏移双
在虚拟盒的情况下,这将有效地做的相当于的一个的 FAR JMP 的为0x0000的:0x7c00物理地址0x00007c00加载引导扇区之后。 A FAR JMP 的(或同等学历)不仅将跳转到指定的地址,它设置的 CS 和 IP 应用于取值。 A FAR JMP 的为0x0000:0x7c00将设置的 CS = 0x0000,并的 IP 的= 0x7c00。
如果一个是不熟悉落后16位段的计算:偏移对他们是如何映射到物理地址,然后这个文件是一个相当不错的起点,理解概念。一般方程从16位段获得一个物理内存地址:偏移对是(段<< 4)+偏移量= 20位物理地址
由于VirtualBox中使用的 CS:IP 的0x0000到:0x7c00将开始在一个物理地址执行code(0×0000<&4;)+ 0x7c00 = 20位物理地址0x07c00。请注意,这是不能保证在所有环境中的情况。因为段的性质:抵消对,有参考物理地址0x07c00一种以上的方式。请参见这个答案就如何妥善处理这一年底。
什么是与你的引导错误去?
假设我们使用VirtualBox和上面在previous节中的信息被认为是正确的,那么 CS = 0x0000,并的 IP 的= 0x7c00进入我们的bootloader在。如果我们把例如code(使用 ORG 0×0000
)我在这个答案的第一部分中写道,并期待在反汇编的信息(我将使用的 objdump的的输出),我们会看到这一点:
objdump的-Mintel -mi8086 -D -b二进制 - 调整 - VMA = 0×0000 BOOT.BIN00000000所述;的.data计算值:
0:0E推CS
1:1F流行DS
2:68 00 B8推0xb800
5:07流行ES
6:B4 0E MOV啊,0xe
8:A0 24 00 MOV人,DS:0X24
A:26 00 A3 00 MOV ES:为0x0,斧
F:A0 25 00 MOV人,DS:0x25
12:26 A3 02 00 MOV ES:0X2,斧
16:B0 4F MOV人,0x4f
18:26 A3 04 00 MOV ES:为0x4,斧
1C:B0 21 MOV等,为0x21
1E:26 06 A3 00 MOV ES:为0x6,斧
22:FA CLI
23:F4 HLT
24:50推斧;字母'P'
25:41 INC CX;字母A
...
1FE:55推基点
1FF:AA STOS BYTE PTR ES:[二],人
由于组装到一个二进制文件时,ORG信息丢失,我用 - 调整 - VMA = 0×0000
,这样值的第一列(内存地址)启动为0x0000。我想这样做,因为我用 ORG 0×0000
原汇编code。我还增加了code一些意见,看看在那里我们的数据部分(以及其中字母 P
和 A
中的code后放置)。
如果你运行在VirtualBox中此计划的前两个字符会出来为乱码。那么,为什么会这样?首先召回的VirtualBox通过的 CS 的设置为0x0000和的 IP 的到0x7c00到达了code。这code再复制的 CS 到 DS 的:
0:0E推CS
1:1F流行DS
由于 CS 的是零,那么的 DS 的是零。现在,就让我们来看看这行:
8:24 A0 00 MOV人,DS:0X24
DS:0X24
实际上是恩codeD地址在我们的数据部分的信息的变量。在字节偏移0X24中有值 P
(0x25具有 A
)。您可能会看到,事情可能出错。我们的 DS 的= 0×0000这样 MOV人,DS:0X24
真的一样 MOV等,为0x0000:0X24
。此语法是无效的,但我更换的 DS 的将0x0000提出一个观点。 0×0000:0X24
是我们code,同时执行将尝试阅读我们的信 P
从。可是等等!即物理地址(0×0000&下; 4;)+ 0X24 = 0x00024。该存储器地址恰好是在存储器中的中断矢量表的中间的底部。显然,这不是我们所预期的!
有几种方法来解决这个问题。最简单的(和preferred法)是真正把正确的段成的 DS 的,而我们的程序运行时不依赖于什么 CS 的可能。由于我们设定的 ORG 的0x0000到,我们需要有一个数据段( DS 的)= 0x07c0。一个片段:offset对0x07c0的:0×0000 =物理地址0x07c00。这是我们的bootloader的地址是什么。所以我们要做的就是通过更换修改code:
推CS
流行DS; DS = CS
使用:
推0x07c0
流行DS; DS = 0x07c0
在的VirtualBox 的运行时,这种变化应该提供正确的输出。现在,让我们来看看为什么。这code并没有改变:
8:24 A0 00 MOV人,DS:0X24
执行时
现在的 DS 的= 0x07c0。这本来好像说 MOV人,0x07c0:0X24
。 0x07c0:0X24
,这将转化成的物理地址(0x07c0<&4;)+ 0X24 = 0x07c24。这是我们所希望的,因为我们的bootloader是由BIOS物理放入内存开始在该位置,因此它应该参考我们的信息的变量正确。
这个故事告诉我们?什么都你使用的 ORG 的应该是在 DS 的寄存器当我们开始program.We应该明确地设置它,而不是依靠什么是在 CS 的
为什么立即值打印?
使用原来的code,打印乱码的第2个字符,但最后两个没有。正如在previous部分讨论有一个原因,第2个字符不会打印,但什么有关所做的最后2个字符?
让我们看看第三个字符 0
更仔细的拆解:
16:B0 4F MOV人,0x4f; 0x4f ='O'
由于我们使用的即时(常量)值,并把它移到到寄存器的 AL 的,性格本身就是连接$ C $光盘作为指令的一部分。它不依赖于通过的 DS 的注册内存访问。正因为如此正确显示的最后2个字符。
罗斯里奇的建议和为什么它在VirtualBox中
罗斯岭建议我们使用 ORG 0x7c00
,你观察到它的工作。为什么会这样?并且是理想解决方案?
使用我的第一个例子,并修改 ORG 0×0000
到 ORG 0x7c00
,然后组装。 objdump的
就已经提供了这个拆卸:
objdump的-Mintel -mi8086 -D -b二进制 - 调整 - VMA = 0x7c00 BOOT.BINBOOT.BIN:文件格式的二进制
段.data拆卸:00007c00<。数据计算值:
7c00:0E推CS
7c01:1F流行DS
7c02:68 00 B8推0xb800
7c05:07流行ES
7c06:B4 0E MOV啊,0xe
7c08:A0 24 7C MOV人,DS:0x7c24
7c0b:26 00 A3 00 MOV ES:为0x0,斧
7c0f:A0 25 7C MOV人,DS:0x7c25
7c12:26 02 A3 00 MOV ES:0X2,斧
7c16:B0 4F MOV人,0x4f
7c18:26 04 A3 00 MOV ES:为0x4,斧
7c1c:B0 21 MOV等,为0x21
7c1e:26 06 A3 00 MOV ES:为0x6,斧
7c22:FA CLI
7c23:F4 HLT
7c24:50推斧;字母'P'
7c25:41 INC CX;字母A
...
7dfe:55推基点
7dff:AA STOS BYTE PTR ES:[二],人
VirtualBox虚拟设置的 CS 的为0x0000,当它上升到我们的bootloader。我们原来的code再复制的 CS 到 DS 的,这样的 DS 的= 0×0000。现在观察一下 ORG 0x7c00
指令做我们产生code:
7c08:A0 24 7C MOV人,DS:0x7c24
请注意,我们现在是如何使用偏移0x7c24的!这就像 MOV等,为0x0000:0x7c24
这是物理地址(0×0000<&4;)+ 0x7c24 = 0x07c24。这就是装载引导加载程序的正确记忆位置,是我们的信息的字符串的正确位置。因此,它的工作原理。
时使用 ORG 0x7c00
一个坏主意?不,它是罚款。但是,我们有一个微妙的问题,与之抗衡。如果另一个虚拟PC环境或真实的硬件确实会发生什么事没有的 FAR JMP 导入使用的 CS我们的bootloader:0x0000到IP 的:0x7c000?这个有可能。有与远跳实际上相当于确实给 BIOS的许多物理PC 0x07c0:0×0000
。那也是物理地址 0x07c00
正如我们已经看到的。在这种环境下,当我们的code运行 CS 的= 0x07c0。如果我们使用原来的code那份 CS 到 DS 的 DS 的现在有0x07c0了。现在观察会发生什么,这code在这种情况下:
7c08:A0 24 7C MOV人,DS:0x7c24
的 DS 的=在这种情况下0x07c0。现在,我们有类似的东西 MOV人,0x07c0:0x7c24
当程序实际运行。 UT-哦,那很糟糕。这是什么转换为物理地址? (0x07c0&下; 4;)+ 0x7c24 = 0x0F824。这超出了我们的bootloader地方,它会包含无论发生什么事在计算机启动后在那里。可能为零,但应假定为垃圾。显然不是我们在那里的信息的字符串加载!
那么,我们如何解决这个问题?修改罗斯什么建议岭,并听取我previously给了有关建议明确设置的 DS 应用于我们真的想(不要以为段的 CS 的是正确的再盲目地复制到的 DS 的),我们应该把为0x0000到的 DS 的时候,如果我们用我们的bootloader启动 ORG 0x7c00
。因此,我们可以改变这个code:
ORG 0x7c00开始:
推CS
流行DS; DS = CS
到
ORG 0x7c00开始:
异斧,斧; AX = 0×0000
MOV DS,AX; DS = 0×0000
在这里,我们不的 CS 的依靠一个不受信任的价值。我们只需设置的 DS 应用于段值有道理给予的 ORG 的我们使用。你可以推0x0000并弹出它变成的 DS 的你一直在做。我更习惯于清零寄存器和移动,为的 DS 的
通过这种方法,它无关紧要什么价值的 CS 的可能已被使用,以达到我们的bootloader中,code仍然会引用相应的内存位置,对于我们的数据。
不要假设第一阶段是通过BIOS调用与CS:IP = 0×0000:0x7c00
在我的<一个href=\"http://stackoverflow.com/questions/32701854/boot-loader-doesnt-jump-to-kernel-$c$c/32705076#32705076\">General Bootloader的建议,我在previous StackOverflow的答案写,提示#1是非常重要的:
The BIOS could have FAR JMP'ed (or equivalent) to our code with jmp 0x07c0:0x0000
, and some emulators and real hardware do it this way. Others use jmp 0x0000:0x7c00
like VirtualBox does.
We should account for this by setting DS explicitly to what we need, and set it to what makes sense for the value we use in our ORG directive.
Summary
Don't assume CS is a value we expect, and don't blindly copy CS to DS . Set DS explicitly.
Your code could be fixed to use either ORG 0x0000
as you originally had it, if we set DS appropriately to 0x07c0 as previously discussed. That could look like:
ORG 0
BITS 16
push word 0xB800 ; Address of text screen video memory in real mode for colored monitors
push 0x07c0
pop ds ; DS=0x07c0 since we use ORG 0x0000
pop es
Alternatively we could have used ORG 0x7c00
like this:
ORG 0x7c00
BITS 16
push word 0xB800 ; Address of text screen video memory in real mode for colored monitors
push 0x0000
pop ds ; DS=0x0000 since we use ORG 0x7c00
pop es
这篇关于从8086实模式存储器中读取,而使用“ORG为0x0000”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!