为什么在下面的代码中我们推入代码段(PUSH CS),然后将其弹出到数据段(POP DS)中?
我将这些行明确地指定为line1和line2。请让我知道MOVSW在这里如何运作。
IF HIGHMEMORY
PUSH DS
MOV BX, DS
ADD BX, 10H
MOV ES, BX
PUSH CS. ;line1
POP DS. ;line2
XOR SI, SI
MOV DI, SI
MOV CX, OFFSET SYSSIZE + 1
SHR CX, 1
REP MOVSW. ;line3
POP DS
PUSH ES
MOV AX, OFFSET SECONDRELOCATION
PUSH AX
AAA PROC FAR
RET
AAA ENDP
SECONDRELOCATION:
more code here..............
最佳答案
暂时设置DS = CS,然后恢复它似乎是对rep movsw
使用CS覆盖前缀的一种无效选择。
A segment override can change the source for movsw
从DS:SI
到CS:SI
。 (ES:DI
的目标不能被覆盖)。
(更新:在原始的8086/8088上,存在硬件“错误” /异常:从REP字符串指令期间发生的中断恢复后,IP将指向指令的最后一个前缀,而不是第一个前缀。因此,取决于在编码上,cs rep movsw
可以解码为rep movsw
或cs movsw
。请参阅@MichaelPetch的注释和https://www.pcjs.org/pubs/pc/reference/intel/8086/,以获取更多8086勘误表和更高版本x86 CPU中已修复的异常信息。)
这段代码正在执行memcpy(dst, code_segment, sizeof(code_segment))
,其中dst
segment:offset是(BX + 16):0
。 rep movsw
之前的指令设置DS = BX + 16并设置DI = 0。
然后,在推入目标段(ES)和其中的偏移量后,使用远的ret
将代码跳转到新位置。 (push offset SECONDRELOCATION
可以使用,但只能在186+上使用。不幸的是,此DOS代码需要与8086保持向后兼容。)
显然,该汇编器不支持ret far
或retf
之类的语法,因此他们必须通过在ret
指令周围声明proc far
来汇编远方的ret
指令。 AAA
是该过程的一个非常奇怪的名称,因为aaa
is also a valid x86 instruction mnemonic (ASCII Adjust after Addition)。
因此,执行将在我们刚刚编写的代码的副本中的SECONDRELOCATION:
标签处继续。(size+1) / 2
舍入为整数,除非大小自动换行,在这种情况下,它会复制零字节而不是64k。 (与loop
不同,rep
在执行一次之前检查计数。)
在运行时执行shr
也是愚蠢的,并且可以在汇编时使用mov cx, (offset endcode - startcode + 1) / 2
之类的方法完成。 (您可能无法将offset
结果除以2,但是在组装时可以找到同一部分中两个标签之间的距离。)
无论如何,大概的重点是将代码重新放置到HIGHMEM中,从而使低内存可用于无法使用HIMEM的程序。