在REP MOVSW之前按PUSH CS / POP DS的目的是什么?

时间:2018-12-04 02:14:12

标签: assembly x86-16 real-mode

为什么在下面的代码中我们推入代码段(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.............. 

2 个答案:

答案 0 :(得分:5)

暂时设置DS = CS,然后恢复它似乎是对rep movsw使用CS覆盖前缀的一种无效选择。

A segment override can change the source for movswDS:SICS:SI。 (无法覆盖ES:DI的目的地)。

(更新:在原始的8086/8088上,存在硬件“错误” /异常:在从REP字符串指令期间发生的中断恢复后,IP将指向 last 前缀指令,而不是第一条指令。因此,根据编码,cs rep movsw可以解码为rep movswcs 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):0rep movsw之前的指令设置DS = BX + 16并设置DI = 0。

然后,代码在推入目标段(ES)和其中的偏移量之后,使用很远的ret跳转到新位置。 (效率低下:push offset SECONDRELOCATION应该可以正常工作。)

显然,此汇编器不支持ret farretf之类的语法,因此他们必须通过在{{1周围声明一个ret来汇编一段远距离的proc far指令。 }}指令。 ret是该过程的一个非常奇怪的名称,因为aaa is also a valid x86 instruction mnemonic (ASCII Adjust after Addition)

因此,执行将继续在我们刚刚编写的代码副本中的AAA标签处。


SECONDRELOCATION:会四舍五入为整数,除非大小自动换行,否则它将复制零字节而不是64k。 (与(size+1) / 2不同,loop在执行一次 之前检查计数。)

在运行时执行rep也是很愚蠢的,并且可以在汇编时使用shr之类的方法来完成。 (您可能无法将mov cx, (offset endcode - startcode + 1) / 2的结果除以2,但是可以在汇编时找到同一节中两个标签之间的距离。)

无论如何,可能的关键是将代码重新放置到HIGHMEM中,从而使低内存可供不使用HIMEM的程序使用。

答案 1 :(得分:0)

序列push cspop ds只是一种将数据段设置为与代码段相同的值的方法。

这类似于使用push axpop bx而不是mov bx, ax,除了它使用内存和可能对某些标志有不同的影响之外,当我的意图只是提供示例时,我不会打扰我:-)

您执行此操作的一个原因可以追溯到x86分段体系结构的早期(相对于更现代的选择器而言),这种情况如今已很少使用。 x86具有各种内存模型,如微型,小型,紧凑型,中型,大型和大型。

这些基本上是您可以使用的代码和数据段的大小和数量的变化,从内存来看,微小的含义是您有一个一个段,既包含代码又包含数据。

因此csds应该设置为相同的值,以便所有指令默认在该段上进行操作。

在特定情况下,您要保存ds,将其设置为与cs相同的值,然后将其还原。有关原因的更可能解释,请参见下文。


关于movsw的工作原理,它只是简单地从ds:si的内存中复制一个单词值到地址es:di,然后更新指针(递增或递减,具体取决于设置方向标志)。

rep前缀会循环执行,将cx递减,直到达到零为止。

因此,它只是大容量存储副本。


现在,由于repsw的来源是根据ds段指定的,因此您看到push/pop暂时设置ds的真正原因就变成了清楚-这是因为数据源显然位于代码段中。