1

Why are we storing SECONDRELOCATION value in AX and then why are we pushing AX in stack?

I am going through basic understanding of MSDOS programming. I am unable to understand how and why control is being transferred between three OFFSETS (at line4, line43, and line53). These lines are explicitly indicated by me as comments.

    CLI
    MOV     AX,CS
    MOV     SS,AX
    MOV     SP,OFFSET LOCSTACK  ;line4

    ASSUME  SS:SYSINITSEG

    IF      NOT ALTVECT
    STI                             ; Leave INTs disabled 
                                    ;for ALTVECT
    ENDIF
    LOCSTACK LABEL BYTE

    CALL    MSDOS
    MOV     WORD PTR [DOSINFO+2],ES ; SAVE POINTER TO DOS 
                                    ;INFO
    MOV     WORD PTR [DOSINFO],DI

    IF      NOT IBM
    IF      NOT IBMJAPVER
    CALL    RE_INIT                 ; Re-call the BIOS
    ENDIF
    ENDIF

    STI
    CLD

    IF      HIGHMEM
    PUSH    DS
    MOV     BX,DS
    ADD     BX,10H
    MOV     ES,BX
    PUSH    CS
    POP     DS
    XOR     SI,SI
    MOV     DI,SI
    MOV     CX,OFFSET SYSSIZE + 1
    SHR     CX,1                    ; Divide by 2 to get 
                                    ;words
    REP     MOVSW
    POP     DS
    PUSH    ES
    MOV     AX,OFFSET SECONDRELOC ;line43 (why we are storing offset value 
                                  ;of SECONDRELOC in AX, if we are moving 
                                  ;there already ofter RE_INIT PROC one more 
                                  ;point is that why we are PUSHing AX value 
                                  ;in stack)
    PUSH    AX  ;<-------
    RE_INIT PROC    FAR
    ....some code here..... 
    RET
    RE_INIT ENDP

    SECONDRELOC:
    MOV     AX,CS
    CLI
    MOV     SS,AX
    MOV     SP,OFFSET LOCSTACK    ;line53
    STI
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
Vstbutwhy
  • 73
  • 1
  • 5
  • Loading `SP` is not a control transfer. The code copies itself elsewhere and `RE_INIT` jumps there using a `push`+`ret` trick. – Jester Dec 19 '18 at 12:17
  • hi jester, i am very much agreed but, after OFFSET SECONDRELOC, i am pushing (or loading SP) with SECONDRELOC (in PUSH AX) but how can we RET from there if procedure RE_INIT could only be called with CALL keyword. in other words after after PUSH AX the function will bypass the proc RE_INIT and will go to the label SECONDRELOC – Vstbutwhy Dec 19 '18 at 12:55
  • i mean to say that after PUSH AX, the control will go to SECONDRELOC directly, by bypassing the RE_INIT PROC then how can we get RET (i.e. return). – Vstbutwhy Dec 19 '18 at 13:04
  • because we can call a procedure only by using CALL keyword. – Vstbutwhy Dec 19 '18 at 13:06
  • `RET` just removes an address from the stack and jumps there. It's basically equivalent to `POP IP` or `POP CS:IP`. `CALL` places a return address on the stack, but the CPU doesn't care. It's perfectly happy to remove an address that you pushed directly. – Jester Dec 19 '18 at 13:13
  • 1
    For future readers, another question about the rep movsw + `ret far` was asked (by the same user) recently: [What's the purpose of PUSH CS / POP DS before a REP MOVSW?](https://stackoverflow.com/a/53604851) Answers there explain some of what it does. – Peter Cordes Dec 19 '18 at 14:52
  • Hi, I am still unable to understand that why we are storing offset value of SECONDRELOCATION in AX and then why we are pushing AX value in stack – Vstbutwhy Dec 25 '18 at 03:31
  • Hi, I am satisfied, still a little question. That after MOV OFFSET, SECONDRELOC. The control will continue from the next line (PUSH AX, in this case) or it will go to the next line after the label SECONDRELOC (MOV AX, CS, in this case) – Vstbutwhy Dec 25 '18 at 07:21

1 Answers1

1

It's using push / push / ret far as a jmp to an absolute offset within a variable destination segment. It pops a new CS:IP from the stack, in this case ES:SECONDRELOC.

Actually, they want to fall into the RE_INIT function with a "fake" CS:IP return address.

x86 has a jmp ptr16:16 that can jump to an absolute seg:off, but that only works with a hard-coded segment as well as offset. So the only way to make a far jump (setting a new CS as well as IP) with segment and/or offset a runtime variable is to have both in memory, for either a jmp [m16:16] or on the stack for a ret far to pop them both.

To jump without leaving stuff on the stack and without reserving any static data, far ret is the only good option. (There's also the bad option of self-modifying code to modify the segment part of a jmp 0000:SECONDRELOC before it executes, but that's probably worse for code-size.)

They mov the value to AX first, instead of using push OFFSET SECONDRELOC directly, because push imm8 and push imm16 were only added in 186. (See the appendix of an old version of the NASM manual: https://pushbx.org/ecm/doc/insref.htm#insPUSH)


This is the same as in the code you showed in What's the purpose of PUSH CS / POP DS before a REP MOVSW?, where I already described how the code pushes a seg:off and then uses a far ret to jump to the new CS:IP.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847