1. 서론
여러가지 기계어도 같은 언어의 사투리 정도로 보는 것이 타당하다.
명령어의 종류
ARM, MIPS, 인텔 x86
ARM 명령어란
2008년 기준 40억개 판매
레지스터 16개, 메모리 워드 2^30개
레지스터 하나의 크기는 32비트, 32비트 한덩어리가 워드
2. 하드웨어 연산
ARM 산술 명령어는 반드시 한 종류의 연산만 지시하며 항상 변수 세 개를 갖는 형식을 엄격히 지킨다.
하드웨어를 단순하게 하자는 원칙과 부합. 피연산자 개수가 가변적이면 하드웨어 복잡해짐
ex) ADD a,b,c
하드웨어 설계원칙
1. 간단하기 위해서는 규칙적인 것이 좋다.(산술명령어의 피연산자가 3개인 이유)
2. 작은 것이 더 빠르다.(레지스터의 개수가 16개인 이유)
3. 자주 생기는 일을 빠르게 하라.
4. 좋은 설계에는 적당한 절충이 필요하다(102p)
3. 피연산자
레지스터 개수가 16개 32비트로 한정
1.일반 레지스터를 가리키는 피연산자. : 그냥
2.메모리 피연산자
what is? 메모리주소를 나타내는 피연산자
why need? 배열이나, 구조체에 접근 할 때 쓰임
- 데이터 전송 명령어 : 메모리와 레지스터의 데이터를 주고 받는 명령어
ldr(Load word into register) : 메모리에서 레지스터로 데이터 복사 명령어
str(store word from register)
A[8], 배열 A의 시작주소가 r3에 기억되어있다고 할 때
-> [r3, #32] -> 32부분을 offset이라고 하고, r3를 베이스 레지스터라 한다.
* ARM에서 워드의 시작주소는 항상 4의 배수여야 한다.
* ARM은 최하위 바이트 주소를 워드 주소로 사용하는 little-endian 계열이다.
- 메모리란 주소가 인덱스 역할을 하는 커다란 1차원 배열이다.
- Register Spilling(스필링) : 자주 사용되지 않는 변수를 메모리에 넣어둠. 컴파일러가 알아서 해준다.
3.수치명령어
what is? #을 붙이면 그 다음에 나오는 숫자는 상수이다.
why need? 자주 생기는 일을 빠르게 하기 위해서. 예를 들어 for문에서 i의 증가 같은 경우..
4. 부호있는 수와 부호없는 수
숫자의 포현
기수 n? n진수
- 2의 보수를 십진수로 변환하는 두가지 방법
1. 시작부터 -1을 해주고 1->0, 0->1
2. 1->0, 0->1을 해주고 +1
5. 명령어의 컴퓨터 내부 표현( Intruction Formats)
명령어도 결국 전기신호로 저장되므로 숫자로 표현할 수 있다. 기계어
16진법에 대한 설명
명령어 형식 : 명령어의 레이아웃(32비트. 하드웨어 설계원칙 1번에 의거) : 규칙적이다.
1. 데이터 처리 명령어(DP)
산술명령어 : 4-2-1-4-1-4-4-12
cond. F I opcode S Rn Rd Operand2
4 2 1 4 1 4 4 12
- condition : 조건부 분기 명령어와 관계, 총 15가지 쓸수 있음(평소에는 1110(14))
- F :다른 명령어 형식을 사용할 수 있게 한다. 산술은 0, 메모리 명령어 형식이면 1
- I : Immediate 필드, 0이면 Source operand(Operand2)는 레지스터에, 1이면 12비트 수치값
- opcode : 연산자. 15개까지 가능
ADD는 4(0100), SUB는 2(0010)
- S : 조건부 분기와 관련
- Rn : First Source(근원지) 피 연산자 레지스터(레지스터는 16번까지 밖에 없으므로 2^4승)
- Rd : Destination 피연산자 레지스터. 연산결과 저장
- operand2 : Second Source 피 연산자. 레지스터일 수도 있고 12비트 수치값일 수도 있다.
2. 데이터 전송 명령어(DT)
메모리 이동 명령어(ldr, str) : 4-2-6-4-4-12
보통의 산술 명령어 형식 I와 S를 제외
cond. f Opcode Rn Rd Offset12
4 2 6 4 4 12
- F : 1이 된다.
- Opcode : 6비트로 바뀐다. I와 S가 Opcode와 통합
LDR은 24(011000), STR은 25(011001)
- Rn은 베이스 레지스터
- Offset12에는 Offset 12비트가.
6. 논리 연산 명령어
what is? 비트 연산을 위한 명령어
why need? 워드 내 일부 비트들에 대한 연산, 심지어는 개개 비트에 대한 연산도 필요하기 때문에
종류
AND : 1-1일때만 1
when? 특정부분만 추출해 내고 싶을 때 ex)서브넷 마스크
OR :
when? 특정부분만 변경하고 싶을 때
MVN(Move not) : MVN r5, r1
r1의 내용은 변경되지 않고 변경된 내용은 r5에 저장된다.
reg r5 = ~reg r1
MOV : MOV r5, r1
r1의 내용을 r5에 복사한다.(논리연산은 아님) 유용한 명령어.
LSL : Register Shift Left왼쪽 시프트
i비트 자리이동하면 2^i의 효과
LSR : Register Shift Right오른쪽 시프트
i비트 자리이동하면 2^-i의 효과
- 시프트 명령은 독립적으로 쓰일수는 없다.
대신 두번째 피연산자(Operand2, second source operand)를 이동시킬 수 있다.
Operand2 12비트 안에 이 값이 저장된다.
"★ex ) ADD r5, r1, r2, LSL #2" ; r5 = r1 + ( r2 << 2 )
ex ) MOV r6, r5, LSR #4 ; r6 = r5 >> 4
7. 판단을 위한 명령어
what is? 조건, 반복을 처리하기 위한 ARM 명령어
why need? 입력데이터나 연산 결과에 따라 다른 명령어를 실행하기 위해서
CPU에는 if-else 같은 건 없다. 무조건분기/조건부 분기만 있을 뿐이다.
종류
CMP r1, r2 두 값을 비교한다.
BEQ(Branch if Equal)
BNQ(Branch if Not Equal) 조건부 분기
B (Branch) : 무조건 분기
else:나 loop: label에 관한 간단한 소개
루프
ADD r12, r6, r3, LSL #2 ; r12 = address of save[i], i가 r3에 들어있음
c에서 봤을 때 i(r3)를 1씩 증가시키는 작업
Basic Block : 분기 명령어로 끝나는 시퀀스에게 붙는 별칭???
조건부 분기는 조건플래그 또는 조건 코드를 이용한다.(S필드)
'BGT, BGE -> 왼쪽이 오른쪽보다 크면, 크거나 같으면'
'BLT, BLE -> 왼쪽이 오른쪽보다 작으면, 작거나 같으면'
부호없는 비교 LO, LS, HI, HS
Case/Switch
ARM에서 프로그램 카운터(PC)는 r15 레지스터.
따라서 r15를 목적지로 하는 LDR명령은 무조건 분기를 의미한다.
ARM 분기 명령어 인코딩(난이도가 높다..)
원래 가장큰 명령어 주소 필드는 12비트이므로 프로그램은 그 이상 커질 수 없다.
2^12 -> 4096 바이트가 한계
그래서 cond.필드를 이용하여, 프로그램을 확장 가능하다.
PC-상대 주소 지정
조건부 실행(PPT 51p)
what is?분기를 제거하고자,
why need? 조건부 분기는 파이프라인 컴퓨터의 성능을 떨어뜨리므로, 편의성을 위해
ARM의 또 다른 특징. 거의 대부분의 명령을 조건부를 실행할 수 있다는 것
->대부분의 ARM 명령어 형식에 4비트 cond 필드가 포함되어 있다.
cond 필드를 이용하여 ADDEQ, SUBNE 등을 표현 14는 항상 실행
8. 하드웨어의 프로시져 지원
why? 이해하기 쉽고 재사용이 가능하도록 프로그램을 구조화하기 위해서.
스파이에 비교. 지정된 임무 외에는 건드리지 않는다. 임무완수하고 흔적을 지우고 돌아온다.
1. 프로시져가 접근할 수 있는 곳에 인수를 넣는다.
2. 프로시져로 제어를 넘긴다.
3. 프로시져가 필요로 하는 메모리 자원을 획득한다.
4. 필요한 작업을 수행한다.
5. 호출한 프로그램이 접근할 수 있는 장소에 결과값을 넣는다.
mov pc, lr(register)
6. 프로시져는 프로그램 내의 여러 곳에서 호출될 수 있으므로 원래 위치로 제어를 돌려준다.
lr : 호출한 곳으로 되돌아가기 위한 복귀 주소를 가지고 있는 링크 레지스터 한 개
bl : 지정된 주소로 점프하면서 동시에 다음 명령어의 주소를 lr 레지스터에 저장하는 명령
프로시져 종료 후 되돌아 올수 있도록 호출한 부분과 프로시져 사이에 링크를 혀엉한다.
proceduerAddress(함수의 시작주소)
함수가 끝나면 mov pc, lr을 사용해서 링크 레지스터가 가지고 있는 주소로 무조건 분기를 일으킨다.
스택 : 최상위 주소에서부터 시작해서 아래쪽으로 자란다.
최하위주소 : 사용이 유보되어 있다.
텍스트 : ARM 기계어 코드가 들어가는 부분
정적 데이터 세그먼트 : 상수와 기타 정적 변수 (배열 같은 경우 크기 고정)
동적 데이터 세그먼트 : heap
동적메모리 할당 : C에서 malloc() 힙에 공간을 할당한 후 이 공간을 가리키는 포인터를 결과값으로 보내준다.
free() 는 포인터가 가리키는 힙 공간을 반납한다.
사용이 끝난 공간을 반납하는 것을 잊어버리면 "메모리누출leak"이 발생하여 메모리부족이 일어날 수 있다.
9. 문자와 문자열
아스키 코드 : 8비트 바이트로 문자를 표현. 거의 모든 컴퓨터가 아스키를 사용한다.
대문자와 소문자의 차이 정확히
32(A:65, a:97)
아스키코드의 MSB 1bit는 패리티 코드이다. 따라서 표현 글자수는 2^7-1 = 127개 KSC-5601 : 한글 2바이트, 영어 1바이트 -> 용량계산이 어려워진다.
UNICODE : 본질은 character마다 4바이트 전세계 모든 문자 포함
요즘은 UTF-8 : 아스키는 8비트로 나타내고 다른 문자들은 16-32 사용
바이트 전송 명령어
why need? 워드 내의 특정 바이트를 추출할 수 있다.
텍스트 데이터를 다루는 프로그램이 많기 때문에 효율성을 위해서
아래 명령어들은 부호없는 수로 취급하여 0으로 확장하여 채운다.
LDRB(load register byte) : 메모리에서 한바이트를 읽어서 레지스터의 오른쪽 8비트에 채운다.
LDRB r0, [sp, #0]
STRB : 레지스터의 오른쪽 8비트를 메모리로 보낸다.
STRB r0, [r1, #0]
LDRH : 메모리에서 한바이트를 읽어서 레지스터의 오른쪽 16비트에 채운다.
STRH : 레지스터의 오른쪽 16비트를 메모리로 보낸다.
10. ARM의 32비트 수치를 위한 주소지정 및 복잡한 주소지정방식
주소지정방식(Addressing mode) why need? 프로그램을 실행하는데 필요한 명령어 개수를 최대한 줄이기 위해서
이것도 ARM의 철학이다. ex) 산술 피연산자의 자리이동, 조건부명령어, 수치값 회전
피연산자의 위치를 지정하는 방식 Operand2를 조작한다.
주소지정방식의 종류
기본적으로 pre는 먼저 사용한다음 연산한다. ++a
post는 연산한다음 증가시킨다. a++
1. 레지스터 변위 Register Offset
베이스레지스터 + 레지스터 연산
한 레지스터는 인덱스를, 다른 레지스터는 배열의 시작주소를 가지고 있을 때 편리
LDR r2, [r0, r1]
2. 스케일된 레지스터 변위 Scaled Register Offser
레지스터를 먼저 자리이동한 후 베이스 레지스터에 더한다.
배열 인덱스를 2비트 자리이동해서 바이트 주소로 변환하는데 유용
LDR r2, [r0, r1, LSL #2]
3번이하로는 베이스 레지스터를 새 주소로 갱신한다. **3. 수치 변위 프리인덱스 Immediate Offset Pre-Indexed
베이스 레지스터는 메모리 접근에 사용했던 주소로 변하게 된다.
배열을 순차적으로 접근할 때 유용
LDR r2, [r0, #4]!
4. 수치 변위 포스트 인덱스 Immediate Offset Post-Indexed
3번과 똑같지만 가능한 모든 경우를 다 지원하기 위해서 제공
프리인덱스나 포스트인덱스 중 하나만 사용하면 됨.
LDR r2, [r0], #4
5. 레지스터 변위 프리인덱스 Register Offset Pre-Indexed
3번과 같으나 상수 대신 레지스터값을 더하거나 뺀다.
LDR r2, [r0, r1]!
6. 스케일된 레지스터 변위 프리인덱스 Scaled Register Offset Pre-Indexed
5번과 같으나 더하거나 빼기전에 자리이동을 한다.
LDR r2, [r0, r1, LSL #2]!
7. 레지스터 변위 포스트인덱스
4번과 같으나 상수대신에 레지스터값을 더하거나 뺀다.
LDR r2, [r0], r1
피연산자의 주소 지정방식
1. 수치값 주소 지정방식 add r2, r0, #5
2. 레지스터 주소 지정방식 add r2, r0, r1
3. 스케일된 레지스터 주소 지정방식 add r2, r0, r1, LSL #2
4. PC 상대 주소 지정방식 BEQ 1000
PC의 값에서 +1000값만큼 점프하자.
why need? 프로그램과 함께 메모리에 저장된 32비트 상수를 인출하기 위함
11. 병렬성과 명령어 : 동기화
12. 프로그램 번역과 실행 Compiler : C원시코드를 어셈플리 언어 프로그램으로 바꾼다.(.asm)
.asm 파일은 typically linking 후 순식간에 삭제된다.
Assembler : 어셈블리 프로그램을 Object file(기계어모듈)로 바꾼다.
기계어 모듈로 만들어주는데 메모리에 적재하기에는 완벽하지 않다.
Linker : 라이브러리에 있는 다른 목적파일을 가져와서 obj 파일들을 합친다.
이과정이 끝나면 디스크에 Exe 파일이 완성된다.
이걸 메모리에 주기 위해 loader를 사용하여 메모리에 올려 수행한다.
* 자바프로그램의 실행
1. 자바는 어셈블리언어로 컴파일을 하지 않고 인터프리트 하기 쉬운 바이트코드로 먼저 컴파일한다. -> 여기에서 javac가 사용.
2. JVM 소프트웨어 인터프리터가 바이트코드(JVM의 명령어 집합)를 실행한다. -> 독립적플랫폼
자바는 별도의 어셈블리 단계가 필요없다.
자바의 목적은 실행시간은 느리더라도 어느 컴퓨터에서나 안전하게 실행시킬 수 있게 하자.
자바의 장점은 이식성. 단점은 성능이 낮다. C보다 10배 정도 느림 따라서 이식성을 훼손하지 않으면서 성능을 개선하기 위해 실행되는 도중에 번역을 하는 컴파일러 제작 그것이 JIT(Just In Time) : 많이 사용되는 메소드를 찾아내서 실행중에 기계어로 컴파일한다.