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]가 되었다.
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”가 되어야 한다.