segment:常量中的偏移量

时间:2017-12-06 08:25:14

标签: assembly nasm x86-16

我正在努力学习装配。我已经编写了这个我想要的短实模式功能。

getndrives:
        push    bx
        push    es
        push    si
            mov     bx,0040h
            mov     es,bx
            mov     si,0075h
            mov     al,byte [es:si]
        pop     si
        pop     es
        pop     bx
    ret

我想要段:偏移在一个常量而不需要推/弹es + si。像

这样的东西
biosmem EQU 0040h:0075h
mov al,[biosmem]

以上代码编译但未返回预期结果。

2 个答案:

答案 0 :(得分:3)

x86没有办法从一条指令中的立即常量far-pointer加载。远远jmp ptr16:16可以将cs:ip设置为32位立即数,但是对于负载,我没有看到任何不使用段寄存器的选项。< / p>

NASM docs suggestDS的{​​16}调用约定通常是调用保留的,但ES可能被认为是call-clobbered,原因就在于你想要(在内存模型中,不要将所有内容保存在一个片段中)。

所以你可以考虑使用这个函数的调用约定来允许它破坏ES。 (或FSGS,如果您的代码只需要在386或更高版本上运行。)

您还可以通过为该段重复使用相同的寄存器来保存指令。您可以两次使用si而不是bx,因为您在设置bx后立即使用es

getndrives:
 ;; return in AL (zero-extended to AX; the upper byte of 0040h happens to be 0)
 ;; clobbers: AH, ES
    mov     ax, 0x0040
    mov     es, ax
    mov     al, [es:0x0475]
    ret

作为奖励,您需要加载到AL中,以便它可以使用跳过ModR / M字节的特殊moffs encoding of mov,以便它只是(ES段覆盖前缀)+ {{1 }}

如果你愿意,你当然可以推动/弹出A0 75 04和/或es,但这显然是笨拙的。如果您要保存/恢复段寄存器,@ Fifoernik指出DS也可以在ax上保存前缀(除非您有任何假设DS保持不变的中断处理程序)

mov

如果你只关心186和更新,getndrives: ;; return in AL (zero-extended to AX; the upper byte of 0040h happens to be 0) ;; clobbers: AH, or nothing if you uncomment the push/pop of AX push es ;push ax ; you might as well use a reg other than AX to simplify this, if you do want to preserve AH, too. And also for performance on CPUs that don't rename low8 partial regs separately, so mov al,[mem] has a dependency on the pop. mov ax, 0x0040 mov es, ax ; or use DS if you don't need it in any interrupt handler ;pop ax mov al, [es:0x0475] pop es ret / push 0040h会避免破坏AH。 (8086没有pop es)。

在您的情况下,您可以避免首先修改段寄存器。请注意,push imm8/imm160040h:0075h的线性地址,您只需从0475hDS的任何0值进行偏移即可访问该地址。 (在实模式下,47h,即左移一个十六进制数字。)

当DS = 0时,您仍然可以访问引导扇区代码/数据(according to this PC memory map,加载到linear = (segment << 4) + offset,又名07C0:0),以及低64kiB中的任何其他位置存储器中。

我实际上并没有写16位代码,因此我不确定您是如何告诉NASM您要设置0:7C00然后拥有它相应地产生偏移。但希望这是可能的。

作为奖励,它可以节省代码大小:DS=0小于xor ax,ax。但我想如果您要针对代码大小进行优化,那么您需要mov ax, imm16 / push 0。 (但8086没有pop ds,后来出现了。我想如果你想让堆栈指针与其他指针兼容,你需要push imm / mov ss, ax以便更多的代码。

但无论如何,那么你的功能就像这样。

mov sp, whatever

在这一点上,使它成为一种功能是愚蠢的。将其设为宏或更好,或getndrives: ;; return in AL ;; requires/assumes: DS=0 mov al, [0x0475] ret ,以便您可以执行%define BIOS_BDA_ndrives 0x0475之类的操作。或者可能add dl, [BIOS_BDA_ndrives]来获取构建时类型检查,例如然后%define BIOS_BDA_ndrives byte [0x0475]无法以操作数大小不匹配进行汇编。

答案 1 :(得分:1)

地址的段部分始终使用段寄存器。您可以使用常量作为偏移量。