016. 주소지정 모드하고의 차이

주소지정 모드와의 차이

TITLE Main
 
  
 
.DOSSEG
 
.8086
 
.NO87
 
.MODEL TINY
 
  
 
.DATA
 
addy DW exit
 
  
 
.CODE
 
.STARTUP
 
mov ax, addy
 
mov bx, OFFSET addy
 
jmp exit
 
jmp addy
 
jmp ax
 
jmp WORD PTR [bx]
 
  
 
exit:
 
; 프로그램 종료
 
mov ah, 4Ch
 
xor al, al ; 리턴값 0
 
int 21h
 
END

exit label 그리고 .DATA addy DW exit

exitStatements의 구성 중 name 에 속한다. 어셈블 시점에 이름 대신 오프셋으로 바뀌어 바이너리에 박힌다.

Programmer's Guide p.21 — Statements

[[name:]] [[operation]] [[operands]] [[;comment]]

name: Labels the statement, so that instructions elsewhere in the program can refer to the statement by name. The name field can label a variable, type, segment, or code location.

어셈블해보면 .exit name 의 오프셋은 0110이다.

  • jmp exit 의 바이너리 값이 EB08인데 Code View에서 JMP 0110이라고 보여준다.

addy 변수의 값을 확인해보자. addy가 사용된 Statment 를 확인해보자

가장 직관적인 Statment mov bx, OFFSET addy를 확인해보면 addy의 오프셋은 0116이다.

d cs:0x0116 로 덤프했다.

  • 0116오프셋의 값은 10
  • 0117오프셋의 값은 01

리틀엔디언으로 메모리에 저장하기 때문에 원래의 값은 0110 2바이트다. exit name의 오프셋과 동일하다.

addy DB exit 또는 addy DD exit하면?

addy DB exit의 경우 “error A2071: initializer magnitude too large for specified size” 오류가 발생해 어셈블 실패한다.

  • “initializer magnitude”가 exit name 오프셋의 크기
  • “specified size”가 DB
  • 각각의 크기를 어셈블 시점에 알 수 있다.

addy DD exit의 경우 “error A2118: cannot have segment address references with TINY model” 오류가 발생해 어셈블 실패한다.

참고

DD에 코드 레이블을 넣으면 segment:offset 쌍(far pointer)을 저장하려 하는데, TINY 모델은 세그먼트가 하나뿐이라 세그먼트 주소 참조 자체가 금지된다. (error A2118).

mov ax, addy

Code View에서 MOV AX,WORD PTR [0116]라고 보여준다.

addy직접 메모리 피연산자로 사용되었다. 이를 어떻게 알 수 있을까?

  • 코드 뷰의 []는 메모리의 주소의 값을 사용해라는 말이다. 따라서 0x0116 주소의 값을 WORD로 읽어서 AX에 저장한다.
    • 어셈블 도중 주소값이 바이너리에 1601 으로 박힌 것에서도 직접 메모리 피연산자임을 확인할 수 있다.
  • 직접 메모리 피연산자에서 “주소를 나타내는 상수나 심볼을 피연산자로 사용 가능”이라고 했다. addy라는 변수를 사용하는 것이 이 케이스에 해당한다.
    • 소거법으로 생각해보면 레지스터 피연산자는 아니고 간접 피연산자도 아니고(4개 레지스터에 해당하지 않음) 즉시 피연산자도 아니다. ([[015. 간접 피연산자#mov-bx-offset-table|mov bx, OFFSET table]] 참고)

실행하면 “AX”의 값은 0110으로 바뀐다.

0116주소에서 2바이트를 읽은 결과를 대입한 것이다.

  • 위의 d cs:0x0116 로 덤프 결과 참고

mov bx, OFFSET addy

[[015. 간접 피연산자#mov-bx-offset-table|mov bx, OFFSET table]]에서 봤듯이 “OFFSET Operator”가 변수에 붙으면 즉시 피연산자로 취급한다.

  • 어셈블 시점에 주소가 바이너리에 박힌다.
    • BB1601
    • Code View[]을 빼고 0116으로 보여준다.
  • 주소에 역참조하지 않고 주소 자체를 값으로 사용한다.

실행 결과 “BX”에 주소 자체가 값(0116)으로 저장되었다.

  • 레지스터에 저장하니 리틀 엔디언 적용되지 않았죠?!

jmp exit

위에서 봤듯이 name 은 어셈블 시점에 오프셋으로 바이너리에 저장된다.

강사님은 직접 메모리 피연산자라고 설명한다.

jmp addy

우선 이 동작이 name 처럼 바이너리에 addy 주소가 박혀서 점프한다고 생각해보자. 데이터 섹션에 점프를..? 이상하다.

  • 따라서 즉시 피연산자는 아니다.

그래서 addy 변수에 어떤 주소값이 있고 그 주소로 점프한다고 직관적으로 이해할 수 있다.

  • 대신에 addy 변수에 들어있는 주소값은 동적으로 바뀔 수 있다.

어셈블 결과 Code ViewJMP WORD PTR [0116] 이라고 보여준다.

  • addy의 주소 0116(메모리)에서 어떤 값을 2바이트로 읽는다.

이번 경우도 addy직접 메모리 피연산자로 사용되었다. 어셈블 시점에 이 변수의 주소는 바이너리에 박히지만 실행 중 역참조가 발생한다.

jmp ax

위의 [[#mov-ax-addy|mov ax, addy]]의 결과를 확인해보자. “ax”에는 addy의 값 즉 exit의 주소 0110이 들어간다.

따라서 jmp exit과 동일하게 동작한다.

jmp WORD PTR [bx]

드디어 간접 피연산자가 나왔다.

조건에 만족한다.

  • bx 사용
  • []사용

Code View는 다음과 같이 보여준다.

  • JMP WORD PTR [BX]
  • 실행 중에 bx의 값은 OFFSET addy로 결정된다. addy 변수의 주소다.
  • 이 주소에 역참조해서 2바이트로 읽는다. 이 값은 exit name의 주소 0110이다.
    • addy DW exit로 변수에 저장된 값
  • 0110으로 점프한다.