C H A P T E R

12
ㅔ

프로시저

공부할 내용

★ 프로시저가 있는 프로그램의 설계

★ 프로시저의 정의, 호출

★ 복귀 주소, CALL과 RET의 사용

★ 엔트리 포인트

★ 파라미터의 전달: 레지스터, 스택 이용

★ 캐리 플래그를 사용한 에러의 표시

★ 여러 조각으로 프로그램을 나누어 작성하는 법, 링크하는 법

프로시저가 있는 프로그램의 설계

★ 긴 프로그램을 작은 프로시저로 나눌 때 사용되는 규칙

(1) 하나의 프로시저는 한 가지의 기능만을 수행하도록 만든다.

(2) 프로시저의 크기를 작게 유지한다.

★ 프로시저의 시작: PROC 지시어

프로시저의 끝:  ENDP 지시어

★ 프로시저의 호출: CALL 명령

호출한 프로시저로의 복귀: RET 명령

프로시저의 정의

(형식)  name    PROC    type

(예)	DISPLAY PROC

; save registers

PUSHA

; -the instructions of the procedure go here-

; restore registers

POPA

; return to calling program

RET

DISPLAY ENDP

프로시저 호출: CALL

(형식) CALL    address

- 주소(address): 직접 또는 간접 주소일 수 있다.

(예)	CALL    DISPLAY

CALL    TABLE[BX]

★ CALL 명령이 하는 일.

(1) 복귀 주소(프로시저가 실행을 끝내고 돌아가는 곳)를 스택에

푸쉬한다.

(2) 프로시저의 주소로 점프한다.

예)	CALL    DISPLAY

ADD     AX, 10

→ 여기에서 프로세서가 CALL 명령문을 실행할 때 복귀 주소는ADD 명령의 주소가 된다.

★ 호출된 프로시저가 FAR형:

프로세서는 완전한 주소를 스택에 푸쉬하여야 한다 (세그먼트 주소와 오프셋).

★ 호출된 프로시저가 NEAR형:

프로세서는 단지 오프셋만을 스택에 푸쉬한다.

CALL 명령문의 사용 규칙

(1) 어셈블러에게 프로시저가 NEAR형인지 FAR형인지를 알려주어야 한다 (형을 지정하지 않으면 NEAR형으로 간주됨)

(2) CALL 명령문에 조금이라도 애매한 점이 있으면 어셈블러에게 주소가 FAR형(full addr.)인지 NEAR형(offset)인지를 알려주어야 한다.

예) 간접 주소를 사용하는 경우

ADDR1   DW      DISPLAY1

→ 프로시저 DISPLAY1이 NEAR형인 경우, 이 프로시저의

주소인 오프셋이 ADDR1에 저장됨

ADDR2   DD      DISPLAY2

→ 프로시저 DISPLAY2가 FAR형인 경우, 이 프로시저의

완전한 주소가 ADDR2에 저장됨

★ 레지스터에 프로시저의 주소가 들어 있는 경우

예) BX에 프로시저의 주소가 들어 있다면 [BX]와 같이 쓴다.

- NEAR 포인터(ADDR1)를 사용하는 예제

LEA     BX, ADDR1

CALL    WORD PTR [BX]

- FAR 포인터(ADDR2)를 사용하는 예제

LEA     BX, ADDR2

CALL    DWORD PTR [BX]

프로시저로부터의 복귀: RET

★ 모든 프로시저는 자신을 호출한 프로시저로 복귀하도록 하여야 한다. (전체 프로그램도 DOS로 복귀하여야 한다.)

(형식) RET     pop-value

→ pop-value : 팝할 값 (뒤에 설명)

★ 대부분의 경우에는 오퍼랜드 없는 RET 명령을 사용한다.

RET

★ RET 명령이 하는 일

□ 스택으로부터 복귀 주소를 팝한다.

□ 복귀 주소로 점프한다.

★  프로시저의 형에 따라 스택에서 팝하는 것이 다름

(1) FAR형인 경우 - 프로세서는 스택으로부터 2워드

(오프셋과 세그먼트 주소)를 팝한다.

(2) NEAR형인 경우 - 프로세서는 스택으로부터 1워드(오프셋)만을

팝한다.

엔트리 포인트

★ 엔트리 포인트란?

- 프로시저가 호출될 때 실행이 시작되는 주소

- 프로시저를 호출한 CALL 명령문의 오퍼랜드에 있는 주소임.

예) CALL    DISPLAY

레지스터를 통한 파라미터 전달

★ 파라미터(매개변수, 전달인자)를 전달하는 방법

(1) 파라미터의 개수가 작을 때는 레지스터를 사용

(2) 스택을 사용

레지스터를 사용한 파라미터 전달의 예

(1) 호출될 프로시저: 프로시저 ADD_LIST

(2) ADD_LIST가 하는 일: 숫자로 된 리스트의 값을 모두 합한다.

결과는 CX 레지스터를 통해 전달.

(3) ADD_LIST가 필요로 하는 정보

리스트내의 숫자의 개수 → AX

리스트의 주소 → BX

(4) ADD_LIST를 호출할 프로시저

현재, ADD_LIST에 넘겨줄 리스트의 주소가 LIST이고,

그 리스트의 숫자의 개수가 COUNT에 저장되어 있다고 하자

(5) ADD_LIST를 호출할 프로시저가 하는 일

MOV     AX, COUNT

LEA      BX, LIST

CALL    ADD_LIST

(6) ADD_LIST 프로시저

............................

; restore registers

POPA

; set output parameter

MOV     CX, SUM

; return to calling procedure

RET

스택을 사용한 입력 파라미터의 전달

★ 호출하는 프로시저는 스택에 파라미터를 푸쉬한다.

★ 호출될 프로시저는 스택의 파라미터를 직접 액세스한다.

★ 호출될 프로시저가 호출한 프로시저에게 파라미터를 되돌려 줄 때도 스택을 사용한다.

예) 호출하는 프로시저가 하는 일

; set input parameters for CALCULATE

PUSH    P1

PUSH    P2

PUSH    P3

PUSH    P4

PUSH    P5

; call CALCULATE to perform calculations

CALL    CALCULATE

BP 레지스터를 사용하는 스택에 저장된 파라미터의 액세스

★ 호출될 프로시저가 받을 파라미터가 모두 푸쉬된 후에 CALL 명령문이 실행되므로, 복귀 주소가 스택의 탑에 있다.

즉, 호출될 프로시저가 복귀 주소를 잃지 않고는 파라미터를 스택에서 팝할 수 없다는 뜻이다.

→ 호출된 프로그램이 BP 레지스터를 사용하여 스택을 직접            액세스한다.

; start of procedure: CALCULATE

CALCULATE	PROC

; establish BP as a stack pointer

MOV     BP, SP

; save registers

PUSHA

; copy parameters to registers

MOV     AX,	[BP+10]   ; P1

MOV     BX,	[BP+8]    ; P2

MOV     CX,	[BP+6]    ; P3

MOV     DX,	[BP+4]    ; P4

MOV     DI,	[BP+2]    ; P5

스택에서 변하지 않는 포인터로서의 BP의 역할

BP+10 →	P1
BP+8 →	P2
BP+6 →	P3
BP+4 →	P4
BP+2 →	P5
BP →	복귀 주소
	레지스터의 값
	레지스터의 값
	□
	레지스터의 값	← SP(스택의 탑)
	□
		← SS(스택의 시작점)

FAR 프로시저에서 스택 안에 있는 파라미터를 액세스하는 방법

BP+12 →	P1
BP+10 →	P2
BP+8 →	P3
BP+6 →	P3
BP+4 →	P5
	세그먼트 주소
BP →	오프셋	← SP(스택의 탑)
	□
		← SS(스택의 시작점)

스택을 사용한 출력 파라미터의 전달

★ 출력 파라미터를 전달하는 데에도 스택을 사용할 수 있다.

이 값들을 스택의 탑에 있는 복귀 주소 위에 올려놓을 수는 없다.

→ 입력 파라미터가 있던 곳을 이용한다.

; set output parameter

MOV     [BP+2], AX

; restore registers

POPA

; return to MAIN

RET

★ 호출하는 프로시져가 호출 전후로 하는 일

PUSH    P1

PUSH    P2

PUSH    P3

PUSH    P4

PUSH    P5

CALL    CALCULATE

; copy result to AX

POP     AX

; pop the rest of the parameters off the stack

POP     TEMP

POP     TEMP

POP     TEMP

POP     TEMP

스택으로부터 파라미터를 팝하는 것

★ 출력 파라미터가 없거나 스택을 통하여 전달되지 않는 경우, 복귀되는 과정의 일부로써, 입력 파라미터를 자동적으로 스택으로부터 팝되어 없어지게 할 수 있다.

RET     pop-value

- pop-value는 복귀되는 과정에서 pop 될 바이트의 수이다.

예)

; set input parameters for MAKE_TABLE

PUSH    P1

PUSH    P2

PUSH    P3

PUSH    P4

PUSH    P5

; call MAKE_TABLE to construct the table

CALL    MAKE_TABLE

..........................

; pop input parameters and return to MAIN procedure

RET     12

캐리 플래그를 사용한 에러의 표시

★ 에러를 CF에 표시하여 호출한 프로시저로 돌려보내는 예

; indicate an error has occurred

BAD_DATA:

STC

JMP     RETURN_READ_DATA

; indicate that no error has occurred

GOOD_DATA:

CLC

; restore the registers and return to procedure MAIN

RETURN_READ_DATA:

POPA

RET

★ 호출한 프로시저는 CF를 검사하여 에러를 확인함

; set input parameters

- instructions to set input parameters go here -

; call READ_DATA to read the data from a file

CALL    READ_DATA

; check for error

JC      DISPLAY_ERROR_MESSAGE

여러 조각으로 나뉘어진 프로그램을 링크하는 법

★ 큰 프로그램을 여러 파일에 나누어 넣고, 따로따로 어셈블할 수 있다.

예) 두 파일(LARGE, SMALL)이 있다고 가정하면(MASM 사용시),

(1) 두 파일을 따로 어셈블한다.

MASM LARGE

MASM SMALL

(2) LARGE.OBJ와 SMALL.OBJ를 링커를 사용하여 링크한다.

LINK LARGE+SMALL, PROGRAM

(3) 실행 화일(PROGRAM.EXE)이 생성된다.

★ 다용도로 쓰일 오브젝트 모듈을 라이브러리에 저장하여 둘 수 있다.

★ 라이브러리내의 한 프로시저를 사용하는 프로그램이 있으면, 그 라이브러리를 링크시킬 때 쓰면 됨.

프로그램을 여러 조각으로 나누어 작성하는 법

★ 프로그램을 여러 조각으로 나누어 각 조각을 다른 파일에 저장할 수 있다. 각 파일에는 1개 또는 여러 개의 세그먼트가 있을 수 있다.

★ 주의 사항 : 한 파일에 있는 이름이 다른 파일에서 사용될 때, 이 사실을 어셈블러에게 알려 주어야 한다.(EXTRN, PUBLIC 지시어)

EXTRN - 다른 곳에서 정의되는 어떤 이름들을 해당 프로시저에서 사용하고자 할 때 이를 알려주는 역할을 한다.

PUBLIC - 해당 프로시저의 어떤 이름들이 다른 파일에서 사용되는가를 알려준다.

★ EXTRN 지시어

(형식)	EXTRN   name:type

(설명)	name: 자료 항목,

type: BYTE, WORD, DWORD, QWORD, TBYTE 중의 하나

name이 레이블인 경우에는 type은  NEAR 또는 FAR임

(예)	EXTRN   MSG2:BYTE

EXTRN   EXTERNAL_PROCEDURE:FAR

★ PUBLIC  지시어

(형식)

PUBLIC  name

(설명) 어떤 이름이 프로그램의 다른 곳에서 외부 참조될지를 알림

PUBLIC  MSG2

PUBLIC  EXTERNAL_PROCEDURE

프로그램 예제

□ 메시지 (MSG1)를 화면에 출력하라.

□ EXTERNAL_PROCEDURE를 호출하라.

□ 메시지 (MSG1)를 다시 화면에 출력하라.

(1) 두 조각짜리 프로그램의 첫째 부분

TITLE   Program that uses external references

.286

SSEG    SEGMENT STACK

DB      32 DUP ("STACK---")

SSEG    ENDS

EXTRN  EXTERNAL_PROCEDURE:FAR

PUBLIC  MSG2

DSEG    SEGMENT

MSG1    DB     "Hello from procedure #1",0DH,0AH,"$"

MSG2    DB     "Hello from procedure #2",0DH,0AH,"$"

DSEG    ENDS

CSEG    SEGMENT 'CODE'

ASSUME  CS:CSEG, SS:SSEG, DS:DSEG, ES:DSEG

MAIN    PROC    FAR

PUSH    DS

PUSH    0

MOV     AX,DSEG

MOV     DS,AX

MOV     ES,AX

; display message #1

MOV     AH,9H

LEA     DX,MSG1

INT     21H

; call the external procedure to display message #2

CALL    EXTERNAL_PROCEDURE

; display message #1 again

MOV     AH,9H

LEA     DX,MSG1

INT     21H

RET

MAIN    ENDP

CSEG    ENDS

END     MAIN

(2) 두 조각짜리 프로그램의 둘째 부분

TITLE   Externally called procedure

.286

EXTRN   MSG2:BYTE

PUBLIC  EXTERNAL_PROCEDURE

CSEG    SEGMENT 'CODE'

ASSUME  CS:CSEG

EXTERNAL_PROCEDURE PROC FAR

PUSHA

MOV     AH,9H

LEA     DX,MSG2

INT     21H

POPA

RET

EXTERNAL_PROCEDURE  ENDP

CSEG    ENDS

END

★ 어셈블, 링크는 다음과 같이 한다.

MASM FIRST

MASM SECOND

LINK FIRST+SECOND, PROGRAM

★  실행 결과

Hello from program #1

Hello from program #2

Hello from program #1