명령어:
메모리:
연산:
다목적:
스택:
ADD a, b
ADD
eax
, ebx
처럼 사용.)eax
, ebx
MOV eax, ebx (ebx 값을 eax에 복사)
MOV eax, ebx
(ebx 값을 eax에 복사)MOV eax, [mem]
(메모리 값을 eax에 복사)MOV [mem], eax
(eax 값을 메모리에 저장)ADD eax, ebx
(eax = eax + ebx)SUB eax, 5
(eax = eax - 5)MUL ebx
(eax = eax * ebx)DIV ecx
(eax = eax / ecx)INC eax
(eax 값 1 증가)DEC eax
(eax 값 1 감소)AND eax, ebx
(eax = eax & ebx, 비트 AND)OR eax, 0xFF
(eax = eax | 0xFF, 비트 OR)XOR eax, eax
(eax = 0, 자기 자신과 XOR하면 0)NOT eax
(eax의 모든 비트 반전)SHL eax, 2
(eax를 왼쪽으로 2비트 시프트)SHR eax, 1
(eax를 오른쪽으로 1비트 시프트)JMP label
(label로 점프)JZ/JE label
(결과가 0/같으면 label로 점프)JNZ/JNE label
(결과가 0이 아님/같지 않으면 점프)JG/JA label
(크면 점프)JL/JB label
(작으면 점프)CALL function
(함수 호출)RET
(함수에서 복귀)PUSH eax
(eax 값을 스택에 저장)POP eax
(스택 값을 eax에 저장)LEA eax, [ebx+4]
(ebx+4 주소를 eax에 로드)CMP eax, ebx
(eax와 ebx 비교, 결과는 플래그 설정)TEST eax, ebx
(eax와 ebx의 비트 AND 테스트)NOP
(아무 동작 안함)INT 0x80
(시스템 콜, 리눅스)SYSCALL
(시스템 콜, 최신 x64)// C 코드
int main() {
int a = 1;
int b = 2;
int c = a + b;
return c;
}
main:
push ebp ; 스택 프레임 설정
mov ebp, esp ; 새 베이스 포인터 설정
sub esp, 12 ; 변수 공간 확보
mov dword [ebp-4], 1 ; a = 1
mov dword [ebp-8], 2 ; b = 2
mov eax, [ebp-4] ; eax = a
add eax, [ebp-8] ; eax = eax + b
mov [ebp-12], eax ; c = eax
mov eax, [ebp-12] ; 반환값 설정
mov esp, ebp ; 스택 정리
pop ebp ; 이전 프레임 복원
ret ; 반환
어셈블리어 명령 | CPU 작동 과정 | 관련 구성 요소 |
---|---|---|
push ebp | 스택에 값 저장 | 메모리 쓰기, 스택 포인터 감소 |
mov ebp, esp | 레지스터 간 데이터 복사 | 범용 레지스터 |
sub esp, 12 | 스택 공간 확보를 위한 뺄셈 | ALU(뺄셈 연산) |
mov [ebp-4], 1 | 메모리에 즉시값 저장 | MAR, MBR, 메모리 쓰기 |
mov eax, [ebp-4] | 메모리에서 레지스터로 데이터 로드 | MAR, MBR, 레지스터 |
add eax, [ebp-8] | 덧셈 연산 수행 | ALU(덧셈 연산), 범용 레지스터 |
ret | 실행 흐름 복귀 | PC 업데이트 |
인출 사이클:
T0: MAR ← PC
T1: MBR ← M[MAR], PC ← PC+1
T2: IR ← MBR
실행 사이클:
T0: MAR ← IR(Addr)
T1: MBR ← M[MAR]
T2: AC ← AC + MBR
x86 어셈블리어 버전:
메모리의 값을 누산기에 더하는 연산
mov eax, [addr1] ; 누산기(eax)에 첫 번째 값 로드
add eax, [addr2] ; 두 번째 값을 누산기에 더함
mov [result], eax ; 결과를 메모리에 저장
; 각 어셈블리 명령어가 CPU 내부에서 처리될 때 일어나는 마이크로 연산:
; 'mov eax, [addr1]'이 실행될 때:
; 1. CPU는 addr1 주소를 MAR에 로드
; 2. 메모리에서 해당 값을 읽어 MBR에 저장
; 3. MBR의 값을 eax 레지스터로 전송
; 'add eax, [addr2]'가 실행될 때:
; 1. addr2 주소를 MAR에 로드
; 2. 메모리에서 해당 값을 읽어 MBR에 저장
; 3. ALU가 eax와 MBR 값을 더해 eax에 저장
T1 T2 T3 T4 T5 T6 T7 T8 T9
명령1: [인출]→[해독]→[실행]→[메모리]→[쓰기]
명령2: [인출]→[해독]→[실행]→[메모리]→[쓰기]
명령3: [인출]→[해독]→[실행]→[메모리]→[쓰기]
if(a>b) x=a; else x=b;
→ x = (a>b) ? a : b;
// 분기 있는 코드
if (value > threshold) {
result += value;
}
// 분기 없는 코드
result += (value > threshold) * value;
https://porolog.tistory.com/15
https://namu.wiki/w/CPU/%EA%B5%AC%EC%A1%B0%EC%99%80%20%EC%9B%90%EB%A6%AC
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <functional>
#include <cstdint>
using namespace std;
// 메모리 클래스
class Memory {
private:
vector<uint8_t> data;
public:
Memory(size_t size = 65536) : data(size, 0) {}
uint8_t read8(uint16_t address) const {
return data[address];
}
uint16_t read16(uint16_t address) const {
return static_cast<uint16_t>(data[address]) | (static_cast<uint16_t>(data[address + 1]) << 8);
}
void write8(uint16_t address, uint8_t value) {
data[address] = value;
}
void write16(uint16_t address, uint16_t value) {
data[address] = value & 0xFF;
data[address + 1] = (value >> 8) & 0xFF;
}
void load_program(const vector<uint8_t>& program, uint16_t start_address = 0) {
for (size_t i = 0; i < program.size(); i++) {
data[start_address + i] = program[i];
}
}
};
// 레지스터 클래스
class Registers {
public:
uint16_t PC; // 프로그램 카운터
uint16_t IR; // 명령어 레지스터
uint16_t MAR; // 메모리 주소 레지스터
uint16_t MBR; // 메모리 버퍼 레지스터
uint16_t AC; // 누산기
uint16_t SP; // 스택 포인터
uint16_t BP; // 베이스 포인터
// 범용 레지스터
uint16_t R0, R1, R2, R3;
// 상태 플래그
bool Z; // 제로 플래그
bool C; // 캐리 플래그
bool N; // 네거티브 플래그
bool V; // 오버플로우 플래그
Registers() : PC(0), IR(0), MAR(0), MBR(0), AC(0), SP(0), BP(0),
R0(0), R1(0), R2(0), R3(0),
Z(false), C(false), N(false), V(false) {}
void reset() {
PC = IR = MAR = MBR = AC = SP = BP = R0 = R1 = R2 = R3 = 0;
Z = C = N = V = false;
}
};
// ALU 클래스
class ALU {
private:
Registers& registers;
public:
ALU(Registers& regs) : registers(regs) {}
uint16_t add(uint16_t a, uint16_t b) {
uint32_t result = static_cast<uint32_t>(a) + static_cast<uint32_t>(b);
// 상태 플래그 업데이트
registers.Z = (result & 0xFFFF) == 0;
registers.C = (result > 0xFFFF);
registers.N = (result & 0x8000) != 0;
registers.V = ((a & 0x8000) == (b & 0x8000)) && ((result & 0x8000) != (a & 0x8000));
return static_cast<uint16_t>(result);
}
uint16_t sub(uint16_t a, uint16_t b) {
return add(a, ~b + 1); // 2의 보수를 이용한 뺄셈
}
uint16_t mul(uint16_t a, uint16_t b) {
uint32_t result = static_cast<uint32_t>(a) * static_cast<uint32_t>(b);
registers.Z = (result & 0xFFFF) == 0;
registers.C = (result > 0xFFFF);
return static_cast<uint16_t>(result);
}
uint16_t div(uint16_t a, uint16_t b) {
if (b == 0) {
// 0으로 나누기 시도 (실제로는 예외처리 필요)
cout << "Error: Division by zero" << endl;
return 0;
}
uint16_t result = a / b;
registers.Z = result == 0;
return result;
}
uint16_t logical_and(uint16_t a, uint16_t b) {
uint16_t result = a & b;
registers.Z = result == 0;
registers.N = (result & 0x8000) != 0;
return result;
}
uint16_t logical_or(uint16_t a, uint16_t b) {
uint16_t result = a | b;
registers.Z = result == 0;
registers.N = (result & 0x8000) != 0;
return result;
}
uint16_t logical_xor(uint16_t a, uint16_t b) {
uint16_t result = a ^ b;
registers.Z = result == 0;
registers.N = (result & 0x8000) != 0;
return result;
}
uint16_t logical_not(uint16_t a) {
uint16_t result = ~a;
registers.Z = result == 0;
registers.N = (result & 0x8000) != 0;
return result;
}
uint16_t shift_left(uint16_t a, uint16_t count) {
uint16_t result = a << count;
registers.Z = result == 0;
registers.N = (result & 0x8000) != 0;
// 마지막으로 밀려나간 비트가 캐리
registers.C = (count > 0) && ((a & (1 << (16 - count))) != 0);
return result;
}
uint16_t shift_right(uint16_t a, uint16_t count) {
uint16_t result = a >> count;
registers.Z = result == 0;
registers.N = (result & 0x8000) != 0;
// 마지막으로 밀려나간 비트가 캐리
registers.C = (count > 0) && ((a & (1 << (count - 1))) != 0);
return result;
}
};
// 명령어 집합 정의 (Op codes)
enum OpCode {
// 데이터 이동
MOV = 0x10,
LOAD = 0x11,
STORE = 0x12,
PUSH = 0x13,
POP = 0x14,
// 산술 연산
ADD = 0x20,
SUB = 0x21,
MUL = 0x22,
DIV = 0x23,
INC = 0x24,
DEC = 0x25,
// 논리 연산
AND = 0x30,
OR = 0x31,
XOR = 0x32,
NOT = 0x33,
SHL = 0x34,
SHR = 0x35,
// 분기
JMP = 0x40,
JZ = 0x41,
JNZ = 0x42,
JG = 0x43,
JL = 0x44,
// 시스템 & 특수 명령어
CALL = 0x50,
RET = 0x51,
NOP = 0x00,
HALT = 0xFF
};
// CPU 클래스
class CPU {
private:
Memory& memory;
Registers registers;
ALU alu;
bool running;
// 명령어 핸들러 맵
map<OpCode, function<void(uint8_t)>> instruction_handlers;
public:
CPU(Memory& mem) : memory(mem), registers(), alu(registers), running(false) {
initialize_instruction_handlers();
}
void initialize_instruction_handlers() {
// 각 명령어 타입별 실행 함수 등록
instruction_handlers[NOP] = [this](uint8_t operand) { /* 아무것도 하지 않음 */ };
instruction_handlers[MOV] = [this](uint8_t operand) {
uint8_t dest = (operand >> 4) & 0x0F;
uint8_t src = operand & 0x0F;
uint16_t value = get_register_value(src);
set_register_value(dest, value);
};
instruction_handlers[LOAD] = [this](uint8_t operand) {
uint8_t reg = (operand >> 4) & 0x0F;
uint16_t address = memory.read16(registers.PC);
registers.PC += 2;
uint16_t value = memory.read16(address);
set_register_value(reg, value);
};
instruction_handlers[STORE] = [this](uint8_t operand) {
uint8_t reg = (operand >> 4) & 0x0F;
uint16_t address = memory.read16(registers.PC);
registers.PC += 2;
uint16_t value = get_register_value(reg);
memory.write16(address, value);
};
instruction_handlers[ADD] = [this](uint8_t operand) {
uint8_t dest = (operand >> 4) & 0x0F;
uint8_t src = operand & 0x0F;
uint16_t a = get_register_value(dest);
uint16_t b = get_register_value(src);
uint16_t result = alu.add(a, b);
set_register_value(dest, result);
};
instruction_handlers[SUB] = [this](uint8_t operand) {
uint8_t dest = (operand >> 4) & 0x0F;
uint8_t src = operand & 0x0F;
uint16_t a = get_register_value(dest);
uint16_t b = get_register_value(src);
uint16_t result = alu.sub(a, b);
set_register_value(dest, result);
};
instruction_handlers[JMP] = [this](uint8_t operand) {
uint16_t address = memory.read16(registers.PC);
registers.PC = address;
};
instruction_handlers[JZ] = [this](uint8_t operand) {
uint16_t address = memory.read16(registers.PC);
registers.PC += 2;
if (registers.Z) {
registers.PC = address;
}
};
instruction_handlers[JNZ] = [this](uint8_t operand) {
uint16_t address = memory.read16(registers.PC);
registers.PC += 2;
if (!registers.Z) {
registers.PC = address;
}
};
instruction_handlers[HALT] = [this](uint8_t operand) {
running = false;
};
// 나머지 명령어 핸들러들 추가...
}
// 레지스터 값 가져오기 헬퍼 함수
uint16_t get_register_value(uint8_t reg_code) {
switch (reg_code) {
case 0: return registers.R0;
case 1: return registers.R1;
case 2: return registers.R2;
case 3: return registers.R3;
case 4: return registers.PC;
case 5: return registers.SP;
case 6: return registers.BP;
case 7: return registers.AC;
default:
cout << "Error: Invalid register code: " << (int)reg_code << endl;
return 0;
}
}
// 레지스터 값 설정 헬퍼 함수
void set_register_value(uint8_t reg_code, uint16_t value) {
switch (reg_code) {
case 0: registers.R0 = value; break;
case 1: registers.R1 = value; break;
case 2: registers.R2 = value; break;
case 3: registers.R3 = value; break;
case 4: registers.PC = value; break;
case 5: registers.SP = value; break;
case 6: registers.BP = value; break;
case 7: registers.AC = value; break;
default:
cout << "Error: Invalid register code: " << (int)reg_code << endl;
}
}
// 인출-해독-실행 사이클
void fetch_decode_execute() {
// 인출 단계
registers.MAR = registers.PC;
registers.MBR = memory.read8(registers.MAR);
registers.PC++;
registers.IR = registers.MBR;
// 해독 단계
uint8_t opcode = registers.IR;
uint8_t operand = memory.read8(registers.PC);
registers.PC++;
// 실행 단계
if (instruction_handlers.find(static_cast<OpCode>(opcode)) != instruction_handlers.end()) {
instruction_handlers[static_cast<OpCode>(opcode)](operand);
} else {
cout << "Error: Unknown opcode: 0x" << hex << (int)opcode << endl;
running = false;
}
}
void reset() {
registers.reset();
running = false;
}
void run() {
running = true;
while (running) {
fetch_decode_execute();
// 디버깅을 위한 상태 출력
print_state();
}
}
// CPU 상태 출력을 위한 디버그 함수
void print_state() {
cout << "PC: 0x" << hex << registers.PC << " ";
cout << "IR: 0x" << hex << registers.IR << " ";
cout << "AC: 0x" << hex << registers.AC << " ";
cout << "SP: 0x" << hex << registers.SP << " ";
cout << "BP: 0x" << hex << registers.BP << endl;
cout << "R0: 0x" << hex << registers.R0 << " ";
cout << "R1: 0x" << hex << registers.R1 << " ";
cout << "R2: 0x" << hex << registers.R2 << " ";
cout << "R3: 0x" << hex << registers.R3 << endl;
cout << "Flags - Z: " << registers.Z << " C: " << registers.C;
cout << " N: " << registers.N << " V: " << registers.V << endl;
cout << "------------------------------------------" << endl;
}
};
// 샘플 프로그램 예제 (간단한 어셈블리 프로그램을 실행)
int main() {
// 메모리 생성
Memory memory;
// 샘플 프로그램 작성
// 이 프로그램은 R0에 10을 로드하고, R1에 20을 로드한 다음, 두 값을 더해 R2에 저장합니다.
vector<uint8_t> program = {
// R0 = 10
MOV, 0x70, // Acc에 값을 로드 (7: AC)
0x0A, 0x00, // 값: 10
MOV, 0x07, // R0 = Acc
// R1 = 20
MOV, 0x70, // Acc에 값을 로드
0x14, 0x00, // 값: 20
MOV, 0x17, // R1 = Acc
// R2 = R0 + R1
MOV, 0x70, // Acc = R0
ADD, 0x71, // Acc += R1
MOV, 0x27, // R2 = Acc
// 프로그램 종료
HALT, 0x00
};
// 프로그램 로드
memory.load_program(program);
// CPU 생성 및 실행
CPU cpu(memory);
cpu.reset();
cpu.run();
return 0;
}