게임 개발/언리얼 강의 (클라-서버)
[언리얼 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
연습문제: 어떤숫자 (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
'게임 개발 > 언리얼 강의 (클라-서버)' 카테고리의 다른 글
[언리얼 MMORPG pt1] 포인터 vs 참조 (0) | 2023.10.25 |
---|---|
[언리얼 MMORPG pt1] 포인터 (0) | 2023.10.24 |
[언리얼 MMORPG pt1] 함수 (0) | 2023.10.23 |
[언리얼 MMORPG pt1] 코드의 흐름 제어 (0) | 2023.10.23 |
[언리얼 MMORPG pt1] 데이터 갖고 놀기 (0) | 2023.10.22 |
Contents
소중한 공감 감사합니다