(
)NASM 在表达式中支持两个特殊的记号,即'$'和'$$',它们允许引用当前指令的地址。'$'计算得到它本身所在源代码行的开始处的地址;所以你可以简单地写这样的代码'jmp $'来表示无限循环。'$$'计算当前段开始处的地址,所以你可以通过($-$$)找出你当前在段内的偏移。
有些时候,对于代码编译后的长度要求达到一个规定的值,需要在代码末尾填充若干个0,这时候,经常会使用到如下的语句:
TIMES n DB 0
这里的n应为一个数值,表示填充的0的个数。
例如,要求目标代码的长度为512字节,此时一个常见的错误是写成:
TIMES 510-$ DB 0
即误认为'$'是一个纯数字,就像 510,所以它们相减的值也是一个纯数字,可以很好地被TIMES 使用。
由于NASM 是一个模块化的汇编器:不同的组成部分被设计为可以很容易的单独重用,所以它们不会交换一些不必要的信息.结果,'BIN'输出格式尽管被'ORG'告知'.text'段应当在 0处开始,但是不会把这条信息传给表达式的求值程序.所以对求值程序来讲,'$'不是一个纯数值:它是一个从一个段基址开始的偏移值.因为'$'和 510'之间的计算结果也不是一个纯数,而是含有一个段基址.含有一个段基址的结果是不能作为参数传递给'TIMES'的.
正确的写法应该如下:
TIMES 510-($-$$) DB 0
在这里,'$'和'$$'是从同一个段基址的偏移,所以它们相减的结果是一个纯数,这句代码会解决上述问题,并产生正确的代码。
PS:下午那个问题已经想明白了,还是概念不清惹的祸。感谢相关同学耐心帮助
PS2:还是写一下吧。下午的那个问题,我在把代码向上翻了N页后,偶然间在定义部分发现了如下内容:
mov ax,SelectorData
mov ds,ax
原来代码开头部分早就定义了数据段,我忘了……
然后呢,DS 数据段(或称数据选择器),这个寄存器的低16 bit连同ESI一同指向的指令将要处理的内存。同时,所有的内存操作指令默认情况下都用它指定操作段(实模式)或内存(作为选择器,在保护模式,这个寄存器可以被装入任意数值,然而在这么做的时候需要小心一些。方法是,首先把数据送给AX,然后再把它从AX传送给DS(当然,也可以通过堆栈来做)。我把两个地址的差值(也就是字符串相对于数据段起始地址的偏移值,这确实是一个纯数值……)传给ESI后,在没有特别指明的情况下,程序就会自动按照[DS:ESI]去读取数据了……唉,基础啊~~概念啊~~咋就忘得这么一干二净了呢……