2007. 2. 15. 17:42
DevX Win32 Assembly Tutorial 1: The Basic

Tutorial 1: The Basic

본 튜토리얼의 독자는 MASM의 사용법을 이미 알고 있는 사람을 대상으로 한다. 만약 MASM에 대하여 자세히 알지 못한다면, 본 튜토리얼을 읽기 전에 win32asm.zip 를 다운로드한 후, 패키지에 들어 있는 파일로 먼저 공부하기 바란다.(영문자료이다.) 그럼 이제 준비가 되었다고 가정하고 시작하겠다.

Theory:

Win32 프로그램은 80286 CPU부터 사용할 수 있고 프로텍트 모드로 동작한다. 그러나, 80286 CPU는 현재는 역사속으로 사라졌기 때문에, 80386 CPU 이상으로 강좌를 진행하겠다. 윈도우즈는 Win32 프로그램을 각각 개별적으로 분리된 가상공간에서 실행한다. 즉, 각각의 Win32 프로그램은, 자신만의 전용의 4 GB의 주소공간을 가지고 있는 것이다. 4 GB의 주소공간이라는 것은, 실제로 메인보드에 장착되어있는 메모리용량을 뜻하지 않고, 프로그램이 사용 할 수 있는 메모리의 범위가 4 GB(즉 32bit)라는 것이다. 윈도우즈는 프로그램이 사용하는 주소가 유일하도록 지정할 필요가 있다. 물론, 각각의 프로그램은 윈도우즈의 엄격한 룰을 따라야만 한다. 이러한 룰을 따르지 않는다면 , 보호 에러(일반적으로 강제 종료라고도 불린다)의 공포로부터 피할 수가 없다.각각의 프로그램은 각자의 주소공간에 대해 독립적으로 실행된다. 이것은 Win16(DOS)과는 대조적인 것으로, Win16에서는 다른 프로그램의 메모리라도 중첩하여 사용할 수 있다 . 다만, Win32에서는 그런 것은 허용되지 않기 때문에, 어떤 프로그램이 다른 프로그램의 코드나 데이터를 수정하는 일이 줄어들게 되었다.

메모리모델도 예전의 16 bit 시대와는 완전히 바뀌었다. Win32에서는, 메모리모델이나, 세그먼트(segment)등에 대해서 전혀 신경쓰지 않아도 된다는 것이다! 즉, 「플랫메모리모델」만 존재하므로,DOS시절의  64K 세그먼트(segment) 제한은 이제 없어졌다. 플랫메모리모델은, 연속적인 4 GB의 메모리 공간이 확보되어 있기 때문에 세그먼트(segment) 레지스터를 이리 저리 조합해서 운영할 필요가 없어졌다. 임의의 세그먼트(segment) 레지스터를 사용해, 4 GB의 메모리공간의 어떤곳이라도 지정해서 엑세스 할 수가 있게 된것이다. 이로인해, Win32상에서, 어셈블리 프로그래밍이 C언어와 같이 쉽게 되었다.

Win32로 프로그램을 만든다면, 몇가지의 중요한 룰을 지키야만 한다.그 중 하나는, 윈도우즈는 esi, edi, ebp, ebx 를 내부적으로(프로그래머가 모르게) 사용하고 있다는 것이다. 그렇기 때문에, 이들 레지스터의 값이 언제 바뀌게 될지 모른다는 것이다. 일단은 이 룰을 기억한다. 만약, 이들 4개의 레지스터를 콜백 함수로 사용하면, 윈도우즈로 제어를 옮기기 전에 반드시 이들 레지스터의 상태를 복구해야한다는 것을 절대로 잊지 말기 바란다. 프로그래머가 등록한 콜백 함수는 윈도우즈로부터 호출된다. 이것의 좋은 예로는, 윈도우즈 프로시저(WinProc)이다. 윈도우 프로시저에서 상기의 4개의 레지스터를 사용할 수 없다는 말은 아니지만, 윈도우즈로 제어를 돌려줄 때에는 반드시 원래의 값을 복구해 주는 것을 절대 잊지 말기 바란다.

Content:

아래의 프로그램은 Win32 프로그램의 표준 뼈대코드이다. 만약 이 프로그램에 대해 모른다해도 걱정하지 않아도 된다. 본 설명서가 그것을 이해하도록 설명하는 것이다.대부분의 프로그램이 아래와 같은 구조로 이루어져있다.

.386
.MODEL Flat, STDCALL
.DATA
  <Your initialized data>
   ......
.DATA?
  <Your uninitialized data>
  ......
.CONST
  <Your constants>
  ......
.CODE
  <label>
  <Your code>
  .....
   end <label>

이것이 전부이다. 자, 그러면―, 설명을 시작 하겠다. (실제코드를 담는 큰 틀을 의미한다)

.386

이 지시어는, 80386 CPU의 명령어세트를 사용하라는 것을 MASM에게 지시하는 의사명령이다.(어셈블러 지시어) 386뿐만이 아니라, .486이나 .586등도 사용 할 수 있지만, .386 이 제일 무난하다. 실제로, 거의 동일한 모델인, .386 / .386p 와 .486 / .486p 라는 CPU 모델이 있지만, 이런 "p"가 붙은 버젼은, 특권모드명령을 동작시킬 때만 필요하다. 특권모드명령은, 보호모드로 작동하는 CPU나 OS에 예약되어 있는, 가상 디바이스 드라이버(Vxd)와 같은 곳에서만 이 특권모드명령을 사용하며 또한 동작된다. 대부분의 경우는, 거의 비특권모드로 작동하므로, 비특권 모드 버젼으로 작성하는 것이 더욱 안전하다.

.MODEL FLAT, STDCALL

.MODEL 지시어는, 메모리모델을 나타낸다. Win32에서는, FLAT 메모리모델만 존재한다.

STDCALL 지시어는, 함수의 호출규약으로 , 함수의 인수를 오른쪽에서 왼쪽으로 전달하는지, 왼쪽에서 오른쪽으로 전달하는지, 스택프레임을 누가 조작하는지 등을 지정하는 것이다.

Win16에서는, 2개의 호출 규약인 ,"C" 와 " PASCAL" 이 있었다.
C 호출 규약은 인수가 오른쪽에서 왼쪽으로 넘겨지게 되어 제일 오른쪽의 인수가 제일 먼저 스택에 푸쉬 되고 CALL한 측이 스택프레임을 조작한다. 예를 들면, foo(int first_param, int second_param, int third_param)라고 하는 함수가 있다고 가정 할 경우, 이 함수를 C호출 규약으로 CALL 할 때의 어셈블리 코드는 아래와 같이 된다.

push [third_param]            ; Push the third parameter
push [second_param]           ; Followed by the second
push [first_param]            ; And the first
call  foo
add   sp, 12                  ; The caller balances the stack frame

반면에, PASCAL 호출 규약은 C 와는 반대이다. 인수는 왼쪽에서 오른쪽이며, CALL된 함수가 스택프레임을 조작한다.

Win16에서는 PASCAL도 적절했다.이유는, PASCAL 형식이 작은 코드가 생성되기 때문이다. C형식은 편하긴 하지만, wsprintf() 함수와같이 가변 인수일때에는 인수가 몇개인지 모른다는 것이다. 그렇기 때문에, 스택프레임을 호출된 측에서 조작할 수 없게되는 것이다.

STDCALL은, C 와 PASCAL 의 혼합형이다. 인수는 오른쪽에서 왼쪽이지만(C형식), 스택프레임을 조작하는 것은 호출된 측(PASCAL 형식)이다. Win32에서는 wsprintf()와 같은 가변인수를 사용하는 함수를 제외하고는, 대부분 STDCALL형식을 사용하고 있다. wsprintf() 함수의 경우에는 반드시 C형식을 사용해야만 한다. (이러한 규칙에 익숙해지기 바란다)

.DATA
.DATA?
.CONST
.CODE

4개 지시어 부분으로 「섹션」이라 불리는 것이다. Win32에서는 세그먼트(segment)를 취급하지 않는다고 얘기했었다. 하지만, 반드시 그렇게 하라는 것은 아니다. 4 GB의 플랫형태의 주소공간을 논리섹션으로 나눌 수가 있다. 섹션의 시작은, 이전의 섹션의 마지막을 나타낸다. 데이터 섹션과 코드 섹션이라는 2개의 그룹이 있고, 데이터 섹션은 다시 크게 3개의 부분으로 나뉘어진다.
  • .DATA
    이 섹션에는 프로그램의 초기화 데이터가 들어있다.
  • .DATA?
    이 섹션에는, 초기화되지 않은 데이터가 있다. 가끔씩, 메모리 영역만 확보하고 초기화 할 필요가 없는 상황이 발생한다. 이 섹션은, 그런 경우를 위한 것이다. 초기화 되지 않은 데이터의 장점은, 데이터의 크기만큼 실행 파일의 크기에 포함되지 않는 것이다. 예를 들면, .DATA? 섹션에 10,000바이트의 메모리를 확보했다 하더라도, 실행파일은 10,000 바이트크기 만큼 커지지 않는다는 것이다. 프로그램에게, 얼마 만큼의 영역이 필요하다! 라는것만 선언 하면 되는 것이다.
  • .CONST
    이 섹션에는, 상수를 선언하는 부분으로서, 절대 변하지 않는 값을 저장하는 장소이다. 즉 *constant* 라는 것이다.

이런 3가지 섹션을 모두 사용해야 할 필요는 없다. 사용하고 싶은 섹션만 선언하여 사용하면 된다.

나머지 섹션은 프로그램 코드를 위한 섹션이다. .CODE 섹션은, 프로그램의 코드부분을 저장하는 장소이다.

<label>
end <label>

<label>부분은, 임의의 레이블명을 사용할 수있고 프로그램의 범위를 나타낸다. 레이블명은 유일해야만 하고,코드들은 label사이에 위치해야 한다.
end 부분은 코드의 종료를 의미하며, 이후의 모든것은 어셈블러가 무시한다. 시작할때 지정한 <label>명을 지정해서 <label> ~ END <label>로 코드부분을 지정하는 것이다.


Posted by openserver