汇编 x86 实模式中的寻址如何工作?为什么标签 return 有不同的值?
How addressing works in assembly x86 Real Mode? Why does label return different values?
我有两种引导加载程序代码变体(它应该在引导加载程序代码的 512b 之后移动 1kb 堆栈)。
起始物理地址始终为 0x7c00(标签“起始”)。 BIOS 在 RAM 中复制引导加载程序代码。
当我使用“MOV SP, start+1024+512”时:
- SP 将是 0x7c00 + 1024 + 512,因为物理地址应该是 SS:SP = 0<<4 + 0x7c00 + 1024 + 512。所以“开始”= 0x7c00
- SP 将是 0 + 1024 + 512,因为物理地址应该是 SS:SP = 0x07c0<<4 + 1024 + 512. 所以“开始”= 0x0000
但如果我写“jmp start”,处理器将始终转到地址 0x7c00。它将计算
- 0 + 0x07c0
- 0x07c0<<4 + 0
为什么 'start' return MOV 和 LMP 的值不同?或者它会是相同的,但是在 MOV 中处理器不添加段,而在 JMP 中添加?另外,在这种情况下,“开始”是根据 DS:SI 而不是 CS:IP 计算的吗?
另一个例子。如果我在最后添加代码
mov SI, main
lodsb ;write data from main to AL
处理器将始终转到完整的物理地址段+偏移量并在寄存器AL中获取值'S'。 SI 只会等于偏移量?处理器会在执行 lodsb 期间添加段吗?
补充问题:
处理器如何执行“jmp main”?该指令在“mov ds, ax”之上。因此 2 变体中的代码有错误,但它有效。
BIOS加载bootloader时默认CS寄存器的值是多少?显然CS:IP应该是0x7c00.
1 个变体
[bits 16]
[org 0x7c00]
start: ;offset= 0x7c00
jmp main
db "Some data" ;actually fake BIOS Parameter Block(BPB)
main:
mov ax, 0
mov ds, ax ; data segment =0.
mov ss, ax ; stack segment = 0
mov sp, start+1024+512 ;stack pointer = 0x7c00+1024+512
2 个变体
[bits 16]
[org 0x0000]
start: ;offset= 0x0000
jmp main
db "Some data" ;actually fake BIOS Parameter Block(BPB)
main:
mov ax, 0x07c0
mov ds, ax ; data segment =0x07c0.
mov ss, ax ; stack segment =0x07c0.
mov sp, start+1024+512 ;stack pointer = 0+1024+512
... always go to the address 0x7c00. It will calculate ...
在分段内存模型中,您不仅应该考虑有效(物理)地址,还必须始终将地址视为实模式或 16 位保护模式下的 (16+16) 位值模式或 32 位保护模式下的 (16+32) 位值。
假设您的程序包含指令 mov al, cs:[100h]
。
这条指令将从地址 CS:0x100
中读取一些字节,实际上是 (CS<<4)+0x100
.
如果执行跳转到0x7C0:0
,该指令将访问地址(0x7C0<<4)+0x100=0x7D00
处的内存;如果执行跳转至 0:0x7C00
,此指令将访问地址 (0<<4)+0x100=0x100
.
处的内存
这意味着如果您跳转到 0x7C0:0
或 0:0x7C00
,您的程序会做一些不同的事情。为此,据说0x7C0:0
和0:0x7C00
是两个不同的地址。
假设 main
位于物理地址 0x7C40
。
这意味着 main
的地址既不是 0x7C40
也不是 0x40
,而是 0:0x7C40
(在“变体 1”中)或 0x7C0:0x40
(在“变体 2”中),因为您始终必须将地址指定为段和偏移量对。
在保护模式下的分段内存模型中,这更加复杂,使用正确的段更为重要!
SI will be equal to offset only?
Also is it true that in this case "start" will be calculated based on DS:SI and not CS:IP?
lodsb
指令访问地址DS:SI
,stosb
访问ES:DI
。
这意味着SI
只保存偏移量而DS
只保存段。
变体 1 和 2 将不同的值加载到 SI
寄存器,因为在一个变体中 main
位于地址 0:0x7C40
(这意味着:SI=0x7C40
)在另一个变体中 0x7C0:0x40
(SI=0x40
)。
所以在变体 1 中,您必须设置 DS=0
,在变体 2 中,您必须设置 DS=0x7C0
.
在一种情况下,lodsb
将访问地址 0:0x7C40
,在另一种情况下,lodsb
将访问地址 0x7C0:0x40
。在这两种情况下,都访问了 RAM 中的相同字节:物理地址 0x7C40
.
How does processor execute "jmp main"? This instruction is above "mov ds, ax". Therefore code in the 2 variant has an error, but it works.
JMP
指令有两种变体:
一种变体不会将固定值写入 IP
寄存器,但会向 IP
寄存器添加一些常量值。因此,如果将 0x40
添加到 0x7C0:0
,代码将在 0x7C0:0x40
处继续执行。如果将 0x40
添加到 0:0x7C00
,代码将在 0:0x7C40
处继续执行。在这两种情况下,下一条指令都位于物理地址0x7C40
。 (可能 jmp main
就是这个变体。)
另一个变体以一对段和偏移量作为参数。您不能跳转到地址 0x7C40
,但可以跳转到地址 0:0x7C40
或地址 0x7C0:0x40
.
What is the value of CS register by default when BIOS loads bootloader?
有少数BIOS跳转到0x7C0:0
,但标准似乎是0:0x7C00
。
出于这个原因,许多引导加载程序执行跳转到 0x7C0:0x60
(作为示例)以确保 CS
寄存器具有定义的值。
我有两种引导加载程序代码变体(它应该在引导加载程序代码的 512b 之后移动 1kb 堆栈)。 起始物理地址始终为 0x7c00(标签“起始”)。 BIOS 在 RAM 中复制引导加载程序代码。 当我使用“MOV SP, start+1024+512”时:
- SP 将是 0x7c00 + 1024 + 512,因为物理地址应该是 SS:SP = 0<<4 + 0x7c00 + 1024 + 512。所以“开始”= 0x7c00
- SP 将是 0 + 1024 + 512,因为物理地址应该是 SS:SP = 0x07c0<<4 + 1024 + 512. 所以“开始”= 0x0000
但如果我写“jmp start”,处理器将始终转到地址 0x7c00。它将计算
- 0 + 0x07c0
- 0x07c0<<4 + 0
为什么 'start' return MOV 和 LMP 的值不同?或者它会是相同的,但是在 MOV 中处理器不添加段,而在 JMP 中添加?另外,在这种情况下,“开始”是根据 DS:SI 而不是 CS:IP 计算的吗? 另一个例子。如果我在最后添加代码
mov SI, main
lodsb ;write data from main to AL
处理器将始终转到完整的物理地址段+偏移量并在寄存器AL中获取值'S'。 SI 只会等于偏移量?处理器会在执行 lodsb 期间添加段吗?
补充问题:
处理器如何执行“jmp main”?该指令在“mov ds, ax”之上。因此 2 变体中的代码有错误,但它有效。
BIOS加载bootloader时默认CS寄存器的值是多少?显然CS:IP应该是0x7c00.
1 个变体
[bits 16]
[org 0x7c00]
start: ;offset= 0x7c00
jmp main
db "Some data" ;actually fake BIOS Parameter Block(BPB)
main:
mov ax, 0
mov ds, ax ; data segment =0.
mov ss, ax ; stack segment = 0
mov sp, start+1024+512 ;stack pointer = 0x7c00+1024+512
2 个变体
[bits 16]
[org 0x0000]
start: ;offset= 0x0000
jmp main
db "Some data" ;actually fake BIOS Parameter Block(BPB)
main:
mov ax, 0x07c0
mov ds, ax ; data segment =0x07c0.
mov ss, ax ; stack segment =0x07c0.
mov sp, start+1024+512 ;stack pointer = 0+1024+512
... always go to the address 0x7c00. It will calculate ...
在分段内存模型中,您不仅应该考虑有效(物理)地址,还必须始终将地址视为实模式或 16 位保护模式下的 (16+16) 位值模式或 32 位保护模式下的 (16+32) 位值。
假设您的程序包含指令 mov al, cs:[100h]
。
这条指令将从地址 CS:0x100
中读取一些字节,实际上是 (CS<<4)+0x100
.
如果执行跳转到0x7C0:0
,该指令将访问地址(0x7C0<<4)+0x100=0x7D00
处的内存;如果执行跳转至 0:0x7C00
,此指令将访问地址 (0<<4)+0x100=0x100
.
这意味着如果您跳转到 0x7C0:0
或 0:0x7C00
,您的程序会做一些不同的事情。为此,据说0x7C0:0
和0:0x7C00
是两个不同的地址。
假设 main
位于物理地址 0x7C40
。
这意味着 main
的地址既不是 0x7C40
也不是 0x40
,而是 0:0x7C40
(在“变体 1”中)或 0x7C0:0x40
(在“变体 2”中),因为您始终必须将地址指定为段和偏移量对。
在保护模式下的分段内存模型中,这更加复杂,使用正确的段更为重要!
SI will be equal to offset only?
Also is it true that in this case "start" will be calculated based on DS:SI and not CS:IP?
lodsb
指令访问地址DS:SI
,stosb
访问ES:DI
。
这意味着SI
只保存偏移量而DS
只保存段。
变体 1 和 2 将不同的值加载到 SI
寄存器,因为在一个变体中 main
位于地址 0:0x7C40
(这意味着:SI=0x7C40
)在另一个变体中 0x7C0:0x40
(SI=0x40
)。
所以在变体 1 中,您必须设置 DS=0
,在变体 2 中,您必须设置 DS=0x7C0
.
在一种情况下,lodsb
将访问地址 0:0x7C40
,在另一种情况下,lodsb
将访问地址 0x7C0:0x40
。在这两种情况下,都访问了 RAM 中的相同字节:物理地址 0x7C40
.
How does processor execute "jmp main"? This instruction is above "mov ds, ax". Therefore code in the 2 variant has an error, but it works.
JMP
指令有两种变体:
一种变体不会将固定值写入 IP
寄存器,但会向 IP
寄存器添加一些常量值。因此,如果将 0x40
添加到 0x7C0:0
,代码将在 0x7C0:0x40
处继续执行。如果将 0x40
添加到 0:0x7C00
,代码将在 0:0x7C40
处继续执行。在这两种情况下,下一条指令都位于物理地址0x7C40
。 (可能 jmp main
就是这个变体。)
另一个变体以一对段和偏移量作为参数。您不能跳转到地址 0x7C40
,但可以跳转到地址 0:0x7C40
或地址 0x7C0:0x40
.
What is the value of CS register by default when BIOS loads bootloader?
有少数BIOS跳转到0x7C0:0
,但标准似乎是0:0x7C00
。
出于这个原因,许多引导加载程序执行跳转到 0x7C0:0x60
(作为示例)以确保 CS
寄存器具有定义的值。