컴퓨터 구조
컴퓨터 원리
코드를 작성해보자.
sasm
을 통해 아래의 코드HelloWorld.asm
을 작성해본다.- 컴퓨터는 아래와 같은 영어코드를 읽는게 아니라
010101...
과 같은 포맷인 기계어를 읽으므로 - 어셈블러 혹은 컴파일러를 통해서 변환을 시켜줘야한다.
- 컴퓨터 구조는 크게 CPU, 메모리, 하드디스크 이렇게 3개가 있다.
- 흐름은 다음과 같다.
- 하드디스크에서 프로그램(exe)를 꺼내와서 메모리에 파일 내용을 다 복사해서 시작, 이제 CPU와 메모리사이를 왔다갔다하면서 프로그램을 진행한다.
%include "io64.inc" section .text global CMAIN CMAIN: ;write your code here ; 10 PRINT_STRING msg xor rax, rax ret section .data msg db 'Hello World', 0x00
컴퓨터가 어떻게 데이터를 저장할까
- 비트 : 0과 1 값만 가질수 있는 측정단위
- 바이트 : 여덟개의 비트로 구성된 데이터의 양을 나타내는 단위
- 8bit = 1byte
- 16byte = 2byte = 1 word
- 32bit = 4 byte = 2 word = 1 dword
- 8 byte = 4 word = 1 qword
컴퓨터는 숫자를 어떻게 표현할 까
- 바이트로 따지면
0000 0011
: 3 과 같다.윈도우에서 프로그래머계산기를 사용하면 숫자계산하기편리하다- 그렇다면 음수는?
- 2의 보수
- 최상위 비트는 음의 부호를 나타낸다.
0111 1111
은 가장 최상위 숫자인127
을 의미한다.- 그렇다면 거기서 한차례 증가시킨
1111 1111
은-1
을 의미한다. - 그렇다면 양의 바이트 나열 (
0010 1011
,43
)에서 최상위 비트를1
로 바꾼값1010 1011
은43
에서128
을 뺀 값인-85
가 된다. - 음수로 만들고 싶으면
0010 1011
에서 모든 비트를 뒤집는다. (1101 0100
) 에서1
을 더해준다.1101 0101
는-43
을 의미한다.
- 컴퓨터는 보통 이진수로 숫자를 이해한다. 이진수는 보통 숫자 앞머리에
0b
를 붙인다. - 16진수는
0x
사용0b10010101
을0x
로 표기해보자.1001
0101
을 끊어서생각하자.1001
은9
0101
은5
를 의미하므로0x95
가 된다.
- 컴퓨터 친화적으로 표기하고픈데? 공간이 별로 없다? 그렇다면 16진수를 사용하면 편리하다.
레지스터
- CPU 안에 내장되어있는 엄청 가까운 메모리
- CPU가 중간중간에 연산한걸 어딘가에 임시적으로 저장을 해야한다. 그걸 레지스터가 한다.
- 저장할 수 있는 용량은 한계가 있다.
- 레지스터는 하나가 아니라 여러개 세트로 있다. 보통 자주 사용하는건
EAX
,EBX
,ECX
,EDX
이다. - 얘를 또 어떻게 쪼개서 사용할 건지 지정을 해줘야한다.
- 레지스터의 가장 큰 크기는 기본적으로 64비트이다.
- 하나를 다 사용하면
rax
라고 하고, 절반만(32비트) 사용하면eax
, 또 절반은ax
,ah
al
로 명명된다. 그러니까rbx
의 절반은ebx
,..bl
로 간다는거구만..! -
명령어는 아래와 같다.
; cst 값을 reg1으로 넣어주세요. mov reg1, cst ; reg2에 있는 값을 reg1으로 복사해주세요. mov reg1, reg2
-
아래의 코드를 빌드해보면 오류가 발생한다. 이유가 무엇일까?
%include "io64.inc" section .text global CMAIN CMAIN: ; rax에서 하위 32비트만 사용하겠다. mov eax, 0x1234 ; rbx 64비트를 통으로 사용하겠다. mov rbx, 0x12345678 ; rcx에서 최하단 1바이트만 사용하겠다. mov cl, 0xfffffffff xor rax, rax ret
-
cl
은 최하단 1바이트만 사용하는데 넣는 값이 이를 초과했기 때문이다. -
아래와 같이 코드를 짠다면
rax
에 어떤게 들어갈까?%include "io64.inc" section .text global CMAIN CMAIN: mov rbp, rsp; for correct debugging mov eax, 0x1234 mov rbx, 0x12345678 mov cl, 0xff mov al, 0x00 mov rax, rdx xor rax, rax ret
rax
에는0x1234
였다가mov al, 0x00
이후에 1byte(8bit)를00
으로 밀어버려서0x1200
으로 바뀐다.
문자와 엔디안
-
16진수 두글자가 1 byte 의미
- 변수 선언 및 초기화
- 초기화 된 데이터 양식 :
[변수이름] [크기] [초기값]
크기 db 1 dw 2 dd 4 dq 8
- 초기화 되지 않은 데이터 양식 :
[변수이름] [크기] [개수]
크기 resb 1 resw 2 resd 4 resq 8
a
메모리 내용을 확인해보면0x110x220x220x33...
으로a
는 시작 위치를 가리키키는 것으로 보인다.
section .data msg db 'Hello World', 0x00 a db 0x11 ; 변수설정 b dw 0x2222 c dd 0x33333333 d dq 0x4444444444444444
- 초기화 된 데이터 양식 :
-
메모리에 데이터가 올라갈때, 메모리에는 구분할 수 있도록 주소가 있다.
mov rax, a ; a 라는 바구니의 주소값을 rax에 복사 mov rax, [a] ; a 라는 바구니 안에든 값을 rax 에 복사 mov [a], byte 0x55 ; a 라는 바구니의 내용물에 0x55 복사
- 같은 데이터를 어떻게 분석하냐에따라서 해석 방법이 다르다.
- 아래의 코드에서
msg
의 출력문은Hello World
이지만,a
의 는 아스키 코드가 있는 부분까지는 기호로 나와서""3333DDDDDDDD
나온다.'Hello World'
은 16진수 두글자가 한 문자를 표시하므로 22개의 16진수의 나열이다.msg2 db 0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64 , 0x00
와 같은 의미이다.- 그러면 일단왜
0x11
의 아스키값인만 나온게 아니라 그 이후까지 나온걸까?
0x00
은 아스키 코드로null
값으로 문자열의 끝을 표현한다.msg db 'Hello World', 0x00
가'Hello World'
만 출력하는 이유이다.
-
바이트 단위로 분석하면 아무 의미 없지만, 아스키 코드로 분석하면 의미가 생성되는 매직!
; 출력 결과 : Hello World""3333DDDDDDDD PRINT_STRING msg2 PRINT_STRING a xor rax, rax ret section .data msg db 'Hello World', 0x00 msg2 db 0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64 , 0x00 a db 0x11 ; 변수설정 b dw 0x2222 c dd 0x33333333 d dq 0x4444444444444444
- 리틀 앤디안과 빅 엔디안
- 빅 엔디안 : 메모리에 값을 집어넣은 순서대로 저장된다.
0x12345678
->0x12
,0x34
,0x56
,0x78
- 장점 : 숫자 비교에 유리, 가장 앞머리 숫자부터 비교를 해야하니까 리틀은 맨 끝에서부터 비교, 빅은 바로 첫 바이트부터 비교
- 리틀 엔디안 : 거꾸로 뒤집혀서 저장
0x12345678
->0x78
,0x56
,0x34
,0x12
- 장점 : 캐스팅에 유리하다. (데이터의 일부만 사용), 1 바이트만 추출하고 싶어 ~ 바로 앞 첫 바이트만 꺼내면 된다. 하지만, 빅 엔디안은 끝까지 가서 마지막 바이트를 꺼내야한다.
- 대부분의 데스크 탑은 리틀 엔디안으로 저장이 된다.
- 빅 엔디안 : 메모리에 값을 집어넣은 순서대로 저장된다.
사칙 연산
- 더하기 연산 형식 :
add a, b
으로a = a + b
와 같은 의미a
는 레지스터 또는 메모리b
는 레지스터 또는 메모리 또는 상수- 단
a
,b
모두 메모리는 불가
CMAIN: mov rbp, rsp; for correct debugging GET_DEC 1, al GET_DEC 1, num add al, 1 PRINT_DEC 1, al
- 곱하기 연산 형식 :
mul reg
mul bl
->al * bl
,ax
에 저장-
mul bx
->ax * bx
,dx
(상위 16비트) ,ax
(하위 16 비트)에 저장; 곱하기 연산 mov ax, 0 mov al, 5 mov bl, 8 mul bl PRINT_DEC 2, ax ; 결과값은 40
- 나누기 연산
-
div reg
->ax / bl
,al
은 몫ah
는 나머지mov ax, 100 mov bl, 3 div bl PRTINT 1, al ; 33 NEWLINE mov al, ah PRINT_DEC 1, ah ; 1
-
쉬프트 연산 및 논리 연산
- 산술 시프트의 특징: 최상위비트(부호판단)를 제외한 나머지 비트만 시프트
- 곱셈, 나눗셈 간편함
- 논리 연산
not
and
or
xor
- 동일한 값으로
xor
을 두번하면 자기 자신으로 되돌아오는 특성 (mov rax 0
과xor rax, rax
와 같은 의미)
- 동일한 값으로
라벨
-
분기 : 어떤 숫자(1~100)가 짝수면 1, 홀수면 0을 출력하는 프로그램
CMAIN: mov rbp, rsp; for correct debugging ; 숫자 입력 GET_DEC 2, ax ; 나눗셈 mov bl, 2 div bl mov al, ah ; ah와 1이 같다면 이동 cmp ah, 1 je LABEL_EQUAL ; 아니면 rcx에 1 저장 mov rcx, 1 jmp LABEL_EQUAL_END LABEL_EQUAL: mov rcx, 0 LABEL_EQUAL_END: PRINT_HEX 1, rcx NEWLINE ;PRINT_DEC 1, ah xor rax, rax ret
-
반복
mov ecx, 10 LABEL_LOOP: PRINT_STRING msg NEWLINE dec ecx ; 0과 ecx 비교 cmp ecx, 0 ; 같지 않다면 LABEL_LOOP로 이동 jne LABEL_LOOP
-
연습 문제 : 1에서 100까지의 합을 구하는 프로그램
mov eax, 100 mov ebx, 0 mov ecx, 0 LABEL_LOOP: add ecx, 1 add ebx, ecx cmp ecx, eax jne LABEL_LOOP PRINT_DEC 4, ebx NEWLINE xor rax, rax ret
-
loop [label]
:ecx
에 반복횟수, loop 할때마다 ecx 1 감소, 0이면 빠져나가고 아니면 라벨로 이동
배열과 주소
- 배열 : 동일한 타입의 데이터 묶음
- 배열을 구성하는 각 값을 배열 요소라고함
- 배열의 위치를 가리키는 값을 index라고 함
mov rax, a
:a
의 주소값이rax
에 복사PRINT_HEX 1, [a]
:a
의 값을 출력- 만약에
a db 0x01, 0x02, 0x03, 0x04, 0x05
와같이 배열일때 두번째 원소를 출력하고 싶으면 어떻게 해야할까? :PRINT_HEX 1, [a+1]