컴퓨터 구조
컴퓨터 원리
코드를 작성해보자.
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로 표기해보자.10010101을 끊어서생각하자.1001은90101은5를 의미하므로0x95가 된다.
- 컴퓨터 친화적으로 표기하고픈데? 공간이 별로 없다? 그렇다면 16진수를 사용하면 편리하다.
레지스터
- CPU 안에 내장되어있는 엄청 가까운 메모리
- CPU가 중간중간에 연산한걸 어딘가에 임시적으로 저장을 해야한다. 그걸 레지스터가 한다.
- 저장할 수 있는 용량은 한계가 있다.
- 레지스터는 하나가 아니라 여러개 세트로 있다. 보통 자주 사용하는건
EAX,EBX,ECX,EDX이다. - 얘를 또 어떻게 쪼개서 사용할 건지 지정을 해줘야한다.
- 레지스터의 가장 큰 크기는 기본적으로 64비트이다.
- 하나를 다 사용하면
rax라고 하고, 절반만(32비트) 사용하면eax, 또 절반은ax,ahal로 명명된다. 그러니까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 regmul 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
-
쉬프트 연산 및 논리 연산
- 산술 시프트의 특징: 최상위비트(부호판단)를 제외한 나머지 비트만 시프트
- 곱셈, 나눗셈 간편함
- 논리 연산
notandorxor- 동일한 값으로
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]