Generating 32-Bit Constants
여기서는 RISC-V에서 32비트 상수를 레지스터에 저장할 때 사용하는 방법을 다룬다.
여기서 핵심은 한 번의 addi 로는 12비트까지만 표현 가능하다는 제약 때문에 lui 명령과 addi를 조합해서 32비트 상수를 표현한다.
addi 12비트의는 즉시값(immediate)를 사용할 수 있다. 범위는 -2048 ~ 2047
그래서 addi 로는 32비트는 표현할 수 없고 lui를 활용한다.
lui s0, 0xFEDC8
addi s0, s0, 0x765
lui는 상위 20비트를 즉시값으로 채우고 하위 12비트를 0으로 채운다.
lui s0, 0xFEDC8 → s0 = 0xFEDC8000
addi 는 지정한 레지스터 값에 12비트 즉시값을 더한다.
이때 즉시값은 부호가 확장되어서 계산된다.
addi s0, s0, 0x765 → s0 = 0xFEDC8000 + 0x765 = 0xFEDC8765
만약 10진수로 -341을 addi로 더한다면??
⇒ -341_10 = 1110 1010 1011_2 = 0xEAB_16 =<부호확장>⇒ 0xFFFFFEAB_16
(cf. 맨 마지막에 생긴 캐리는 오버플로우로 버린다.)
Logical Instructions
이 부분은 RISC-V에서 비트 단위로 데이터를 조작하는 명령어들을 다루는 부분이다.
각 비트를 따로 조작해서 마스크하고 합치고 반전한다.
|
명령어
|
의미
|
동작
|
|
and
|
논리곱 (bitwise AND)
|
두 비트가 모두 1이면 1
|
|
or
|
논리합 (bitwise OR)
|
둘 중 하나라도 1이면 1
|
|
xor
|
배타적 논리합 (bitwise XOR)
|
둘 중 하나만 1이면 1
|
AND(masking)
s1 = 0xFFFF0000
s2 = 0x46A1F1B7
and s3, s1, s2
0xFFFF0000
AND 0x46A1F1B7
--------------
= 0x46A10000
F는 1111이라 마스킹이 되는 것
OR(combine)
or s4, s1, s2
0xFFFF0000
OR 0x46A1F1B7
--------------
= 0xFFFFF1B7
XOR(exclusive OR)
xor s5, s1, s2
0xFFFF0000
XOR 0x46A1F1B7
--------------
= 0xB95EF1B7
완전 반전
Shift Instructions
비트를 왼쪽이나 오른쪽으로 밀어서 shift하는 것, 값을 2배, 1/2배, 혹은 부호 보존 상태를 조정하는 명령어
|
명령어
|
의미
|
부호 처리
|
예시
|
|
sll
|
Shift Left Logical
|
부호 무시 (0 채움)
|
sll t0, t1, t2 → t0 = t1 << t2
|
|
srl
|
Shift Right Logical
|
0 채움
|
srl t0, t1, t2 → t0 = t1 >> t2
|
|
sra
|
Shift Right Arithmetic
|
부호비트 유지(1 채움)
|
sra t0, t1, t2 → t0 = t1 >>> t2
|
Left shift(sll)
0000 0011 (3)
<< 1 → 0000 0110 (6)
<< 2 → 0000 1100 (12)
왼쪽으로 한 칸 밀 때마다 2배씩 커짐
Right shift(srl)
0000 1100 (12)
>> 1 → 0000 0110 (6)
>> 2 → 0000 0011 (3)
오른쪽으로 한 칸 밀면 2로 나눈 것과 같음
Right shift Arithmetic(sra)
t1 = 0xFFFFFFF0 = -16 (32비트)
sra t0, t1, 2 → 0xFFFFFFFC = -4
부호비트(1)가 유지돼서 결과도 음수 cf. 반면 srl은 부호비트 무시
srl t0, t1, 2 → 0x3FFFFFFC (양수!)
Immediate Shift Instructions
이건 앞에서 배운 sli, srl, sra랑 거의 똑같은데, 시프트 양(얼마나 밀지)을 레지스터가 아니라 즉시값으로 직접 주는 버전
|
명령어
|
방향
|
채움
|
즉값 범위
|
예시
|
결과
|
|
slli
|
왼쪽
|
0
|
0~31
|
slli t0, t1, 3
|
t1 × 8
|
|
srli
|
오른쪽
|
0
|
0~31
|
srli t0, t1, 2
|
t1 ÷ 4
|
|
srai
|
오른쪽
|
부호비트(1)
|
0~31
|
srai t0, t1, 2
|
t1 ÷ 4 (부호 유지)
|
Multiplication
RISC-V에서는 정수끼리의 곱셉은 64비트 결과를 반환한다.
32비트 X 32비트 = 64비트가 나오는데, 이때 상위랑 하위 결과를 나누어서 저장하는 방법이다.
|
명령어
|
의미
|
결과 저장
|
설명
|
|
mul
|
Multiply
|
하위 32비트
|
보통 우리가 쓰는 일반 곱셈
|
|
mulh
|
Multiply High (signed)
|
상위 32비트
|
피연산자 2개 모두 signed로 취급
|
mul s3, s1, s2 # s3 = (s1 × s2)의 하위 32비트
s1 = 0x40000000
s2 = 0x80000000
s1 × s2 = −2⁶¹ = 0xE0000000_00000000
상위 32비트 = 0xE0000000
하위 32비트 = 0x00000000
mul s3, s1, s2 # s3 = 0x00000000
mulh s4, s1, s2 # s4 = 0xE0000000
{s4, s3} = s1 × s2 = 0xE0000000_00000000
Division
division은 multiplication과는 반대로 피제수를 제수로 나눠서 몫과 나머지로 나눈다
|
명령어
|
부호
|
결과
|
예시
|
결과
|
|
div
|
signed
|
몫
|
-13 / 5
|
-2
|
|
rem
|
signed
|
나머지
|
-13 % 5
|
-3
|
addi s1, zero, 13 # 피제수
addi s2, zero, 5 # 제수
div s3, s1, s2 # 몫
rem s4, s1, s2 # 나머지
13 ÷ 5 = 2 (몫)
13 - (2×5) = 3 (나머지)
Branching
Branching은 flow control을 담당하는 부분이다
조건에 따라서 코드를 건너뛰거나 루프나 조건문 같은 특정 주소로 점프할 수도 있다.
|
명령어
|
의미
|
조건
|
|
beq
|
Branch if Equal
|
rs1 == rs2
|
|
bne
|
Branch if Not Equal
|
rs1 != rs2
|
|
blt
|
Branch if Less Than (signed)
|
rs1 < rs2
|
|
bge
|
Branch if Greater or Equal (signed)
|
rs1 ≥ rs2
|
예시 - beq
addi s1, zero, 5
addi s2, zero, 5
beq s1, s2, LABEL
addi s3, zero, 100 # 실행 안 됨 (분기)
LABEL:
addi s3, zero, 999 # 실행됨
실행 순서
- s1 == s2 → 참
- beq → LABEL 위치로 이동
- 다음 줄의 addi s3, 100 건너뜀
- 결과 s3 = 999
예시 - bne
addi s1, zero, 3
addi s2, zero, 4
bne s1, s2, NOT_EQUAL
addi s3, zero, 0
NOT_EQUAL:
addi s3, zero, 1
s1 ≠ s2 → 조건 참 → 분기 발생 → s3 = 1
예시 - blt, bge
addi s1, zero, -5
addi s2, zero, 2
blt s1, s2, LESS
addi s3, zero, 0
LESS:
addi s3, zero, 1
s1 < s2 → 조건 참 → 분기 발생 → s3 = 1
Conditional Statements
RISC-V에는 if나 else 같은 명령어가 없어서 Branch랑 Jump 적절하게 조합해서 조건문을 표현할 수 있다.
예시 - if only
addi s1, zero, 5
addi s2, zero, 10
blt s1, s2, SMALLER
addi s3, zero, 99 # s1 >= s2 이면 실행됨
j EXIT
SMALLER:
addi s3, zero, 1 # s1 < s2 이면 실행됨
EXIT:
예시 - if else
addi s1, zero, 4
addi s2, zero, 4
bne s1, s2, ELSE # if (s1 != s2) → ELSE로
addi s3, zero, 111 # 참일 때 실행
j EXIT
ELSE:
addi s3, zero, 222 # 거짓일 때 실행
EXIT:
Loops
반복문은 브랜치를 이용해서 되돌아가는 구조로 만든다.
RISC-V에서는 j, blt, bne 로 만들 수 있다.
예시 - while, for
addi t0, zero, 0 # i = 0
addi t1, zero, 5 # N = 5
LOOP:
bge t0, t1, EXIT
addi t0, t0, 1
j LOOP
EXIT:
예시 - do while
DO:
...
blt x, y, DO # 조건 참이면 되돌아감
Arrays
주소 계산이랑 load랑 store 계념 잘 알면 쉽다.
# s0 = array base address
lw t1, 0(s0) # t1 = array[0]
slli t1, t1, 1 # t1 = t1 * 2
sw t1, 0(s0) # array[0] = t1
lw t1, 4(s0) # t1 = array[1]
slli t1, t1, 1 # t1 = t1 * 2
sw t1, 4(s0) # array[1] = t1
'지식 > 컴퓨터아키텍쳐' 카테고리의 다른 글
| Machine Language (0) | 2025.10.16 |
|---|---|
| Instructions(3) (0) | 2025.10.07 |
| Instructions(1) (0) | 2025.10.07 |
| Computer Abstractions and Technology(2) (0) | 2025.10.07 |
| Computer Abstractions and Technology(1) (0) | 2025.10.07 |