요약

변수를 만든다는 것은 메모리에 칸을 잡아 두고 이름표를 붙이는 일이다. 한 줄에 이름, 칸의 크기, 처음 넣을 값을 적으면 어셈블러가 그 값을 바이트로 바꿔 적은 순서대로 메모리에 놓는다. 정해지는 것은 칸의 크기뿐이다. 그 안의 바이트를 수로 읽을지 글자로 읽을지는 나중에 그 칸을 쓰는 명령이 정하므로, 문자열 끝 표시를 빼먹어도 어셈블러는 알려 주지 않는다.

예시로 보기

레지스터는 여덟 개 남짓이다. 값이 그보다 많거나 오래 두어야 하면 메모리에 둘 곳이 필요하다. 그런데 메모리는 번호(주소)만 붙은 바이트의 긴 줄이다. “404000번 칸의 값”이라고 쓰는 대신 이름을 붙이고, 그 칸이 몇 바이트짜리인지 정해 두는 문장이 데이터 정의문이다.

value1 BYTE 10

value1이 이름, BYTE가 칸의 크기(1바이트), 10이 처음 넣을 값이다. 값을 여러 개 적으면 그 순서대로 이어서 놓인다.

list4 BYTE 0Ah,20h,'A',22h
주소(list4 기준)   +0    +1    +2    +3
메모리 바이트      0A    20    41    22

이름 list4는 첫 칸(+0)의 주소다. 'A'는 그 글자의 ASCII 코드 41h가 되어 들어간다. 10진수로 적든 16진수로 적든 글자로 적든 메모리에 들어가는 것은 모두 2진 바이트다. list3 BYTE ?,32,41h,00100010b의 뒤 세 값도 20h, 41h, 22h로 list4의 뒤 세 칸과 똑같다.

검증: 이 문서의 바이트 배치와 바이트 수(list2 12, str1 16, greeting 62, menu 137, var3 20, var4 5), AddSub2의 30000h, 카드 C1·C3. 파이썬 흉내로 확인 — 12_asm-data-definition_verify.py

정의

데이터 정의문은 변수 하나가 쓸 메모리를 잡는다. 이름은 붙여도 되고 안 붙여도 된다.

[이름] 지시어 초깃값 [,초깃값] ...

지시어가 칸 하나의 크기를 정한다. 어셈블러에 들어 있는 자료형은 다음과 같다.

지시어 칸 하나의 크기 담는 값
BYTE / SBYTE 1바이트(8비트) 부호 없는 정수 0~255 / 부호 있는 정수 −128~127
WORD / SWORD 2바이트(16비트) 부호 없는 정수 0~65,535 / 부호 있는 정수 −32,768~32,767
DWORD / SDWORD 4바이트(32비트) 부호 없는 정수 / 부호 있는 정수(−2,147,483,648부터)
QWORD 8바이트(64비트) 정수
TBYTE 10바이트(80비트) 정수
REAL4 / REAL8 / REAL10 4 / 8 / 10바이트 IEEE 실수(짧은, 긴, 확장)

앞에 S가 붙은 것은 부호 있는(signed) 수라는 뜻이다. 칸의 크기는 같다.

초깃값 자리에는 네 가지가 올 수 있다.

  • 값 하나. value3 BYTE 255. 글자 상수 'A'도 값 하나다.
  • 물음표. value6 BYTE ?. 칸만 잡고 값은 정하지 않는다.
  • 쉼표로 이은 여러 값. list1 BYTE 10,20,30,40. 4바이트를 잡는다. 이름은 첫 값의 자리만 가리킨다. 다음 줄에 이름 없이 BYTE 50,60,70,80을 쓰면 바로 뒤에 이어서 놓인다.
  • 되풀이. 개수 DUP (값). 같은 값을 개수만큼 놓는다. 개수와 값은 상수여야 한다.

예제

문자열. 문자열은 글자(바이트)의 배열이다. 따옴표로 묶어 쓰면 글자마다 한 바이트가 된다. 끝에 0을 붙여 “여기서 끝”을 표시하는 일이 많다(널 종료).

str1 BYTE "Enter your name",0        ; 글자 15개 + 0 = 16바이트
str3 BYTE 'A','E','I','O','U'        ; 5바이트, 끝 표시 없음
greeting BYTE "Welcome to the Encryption Demo program "
         BYTE "created by Kip Irvine.",0

긴 문자열 하나를 여러 줄에 쓰려면 줄 끝마다 쉼표를 둔다. 줄을 바꾸는 글자는 두 바이트 0Dh(맨 앞으로, carriage return), 0Ah(다음 줄로, line feed)를 이어 쓴다.

str1 BYTE "Enter your name:    ",0Dh,0Ah
     BYTE "Enter your address: ",0
newLine BYTE 0Dh,0Ah,0

DUP.

var1 BYTE 20 DUP(0)          ; 20바이트, 모두 0
var2 BYTE 20 DUP(?)          ; 20바이트, 값은 정하지 않음
var3 BYTE 4 DUP("STACK")     ; 20바이트: "STACKSTACKSTACKSTACK"
var4 BYTE 10,3 DUP(0),20     ; 5바이트: 0A 00 00 00 14

더 큰 칸.

word1  WORD  65535           ; 부호 없는 가장 큰 값
word2  SWORD -32768          ; 부호 있는 가장 작은 값
myList WORD  1,2,3,4,5       ; 워드 5개 = 10바이트
array  WORD  5 DUP(?)        ; 값을 정하지 않은 워드 5개
val1   DWORD 12345678h
val3   DWORD 20 DUP(?)       ; 더블워드 20개 = 80바이트
val4   SDWORD -3,-2,-1,0,1

2바이트 이상인 칸은 바이트가 놓이는 순서가 따로 정해져 있다: 리틀 엔디언.

변수를 쓰는 프로그램. 변수는 .data 아래에 정의하고 .code의 명령에서 이름으로 쓴다.

.data
val1     DWORD 10000h
val2     DWORD 40000h
val3     DWORD 20000h
finalVal DWORD ?
.code
main PROC
    mov eax,val1         ; 10000h로 시작
    add eax,val2         ; 40000h를 더한다
    sub eax,val3         ; 20000h를 뺀다
    mov finalVal,eax     ; 결과 30000h를 저장

활용

  • 프로그램이 쓰는 문자열은 데이터 세그먼트의 한곳에 모아 정의한다.
  • SBYTE로 정의한 변수는 Microsoft 디버거가 부호 붙은 10진수로 보여 준다. 메모리에 들어간 바이트는 BYTE로 정의했을 때와 같다. −1과 255는 둘 다 FFh다1.
  • 흔한 실수: 문자열 끝의 0을 빼먹는다. 문자열을 찍는 코드는 0을 만날 때까지 읽으므로, 0이 없으면 바로 뒤에 놓인 다른 변수까지 읽는다1.
  • 흔한 실수: var4 BYTE 10,3 DUP(0),20을 “0이 3개”가 아니라 “3이 들어간다”로 읽는다. DUP 앞의 수는 개수, 괄호 안이 값이다.

연결

확인 문제

C1 buf BYTE 3,2 DUP(0FFh),"OK",0은 몇 바이트를 잡는가? 메모리에 놓이는 바이트를 순서대로 16진수로 쓰라.

답: 6바이트. 03 FF FF 4F 4B 00.
이유: 값 3이 1바이트, 2 DUP(0FFh)가 FFh 두 바이트, "OK"가 글자 둘(O = 4Fh, K = 4Bh), 끝의 0이 1바이트다. 2 DUP의 2를 값으로 읽으면 03 02 FF …라는 오답이 나온다.

C2 a SBYTE -1과 b BYTE 255는 메모리에 서로 다른 바이트를 남기는가? 그렇다면, 또는 아니라면 SBYTE와 BYTE를 나눠 쓰는 까닭은 무엇인가?

답: 같은 바이트 FFh를 남긴다. 지시어의 부호 표시는 메모리에 적히지 않는다. 나눠 쓰는 까닭은 읽는 사람과 도구에게 뜻을 알려 주기 위해서다. 디버거는 SBYTE 변수를 부호 붙은 10진수(−1)로 보여 준다.
이유: 칸에는 비트만 들어간다. FFh를 255로 볼지 −1로 볼지는 그 값을 쓰는 명령과 프로그래머가 정한다.

C3 다음 세 정의가 잡는 메모리는 모두 몇 바이트인가? w WORD 3 DUP(0) / dd DWORD 1,2 / s BYTE "Hi",0Dh,0Ah,0

답: 19바이트. 워드 3개 6바이트, 더블워드 2개 8바이트, 글자 2개와 줄바꿈 2바이트와 끝의 0으로 5바이트.
이유: 바이트 수 = 값의 개수 × 칸 하나의 크기다. 문자열은 글자 수만큼 값이 있는 것으로 센다.

  1. 바이트 배치는 파이썬 흉내로 확인했다. 실제 어셈블러로 돌린 결과가 아니다. ↩ ↩2