3-1. 소스코드와 명령어
: 모든 소스 코드는 컴퓨터 내부에서 명령어로 변환
(1) 고급언어와 저급언어
- 고급 언어 : 우리가 이해할 수 있는 언어
- 저급 언어 : 컴퓨터가 이해할 수 있는 언어
=> 우리가 프로그래밍 언어(고급언어)로 작성한 코드가 실행되려면 반드시 명령어(저급언어)로 변환
1) 저급언어
- 기계어 : 명령어
- 어셈블리어 : 0과 1로 이루어진 명령어를 읽기 편한 형태로 변환
ex) 기계어(명령어) ----------------> 어셈블리어
0101 0101 -----------------> push rbp
0101 1101 -----------------> pop rbp
1100 0011 -----------------> ret
2) 컴파일 언어와 인터프리터 언어
: 고급언어(컴파일/인터프리터 언어) -> 저급언어(명령어) 변환 방식
- 컴파일 언어 -> 저급언어 : 컴파일
- 인터프리터 언어 -> 저급언어 : 인터프리터
2-1) 컴파일 언어 (ex. C언어)
: 컴파일러(컴파일 수행하는 도구)에 의해 소스 코드 전체가 저급 언어로 변환되어 실행되는 고급 언어
cf> 컴파일러
: 개발자가 작성한 전체 소스코드를 쭉 훑어보며 소스 코드에 문법적 오류 있는지, 실행 가능한 코드인지, 실행하는데 불필요한 코드는 없는지 따지며 소스 코드를 처음부터 끝까지 저급언어로 컴파일
=> 이때, 컴파일러가 소스 코드 내에서 오류 하나라도 발견하면 해당 소스 코드는 컴파일 실패
=> 컴파일 성공한 경우 : 개발자가 작성한 소스 코드는 저급 언어로 변환
(고급 언어) (컴파일) (저급언어)
소스코드 ------> 컴파일러 --------> 목적코드
2-2) 인터프리터 언어 (ex. python)
: 인터프리터 언어는 소스 코드를 한 줄씩 저급 언어로 변환
=> 컴퓨터와 대화하듯 소스 코드 한 줄씩 실행하기에 소스 코드 전체를 저급 언어로 변환하는 시간 기다릴 필요 없다
3) 컴파일 언어 vs 인터프리터 언어
- 컴파일 언어 : 소스 코드 내에 오류가 하나라도 존재 => 컴파일 실패
- 인터프리터 언어 : 소스 코드 N번째 줄에 문법 오류 존재 => N-1번째까지는 올바르게 수행
=> 속도 : 컴파일 언어 > 인터프리터 언어
=> Why?
: 컴파일언어의 목적 코드는 컴퓨터가 이해하고 실행할 수 있는 저급언어이지만 인터프리터 언어는 소스 코드 마지막에 이를때까지 한줄 한줄씩 저급언어로 해석하고 실행해야 하기때문이다
cf> 컴파일 언어와 인터프리터 언어는 경계가 명확하지않다(ex. 파이썬 언어는 인터프리터 언어지만 컴파일 가능)
=> 고급 언어가 저급언어(기계어=명령어)로 변환되는 방식에는 컴파일 방식과 인터프리터 방식이 있다고 이해할 것

4) 목적 파일과 실행파일
ex) 이미지로 이루어진 파일 : 이미지 파일
텍스트로 이루어진 파일 : 텍스트 파일
↓
목적 코드로 이루어진 파일 : 목적파일
실행 코드로 이루어진 파일 : 실행파일
4-1) 목적 코드 (컴퓨터가 이해하는 저급언어)
=> 목적 파일 ≠ 실행파일
: 목적 코드가 실행 파일이 되기 위해서는 링킹 과정 필요
cf> 링킹
ex) 컴파일 언어로 heler.c와 main.c 2개의 소스코드 작성한 경우

=> 컴파일된 목적 코드는 소스 코드 내용이 그대로 저급언어로 변환된 파일일 뿐이다
=> 따라서 main.o 실행되면 main.c에 없는 외부 기능(HELPER_더하기, 화면_출력) 어떻게 실행하는지 모르기에 main.o와 외부 기능들을 연결 짓는 작업이 필요하다 = 링킹
5) 프로그램 실행 과정
: 소스코드 ---------------------> 목적코드 ------------------> 실행파일 생성
(고급언어) 컴파일 (저급언어) 링킹
3-2. 명령어의 구조
(1) 연산 코드(연산자)와 오퍼랜드(피연산자)
ex) 명령어 : 연산코드 필드 + 오퍼랜드 필드
더하라 | 100과 | 120을 |
저장해라 | 10을 | 메모리 128번지에 |
1) 연산 코드
: 명령어가 수행할 연산
- 데이터전송
- 산술/논리연산
- 제어 흐름 변경
- 입출력 제어
㉮ 데이터 전송
- MOVE : 데이터를 옮겨라
- STORE : 메모리에 저장해라
- LOAD(FETCH) : 메모리에서 CPU로 데이터를 가져와라
- PUSH : 스택에 데이터 저장해라
- POP : 스택의 최상단 데이터 가져와라
㉯ 산술/논리 연산
- ADD/ SUBTRACT/MULTIPLY/ DIVIDE : 덧셈/뺄셈/곱셈/나눗셈 수행해라
- INCREMENT/ DECREMENT : 오퍼랜드에 1을 더해라/ 오퍼랜드에 1을 빼라
- AND/OR/NOT: AND/OR/NOT 연산 수행해라
- COMPARE : 두 개의 숫자 또는 TRUE/FALSE 값 비교하라
㉰ 제어흐름 변경
- JUMP : 특정 주소로 실행 순서 옮겨라
- CONDITION JUMP : 조건에 부합할 때 특정 주소로 실행 순서 옮겨라
- HALT : 프로그램 실행 멈춰라
- CALL : 되돌아올 주소를 저장한채 특정주소로 실행 순서 옮겨라
- RETURN : CALL 호출할 때 저장했던 주소로 돌아가라
㉱ 입출력 제어
- READ(INPUT) : 특정 입출력 장치로부터 데이터 읽어라
- WRITE(OUTPUT) : 특정 입출력 장치로 데이터 써라
- START IO : 입출력 장치 시작하라
- TEST IO : 입출력 장치의 상태 확인하라
2) 오퍼랜드
2-1) 오퍼랜드 필드 (=주소필드)
: 연산에 사용할 데이터 OR 연산에 사용할 데이터의 주소가 저장된 위치
(보통, 데이터의 크기제한때문에 주소가 저장되어있음)
=> 명령어 안에 하나도 없을 수도 있고 하나만 존재할 수도 있고,...
cf> 오퍼랜드 1개 : 1-주소 명령어, 오퍼랜드 2개 : 2-주소 명령어
=> 기계어와 어셈블리어 언어도 명령어이기에 연산 코드와 오퍼랜드로 구성
(2) 주소 지정 방식
: 유효 주소 찾는 방법 ( = 연산에 사용할 데이터의 위치 찾는 방법)
연산 코드에 사용할 데이터가 저장된 위치 : 유효 주소
1) 즉시 주소 지정 방식
: 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시
=> 데이터 크기 小 , 속도 大
2) 직접 주소 지정 방식
: 오퍼랜드 필드에 유효 주소 직접적으로 명시하는 방식
=> 즉시 주소 지정방식보단 데이터 크기 大
=> 여전히 오퍼랜드 필드의 길이는 연산 코드의 길이에 영향 받음

3) 간접 주소 지정 방식
=> 직접 주소 지정 방식보단 데이터 크기 大
=> 속도는 느림

4) 레지스터 주소 지정 방식
: 연산 코드에 사용할 데이터를 저장한 레지스터를 오퍼랜드 필드에 직접 명시
=> 간접 주소 지정 방식과 비슷하게 표현할 수 있는 레지스터 크기 제한 존재
5) 레지스터 간접 주소 지정 방식
: 연산에 사용할 데이터를 메모리에 저장하고 유효주소를 저장한 레지스터를 오퍼랜드 필드에 명시

< 총정리 >
- 주소지정방식 : 연산 코드에 사용할 데이터 찾는방법
- 즉시 주소 지정 방식 : 연산에 사용할 데이터
- 직접 주소 지정 방식 : 유효주소(메모리주소)
- 간접 주소 지정 방식 : 유효주소의 주소
- 레지스터 주소 지정 방식 : 유효주소(레지스터 이름)
- 레지스터 간접 주소 지정 방식 : 유효 주소 저장한 레지스터
'컴퓨터 > 구조' 카테고리의 다른 글
[CH2] 데이터 (0) | 2023.09.20 |
---|---|
[CH1] 컴퓨터 구조 (0) | 2023.09.20 |