实模式内存寻址中的段和偏移量是多少?

我正在读关于内存寻址的内容。我读了关于段偏移然后关于描述符偏移的内容。我知道如何计算实模式下的确切地址。这一切都没问题,但我无法理解究竟是什么偏移?到处都读到:   在实模式下,寄存器只有16位,因此您只能寻址   高达64k。为了允许寻址更多的内存,地址是   从段* 16 +偏移量计算。 在这里我可以理解第一行。我们有16位,所以我们可以解决最多2 ^ 16 = 64k。 但这第二行是什么?细分代表什么?为什么我们将它乘以16?为什么我们添加偏移。我只是无法理解这个偏移是什么?有人可以解释我或给我链接吗?     
已邀请:
在x86实模式内存下,物理地址为20位长并且是calcolated:
PhysicalAddress = Segment * 16 + Offset
另请参阅:实模式内存管理     
当英特尔正在构建8086时,有一个有效的案例,在一台机器上有超过64KB,但它没有办法使用32位地址空间。那时候,甚至一兆字节都是一大堆内存。 (还记得臭名昭着的引语“640K应该对任何人来说都足够了吗?”这实际上是一个错误翻译的事实,当时,1MB非常大。)“gigabyte”这个词在未来15到20年内不会被普遍使用,并且在那之后的5到10年内它不会像RAM一样。 因此,他们没有实现如此巨大的地址空间以至于“永远不会”被充分利用,而是实现了20位地址。他们仍然使用16位字作为地址,因为毕竟这是一个16位处理器。上面的单词是“段”,下面的单词是“偏移”。然而,这两个部分相当重叠 - “段”是一个64KB的内存块,从
(segment) * 16
开始,“offset”可以指向该块内的任何位置。为了计算实际地址,您将地址的段部分乘以16(或将其左移4位......相同的事物),然后添加偏移量。当你完成后,你有一个20位的地址。
 19           4  0
  +--+--+--+--+
  |  segment  |
  +--+--+--+--+--+
     |   offset  |
     +--+--+--+--+
例如,如果段为0x8000,偏移量为0x0100,则实际地址为
((0x8000 << 4) + 0x0100)
==
0x80100
   8  0  0  0
      0  1  0  0
  ---------------
   8  0  1  0  0
数学很少很整洁,但是 -
0x80100
可以用几千个不同的段表示:偏移组合(4096,如果我的数学是正确的)。     
我想在这里添加一个答案,因为我一直在搜索互联网,试图理解这一点。其他答案是遗漏了一条关键信息,我从其中一个答案中提供的链接中得到了这些信息。但是,我几乎完全错过了它。通过链接页面阅读,我仍然不明白这是如何工作的。 我可能遇到的问题是我自己才真正了解Commodore 64(6502处理器)如何布局。它使用类似的表示法来处理内存。它有64k的总内存,并使用PAGE:OFFSET的8位值来访问内存。每页长度为256字节(8位数),偏移量指向该页面中的一个值。页面在内存中背靠背间隔。所以第2页从第1页结束开始。我正在考虑同样风格的386。事实并非如此。 真实模式使用类似的风格,即使它的措辞不同:SFSMENT:OFFSET。一个段的大小为64k。然而,这些细分市场并不像Commodore那样背靠背布局。它们彼此间隔16个字节。偏移量仍然相同,表示页面段开始的字节数。 我希望这个解释可以帮助那些发现这个问题的人,它帮助我写了这个问题。     
我可以看到问题和答案已经存在了几年,但有一个错误的说法是在实模式中只存在16位寄存器。 在实模式下,寄存器不仅是16位,因为也有8位寄存器。 这8位寄存器中的每一个都是16位寄存器的一部分,它们被分成16位寄存器的低位和高位。 并且使用80386+启动实模式,我们成为32位寄存器,另外还有两个新的指令前缀,一个用于覆盖/反转默认操作数大小,另一个用于覆盖/反转内部一条指令的默认地址大小。代码段。 这些指令前缀可以组合使用,以便为一条指令反转操作数大小和地址大小。在实模式下,默认操作数大小和地址大小为16位。使用这两个指令前缀,我们可以使用32位操作数/寄存器示例来计算一个32位寄存器中的32位值,或者用于将32位值移入和移出存储器位置。我们可以使用所有32位寄存器(可能与基数+索引*标度+位移组合)作为地址寄存器,但有效地址的总和不必超过64 kb段大小的限制。 (在OSDEV-Wiki页面上,我们可以在表中找到“操作数大小和地址大小覆盖前缀”,“0x66操作数前缀”和“0x67地址前缀”是N / A(不可用)实模式和虚拟8086模式.http://wiki.osdev.org/X86-64_Instruction_Encoding 但这完全是错误的,因为在英特尔手册中我们可以找到这样的声明:“这些前缀可用于实地址模式以及受保护模式和虚拟8086模式”。) 从奔腾MMX开始,我们成为8个64位MMX寄存器。 从Pentium 3开始,我们成为8个128位XMM寄存器。 .. 如果我没有错,则在实模式下不能使用256位YMM寄存器和512位ZMM寄存器以及x64的64位通用寄存器。 短剑     
最小的例子 附: 偏移=
msg
segment =
ds
mov $0, %ax
mov %ax, %ds
mov %ds:msg, %al
/* %al contains 1 */

mov $1, %ax
mov %ax, %ds
mov %ds:msg, %al
/* %al contains 2: 1 * 16 bytes forward. */

msg:
.byte 1
.fill 15
.byte 2
所以如果你想访问64k以上的内存:
mov $0xF000, %ax
mov %ax, %ds
请注意,如果您使用以下内容,则允许大于20位宽的地址:
0x10 * 0xFFFF + 0xFFFF == 0x10FFEF
在只有20条地址线的早期处理器上,它被简单地截断了,但后来事情变得复杂了A20线(第21条地址线):https://en.wikipedia.org/wiki/A20_line 在带有所需样板的GitHub仓库上运行它。     
16位寄存器最多只能寻址0xFFFF(65,536字节,64KB)。当这还不够时,英特尔增加了段寄存器。 任何逻辑设计都会简单地将两个16位寄存器组合成一个32位的地址空间(例如
0xFFFF : 0xFFFF = 0xFFFFFFFF
),但是没有......英特尔不得不对我们感到奇怪。 从历史上看,前端总线(FSB)只有20条地址线,因此只能传输20位地址。为了“纠正”这一点,英特尔设计了一种方案,其中段寄存器仅将地址扩展4位(理论上为16位+ 4 = 20)。 为实现此目的,段寄存器从其原始值左移4位,然后添加到通用寄存器中的地址(例如
[es:ax] = ( es << 4 ) + ax
)。注意:左移4位相当于乘以16。 而已。这是一些说明性的例子:
;; everything's hexadecimal

[ 0:1 ] = 1

[ F:1 ] = F1

[ F:0 ] = F0

[ F:FF] = 1EF ; [F becomes F0, + FF = 1EF]

[ F000 : FFFF ] = FFFFF (max 20-bit number)

[ FFFF : FFFF ] = 10FFEF (oh shit, 21-bit number!)
因此,您仍然可以处理超过20位。怎么了?地址“环绕”,就像模数运算(作为硬件的自然结果)。所以,
0x10FFEF
变成了
0xFFEF
。 你有它!英特尔聘请了一些愚蠢的工程师,我们必须忍受它。     

要回复问题请先登录注册