지난 포스팅에는 어셈블리어가 무엇이고, 레지스터가 어떤 방식으로 동작하는지 간단하게 가지고 놀아봤다.
이번 포스팅에서는 메모리와 레지스터를 핑퐁하는 전반적인 얘기를 다뤄볼까 한다.
Memory(메모리)
우리가 어떤 실행 파일을 만들고 난 후 실행하게 되면 그 파일이 메모리에 올라가게된다. 메모리에 올라갈 때는 여러가지 정보들이 함께 올라가는데 우리가 지정한 값들은 data 영역에 속하게 된다.
메모리에 무언가를 가장 쉽게 올리는 방법은 바로 data와 bss를 사용하는 것이다.
바로 어셈블리어를 통해 알아보자.
변수라는 것은 그냥 데이터를 저장하는데 쓰이는 바구니라고 생각하면 된다.
data 변수를 선언하고 사용하기 위해서는 먼저 바구니를 사용하겠다고 알려주어야한다. (이름과 크기를 지정)
data 변수는 선언을 할 때 [이름] [크기] [값]의 형식을 따르는데 여기서 크기는 우리 마음대로 지정할 수 있고 특정한 키워드가 정해져 있다.
db = 1 byte, dw = 2 byte, dd = 4 byte, dq = 8 byte 이다. 이것의 이름은 지난 포스팅을 보면 유추할 수 있다.
data는 초기화가 된 변수를 뜻한다면 bss는 초기화가 되지 않은 변수를 뜻한다. 그렇기 때문에 사용하는데 있어서 명령어가 다르다. [이름] [크기] [개수] 로 이루어져 있으며 여기서 크기도 특정한 키워드가 정해져 있다.
resb = 1 byte, resw = 2 byte, resd = 4 byte, resq = 8 byte 이다.
자 그럼 여기서 의문이 하나 들 수 있다. 왜 data와 bss를 굳이 굳이 나눠났을까?? 초기화가 되지 않았던 됐던 전부다 data 영역에 저장한 후 메모리에 올리면 되는것 아닌가?? 라고 생각할 수 있다.
실행 파일은 data의 모든 값을 알고 저장해서 그 영역을 만들게 된다. bss는 맨 처음 전부 모든 값을 0으로 초기화 하게 되는데 그렇기 때문에 bss 영역의 크기만 알고 있으면 된다.
즉, 실행 파일의 크기를 줄일 수 있다. data 값이 커지면 파일 크기가 커지기 때문에 메모리 비용이 올라가게 되는것이다.
요즘 컴퓨터들은 사양이 매우 좋지만 20년 전에는 메모리 비용을 아끼기위해 엄청난 노력을 한 것이다.
메모리와 레지스터 실습
먼저 우리가 위에서 data와 bss를 설정해 주었다. 이 값이 제대로 메모리에 올라갔을까 확인해보자.
프로그램을 실행한 후 메모리 영역을 확인해보면 제대로 값이 지정된 것을 확인할 수 있다.
메모리의 값을 레지스터에 올리기 위해선 어떻게 해야할까? 이전 포스팅에서 봤던 mov와 연관이 있을 것이다.
이 명령어를 통해 위에서 설정한 a를 A 레지스터에 옮겨보자.
rax에 0x11이 아닌 0x403010이라는 값이 저장되어있다. 분명 우리는 a라는 값을 A레지스터에 저장하려고 했는데 그 동작이 제대로 되지 않은것인가??
그건 아니다. 여기서 0x403010은 주소를 뜻한다. 여기서 주소라는게 생소할 수 있다. 사실 메모리는 전부 주소로 이루어져있고 그 주소에 값을 저장하는 형식이다. 이 개념은 c++의 포인터를 배울 때 매우 중요한 개념이기에 꼭 기억하자.
그렇다고 하면 403010이라는 주소에는 어떤 값이 있는지 확인해보자.
뒤의 Address 토글을 활성화 해주자 우리가 원하는 값이 나오는걸 확인할 수 있다.
이로써 mov rax, a는 a라는 바구니의 주소를 A레지스터에 저장해라 라는 의미인 것을 확인할 수 있다.
그렇다고 하면 값을 꺼내올 수 없는것인가? 아니다. 값을 꺼내오기 위해서는 명령어를 수정해야 한다.
mov rax, [a]로 수정해주면 a라는 바구니의 값을 A레지스터에 저장한다는 의미이다.
그렇다면 그 반대의 경우도 가능하다는 것을 당연히 생각할 수 있을것이다.
글자가 안보인다면 화이트모드로 변경
%include "io64.inc"
section .text
global CMAIN
CMAIN:
mov rbp, rsp; for correct debugging
;write your code here
mov rax, a ;a 라는 바구니의 주소값을 rax에 복사
mov rax, [a] ;a라는 바구니 안에 있는 값을 rax에다가 복사
mov al, [a] ;1 byte만 꺼내오기
mov [a], byte 0x55 ;a 라는 바구니 안에 있는 값을 1byte 0x55로 수정
mov [a], word 0x6666
mov [a], cl
xor eax, eax
ret
section .data
a db 0x11
b dw 0x2222
c dd 0x33333333
d dq 0x4444444444444444
section .bss
e resb 10
메모리 결론
이번 포스팅에서 중요한 건 실질적으로 프로그램이 실행하면 우리가 선언한 변수들이 메모리에 할당 된다는 것과 메모리에서 레지스터로, 레지스터에서 메모리로 이동이 가능하다는 것, 메모리는 전부 주소로 이루어져 있다는 점이다.
이 개념은 C++의 포인터 등의 동작을 이해하는데 중요한 개념이다.
'C++ > 기초' 카테고리의 다른 글
[C++] 데이터 가지고 놀기 - 1 (정수, 불리언, 부동소수점, 문자열) (1) | 2023.11.22 |
---|---|
[Assemble] SASM - 5 (1) | 2023.11.21 |
[Assemble] SASM - 4 (0) | 2023.11.21 |
[Assemble] SASM - 3 (1) | 2023.11.21 |
[Assemble] SASM - 1 (2) | 2023.11.20 |