RISC-V 어셈블리어
어셈블리어인 RISC-V (2010~)를 먼저 쉬운 형태로 배울 것이다
(사실 명령어 집합들은 모두 다 컴퓨터의 같은 원리를 쓰고 제공되는 기능이 몇개 안되고,
최소 비용으로 최대 성능을 내야해서 비슷하게 생겼다)
add a, b, c
b와 c를 더하고 a에 넣다
a, b, c, d를 더하는 예
add a, b, c
add a, a, e
add a, a, d
특징
1. 한 줄에 명령어 하나만을 쓸 수 있다 (컴퓨터는 한번에 한가지 명령만 처리하도록 설계 되었으므로)
2. 명령어의 피연산자는 보통 명령어 하나당 2~3개이고 위치가 우측이다 (간단함을 위해 규칙성을 채택했다)
(그래서 빼기는 sub a, b, c 이다)
C언어의 어셈블리어로의 번역 예시
f = (g + h) - (i + j)
↓
add t0, g, h
add t1, i, j
sub f, t0, t1
피연산자 특징 : 연산 되려면 레지스터(CPU 내부의 메모리)에 들어가야만 한다
레지스터 특징
기능 : 이진수 문장 저장
원리 : on, off나 음극 양극 같은 두가지 상태를 가질 수 있는 트랜지스터들이 각각의 글자 역할을 하면서 문장을 표현
트랜지스터 개수 = 64개
(=64bit, 더블워드로도 불림) (32bit일 경우엔 워드로 불림)
CPU 하나에 담긴 레지스터 개수 = 32개
(개수가 늘면 CPU 내부전선의 길이도 늘어난다. 이것은 클럭 사이클 시간을 늘려서 속도감소를 일으켜서 개수를 제한한다.)
표기법 = 모든 레지스터는 x 뒤에 차례대로 붙인 번호를 이름으로 쓴다 (x0, x1, x2, x3 ... x31)
(사실 어셈블리어의 피연산자는 변수명이 아니라 이것을 그대로 쓴다. 예를 들어 add x5, x20, x21)
입력 개수 > 레지스터 개수인 경우
초과된 입력이 메모리에 자동 저장 돼서, 다시 레지스터로 옮겨야 한다
이때 쓰는 명령어 : 데이터 전송 명령어 (RISC-V에선 ld) (=load doubleword)
ld 다른 사용 예
g = h + A[8] 의 번역에서 배열은 메모리에 저장 되서, ld가 필요하다.
g는 x20, h는 x21, A의 메모리 시작주소는 x22에 저장 됐다.
A는 메모리에 있어서 레지스터로 가져와야한다
↓
ld x9, 64(x22)
( 뜻 : x22의 시작주소의 8번째 값을 x9에 넣는다
64인 이유 : 8바이트 주소를 쓰므로, 모든 값은 주소가 8씩 차이난다. 그리고 8번째 값이라 8*8로 64가 되었다
명칭 : 64는 변위, x22는 베이스 레지스터라고 한다 )
↓
마지막으로 add x20, x21, x9 를 하면 끝이 난다
추가 예
A[12] = h + A[8] 의 번역 (A[12] 같은 메모리에 다시 넣으려면 sd (=store doubleword)를 써야한다 )
↓
ld x9, 64(x22)
add x21, x21, x9
sd x21, 96(x22) (8*12 = 96)
상수를 피 연산자로 쓰는 법 1
ld x9, AddConstant4(x3) (상수를 쓰려면 메모리에서 가져와야 한다)
add x22, x22, x9
상수를 피 연산자로 쓰는 법 2
addi x22, x22, 4
부호를 바꾸는 경우
sub x22, x0 (x0은 0으로 고정이다. 왜냐하면 0은 다양한 형태로 자주 쓰기 때문이다. 예를들어 sub에서 쓰면 부호를 바꾸는 기능을 가진다)
RISC-V의 모든 명령어
다른언어의 논리연산 명령어와 비교 및 설명
shift는 이진수 문장을 왼쪽 혹은 오른쪽으로 이동 시키고 표시될 자리가 없는 값은 소거되고 빈 자리는 0으로 표시 되게한다. 하지만 예외적으로 Shift right arithmetic은 빈자리를 부호 비트로 채운다
AND는 두 이진수 문장을 비교해서 각 비트가 둘다 1인 경우에만, rd의 해당 자리의 트랜지스터를 1로 만들고 OR는 둘중 하나라도 1이면 1로 만들고 나머지는 0으로 한다
NOT은 지원하지 않고 대신 XOR로 대신한다
XOR은 두 피연산자가 서로 다를 때만 1로 만든다
판단 명령어
컴퓨터는 단순 계산기와 다른점은 판단 기능이 있다.
그 예로, 입력이나 연산 결과에 따라 다른 명령어를 실행할 수 있다. 이것이 프로그래밍 언어에서는 보통 if문으로 표현한다. RISC-V에서도 비슷한것이 있다
beq rs1, rs2, L1 인데 rs1와 rs2가 서로 같으면 L1으로 가라는 뜻이다
풀네임은 beq = branch if equal이다
설명할 명령어가 하나 더 있다
bne rs1, rs2, L1
인데 rs1과 rs2가 서로 안같으면 L1으로 가라는 뜻이다. 풀네임은 bne = branch if not equel이다
위 두가지는 조건부 분기라고 부른다
(조건부분에 따라 분리되는 시기)
이 두가지로 조건문 하나를 번역해 볼것이다
if ( i == j ) f = g + h; else f = g - h;
// f , g , h , i , j = x19 , x20 , x21 , x22 , x23
(참고로 beq보다 bne를 먼저 쓰는게 효율적이다)
↓번역
bne x22, x23, Else
add x19, x20, x21
beq x0, x0, Exit //이것은 '무조건 분기'라고 부른다. 항상 참인 조건으로 무조건 분기를 하도록 하는 것을 의미한다
Else: sub x19, x20, x21
Exit:
이제 순환문을 번역해볼 것이다
while (arr[i] == k)
i += 1;
// i , k , arr = x22 , x24 , x25
↓번역
Loop: // while {
slli x10, x22, 3 // i*8로 바이트 주소화
add x10, x25, x10 //arr[i]주소=시작주소+인덱스*8
ld x9, 0(x10) // arr[i]값 가져오기
bne x9, x24, Exit // (arr[i]==k)
addi x22, x22, 1 // i += 1;
beq x0, x0, Loop // }
빠른 경계 검사 방법
x20 >= x11 || x20 < 0 일때 indexOutOfBounds로 가라를 코딩하면 아래처럼 된다
bgeu x20, x11, IndexOutOfBounds
Case/Switch
이것의 구현은 if / else의 반복으로 가능하다
'BackEnd > Computer Architecture' 카테고리의 다른 글
1.2.6 어셈블리어로 긴 수치와 주소를 쓰는 법 (0) | 2022.03.02 |
---|---|
1.2.5 어셈블리어로 문자와 문자열 사용해보기 (0) | 2022.02.27 |
1.2.4 어셈블리어로 프로시저 구현 (0) | 2022.01.18 |
1.2.3 어셈블리어 구현 (0) | 2022.01.07 |
1.2.2 숫자 구현 (0) | 2022.01.06 |
댓글