<명령어 표현>
■ 챕터 12에서, 우리는 instruction set이 무엇을 하는지에 대하여 초점을 두었다.
상세하게, Operand의 종류와 기계 명령어에 의해서 구체화 될 수 있는 동작에 대해 조사했다.
■ Opcodes(연산자)는 mnemonics라고 불리는 약자에 의해 표현된다.
-> 데이터 전송
-> 산술적 연산 (+,- ...)
-> 논리적 연산 (and, or, xor ...)
-> 변환 (rotate...)
-> I/O
-> 시스템 제어 (branch ...)
-> 제어의 전송
■ Operands(연산부) 또한 symbolically하게 표현된다.
-> 메인 혹은 가상(virtual) 메모리
-> I/O 장치
-> 프로세서 레지스터
-> 즉치 데이터(Immediate data)
<개요>
■ 이번 챕터는 operand와 명령어의 동작을 어떻게 구체화하는지에 대해 알아볼 것이다.
■ 두 가지 주제가 있다.
-> Addressing modes : operand의 주소를 어떻게 구체화하는가?
-> 명령어 형식
operand address와 명령어의 동작부분을 정의하기 위해 명령어의 비트를 어떻게 정의하는가?
//명령어와 연산자를 binary format으로 표현하기 위한 약속 → 기계어(Machine language)
<13.1 Addressing Modes>
■ 전형적인 명령어 형식에서 address field 혹은 fields는 비교적 작다. 우리는 메인메모리 혹은 가상 메모리 같은 어떤 시스템에서 큰 영역의 공간을 참조할 수 있어야 한다.
//나타낼 수 있는 번지의 수는 비교적 작은데, 실제 메모리 영역은 크다.
■ 이런 목표를 달성하기 위해, 다양한 addressing 기법이 채택된다.
■ 그들은 한편으로 address range와 addressing flexibility 사이의 trade-off를 포함한다, 다른 부분에서는 명령어안에 메모리 참조의 수와 주소계산의 복잡도의 trade-off도 포함한다.
■ 가장 흔한 addressing 기법 혹은 모드는 :
1. Immediate
2. Direct
3. Indirect
4. Register
5. Register indirect
6. Displacement
7. Stack
<Addressing Modes>
//EA는 실제 Operand가 있는 주소, (X)는 번지 X에 해당하는 내용
//immediate addressing 모드는 주소를 갈 필요가 없이 immediate data가 이미 명령어 안에 있다.
//그 immediate data는 code memory에 같이 존재한다. 비교적 명령어 수행이 빠르다.
//별도에 fetch가 필요 없다.
//Register addressing 모드는 명령어에 register 이름을 가지고 있다.
//register file을 가서 register 이름에 해당하는 데이터(operand)를 찾는다.
//Immediate 모드보단 느리지만, 메인 메모리를 갈 필요 없이 register file에서 접근하므로 비교적 빠름
//Stack addressing 모드는 stack register를 찾아간다.
//메모리를 찾아가는 시간보단 빠르다.
//나머지 녀석들은 메모리로 접근하는 경우, cpu 바깥에 있다.
//direct addressing 모드는 명령어 안에 메모리의 번지(위치)가 있다. 그 번지에 operand가 있다.
//indirect addressing 모드는 명령안에 메모리의 번지의 번지가 있다. 즉 2번 뒤진다.
//명령어 상에 표현 가능한 비트가 제한적일 때, 나누어 할당한다??
//제일 수행속도가 느리다.
//Register indirect addressing 모드는 명령어 안에 register 이름이 있다.
//그 register 파일에는, 해당되는 메모리 번지를 가지고 있다. 수행속도가 direct보다 느리고, indirect보다 빠르다.
//displacement(변이) addressing 모드는 명령어 안에 register의 이름, offset도 가지고 있다.
//그 것을 연산하여 메모리에 해당 operand를 찾아간다.
//indirect 보단 낫지만 indirect register 모드 보단 못하다.
<Table 13.1 Basic Addressing Modes>
■ 주소계산은 각 addressing mode에 수행된다.
■ Comment-1 : addressing mode 설정 방법
-> 임의 opcode는 특정 addressing mode를 사용하거나, mode field를 사용하는 방법
■ Comment-2 : effective address (EA) 해석 방법
-> virtual memory가 없는 경우, EA는 main memory address 또는 a register이다.
-> virtual memory인 경우, physical address로의 actual mapping은 memory management unit(MMU)에 의하여 수행되지만, 프로그래머는 알 수 없다.
<13.1.1 Immediate Addressing>
■ 가장 간단한 형식의 addressing : Operand = A
■ 이 모드는 상수를 정의, 사용 혹은 변수의 초기 값을 설정하여 사용된다.
-> 전형적으로 숫자가 2의 보수 형식으로 저장된다.
-> operand 영역에 leftmost bit은 sign bit로 사용된다.
sign bit는 왼쪽에서 full data word size만큼 연장된다. 어떤 경우에, immediate binary 값은 unsigned non-negative integer로 해석된다.
■ 장점
-> operand를 가져오기 위해서 명령어 fetch를 하는 다른 명령어들에 비하면 메모리 레퍼런스가 없다.
그러므로, 한 메모리 혹은 명령어 사이클 안에 cache cycle을 아낄 수 있다.
//메모리 레퍼런스가 없다.
■ 단점
-> 숫자 크기가 address 영역 사이즈만큼 제한된다. 그것은 대부분 명령어 set에서, word 길이보다 작다.
//사이즈의 제한
//2의 보수 표현 범위??
<13.1.2 Direct Addressing>
■ Address 영역은 effective operand의 주소를 포함한다.
-> EA = address field A
■ 이 기법은 컴퓨터의 초기 세대에서 흔하게 사용되었다. 그러나 현대 구조에서는 거의 볼 수 없다.
■ 오직 한 개의 메모리 레퍼런스를 필요로 하고, 특별한 계산이 없다.
■ 한계점은 제한된 주소공간을 제공한다는 점이다.
//명령어의 공간으로는 메모리의 일부분만 나타낼 수 있다.
<13.1.3 Indirect Addressing>
■ operand의 full-length를 가진 메모리에 있는 word의 주소 레퍼런스
■ EA = (A)
-> ()는 ~의 내용이라는 의미로 해석된다.
■ 장점 : N의 word 길이에 대하여, 2^n개의 주소 공간이 사용가능하다.
■ 단점
-> 명령어 실행이 operand를 불러오는데 2개의 메모리 레퍼런스를 필요로 한다.
- 하나는 그것의 address를 가지고, 두 번째는 그것의 값을 가진다.
■ 드물게 사용되는 indirect addressing의 변종은 멀티레벨 혹은 연속된 indirect addressing이다.
-> EA = ( . . . (A) . . . )
-> 단점으로는 Operand를 불러오는데 3개 이상의 메모리 레퍼런스가 필요하다.
<13.1.4 Register Addressing>
■ Address 영역이 메인 메모리의 주소가 아니라 레지스터의 주소로 언급된다.
■ EA = R
■ 장점
-> 명령어에서 작은 비트 수의 주소 형식만 있어도 된다.
-> 메모리 레퍼런스할 때보다 시간을 덜 쓴다.
■ 단점 : 주소 공간이 매우 제한적이다. //RISC에선 좀 의미 있으나, CISC에는 의미 없다.
<13.1.5 Register Indirect Addressing>
■ indirect addressing과 유사하다.
-> 차이점은 오직 address 영역이 메모리 공간을 언급하는지, 레지스터를 언급하는지다.
■ EA = (R)
■ 주소 영역에서 주소 공간의 한계는 주소를 포함하고 있는 word-길이의 공간을 언급하는 영역을 가짐으로써 극복한다.
■ indirect addressing 보다 한 개 적은 메모리 레퍼런스를 사용한다.
<13.1.6 Displacement Addressing>
■ 가능성의 조합 -> direct addressing과 register indirect addressing
■ EA = A + (R)
■ 명령어가 두 개의 분명하게 한 개 이상인 주소 영역을 필요로 한다.
-> 한 개의 주소 영역(value = A)안에 포함된 값이 직접 사용된다.
-> 다른 주소 영역은 레지스터를 언급한다. effective address를 만들기 위해서 그 레지스터의 내용이 A에 더해진다.
■ 대부분 흔히 사용되는 것들 //기준에 따라 세 가지로 나뉜다.
1. Relative addressing
2. Base-register addressing
3. Indexing
<13.1.6.1 Relative Addressing [PC-Relative]> //PC값을 기준으로
■ 내포되어 언급되는 register는 program counter(PC)다.
■ EA = A + (PC)
-> 다음 명령어 주소는 EA를 생산하기 위해 주소 영역에 더해진다.
-> 전형적으로 이 동작에서 주소 영역은 2의 보수로 다뤄진다.
-> 그래서, effective address는 명령어의 주소와 관련된 displacement다.
<13.1.6.2 Base-Register Addressing> //외부적으로 내부적으로 기준을 지정가능
■ 레퍼런스된 레지스터는 메인메모리 주소를 포함하고, 주소 영역은 그 주소로부터 displacement를 포함
■ 레지스터 레퍼런스는 아마도 explicit 혹은 implicit하다.
■ 메모리 레퍼런스의 지역성을 활용한다.
■ segmentation을 실행하는 편리한 수단
■ 어떤 실행에서 single segment base 레지스터가 채택되고, implicitly하게 사용된다.
■ 다른 방법으로, 프로그래머가 세그먼트의 base 주소를 잡기 위해 레지스터를 선택 할 수 있다.
그리고 명령어가 반드시 그것을 explictly하게 레퍼런스해야 한다.
<13.1.7 Stack Addressing> //PUSH POP 명령어에 해당하는 모드
■ 스택은 공간의 연속된 배열이다.
-> 때때로 pushdown list로 언급되거나 last-in-first-out queue로 언급된다.
■ Stack은 공간의 보존된 블록이다.
-> 아이템이 stack의 꼭대기에 추가 되어, 블록이 부분적으로 채워진다.
■ stack과 연합된 포인터는 그 값이 stack의 꼭대기의 주소다.
-> stack 포인터는 레지스터 안에서 유지된다.
-> 그래서 메모리에서 stack 공간의 레퍼런스는 사실 register indirect address다.
■ 이것은 implied addressing 형식이다.
■ 기계어는 메모리 레퍼런스를 포함하는 것을 필요로 하지 않는다. 그러나 내부적으로 stack의 꼭대기에서 실행한다.
<13.2.1 x86 Addressing Modes>
//x86 Addressing Mode가 복잡한 이유 -> 패밀리 개념 때문이다.
//Effective address = linear address
<ARM Addressing Modes> ★
//Displacement Addressing Mode가 branch 부분이다.
//Data Processing Mode는 2가지, Branch Mode는 PC-relative addressing mode 1가지 ??
<Data Processing : A Group 명령어> //3개의 operand
■ 기본 형식 : op Rd, Rn, <opnd1>
-> op는 동작 이름이며, Rd는 동작의 결과가 저장되는 목적지(destination) 레지스터, Rn은 동작의 대상이 되는 처음 출발지(source) 레지스터이다. opnd1은 동작의 대상이 되는 두 번째 출발지 값인데, 상수 또는 레지스터이다.
■ 예제
-> immediatae : add r0, r1, #20
= r1의 내용에 20을 더해서 결과를 r0에 저장한다.
-> register : add r0, r1, r2
= r1 내용에 r2 내용을 더해서 결과를 r0에 저장한다.
-> shift by immediate : add r0, r1, r2, lsl #3
= r1 내용에 r2 레지스터를 좌측 논리 쉬프트 3회 실시한 내용을 더해서 결과를 r0에 저장한다.
-> shift by register : add r0, r1, r2, lsl r3
= r1 내용에 r2 레지스터를 r3 내용만큼 좌측 논리 쉬프트한 내용을 더해서 결과를 r0에 저장한다.
-> rotate right extend : add r0, r1, r2, rrx
= r1 내용에 r2 레지스터를 C 플래그를 경유하는 우측 회전을 한 번 했을 때 얻어진 내용을 더해서 결과를 r0에 저장한다.
//RISC는 명령어가 많지 않기 때문에 위와 같은 형식으로 모아쓰지 않고 분리해서 쓰면, 수행시간이 길다.
<Data Processing : B Group 명령어> //2개의 operand
■ 기본 형식 : op Rd, <opnd1>
-> 세 개의 오퍼랜드를 갖는 소구분 A의 명령과 달리 소구분 B의 명령들은 두 개의 오퍼랜드를 갖는다.
OP는 동작 이름이며, Rd는 동작의 결과가 저장되는 목적지 레지스터, opnd1은 동작의 대상이 되는 출발지
■ 예제
-> Immdiate : mov r0, #20
= 20을 r0에 저장한다
-> Register : mov r0, r1
= r1 내용을 r0에 저장한다.
-> shift by immediate : mov r0, r1, lsl #3
= r1 레지스터를 r2 내용만큼 좌측 논리 쉬프트한 내용을 r0에 저장한다.
-> shift by register : mov r0, r1, lsl r2
= r1 레지스터를 r2 내용만큼 좌측 논리 쉬프트한 내용을 r0에 저장한다.
-> rotate right extend : mov r0, r1, rrx
= r1 레지스터를 C 플래그를 경유하는 우측 회전을 한 번 했을 때 얻어진 내용을 r0에 저장한다.
<Data Processing : C Group 명령어> //결과는 플래그에 저장된다.
■ 기본 형식 : op Rn, <opnd1>
-> 소구분 C의 명령에서는 두 개의 오퍼랜드 Rn과 opnd1를 서로 비교하고, 비교결과는 상태 레지스터의 플래그에 저장되기 때문에 별도의 Rd 레지스터가 필요하지 않은 것이다.
■ 예제
-> Immediate : cmp r0, #20
= r0 내용과 20을 비교한다.
-> register : cmp r0, r1
= r0 내용과 r1 내용을 비교한다.
-> register : cmp r0, r1
= r0 내용과 r1 내용을 비교한다.
-> shift by immediate : cmp r0, r1, lsl #3
= r0 내용과 r1 레지스터를 좌측 논리 쉬프트 3회 실시한 내용을 비교한다.
-> shift by register : cmp r0, r1, lsl r2
= r0 내용과 r1 레지스터를 r2 내용만큼 좌측 논리 쉬프트한 내용을 비교한다.
-> rotate right extend : cmp r0, r1, rrx
= r0 내용과 r1 레지스터를 C플래그를 경유하는 우측 회전을 한번 했을 때 얻어진 내용을 비교한다.
<Branch 명령어>
■ 기본형식 : op <offset24>
-> op는 동작 이름으로 b 또는 bl이다. b는 무조건 분기이며, bl은 branch-and-link, 즉 리턴 어드레스를 lr 레지스터에 저장하고 분기하는 명령이다.
-> 조건 분기를 하기 위해서는 다음 조건 코드를 사용한다.
-> 분기명령을 만나면 현재 실행 위치에서 offset24 만큼 떨어진 위치로 점프한다. 즉 이 명령에서 사용되는 어드레싱 모드는 PC-relative 모드이다.
-> offset24는 24비트 크기이므로 16MB, 즉 현재 위치에서 ±8MB 만큼 떨어진 위치를 지정할 수 있는 크기인데, 모든 ARM 명령 크기는 4바이트로 고정되어 있기 때문에 실제로는 이 값에 4를 곱한 값, 즉 ±32MB만큼 떨어진 위치를 지정할 수 있다.
//2^24 = 2^4 * 2^20 = 16MB = -2^8 ~ 2^8
■ 예제
-> ARM 프로세서는 명령을 실행할 때 프로그램 카운터(PC)는 현재 위치보다 8만큼 증가한 값을 갖도록 설계되어 있다. 예를 들어 메모리 100번지에 다음 명령이 있다고 하자.
//100
//104
//108
-> B 3
- 이 명령이 실행될 당시 PC는 100 + 8 = 108을 가리키고 있으므로, 실제로 분기할 위치는 108 + (3*4) = 120번지가 된다.
//Branch 할 때 당시 PC는 +8을 해준 상태에서 떨어진 offset*4를 더해줘야 한다
< Load/Store 명령어: A Group 명령어 > //word(4byte)아니면 byte다
■ 소구분 A 형식 : op Rd, <opnd21>
-> op는 동작 이름, Rd는 적재 또는 저장 대상 레지스터이다. op가 적재(Load) 명령일 때는 메모리의 내용이 Rd 레지스터로 옮겨지고, op가 저장 명령(Store)일 때는 Rd 레지스터 내용이 메모리로 옮겨진다. 즉 Rd는 op에 따라 목적지 오퍼랜드 일수도 있고 출발지 오퍼랜드 일수도 있다.
-> 소구분 A에 속하는 명령들은 워드, 즉 4바이트 크기의 데이터를 옮기거나, 1바이트 크기의 데이터를 옮기는데 사용된다. 워드 크기의 데이터를 옮기는 명령은 ldr, str이며, 바이트 크기의 데이터를 옮기는 명령어는 ldrb, strb이다. ldrb는 메모리에 있는 바이트 크기 데이터를 가져와서 Rd의 하위 1바이트에 넣고 나머지 3바이트는 0으로 채운다. strb는 Rd의 하위 1 바이트만 메모리에 저장하고 나머지 3바이트 는 고려하지 않는다.
<Load/Store 명령어>
■ 예제
-> immediate offset : ldr r0, [r1, #20]
= r1에 20을 더한 번지에 있는 메모리 내용을 r0로 가져온다.
//r1값은 바뀌지 않는다.
-> immediate pre-indexed : ldr r0, [r1, #20]!
= r1값은 원래 값보다 20만큼 증가한 값으로 변한다. r1 번지에 있는 메모리 내용을 r0로 가져온다.
//r1값을 20만큼 증가한 값으로 바꾸고 r0에 넣는다.
-> immediate post-indexed : ldr r0. [r1], #20
= r1 번지에 있는 메모리 내용을 r0로 가져온다. r1 값은 원래 값보다 20만큼 증가한 값으로 변한다.
//r1을 r0에 넣고 나서, r1의 값을 20만큼 증가 시킨다.
-> register offset : ldr r0, [r1, r2]
= r1에 r2를 더한 번지에 있는 메모리 내용을 r0로 가져온다.
//r1과 r2 자체의 값은 변하지 않는다.
-> register pre-indexed : ldr r0, [r1 r2]!
= r1 값은 원래 값보다 r2 만큼 증가한 값으로 변한다. r1 번지에 있는 메모리 내용을 r0로 가져온다.
//r1 값을 r2만큼 증가시킨 다음, 그 값을 r0에 넣는다.
-> register post-indexed : ldr r0, [r1], r2
= r1 번지에 있는 메모리 내용을 r0로 가져온다. r1 값은 원래 값보다 r2 만큼 증가한 값으로 변한다.
//r1 값을 r0에 넣고나서, r1 값을 r2만큼 증가시킨다.
<Load/Store 명령어 : B Group 명령어>
■ 소구분 B 형식 : op Rd, <opnd22>
-> 소구분 B에 속하는 명령들은 하프워드(halfword), 즉 2바이트 크기의 데이터를 옮기거나, 1바이트 크기 의 데이터를 옮기거나, 1바이트 크기의 데이터를 옮기는데 사용된다. 오퍼랜드로 사용되는 Rd의 크기는 4바이트이므로 하프워드 또는 바이트 크기의 데이터를 옮길 때는 서로 크기가 같지 않으므로 크기를 맞춰 주어야 할 필요가 있다.
-> 메모리에 있는 하프워드 크기의 데이터를 가져올 때는 ldrh 또는 ldrsh 명령을 사용한다. ldrh는 데이터 를 가져와서 Rd의 하위 2바이트에 넣고 상위 2바이트는 0으로 채운다. 반면 ldrsh는 데이터를 가져와서 4바이트로 부호 확장하여 Rd에 넣는다.
//ldrh는 나머지 2비트를 0으로 채우지만, ldrsh는 자신의 sign bit로 확장한다.
-> 반대로 하프워드 크기의 데이터를 메모리에 저장할 때는 strh 명령을 사용한다. strh는 Rd의 하위 2 바이트를 메모리에 저장한다. Rd의 상위 2바이트는 고려하지 않는다. strsh와 같은 명령은 존재하지 않는다.
-> 또한 메모리에 있는 바이트 크기의 데이터를 가져올 때는 ldrsb 명령을 사용한다. 이 명령은 메모리에 있는 1바이트 데이터를 가져와서 4바이트로 부호 확장하여 Rd에 넣는다. 소구분 A에 속한 ldrb 명령과 구별해야 한다. ldrb는 부호 확장 없이 Rd의 상위 3바이트를 0으로 채워 넣는다.
-> 바이트 크기의 데이터를 메모리에 저장할 때는 소구분 A에 속한 strb 명령을 사용한다. strsb와 같은 명령어는 존재하지 않는다.
<Data Processing and Branch Instruction Addressing @ ARM>
■ Data processing instructions addressing
-> register addressing 혹은 reigster와 immediate addressing의 혼합형을 사용한다.
-> register addressing에 대하여, 한 개의 register operands안에 값은 5개의 shift operator중 한 개를 사용하여 scale 될 수도 있다.
■ Branch instructions addressing
-> branch instruction에 대한 addressing의 형식은 오직 immediate이다.
-> 명령어는 24비트 값을 가진다.
- 2비트를 왼쪽으로 쉬프트해서, 주소가 word의 경계에 오도록 한다.
- Effective range는 PC로부터 ± 32MB
//±8MB * 4 = ±32MB
<13.2.2 Load/Store Instruction Addressing @ARM>
■ 메모리를 참조하는 유일한 명령어
■ base register에 offset을 더하여 indirectly하게 접근한다.
1. Offset : Offset은 base register 내용에서 memory address 형식으로 더해지거나 빼진다.
//200 + C
2. Preindex
-> 메모리 주소는 offset addressing에 대해 생성된다.
-> 메모리 주소는 또한 base register에도 쓰인다.
-> 따라서 base register 값은 offset 값에 의해 증가되거나 감소된다.
//r1이 이미 바뀌고, 나서 주소를 찾아간다.
3. Postindex
-> 메모리 주소는 base register 값이다.
-> Offset이 더해지거나 빼진다. 결과 값이 base register에 다시 써진다.
//r1을 r0에 넣고 나서, 값을 변경한다.
//이렇게 명령어를 함축적으로 표현해서, 수행시간을 절감한다.
■ Base register는 preindex와 postindex addressing에서 index register 같이 행동한다.
■ offset은 명령어의 immediate 값 혹은 다른 레지스터에 있다.
■ 만약 레지스터 scaled register addressing이 가능하다면
-> Offset register 값이 shift 연산자에 의해 scaled 된다.
-> 명령어가 shift의 크기를 구체화한다.
<ARM Load/Store Multiple Addressing>
■ Load/store subset of general-purpose registers
■ 16-bit instruction field specifies list of registers
■ Sequential range of memory addresses
■ Increment after, increment before, decrement after, and decrement before
■ Base register specifies main memory address
■ Incrementing or decrementing starts before or after first memory access
<13.3 Instruction Formats> //그 명령어의 비트가 어떤 형식으로 저장되나
■ Instruction format은 그것을 구성하는 영역의 관점에서 명령어 비트의 레이아웃을 정의한다.
■ Instruction format은 반드시 opcode 그리고, 내부적으로 혹은 외부적으로 0개 이상의 operand를 가져야 한다.
■ 각 외부 operand는 section 3.1에서 묘사된 addressing mode중 한 개를 사용하여 참조된다.
그 형식은 반드시, 외부적 혹은 외부적으로, 각 operand에 대한 addressing mode를 나타내야 한다.
■ 대부분의 명령어 set에서, 한 개 이상의 명령어 형식이 사용된다.
■ 명령어 형식의 디자인은 복잡한 일이다, 그리고 엄청나게 다양한 디자인이 실행되어져 오고 있다.
■ 우리는 핵심 디자인 이슈를 조사할 것이다. 간단하게 핵심을 설명하는 디자인들을 보고, x86과 ARM 해결법을 자세하게 조사할 것이다.
<Instruction Length>
■ 가장 기본 디자인 항목
■ 다음과 같은 것들에 의해 영향을 받거나, 준다.
-> 메모리 사이즈
-> 메모리 구조
-> 버스 구조
-> 프로세서 복잡도
-> 프로세서 속도
//Fixed Length = RISC(ARM)
//Variable Length = CISC(Intell)
■ 메모리 전송길이와 같거나 다른것의 배수가 되어야 한다.
■ 보통 8비트인 문자 길이와 fixed-point 숫자 길이의 배수가 되어야한다.
<Allocation of Bits>
■ Number of addressing modes
-> 때때로 addressing mode는 내부적으로 표시될 수 있다. 예를 들면, 특정 opcodes가 아마도 항상 indexing을 찾을 때, 다른 케이스로는, addressing mode가 반드시 외부적이야 할 때, 그리고 한 개 이상의 모드 비트가 필요하다.
■ Number of operands
-> 우리는 더 적은 수의 주소가 더 길고, 더 이상한 프로그램 (예시. Figure 10.3)을 만든다는 것을 봐왔다.
오늘날 머신들의 전형적인 명령어 형식은 2개의 operand를 가지고 있다. 명령어 안에 각 operand 주소 는 아마도 자신의 모드를 나타내는 것을 필요로 할 것이다. 혹은 모드를 나타내는 것의 활용이 단지 하나의 주소 영역에 제한될 수도 있다.
■ 레지스터 vs 메모리
-> 머신은 반드시 레지스터를 가져야한다. 그래야 데이터가 프로세서 안으로 들어와서 데이터 처리를 할 수 있기 때문이다. single user-visible 레지스터와 함께(보통 accumulator로 불린다), 한 개의 operand는 implicit 하고 명령어 비트를 차지하지 않는다. 그러나 single-register 프로그래밍은 이상하고, 많은 명령어를 필요로 한다. 심지어 여러개의 레지스터도, 레지스터를 구체화하는데, 매우 적은 비트가 사용 된다. 레지스터가 더 많이 operand 레퍼런스로 사용될수록, 더 적은 비트가 필요하다. 수많은 연구가 총 8 to 32 user-visible register가 바람직하다고 나타내고 있다. [LUND77, HUCK83]. 거의 대부분의 현대 아키텍처는 적어도 32개의 레지스터를 가지고 있다.
■ Number of register sets
Most contemporary machines have one set of general- purpose registers, with typically 32 or more registers in the set. These registers can be used to store data and can be used to store addresses for displacement addressing. Some architectures, including that of the x86, have a collection of two or more specialized sets (such as data and displacement). One advantage of this latter approach is that, for a fixed number of registers, a functional split requires fewer bits to be used in the instruction. For example, with two sets of eight registers, only 3 bits are required to identify a register; the opcode or mode register will determine which set of registers is being referenced.
■ Address range
For addresses that reference memory, the range of addresses that can be referenced is related to the number of address bits. Because this imposes a severe limitation, direct addressing is rarely used. With displacement addressing, the range is opened up to the length of the address register. Even so, it is still convenient to allow rather large displacements from the register address, which requires a relatively large number of address bits in the instruction.
■ Address granularity //Displacement가 되느냐??
For addresses that reference memory rather than registers, another factor is the granularity of
addressing. In a system with 16- or 32-bit words, an address can reference a word or a byte
at the designer’s choice. Byte addressing is convenient for character manipulation but requires,
for a fixed-size memory, more address bits.
<Variable-Length 명령어>
■ Variations can be provided efficiently and compactly
■ Increases the complexity of the processor
■ Does not remove the desirability of making all of the instruction lengths integrally related to
word length
-> Because the processor does not know the length of the next instruction to be fetched a
typical strategy is to fetch a number of bytes or words equal to at least the longest possible
instruction
-> Sometimes multiple instructions are fetched
<13.4 x86 명령어 형식>
//명령어의 형식은 가변적이다. (Variable length)
<ARM 명령어 형식>
■ All instructions in the ARM architecture are 32 bits long and follow a regular format
(Figure 13.10). //기본적으로 32bits 길이다.
1. The first four bits of an instruction are the condition code. As discussed in Chapter 12,
virtually all ARM instructions can be conditionally executed.
2. The next three bits specify the general type of instruction.
3. For most instructions other than branch instructions, the next five bits constitute an opcode
and/or modifier bits for the operation.
4. The remaining 20 bits are for operand addressing.
■ The regular structure of the instruction formats eases the job of the instruction
decode units.
<ARM 명령어 형식> //기본적으로 32비트
//ARM은 인텔과 다르게 fixed length이다.
<ARM Immediate Constants의 활용 예시>
■ ROR 값에 따른 immediate 값 설정
-> to achieve a greater range of immediate values, the data processing immediate format specifies both an immediate value and a rotate value. The 8-bit immediate value is expanded to 32 bits and then rotated right by a number of bits equal to twice the 4-bit rotate value. Several examples are shown in Figure 13.11.
<Thumb ADD 명령어와 ARM 명령어의 동일 표현 예>
//Thumb 명령어는 16비트, ARM이 down scale 했음
//32비트와 16비트를 함께 섞어 놓은 것이 Thumb-2 명령어
<Thumb-2 명령어 set 개요>
//ARM이 16비트 Thumb 명령어, 32비트 명령어, 섞어놓은 Thumb-2 명령어를 만들었다.
■ The only instruction set available on the Cortex-M microcontroller products
■ Is a major enhancement to the Thumb instruction set architecture (ISA)
-> Introduces 32-bit instructions that can be intermixed freely with the older 16-bit Thumb
instructions
-> Most 32-bit Thumb instructions are unconditional, whereas almost all ARM instructions can
be conditional
-> Introduces a new If-Then (IT) instruction that delivers much of the functionality of the
condition field in ARM instructions
■ Delivers overall code density comparable with Thumb, together with the performance levels
associated with the ARM ISA
■ Before Thumb-2 developers had to choose between Thumb for size and ARM
for performance
<Thumb-2 명령어 Set 성능> //ARM Thumb-2 Thumb 성능, 사이즈 비교
■ [ROBI07] 비교: Thumb-2, ARM and original Thumb instruction sets.
■ Embedded Microprocessor Benchmark Consortium (EEMBC) benchmark에 대하여 코드 사이즈와
성능을 비교
■ 코드 사이즈 비교
-> With compilers optimized for performance, Thumb-2 size was 26% smaller than ARM, and
slightly larger than original Thumb.
-> With compilers optimized for space, Thumb-2 size was 32% smaller than ARM, and slightly smaller than original Thumb.
//성능(수행시간)은 ARM, Thumb-2, Thumb 순으로 좋다(수행시간이 작다)
//코드의 길이, 프로그램의 크기는 Thumb2, Thumb, ARM 순으로 작다. Thumb2가 Thumb보다 좋다.
■ 성능 비교 //마이크로 프로세서 관점이다.
-> With compilers optimized for performance, Thumb-2 performance on the benchmark suite
was 98% of ARM performance and 125% of original Thumb performance.
<13.5 Assembly Language>
■ 프로세서는 기계명령어를 이해하고 실행할 수 있다. 이런 명령어는 간단한 이진 숫자로 컴퓨터에
저장된다.
//어셈블리어의 기본 형식
//라벨, 니모닉, 오퍼렌드들, comment
//로 구성된다.
<13.5 Assembly Language>
■ 어셈블리 언어의 장점, 어셈블리 언어를 이해함으로써, 제공되는 지식은 다음과 같다.
-> OS를 가진 프로그램의 인터페이스, 프로세서와 BIOS;
-> 메모리와 다른 외부 장치의 데이터 표현
-> 프로세서가 명령어에 어떻게 접근하고 실행하는지
-> 명령어가 데이터에 어떻게 접근하고 처리하는지
-> 프로그램이 외부 장치에 어떻게 접근하는지
■ 어셈블리 언어를 사용함으로써 얻는 다른 장점들은 다음과 같다.
-> 더 적은 메모리 소모량과 실행 시간을 필요로한다.
-> 하드웨어-specific complex job을 더 쉽게 하도록 해준다.
-> time-critical jobs에 적절하다.
-> 인터럽트 서비스 루틴과 다른 메모리 거주 프로그램을 쓰는데 적합하다.
<Addressing modes for Data Processing Operand (즉,op1)>
■ Unmodified Value
-> 이 addressing mode는, 레지스터 혹은 값이 unmodified하게 즉,어떠한 shift 혹은 rotation도
없이 주어진다.
-> 예시, MOV R0, #1234H //이 명령은 즉치 상수 1234 값을 레지스터 R0로 이동시킨다.
■ Modified Value
-> 이 addressing mode는, 주어진 값 혹은 레지스터가 shift 되거나 rotate 된다.
-> 아래 예시로 나열된 shift와 rotate 동작들은 종류가 서로 다르다.
-> (1) Logical shift left, 이것은 레지스터의 값을 가지고, most Significant bits 쪽으로 그 값만큼 n-bits 만큼 shift 한다는 것이다.
-> 예시, MOV R0, R1, LSL #2 //명령어의 실행이 끝나면, R0에는 R1이 왼쪽으로 2비트 밀린 값을 저장
-> (2) Logical shift right, 이것은 레지스터의 값을 가지고, 오른쪽으로 n-bits만큼 shift한다는 것이다.
-> 예시, MOV R0, R1, LSL R2
//명령어의 실행이 끝나면, R0는 R1이 R2만큼 오른쪽으로 밀린 값을 가진다. R1,R2는 값이 안바뀜
-> (3) Arithmetic shift right, 이것은 MSB가 유지된다는 것 제외하고 logical shift right와 비슷하다,
-> 예시, MOV R0, R1, ASR #2
//명령어 실행이 끝나면, R0는 R1 Arithmetic의 값을 가진다. 오른쪽으로 2bits 간다.
-> (4) Rotate right, 이것은 레지스터의 값을 가지고 n-bits 만큼 오른쪽으로 rotate 한다.
-> 예시. MOV R0, R1, ROR R2
//명령어 실행이 끝나면, R0는 R1이 R2만큼 오른쪽으로 rotate한 값을 가진다.
-> (5) Rotate right extended, 이것은 Rotate right를 MSB로 들어가는 carry flag와 함께 1바이트
만큼 한것과 비슷하다. 즉, carry를 통해서 오른쪽으로 rotate 한다.
-> 예시, MOV R0, R1, RRX
//명령어 실행이 끝나면, R0는 R1이 1-bit carry된 상태에서 오른쪽으로 rotate한 값을 가진다.
<Addressing Modes for Memory Access Operand>
■ 미리 언급한 것처럼, load와 store 명령어가 메모리에 접근하기 위해 사용된다.
서로 다른 메모리 접근 addressing mode는 다음과 같다.
-> (1) Register indirect addressing mode
-> (2) Relative register indirect addressing mode
-> (3) Base indexed indirect addressing mode
-> (4) Base with scaled register addressing mode
■ 각각 이러한 addressing mode는 앞서 각 addressing mode에 대한 예시에서 설명한
offset addressing, Pre-index addressing 그리고 post-index addressing 모드가 있다.
-> (1) Register indirect addressing mode
-> 이 addressing mode에서는, 레지스터가 접근되고자 하는 메모리공간의 주소를 주는데 사용된다.
-> 예시, LDR R0, [R1]
//이 명령어는 레지스터 R1의 메모리 주소에 있는 레지스터 R0에 32-bit word와 함께 불러진다.
-> (2) Relative register indirect addressing mode
-> 이 addressing mode에서는, 메모리 주소는 레지스터에 더해진 immediate value 값에 의해 발생된다.
Pre index 그리고 post index는 이 addressing mode에서 지원된다.
-> 예시, LDR R0, [R1, #4]
//이 명령어는 레지스터 R1 레지스터 상수 주소에 4를 더하여 R1에 저장하고, 그 값을 R0에 불러온다.
-> 예시, LDR R0, [R1, #4]!
//이 명령어는 pre-index addressing이다. 이 명령어는 R1에 R1+4를 더하여 새로운 주소값을 넣고,
그 R1 레지스터의 값을 R0에 넣는다.
-> 예시, LDR, [R1], #4
//이것은 post-index addressing이다. 이 명령어는 레지스터 R0에 레지스터 R1에 주어진 메모리 주소에
있는 word를 저장하고, 그 뒤에 R1에 4를 더함으로써 새로운 주소를 계산하고, R1에 새로운 주소를
넣는다.
-> (3) Base indexed indirect addressing mode
-> 이 addressing mode에서는, 메모리 주소가 두 개 레지스터의 값을 더함으로써 발생된다.
Pre-index와 post-index가 이 addressing mode에서 지원된다.
-> 예시(a), LDR R0, [R1, R2]
//이 명령어는 R0 레지스터에, 레지스터 R1과 R2를 더함으로써 계산된 메모리 주소의 word를 저장한다.
-> 예시(b), LDR R0, [R1, R2]!
//이 명령어는 pre-index addressing이다. 이 명령어는 예시(a)와 똑같은데, 다만 R1레지스터에 새로운
주소값인 R1+R2를 넣는다.
-> 예시(c), LDR R0, [R1], R2
//이 명령어는 post-index addressing이다. 이 명령어는 레지스터 R0에 주어진 R1레지스터 주소공간에
존재하는 word를 불러오고, 그 후에 R2 레지스터에 있는 값을 레지스터 R1의 새로운 주소로 넣어준다.
-> (4) Base with scaled register addressing mode
-> 이 addressing mode의 메모리 주소는 레지스터의 값을 다른 레지스터가 왼쪽으로 쉬프트 한 값에
더해준 것에 의해 발생한다. Pre-index와 post-index가 이 addressing mode에서 지원된다.
-> 예시(a) LDR R0, [R1, R2, LSL #2]
//이 명령어는 레지스터 R0에, 레지스터 R2를 왼쪽으로 2비트 shift 한 것을 레지스터 R1과 더하여
계산된 메모리 주소의 word를 R0에 불러온다.
-> 예시(b) LDR R0, [R1, R2, LSL #2]!
//이것은 pre-indexed addressing이다. 이 명령어는 레지스터 R0에 R2를 2비트 왼쪽으로 쉬프트 한 값을
R1에 더하여 계산된 메모리 주소에 있는 word를 R0에 불러온다. 그리고 나서 그 값을 R1에 저장한다.
즉, R1 = R1 + R2
-> 예시(c) LDR R0, [R1], R2, LSL #2
//이것은 post-indexed addressing이다. 이 명령어는 레지스터 R0에 레지스터 R1 메모리 주소에 있는
word를 R0에 불러오고, 그 후에 새로운 R1의 주소를 R2를 왼쪽으로 2비트 쉬프트 한 값을 R1에
더해주어 저장한다.
'임베디드구조' 카테고리의 다른 글
임베디드구조)#12 Instruction Sets: Characteristics and Functions(2) (0) | 2020.05.28 |
---|---|
임베디드구조)#12 Instruction Sets: Characteristics and Functions(1) (0) | 2020.05.27 |
임베디드구조)#1 Basic Concepts and Computer Evolution(1) (0) | 2020.05.26 |