풀이를 시작하기 전에 내가 이해한 바를 적겠다. 레지스터에 저장하고, 메모리에서 참조하는 것을 가져오는 과정 등을 대충은 이해를 하다가 말로 표현이 가능할 정도로 감이 잡힌 바를 정리하겠다. 아는 사람에게는 당연한 얘기일 것이다.
※ 레지스터에는 항상 값으로 숫자를 저장한다. 레지스터의 주소 이런건 없다. 64비트인 rax레지스터에는 64개의 이진수를 저장하고, 이를 4개씩 끊어 16진수로 보기 쉽게 표현한다. 이때 예를 들면 %eax에는 0x12345678 이런숫자가 저장돼있다는 거다. 이 숫자는 어느 주소일수도 있고, 값일 수도 있다. 일단 숫자가 저장돼있는 것이다.
※ %rsi가 어떤 포인터 변수를 뜻한다고 하자.
void Access(long A, long *pA) {
*pA = A;
}
mov %rdi, (%rsi)
처럼 어셈블리어가 표현될 것이다.
%rsi는 레지스터니까 값에 어떤 숫자가 저장된다. 실행 후에 그 숫자열은 변수 A의 주소를 뜻할 것이다.
만약 A가 0xFFFFFFFF($-2^{63}$)이고, 변수 A가 메모리에 저장된 주소가 0xAAAA라면
A를 뜻하는 레지스터 %rdi = 0xFFFFFFFF로 써진다.
pA를 뜻하는 레지스터 %rsi = 0xAAAA가 써지고, (%rsi)를 하면 %rsi가 가진 숫자 값을 주소로 간주하고, 그 주소의 값을 가져오기 때문에 0xFFFFFFFF가 써지는 것이다.
(%rsi) = memory[0xAAAA] = 0xFFFFFFFF
즉 레지스터에는 그냥 숫자만 있고, 어셈블리어로 소괄호 ()를 씌워주면, 그 숫자를 주소로 간주해서 Memeory 그 주소의 값을 가져오는 것이다. 그냥 레지스터 Value 자체는 단순 숫자열이기 때문에 주소로서 의미를 가지는지, 값으로서 의미를 가지는지는 모른다.

Sol)
| Operand | Value |
| %rax | 0x100 |
| 0x104 | 0xAB |
| $0x108 | 0x108 |
| (%rax) | 0xFF |
| 4(%rax) | 0xAB |
| 9(%rax, %rdx) | 0x11 |
| 260(%rcx, %rdx) | 0x13 |
| 0xFC(, %rcx, 4) | 0xFF |
| (%rax, %rdx, 4) | 0x11 |
1. %rax 레지스터는 0x100이 저장돼있다. 즉 %rax의 Value는 0x100이다.
2. 0x104는 16진수 104로 생각할 수 있지만, Immediate형은 앞에 $기호를 붙여야 한다. 단순 0x104는 주소를 뜻하는 것이고, 그 주소의 값은 0xAB이다.
3. Immediate $0x108은 그 자체로 숫자인 16진수 $108_{16}$이다.
4. (%rax)는 %rax에 저장된 숫자열을 주소로 간주하고, 그 주소에 있는 Value를 가져온다. %rax에 저장된 0x100을 주소로 간주해, 메모리 주소에서 찾아온다 ==> memory[0x100] = 0xFF
5. 4(%rax)는 %rax에 저장된 주소가 가르키는((%rax)를 뜻함) 위치에서 4B 뒤에 위치(주소)의 값을 뜻한다. 즉 0x100에서 4bit 뒤인 0x104 주소가 가르키는 값인 0xAB이다.
다시 말하면 4(%rax)는 %rax가 가진 자체 Value에 4를 더한 숫자를 주소로 간주해, Memory의 그 주소에서 값을 가져온다.
6. 9(%rax, %rdx)는 "%rax가 가진 값 + %rdx가 가진 값 + 9"를 주소로 간주하고, Memory에서 해당 값을 가져온다. 즉 (0x100 + 0x3 + 0x9) = (0x10($12_{10}$)) = (0x10C)이고, 0x10C 주소가 가르키는 값인 0x11을 뜻하는 것이다.
7. 6번이랑 똑같이 하면 된다.
(260 + %rcx + %rdx) = (0x104 + 0x1 + 0x3) = ($264_{10}$) = (0x108) = 0x13
이다.
8. 이것도 똑같다.
(0xFC + %rcx * 4) = (0xFC + 0x1 * 4) = (0xFC + 0x4) = (0x100) = 0xFF
9. (%rax, %rdx, 4) = (0x100 + 0x3 * 4) = (0x100 + 0xC) = (0x10C) = 0x11

Sol)
1. l
2. w
3. b
4. b
5. q
6. w
설명은 생략한다

Sol)
1. x86-64에서는 주소 계산에 64bit 레지스터만 사용 가능하다. 즉 (%ebx)로 쓰면 안되고 (%rbx)라고 써야 한다.
2. movl은 32bit 데이터를 옮기는 건데, %rax는 64bit 레지스터다. %eax로 고치거나, movq로 고쳐야 한다.
3. Memory에서 Memory로 직접 데이터 이동은 불가능하다. 반드시 레지스터를 거쳐야 한다
movw (%rax), %bx
movw %bx, 4(%rsp)
4. %sl이라는 레지스터는 없다. %rsi의 1바이트 범위를 말하는거면 %sil이다.
5. Immediate에 값을 넣을 수는 없다. 즉 mov의 dest는 Imm.이 아닌 Reg.나 Mem.만 가능하다.
6. 작은 크기의 레지스터 값을 큰 레지스터에 넣을 때는 남은 비트를 어떻게 extension할것인지 명시해야 한다.
즉 signed extension을 하고 싶으면 movslq로 하고
zero extension을 하고 싶으면 movl을 하고 dest를 %rdx가 아닌 %edx라고 해야 한다.(movzlq는 없다!!)
7. %si는 2byte 범위의 레지스터다. 즉 명령어를 movw로 바꾸거나
%si가 아닌 %sil이라고 써야 한다.



Sol)
※확장이동인 movs, movz의 dest는 항상 Reg.이다. 즉 Mem.에는 한번에 확장하면서 옮길 수 없다.
2. char형은 signed고, 더 큰 자료형인 int형에 넣는 과정이므로
movsbl (%rdi), %eax
movl %eax, (%rsi)
3. 2번과 동일
4. unsigned char이므로 zero extension
movzbq (%rdi), %rax
movq %rax, (%rsi)
한편 아래처럼 쓸 수도 있다.
movzbl (%rdi), %eax // 32비트 연산의 특성으로 상위 4B가 자동으로 0이 됨
movq %rax, (%rsi) // 이미 0으로 채워진 %rax 전체를 저장
이게 명령어 바이트 크기가 더 작아서, 속도가 빠르다.
5. int에서 더 작은 char로 이동하는 과정은 알아서 truncated된다.
movl (%rdi), %eax
movb %al, (%rsi)
이때 첫번째 줄에서 "movb (%rdi), %al"로 대신 쓰면 안되나 생각할 수 있다.
안되는 건 아니고, 결과는 같다. 하지만 여러 이유로 인해 Sol처럼 작성한다.
- 1: x86-64는 little endian이라서 0x0000012C를 저장할 때 2C 01 00 00 순서로 저장되고, movb를 하면 2C를 전달한다. 이때 big endian이면 00을 전달한다.
- 2: %al만 업데이트하면 나중에 %rax또는 %eax를 읽으려 할 때 cpu내부 최적화된 파이프라인을 방해하여 하드웨어적 비효율이 일어난다.
- 3: "movb (%rdi), %al"을 분석할 때 %rdi 주소에 char형 데이터가 있다고 착각할 수 있다.
6. 5번과 동일
7. signed형인 char형을 더 큰 자료형인 short에 저장하므로 signed extension한다.
movsbw (%rdi), %ax
movw %ax, (%rsi)

Sol)
void decode1(long *xp, long *yp, long *zp) {
long temp = *xp;
*xp = *yp;
*yp = *zp;
*zp = temp;
}


Sol)
1. q+9
2. q+p
3. q+3p
4. 2+p+7q
5. 0xE + 3q
6. 6+p+7q
※ 뭘 의도한 문제인지는 알겠는데, 저 어셈블리어는 오류다. Scale자리에 3, 7이 올 수 없다. 오직 1, 2, 4, 8, ... 인 2ⁿ꼴만 올 수 있다.

이것도 마찬가지다. 첫번째 줄은 어찌어찌 10y라고 해도, 세번째 줄에서 세번째 요소를 scale로 쓰는건지가 헷갈려서 그냥 풀지 않았다

Sol)
1. (%rax)는 %rax값을 주소로 간주하고, 주소가 가르키는 값에 데이터를 읽는 것이다
%rax의 Value가 0x100이므로, addq ~~의 dest. = 0x100이다.
0x100의 Value는 0xFF였는데 여기에 %rcx = 0x1을 더하면 Value = 0x100이다.
2. 마찬가지로 dest는 %rax+8 = 0x108을 주소로 가지는 곳이다. dest. = 0x108
0x108에는 0xAB가 있는데, 여기에서 %rdx = 0x3을 빼면 Value = 0xA8이다
3. %rax+%rdx*8 = 0x100 + 0x3 * $8_{10}$ = 0x100 + 0x18 = 0x118이므로 Dest. = 0x118
0x118의 값인 0x11에 8을 곱해서 저장하므로 Value = 0x88
4. 16(%rax) = *($16_{10}$+%rax) = *(0x110)이다. Dest. = 0x110
0x110에는 0x13이 있는데 여기에서 incq하면 0x14가 된다.
5. %rcx를 decq하면 Dest. = %rcx이고
Value = 0x0이다.
6. Dest. = %rax
Value = 0xFD

Sol)
salq $4, %rax
sarq $4, %ecx
이때 아래줄에서 어차피 4비트 뺄꺼니까 상위 비트는 안가져오려고 movl을 썼다는 것만 유의하면 된다

오류 투성이다. short형인데 접미사를 w가 아닌 q를 쓰고 있고, movq instruction의 dest로 %rbx가 아닌 %bax로 오타를 냈다.
Sol)
short arith3(short x, short y, short z)
{
short p1 = y | z;
short p2 = p1 >> 9;
short p3 = ~p2;
short p4 = p3 - y;
return p4;
}
답지에 p4 = y-p3라고 써있는데, 오류로 판단된다.

Sol)
A. %rcx 레지스터를 전부 0으로 바꿔준다.
B. movq $0, %rcx로 대체할 수 있다. (movl $0, %ecx도 된다. 이게 더 빠르기도 하다)
C. 명령어 바이트 수는 안배웠는데, xorq %rcx, %rcx는 3B고, movq $0, %rcx는 7B이상이라고 한다.

Sol)
uremdiv:
movq %rdx, %r8
movq %rdi, %rax
xorl %edx, %edx
divq %rsi
movq %rax, (%r8)
movq %rdx, (%rcx)
ret
uremdiv는 remdiv와 다르게 %rax의 부호대호 확장하는 cqto가 필요없고, 그냥 %rdx를 0으로 채워주면 된다. 앞 연습문제에서 배운대로 %rdx를 0으로 채워야 하는데, 이 레지스터를 써야 하므로 저장된 unsigned long *xp 정보를 %r8에 복사하고 작업한다.


일단 문제에서 a가 %rdx에 있다고 하는데, %rdi에 있음이 맞아보인다.
Sol)
A. data_t : int
COMP : <
B. data_t : short
COMP : >=
C. data_t : unsigned char
COMP : <=
D. data_t : long, unsigned long, ptr
COMP : !=

Sol)
A. data_t : long
TEST : >=
B. data_t : short, unsigned short
TEST : ==
C. data_t : unsigned char
TEST : > 또는 !=
D. data_t : int
TEST : <=

Sol)
A. 4003fc
B.
'Computer Systems' 카테고리의 다른 글
| [Computer Systems] Chapter.2_Homework problems (0) | 2026.04.03 |
|---|