Chapter 4. Machine Language
Goal
-
컴파일링 이후에 고급 언어에서 사용 된 배열의 개념은 사라진다. 따라서, Java or Python이 코드를 기계어로 변환하는 작업을 어셈블러가 완료 했을 때 기계어는 더 이상 배열을 인식하지 못한다. 이 기계어에서 어떻게 배열을 관리할까
-
디스플레이 유닛을 이용 하여 입력 하는 방법과 출력 유닛을 이용하여 키보드를 제어 하는 방법을 과제를 통해 학습
-
Memory
- 고유한 주소를 가지며, 위치 또는 메모리 레지스터로도 참조될 수 있는 셀들의 연속적인 배열이다.
메모리는 Register 들의 Sequential이기 때문에 `log2n`개 만큼의 비트 표현을 갖고 있으며, 이 때 접근 방식은 개수와 상관 없이 접근이 가능하다 -
메모리 접근
- 주소 레지스터를 통해 메모리 조작
1. 메모리 위치 17에 값 1을 설정하기 - `load A, 17 | load M, 1` - load A, 17 -> 17번 메모리 레지스터 선택 - load M, 1 -> 선택된 메모리 레지스터의 값 설정

- 주소 레지스터를 통해 메모리 조작
-
Processor
- 특정한 기초 연산들을 수행 하는 장치로 산술 및 논리 연산, 메모리 접근 연산, 그리고 제어 연산이 포함됨.
- 프로세서는 2진 명령어를 파싱하고 실행 할 수 있는 논리 게이트 칩, ALU, 레지스터 모음으로 구성 됨
-
Register
- 두 칩 사이에 데이터를 옮기는 건 상대적으로 느린 작업이다. 프로세서는 보통 값 하나를 저장 할 수 있는 레지스터를 여러 개 장착하고 있고, 이 레지스터들은 프로세서 칩 내부에 위치하며 프로세서가 데이터와 명령어를 빠르게 조작 할 수 있도록 로컬 고속 메모리 역할을 한다.
CPU 내에 위치한 레지스터의 종류 1. 데이터를 저장하는 데이터 레지스터 2. 데이터 값이나 메모리 주소로 해석되는 값들을 저장 하는 주소 레지스터 - 컴퓨터 아키텍처는 주소 레지스터에 특정 값 n을 설정할 경우 주소가 n인 메모리 위치가 즉시 선택되도록 구성된다. 그리고 선택된 메모리 위치에서 후속 연산을 수행 할 준비를 한다.
- A 명령어
- 프로그램의 제어하에 컴퓨터에 상수를 입력하는 유일한 수단
- 선택된 RAM 레지스터의 주소를 A에 설정함으로써, 다음 C 명령어가 어느 메모리를 읽어야 하는지 알려줌
- C 명령어
- 무엇을 계산 할지, 어디에 계산 된 값을 저장할지, 다음은 어떤 명령을 실행 할지 지정함
본 책에서 설명하는 핵 프로그램에서 A-명령어와 C-명령어 두 가지로 컴퓨터를 흉내 낼 수 있다. 어느 지점으로 가야하고, 어떤 명령을 실행 할지 두 곳에서 결정되기 때문이다. 하지만, A 레지스터를 사용하는 경우 핵 컴퓨터는 RAM[n] 레지스터와 ROM[n] 레지스터를 구별 하기 어려워 둘 다 선택된다는 점을 기억해야한다.
Working with registers and memory

- @0: 0번째 RAM에 접근
- D = M: D라는 변수에 현재 접근한 RAM에 담겨 있는 값(M) 저장
- @1: 1번째 RAM에 접근
- D = D+M D라는 변수에 저장 된 값과 현재 RAM에 담겨 있는 값을 더한 후 D에 저장
- @2: 2번째 RAM에 접근
- M = D: D값을 현재 RAM주소에 저장
물리적 디스플레이 장치가 메모리 맵의 내용으로부터 지속적으로 새로 고침 되는 것이 관건이며, 그 행위는 매 초 마다 일어난다. 디스플레이 유닛에 무언가를 쓰고 싶을 때 메모리 맵의 일부 비트를 조작하는 것 뿐이다. 그리고 다음 새로고침 주기에 메모리에서 변경한 내용이 화면에 반영 된다.
고급 프로그래밍 언어에서 사용 하던 hello world 같은걸 디스플레이 표시 하려면 매우 복잡할 것이다.
Screen memory map

메모리맵은 16비트 값의 시퀀스이고, 8k, 16비트 워드를 사용하고있다. 그리고 메모리맵은 화면에 나타내는 픽셀 값을 담고있다. 여기서, 읽기 쓰기는 항상 16비트를 제어해야 하며, 개별 비트에 접근 할 수 없다.

- 인간에게 익숙한 고급언어 특성의 의사코드를 작성한 후 기계어로 변환 하는 과정을 거치는 것이 중요!

-
키보드의 케이블을 따라가보면 키보드 메모리맵에 연결 되는 것을 알 수 있다. 키보드 메모리 맵은 16비트 레지스터에 접근한다.
-
만약 ‘k’를 누르면 임의의 값이 이진수로 표현되어 키보드 메모리 맵에 저장 되는 형태이다. 그렇기 때문에 키보드의 모든 키에는 바이너리 코드로 스캔되는 코드가 존재한다.
- 아스키코드표와 비슷한 개념을 지니고 있음
- 사용자가 키보드의 자판을 누면 스캔 코드의 바이너리 코드가 키보드 메모리 맵에 저장된다.
- 만약 키보드의 출력을 제어 하고 싶다면 키보 메모리 맵을 읽어야한다.
- 이 때, RAM[0]에 위치 해 있다면 키보드는 IDLE 상태로 어떠한 입력도 받지 않고 대기중인 상태가 된다.
Display unit을 제어 하려고 한다. 이 때, 4행 55열의 비트를 변경하려면 어떤 주소를 검색하고 어떤 비트를 변경해야 할까?
- Screen[131], Set 7bits
Summary
-
주소 지정 모드에는 크게 3가지가 존재
-
직접 주소 지정 방식 : 특정 주소나 주소를 나타내는 기호를 활용
LDR, R1 67 -> LDR R1, bar (R1 = MEM[67]) -
즉시 주소 지정 방법 : 레지스터에 주소에서 값을 가져오는 대신 상수를 바로 저장
LDR R1, 67(R1 = 67) -
간접 주소 지정 방식 : 포인터의 개념
ADD R1, foo, j -> LDR*R2, R1 -> STR R2, x 1. `ADD R1, foo, j`: 이 명령은 `foo` 변수의 주소와 `j`를 더한 값을 레지스터 R1에 저장, 이 레지스터 R1은 이후에 사용될 포인터 변수 역할을 함 2. `LDR* R2, R1`: R1에 저장된 주소로부터 데이터를 읽어와서 R2 레지스터에 저장, 이 명령은 R1의 값이 `foo` 변수의 주소를 가리키므로, `*(foo+j)`의 결과값을 R2에 저장합니다. 3. `STR R2, x`: 마지막으로 R2에 저장된 값을 `x` 변수에 저장합니다.
-
-
물리적 I/O 장치와 메모리 맵은 계속 갱신되는 루프를 통해 동기화 되기 때문에 키보드로 타자를 치는 행위, 디스플레이로 화면을 보는 행위가 이뤄질 수 있다.
- 그렇다면 이 때, I/O interrupt 가 발생 했다고 운영체제가 판단 하여 전달 하는 것일까?
-
CPU Emulator는 프로그램을 실행 시킨 이후에 종료되는 시점이 없기 때문에 실행 상태에 놓이며, 이 때 의도치 않은 명령이 실행 될 수 있다.
- 실제로 이런 상황을 유발 하는 보안점 공격을 시도 하기도 한다. 이 것을 Nope-Slide 라고 부르며, 이 때 종료되지 않은 프로그램에 의해 컴퓨터가 종료되는 현상을 일으키기도 한다.
- Low-Level에서 컨트롤 할 때는 최선의 방법으로 Jump를 통해 0번으로 보내며, 이렇게 무한 루프에 빠지게 만드는 것이 취약한 부분을 건들지 않고 명령어를 통해 컴퓨터를 통제 할 수 있는 방법이다.