본문 바로가기

운영체제

운영체제) #10 System Call의 이해: 어셈블리어(PC버전)

<System Call의 이해/ 어셈블리어(PC버전)>

기계 명령의 존재를 확인해 보자

프로그램의 실체를 파헤쳐보자

내가 만든 프로그램은 어떻게 실행되는지 확인해 보자.

low level 처리가 가능한 high level 엔지니어가 되어 보자. 


어셈블리 프로그래밍

-> 8051/AVR 어셈블리 프로그래밍 (8비트/16비트)

-> 임베디드 보드용 ARM 어셈블리 프로그래밍 (32비트)

-> PC용 어셈블리 프로그래밍 (16비트/32비트) : 짧은시간에 가장 간단하고 유용하게 익혀볼 수 있는 환경




 

<컴파일 & 실행 파일 만드는 법>

1. DOS Box 설치 / MASM 폴더 복사 / 폴더 Mount

2. 윈도우 환경의 Editor로 편집한다.

3. Dos Box에서 Masm sample.asm

4. Link ascii.obj

5. 혹은 3. 4. 과정을 줄여서 ml sample.asm

6. 실행

7. 다음 페이지를 참조

 

//f:\masm경로에 파일이 있으면 x라는 곳에 마운트 시켜 간단하게 사용가능하다.



//x 디렉토리에 dtype.asm을 저장한다.

 


//dirdtype의 파일 형식을 보여준다.



//cv /s dtype은 디버그 창을 띄워준다.


 

 



<개발 과정 설명>

에디터로 source 파일을 작성한다. ex) example.asm

DOS 창에서 아래 작업을 수행한다.

1. C:\MASM> masm example;

- 에러가 발생하면 에러 메시지와 라인 번호를 참조하여 에디터로 수정 (Assemble error)

- 에러가 없으면 Object 파일(example.obj) 생성

 

2. C:\MASM>link example;

- 에러가 발생하면 에러 메시지를 참조하여 에디터로 수정 (Link error)

- 에러가 없으면 실행 파일(example.exe) 생성

 

3. C:\MASM>example

- 제대로 수행되지 않으면 디버깅 필요 (Run-time error)

 

4. C:\MASM>debug example.exe

 

 

 

 

 

<Register>

Register?

-> Data 저장, 연산 등을 위한 임시 기억 장치

-> register는 자체의 특수한 용도와 제한점이 있다.

-> 장점 : Memory(변수)보다 access 속도가 빠르다.

-> 단점 : 개수가 한정되어 있고, 용도가 제한적이다.

 

종류

-> 범용 register : 8

-> 세그먼트 register : 4

-> 프로세서 컨트롤 register : 2


  



<Register의 종류>

//AX,BX,CX,DX는 범용 레지스터, 특별한 용도가 있긴 하다.

//AH 8비트, AL 8비트는 AX 16비트를 반으로 나눈 것이다.

//SP,BP,SI,DI는 위치나 주소를 알려주는데, 즉 인덱스로 사용한다.

 

//우리가 만든 CodeCS에 들어간다. DataDS에 들어간다. CS DS 둘이 묶여서 사용될 때도 있다.

//SSStack 관리, ES는 여분 공간을 위해 만듬

 

//IP는 어떤 위치에 명령어를 가르킬지

//FL은 어떤 상태가 어떤 이유를 알려주는지 FLAG를 알려준다.

 

 



<Register의 기능>

범용 register : +, -, *, / 등의 사칙 연산과 memory address 지정

 

연산

-> AX register

- 입출력, 거의 모든 산술연산에 사용

- 곱셈, 나눗셈, 변환(translate) 명령에서 반드시 사용

 

-> BX register

- 주소 지정을 확대하기 위해 index로 사용

- 일반적인 계산에 사용

 

-> CX register

- 루프(loop)의 반복 횟수, 쉬프트(shift) 비트 수를 기억

- 일반적인 계산에 사용

 

-> DX register

- 몇몇 입출력 동작에서 반드시 사용

- 큰 수의 곱셈과 나눗셈에는 DXAX를 쌍으로 사용

 

 

번지 계산

-> SP, BP register : 스택 조작에 사용

-> SI, DI register : 메모리 번지의 간접 지정에 사용

 

 

Segment register //전체 메모리를 segment 단위로 구분

-> CPU가 명령을 fetch할 때 - CS:IF로부터 명령을 fetch

//메모리로부터 읽어 오는 동작

-> Data 전송 - DS : offset address

-> Stack 동작 - SS : SP //PUSH, POP

Segment들은 서로 중복 가능

-> Code Segment : 프로그램 code 저장

-> Data Segment : 변수, 상수 저장

-> Stack Segment : 돌아갈 주소 등 저장

 


Processor control register

-> IP (Instruction Pointer) : 다음에 실행할 명령어의 번지를 가리키는 register

-> FL (Flag Register)

- CPU의 현재 상태 표시

- 비교, 연산 결과를 보존




 

<Flag register>






 

<8086의 주소 지정> //16비트 2개를 가지고, 20비트를 만들어 낸다.

8086 프로세서는 1MB까지의 memory를 취급(00000H ~ FFFFFH)

1MB = 2^20bit = address data20bit 필요

8086register16bit 크기뿐 2개의 register를 조합


Segment register의 역할

//segment + offset의 조합

 


Segment 배치의 예


 




<명령의 구성>

일반적인 명령의 구성

-> Label : 명령행이 존재하는 번지를 지정 (의사 명령), 생략 가능

-> OP code (Operation code) : 연산자, 명령의 종류 //여러 개 있을 수도, 없을 수도 있다.

-> Operand : 피연산자, 명령에 따라 0~ 2, Operand 2에서 Operand 1 방향으로 조작이 이루어짐

-> Comment : 설명, 비실행문

-> 조작의 방향 : 모든 경우에 뒤에서 앞으로 동작이 이루어짐

 

 

일반적인 명령의 구성에 대한 부연 설명

-> OP code(연산자)Operand(피연산자)는 반드시 필요

-> Label은 대부분의 경우 필요가 없고, 특별한 경우에 붙임

-> Comment는 명령에 대한 간단한 설명을 붙이는 것으로 실제 어셈블하면 기계어 code로는 변환되지 않고, 단지 설명일 뿐임

 

ex) MOV AX, BX //C언어로는 AX = BX; 와 같이 구현

BX register에 들어 있는 값을 AX register로 전송하라는 (MOVe)명령, 결과적으로 AX registerBX register는 같은 값을 갖게 된다.

 

ex) ADD AX, BX //C언어로는 AX += BX; 와 같이 구현

BX register의 값을 AX register의 값과 더해서 (ADDition) 결과 값을 다시 AX register에 저장하라는 명령

 

 



<의사 명령어(Pseudo Code)>

명령어 : 어셈블하면 기계어 코드로 변환되며, CPU에게 데이터 처리를 내리는 명령 ex) MOV, ADD, SUB, ...

의사 명령어 : 어셈블 할 때 기계어 코드로 변환되지 않으며, 어셈블러(컴파일러) 자체에게 작업을 지시하는 명령

ex) ASSUME, SEGMENT, ENDS, DB, Label, ...

 




<프로그램의 초기 작성>

세그먼트 선언 //세그먼트는 영역을 나눠준다.

-> Code Segment, Data Segment, Stack Segment 중 필요한 Segment를 선언

-> Code Segment는 반드시 선언

 

세그먼트 선언 형식


 



 

<세그먼트 선언>

//CODE SEGMENT는 시작을 알리고 CODE ENDS는 끝을 알려준다.

//DATA SEGMENT가 양이 너무 적으면 CODE SEGMENT 안으로 들어갈 수도 있다.

 

 

 

 


<ASSUME 의사 명령어>

Segment 이름과 Segment register의 대응 관계를 선언

 

1. Code Segment만 존재(Data 없음) - ASSUME CS:CODE

2. Code SegmentData Segment가 분리 존재 - ASSUME CS:CODE, DS:DATA

3. Code Segment 내에 Data 저장 - ASSUME CS:CODE, DS:CODE

//ASSUME은 가정을 하여 컴파일러에게 알려주는 것,//실제 프로그램 할때는 ASSUME ~ 과정을 다시 코드를 써야 한다.

//ENDS는 세그먼트가 끝난거고, END는 프로그램 전체가 끝난 것이다.

 



 

<예제 (sample1.asm)>

//CODE SEGMENT 하나만 있다.

//CS는 자동으로 잡히는데, DS는 잡히지 않는다.

 

 

//여기부터가 실제 코드

 

 

 

//프로그램을 종료하는 인터럽트 시스템 콜(4CH)

 

 

 

 

 









 

 



<인텔 8086 명령어 세트 찾기>

https://www.gabrielececchetti.it/Teaching/CalcolatoriElettronici/Docs/i8086_instruction_set.pdf


//MOV는 복사한다는 뜻

//imm은 숫자

//MOV imm,imm은 안된다. 어떤 숫자를 어떤 숫자로 복사하지 못한다.

 

//MOV mem,mem도 안된다. 어떤 메모리 번지의 값을 어떤 메모리 값을 복사할 수 없다. 못하게 한다.

//윗줄은 레지스터를 거쳐야 가능하다.






//명령에 따른 필요한 클락 수

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 










//명령어가 실제로 이렇게 저장된다

 


//모드에 따라 처리되는게 다르다

 

 



<MOV AL, BL>

MOV Reg, Reg 100010dw oorrrmmm

 

opcode : 100010 (MOV)

d : 1 or 0 (1로 해 보자)

: (from R/M to REG, BL -> AL)

: , R/M = BL, REG = AL

 

w : 0 (byte)

mod : 11 (two register)

reg : 000 (REG = AL)

r/m : 011 (R/M = BL)

 



■ 100010 1 0 11 000 011

1000 1010 1100 0011 = 8AC3

 

 



<MOV AL, BL (ALT)>

MOV Reg, Reg 100010dw oorrrmmm

 

opcode : 100010 (MOV)

d : 1 or 0 (0으로 해 보자)

: (from REG to R/M)

: , R/M = AL, REG = BL

 

w : 0 (byte)

mod : 11 (two register)

reg : 011 (REG = BL)

r/m : 000 (R/M = AL)

 



100010 0 0 11 011 000

1000 1000 1101 1000 = 88D8

 

 

 

 

<간단한 어셈블리 프로그래밍>

Code Segment만 존재하는 예

//위쪽 코드는 그냥 프로그램 부분이다.

//아래쪽 빨간 박스 코드 부분이 시스템 콜 부분이다.

//4CINT를 합쳐 인터럽트를 호출하는 시스템 콜이고, 21H는 십진수로 33, TRAP을 의미한다.

 

 



<데이터 정의 의사 명령어>

데이터를 기억 장소(memory)에 할당하기 위해 Data type을 결정

형식 -> Variable_name 지시자 초기값

- ex) SUM DW 0 cf) int sum = 0;

//여러 개 쓰기 가능하다. 예를 들면 SUM DW 0, SUB DW 0에서 SUM SUB(시작점) 이름

//초기 값 없으면 ? 써주면 된다.

 

Data type

//DDefine의 준말

 




<Data type의 저장 방법>

ex) 문자 출력 프로그램 dtype.asm으로 저장

//코드, 데이터 세그먼트 각각 1개씩이다.

//ASSUME은 컴파일러에게만 알려주므로, DS:DATA가 제대로 전달되도록 //MOV AX, DATA //MOV DS, AX로 위치를 잘 가르키게 한다. (AX 레지스터를 경유)

//DATA에서 바로 DS를 접근하는 명령어는 없다. DS는 중요한 영역이기 때문이다.

 

//MOV AH,2 = SubFunc

//INT 21H = Interrupt System Call

//INT 21H 함수의 FUN2가 들어갔구나

//Function code2번은 아스키한문자를 출력하는 System Call



 

<주소 지정 방식(Addressing mode)>

Operand의 내용이나 장소를 지정하는 방식

(1) 즉치 주소 지정 방식(Immediate addressing)

ex) MOV AX, 1234H //직접 수치 대입

ex) DAT EQU 1234H//EQUC언어에서 DEFINE과 같다.

MOV AX, DAT

//CS,DS,ES,SS/IP,FL에 직접 수치 전송 불가능

 

 

(2) 직접 방식(Register addressing; Direct addressing)

- Register의 내용을 직접 전송 //값이 Register 내에

ex) MOV AH, DH //8bit

MOV DS, AX //16bit

ex) MOV AX, DH (X) //사이즈가 같아야 함

//Segment register간 전송 불가능

//IP, FL register 사용 불가

//CS register로 전송 불가


 

Register의 크기에 따라 전송할 데이터 크기 결정

ex) MOV DX, [BX] //16비트 전송

MOV DL, [BX] //8비트 전송

 

 

 

(3) 간접 방식(Memory reference addressing; Indirect addressing)

- 전송하는 값이 저장되어 있는 번지를 지정 //값이 메모리 내에

ex) MOV AX, DAT //변수이름 = 데이터가 들어있는 번지

MOV AX, [BX+DI+4] //번지의 내용 - Pointer 개념

 

간접 방식에서의 번지 지정법


 

- 허용되지 않는 조합

(1) 3개 모두 없으면 안된다.

(2) 8bit 숫자만 사용해야 한다. //번지는 16bit

(3) [BP] --> [BP + 0]으로 사용해야 한다. //(3x3x3=27)-3 = 24(기계어 코드 가지 수)

 

ex) MOV DL, [BX+DI]

ex) MOV AX, [1234H] (X) //직접 memory 번지 지정 불가능 다른 register 거쳐서 ??16비트 숫자 안됨??

BP 포함 : 데이터는 stack segment 내에

그 외 : 데이터는 data segment 내에

//EQUC언어에서 DEFINE과 같다.

//OFFSET BUFFERBUFFER라는 놈의 어떤 SEGMENT안에 상대적인 주소를 나타내라는 말

//OFFSET'example'에서 'e'를 가리킨다. [BX+SI]‘a’를 가리킨다.

//CR, LF쪽 코드는 줄띄움 기능을 한다.

 

 

Memory로 숫자를 move할 때 크기 지정 필요

ex) MOV [BX], 12H

(1)Byte 데이터 (2)Word 데이터

//MOV DX,12H ;0012H

//MOV DX,1234H

 

ex) MOV BX, OFFSET DATA1

MOV BYTE PTR [BX], 12H

MOV WORD PTR[BX], 3456H

DATA1 DB ?

 




<역워드 형식(참고)>

16bit register 상의 데이터를 memory에 전송할 때 상하위 바이트가 바뀐다.

- 저장할 때 바꿔서 저장하고, 읽어올 때 바꿔서 읽어오기 때문에 관계는 없음

ex) DATA SEGMENT

SCR_1 DB 'A'

SCR_2 DW 4243H

DATA ENDS

//앞에 43이 먼저 저장되고 42가 그다음 저장된다.




 

<덧셈 명령>

ADD(ADDition)

-> 형식 : ADD OP1, OP2 //Size 같아야

reg imm

reg reg

reg mem

mem imm

mem reg

-> 기능 : OP1 OP1 + OP2 //Memory끼리는 안됨

mem mem(x)

 

ADC(ADdition with Carry)

-> 기능 : OP1 OP1 + OP2 + Carry Flag //앞 연산 결과를 반영

//NC : +0

//CY : +1

//Carry Flag0인 경우 : ADC =ADD


 



 


<어셈블리 프로그램 예제 실행>

 

1. 현재 나의 masm파일 위치로 용이한 접근을 위해 경로를 x에 마운트 시킨다.


 

2. 예제 1번을 C:\masm경로에 저장 ml 1dtype.asm 1dtype 실행