본문 바로가기
전공/시스템 프로그래밍

[시스템 프로그래밍 5-1] 어셈블리 기본 데이터 타입

by 뜨거운 개발자 2023. 3. 30.

어셈블리어에서 데이터를 정의하는 방법

기본 데이터 타입

  • 기본적으로 사이즈를 정의하는 형태이다.
  • 실제로 크기가 같아도 다른 용도로 쓰기위해서 각자 용도를 고려해서 자료형을 정의하지만 어셈블리어에서는 오로지 크기의 관점으로만 데이터 타입이 주어진다.
    • C 언어를 예로 들면 float 과 int는 모두 4바이트 또는 int 와 unsigned int 예시
  • 어셈블러는 피연산자의 크기만 평가한다.
    • WORD는 16 비트 unsigned int
    • SWORD 16 bit signed int
    • DWORD (DOUBLE WORD) : 32bit unsigned int
  • 즉 크기만 같다면 뭘 쓰던지 동작에 전혀 상관은 없다.
    • 이런식으로 분류가 되어있긴 하지만 실제 CPU의 관점에서는 사이즈만을 생각하기 때문에 DWORD로 데이터를 만들던 SDWORD로 데이터를 만들던 CPU의 관점에서는 동일하게 본다.
    • DWORD, SDWORD, REAL4 는 32bit 정수에 사용 가능( 같은 결과)

데이터 정의 선언문

  • 선택적 이름을 사용해서 변수를 위한 메모리 저장공간을 따로 설정한다.
  • 내제적 데이터 유형을 기반으로 변수를 생성한다.

문법 : [name] directive initializer [,initializer]..

  • 예시 : count DWORD 12345

1. name(필수 아님) : 변수명이라고 생각

  • 변수의 이름은 식별자 규칙을 준수해야한다.

2. directive(필수)(지시어)

  • 타입은 32비트에서 새로 정의된 BYTE, WORD, DWORD, SBYTE, SWORD 사용 가능하다.
  • 다만 과거 16비트에서는 2글자짜리 지시어를 사용했는데 그것을 레거시 지시어라고 부른다.
  • 이전과 호환성을 지키도록 레거시 데이터 지시문도 사용가능하다.

3. initializer (1개는 필수)

  • 데이터 정의에 0이더라도 하나 이상의 이니셜라이저가 필요하다.
  • 추가적으로 있는 경우 쉼표로 구분한다.
  • int타입에 대해서는 뒤에 나오는 녀석이 정수 상수(리터럴)이거나, 정수의 표현(BYTE또는 WORD) 이어야 한다.
  • 변수를 초기화하지 않은 상태로 사용하려면 ? 기호를 사용해서 이니셜라이저로 사용할 수가 있다.
  • 형식에 상관없이 모든 이니셜라이저는 어셈블러에 의해 바이너리 데이터로 변환된다.
  • 조금 생각해보면 우리가 변수를 선언하는게 사실상 원하는 크기만큼 메모리를 할당해주고 그 메모리의 시작주소를 변수에 집어넣는 것이라고 생각해야한다.

예시 어셈블리 코드 상세 해석

;AddTowSum.asm - chapter 3 example ;주석입니다.
	.386
	.model flat,stdcall
	.stack 4096
	ExitProcess PROTO, dwExitCode:DWORD
	
	.data ;data 세그먼트
	sum DWORD 0 ; 변수명 타입(사이즈) 초기값 - 4바이트 할당됨.
	
	.code ;코드 세크먼트
	main PROC ;main함수가 여기서 시작한다는 뜻
		mov eax, 5 ; cpu안에 있는 eax 레지스터 안에 숫자 5를 넣어라
		add eax, 6  ; 숫자 6과 eax를 더해라
		mov sum,eax ; EAX레지스터에 11이 있고 다시 sum위치로 보냄. 레지스터값을 메모리로 옮김

		INVOKE ExitProcess,0 ;INVOKE 는 함수호출의 개념. 
;운영체제에서 제공하는 함수인데, 프로그램 종료할 때 필요.
;인자로 0을 줬다는 의미이다.
	main ENDP
	END main

1. BYTE 와 SBYTE Data

  • 하나 이상의 부호없는 값 또는 부호있는 값에 대한 공간 할당
  • 각 이니셜라이저는 반드시 8비트에 맞아야 한다.
  • A는 아스키 값으로 65가 들어갈 것이다.
  • 즉 이름은 별수를 둘러싸는 세그먼트의 시작부분을 나타내는 offset이다. (주소의 시작부분을 나타냄)
    • 예를 들어서 value1이 오프셋 0000에 위치하며 1바이트의 저장소를 소비하는 경우 value2는 자동으로 오프셋0001에 위치하게 된다.
  • 레거시를 위해서 DB를 사용할 수도 있는데 signed 로도 가능하고 unsigned로도 사용가능하다.

1-1. 다중 이니셜라이저

list BYTE 10, 20, 30, 40 이렇게 선언을 한 경우 다중 이니셜라이즈가 되는데 이렇게 첫번째 이니셜 라이즈의 오프셋만 참조를 하게 된다. (list는 첫번째 녀석)

  • 따라서 모든 데이터 정의해 레이블이 필요한 것은 아니다.
    • 레이블로 시작한 바이트 배열을 계속하려면 다음줄에 추가 바이트를 정의하면 된다.
  • 단일 데이터 정의 내애서는 이니셜라이저는 서로 다른 형식(radix)을 사용할 수도 있다.
  • 여기서 보면 16진수고 바이너리고, 캐릭터형이고 인트고 등등등 가능하다. →어차피 다 바이너리로 바뀌기 때문에 사용가능하다.

1-2. 문자열 정의

  • 문자열을 정의할 때는 작은 따옴표 또는 큰 따옴표로 묶는다.
  • 널로 끝나는 문자열( C언어에서 대표적으로 사용하는 방식)
    • 가장 일반적인 유형의 문자열은 널로 끝난다.
    • 각줄마다 레이블을 지정할 필요 없이 문자열을 여러줄로 나눌 수 있다.
    • 여기서 16진수 코드 0dh 와 0ah는 CR/LF (줄변환 문자)라고 불린다.
    • 줄 연결문자(\) 는 두개의 소스 코드 줄을 하나의 문으로 연결한다. 다만 줄의 마지막 문자여야한다.

1-3. DUP 연산자

  • 0의 값을 가지는 BYTE를 20개 연달아서 만들겠다는 의미이다.
  • 값이 할당되지 않는 20개의 BYTE만들기
  • 문자열이나 배열에 공간을 할당할 때 유용하다.
  • 초기화되거나 초기화 되지 않은 데이터와 함꼐 사용할 수 있다.
  • STACK이라는 5글자를 4번 반복해서 복사했다. (20바이트)

2. WORD 와 SWORD

  • 2바이트 (16비트 int)
  • 역시 이전버전도 가능
  • 이전에 본 방식과 사용은 같다.
  • 16비트 단어 배열

3. DWORD와 SDWORD

  • 32 bit 정수를 위한 스토리지 할당
  • 재미있는 특징 : DWORD는 다른 변수의 offset을 저장하기 위한 변수로 사용할 수 있다.
    • high level 언어에서 pointer라고 부르는 개념과 유사
    • 변수의 offset을 저장하는 것.
    • 왜 사용할 수 있을까? 우리가 사용하고 있는 프로세서는 32bit 프로세서이기 떄문이다. 따라서 우리는 주소가 32bit 라서 이렇게 사용을 할 수 있는 것이다.
    • 따라서 포인터의 크기는 Address의 크기와 같다.

4, QWORD Data 정의

  • 64 비트(8바이트) 값에 대한 스토리지를 할당

5. 부동 소수점 타입 제공

  • REAL4는 4바이트 단정밀도 부동소수점 변수를 정의한다. (C언어에서 float)
  • REAL8은 8바이트 더블 정밀도 값을 정의한다. (C언어에서 double)
  • REAL10 은 10바이트의 확장 정밀도 값을 정의한다.
  • 각각 하나이상의 실제 상수 초기화자가 필요하다.
  • 역시나 레거시 지원

변수를 추가하는 프로그램 예시코드

; AddVariables.asm - Chapter 3 example

	.386
	.model flat,stdcall
	.stack 4096
	ExitProcess PROTO, dwExitCode:DWORD

 	.data ;데이터 세그먼트 전부 4바이트로 초기화 되어있다.
	firstval DWORD 20002000h
	secondval DWORD 11111111h
	thirdval DWORD 22222222h
	sum DWORD 0

	.code  ;코드 세크먼트
	main PROC
		mov eax,firstval
		add eax,secondval
		add eax,thirdval
		mov sum,eax

		INVOKE ExitProcess,0 ;INVOKE 는 함수호출의 개념. 
;운영체제에서 제공하는 함수인데, 프로그램 종료할 때 필요.
;인자로 0을 줬다는 의미이다.
	main ENDP
	END main
  • x86 의 특징 메모리 끼리 더하는 것을 지원하지 않기 때문에 꼭 레지스터로 가져와서 연산을 진행해야만 한다.

[특징1] 리틀 앤디안

  • X86 프로세서는 리틀앤디안 순서(낮음에서 높음)을 사용하여 메모리에서 데이터를 저장하고 검색한다.
  • LSB 는 데이터에 할당 된 데이터가 첫번째 메모리 주소에 저장된다.
  • 나머지 바이트는 다음 연속 메모리 위치에 저장된다.
  • 예시
  • 빅엔디안(높은 순에서 낮은순)을 사용하는 컴퓨터도 존재한다. (네트워크에서도)

[특징 2] 초기화되지 않은 데이터 선언

  • .DATA? 지시문은 초기화되지 않은 데이터를 선언한다.
  • 초기화되지 않은 큰 데이터 블록을 정의할 때 .DATA? 지시어를 사용하면 컴파일된 프로그램의 크기를 줄일 수 있다.
  • 실제로는 메모리에 할당은 해주지만 메모리에 할당은 해주지 않기 때문에 크기는 훨씬 작다.

만약 이렇게 코드를 짜면 크기가 헐씬 커지고 비효율적이다.

  • 코드와 데이터의 혼합
    • 코드세그먼트와 데이터 세그먼트가 있는데 그것들을 번갈아 가면서 사용해도 된다.

Uploaded by N2T

728x90