Subprogram-1

subprogram
모듈화된 프로그램(modular program) 및 C와 같은 고급 언어들과 함께 작업하기 위해서
subprogram을 이용할 수 있다.

고급언어에서 subprogram으로는 함수와 프로시져들이 있다.
서브프로그램을 호출하는 코드와 서브프로그램 그 자체는 반드시 같은 방식으로 데이터를 주고 받아야 한다.
데이터를 주고받는 방법을 “호출 규약(calling convention)” 이라고 한다.
(C프로그램과 interface 할 수 있는 assembly 서브프로그램을 제작할 것이며,
이는 C 호출 규약에 따른다.
이 호출 규약은 데잍의 주소를 전달해 서브프로그램이 메모리의 데이터에 접근 할 수 있게 하는 방식으로 이루어져 있다.)
간접 주소 지정
간접 주소 시정은 레지스터를 포인터 변수처럼 사용할 수 있게 한다.
mov ax,[Data] ;
mov ebx,[Data] ;ebx=&Data
mov ax,[ebx] ;ax=*ebx
만일 1행 코드 AX에 AX 대신 AL을 대입한다면,
3행에서 2byte가 아닌 1byte의 데이터를 읽어들이게 될 것이다.

이와 같이 C언어와 달리 레지스터들은 형(Type)이 없다.
모든 32bit 범용 레지스터(EAX, EBX, ECX, EDX)와 인덱스(ESI, EDI)들은
모두 간접 주소 지정으로 사용될 수 있다.(통상적으로 8, 16bit는 사용할 수 없다.)
서브 프로그램은 프로그램의 다른 영역에서 쓰이는 독립적인 코드라고 볼 수 있다.
즉, C언어에서의 하나의 함수라고 생각하면 된다.
sub1.asm
위 코드에서는 라벨을 이용하거나, $에 분기할 명령어 코드의 위치까지의 차를 더함을 이용하였다.
Stack(스택)
대부분의 CPU가 지원하는 방법이다. Last-In First-Out, LIFO라고 부르며, 후입 선출 리스트 이다.
PUSH와 POP 명령어를 통해서 스택을 관리한다.

SS 세그먼트 레지스터는 스택을 보관한 세그먼트를 정의한다.
ESP 레지스터는 스택으로 부터 빼내질 데이터의 주소를 보관한다.(top-최상위 에 있는 스택을 가르킴)

스택의 데이터는 오직 더블워드의 형태로만 저장된다. 따라서 단일 바이트를 집어 넣을 수 없다.
(32bit 보호모드에서는 가능은 하다.)
PUSH 명령은 ESP 레지스터의 값을 4 감소시킨 후(더블워드- 4byte),
[ESP]에 위치한 더블워드에 새로운 데이터를 집어 넣는다.
POP 명령은 [ESP]에 위치한 더블워드를 읽어 들이고,
ESP에 4를 더한다.
push dword 1 ;1 0FFCh , ESP=0FFCh
push dword 2 ;2 0FF8h , ESP=0FF8h
push dword 3 ;3 0FF4h , ESP=0FF4h
pop eax ;EAX=3, ESP=0FF8h
pop ebx ;EBX=2, ESP=0FFCh
pop ecx ;ECX=1, ESP=1000h
위 코드는 ESP가 1000H이였다고 할 때 명령에 따른 변화를 보여준다.
80x86에서는
PUSHAPOPA 명령을 이용하여,
EAX, EBX, ECX, EDX, ESI, EDI, EBP 레지스터의 값들을 모두 스택에 push 할 수 있게 한다.
CALL & RET
CALL과 RET 명령어는 80x86에서 서브프로그램을 빠르고 간편하게 호출하기 위한
스택을 이용하는 명령어이다.

CALL 명령은 서브프로그램으로 무조건 분기한 후, 그 다음에 실행될 주소를 스택에 푸시(push) 한다.
RET 명령은 그 주소를 팝(pop)한 후, 그 주소로 점프한다.
sub1.asm에서
mov ebx,input1 ;input1 ebx
mov ecx,ret1 ;ecx
jmp short get_int
ret1:
mov eax,prompt2
call print_string
mov ebx,input2
mov ecx, $+7 ;ecx=+7
jmp short get_int
get_int:
call read_int
mov [ebx],eax ;input
jmp ecx ;
위의 코드들을 CALL과 RET 을 이용하여
mov ebx,input1
call get_int
mov ebx,input2
call get_int
get_int:
call read_int
mov [ebx],eax
ret
와 같이 짧고 간결하게 바꿀 수 있다.
get_int:
call read_int
mov [ebx],eax
push eax
ret
위의 코드와 같이 push된 모든 데이터들이 pop 되지 않을시 올바르게 리턴이 안될 수가 있다.
따라서 스택에 push된 모든 데이터는 나중에 반드시 pop 되어야 한다.