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

Vst*_*why 2 assembly real-mode x86-16

为什么在下面的代码中我们推送代码段(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.............. 
Run Code Online (Sandbox Code Playgroud)

Pet*_*des 6

暂时设置DS = CS,然后恢复它看起来像是使用CS覆盖前缀的低效替代方法rep movsw.

跨段可以更改源为movswDS:SICS:SI.(ES:DI无法覆盖的目的地).

(更新:原始8086/8088,有一个硬件"错误"/异常:从REP-string指令发生的中断恢复时,IP将指向指令的最后一个前缀,而不是第一个.所以取决于关于编码,cs rep movsw将解码为rep movswcs movsw.请参阅@ MichaelPetch的评论,并https://www.pcjs.org/pubs/pc/reference/intel/8086/了解更多8086勘误和已在以后的x86 CPU中修复的异常.)


这段代码正在执行amemcpy(dst, code_segment, sizeof(code_segment)),其中dstsegment:offset是(BX + 16):0.rep movsw设置DS = BX + 16并设置DI = 0 之前的指令.

然后代码跳转到新位置,ret在推送目标段(ES)和其中的偏移之后使用远.(效率不高:push offset SECONDRELOCATION应该工作得很好.)

显然,这个汇编器不支持的语法像ret far或者retf,所以他们必须装配远远ret声明一个指令proc far的周围ret指令. AAA这个proc是一个非常奇怪的名称,因为aaa它也是一个有效的x86指令助记符(加法后的ASCII调整).

因此,执行继续在SECONDRELOCATION:我们刚刚创建的代码副本中的标签处.


(size+1) / 2舍入到整数个单词,除非大小包装,在这种情况下它复制零字节而不是64k.(不像loop,rep检查计数之前执行一次).

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

无论如何,可能重点是将代码重新定位到HIGHMEM中,使低内存空闲,供不能使用HIMEM的程序使用.

  • 不使用`REP CS:MOVSW`的可能原因是8086/8088处理器异常的b/c.当在操作期间发生中断时,在返回时它将仅通过使用最后一个前缀(在这种情况下是段前缀)继续.结果令人惊讶的是,当中断结束时,`REP`前缀实际上被忽略了.有一个解决方法.这种代码将是一种机制(将其减少为一个前缀).另一个是如果CX仍然非零,则重新启动指令.这需要循环并开始指令,直到CX变为零 (5认同)
  • 做了一些挖掘,似乎有人试图记录这些*特征*(我称之为*异常*)礼貌地避免术语*bug*.它不是正式的,但通过它们看来,它们似乎已经抓住了我们当时关注的许多事情:https://www.pcjs.org/pubs/pc/reference/intel/8086/ (2认同)