포인터 로드 니모닉

lea: near 포인터를 로드

간접 메모리 피연산자의 주소를 로드할 때 lea 가 유일한 방법

MASM 6.1 Programmer's Guide, Ch.3 p.83 — "Stack Variables"

The technique for loading the address of a stack variable is significantly different from the technique for loading near addresses. You may need to put the correct segment value into ES for string operations. The following example illustrates how to load the address of a local (stack) variable to ES:DI:

Task    PROC
        LOCAL   Arg[4]:BYTE
 
        push    ss      ; Since it's stack-based, segment is SS
        pop     es      ; Copy SS to ES
        lea     di, Arg ; Load offset to DI

The local variable in this case actually evaluates to SS:[BP-4]. This is an offset from the stack frame (described in “Passing Arguments on the Stack,” Chapter 7). Since you cannot use the OFFSET operator to get the offset of an indirect memory operand, you must use the LEA (Load Effective Address) instruction.

[[015. 간접 피연산자#mov-bx-offset-table|mov bx, OFFSET table]]에서 OFFSET Operator의 동작은 어셈블 시점에 바이너리에 주소를 그대로 넣는다는 것을 알 고 있다.

  • 즉시 피연산자

따라서 일반 near 변수의 경우 mov reg, OFFSET var과 같이 주소를 로드할 수 있다.

하지만 스택 변수의 경우 또 다른 테크닉이 필요하다.

참고로 “You may need to put the correct segment value into ES for string operations” 라고 설명한이유는 나중에 배울 문자열 니모닉의 목적지가 항상 es:di로 고정 되어있기 때문이다.

위 인용에 나온 코드의 목적은 함수의 콜스택에 4바이트를 확보하고, 이 주소를 es:di에 저장하는 코드다.

MASM의 LOCAL 디렉티브:

  • 프로시저 안에서 스택에 로컬 변수를 선언하는 문법이다.
  • Arg 라는 이름의 4바이트 공간을 스택프레임(BP 기준)으로 확보하는 개념이다.
BP     
BP-1   → Arg[3]
BP-2   → Arg[2]
BP-3   → Arg[1]
BP-4   → Arg[0]  ← LEA가 이 주소를 DI에 넣음

결과적으로 ES:DI = SS:[BP-4]Arg의 전체 주소가 된다.

스택 변수가 이런 테크닉이 필요한 이유는 OFFSET operator간접 메모리 피연산자에 사용할 수 없기 때문이다.

  • OFFSET Arg 는 어샘블이 실패한다. Arg의 주소값이 실행 중에 결정되기 때문이다.

어샘블 해보면

lea di, Arg가 어셈블 결과 LEA DI, WORD PTR [BP-04]가 되었다.

  • 실행 중 BP의 값에 따라 동적으로 최종 주소가 결정된다.

어샘블 시점에 정적으로 주소가 결정되는 변수는 mov가 효율적이다

MASM 6.1 Programmer's Guide, Ch.3 p.83 — "Direct Memory Operands"

To get the address of a direct memory operand, use either the LEA instruction or the MOV instruction with OFFSET. Though both methods have the same effect, the MOV instruction produces smaller and faster code, as shown in this example:

lea si, Msg ; Four byte instruction 
mov si, OFFSET Msg ; Three byte equivalent 

OFFSET 오퍼레이터를 사용해 즉시 메모리 피연산자처럼 주소를 어셈블 시점에 결정할 수 있다면 mov가 효율적이다.

특이 사항

피연산자가 단순하다 src는 메모리 고정, dst는 레지스터 고정이다.

  • 이 때 src 메모리 주소의 오프셋 부분만 저장하기 때문에 dst는 “reg16”가 되어야 한다.

유효 주소란?

세그먼트 + 오프셋 으로 계산한 20비트 주소다.

  • TINY 모델에서 세그먼트는 모두 동일하니 신경 쓸 필요없다.

lds/les: far 포인터를 로드

특이 사항

lea와 다르게 세그먼트의 주소까지 저장한다.

오프셋은 16비트 레지스터에 저장한다.

세그먼트 값은 니모닉마다 목적지가 다르다.

  • lds의 경우 ds
  • les 의 경우 es