汇编 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”时:

  1. SP 将是 0x7c00 + 1024 + 512,因为物理地址应该是 SS:SP = 0<<4 + 0x7c00 + 1024 + 512。所以“开始”= 0x7c00
  2. SP 将是 0 + 1024 + 512,因为物理地址应该是 SS:SP = 0x07c0<<4 + 1024 + 512. 所以“开始”= 0x0000

但如果我写“jmp start”,处理器将始终转到地址 0x7c00。它将计算

  1. 0 + 0x07c0
  2. 0x07c0<<4 + 0

为什么 'start' return MOVLMP 的值不同?或者它会是相同的,但是在 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:00:0x7C00,您的程序会做一些不同的事情。为此,据说0x7C0:00: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:SIstosb访问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 寄存器具有定义的值。