【问题标题】:NASM: Invalid effective address in Real ModeNASM:实模式下无效的有效地址
【发布时间】:2021-06-30 15:44:54
【问题描述】:

我正在尝试在实模式下绘制到屏幕,因此我正在尝试使用分段访问 0xB8000

我的汇编代码是这样的

[BITS 16]
org 0x7c00

begin:
  mov ah, 0x01 ; disable cursor
  mov ch, 0x3f
  int 0x10

  mov ch, 0x0000
  mov cs, 0xb800
  mov ah, 0x0000
  mov [cs:ah], ch ; invalid effective address

end:
  jmp end

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

如何正确使用分段解决 0xB8000?

【问题讨论】:

  • 查看a16 addressinga32 addressing。对于 a16(您必须在真正的 8086、186 或 286 上使用),您必须使用 disp16disp8bpbx,以及 sidi。对于 a32(您可以在 386+ 上使用),任何 32 位通用寄存器都可以用作基础。 (在 Real 86 模式下,您需要使用评估为 FFFFh 或更低的地址,但在此约束下允许使用 a32。)
  • mov cs 是无效的。 cs: 可以用作段前缀,但它指的是您正在执行的任何 代码段。如果您想处理一些其他数据,您应该使用数据段 ds 或额外段es[cs:ah] 是无效地址,因为没有以ah 作为寄存器的地址编码。
  • 请注意,对引导扇区中的段寄存器进行任何事情是不明智的。甚至没有 cs:BIOS 将在线性地址 0x07c00 加载您的代码,但一些 BIOS 将使用 cs=0ip=0x7c00 跳转到您的代码,而另一些则使用 cs=0x07c0ip=0

标签: assembly nasm x86-16 osdev real-mode


【解决方案1】:

记住cs代码段 寄存器。 You can't mov into it。即使可以,您也不会喜欢结果;由于cs:ip 用于确定从何处获取指令,您会发现您的CPU 正在执行视频内存的内容。所以你应该改用dses

接下来,你不能用立即数加载段寄存器;您只能从另一个寄存器或内存中加载它。

最后,您不能使用像ah 这样的 8 位寄存器作为地址的偏移量部分:偏移量是 16 位。事实上,可以在 16 位 8086 的有效地址中使用的唯一寄存器是 bx, si, di, bp。见differences between general purpose registers in 8086。 (bp 寻址使用 ss 段而不是 ds 除非被覆盖,所以这里可能不应该是您的首选。)

所以你可以使用一些选项:

mov ax, 0xb800
mov ds, ax
mov bx, 0x0000 ; but xor bx, bx is more efficient
mov [bx], ch
mov ax, 0xb800
mov es, ax
mov bx, 0x0000
mov [es:bx], ch
mov ax, 0xb800
mov es, ax
mov [es:0x0000], ch

作为一般原则,在汇编中编写时,您不能只猜测哪些指令采用哪些操作数组合;你必须查一下。您只能使用架构实际实现的那些形式,有时它们的选择与您可能在逻辑上假设的不符。随着时间的推移,您可能会开始识别一些模式,但作为初学者,请计划参考您编写的每一行的说明参考。

【讨论】:

  • 如果您无论如何都要使用偏移量来破坏 BX,您可以通过使用 mov bx, 0xb800 / mov es, bx 设置 ES 或 DS 来保持 AX 不变。由于这段代码当时没有将 AX 用于其他任何事情,因此它并不是“更好”,但将它们混合起来可能会很有趣。
  • 虽然对于代码大小来说更好的是xor bx,bx / mov [bx], bl,因为您已经在寄存器中有一个零,无需初始化 CX 或 CH。 (写mov ch, 0x0000 也很奇怪,因为 CH 只是一个 8 位寄存器。)但是对于未来只对段寄存器感兴趣的读者来说,谈论这个可能会使它成为一个更糟糕的答案,而不是其他奇怪的东西。问题的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
  • 2021-07-28
  • 2021-12-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多