일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 밑바닥부터 만드는 운영체제
- 운용 시 유용한 쿼리
- ix is lock
- nandtotetris
- 밑바닥부터 만드는 컴퓨팅 시스템
- BastianHost
- innodb 버퍼풀
- mysql 엔진
- 온라인 ddl
- mysql 아키텍쳐
- 마운트
- 핵기계어
- 필수 스크립트
- SessionManager 터널링
- MySQL
- 어뎁티브 해시 인덱스
- 리눅스
- performance스키마
- Terraform
- 도커
- dff
- 안전하게 테이블 변경
- InnoDB
- 밑바닥부터 구현하는 컴퓨팅 시스템
- s3
- x lock s lock
- mysql 구조
- s3 sync
- ec2
- innodb구조
- Today
- Total
이것이 점프 투 공작소
NandToTetris-Hardware simulator / 기계어 (밑바닥부터 만드는 컴퓨팅 시스템) 본문
기계어
하드웨어(기계)를 제어하는 언어이며, 하드웨어와 소프트웨어 사이 가장 중요한 인터페이스입니다.
CPU가 명령을 처리 할 때 사용되는 언어로 2진수로 되어있습니다.
모든 기계어는 기본적인 산술연산과, And, Or, Not같은 논리연산들을 위한 명령어들을 지원합니다.
기계어에서 수행되는 많은 작업들은 특정메모리에 접근하여 메모리를 조작하는 것과 관련이 있습니다.
아래 예시는 완전한 기계어(2진수)가 아니라 어셈블리어 입니다.
어셈블리어는 어셈블러에 의해 기계어로 변경되어 하드웨어에 전달됩니다.
CPU는 메모리에 올라가있는 명령들을 순서대로 실행하지만,
특정 명령어로 점프하거나, 반복해야하는 경우도 있습니다.
이러한 분기동작을 위한 기능도 존재합니다.
(R1, R2는 레지스터를 의미합니다.)
핵 기계어
책에서 다루는 핵 컴퓨팅 시스템은 폰 노이만 구조를 따르며
16bit 컴퓨터입니다. 즉 CPU와 메모리 장치가 16bit값들을 처리하고 저장하도록 설계되었다는 의미입니다.
메모리
RAM과 ROM 두개의 메모리를 사용하며 두 메모리 모두 폭(width)이 16bit , 주소공간은 15bit입니다.
데이터 메모리(RAM)는 프로그램이 조작하는 데이터(2진수)를 저장하고
명령어 메모리(ROM)는 프로그램의 명령어들을 저장합니다.
각각의 주소공간 (2^15) 마다 width (16bit) 만큼의 데이터를 저장 할 수 있어 총 64kb 의 데이터를 저장 할 수 있습니다.
메모리에 주소는 0 ~ 32k -1까지 존재합니다.
즉 주소지정 가능한 16bit 레지스터가 일렬로 32k -1까지 나열되어있어 있다고 생각하면 편합니다.
레지스터
3개의 16bit 레지스터 데이터 레지스터(D), 주소 레지스터(A), 선택된 데이터 메모리 레지스터(M)이 있습니다.
데이터 레지스터(D)는 16bit값을 저장합니다.
주소 레지스터(A)는 RAM 혹은 ROM의 주소를 지정합니다.
핵 기계어 문법
A-명령어 (op-code가 0일때)
A레지스터에 15bit값을 지정합니다.
레지스터에 들어오는 16bit중 가장 왼쪽 비트는 연산코드(op-code)를 나타내고 나머지 음수가 아닌 이진수를 가리키는 15bit를 A레지스터에 저장합니다.
A레지스터에 값을 저장하면 선택된 레지스터의 주소는 선택된 데이터 메모리 레지스터(M)에도 설정됩니다.
이후 M을 참조하여 다음 C명령어에서 해당 레지스터를 조작 할 수 있게 합니다.
변수지정 : @xxx
핵 명령어 @xxx는 A 레지스터에 값 x를 지정합니다.
A레지스터에 지정한 값 xxx가 주소로 사용될지, 값으로 사용될지는 다음에 나오는 명령어에 따라 달라집니다.
// 예시1 : 데이터 레지스터에 상수값 입력
@23 // A레지스터의 값을 23으로 설정
D=A // 레지스터(D)에 레지스터(A)값 저장 D=23
//예시2 : 주소값 사용
@17 // A레지스터의 값을 17로 설정
M=0 // RAM[17]을 0으로 설정, RAM[17] = 0
// 예시3 : RAM[100]= 17
@17
D=A
@100
M=D
// 예시 4 : RAM[100] = RAM[200]
@100
D=M
@200
M=D
C-명령어 (op-code가 1일때)
무었을 계산할지 (comp), 어디에 계산된 값을 저장할지(dest), 그 다음 무엇을 할지(jump)에 대한 명령어입니다.
16bit 중 맨 왼쪽은 연산코드(op-code)
다음 두 비트는 사용되지 않는 비트 (관례상 1로 표시)
다음 7개 비트는 comp
다음 3개 비트는 dest
맨오른쪽 3개의 비트는 jump를 나타냅니다.
comp (acccccc)
7비트로 ALU가 수행할 연산을 결정하는 comp 비트입니다.
첫번째 ALU입력은 D레지스터에서 받습니다.
두번째 ALU입력은 A레지스터(a비트가 0일때)나 M레지스터(a비트가 1일때)에서 입력을 받습니다.
comp에서는 a가 0일때와, 1일때 수행하는 연산이 따로 나눠져있습니다.
dest (ddd)
3비트로 ALU 연산 결과를 담을 목적지입니다.
저장하지 않을지(null), M에 담을지(RAM[A]), D 레지스터에 담을지, A레지스터 D레지스터 둘다에 연산 결과를 담을지 결정합니다.
jump (jjj)
jump 비트는 연산 결과에 따라 A레지스터에 지정된 주소의 명령어를 가져옵니다. (점프)
000인경우 점프 없이 다음 명령어를 불러오고,
001 인경우 COMP의 연산 결과가 0보다 크면 점프합니다.
무조건 점프 JMP(111)는 관례상 0;JMP로 정의됩니다.
(A 레지스터를 사용할 때 충돌 방지필요)
핵 컴퓨터는 RAM과 ROM의 주소를 지정할 때 주소 레지스터 하나만 사용됩니다.
따라서 @n 명령어를 실행할 때 RAM[n], ROM[n]이 모두 선택됩니다.
그래서 이어지는 C-명령어가 M을 조작하거나, 점프를 수행하게 된다.
이때 충돌을 피하기 위해서는 M에 대한 참조를 포함하는 C-명령어에서는 점프를 지정하지 않도록 하고,
점프를 지정한 C-명령어에서는 M을 참조하지 말아야 합니다.
예제
1. 두 레지스터(R1, R2)에 값들을 더하고, 그 합에 17을 더한 후 그 결과를 세번째 RAM레지스터에 저장
각 명령어의 의미는 아래와 같습니다.
1. @0 // RAM [0] 지정
2. D=M // 먼저 RAM[0]의 값을 D레지스터로 옮긴다.
3. @1 // RAM[1] 지정
4. D=D+M // 이후 RAM[1]을 선택하여 D레지스터에 들어있는 값과 현재 선택된 메모리 레지스터의 값을 더해 D레지스터에 저장한다.
5. @17 // A레지스터에 정수 17 입력
6. D=D+A // D레지스터에 A레지스터의 값 더함
7. @2 // RAM[2] 지정
8. M=D // 선택된 레지스터 RAM[2]에 D레지스터의 값 (17)을 입력
2. 1+2+3+ ... + n을 계산하시오 (n은 첫번째 레지스터의 값, 두번째 RAM레지스터에 결과 출력)
// i, sum 변수 초기화
@i
M=1
@sum
M=0
// 반복문 시작
(LOOP)
@R0 // 반복할 횟수 N이 들어있음
D=M
@i
D=D-M // D = D - i (D는 R0 - i)
@STOP
D;JLT // i가 R0보다 크면 STOP으로 점프 (반복 종료)
@sum
D=M
@i
D=D+M
@sum
M=D
@i
M=M+1
@LOOP
0;JMP // 다시 LOOP로 돌아가서 계속 반복
(STOP)
@sum
D+M
@R1
M=D
(END)
@END
0;JMP
3. for i=0...n {do somthing with arr[i]} 를 구현해보자
@n
M=0 // RAM[n] = 0
(LOOP)
@n
D=M // D = 0
@R1 // 특정값
D=D-M // R1과 D의 차 계산
@END
D;JEQ // R1과 D가 같은지 비교 -> D가 0일 경우 @END로 점프
@R0
D=M // D = RAM[0]
@n
A=M+D // RAM[n]과 D(R0) 값을 더한 결과 A에 지정
M=-1 // RAM[A]에 -1 저장
@n
M=M+1 // n값 1증가
@LOOP
0;JMP
(END)
@END
0;JMP