새소식

인기 검색어

게임 개발/언리얼 강의 (클라-서버)

[언리얼 MMORPG pt1] 어셈블리 언어 입문

  • -

1. 환경설정 및 Hello World

1. SASM 설치

2. 환경설정에서 build -> x64, nasm 으로 설정

3. 새 프로젝트 만들기

4. 다음과 같이 입력하면 Hello World 를 출력할 수 있다.

%include "io64.inc"

section .text
global CMAIN:
CMAIN:
    ;write your code here
    
    PRINT_STRING msg
    
    xor rax, rax
    ret
    
section .data
    msg db 'Hello World', 0x00

5. save .exe 하면 실행 파일로 저장도 가능하다.

2. 데이터 기초

2.1 컴퓨터는 정수를 어떻게 저장하는가?

비트와 바이트

1바이트 == 8비트

비트로 숫자를 표현할때, 최상위 비트는 부호를 의미하고, 음수를 나타낼때는 2의 보수 개념 사용.

진수 표현

  • 0b00: 2진수
  • 0x00: 16진수

ex) 0b 1001 0101 == 0x95

 

3. 레지스터 기초

컴퓨터 구조
레지스터

%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    
    ; 8 bit = 1 byte
    ; 16 bit = 2 byte = 1 word
    ; 32 bit = 4 byte = 2 word = 1 dword (double-word)
    ; 64 bit = 8 byte = 4 word = 1 qword (quad-word)
    
    mov eax, 0x1234
    mov rbx, 0x12345678
    mov cl, 0xff
    
    mov al, 0x00
    mov rax, rdx
    
    xor rax, rax
    ret
    
section .data

4. 변수와 레지스터

메모리 구조

%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    
    ; 8 bit = 1 byte
    ; 16 bit = 2 byte = 1 word
    ; 32 bit = 4 byte = 2 word = 1 dword (double-word)
    ; 64 bit = 8 byte = 4 word = 1 qword (quad-word)
    
    mov eax, 0x1234
    mov rbx, 0x12345678
    mov cl, 0xff
    
    mov al, 0x00
    mov rax, rdx
    
    ; 메모리 <-> 레지스터
    ;mov rax, a ; a라는 바구니의 주소값을 rax에 복사
    ;mov rax, [a] ; a라는 바구니 안에 있는 값을 rax에다 복사 (8바이트 복사)
    mov al, [a] ; 1바이트 복사
    
    mov [a], byte 0x55
    mov [a], word 0x6666
    mov [a], cl
    
    xor rax, rax
    ret
    
    
    
    ; 변수의 선언 및 사용
    ; 변수는 그냥 데이터를 저장하는 바구니 [ ]
    ; ~처음에 바구니 사용하겠다 선언 (이름과 크기 지정)
    
    ; 초기화 된 데이터
    ; [변수이름] [크기] [초기값]
    ; [크기] db(1) dw(2) dd(4) dq(8)
section .data
    a db 0x11
    b dw 0x2222
    c dd 0x33333333
    d dq 0x4444444444444444
    
    
    ;초기화 되지 않은 데이터
    ; [변수이름] [크기] [개수]
    ; [크기] resb(1) resw(2) resd(4) resq(8)
section .bss
    e resb 10

5. 문자와 엔디안

문자는 ASCII 코드 등으로 결국 숫자, 01 로 저장되는 것.

엔디안

  • 리틀 엔디안: 캐스팅에 유리
  • 빅 엔디안: 숫자비교에 유리

6. 사칙연산

%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    
    GET_DEC 1, al ; 1입력 
    GET_DEC 1, num ; 2입력 
    
    ;PRINT_DEC 1, al
    ;NEWLINE
    ;PRINT_DEC 1, num
    
    ; 더하기 연산
    ; add a, b (a = a + b)
    ; a 는 레지스터 or 메모리
    ; b 는 레지스터 or 메모리 or 상수
    ; - 단! a, b 모두 메모리로 하면 x
    
    add al, 1 ; 레지스터 + 상수
    PRINT_DEC 1, al ; 1+1=2
    NEWLINE
    
    add al, [num]; 레지스터 + 메모리 
    PRINT_DEC 1, al ; 2+2=4
    NEWLINE
    
    mov bl, 3 ; 레지스터 + 레지스터 
    add al, bl ; 4+3=7
    PRINT_DEC 1, al
    NEWLINE
    
    add [num], byte 1 ; 메모리 + 상수
    PRINT_DEC 1, [num] ;2+1=3
    NEWLINE
    
    add [num], al ; 메모리 + 레지스터
    PRINT_DEC 1, [num] ;3+7=10
    NEWLINE
    
    ; 곱하기 연산
    ; mul reg
    ; - mul bl => al * bl
    ; -- 연산 결과를 ax에 저장
    ; - mul bx => ax * bx
    ; -- 연산 결과는 dx (상위 16비트) ax(하위16비트)에 저장
    ; -mul ebx => eax * ebx
    
    ; ex) 5*8 은?
    mov ax, 0
    mov al, 5
    mov bl, 8
    mul bl
    PRINT_DEC 2, ax
    NEWLINE
    
    
    ; 나누기 연산
    ; div reg
    ; -div bl => ax / bl
    ; -- 연산 결과는 al(몫) ah(나머지)
    
    ; ex) 100/3 은? 
    mov ax, 100
    mov bl, 3
    div bl
    PRINT_DEC 1, al
    NEWLINE
    mov al, ah
    PRINT_DEC 1, al ; ah는 바로 출력이 안됨(그냥 규칙이 그렇다고 함)
    
    xor rax, rax
    ret
    
    
    
    ; 변수의 선언 및 사용
    ; 변수는 그냥 데이터를 저장하는 바구니 [ ]
    ; ~처음에 바구니 사용하겠다 선언 (이름과 크기 지정)
    
    ; 초기화 된 데이터
    ; [변수이름] [크기] [초기값]
    ; [크기] db(1) dw(2) dd(4) dq(8)
;section .data

    
    
    ;초기화 되지 않은 데이터
    ; [변수이름] [크기] [개수]
    ; [크기] resb(1) resw(2) resd(4) resq(8)
section .bss
    num resb 10

7. 시프트 연산

  • 시프트 연산은 *2 한것과 같다고 생각하면 됨.
  • 한칸씩 비트 옮기는 거.
  • not and or xor 논리연산. (너무 쉬운거라 스킵)
  • xor 같은값으로 두번 연산하면 다시 원래값으로 돌아옴
  • xor 는 자기자신으로 연산하면 무조건 0됨.
%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    
    ; 쉬프트(shift) 연산, 논리(logical) 연산
    mov eax, 0x12345678
    PRINT_HEX 4, eax
    NEWLINE
    shl eax, 8
    PRINT_HEX 4, eax
    NEWLINE
    shr eax, 8
    PRINT_HEX 4, eax
    NEWLINE
   
    
    xor rax, rax
    ret
    
    
    
    ; 변수의 선언 및 사용
    ; 변수는 그냥 데이터를 저장하는 바구니 [ ]
    ; ~처음에 바구니 사용하겠다 선언 (이름과 크기 지정)
    
    ; 초기화 된 데이터
    ; [변수이름] [크기] [초기값]
    ; [크기] db(1) dw(2) dd(4) dq(8)
;section .data

    
    
    ;초기화 되지 않은 데이터
    ; [변수이름] [크기] [개수]
    ; [크기] resb(1) resw(2) resd(4) resq(8)
section .bss
    num resb 10

8. 분기문

%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    
    ; 분기문 (if)
    ; 특정 조건에 따라서 코드 흐름을 제어하는 것
    
    ; CMP dst, src (dst가 기준)
    ; 비교를 한 결과물은 Flag Register 저장 
    
    ; JMP [label] 시리즈 
    ; JMP : 무조건 jump
    ; JE : JumpEquals 같으면 jump
    ; JNE, JG, JGE, JL, JLE ... 종류 많음 
    
    ; 두 숫자가 같으면 1, 아니면 0을 출력하는 프로그램 
    
    mov rax, 10
    mov rbx, 20
    
    cmp rax, rbx
    
    je LABEL_EQUAL
    ; 점프를 안했다면 같지 않다는 의미
    mov rcx, 0
    jmp LABEL_EQUAL_END
    
LABEL_EQUAL:
    mov rcx, 1
LABEL_EQUAL_END:
    PRINT_HEX 1, rcx
    NEWLINE
    
    xor rax, rax
    ret
    
    
    ; 초기화 된 데이터
    ; [변수이름] [크기] [초기값]
    ; [크기] db(1) dw(2) dd(4) dq(8)
;section .data

    
    
    ;초기화 되지 않은 데이터
    ; [변수이름] [크기] [개수]
    ; [크기] resb(1) resw(2) resd(4) resq(8)
section .bss
    num resb 10

cmp eflag

연습문제: 어떤숫자 (1~100)가 짝수면 1, 홀수면 0을 출력하는 프로그램

%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    
    ; 분기문 (if)
    ; 특정 조건에 따라서 코드 흐름을 제어하는 것
    
    ; CMP dst, src (dst가 기준)
    ; 비교를 한 결과물은 Flag Register 저장 
    
    ; JMP [label] 시리즈 
    ; JMP : 무조건 jump
    ; JE : JumpEquals 같으면 jump
    ; JNE, JG, JGE, JL, JLE ... 종류 많음 
   
    
    ; 연습 문제 : 어떤 숫자(1~100)가 짝수면 1, 홀수면 0을 출력하는 프로그램
    mov ax, 100
    
    ; 나누기 연산
    ; div reg
    ; - div b1 => ax / b1 (al몫 ah나머지)
    
    mov rax, 99
    
    mov bl, 2
    div bl
    cmp ah, 0
    je LABEL_EQUAL
    mov rcx, 0
    jmp LABEL_EQUAL_END
LABEL_EQUAL:
    mov rcx, 1
LABEL_EQUAL_END:
    PRINT_HEX 1, rcx
    NEWLINE
    
  
    xor rax, rax
    ret
    
    
    ; 초기화 된 데이터
    ; [변수이름] [크기] [초기값]
    ; [크기] db(1) dw(2) dd(4) dq(8)
;section .data

    
    
    ;초기화 되지 않은 데이터
    ; [변수이름] [크기] [개수]
    ; [크기] resb(1) resw(2) resd(4) resq(8)
section .bss
    num resb 10

9. 반복문

%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    
    ; 반복문 (while for)
    ; 특정 조건을 만족할때까지 반복해서 실행
    
    mov ecx, 10
    
LABEL_LOOP:
    PRINT_STRING msg
    NEWLINE
    dec ecx ; sub ecx, 1과 동일 (decrease)
    cmp ecx, 0
    jne LABEL_LOOP
    
  
    xor rax, rax
    ret
    
    
    ; 초기화 된 데이터
    ; [변수이름] [크기] [초기값]
    ; [크기] db(1) dw(2) dd(4) dq(8)
section .data
    msg db 'Hello World', 0x00

    
    
    ;초기화 되지 않은 데이터
    ; [변수이름] [크기] [개수]
    ; [크기] resb(1) resw(2) resd(4) resq(8)
section .bss
    num resb 10

연습문제: 1에서 100까지의 합을 구하는 프로그램

%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    
    ; 반복문 (while for)
    ; 특정 조건을 만족할때까지 반복해서 실행
    

    
    ; 연습 문제) 1에서 100까지의 합을 구하는 프로그램
    mov ecx, 0 ; xor ecx, ecx
    mov eax, 0 ; xor eax, eax
LABEL_LOOP:
    inc ecx
    add eax, ecx
    cmp ecx, 100
    jne LABEL_LOOP
    
    PRINT_DEC 4, eax
    NEWLINE
    
    ; loop [라벨] 
    ; - ecx에 반복 횟수 
    ; - loop 할때마다 ecx 1감소, 0이면 빠져나감. 아니면 라벨로 이동 
    mov ecx, 100
    xor ebx, ebx ; mov ebx, 0 ebx: 결과물
LABEL_LOOP_SUM:
    add ebx, ecx
    loop LABEL_LOOP_SUM
    PRINT_DEC 4, ebx
  
    xor rax, rax
    ret
    
    
    ; 초기화 된 데이터
    ; [변수이름] [크기] [초기값]
    ; [크기] db(1) dw(2) dd(4) dq(8)
section .data
    msg db 'Hello World', 0x00

    
    
    ;초기화 되지 않은 데이터
    ; [변수이름] [크기] [개수]
    ; [크기] resb(1) resw(2) resd(4) resq(8)
section .bss
    num resb 10

10. 배열과 주소

%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    
    ; 배열과 주소
    
    ; 배열 : 동일한 타입의 데이터 묶음
    ; - 배열을 구성하는 각 값을 배열 요소(elements)라고 함 
    ; - 배열의 위치를 가리키는 숫자를 인덱스(index)라고 함 
    
    ; 주소
    ; [시작주소 + 인덱스 * 크기] 
    
    mov rax, a ; a는 주소값 
    
    ; 연습문제 : a배열의 모든 데이터 출력해보기
    xor ecx, ecx ;보통 루프에서 i용으로 쓰는게 ecx 
LABEL_LOOP:
    PRINT_HEX 1, [a+ecx]
    NEWLINE
    inc ecx
    cmp ecx, 5
    jne LABEL_LOOP
    
    ; b 데이터 출력 
    xor ecx, ecx
LABEL_LOOP2:
    PRINT_HEX 2, [b+ecx*2]
    NEWLINE
    inc ecx
    cmp ecx, 5
    jne LABEL_LOOP2
    
    xor rax, rax
    ret
    
    ; 초기화 된 데이터
    ; [변수이름] [크기] [초기값]
    ; [크기] db(1) dw(2) dd(4) dq(8)
section .data
    msg db 'Hello World', 0x00
    a db 0x01, 0x02, 0x03, 0x04, 0x05 ; 5 * 1 = 5 byte
    b times 5 dw 1 ; 5 * 2 = 10byte (word 는 2바이트)
    
    
    ;초기화 되지 않은 데이터
    ; [변수이름] [크기] [개수]
    ; [크기] resb(1) resw(2) resd(4) resq(8)
section .bss
    num resb 10

11. 함수 기초

%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    
    ; 함수 (프로시저 procedure 서브루틴 subroutine)
    ;call PRINT_MSG
    
    mov eax, 10
    mov ebx, 15
    call MAX
    PRINT_DEC 4, ecx
    NEWLINE
    
    xor rax, rax
    ret
    
PRINT_MSG:
    PRINT_STRING msg
    NEWLINE
    ret
    
MAX:
    cmp eax, ebx
    jg L1
    mov ecx, ebx
    jmp L2
L1:
    mov ecx, eax
L2:
    ret
    
    
    ; 스택(stack) 이라는 메모리 영역을 사용
    ; 함수가 사용하는 일종의 메모장 
    ; - 매개 변수 전달
    ; - 들어갈 주소 관리
    ; 인셉션과 비슷. 꿈속의 꿈. 함수속에서 함수 또 호출~
    ; - 꿈이 유요한 동안에는 그 꿈을 유지시켜야 함 (유효 범위의 개념이 있다.)
    ; - 꿈이 끝나면 그 꿈을 부셔버려도 됨 (정리의 개념이 있다)
    ; - 꿈에서도 또 꿈을 꿀 수 있다는 것을 고려해야 함 (유동적으로 유효 범위가 확장 가능)
    
    ; 초기화 된 데이터
    ; [변수이름] [크기] [초기값]
    ; [크기] db(1) dw(2) dd(4) dq(8)
section .data
    msg db 'Hello World', 0x00
    a db 0x01, 0x02, 0x03, 0x04, 0x05 ; 5 * 1 = 5 byte
    b times 5 dw 1 ; 5 * 2 = 10byte (word 는 2바이트)
    
    
    ;초기화 되지 않은 데이터
    ; [변수이름] [크기] [개수]
    ; [크기] resb(1) resw(2) resd(4) resq(8)
section .bss
    num resb 10

12. 스택 메모리

메모리 구조
스택 프레임

%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp; for correct debugging
    
    ; 스택 메모리, 스택 프레임 
    
    ; 레제스터는 다양한 용도로 사용
    ; - a b c d 범용레지스터
    ; - 포인터 레지스터 (포인터 = 위치를 가리키는~) 
    ; -- ip (Instruction Pointer) : 다음 수행 명령어의 위치
    ; -- sp (Stack Pointer) : 현재 스택 top 위치 (일종의 cursor)
    ; -- bp (Base Pointer) : 스택 상대주소 계산용 
    
    push rax
    push rbx
    push 8
    push 5
    call MAX
    PRINT_DEC 8, rax
    NEWLINE
    add rsp, 16 ; push 한 만큼 pop 해 줘야 함. 팝팝 해도되고 16만큼 sp이동해도 됨 
    pop rbx
    pop rax
    
    xor rax, rax
    ret
    
MAX:
    push rbp
    mov rbp, rsp
    
    mov rax, [rbp+16]
    mov rbx, [rbp+24]
    cmp rax, rbx
    jg L1
    mov rax, rbx
L1:
    pop rbp
    ret
    
    pop rbp
    ret
   

    ; 스택(stack) 이라는 메모리 영역을 사용
    ; 함수가 사용하는 일종의 메모장 
    ; - 매개 변수 전달
    ; - 들어갈 주소 관리
    ; 인셉션과 비슷. 꿈속의 꿈. 함수속에서 함수 또 호출~
    ; - 꿈이 유요한 동안에는 그 꿈을 유지시켜야 함 (유효 범위의 개념이 있다.)
    ; - 꿈이 끝나면 그 꿈을 부셔버려도 됨 (정리의 개념이 있다)
    ; - 꿈에서도 또 꿈을 꿀 수 있다는 것을 고려해야 함 (유동적으로 유효 범위가 확장 가능)
    
    ; 초기화 된 데이터
    ; [변수이름] [크기] [초기값]
    ; [크기] db(1) dw(2) dd(4) dq(8)
section .data
    msg db 'Hello World', 0x00
    a db 0x01, 0x02, 0x03, 0x04, 0x05 ; 5 * 1 = 5 byte
    b times 5 dw 1 ; 5 * 2 = 10byte (word 는 2바이트)
    
    
    ;초기화 되지 않은 데이터
    ; [변수이름] [크기] [개수]
    ; [크기] resb(1) resw(2) resd(4) resq(8)
section .bss
    num resb 10
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.