Team Project

Report
발표일 : 2013년 4월 18일
팀장 :박태영 / 팀원 : 조정민, 이지영, 이숭운, 권승현
박태영
구 분
성
팀장
팀원
명
핸 드 폰
이메일
비
고
박태영
010-6250-5242
[email protected]
m
5.4
조정민
010-3648-8055
[email protected]
5.1,5.2
이지영
010-9596-4948
[email protected]
om
5.3
이숭운
010-8831-7520
[email protected]
m
5.5
권승현
010-9580-8639
[email protected]
com
5.6
조정민
 이 장은 입출력과 문자열 처리와 관련된 일을 간단하게 사용할 수
있는 편리한 라이브러리를 소개하는 장이다.
 이 장은 서브루틴을 호출하여 프로그램을 어떻게 처리하기 쉬운
단위로 나누는가와
 프로그래밍 언어는 서브루틴 호출을 추적하기 위해서 실행 시간
스택을 어떻게 사용하는가에 대한 것도 알아볼 것이다.
5.3절에서 Irvine32.lib와 Irvine16.lib라는 이름의 링크
라이브러리로부터 프로시저를 어떻게 호출하는지를 보여준다.
조정민
메인 루틴에 대응되는
단어로, 프로그램 중의
하나 이상의 장소에서
필요할 때마다 반복해서
사용할 수 있는 부분적
프로그램으로, 그 자체가
독립해서 사용되는 일
없이 메인 루틴과 결부
시킴으로써 그 기능을
완수한다
 서브루틴 본체를 프로그램 중의 한 장소에만 실행시에 호출하는
방법을 폐쇄 서브루틴이라고 한다.
 서브루틴의 본체를 프로그램 중의 필요한 개소에 직접 전개하는
방식을 개방 서브루틴이라고 한다.
조정민
 링크라이브러리
기계어 코드로 어셈블된 프로시저를 포함하고 있는 파일
하나 이상의 소스 파일로 시작하며
소스 파일은 오브젝트 파일로 어셈블된다.
오브젝트 파일은 링커 유틸리티가 인식하는 특별한 형식의
파일에 삽입된다.
조정민
5.2.1 배경 정보
 링크 라이브러리는 기계어 코드로 어셈블된 프로시저를 포함하고
있는 파일이다. 다시 말해 링크 라이브러리 안에 있는 프로시저들은
메크로 비슷한 것이라고 보면 된다.
WriteString 프로시저를 호출하여 콘솔 윈도우에 문자열을 출력한다고
하자, 프로그램 소스는 WriteString을 식별하는 PROTO 디렉티브를
포함해야 한다.
문자열을 출력하는 프로시저
WriteString PROTO
Call WriteString
Call명령어는 WriteString을 실행한다.
프로그램이 어셈블될 때 어셈블러는 CALL 명령어의 목표 주소를
빈칸으로 두며, 이것은 링커에 의해 채워진다.
링커는 링크 라이브러리에서 WriteString을 찾아서 적절한 기계어
명령어들을 링크 라이브러리로부터 프로그램 실행파일로 복사한다.
만약 호출한 프로시저가(위의 WriteString) 링크 라이브러리에 없다면
링커는 오류 메시지를 내놓고 실행파일을 만들지 않는다.
조정민
조정민
 링커 명령 옵션
링커 유틸리티는 프로그램의 오브젝트 파일들을 하나 이상의 다른
오브젝트 파일과 링크 라이브러리와 합친다. 예를 들어 다음 명령은
hello.obj파일을 irvine32.lib와 kernel32.lib에 링크한다.
Link hello. obj irvine32.lib kernel32.lib
Kernel32라이브러리링크
Irvine32라이브러리와
Hello라는 이름의 오브젝트
파일을
링크시키는 link명령어
조정민
 32비트 프로그램 링크
마이크로소프트 Windows 플랫폼 소프트웨어 개발 키트의 일부인
kernel.lib 파일은 kernel32.dll이라는 파일에 있는 시스템 함수에
대한 링크 정보를 포함한다. Kernel32.dil은 MS-Windows의 기본이
되는 부분이며 동적 링크 라이브러리 라고 한다. 이 파일은 문자
기반 입출력을 수행하는 실행 함수를 포함한다.
내 프로그램
자동으로 링크
Link명령어로 링크
가능
Irvine32.lib
자
동
으
로
링
크
kernel32.lib
실
행
kernel32.dll
조정민
 Irvine32.lib는 입출력할 때에 MS-Windows API와
링크하는 피로시저를 포함한다.(32비트 보호모드에서
작성되는 프로그램을 위한 것)
 Irvine16.lib는 입출력할 때에 MS-DOS 인터럽트를
수행하는 프로시저를 포함한다.(16비트 실제주소
모드로 작성되는 프로그램을 위한 것)
이지영
•
링크 라이브러리(link library)는 기계어 코드로 어셈블된 프로
시저(서브루틴)를 포함하고 있는 파일이다.
• 링크 라이브러리는 하나 이상의 소스 파일로 시작하며, 소스
파일은 오브젝트 파일로 어셈블된다. 오브젝트 파일은 링커
유틸리티가 인식하는 특별한 형식의 파일에 삽입된다.
• 프로그램이 WriteString 프로시저를 호출하여 콘솔 윈도우에
문자열을 출력한다고 하자. 프로그램소스는 WriteString 프로
시저를 식별하는 PROTO 디렉티브를 포함해야 한다.
이지영
• WrtieString PROTO
• 다음에 CALL 명령어는 WriteString 을 수행한다.
• call WriteString
• 프로그램이 어셈블될 때에 어셈블러는 CALL 명령어의 목표 주
소를 빈캄으로 두며, 이것은 링커에 의해서 채워질 것이다. 링
커는 링크 라이브러리에서 WtringString 을 찾아서 적잘한 기계
어 명령어들을 링크 라이브러리로부터 프로그램 실행파일로
복사한다. 여러분이 호출한 프로시저가 링크 라이브러리에 없
다면 링커는 오류 메시시지를 내놓고 실행파일을 만들지 않는
다.
이지영
• ClossFile = (Irvine32전용) 이전에 생성하거나 열었던 파일을
닫음
• 파일은 EAX로 전달되는 32비트 정수 핸들로 식별됨.
• Clrscr = 콘솔 윈도우를 깨끗하게 지움. 일반적으로 프로그램
의 시작과 끝에서 호출.
• *먼저 WaitMsg를 호출하여 프로그램을 멈추게 할 필요가 있
다.
이지영
• Crlf = 콘솔 윈도우에서 커서를 다음 줄의 시작으로 가게 함.
ASCII문자코드 0Dh 와 0Ah값으로 구성되는 문자열을 출력.
• GetMseconds = EAX 레지스터에 자정이후의 경과시간을 mse
c 단위로 얻음.
• GetTextColor = 콘솔 윈도우에서 지정된 행과 열에 커서를 위
치. 기본적인 윈도우 X좌표 : 0~79, Y좌표 : 0~24
이지영
• DumpMem = 일정 범위 위치의 메모리를 콘솔 윈도우에 16진
수로 출력.
메모리의 시작주소-ESI,출력단위크기-EBX,출력단위개수-ECX로
전달.
• DumpRegs = EAX,EBX,ECX,EDX,ESI,EDI,EBP,ESP,EIP,EFL
레지스터를 16진수로 출력.
CF,SF,ZF,OF,AF,PF 플래그의 값도 출력.
• EIP의 출력 값은 DumpRegs에 대한 호출 다음 명령어의 오프
셋. DumpRegs는 CPU스냅샷을 출력.
이지영
• MsgBox = (Irvine32전용) 제목을 선택사항으로 갖는 그래픽 팝
업 메시지 상자를 표시한다. 상자 안에 나타낼 문자열의 오프셋
을 EDX 에 전달한다. 선택사항으로 상자의 제목 문자열의 오프
셋을 EBX에 전달한다.
• 제목을 빈칸으로 두려면 EBX를 0으로 설정한다.
• MsgBoxAsk = (Irvine32 전용) Yes아 No 버턴을 갖는 그래픽 팝
업 메시지 상자를 표시한다. 상자 안에 나타낼 질문 문자열의
오프셋을 EDX 에 전달한다 선택 사항으로 상자의 제목 문자열
의 오프셋을 EBX에 전달한다.
• 제목을 빈칸으로 두려면 EBX를 0으로 설정한다. MsgBoxAsk
는 EAX에 사용자가 어떤 버튼을 선택했는지를 알려주는 정수
IDYES(6과같음) 또는 IDNO(7과 같음)를 반환한다.
이지영
• ParseInteger32 = 부호있는 10진수 문자열을 32비트 이진수로 변환
한다. 숫자가 아닌 문자 앞에 있는 모든 유효한 자리 숫자가 변환된다.
앞에 있는 빈캄은 무시한다. 문자열의 오프셋을 EDX에, 문자열의 길
이를 ECX에 전달한다. 이진수는 EAX에 반환된다.
• Random32 = 32비트 난수 정수를 생성하고 EAX에 반환한다. 이 프
로시저가 반복적으로
• 호출될 때에 무작위로 발생하는 난수를 시뮬레이션하는 수열을 생성
하며, 이 수열의 각 수를 의사 난수 정수(pseudorandom integer) 라고
부른다. 숫자들은 시드(seed)라고 하는 입력을 갖는
• 간단한 함수를 사용하여 생성된다. 함수는 난수를 생성하는 공식에서
시드를 사용한다. 다음에이어지는 난수는 이전에 생성된 난수를 시드
로 사용하여 생성된다. 이러한 점 때문에 난수라는 용어는 의사 난수
를 의미한다.
이지영
• Randomize = Random32와 RandomRange 프로시저의 시작 시드값
을 초기화한다.
• 시드는 100분의 1초 단위의 시간 값과 같다. (Randomize) 프로그램
이 Random32와 RandomRange 를 호출할 때마다 생성되는 난수열
은 다를 것이고, 어떠한 나수열도 유일할 것이다. Randomize를 프로
그램의 시작에서 한 번만 호출할 필요가 있다.
• RandomRange = 0부터 n-1 까지의 범위의 난수 정수를 생성한다. 여
기서 n은 EAX레지스터에전달되는 입력 매개변수이다. 난수 정수는
EAX에 반환된다. 다음 예는 0과 4999 사이의 하나의 난수 정수를 생
성하여 EAX에 넣는다.
이지영
• ReadInt = 키보드에서 32비트 부호있는 정수를 읽어서 값을
EAX에 반환한다. 숫자 앞의 +와 - 기호는 선택사항으로 입력할
수 있으며 숫자의 나머지 자리는 숫자로만 구성될 수 있다.
• ReadInt는 Overflow 플래그를 1로 설정하고 입력된 값이 32비
트 부호 있는 정수로 표시될 수 없다면 오류 메시지를 표시한다.
반환값은 숫자가 아닌 문자를 만날 때까지의 모든 유효 자리로
부터 계산된다.
• ex) +123ABC를 입력한다면 반환되는 값은 +123 이다.
• ReadString = 키보드에서 문자열을 읽어 들이고 Enter 키를 누
를때에 멈춘다. 버퍼의 오프셋을 EDX에 전달하고 ECX를 (문
자열 종료 널 바이트에 대한 공간을 확보하기 위하여) 사용자가
입력할 수 있는 최대 문자수에 1을 더한 값을 설정한다. 프로시
저는 사용자가 입력한 문자수를 EAX에 반환한다.
이지영
• SetTextColor = (Irvine32 전용) 텍스트 출력에 대한 전경색과 배경색
을 설정한다.
• SetTextColor를 호출할 때에 색 속성을 AX에 지정한다. 다음의 미리
정의된 색상 상수가 전경색과 배경색 모두에 대해서 사용될 수 있다.
• 색상 상수는 include 파일 Irvine32.inc 와 Irvine16.inc 에서 정의된다.
배경색에 16을 곱하고 전경색을 더한다.
• WaitMsg = "Press any key to continue..." 라는 메시지를 표시하고 사
용자가 키를 누르기를 기다린다.
• 이 프로시저는 데이터가 화면이 스크롤되어 사라지기 전에 화면을 정
지시키고자 할 때에 유용하다. 이 프로시저는 입력 매개변수가 없다.
이지영
• WriteBin = 정수를 ASCII 이진수 형식으로 콘솔 윈도우에 출력
한다. 정수를 EAX에 전달한다.
• 이진수 비트는 읽기 쉽도록 4개씩 묶어서 표시된다.
• WriteBinB = 32비트 정수를 ASCII 이진수 형식으로 콘솔 윈도
우에 출력한다. 값을 EAX 레지스터에 전달하고 EBX는 표시할
크기를 바이트 단위 (1,2,3)로 지시하게 한다.
• 비트들을 읽기 쉽도록 4개씩의 그룹으로 표시된다.
이지영
•
WriteChar = 콘솔 윈도우에 하나의 문자를 출력한다. 문자(또는 문자의
ASCII)를 AL에 전달한다.
•
WriteDec 프로시저는 32비트 부호없는 정수를 앞에 0이 없는 10진수 형
식으로 콘솔 윈도우에 출력한다음 정수를 EAX에 전달한다
•
WriteHex 프로시저는 32비트 부호없는 정수를 8자리 16진수 형식으로
콘솔 윈도우에 출력한 후 필요하면 앞에 0들이 삽입된다. 정수를 EAX에
전달한다.
•
WriteHexB 프로시저는 32비트 부호없는 정수를 16비트 형식으로 콘솔
윈도우에 출력한다.
필요하면 앞에 0들이 삽입된다. 정수를 EAX에 전달하고 EBX는 표시할
크기를 바이트 단위(1,2,4)로 나타내게 한다.
•
•
WriteInt 프로시저는 32비트 부호있는 정수를 부호는 포함하고 앞에 0이
없는 10진수 형식으로 콘솔 윈도우에 출력한다. 정수를 EAX에 전달한다.
이지영
• WriteString 프로시저는 널 종료 문자열을 콘솔 윈도우에 출력한다.
문자열의 오프셋을 EDX에 전달한다.
• WriteToFile = (Irvine 32전용) 프로시저는 버퍼의 내용을 출력 파일에
저장한다. 유효한 파일 핸들을 EAX에 , 버퍼의 오프셋을 EDX에, 출
력할 바이트 수를 ECX에 전달한다.
• 프로시저가 반환될 때에 EAX는 출력된 바이트 수를 포함한다.
• WriteWindowsMsg = (Irvine32 전용) 가장 최근에 생성한 오류의 메
시지를 포함하는 문자열을 표시한다. 여러분의 프로그램이 파일을 생
성하거나 열 수 없을 때에 가장 유용한다.
• 다음 예는 입력을 위해서 파일 열기를 시도하고, 오류를 발견하면 오
류 메시지를 표시하려고 WriteWindowsMsg를 호출한다.
박태영
 자료 구조의 하나로, 자료의 삽입과 삭제가 끝에서만 일어나는 선형
목록
자료의 삽입, 삭제가 일어나는 곳을 스택의 톱(top)이라고 하며 자료를
스택에 넣는 것을 푸시(push), 스택에서 자료를 꺼내는 것을 팝(pop)이
라 한다
스택에 마지막으로 넣은 값이 항상 맨 먼저 꺼내는 값이기 때문에
스택은 LIFO(Lask-In First-Out) 구조라고도 부른다.
 새로운 접시는 스택의 맨 위에 추가할 수 있지만, 스택의 바닥이나,
중간에 추가할 수는 없음
박태영
 다음 그림과 같이 10개의 숫자를 쌓아 놓을 때에 만들어진 것을 스택
(stack)이라고 말할 수 있다.
10
9
8
7
6
5
4
3
2
1
TOP
새로운 값은 스택의 맨 위에
추가되고 스택의 기존 값은
맨 위부터 제거됨
bottom
스택에 마지막으로 넣은 값이 항상 맨 먼저 꺼내는 값이기 때문에
스택은 LIFO(Last-In First-Out) 구조라고도 부른다.
 실행시간 스택 : 스택 포인터 레지스터라고 하는
ESP레지스터를 사용하여 CPU가 직접 관리하는 메
스택에 있는 어떤
모리 배열
ESP는 항상 스택에서 푸쉬 된
마지막 정수를 가리킨다.
위치에 대한 32비트
오프셋을 저장함
 ESP(extended destination index): 직접 조작하는 일은
거의 없으며 CALL,RET,PUSH,POP과 같은 명령어를 사
용하여 간접적으로 수정함
박태영
Offset
000001000
00000FFC
00000006
ESP = 00001000h
*단일 값을 포함하는 스택
00000FF8
00000FF4
00000FF0
각 스택의 위치는 32비트를
저장함
이것은 프로그램이 32비트 모드에서 수행되고 있는 경우임
16비트 실제 주소 모드에서 SP(Stack Point)레지스터가 가장 최근에 푸쉬
된 값을 가리키며 스택 엔트리들은 대개 16비트 길이임
박태영
5.4.2 Push 연산
그림1
000001000
32비트 push 연산은 스택 포인터를 4씩
감소시키고 값을 스택 포인터가 가리키는
스택 위치로 복사함
BEFORE
00000006
ESP
00000FFC
00000FF8
00000FF4
그림2
AFTER
00000FF0
ESP
00000006
000001000
000000A5
00000FFC
00000FFC
00000FF4
00000FF0
박태영
5.4.2 Pop 연산
BEFORE
000001000
00000FFC
AFTER
00000006
00000006
000001000
000000A5
000000A5
00000FFC
00000001
00000FFC
00000002
00000FF4
00000FF8
00000001
00000FF4
00000002
ESP
ESP
00000FF0
00000FF0

Pop연산은 스택에서 팝된 후에 스택 포인터는 스택의 그 다음
높은 주소를 가리키도록 증가된다
 ESP 아래의 스택 영역은 논리적으로 빈 상태(logically empty)이며
현재의 프로그램이 스택에 값을 푸쉬하는 명령어를 수행할 때 덮어
쓰임
박태영
• 프로그램에서 실행시간 스택의 몇 가지 중요한 용도는 다음과 같다
1. 스택은 레지스터가 한 가지 이상의 목적으로 사용될 때에 레지스터
를 위한 간편한 임시 저장 영역으로 사용된다. 레지스터가 수정된 뒤
에 원래의 값으로 복원될 수 있다.
2. CALL 명령어를 수행할 때에 CPU는 현재 서브루틴의 복귀 주소를
스택에 저장한다.
3. 서브루틴을 호출할 때에 인수(argument)라고 하는 입력 값을 스택에
푸쉬하여 전달한다.
4. 스택은 서브루틴 내의 지역 변수를 위한 임시 저장 공간을 제공한다.
박태영
PUSH 명령어는 먼저 ESP를 감소시키고 다음에 소스 피연산자
를 스택에 복사한다.
16비트 피연산자는 ESP를 2씩 감소시킨다.
32비트 피연산자는 ESP를 4씩 감소시킨다.
세개의 명령어 형식이 있다
PUSH reg/mem16
PUSH reg/mem32
PUSH imm32
명령어 피연산자
표기법 p111 참조
박태영
POP명령어는 먼저 ESP가 가리키는 스택 원소의 내용을
16비트 또는 32비트 목적지 피연산자로 복사하고 다음에
ESP를 증가시킨다.
피연산자가 16비트이면 ESP는 2씩 증가되며 32비트이면
ESP는 4씩 증가된다.
POP reg/mem16
POP reg/mem32
박태영
• (PUSH Flags Double) (POP Flags Double)
 PUSHFD 명령어는 32비트 EFLAGS 레지스터를 스
택에 푸쉬한다
 POPDF 명령어는 스택을 EFLAGS로 팝 한다
 32비트 피연산자는 ESP를 4씩 감소시킨다
16비트 프로그램은 16비트 FLAGS 레지스터를 스택에 푸쉬하기 위해서
PUSHF 명령어를 사용하고
FLAGS로 팝 하기 위해서 POPF를 사용한다.
박태영
(PUSH All Double) (POP All Double)
 PUSHAD 명령어는 모든 32비트 범용 레지스터를 EAX, ECX,
EDX, EBX,ESP (PUSHAD를 실행하기 이전 값),
EBP,ESI,EDI의 순서로 푸쉬한다
 80286에서 도입된 PUSHA 명령어는 16비트 범용 레지스터
들(AX,CX,DX,BX,SP,BP,SI,DI)을 나열된 순서대로 스택에 푸
쉬 한다
인텔 80286은 1982년에 개발된 16비트
개인용 컴퓨터 마이크로프로세서이다.
 POPAD 명령어는 같은 레지스터들을 반대 순서로 스택에서
팝 한다
 POPA 명령어는 같은 레지스터들을 반대 순서로 팝 한다
이숭운
 고급언어를 이미 배웠다면 프로그램을 서브루틴으로 나누는 것이
얼마나 유용한지 알 것이다.
 어셈블리 언어에서는 일반적으로 서브루틴의 의미로 프로시저라는
용어를 사용한다.
 어셈블리 언어는 객체지향 프로그래밍보다 오래 전에 만들어져서
객체지향 언어에서 볼 수 있는 형식 구조로 되어있지 않다.
이숭운
 프로시저의 정의
 비공식적으로 프로시저는 이름이 부여된, return 문으로 끝나는
문장들의 블록으로 정의할 수 있다.
 프로시저는 PROC와 ENDP 디렉티브를 사용하여 선언된다.
프로시저는 (유효한 식별자인) 이름을 부여 받는다.
우리가 지금까지 작성했던 각 프로그램은 다음 예와 같이
main 이라는 이름의 프로시저를 가지고 있다.
main PROC
.
.
main ENDP
이숭운
 프로그램의 시작 프로시저가 아닌 다른 프로시저를 만들 때 프로시저는
RET 명령어로 끝난다. RET는 CPU가 프로시저를 호출했던 위치로
되돌아가게 한다.
sample PROC
.
.
ret
sample ENDP
 시작 프로시저는 (main)는 exit 문으로 끝나기 때문에 특별한 경우이다.
INCLUDE Irvine32.inc 문을 사용할 때, exit는 프로그램을 종료하는
시스템 프로시저인 ExitProcess 를 호출하는 것에 대한 별명이다.
INVOKE ExitProcess, 0
INCLUDE Irvine16.inc 문을 사용한다면 exit는 .EXIT 어셈블러 디랙티브로 변환된다.
이 디렉티브는 어셈블러가 다음의 두 명령어를 만들도록 한다.
mov ah, 4C0 0h
int 21h
;
;
call MS - DOS function 4ch
terminate program
이숭운
 프로시저의 레이블
 기본적으로 레이블은 선언된 프로시저 내에서만 사용할 수 있다.
이 규칙은 점프와 루프 명령어에 영향을 준다.
다음 예에서 Destination이라는 레이블은 JMP 명령어와 같은
프로시저에 있어야 한다.
jmp Destination
 이중 콜론(::)으로 표시하는 레이블을 선언하여 이 제한을 벗어나는
것이 가능하다.
Destination ::
이숭운
 프로시저는 실행시간 스택을 반환하고 조정하는 자동화된 방식을
가지고 있다.
 프로시저 바깥으로 직접 이동한다면 실행시간 스택은 쉽게 훼손될
수 있다.
예제: 세 개의 정수의 합
세 개의 32비트 정수의 합을 계산하는 Sumof라는 프로시저를
만들어 보자. 관련된 정수는 호출되기 전에 EAX, EBX, ECX에
저장되어 있다고 가정할 것이다. 프로시저는 EAX에 합을 반환한다.
SumOf PROC
add eax, ebx
add eax, ecx
ret
SumOf ENDP
이숭운
 프로시저의 문서화
다음은 프로시저의 시작 부분에 넣을 수 있는 정보에 대한 몇 가지 제안이다.
 프로시저가 수행하는 모든 작업에 대한 설명
 입력매개변수와 사용법. Receives와 같은 단어를 레이블로 사용하며
입력 매개변수의 입력 값에 대한 특정 요구사항이 있으면 여기에
기재하라.
 프로시저가 반환하는 값에 대한 설명. Returns과 같은 단어를
레이블로 사용한다.
 프로시저가 호출되기 이전에 충족되어야 하는 전제조건(precondition)
이라고 하는 특별한 요구사항에 대한 목록. 이것은 Requires라는
단어를 레이블로 사용할 수 있다.
우리가 선택한 Receives, Returns, Requires와 같은 설명 레이블은 절대적인 것이 아니다;
다른 유용한 이름들이 종종 사용된다.
이숭운
이러한 생각들을 유념하면서 SumOf 프로시저를 적절히 문서화 해보자
;---------------------------------------------------------------------------------------------SumOf PROC
;
; Calculates and returns the sum of three 32-bit integers.
; Receives : EAX, EBX, ECX, the three integers. May be
;
signed or unsigned.
; Returns : EAX = sum
;---------------------------------------------------------------------------------------------add eax, ebx
add eax, ecx
ret
SumOf ENDP
C와 C++와 같은 고급언어로 작성된 함수는 8비트 값은 AL에,
16비트 값은 AX에, 그리고 32비트 값은 EAX에 반환한다.
이숭운
 CALL 명령어는 프로세서가 새로운 메모리 위치에서 수행을
시작하도록 지시하여 프로시저를 호출한다.
 프로시저는 RET(return from procedure) 명령어를 사용하여
프로세서가 프로시저를 호출했던 프로그램 위치로 되돌아오게
한다.
 기계적으로 말하면 CALL 명령어는 복귀 주소를 스택에 푸쉬하고
호출되는 프로시저의 주소를 명령어 포인터로 복사한다.
이숭운
 프로시저가 반환될 준비가 될 때에 RET 명령어는 복귀 주소를
스택에 명령어 포인터로 팝한다.
 32비트 모드에서 CPU는 EIP(instruction)가 가리키는 메모리에서
명령어를 수행한다. 16비트 모드에서는 IP가 명령어를 가리킨다.
이숭운
 호출과 반환 예제
main 에서 CALL 명령어가 오프셋 00000020에 있다고 가정하자.
이 명령어는 일반적으로 5 바이트의 기계어 코드를 필요로 한다.
그래서 다음 문장(여기서는 MOV)은 오프셋 00000025에 위치한다.
00000020
00000025
main PROC
call
MySub
mov eax, ebx
다음에 MySub의 첫 번째 명령어가 오프셋 00000040에 있다고 하자.
00000040
MySub PROC
mov eax, edx
.
.
ret
MySub ENDP
이숭운
CALL 명령어가 실행될 때에 (그림 5-8) call의 다음주소(00000025)가
스택에 푸쉬되고 MySub의 주소가 EIP에 적재된다. MySub에 있는
모든 명령어는 RET 명령어를 만날 때까지 실행된다.
RET 명령어가 실행될 때에 ESP가 가리키는 스택 값이 EIP로
팝된다. (그림 5-9의 단계1)
단계 2에서 ESP는 감소하여 스택의 이전 값을 가리킨다.(단계 2)
step 1:
????
00000025
ESP
ESP
????
00000025
EIP
00000025
00000040
EIP
step 2:
ESP
그림 5-8 CALL 명령어 수행
????
그림 5-9 RET 명령어 수행
이숭운
 중첩된 프로시저의 호출
중첩된 프로시저의 호출(nested procedure call)은 호출된
프로시저가 처음 프로시저로 변환되기 전에 다른 프로시저를 호출 할
때 일어난다.
(ret to main)
(ret to Sub1)
(ret to Sub2)
main
ESP
PROC
.
.
call Sub1
exit
main ENDP
Sub1 PROC
.
.
call Sub2
ret
Sub ENDP
그림 5-10 중첩된 프로시저의 호출
Sub2 PROC
.
.
call Sub3
ret
Sub2 ENDP
Sub3 PROC
.
.
ret
Sub3 ENDP
이숭운
반환된 후에 ESP는 그 다음 높은 주소의 스택 엔트리를 가리킨다.
Sub2의 끝의 RET 명령어가 실행되려고 할 때 스택은 다음과 같이
나타난다.
(ret to main)
(ret to Sub1)
ESP
마지막으로 Sub1이 반환될 때에 스택 [ESP]은 명령어 포인터로
팝되고 프로그램 실행은 main에서 계속된다.
(ret to main)
ESP
확실히, 스택은 중첩된 프로시저 호출을 포함하여, 정보를 기억하는 데
유용한 장치임이 입증되었다.일반적으로 스택 구조는 프로그램이
특정한 순서로 수행한 단계를 재추적해야 하는 상황에서 사용된다.
이숭운
 레지스터 인수의 프로시저 전달
 정수 배열의 합을 계산하는 것과 같은 표준 연산을 수행하는
프로시저를 작성한다면 프로시저 내에서 특정한 변수를 참조하는
것은 좋지 않다. 그렇게 한다면 프로시저는 특정한 한의 배열에만
사용 될 수 있다.
 더 좋은 방법은 배열의 오프셋을 프로시저에 전달하고 배열 원소의
명시하는 정수도 전달하는 것이다. 이것들을 인수(argument) 또는
입력 매개변수(input parameter)라고 한다.
어셈블리 언어에서는 인수를 범용레지스터로 전달하는 것이
일반적이다.
이숭운
이전 절에서 EAX, EBX, ECX에 있는 정수를 더하는 SumOf라는
간단한 프로시저를 만들었다. main에서 SumOf를 호출하기 전에
EAX, EBX, ECX에 값을 지정한다.
.data
theSum DWORD ?
.code
main PROC
mov eax, 10000h
mov ebx, 20000h
mov ecx, 30000h
call
Sumof
mov theSum, eax
;
;
;
;
;
argument
argument
argument
EAX = (EAX + EBX + ECX)
save the sum
CALL 문 뒤에 EAX에 있는 합을 변수로 복사하는 동작은 선택사항이다.
이숭운
 C++이나 Java로 이미 작성해 보았을 지도 모르는 매우 일반적인
루프의 유형은 정수 배열의 합을 계산하는 것이다.
 이것은 어셈블리 언어로 매우 쉽게 구현되며 가능한 한 빠르게
수행되도록 작성될 수 있다. 예를 들어서 루프 안에서 변수 대신에
레지스터를 사용할 수 있다.
이숭운
호출하는 프로그램에서 32비트 정수 배열의 포인터와 배열 값의
개수인 두 개의 매개 변수를 받는 ArraySum 프로시저를 만들어 보자.
이 프로시저는 배열의 합을 계산하여 EAX로 반환한다.
;--------------------------------------------------------------------ArraySum PROC
;
; Calculates the sum of an array of 32-bit integers.
; Receives : ESI = the array offset
;
ECX = number of elements in the array
; Returns : EAX = sum of the array elements
;--------------------------------------------------------------------push esi
; save ESI, ECX
push ecx
mov eax, 0
; set the sum to zero
L1: add eax, [esi]
; add each integer to sum
add esi, TYPE DWORD
; point to next integer
loop L1
; repeat for array size
pop ecx
; restore ECX, ESI
pop esi
ArraySum ENDP
이 프로시저에는 배열 이름이나 배열 크기에 대해서 아무것도 특정하게
지정되지 않았다. 이 프로시저는 32비트 정수 배열의 합이 필요한
어떠한 프로그램에서도 사용될 수 있다.
이숭운
ArraySum 호출 다음은 array의 주소를 ESI에, 배열 카운트를 ECX에
전달하는 ArraySum 을 호출하는 예이다. 호출한 후에 EAX에 있는 합을
변수로 복사한다.
.data
array
DWORD 100000h, 200000h, 300000h, 400000h, 500000h,
theSum DWORD ?
.code
main PROC
mov
esi, OFFSET array
; ESI point to array
mov
ecx, LENGTHOF array
; ECX = array count
call
ArraySum
; calculate the sum
mov
theSum
; returned in EAX
이숭운
 순서도는 프로그램 논리를 그리는 잘 확립된 방법이다.
 순서도에서 각 도형은 하나의 논리 단계를 나타내고
도형을 연결하는 화살표 선은 논리단계의 순서를 보여준다.
이숭운
 기본 순서도 도형
분기 방향을 보여주기 위해서
yes와 no와 같은 텍스트 표기를
결정(decision) 심볼 옆에
붙인다
begin/end
yes
decision
Process (task)
프로세스(process) 심볼은 하나
이상의 밀접하게 관련되는
명령어를 포함한다.
no
Procedure call
 명령어들은 구문적으로 올바를 필요는 없다.
ex) CX에 1을 더하는 데에 다음 프로세스 심볼을 모두 사용할 수 있다.
cx = cx + 1
add cx, 1
이숭운
 ArraySum 프로시저에 대한 순서도
begin
push esi, ecx
eax = 0
add eax.[esi]
add esi, 4
push esi
push ecx
mov eax, 0
AS1:
add eax, [esi]
add esi, 4
loop AS1
ecx = ecx – 1
pop ecx
pop esi
yes
ecx > 0?
pop ecx, esi
end
 LOOP가 레이블로 제어권을
전달하는지를 (CX 값에 따라서)
결정해야 하기 때문에 LOOP
명령어에 대해서 결정 심볼을
사용한다. 함께 넣은 코드는 원래의
프로시저의 소스를 보여준다
이숭운
 ArraySum 예에서 ECX와 ESI는 프로시저의 시작에서 스택에
푸쉬되고 끝에서 팝된다.
 프로시저가 수정하는 레지스터는 항상 복원하여, 호출하는 프로그램이
어떠한 레지스터 값도 바뀌지 않음을 확신할 수 있도록 한다.
 이 규칙에 대한 예외는 반환값으로 사용하는 레지스터(대개 EAX)에
적용된다. 이 레지스터들은 푸쉬와 팝을 하지 않는다.
이숭운
 USES 연산자
 USES는 어셈블러가 두 가지 일을 하도록 알려준다.
1. 프로시저의 시작에 스택에 레지스터를 저장하는 PUSH 명령어 생성한다.
2. 프로시저의 끝에 레지스터값을 복원하는 POP명령어를 생성한다.
 USES 연산자는 PROC의 바로 뒤에 오며 레지스터들을 같은 줄에
(컴마가 아닌) 빈칸이나 탭으로 구분하여 나열한다.
이숭운
 5.5.3절의 ArraySum 프로시저는 ESI 와 ECX 를 저장하고 복원하기 위해서 PUSH 와
POP명령어를 사용했다
ArraySum PROC USES esi ecx
mov
eax, 0
L1 :
add
eax, [esi]
add
esi , TYPE DWORD
ret
ArraySum ENDP
; set the sum to zero
; add each integer to sum
; point to next integer
; sum is in EAX
디버깅 팁
 어셈블러가 생성한 코드는 USES 의 사용 결과를 보여준다.
ArraySum
push
push
mov
L1 :
add
add
loop
PROC
esi
esi
eax, 0
eax, [esi]
esi , TYPE DWORD
L1
; set the sum to zero
; add each integer to sum
; point to next integer
; repeat for array size
마이크로소프트 비쥬얼 스튜디오 ( Visual
Studio) 디버거를 사용할 때에, 여러분은
MASA의 고급 연산자와 디렉티브가
생성한 숨겨진 기계어 명령어를 볼 수
있다. View 메뉴에서 Debug windows를
선택하라. 그리고 Disassembly를
선택하라. 이 윈도우는 프로그램 소스
코드를 어셈블러가 생성한 숨겨진 기계어
명령어와 함께 표시한다.
pop
ecx
pop
esi
ret
ArraySum ENDP
[예외] 프로시저가 레지스터(대개 EAX) 에 값을
변환할 때에 적용되는 레지스터 저장에
대해서 우리가 새로운 규칙에 대한 중요한
예외가 있다. 이 경우에 반환 레지스터는
푸쉬되거나 팝되지 않아야 한다. 예를 들어서
SumOf 프로시저에서 EAX를 푸쉬하고
팝한다면 프로시저의 반환값은 없어질 것이다
SumOf PROC
push
eax
add
eax, ebx
add
eax, ecx
pop
eax
SumOf ENDP
;
;
;
;
;
sum of three integers
save EAX
calculate the sum
of EAX, EBX, ECX
lost the sum !
권승현
• 기능적 분해 or 하향식 설계
• 큰 문제는 작은 작업으로 쉽게 나누어 질 수 있음
• 프로그램은 각 프로시저를 개별적으로 테스트하면 관리
하기 더 쉬움
• 하향식 설계는 프로시저가 서로 어떻게 관련되는지 알
수 있음
• 전체 설계가 확실히 되었을 때, 상세한 부분에 더 쉽게 집
중할 수 있음
권승현
• 프로그램 작성시 명세[설명서]를 작성
• 전체 문제를 개별 작업으로 나눔
• 기능적 분해 or 하향식 설계
• 프로그램은 각 프로시저를 개별적으로 테스트하면 관리
하기 더 쉬움
• 하향식 설계는 프로시저가 서로 어떻게 관련되는지 알
수 있음
• 전체 설계가 확실히 되었을 때, 상세한 부분에 더 쉽게 집
중할 수 있음
권승현
Summation
Program (main)
Clrscr
PromptForIntegers
WriteStrin
g
ReadInt
ArraySum
DispalySu
m
WriteString
WriteInt
권승현
Integer Summation Program
Prompt user for three integers
Calculate the sum of the array
Display the sum
 프로그램 작성에 대비하여 프로시저 이름을 각 작업에 부여
Main
• PromptForIntegers
• ArraySum
• DisplaySum
권승현
Main
• Clrscr
• PromptForIntegers
 WriteString
 Read Int
•
•


ArraySum
DisplaySum
WriteString
WriteInt
;화면을 지움
;문자열 출력
;정수들의 합
;정수열 출력
;정수열 입력
권승현
 최소 버전의 프로그램
 이 프로그램은 빈(또는 거의 빈) 프로시저만 포함한다.
 프로그램은 어셈블 되고 실행되지만, 어떠한 유용한 일도 하지 않는
다.
 스터브 프로그램은 모든 프로시저 호출에 대한 계획을 수립하고,
 프로시저 간의 종속성을 검토하고 세부 동작을 코딩 하기 전에 구조
적인 설계를 개선할 기회를 제공한다.
권승현
TITLE Integer Summation Program
;이 프로그램은 사용자를 위해 세 개의 정수를 계산
;한 배열에 있는 수들을 저장, 그 배열의 정수들의 합을 계산,
;그리고 화면에 합계를 출력한다.
INCLUDE Irvine32.inc ; 헤더파일
.code
main PROC
;출력: Clrscr, PromptForIntegers, ArraySum, DisplaySum
exit
Main ENDP
권승현
권승현
PromptForIntegers PROC
;유저를 위해 세계의 정수를 계산, 한 배열 안에 정수들을 입력
;받음: ESI - 8바이트로 구성되는 워드 정수 값, ECX = 배열 크기
;반환: 없음
;출력: ReadInt, WriteString
ret
PromptForIntegers ENDP
ArraySum PROC
;32비트의 정수로 이루어진 배열의 합을 계산
;받음: ESI 배열로 나타냄, ECX = 배열 크기
;반환: EAX = 배열 원소들의 합
ret
권승현
DisplaySum PROC
;화면에 합을 출력
;받음: EAX = 합
;반환: 없음
;출력: WriteString, WriteInt
ret
DisplaySum ENDP
END main
//스터브 프로그램 종료
권승현
INCLUDE Irvine32.inc ; 헤더파일
Integer_COUNT = 3 ; 세 개의 정수의 배열 선언
.data
str1 BYTE “Enter a signed integer : “ , 0 ; 화면의 안내문
str2 BYTE “The sum of the integers is : “ , 0 ; 화면의 안내문
array DWORD INTEGER_COUNT DUP ( ? ) ; 배열 크기에 대해 변경 할 시
.code
권승현
main PROC
; main 함수 시작
call Clrscr ; 화면 지움
mov esi, OFFSET array ; esi 값에 array 배열 값을 복사
mov ecx, INTEGER_COUNT ; ecx값에 INTEGER_COUNT값을복사
call PromptForIntegers ; 프로시저 배열 포인터를 전달
call ArraySum ; 호출
call DisplaySum ; 호출
exit ; 종료
main ENDP ; main 함수 종료
권승현
PromptForIntegers PROC USES ecx edx esi ; 함수 시작과 레지스터 이름 열
거
mov edx, OFFSET str1
; edx 값에 str1 값을 복사
L1 : call WriteString ; 정수 입력 안내문 출력 반복
call ReadInt ; 정수를 입력 받음
call Cr.lf ; 개행
mov [esi] , eax ; [esi] 주소에 eax 값을 복사
add esi, TYPE DWORD ; 정수를 esi 배열에 추가한다
다음 정수로
loop L1 ; 루프 반복
ret ; EAX = 배열의 원소들의 합
PromptForIntegers ENDP ; 함수 종료
권승현
ArraySum PROC USES esi ecx ; 함수 시작과 레지스터 이름 열거
mov eax , 0
; eax를 0으로 초기화
L1 : add eax , [esi] ;덧셈 연산 루프 반복 eax+esi[주소값]
add esi, TYPE DWORD ; 정수를 esi 배열에 추가한다
loop L1 ; 배열 크기로 반복
ret ; 종료 - 합의 값은 EAX
ArraySum ENDP - 함수
DisplaySum PROC USES edx ; 함수 시작과 레지스터 이름 열거
mov edx, OFFSET str2 ; edx값에 str2 주소값을 복사 - 결과
call WriteString ; 문자열 출력
call WriteInt ; 정수 출력 – EAX 값
call C r.lf ; 개행
ret ; EAX 반환
DisplaySum ENDP ; 함수 종료
END main ; main 함수 종료

similar documents