설마 어셈블러까지 건드리겠어 라고 생각하시는 분들이 많으시겠지만,
실무에서 타사랑 경쟁하고 있다면 어셈블러를 쓰는게 더 낳은경우가 많습니다. (아무래도 기계 성능을 100%활용하니..)

문제는 C언어의 #if, #ifdef 같은걸 어셈에 쓰기가 애매하죠. 자료도 그닥 없고.. ㅠㅠ
gcc경우
$gcc -D DEBUG_WINDOWS=1 ...

하면 #if DEBUG_WINDOWS부분이 활성화 되서 이쪽만 소스가 컴파일 되지만
어셈도 그짓을 할려면 조금 특수한 방법을 써야 합니다.

우선 어셈 소스중 분기하고 싶은곳의 맨앞에
.ifdef DEBUG_WINDOWS

를 추가하고, 끝의 머리엔
.endif

를 추가합니다.

다음 컴파일 할땐
$gcc -c -x assembler-with-cpp -pipe -Wa,--defsym=DEBUG_WINDOWS=0 ...
식으로 컴파일 하면 됩니다.

빨간부분은 띄어쓰기 하면 안되고, 심볼을 넘길땐 = 기호가 없으면 안됩니다.
의미는
as에 심볼을 넘겨라 인데.
Wa가 as에 파라메터를 넘기는 옵션이고
--defsym은 심볼을 지정하는 옵션입니다.
심볼을 넘길땐, 심볼이름=데이터 를 넘기지만, 여기선 데이터엔 의미가 없으니 그냥 0을 썻습니다.

즉, 위의 어셈 소스는, 심볼이 선언되었다면 실행....

그보다... 울나란 어셈블러 자료가 너무 없는거 같은... ㅠㅠ

1. 개요

    이 문서는 GNU 어셈블러 문법에서 지시자로 사용되는 의사 명령어를 간단히 정리 한 문서이다. 
    더 많은 참조를 원한다면 다음 사이트를 방문한다.
    
        http://tigcc.ticalc.org/doc/gnuasm.html#SEC70
    
2. 데이터 지시자 

    .byte   <byte1> {,<byte2>} ...      : 8  비트 데이터 
    .hword  <short1> {,<short2>} ...    : 16 비트 데이터 
    .word   <word1> {, <word2>} ...     : 32 비트 데이터 
    .ascii  "<string>"                  : 문자열 
    .asciz  "<string>"                  : 문자열 : 마지막에 자동으로 NUL 문자인 0을 추가 한다. 
    .space  size[, fill]                : 주어진 바이트 수를 할당한다. 바이트들은 0 이나 fill 값으로 채워져 있다. 

3. 상수 또는 심볼릭 관련 지시자 

    .equ <symbol name>, <value>                 : 심볼값을 정의 한다. 
    <register_name> .req <register_name>        : 레지스터의 이름을 짓는다. 
    .global <symbol>                            : 심볼을 외부 참조가 가능하게 한다 
    .set <variable_name>, <variable_value>      : 변수 값 설정
    

4. 코드 블럭 관련 지시자 
    
    .code <number_of_bits>    : 명령어 폭을 비트로 설정한다.

                                                Thumb 어셈블리에서는 16을, ARM 어셈블리에서는 32를 사용한다. 
    .section name[, "flags"]  : 새로운 코드 섹션 또는 데이터 섹션을 지정한다. 
                                섹션 명은 링크 디스크립터에 정의 되어 있어야 한다. 일반적으로 다음항목을 사용한다.
                                   .text : 코드 섹션
                                   .data : 초기화된 데이터 섹션
                                   .bss  : 초기화되지 않은 데이터 섹션. 
                                flags 는 ELF 포멧에서 
                                   a     : 섹션 할당
                                   w     : 쓰기 가능한 섹션
                                   x     : 실행 가능한 섹션


    .align alignment[, [fill][, max]]    : 주소를 2^n 바이트 단위로  정렬한다. 
                                           어셈블러는 fill나 적절한 디폴트 값을 추가하여 정렬을 맞춘다. 
                                           만약 max보다 더 많은 fill 바이트가 필요하다면 정렬되지 않는다.
                                                  
     .rept count                         : count 만큼 코드 블록을 반복한다. .endr로 끝난다.
    .endr                                : 반복 루프를 끝낸다. 

5. 매크로 

    .macro name [macargs...]       : name 이름의 어셈블러 매크로를 정의한다. 매크로 정의는 .endm으로 끝난다. 
    .endm                          : 매크로 정의를 끝낸다. 
    .exitm                         : 매크로를 빠져나온다. armasm에서의  유사하다.

6. 조건 컴파일 

    .if absolute_expression        : 컴파일 조건문 시작 .endif를 사용하여 끝낸다. 
    .ifdef symbol                  : symbol 이 정의되어 컴파일 대상에 포함되는 시작 .endif를 사용하여 끝낸다. 
    .else                          : .if와 .endif와 예외 조건이다. 
    .elseif                        : else 와 if 를 합친 것이다. 
    .endif                         : 조건부 컴파일 지정을 끝낸다(.if, .ifdef, .ifndef 참고). 
    .err                           : 에러가 있으면 어셈블리를 정지시킨다.

7. 기타 

    .include "<filename>"          : 지정한 소스 파일을 포함한다

8. GNU ASM 과 ARM ASM 의 대치표

    .ascii      : DCB
    .byte       : DCB
    .hword      : DCW
    .word       : DCD
    .space      : SPACE
    .set        : SETA
    
    .equ        : EQU
    .req        : RN
    
    .code       : CODE16,CODE32
    .sectoin    : AREA
    .balign     : ALAIN
    
    .macro      : MACRO
    .endm       : MEND
    .exitm      : MEXIT
    
    .if         : IF     
    .else       : ELSE
    .endif      : ENDIF
    .include    : INCLUDE
    
   
출처 : http://forum.falinux.com/zbxe/?document_srl=550700



 제목: [강좌] MASM 6.0 사용법 2/8
 ------------------------------------------------------------------------------

[MASM6.0/2]인스톨, 에디터를 맘에 들게..

      이번 강좌에서는 매즘 6.0의 인스톨 방법과 에디터를 사용자 맘에 들
    게 바꾸는 것에 대해 얘기를 하겠습니다.
   
    **** 매즘 6.0의 인스톨에 대해 ****
      
    1. 인스톨 방법에 대해..
    --> 매즘 6.0의 인스톨 방법은 아주 간단합니다. SETUP디스크를 A 드라
        이브에 넣고 SETUP을 타이프 하면 다음과 같은 메뉴가 나옵니다.
        (편의상 번호를 삽입합니다.)
        1. Install the Microsoft Macro Assembler
        2. Install the Macrro Assembler using defaults
        3. Run SETUP without installing any files
        4. View important documentation notes (README.DOC)
        5. View the packing list (PACKING.LST)
        6. Copy a file from the distribution disks
        7. Exit SETUP
        만약 여러 프로그램들을 다루신 분들이라면 설명이 없이도 금방 인
        스톨하시리라 봅니다. 가장 간단한 방법은 2번을 고르면 금방 됩니
        다. 그러면 여러 결정 상황 ( SAMPLE프로그램들을 복사할 것인가의
        여부, BRIEF 에디터의 에뮬레이션을 할 것인가의 여부 등등 )
        을 보여주고, 거기서 바꿀만한 것을 바꾸고 메뉴 맨 위에 위치한
        NO CHANGE를 선택하면 곧바로 인스톨을 시작합니다.

        모든 결정상황에 대해 계속 문답식으로 내용을 결정하면서 인스톨
        을 하고자 1번을 선택하면 됩니다. 그런데 제가 하나 터보 계열의
        에디터을 쓰시는 분께 권하고 싶은 것은, 디폴트로 되어 있지는
        않지만 BRIEF 에디터의 에뮬레이션을 선택하는 것이 좋을 듯 싶습
        니다. 터보 계열과 키가 많이 비슷하므로 조금만 바꾸면 별 어려움
        없이 에디터를 사용할 수 있기 때문입니다.

    2. 인스톨 후에 해야 할 것들..
    --> 위의 방법으로 다 인스톨을 하고 난다음 해야 할 중요한 것은 바로
        AUTOEXEC.BAT과 CONFIG.SYS를 바꾸는 것입니다. 그리고 TOOLS.PRE
        라는 파일을 TOOLS.INI로 이름을 바꾸어야 합니다.
       
        AUTOEXEC.BAT와 CONFIG.SYS에 첨가할 내용이 들어있는 파일은 기본
        디렉토리인 C:/MASM/BIN에 NEW-VARS.BAT과 NEW-CONF.SYS가 있습니
        다. 이것을 각각 AUTOEXEC.BAT과 CONFIG.SYS에 삽입하면 됩니다.
        NEW-CONF.SYS는 FILES과 BUFFERS를 정하는 것으로 별로 중요하지는
        않으나 NEW-VARS는 헬프 파일과 INCLUDE파일 등의 경로가 들어 있
        으므로 꼭 AUTOEXEC.BAT에 삽입을 해 주어야 합니다.

        또,  TOOLS.PRE가 기본디렉토리인 C:/MASM/INIT에 있는데 이를
        TOOLS.INI로 바꾸어 주어야 합니다.

    **** PWB의 간략한 소개와 에디터를 맘에 들게 바꾸는 법 ****

    1. Programmer's WorkBench (PWB)에 대해서..
    --> 저번에도 말했듯이 PWB는 윈도우 방식의 통합 개발 환경으로서 거
        기서 소스 작성, 어셈블, 링킹, 디버깅, 소스 브라우져(browser)
        기능, 완벽한 온라인 헬프 시스템을 이용할 수 있습니다.
       
        PWB를 실행시키려면 간단히 PWB라고 치면 됩니다. PWB를 사용하는
        방법은 터보나 볼랜드 계열의 IDE(통합 개발 환경)과 비슷하고
        또한 온라인 헬프에 아주 자세히 나와 있으므로 생략을 합니다.

    2. 에디터 사용에 관해서...
    --> 여러 에디터를 씀에 따라 문제가 되는 것은 각각 에디터 마다 쓰는
        키가 다르다는 것입니다. 원래 pwb에디터의 키는 많이 쓰이고 있는
        터보 계열과 많이 달라서 애를 먹습니다. 가령 터보 계열에서는
        파일의 맨 처음으로 가는 키는 Ctrl+PageUp 키이나 pwb에서는
        Ctrl+Home키 입니다. 비교적 비슷한 것이 위에서 말한 Brief에디터
        입니다. 그래서 터보계열을 많이 쓰시는 분은 위에서 인스톨을 할
        때 Brief에디터 에뮬레이션을 선택하라고 권고 했습니다. 그럼
        이번에는 에디터의 키를 자기 맘대로 바꾸는 것에 대해 이야기 하
        겠습니다.

        에디터의 키를 바꾸려면 pwb의 메뉴에서 OPTION의 Key Assignments
        를 선택을 합니다. 그러면 그냥 어떤 파일이 화면에 뜨게 됩니다.
        그 내용은  함수 : 키 의 형식이 되어 있는데 단순히 키를 변환 시
        키면 됩니다.

        가령 파일의 맨 처음으로 오는 함수의 이름은 pwb에서 begfile입니
        다. 만약 Brief 에뮬레이션 모드를 선택하지 않고 디폴트로 인스톨
        을 했으면 다음과 같이 나올 것입니다.
           begfile : Ctrl+Home
        여기서 만약 그 키를 Ctrl+PgUp으로 바꾸고 싶으면 단순히 다른 에
        디터를 쓰는 것처럼 Del키를 이용해서 위의 Home을 PgUp으로 바꾸
        면 됩니다. 그러면 바뀌었다는 것을 알려 주기 위해서 바뀌어진 줄
        의 색깔이 바뀌게 됩니다. 만약 인식할 수 없는 키를 적거나 ( 가
        령  sadf처럼) 잘못된 형식으로 적거나 ( 키의 조합은 +로 연결되
        어야 하는데 -으로 연결을 할때)하면 다음줄로 내려 가지 못하게
        되어 키를 제대로 써 주어야 계속 키를 바꿀  있게 됩니다.

        그러면 많이 쓰이면서도 에디터 마다 다른 함수를 열거해보죠.

        begfile --> 파일의 맨 처음에 커서를 위치.
        endfile --> 파일의 맨 끝에 커서를 위치.
        insertmode --> insert, overwrite 상태를 토글(toggle)
        ldelete    --> 한 줄을 지움
        menukey --> 메뉴를 부름
       
        다음은 pwb기본 에디터에는 함수 인데 brief에디터를 에뮬레이션하
        기 위해 만들어지 매크로 입니다. 즉 이것들은 brief에뮬레이션을
        선택했을 경우에만 나옵니다.

        beginnig_of_line --> 줄의 맨처음으로 간다. ( 칼럼 1)
                             ( 기본 함수에 begline이라고 있는데 이것은
                               앞의 공백문자를 무시한, 맨 처음 문자로
                               가는 함수 입니다. )                    
  
        delete_next_word -->  앞의 한 단어를 지운다.
        delete_previous_word --> 뒤의 한 단어를 지운다.
        delete_to_eol        --> 커서의 위치 부터 줄 끝까지 지운다.

       
        아마 이 정도만 바꾸어도 그리 불편하지 않게 쓰실 수 있을 것입니
        다. 다른 기능들, 가령 컴파일하는 키라던지 찾기의 단축키라던지
        등을 바꾸시려면 그에 해당하는 키를 바꾸시면 됩니다. 더 능력이
        되시면 스스로 매크로를 만들어서 tools.ini에 삽입하여 자기만의
        기능을 에디터에 삽입할 수도 있습니다. 위의 delete_next_word도
        매크로로 만든 예입니다. 인스톨시 Brief 에디터 에뮬레이션을 선
        택한 후 tools.pre 혹은 tools.ini를 보시면 그 안에 매크로를 정
        의한 것을 보실 수 있을 것입니다. 함수에 대한 설명이나 매크로를
        만드는 방법은 도움말에 자세히 나오니 참조하시길 바랍니다.

        그리고 키를 바꾸실 때 어떤 함수의 이름이 나왔는데 그 함수를 잘
        모르시겠다면 커서를 그 함수 이름에다 위치하고 F1키를 누르시면
        곧바로 잘 된 설명을 보실수 있을 것입니다. 마우스로는 마우스 커
        서를 위치하고 오른쪽 버튼을 누르면 도움말이 나옵니다. 실제로
        모든 것이 이런식으로 도움말을 볼 수 있는데 가령 소스를 보다가
        inc라는 명령어가 나왔는데 이것이 어떤 명령어인지 잘 모르겠다면
        커서를 inc위에다 위치시키고 마우스를 버튼을 누르거나 F1키를 누
        르면 곧 도움말을 볼 수 있습니다. 

        !!!! 주의할 점 !!!!
        위의 키를 바꾼것을 계속 유지하려면 꼭 세이브를 해야합니다. 메
        뉴에서 세이브를 선택하던지 세이브 단축키를 이용하면 됩니다.

        그리고 그밖의 에디터에 관한 옵션들 가령 화면 색이라던지 탭을
        몇 칸 뛰울 것이라던지 하는 것은 Options에  Editor Settings을
        고르고 위의 키 바꾸는 것처럼 값을 바꾸어 주면 됩니다.

        ** 블럭지정에 관한 팁 **

        블럭을 지정하고 블럭 지우기, 버퍼에 복사하기 같은 것을 하는데
        이런 키들은 볼랜드 계열의 키와 비슷합니다. 즉 블럭을 지정하는
        것은 shift키를 누르고 커서 이동키를 누르면 되고요. 카피하는 것
        은  Ctrl+Ins 이고 지우는 것은 Ctrl+Del이고 뭐 이런 것은 다 같
        은데 하나 좋은 것은 블럭을 지정을 할 때 한글의 F4를 지정해서
        블럭을 설정을 할 때처럼 박스모양의 블럭을 지정할 수 있다는 것
        입니다. 즉 마우스로 블럭을 지정할 때 오른쪽 버튼을 누르면
        박스모양의 블럭과 일반 줄단위의 블럭을 토글하면서 지정을 할
        수 있습니다. 한 번 해보세요. 키는 Ctrl+B를 누르면 두 지정방
        식간의 토글을 할 수 있습니다.


 ───────────────────────────────────────
 제목: [강좌] MASM 6.0 사용법 3/8
 ------------------------------------------------------------------------------

[MASM6.0/3] 소스를 빌드하는 방법..

  이번 강좌에서는 소스 프로그램을 빌드(Build)를 하여 실행 파일로
만드는 방법을 알아보도록 하겠습니다.


**** 소스 프로그램을 빌드 하는 법 ****

  소스 프로그램을 빌드하는 방법은 이미 고급언어로 실행 파일을 만
어본 경험이 있으신 분은 아마 쉽게 하리라고 봅니다. 그러나 몇가지
주의할 점이 있고 또한 여러 모듈로 된 프로그램을 빌드하는 방법에
대해 잘 모르시는 분이 있으실 것 같아 간단히 설명해 보겠습니다.

1. 하나의 소스로 구성된 프로그램을 빌드하는 방법
-->어떤 소스가 파일로 저장되어 있다고 가정을 퇸襟때 그것을 어셈블

   을 시키기 전에 먼저 해주어야 될 것은 어셈블할 프로그램의 타입
   을 정해주어야 합니다. 매즘 6.0에서 제공하는 타입은 여러가지가
   있는데, 가령 크게 나누어서 도스용 실행파일과 OS용 실행파일이
   있는데 각각 실행파일의 형태가 달라지기 때문에 이러한 것들을
   정해주어야 합니다. 또한 도스용 실행 파일중에서도 확장자가 EXE
   인 것도 있고 COM인 것도 있는데 이 둘 역시 실행파일에 있어서
   차이가 나기 때문에 이러한 타입들을 정해 주는 과정이 필요합니다.

   그래서 어셈블할 프로
 ?텝타입을 정해주려면..
   (1) Options메뉴에서 Build Options을 선택합니다.
   (2) Set Main Language를 선택하여 assembler를 선택합니다.
   (3) Set Initial Build Options을 선택하여 어셈블할 파일의 타입을
       정합니다. 만약 EXE파일이라면 DOS EXE를 COM 파일이면 DOS COM
       을 선택하면 되겠습니다.

    프로그램의 타입과 함께 고려해야 할 것은 실행파일을 어떤 버젼으로
   만드는가를 결정하는 것입니다. 여기서 버젼이라함은 Release 혹은
   Debug버젼을 말하는 것으로 두 버젼의 차이점을 말하자면5  Release
   버젼은 Debug버젼과는 달리 실행 파일내에 디버그 정보를 포함하지 않
   습니다. 말 그대로 Release버젼은 프로그램이 버그가 없어서 곧 발표(?)
   해도 될 때 Release버젼으로 프로그램을 빌드하는 것입니다. 만약 어떤
   에라가 있어서 계속 디버깅을 한다면은 Debug버젼으로 빌드를 하는 것이
   좋습니다. 왜냐하면 Debug버젼으로 프로그램을 빌드하면  코드뷰라는
   디버거의 기능을 십분 발휘할 수 있기 때문입니다. 그러나 Debug버젼은
   실행 파일내에 디버그 정보가 들어가기 때문에 실행파일이 더 커지게
   됩니다. 그러므로 상황에 맞게 버젼을 선택해야 하겠습니다. 두 버젼을
   선택하는 것은 위의 Options 메뉴의 Build Options에서 정할 수 있습
   니다.

2. 여러개의 모듈로 이루어진 프로그램을 빌드하는 법
-->하나의 소스로 이루어진 프로그램과는 달리 여러개의 파일로 이루어진
   프로그램을 빌드할 때는 Program List를 만들어야 합니다. Program
   List란 어떤 프로그램을 구성하는 소스 파일의 리스트를 말하는 것으로
   PWB는 이를 가지고 make file을 만들게 됩니다. make file이란 고급언
   어에서 프로젝트 파5일과 유사한 역할을 하는 파일로 확장자가 .MAK입
   니다. 아마 볼랜드 C같은 데서 프로젝트 파일을 만들어본 경험이 있으신
   분은 쉽게 프로그램 리스트를 작성하실 것이라고 봅니다.

   그럼 실제로 얘를 들면서 여러개의 모듈로 이루어진 프로그램을 빌드
   하는 방법을 보도록 합시다.

   다음의 예제 프로그램은 화면에 Hello, world를 찍는 프로그램으로
    HELLO.ASM과 PUTSTR.ASM 두개의 파일로 이루어진 프로그램입니다. 메
   인 모듈인 HELLO.ASM에서 외부 함수인 PutStr불러서 화면에 문자를 뿌
   리게 되어 있습5니다. 소스의 곳곳에 매즘 5.1에서는 생소한 것들이 있는
   데 추후 강좌를 통해서 설명하기로 하고 일단은 소스를 살펴봅시다.

   메인 모듈 - HELLO.ASM

    ; HELLO.ASM defines a string and calls the procedure PutStr to
    ; display the text. PutStr is in a separate module PUTSTR.ASM

                .MODEL  small, c
   
    ; Tell assembler PutStr's argument type and how to call PutStr:
    PutStr      PROTO   pMsg:PTR BYTE

                .DOSSEG
                .STACK

                .DAT5A
    msg         BYTE    "Hello, world.", 13, 10, 0  ; null-terminated
                                                    ; string
                .CODE
                .STARTUP                  ; Initialize data and stack
                                          ; segments
                INVOKE  PutStr, ADDR msg  ; call external procedure
                  .ENDW
           
                ret
    PutStr      ENDP
       
                END       
                   

    그러면 위얹텝소스를 각각 HELLO.ASM과 PUTSTR.ASM로 저장을 합니다.
    자 이제부터 위의 프로그램을 빌드하는 방법을 단계를 밟아 가면서
    살펴보도록 하죠.

    (1) 빌드할 프로그램의 타입을 정한다.
        --> 위에서 설명했든이 메뉴의 Options의 Build Options을 선택
            하여 Set Main Language을 선택하여 assembler를 선택을 하고
            Set Initial Build Optins을 선택하여 DOS EXE를 선택을 합니
            다. 버젼은 아직 디버그가 안되었다고 보고 Debug버젼을 선택
            을 합니다.
    (2) Prog5ram List를 만든다.
        --> Make메뉴에서 Set Program List를 선택을 합니다. 그러면 다이
            알로그 박스가 나오는데 메인 프로그램의 파일 이름을 적습니
            다.(HELLO). 그러면 PWB가 HELLO.MAK가 있다면 그 파일을 로드
            하게 됩니다. 없을 때는 새로운 makefile을 만들것이냐고 물어
            보는데 Yes를 선택을 합니다. 그러면 다이알로그 박스가 나
            오는데 거기엔 현재 디렉토리에 있는 파일들의 리스트가 나
            오게 됩니다. 거기서 프로그램을 구성하는 파일들얹밗선택하면
            됩니다.
            위의 예에서는 HELLO.ASM, PUTSTR.ASM이 되겠죠. 선택이 끝났
            으면 Save List를 선택합니다. 그러면 자동적으로 PWB가 HELLO
            .MAK이라는 makefile을 만들게 됩니다.

    (3) 프로그램을 빌드한다.                                          
             
        --> Set Program List를 선택하여 확장자가 .MAK인 makefile을
            불러 옵니다. 위처럼 Program List를 바로 만드후면 다시 부를
            필요는 없습니다. makefile을 로드한 후5에 여러 옵션들을 정합
            니다. MASM 옵션도 있고 BROWSE 옵션, LINK, NMAKE옵션 등이
            있지만 처음엔 디폴트 옵션으로 빌드를 하고 나중에 필요가
            있다면 적절하게 옵션들을 정합니다. 옵션을 정한후에는 MAKE
            메뉴의 Build 혹은 Rebuild All을 선택하여 프로그램을 빌드
            합니다. 아무 에라가 없으면 실행 파일이 만들어집니다.


 ───────────────────────────────────────
 제목: [강좌] MASM 6.0 사용법 4/8
 ------------------------------------------------------------------------------

[MASM6.0/4] 브라우져, 코드뷰 사용법..

  그럼 디버깅에 유용한 소스 브라우져와 코드뷰의 간단한 사용
법에 알아보도록 합시다.

**** 소스 브라우져와 코드뷰의 간단한 사용법 ****

1. 소스 브라우져의 사용법
-->소스 브라우져(Source Browser)는 함수와  변수들의  관계에
   관한 정보를 보여주는 역할을 합니다. 즉  함수와  변수들의
   정의(definition)와 그것들이 어디서 참조(reference)되었는
   지를 화면에 보여줍니다. 실제로 사용해 보시면 더욱 이해가
   쉬울 것이고요 온라인 헬프에 아주 잘나와있으므로 그것을 참
   조하면 좋을것입니다. (물론 조금의 영어 실력이 필요하죠.)
   소스 브라우져는 메뉴의 Browse 명령을 통해서 이용할 수 있
   는데 먼저 이를 이용하려면 데이타 베이스를 정의하는  준비
   가 필요합니다.

   데이타 베이스를 정의하려면....
   (1) MAKE메뉴에서 Set Program List를 선택을 하여  program
       list를 만듭니다.  이 program list는  데이타 베이스에
       필요하게 됩니다.
   (2) 메뉴의 Options에서 Browse Options을 선택합니다. 거기
       서 Generate Browse Information을 꼭 선택을 해야 합니
       다.
   (3) MAKE 메뉴에서 빌드를 하면 확장자가 .BSC(Browser Source
       Cache)인 파일이 생기는데 이 파일은 브라우져에 이용되는
       자료가 저장되어 있습니다.
                                       
   위와 같이 데이타 베이스를 정의하면  다음과  Browse메뉴에서
다음과 같은 명령어를 사용할 수 있습니다.

   (1) Goto Definition
       --> 이 명령어는 변수나 함수, 매크로 등이 정의된 곳으로
           커서를 옮겨 줍니다. 먼저 다이알로그 박스에 변수,함
           수, 매크로들의 이름이 나오는 데 그 중 커서를  옮길
      곳을 선택하고 O.K 버튼을 선택하면 곧바로 그것이 정
           의된 소스로 커서를 옮겨 줍니다. 아주 긴 소스를 보거
           나 여러개의 파일로 된 소스를 볼 때 이 명령을 사용하
           면 어떤 함수나, 매크로, 변수가 정의된 곳을 쉽게 찾
           아 볼 수 있습니다.
   (2) Goto Reference
       -->이 명령어는  함수나 변수, 매크로 등이 참조된 파일과
          줄번호들을 보여 줍니다.
   (3) View Relationships
       -->이 명령어는 프로그램에  관련된 여러 정보를 자세하게
          보여줍니다.  이 명령어를 사용하는 데는 다음과 같은
          절차가 필요합니다.
          1. 오브젝트를 설정한다.
          -->여기서 오브젝트란 각종 심볼(함수, 변수..)를 말합
             니다.
          2. 오브젝트에 적용될 오퍼레이션을 정한다.
          -->오퍼레이션은 여러 가지가 있는 데 여기서 다 설명을
             하는 것은 어려우므로 실제적으로 한번 사용해보시면
             쉽게 이해를 하시리라 봅니다.
          3. 보여줄 것들을 정한다.
          -->보여줄 것이라함은 함수, 매크로, 변수등을 말하는
             데 예를 들어서 2번의 오퍼레이션을 USE를 선택하면
             어떤 함수나 매트로 등이 사용한 것들을 죽 나열하게
             되어 있는데 여기서 보여줄 것을 함수와 변수만을 선
             택하면 그 함수가 사용한 함수와 변수만을 보여주게
             됩니다.
   (4) List References
       --> 이 명령어는 함수, 매크로, 변수, 심볼 등이 어떤 파일
           어떤 함수에 의해서 참조되었나를 쭉 나열하게 됩니다.
   (5) Call Tree
       -->이 명령어는 함수간의 관계, 즉 어떤 함수가 어떤 다른
          함수를 부르는 가를 트리구조로 보여줍니다.

 이상 소스 브라우져 사용법을 간단히 알아 보았는데 실제로 한번
써보시면 쉽게 그 기능을 이해하시리라 봅니다.

 그럼 코드뷰라는 디버거에 대해서 간단히 알아보도록 합니다.

**** 코드뷰의 간단한 사용법 ****

  먼저 코드뷰를 사용하기 전에...
 
  코드뷰를 사용하려면 저번 강좌에서 말했듯이 준비해야 할 것이
있습니다. 즉 빌드를 할 때 Debug 버젼으로 프로그램을  빌드해야
합니다. 잊어버리신 분을 위하여 다시 말씀해 드리면 Options 메뉴
에 Build Options을 선택을 해서 Debug버젼을 선택을 하시면  됩니
다. 그러면 실행파일에 Debug 정보가 들어가는데 코드뷰는 이를 이
용해서 여러가지 정보를 제공해 줍니다. 물론 보통 EXE파일도 코드
뷰를 이용할 수는 있지만 소스 레벨에서의 디버깅이 안되므로 코드
뷰를 잘 사용하시려면 Debug버젼으로 빌드하시고 사용하시는  것이
좋겠습니다.

  코드뷰가 보여주는 것들...

  RUN 메뉴에서 Debug 명령어를 선택하면 곧바로 코드뷰  화면으로
넘어가는 데 화면에 코드뷰가 보여주는 것들은 다음과 같습니다.

  먼저 프로그램을 보여주는 데 다음과 같은 세가지 방법으로 보여
줄 수 있습니다. ( Options의 Source Window를 선택 )

  1. 매즘 소스 코드 (Source Code)
  -->이것은 기본 선택 사항으로 PWB에서 짠 소스 코드를 그대로 보
     여줍니다.
  2. 디스어셈블된 소스 코드
  -->소서와 같은 디스어셈블러와 같이 디스어셈블된 어셈블리 코드
     를 보여주므로 1번과 같이 심볼들을 볼 수 없게 됩니다.
  3. 1과 2의 혼합
  -->즉 매즘 소스 코드와 함께 그 소스 코드가 디스어셈블된 코드가
     같이 나오게 됩니다. 이 경우는 매크로나 지시어등이 어떻게 기
     계어로 바뀌었는지 살펴 볼때 좋습니다.

  위의 프로그램을 보여주는 것 뿐만 아니라 디버깅에 유용한 여러정
보들을 화면에 보여 줍니다.
  즉 메모리, 레지스터, 지역 변수 등을 보여주는데 이는 코드뷰 메뉴
에서 View 명령어를 선택하여 보고 싶은 것만 볼 수 있도록 합니다.
 
  기타 여러 기능을 사용하는 방법 - Watch를 사용하거나, Go, Trace
Step 하는 방법, Break Point를 지정하는 방법 등은 일반 터보 계열이
나 볼랜드 계열의 고급언어에서 제공하는 것과 비슷하므로 생략하기로
합니다.

  다음 강좌부터는 일반 환경 사용법이 아닌 언어상으로 매즘 6.0에서
나아진 점에 대해 살펴보도록 합시다.


 ───────────────────────────────────────
 제목: [강좌] MASM 6.0 사용법 5/8
 ------------------------------------------------------------------------------

[MASM6.0/5] 고급언어적 지시어들..

  먼저 강좌가 늦어진 점에대해 죄송하게 생각합니다. 그럼  요번강좌
에 서는 MASM 6.0에서 획기적으로 나아졌다고 할수  있는  고급언어적
인 지시어들에 대해 설명을 해보겠습니다. 이  기능으로 말미암아  어
셈 프로그래밍이 한결 편해졌으며   소스를  보는 것이 매우  쉬어진,
그야말로 MASM6.0에서 가장 맘에 드는   변화일  것입니다.  간단하게
기능을 설명을 하자면 마치  고급언어(특히 C)적인 명령어를 제공하게
된 것입니다. 즉 while, if, else, repeat 등의 기능과 비슷한 기능을
이용할 수 있게 된 것입니다. 그럼 하나 하나의 기능들을 살펴 보기로
합시다.

1. 비교 판단 지시어
-->비교 판단 지시어로는 .IF, .ELSEIF, .ELSE 지시어가  있습니다. 
   구문을 살펴보면..

   .IF 조건1
   명령문
   [.ELSEIF 조건2
   명령문]
   [.ELSE
   명령문]
   .ENDIF

   여기서 []는 그  안에  있는  것은  생략될  수   있음을   말합니    
   다. 예를 들어서 살펴보면..

   예제)
   .IF       cx == 20
   mov       dx, 20
   .ELSE
   mov       dx, 30
   .ENDIF

   만약 위와 같은 프로그램은 실제로 어셈 명령어로 바뀔  때 다음과
   같이 번역됩니다.
   .IF       cx == 20
             cmp      cx, 014h        ; 위의 고급언어적
             jne      @c0001          ; 지시어가 프로세서
             mov      dx, 20          ; 명령어로 바뀐 코드
   .ELSE
             jmp      @c0003          ; ""
   @c0001:
             mov      dx, 30          ; ""
   .ENDIF
   @c0003:

   비교에 보시면 아시겠지만 고급 언어적인 지시어를  사용해서  프
   로그래밍을 하면 훨씬 편하며 보기도 쉬움을 알 수  있을  것입니
   다.

2. 루프 지시어
-->고급 언어에서 처럼 루프를 실행할 수 있는 새로운 지시어가  생겼
   는데, 고급언어의 WHILE물 같은 .WHILE, .ENDW, REPEAT문의 역할
   을 하는 .REPEAT, .UNTIL, .REPEAT, .UNTILCX 등이 있습니다. 그러
   나, 이런 새로운 지시어를 사용함에 있어서 새겨두어야 할 것이 있
   는데..

   1. 위의 지시어들은 새로운 명령어가 아니라 적당하게 프로세서 명
      령어들로 바뀌어지는 것입니다.
   2. 위의 루프 지시어들은 조건을 가지고 판단하기 때문에 부호있는
      데이타의 선언에 주의를 해야 합니다.

   2번을 부가 설명을 하자면, 조건을 底은疵   평가함에 있어서 그
   조건에 있는 오퍼랜드가 부호가 있는가의 여부를 알아야 합니다.그
   것을 확실히 해주지 않으면 예상하지 않은 결과가 나올 수 있기 때
   문입니다. 구체적으로 조건이 음수를 담고 있는 메모리를 참조하고
   자 할때는 그 메모리를  할당을  할때  새로생긴  지시어인  SBYTE
   (signed bytes), SWORD (signed words),  SDWORD  (signed  double

   words)를 사용해야 하거나 PTR 오퍼레이터를 써야 합니다.

   2.1 .WHILE 문
   -->.WHILE문을 구현하려면 두 지시어 .WHILE, .ENDW을 사용합니다.
      여기서 .ENDW은 WHILE문의 끝을 알리는 데 쓰입니다.

      구문)
      .WHILE 조건
      명령문
      .ENDW

      한가지 예를 들어봅시다. 다음 예제는 한 버퍼에서 다른 버퍼까
      지 데이타를 카피하는 것인데 데이타의 끝을  나타내는데  달러
      마크($)가 사용됩니다.

                .DATA
      buf1      BYTE       "This is a string", '$'
      buf2      BYTE       100 DUP (?)
                .CODE
                sub        bx, bx               ;BX를 0으로
                .WHILE     (buf1[BX] != '$')
                mov        al, buf[bx]          ; 한문자를 읽음
                mov        buf2[bx], al         ; buf2에 카피
                inc        bx                   ; 다음 문자
                .ENDW                                    

   2.2 .REPEAT 문
   -->.REPEAT문은 C에서 do문, 파스칼에서 repeat문과  같은  역할을
      하는 것입니다. 지시어는 .REPEAT 과 .UNTIL(또는  UNTILCXZ)을
      사용합니다.

      구문)
      .REPEAT
      명령문
      .UNTIL 조건
      .REPEAT
      명령문
      .UNTILCX [조건]

      예를 보면 확실히 이해가 갈겁니다. 다음 예제는 키보드로 입력
      을 받아서 버퍼에 넣어주는 예제입니다. 엔터를 누르면  루프는
      끝이 나도록 되어 있습니다.

                .DATA
      buffer    BYTE       100 DUP (?)
                .CODE
                sub        bx, bx
                .REPEAT
                mov        ah, 01h
                int        21h             ; 키보드로 부터 입력
          mov        buffer[bx], al  ; 버퍼에 저장
                inc        bx              ; 다음 버퍼 포인트
                .UNTIL     (al == 13)      ; 엔터일때 까지 루프
      여기서  .UNTIL  지시어는  비교   점프   명령어를   생성하고
      .UNTILCXZ는 loop 명령어를 생성합니다. 다음의 예제는  지시어
      를 명령어 코드로 바꾸어 표시한 것인데 이를 살펴보면  이해가
      갈 것입니다.

      ASSUME    bx:PTR SomeStruct

                .REPEAT
      @C001:
                incax
                .UNTIL ax == 6
                cmp        ax, 06h      ;비교 점프 명령어로
                jne        @C001        ;바뀜


                .REPEAT
      @C003:
                mov        ax, 1
                .UNTILCXZ
                loop       @C003        ;루프 명령어로 바뀜


                .REPEAT
      @C004:
                .UNTILCXZ  [bx].field != 6  ; 루프 명령어로
                cmp        [bx].field, 06h  ; 바뀜
                loope      @C004            ;
      


   2.3 .BREAK, .CONTINUE 문
   -->.BREAK와 .CONTINUE문은 .REPEAT 이나 .WHILE문에서 루프를  빠
      져 나가거나 계속 루프를 돌릴 때  쓰입니다.  마치  C언어에서
      break와 continue와 비슷합니다.

      구문)
      .BREAK [.IF 조건]
      .CONTINUE [.IF 조건]

      이 구문들에서 주의할 것은 .IF다음에 .ENDIF가 쓰이지 않는 것
      입니다. 당연하죠 보통의 .IF문처럼 조건이 맞으면  그  아래에
      있는 명령어가 수행되는 것이 아니라 그냥 루프를 빠져  나오거
      나 다시 루프를 시작할 때 쓰이니까요. 그럼 예제를 보면서  살
      펴봅시다. 다음 예제는 '0' 부터 '9'사이의 키만 받아들이고 그
      문자를 화면에 내보냅니다. 그 외의 다른 문자가 들어오는 경우
      는 계속 키를 입력을 받습니다. 그리고 엔터가 들어면 끝이나게
      되어있습니다.

                .WHILE 1                ; 무한 루프
                mov        ah, 08h     
                int        21h          ; get key without echo
                .BREAK     .IF al == 13 ; 엔터면 루프를 나감
                .CONTINUE  .IF (al<'0') || (al>'9')
                                        ; 숫자가 아니면 다시 루프
                mov        dl, al      
                mov        ah, 02h
                int        21h          ; 문자를 화면에 내보냄
                .ENDW

      만약 위의 예제를  /Fl, /Sg 옵션을 써서 어셈블을 하고 리스팅
      파일을 살펴보면 다음과 같이 나옵니다.

                         .WHILE 1              ; 무한 루프
 0017          
*@C0001:
 0017  B4 08             mov        ah, 08h     
 0019  CD 21             int        21h       ; get key without echo
                         .BREAK     .IF al == 13 ;엔터면 루프를 나감
 001B  3C 0D    *        cmp    al, 00Dh
 001D  74 10    *        je     @C0002
                         .CONTINUE  .IF (al<'0') || (al>'9')
 001F  3C 30    *        cmp    al, '0'
 0021  72 F4    *        jb     @C0001
 0023  3C 39    *        cmp    al, '9'
 0025  77 F0    *        ja     @C0001
                                           ; 숫자가 아니면 다시 루프
 0027  8A D0             mov        dl, al      
 0029  B4 02             mov        ah, 02h
 002B  CD 21             int        21h      ; 문자를 화면에 내보냄
                         .ENDW
 002D  EB E8    *        jmp    @C0001
 002F          
*@C0002:
            

      주의 : 칼럼 제한으로 리스팅 파일을 일부 고쳤습니다.


3. 조건에 대해서
-->.IF, .REPEAT, .WHILE 지시어의 조건을 씀에 있어서 관계형 오퍼레
   이터나 속성을 나타내는 PTR을 쓸 수 있습니다.  여기서는  조건에


   쓰이는 각종 오퍼레이터와 오퍼랜드, 속성, 우선순위에 대해  알아
   보도록 합시다.

   3.1 오퍼레이터에 대해서
   -->조건문에 쓰이는 오퍼레이터는 C의 오퍼레이터와 똑같습니다.

      오퍼레이터             의미
      ----------             ----
          >                  크다.
         >=                  크거나 같다.
          <                  작다
         <=                  작거나 같다.
          &                  bit test
          !                  logical NOT
         &&              logical AND
         ||                  logical OR

      오퍼레이터가 없는 조건문은 c언어에서와 같이 다룹니다. 즉 예


      를 들어서 .WHILE (x)는 .WHILE(x != 0)과 같고,  .WHILE(!x)는
      .WHILE (x == 0)과 같습니다.

      또한, 플랙 이름(ZERO?, CARRY?, OVERFLOW, SIGN?,  PARITY?)을
      써서 조건을 나타 낼 수도 있는데 즉 .WHILE  (CARRY?)같이  쓸
      수 있습니다.

   3.2  Signed, Unsigned 오퍼랜드에 대해
   -->조건에 쓰일 수 있는 오퍼랜드는 레지스터, 상수, 메모리가  될
      수 있습니다. 기본적으로 조건문에 있는 오퍼랜若 unsigned로
      가정을 합니다. 그런데 만약에 비교되는 오퍼랜드가 singed라면
      문제가 생기게 됩니다. 이럴땐 PTR 오퍼레이터로 특정한 오퍼랜
      드가 singed인 것을 알려줄 수 있습니다. 예를 들어서..

                .WHILE SWORD PTR [bx] <= 0
                .IF    SWORD PTR mem1 > 0

      만약  여기서  PTR  오퍼레이터가  안쓰였다면   bx의   내용을

      unsigned로 가정하고 명령어를 생성했을 것입니다.

      위에서 말했다시피 메모리 오퍼렌드의 속성을 지정할 수도 있습

      니다. 즉..

                .DATA
      mem1      SBYTE   ?
      mem2      WORD    ?
                .IF     mem1 > 0
                .WHILE  mem2 < bx
                .WHILE  SWORD ptr ax < count

      위의 예제에서는 메모리를 할당을 할 때 미리 SBYTE라고 알려주
      었기 때문에 전의 예제처럼 SWORD PTR을 쓸 필요가 없습니다.

   3.3 우선 순위에 대해
   -->C언어처럼 &&, ||, !으로 조건들을 연결을 해서 쓸  수  있는데
      이렇게 쓸 때 우선순위는 !,&&, ||순입니다. 또한 C처럼  왼쪽

      에서 오른쪽으로 조건을 살핍니다.

  오늘 강좌에서 보았듯이 MASM 6.0에서는 마치  고급언어(특히  C)와
비슷한 구문을 제공하여 프로그래밍을 매우 편하게 그리고 소스를  보
기 쉽게 만들어 주고 있습니다. 다음 강좌에서는 프로시져에 대한  일
반적인 얘기와 MASM 6.0에서 나아진 프로시져 지시어들에 대해서 알아
보도록 합시다.

 ───────────────────────────────────────
 제목: [강좌] MASM 6.0 사용법 6/8
 ------------------------------------------------------------------------------

[MASM6.0/6] 확장된 프로시져 기능(1)

  요번 강좌에서는 프로시져 지시어를 이용해서 효과적으로  파라메터를
주고받는 방법과 파라메터의 수가 가변적인 것을 다루는 방법에  대해서
얘기해 보도록 하겠습니다.

1.1 프로시져에 인수(argument)를 전달하기
    프로시져에 인수를 전달하는 방법은 여러 가지 방법이 있는데, 예를
    들면 레지스터를 통해서 전달하는 방법과 global 변수를 통해  전달
    하는 방법 등이 있다. 또한 고급언어에서 쓰는 방법으로 가장  일반
    적인 전달 방법으로 쓰이는 것은 스택을 통해서  전달하는  것이다.
    그러나 인수를 스택에 전달하는 방법도 언어에 따라서 다른데  이렇
    게 스택을 이용하여 인수를 전달하는 관행을 calling convention(호
    출 관행)이라고 한다.
      다음의 예는 c 스타일의 호출 관행에 따라  프로시져를  정의하고
    스택을 통하여 인수를 전달하는 부분이다.
      C에서 addup이라는 3개의 인수들을 받아 그 인수들을 모두 더해서
    값으로 돌려주는 함수를 생각하자. c 프로그램에서  addup  (  arg1
    ,arg2, 10)이라고 함수를 호출하면  아래와 같은 어셈블리로 번역될
    것이다. 단 arg1, arg2를 word크기의 변수라고 생각을 하자(즉 int
    와 같이 2 byte 크기)

                mov     ax, 10   
                push    ax       ;세번째 인수를 스택에 저장
                push    arg2     ;두번째 인수를 스택에 저장
                push    arg3     ;첫번째 인수를 스택에 저장
                call    addup    ;함수를 호출
                add     sp, 6    ;인수의 스택 프레임을 없앰
                ...       
                ...
    addup       PROC    NEAR     ;near형 프로시져 이므로 스택에
                              ;어드레스 저장으로 2byte를 차지
                push    bp       ;base pointer 를 저장 (2bytes)
                mov     bp, sp   ; bp=sp
                mov     ax, [bp+4] ; 첫번째 인수를 읽어드림
                add     ax, [bp+6] ; 두번째 인수랑 더함
                add     ax, [bp+8] ; 세번째 인수랑 더함
                mov     sp, bp
                pop     bp
                ret                ;결과값을 전달해줌
    addup       ENDP
           
      위의 소스를 가지고 스택이 어떻게 변화되는가를 그림으로 설명을
    하면 다음과 같다.

    1. call addup 하기전             2. call addup 후                                      
    상위    |           |            상위    |           |
    메모리  +-----------+            메모리  +-----------+
            |세번째 인수|                    |세번째 인수|
            +-----------+                    +-----------+
            |두번째 인수|                    |두번째 인수|
            +-----------+                    +-----------+
            |첫번째 인수|<-SP                |첫번째 인수|
            +-----------+                    +-----------+
            |           |                    | 리턴 주소 |<-SP
            +-----------+                    +-----------+
     하위   |           |             하위   |           |
     메모리 +-----------+             메모리 +-----------+
            |           |                    |           |


   3. push bp                         4. pop bp 한 후 
      mov bp, sp 한후
                       
  상위    |           |               상위    |           |      
  메모리  +-----------+               메모리  +-----------+      
          |세번째 인수|<-BP+8                 |세번째 인수|
          +-----------+                       +-----------+      
          |두번째 인수|<-BP+6                 |두번째 인수|
          +-----------+                       +-----------+      
          |첫번째 인수|<-BP+4                 |첫번째 인수|
          +-----------+                       +-----------+      
          | 리턴 주소 |                       | 리턴 주소 |<-SP      
          +-----------+                       +-----------+      
   하위   |이전 BP 값 |<-BP/SP        하위    |           |
   메모리 +-----------+               메모리  +-----------+      
          |           |                       |           |


  5. ret 한 후                        6. add sp, 6 한 후
                               
  상위    |           |               상위    |           |->SP
  메모리  +-----------+               메모리  +-----------+      
          |세번째 인수|                       |           |
          +-----------+                       +-----------+      
          |두번째 인수|                       |           |
          +-----------+                       +-----------+      
          |첫번째 인수|<-SP                   |           |
          +-----------+                       +-----------+      
          |           |                       |           |      
          +-----------+                       +-----------+      
   하위   |           |                하위   |           |
   메모리 +-----------+                메모리 +-----------+     
          |           |                       |           |      


      좀더 부연 설명을 하자면, 위에서 보다시피 c의 호출  관행에서는
    인수의 마지막 것부터, 즉 위의 소스에서는 상수(10)를 먼저 스택에
    저장을 하고 그리고 두번째, 첫번째 이렇게 거꾸로  스택에  인수를
    저장을 한다. 그러나 파스칼은 이와는 달리 순행으로 스택에 인수를
    저장한다. 그리고 c에서는 위의 예제와 같이 호출한 쪽에서  인수를
    위한 스택 프레임을 없애기 때문에(위의 소스에서 add sp, 6)  인수
    의 갯수를 가변적으로 할 수 있으나 파스칼은 호출된 쪽, 즉 프로시
    져 자체에서 인수를 위한 스택 프레임을 없애기 때문에 가변적인 인
    수를 가질 수 없다. 이렇듯 언어마다 호출관행이  다르므로  나중에
    혼합 언어 프로그래밍을 할때 이러한 점을 잘 고려하여 프로그램을
    작성해야 한다.

1.2 프로시져 정의하기
    PROC 지시어를 사용해서 프로시져를 정의할 때에는 저장되어야할 레
    지스터들과 프로시져에서 받는 파라메터들을 정의할 수 있다.  이전
    버젼과 달리 파라메터들을 프로시져 정의할 때 정의함으로써 편하게
    파라메터들을 이용할 수 있다. 그럼 예를 들어서 C 형식의 프로시져
    를 정의하면 다음과 같다.

      myproc PROC FAR C PUBLIC USES di si, var1:WORD, arg1:VARARG
      |    |      |          | |        |  |       |
      +--+-+      +----+-----+ +---+----+  +---+----+
         |             |           |           |
      label        attributes    reglist    parameters

    일반적으로 프로시져의 구문을 살펴보면..

    label PROC [attributes] [USES reglist] [,parameter[:tag]...]


    구성 요소                 설    명                 
    ---------                ------------
     label                   프로시져의 이름
    
     attributes              distance와 언어 종류, visibility와 같
                             은 속성을 나타낸다.
    
     reglist                 USES 지시어에 따르는 레지스터들은 프로
                             시져의 시작부분에  저장된다.  레지스터
                             들은 빈칸이나 탭으로  구분되어야  하며
                             콤마로 구분되면 안된다.  USES 지시어가
                             오면 자동적으로 어셈블러가  프로시져의
                             앞부분(프롤로그)에는 push 명령어로  레
                             지스터들을 스택에 보관하고  프로시져의
                             끝부분(에필로그)에는 pop 명령어로 다시
                             저장된 레지스터 값을 복귀한다.  프로시
                             져가 레지스터의 내용을  바꾸게  될  때 
                             USES reglist를 사용함으로써 프로시져가
                             호출된 쪽으로 복귀해도 레지스터의 내용
                             이 바뀌지 않도록 할 때 사용한다.

     parameter               스택을 통해서 프로시져에전달되는 파라
                             메터 리스트들. 리스트가 한 줄을 넘어서
                             게되면 줄 끝에 콤마를 하고 다음줄에 연
                             결하여 나타낼 수 있다.


    1.2.1 Attributes
      프로시져의 attribute 구문은 다음과 같다.

      [distance] [langtype] [visibility] [<prologuearg>]

      구성요소        설  명
      ---------      --------
      distance        RET 명령어의 형태(retn, retf)를 결정하게 되는
                      요소이다. NEAR나 FAR 둘중의 하나가 될  수  있
                      다.  만약에  distance가   기술되지   않았다면
                      .MODEL에  의해서   결정된다.   TINY,   SMALL,
                      COMPACT, FLAT은 NEAR가  되고  MEDIUM,  LARGE,
                      HUGE는 FAR가 된다.

      langtype        위에서 언급한 호출관행(calling  convention)을
                      결정하게  되는  요소이다.   BASIC,   FORTRAN,
                      PASCAL은 프로시져의 이름을 대문자로  변환하고
           마지막 파라메터가 스택의 가장 처음에 위치하게
                      된다. (가장 낮은 메모리에 위치하게 된다.) RET
                      n 명령어를 통해서  스택프레임을  프로시져에서
                      없애게 되어 가변적인 파라메터를 구현할 수  없
                      다.
                      C나 STDCALL은 프로시져의 scope가 PUBLIC이거나
                      EXPORT일 경우는 프로시져 이름의  맨  앞부분에
                      밑줄('_')을 붙이고 첫번째  파라메터가  스택의
                      가장 처음에 위치하게 된다. (가장 낮은  메모리
                      에 위치하게 된다.) SYSCALL은  C의  호출관행과
                      똑같은데 다만 프로시져 이름의 앞부분에 밑줄을
                      붙이지 않는다. STDCALL은 프로시져를 호출한 쪽
                      에서 스택 프레임을 없애기  때문에  파라메터의
                      수가 가변적일 수 있다.

      visibility      프로시져가 다른 모듈에서 사용 가능한가를 나타
                      낸다. visibility에는 PRIVATE, PUBLIC,  EXPORT
                      가 될 수 있다. 프로시져는 PRIVATE이라고  명시
                      되지 않은 이상 기본적으로 PUBLIC로  간주된다.
                      만약에 visibility가 EXPORT인 경우는  linker가
                      프로시져의 이름을 export table에 올린다. 당연
                      히 EXPORT는 PUBLIC을 포함하게 된다. OPTION 지
                      시어를 써서 default visibility를 정할  수  있
                      다. OPTION PROC:PUBLIC 이라고 하면 프로시져의
                      기본 visibility는 PUBLIC이 된다.

      prologuearg     프로시져의 처음과 끝부분의 코드 즉 prologue와
                      epilogue 코드를 생성하는 데 영향을 주는  요소
                      이다. 나중에 설명할 것이다.


    1.2.2 Parameters
      만약 PROC 지시어에 reglist가 존재하면 레지스터 리스트와 파
      라메터는 콤마로 구분된다.

      파라메터의 구문을 살펴보면..

      parmname[:tag]

      parmnane은 파라메터의 이름이고 tag는 BYTE나 WORD와 같은 타
      입이나 가변 파라메터를 나타내는 키워드 VARARG가 될  수  있
      다. VARARG 키워드는 항상 파라메터의 리스트의 맨끝에 와야한
      다.


                           Parmname  -------+    +-- Qualifiedtype
      EX)                                   |    |
      myproc PROC FAR C PUBLIC USES di si, var1:WORD, arg1:VARARG
            레지스터 리스트와            | |       |         |
            콤마로 구분된다.  -----------+ +---+---+         |
                                               |             |
                                           Parameters        |
                                VARARG는 파라메터의   -------+
                                 맨끝에  온다.


      다음의 예는 위의 1.1의 예제 중에서 프로시져 정의부분을  파
      라메터 리스트를 나열하는 기능을 이용하여 다시 고쳐 쓴 것이
      다. 역할은 1.1.의 프로그램과 똑같으나 훨씬 보기쉽고 프로그
      래밍도 쉬워진다는 것을 알 수 있다.

      addup     PROC    NEAR C,
                arg1:WORD, arg2:WORD, count:WORD
                mov     ax, arg1
                add     ax, count
                add     ax, arg2
                ret
      addup     ENDP
        
      만약에 프로시져의 argument가 포인터 타입이라면 위의 예제와
      같이 단순히 mov 명령어 하나로 포인터가 가르키는 곳의  값을
      얻을 수  없다.  만약에  그  값을  얻으려면  포인터  타입의
      argument를 어드레스로 간주하고 값을 얻어 와야 한다. 만약에
      포인터가 near 포인터라면 mov 명령어를 두번 써서 그 값을 구
   할 수가 있다. 처음 mov 명령어는 파라메터의 어드레스를 얻어
      오고 두번째 mov 명령어는 실제 파라메터의 값을 얻어오는  것
      이다. 다음의 예제를 보면 쉽게 이해할 것이다.

      ; Call from C as a FUNCTION returning an integer

                .MODEL medium, c
                .CODE
      myadd     PROC    arg1:NEAR PTR WORD, arg2:NEAR PTR WORD

                mov     bx, arg1     ; argument의 주소를 얻는다.
                mov     ax, [bx]     ; 실제 값을 얻는다.
                mov   bx, arg2      
                add     ax, [bx]     ; 두번째 값과 더한다.
                ret

      myadd     ENDP
                END

      만약에 포인터 타입이 near인지 far인지 잘 모를 경우나  여러
      모델에 상관없이 값을 제대로 얻으려면  conditional-assembly
      지시어(directive)를 이용하여 해결할 수 있다.  아래  예제는
      위의 myadd라는 프로시져가 far나 near에  상관없이  포인터가
      가르키는 값을 얻어서 제대로 수행할 수 있게 고친것이다.

                .MODEL medium, c       ; 아무 모델이나 상관없다.
                .CODE
      myadd     PROC    arg1:PTR WORD,  arg2:PTR WORD
                IF      @DataSize
                les     bx, arg1       ; far인 파라메터
                mov     ax, es:[bx]
                les     bx, arg2
                add     ax, es:[bx]
                ELSE
                mov     bx, arg1     ; near인 파라메터
                mov     ax, [bx]    
                mov     bx, arg2      
                add     ax, [bx]
                ENDIF
                ret
      myadd     ENDP
                END

      NOTE :  위의  예제에서  @DataSize는  메모리  모델이  tiny,
              small, medium, flat일 경우는 0,  compact,  large는
              1, huge는 2의 값을 주는 predefined symbol이다.  위
              의 예제에서는 @DataSize의 값이 0인 경우는 데이타가
              near에 위치하고 그 외에는 far에 위치함을 이용한 것
              이다.


    1.2.3 파라메터의 수가 가변적인 경우( Using VARARG )
      위에서 언급한 것과 같이 호출관행이 C, SYSCALL, STDCALL인 경우
      는 PROC 지시어를 이용하여 파라메터의 맨끝에 VARARG를 이용함으
      로써 파라메터의 수가 가변적인 것도 받아 들일 수가 있다.  다음
      의 예제를 살펴보자.

      addup3    PROTO   NEAR C, argcount:WORD, arg1:VARARG

                invoke  addup3, 3, 5, 2, 4

      ddup3    PROTO   NEAR C, argcount:WORD, arg1:VARARG
                sub     ax, ax          ; ax=0
                sub     si, si          ; si=0
 
                .WHILE  argcount > 0
                add     ax, arg1[si]   
                inc     si
                inc     si
                dec     argcount                 
                .ENDW
                ret
      addup3    ENDP

        예제의 둘째줄의 invoke는 MASM 6.0에서 처음으로 생긴  프로세
      서 명령어가 아닌 어셈블러 지시어인데 프로시져를 호출할 때  파
      라메터까지 넘겨주는 기능과 또 다른 여러가지 기능으로 마치  고
      급언어에서 함수를 호출하도록 할 수 있도록 하였다. 이에 대해서
      는 다음 강좌 때 설명하기로 하겠다.

        위의 addup3라는 프로시져는 3개의 숫자를  더하는  프로시져이
      다. 3개의 숫자는 파라메터로 받는데 VARARG를  써서  파라메터의
      수를  가변적으로 받고 있다. invoke addup3, 3, 5, 2, 4라고  하
      면  addup3라는  프로시져를  부르게  되며  처음에  나오는  3은
      argcount라는 파라메터 값으로 들어가게 된다. addup3 프로시져에
      서 argcount 값은 가변적인 파라메터의 갯수를 나타내는 데  첫번
      째 3은 뒤 나오는 더해질 숫자(5, 2, 4)들의  갯수를  나타내는
      것이다.  addup3  프로시져에서는  .WHILE  루프에서   add   ax,
      arg1[si]으로 각 파라메터를 더하게 되어 있으며 si+2를 함으로써
      (위의 inc si 명령어 2개) 다음 파라메터를 읽어올 수 있게  되어
      있다. 루프의 종료 조건으로 argcount를 매 루프마다 1씩  줄임으
      로써 파라메터의 갯수를 가변적으로 할 수 있게 했다.
       위의 예제에서 invoke addup3, 5, 1, 2, 3, 4, 5라고 하면 1부터
      5까지 다섯개의 숫자를 더하게 됨을 볼 수 있다.


 ───────────────────────────────────────
 제목: [강좌] MASM 6.0 사용법 7/8
 ------------------------------------------------------------------------------

[MASM6.0/7] 확장된 프로시져 기능(2)


  고급언어에서 지역 변수(local variables)는 대부분 스택에 저장된다.
어셈블리 언어에서도 지역 변수를 구현할 수 있는데, 맨처음에는 기본적
으로 어셈블리 언어에서 지역 변수를 구현하는 방법과  다음에는  LOCAL
지시어를 이용하여 자동적으로 지역 변수를 만들고 이용하는 방법에  대
해 알아보겠다.

1.1 기본적인 지역 변수 사용법
  우선 지역 변수를 사용하려면 프로시져의 시작 부분에 지역 변수를 위
  한 공간을 스택에 마련해야 한다. 그런다음 각각의 지역 변수는  스택
  에 존재하는 위치를 통해서 참조할 수 있다. 프로시져의 끝에는  스택
  포인터를 적절히 바꾸어 줌으로써 지역 변수를 위해 할당된 스택 공간
  을 없애주어야 한다.

  다음의 예재는 지역 변수를 위해서 스택에 공간을 마련하는 것과 base
  pointer를 통해서([bp-2]) 지역변수를 참조하는 방법을  보여주고  있
  다.

                push    ax              ; 인수를 스택에 저장
                call    task
                ...
                ...
  task          PROC    NEAR
                push    bp              ; save base pointer
                mov     bp, sp          ; bp<--sp
                sub     sp, 2           ; 지역 변수를 위해 스택
                                        ; 공간을 확보
                ...
                ...
                mov     WORD PTR [bp-2], 3  ; 지역 변수를 3으로       
      
                                            ; 초기화한다.
                add     ax, [bp-2]      ; 지역변수를 ax에 더함
                sub     [bp+4], ax      ; 인수에서 ax를 뺌
                ...
                ...
                mov     sp, bp          ; 로컬 변수를 위한 공간을
                                        ; 없앤다.
                pop     bp              ; restore base pointer
                ret     2
  task          ENDP

  위의 예제 끝부분에서 mov sp, bp함으로써 SP의 원래값으로  바꾸어주
  고 있다. mov sp, bp는 sp의 내용이 프로시져안에서 바뀔 때 필요하게
  되는데 일반적으로 지역 변수를 할당할 때 위에서 sub sp,  2와  같이
  sp의 내용이 바뀌게 되므로 필요하게 되는 것이다. ret 2에서 2는상
  수로써 스택 포인터를 상수만큼 더함으로써 스택 프레임을 없애는  역
  할을 한다. 바로 전 강좌에서 언급했듯이 PASCAL 같은 언어는  이렇게
  프로시져 안에서 ret n(n은 상수)를 이용해서 인수를 위한 스택  프레
  임을 없애게 되어 가변 파라메터를 사용할 수 없게 된다.  이해가  잘
  안되면 바로 전 강좌를 살펴보기 바란다.

  예제의 명령문에 따른 스택의 상태를 그림으로 보면 쉽게  이해가  갈
  것이다.

    1. call task 하기전             2. call task 후 
                                    
    상위    |           |            상위    |           |
    메모리  +-----------+            메모리  +-----------+
            |  인  수   |<-SP                |  인  수   |
            +-----------+                    +-----------+
            |           |                    | 리턴 주소 |<-SP
            +-----------+                    +-----------+
            |           |                    |           |
            +-----------+                    +-----------+
            |           |                    |           |
            +-----------+                    +-----------+
     하위   |           |             하위   |           |
     메모리 +-----------+             메모리 +-----------+
            |           |                    |           |


   3. push bp                         4. sub sp, 2 한 후 
      mov bp, sp 한후
                       
  상위    |           |               상위    |            |      
  메모리  +-----------+               메모리  +------------+      
          |  인   수  |<-BP+4                 |  인  수    |<-BP+4
          +-----------+                       +------------+      
          | 리턴 주소 |                       | 리턴 주소  |
          +-----------+                       +------------+      
          | 이전 BP값 |<-BP/SP                | 이전 BP값  |<-BP
          +-----------+                       +------------+      
          |           |                       |지역변수공간|<-BP-2    
          +-----------+                       +------------+      
   하위   |           |               하위    |            |
   메모리 +-----------+               메모리  +------------+      
          |           |                       |            |


  5. mov sp, bp                        6. ret 2 한 후
     pop bp  한후                             

  상위    |           |               상위    |           |->SP
  메모리  +-----------+               메모리  +-----------+      
          |  인   수  |                       |           |
          +-----------+                       +-----------+      
          | 리턴 주소 |<-SP                   |           |
          +-----------+                       +-----------+      
          |           |                       |           |
          +-----------+                       +-----------+      
          |           |                       |           |      
          +-----------+                       +-----------+      
   하위   |           |                하위   |           |
   메모리 +-----------+                메모리 +-----------+      
          |           |                       |           |      


1.2 LOCAL 지시어를 통한 지역 변수 사용
  LOCAL 지시어를 사용하면 지역 변수를 사용하는 것이 훨씬 쉽고  시간
  도 절약되며 프로그램이 보기에도 쉬워진다. 이 지시어를 사용하여 지
  역 변수의 이름과 타입을 주면 어셈블러가 자동으로 스택에  얼마만큼
  의 공간이 필요한지를 계산하고 그에 따라서 그만큼의 공간을  확보하
  기위해 SP를 적절히 맞추어주는 명령어를 생성한다. 그리고  프로시져
  의 끝, 즉 프로시져가 리턴하기 전에 SP를 다시 원래의  값을  되돌려
  주는 명령어를 생성하게 된다. 즉, 위의 1.1의  예제에서  프로시져의
  맨처음과 끝의 명령어들('...'으로 양끝을 구분했음)을 자동적으로 생
  성하게 된다. 그럼 LOCAL 지시어를 사용하여 1.1의 예제를 다시  고쳐
  써보자.

  task          PROC    NEAR    arg:WORD
                LOCAL   loc:WORD
                ...
                ...
         mov     loc, 3              ; 지역 변수를 3으로       
      
                                            ; 초기화한다.
                add     ax, loc             ; 지역변수를 ax에 더함
                sub     arg, ax             ; 인수에서 ax를 뺌
                ...
                ...
                ret
  task          ENDP

  LOCAL 지시어는 PROC 지시어 바로 다음에 와야되며 LOCAL 지시어 전에
  어떠한 명령어도 오면 안된다. LOCAL 지시어의 구문은 다음과 같다.

  LOCAL vardef[,vardef]...

  각각의 vardef는 지역 변수를 정의하게 되는데 다음과 같이 구성된다.
  label [[count]][:qualifiedtype]

    구성 요소              설  명
  ------------           ----------
    label                 지역 변수의 이
    count                 주어진 label로 몇개를 할당할 것인가를  나
                          타낸다. 쉽게 말해서 배열과 같은 기능을 하
                          는 것이다. count의 양쪽에 '[]'을 표시해야
                          한다.  count가  명시되지  않으면   하나의
                          element로 간주한다.
    qualifiedtype         WORD, BYTE와 같은 타입을 말한다.

    지역 변수 리스트가 한줄을 넘어서게 되면 줄의 끝에  콤마를  찍고
    다음줄에 계속 나열하거나 다시 LOCAL 지시어를 쓰면 된다.

    어셈블러는 지역 변수를 초기화하지 않으므로 따로  초기화를  하는
    프런瀏 코드를 넣어주어야 한다. 다음의 예제는  LOCAL  지시어를
    이용하여 배열형의 지역 변수를 생성하고 0으로 초기화하는  예제이
    다.

    arraysz     EQU     20
    aprocPROC    USES di
                LOCAL   var1[arraysz]:WORD, var2:WORD
                ...
                ...
    ; 지역 변수를 0으로 초기화한다.
                push    ss
                pop     es              ; ss=es
                lea     di, var1        ; ES:DI는 array를 가르키게
                                        ; 된다.
                mov     cx, arraysz     ; 카운트
                sub     ax, ax         
                retp    stosw           ; 0으로 초기화
  ...
                ...
                ret
    aproc       ENDP

    스택에 존재하는 지역 변수를 지역 변수의 이름으로 참조를  하더래
    도 어셈블러는 지역 변수를 BP로부터의 상대적 위치로 간주하게  된
    다. 그러므로 다른 프로시져는 이 지역 변수를 참조할 수  없게  된
    다. 그런데 지역 변수를 사용할 때 주의할 점이 있는데 그것은 지역
    변수가 BP에 대한 상대적 위치로 나타내어지므로 BX랑은 결합이  될
    수 없다는 점이다. BX와  BP는  SI와  DI랑  결합할  수  있다.  즉
    [BP+BX+2]와 같은 표현은 안되고 [BP+SI+2]나  [BP+DI+2]는  가능하
    다.
    다음의 예제를 살펴보고 무엇이 잘못ed낮나 알아보자.

    index       EQU     10
    test        PROC    NEAR
                LOCAL   array[index]:WORD
                ...
                ...
                mov     bx, index
    ;           mov     array[bx], 5    ; 잘못된 문장

    위의 예제에서 array[bx]는  [bp+bx+arrayoffset]같이 bp와 bx가 결
    합하므로 잘못된 것이다. 이러한 에러는 지역 변수가  bp의  상대적
    위치로 표현된다는 것을 잘 모르면 찾아내기 어려우므로 이와  같은
    점을 알아둬야한다.


 ───────────────────────────────────────
 제목: [강좌] MASM 6.0 사용법 8/8
 ------------------------------------------------------------------------------

[MASM6.0/8] 확장된 프로시져 기능(3)

  저번 강좌에서 설명했듯이 호출 관행(calling convention)에  따라서
파라메터를 스택에 넣는 방식은 달라지게 된다. 그러나 앞으로  설명하
게될 INVOKE 지시어를 이용하면 호출 관행에 상관없이 자동적으로 알맞
게 파라메터를 스택에 넣어주고,  프로시져 호출에 관련된 여러가지 일
들을 관리해주는 기능을 한다.
  INVOKE 지시어를 이용하려면, 먼저 PROC 지시어로 프로시져가 정의되
어야 한다. 또한, C언어에서와 같이  함수의  프로토타입(prototype)을
앞에다 정의할 수 있는데, 이때는 PROTO라는  지시어를  이용하게된다.
이는 어셈블러에게 함수가 필요로하는 인수의 갯수와 타입을  알려주어
나중에 INVOKE로 함수가 호출되었을 때 건내받는 인수의 갯수와 타입을
자동적으로 검사하도록 한다.


  1.1 프로시져의 프로토타입(prototype) 정의하기
    위에서 말했듯이 MASM 6.0에서의 프로토타입은 C 언어나 다른 고급
    언의 프로토타입과 비슷한 기능을 하게된다. 프로시져의  프로토타
    입은 프로시져의 이름, 타입, 그리고 옵션으로 파라메터의 모든 이
    름을 넣을 수가 명시할 수 있다. 프로토타입은 대체적으로  프로그
    램의 맨 처음부분이나 따로 분리된 인클루드(include) 파일에 위치
    한다. 프로토타입은 특히 다른 모듈이나 다른 언어에서 프로시져가
    호출될 경우 유용한데, 이는 어셈블러가 타입이나 갯수가 맞지  않
    는 인수들을 체크할 수 있도록 하기 때문이다.

    프로시져의 프로토타입을 정의하는 것은 옵션으로 C처럼 꼭 해야하
    는 것은 아니다. 그러므로, MASM 5.X이하의  버젼  방식인  PROC과
    CALL이용하여 프로시져를 호출, 이용할 수 있다.

    프로토타입의 정의 구문은 프로시져 정의 구문에서 레지스터  리스
    트와 prologuearg 리스트, 프로시져의 scope를 제외하고는  동일하
    다. 또한, 프로토타입은 프로시져와 같이 언어종류(langtype)와 거
    리(distance)를 나타내는 속성을 정의할 수 있는데 이 속성들이 생
    략되면 .MODEL 이나 OPTION LANGUAGE 명령문에 준거하여 정해진다.
    또한 프로시져에서 정의되는 :VARARG는 옵션으로, 써도되고 안써도
    된다.

    한 함수에 대해, PROC문과 PROTO문이 같이 나오면, 둘의 속성과 파
    라메터의 갯수, 파라메터의 타입이 일치되어야 한다. 프로토타입을
    정의하는 가장쉬운 방법은  에디터로 PROC문을 카피한 후  PROC을
    PROTO로 바꾸어주고 USES reglist와 prologuearg, visibility를 지
    우는 것이다.

    예)

    ; Procedure prototypes

    addup       PROTO   NEAR C, argount:WORD, arg2:WORD, arg3:WORD

    myproc      PROTO   FAR C, argcount:WORD, arg2:VARARG

    ; Procedure declarations

    addup       PROC   NEAR C, argount:WORD, arg2:WORD, arg3:WORD

    myproc      PROC   FAR C PUBLIC <callcount> USES di, si,
                argcount:WORD,
       arg2:VARARG

    INVOKE로 프로시져를 호출하면, 어셈블러는 INVOKE에서 주어진  인
    수와 PROC에서 정의된 인수를 비교하여 체크한다. 만약에 둘  사이
    의 타입이 맞이 않으면, MASM은 에라를 내거나,  INVOKE로  주어진
    타입을 프로시져에서 요구하는 타입으로 맞게 바꾸게 된다.


  1.2 INVOKE로 프로시져 호출하기
    INVOKE는 인수를 스택에 넣고 프로시져를 호출하는 일련의  프로세
    서 명령어를 자동적으로 생성한다. INVOKE는 또한 프로시져를 호출
    할 때 다음과 같은 작업을 자동적으로 하게된다.

    * 인수를 기대되는 타입으로 바꾸어준다.
    * 인수를 올바른 순서로 스택에 넣는다.
    * 프로시져가 수행을 마친후 스택 프레임을 없앤다.

    INVOKE의 구문은 다음과 같다.

    INVOKE expression, [,arguments]

    여기서 expression은 프로시져의 이름을  나타낸다.   arguments는
    12*2 와 같은 expression이거나, 레지스터, 또는 ADDR로  나타내어
    지는 exprssion이다.

    예)
    addup       PROTO   NEAR C, argount:WORD, arg2:WORD, arg3:WORD

    myproc      PROTO   FAR C, argcount:WORD, arg2:VARARG

    addup       PROC   NEAR C, argount:WORD, arg2:WORD, arg3:WORD

    myproc      PROC   FAR C PUBLIC <callcount> USES di, si,
                argcount:WORD,
                arg2:VARARG

    프로토타입과 프로시져 구문이 위와 같이 주어졌을  때   INVOKE는
    다음과 같이 쓸 수 있다.

    INVOKE      addup,  ax, x, y
    INVOKE      myproc, bx, cx, 100, 10

    어셈블러는 타입이 안맞는 인수들을 적당한 타입에 맞추어  바꾸어
    줄 수 있다. 예를 들면 위의 addup 프로시져는 WORD형타입을 요구
    하지만 아래의 경우도 문제없이 쓸 수 있다.

    * BYTE, SBYTE, WORD, SWORD
    * 8 비트 또는 16비트 레지스터
    * -32K 부터 64K 까지의 상수
    * NEAR 포인터

    만약에 요구되는 타입보다 넘어오는 인수의 타입이 크기가 작을 경
    우, 어셈블러는 넘어오는 타입의 크기를 늘리게 된다.

    1.2.1 에라 찾기
      어셈블러가 인수의 크기를 늘리려고 할 때, 어떤 레지스터를  이
      용하게 되는데, 이는 레지스터값으로 들어오는 인수의 값과 중복
      될 수 있어서 이는 에라의 원인이 된다. 예를 들면 다음과  같이
      C 언어 타입의 호출이 있다고 하자.

      INVOKE myprocA, ax, cx, 100, arg

      위에서 arg가 BYTE 크기의 인수이고 myprocA는 다 WORD크기의 인
      수를 요구한다고 하면 어셈블러는 BYTE크기의 arg를 WORD 크기로
      늘려서 스택에 넣어야 한다. 즉, 다음과 같은  코드를  생성하게
      된다.

                mov     al, DGROUP:arg
                xor     ah, ah
                push    ax

      C는 저번 강좌에서 말했듯이, 인수를 역순으로  스택에  넣는다.
      즉 위의 예에서는 arg, 100, cx, ax 순으로 넣는다. 그러나 위의
      코드처럼 arg를 늘리게 될 때 쓰는 레지스터가 ax이므로  맨처음
      의 인수, ax는 중복되게 되므로 내용이 달라지게 된다. 이  경우
      에는 어셈블러가 에라 메세지를 내게 된다. 이런 경우은  invoke
      를 다시 적당히 고쳐주어야 한다.

      INVOKE는 되도록이면 인수의 크기를 늘이기 위해 적은  레지스터
      를 사용한다. 그러나 인수를 늘리게 되면, 불가피하게  레지스터
      를 사용하게 되는데, AX 레지스터를 사용하게 되고, 어쩔때는 DX
      레지스터도 사용하게 된다. 결과적으로 AL, AH,  AX는  빈번하게
      사용되므로 이 레지스터를 이용하여 인수를 넘기는 것은  되도록
      이면 삼가하는 게 좋고 많이 쓰이지 않는 DX같은 레지스터를  쓰
      는 것이 좋다.

    1.2.2 생성된 코드 검사
      INVOKE를 이용하면 위와 같이 인수의 크기를 늘이게 되는 작업을
      하는 코드를 자동적으로 생성하게 된다. 자동적으로 생성된 코드
      를 확인하려면 코맨드 라인(command-line) 옵션에서 /Sg  옵션을
      이용해서 리스트 파일을 보면 어떻게 코드가 생성ed낮는지  확인할
      수 있다.


      강좌를 마치면서...

      사실은 강좌를 몇번 더 해야되는데, 제가 곧 군입대를 하게 되어
      서 강좌를 이쯤 마치게 되니 죄송하게 되었습니다. 변변치  않은
      강좌를 게으르게 쓰느라고 미안한 점도 많네요.  그동안  도움이
      되셨는지 모르겠습니다. 송구스런 마음으로 이만 강좌를  마칠까
      합니다.

'C/C++언어' 카테고리의 다른 글

함수포인터란 ?  (0) 2007.09.06
[본문스크랩] 파일 입.출력  (0) 2007.09.06
[본문스크랩] VC++ Article  (0) 2007.09.06
[강좌] MASM 6.0 사용법  (0) 2007.09.06
어셈블리 초급 기초 #1(정태식님)  (0) 2007.09.06
어셈블리 초급 기초 #2(정태식님)  (0) 2007.09.06
 [1] 제목 : 감히 어셈블러 강의를 해볼까 합니다.
 올린이 : 까망벌레(정태식  )    94/10/24 17:25    읽음 : 599  관련자료 없음

안녕하세요...
후후....아직 가입도 안된 상태에서 무슨 글을 올린다는것이.
조금 그렇네요,
하지만 여기 와 보니깐 어셈블러에 대한 강의가 하나도 없기에
제가 먼저 싲가해 볼까 합니다.
저도 사실 아는거 별로 없지만 그래도 이것만이라도 여러분과 함께
나누고 싶어서 .....
우선 어셈블러에 들어가기 앞서 하드웨어의 기본적 구성에 대해
올릴까 합니다. 조금 지겹더라도 읽어 주세요...
그리고 혹시 모르시는 점이나, 아님 궁금한점,
또는 흉을 보실일이나 분제점 등이 있다면,
과감히 말씀 하셔요~
헤헤~
그럼 시삽님에게 양해를 구하며 시작 해 보겠습니다.
처음 시작하는 것은 [초급-기초]로서 여기서는 하드웨어의
기본적인 구성에 대해서 아주 간략히 소개해 드립니다.
그 다음 [초급-실습] 에서는 직접 프로그램들을 해 보면서
각각에 대해 관련된 설명들을 해 나가겠습니다.
그리고 [초급-보충]은 설명 중간중간에 말하기에는
너무 양이 많고 중요한 것들을 따로 묶어서 설명을 하겠습니다.

그 다음 과정으로 물론 [중급]이겠지요....
여기서는 프로시져 및 함수들, 그리고 C언어와의 링크등을
설명할 계획이고요, 또한 의사명령에 대해 알아보겠습니다.

[상급]과정은 과연 제가 할 수 있을지는 모르지만...
헤헤.,...아직 까지는 저도 상급과정을 공부하는 중이라...
아마 한다고 하면 여기서는 시스템 프로그래밍 기법에 대해
할 계획 입니다.
시스템 프로그램이란 SUPER VGA메모리및 직접 제어, 또는
모뎀이나 클럭주파수 등과 같은 것들을 다루어 볼까 합니다.
사실 저로서는 자신이 없네요....
저도 딸리니깐요...헤헤~
일단은 하는데 까지 해보겠습니다.


 [3] 제목 : [초급-기초] 맛보기...
 올린이 : 까망벌레(정태식  )    94/10/24 17:28    읽음 : 828  관련자료 없음


맛보기...


어떤 프로그램을 실행시키게 되면, 과연 컴퓨터는 그 과정을 어떻게 처리하는지

에 대해 일단 간단히 설명을 드리겠습니다.

프로그램은 DOS 에서 화일이라는 방법으로 디스크에 저장되어 있습니다.

하지만 컴퓨터 자체에서 이 프로그램을 수행할려면, 디스크 상에서 직접 수행을

하지는 못함니다. 그래서 이 프로그램을 램(이하 메모리)으로 읽어 들이게 됩니다.

그럼 메모리로 읽어 들인것 만으로 수행이 가능 할까요?

그렇지는 않습니다. 왜냐하면 램에서는 연산기능 ( 예를 들면 덧셈 뺄셈 등등 )

을 직접 하는 기능이 없기 때문 입니다. 램에서는 그냥 데이터를 보존하는 기능

밖에는 없습니다. 이런 점에서 디스크와 별다른 차이점은 없습니다. 하지만 램이 꼬옥

필요한 이유중의 하나는 데이터를 가져오거나 보관할때 디스크와는 속도가 차원이

다르게 빠르다는 것이지요.            그럼 왜 프로그램을 수행하는데 있어서

이러한 연산기능이 꼬옥 필요할까요? 그건 모든 프로그램들은 근본적으로 어떠한

주어진 데이터를 더하거나 빼거나 해서 수행되어지는 일련의 과정들이라고 할 수 있기

때문입니다.

그럼 과연 어디서 이러한 연산들을 수행하는가 하면, 여러분도 아시다 시피 바로

컴퓨터의 가장 핵심부분이라고 할 수 있는 CPU라는 곳에서 수행하고 있습니다.

정리해서 총괄적으로 다시 말하자면, 일단 어떤 프로그램을 실행하기 위해서는

디스크로 부터 화일의 내용을 메모리로 읽어 들여 그 메모리에 저장되어 있는

데이터                                                                                                                                                                                                                                                           
레지스터란 과연 무엇인가?

비유를 하자면, 연습장이라고 할 수 있습니다. 물론 이 '연습장'이라는 단어가

레지스터의 모든 기능을 나타내어 주는 단어는 결코 아닙니다. 그냥 레지스터가

담당하는 수많은 기능들의 한 부분(기능)만을 뜻하는 것입니다.

그리고 메모리라는 것은 어떤 주어진 데이타 목록 이라고나 할까요.

( 여기서 데이타는 컴퓨터에게 직접 수행 명령을 주는 명령어도 포함 됩니다 )

우리는 이 목록에서 원하는 값을 얻기 위해 연습장에다가 쓰고 지우고 하게

됩니다. 그래서 최종적으로 원하는 값을 얻게 되지요.

사실 레지스터라는것은 별거 없습니다. 메모리와 거의 같은 구조로 되어 있고,

여기에 그 메모리(레지스터) 속에 들어 있는 데이터를 연산하는 기능이 하나

추가된 것이라고 생각 하시면 간단합니다.


예를 들어 '3+4=' 이라는 문제의 답을 원한다고 합시다.

이것은 우선 어떤 화일(디스크)에다가 저장을 시켜야 겠지요.

그럼 일단 컴퓨터에서는 이 '3+4='라는 '데이터'를 메모리로 읽어 오게 됩니다.

( 엄격히 말하자면 여기서 데이터라는 것은 '3' 과 '4'뿐이고 '+' 나 '='라는 것은

컴퓨터에게 주는 연산 명령이라고 할수 있습니다. 하지만 이 '데이터'와 '연산명령'을

일단은 메모리로 모두 읽어오게 됩니다 )

다음으로 이 첫번째 값인 '3'을 레지스터에 집어넣게 됩니다. 그 다음 '+' 라는 것을

읽어 옴으로써 '아.. 3 에다가 어떤 값을 더하라는 것이구나' 라고 생각하고

준비합니다. 그 다음에 오는 '4' 를 보구서 미리 준비했던것 같이 바로 '3'에다가

'4'를 더해서 그 결과를 얻게 되는 것입니다.

그 결과를 어디에 놓는지는 나중에 설명 드리겠습니다.


( 혹시 램(RAM) 에 대해서 모르시는 분이 계시지는 않겠지요 )


흐~~~ 역시 버벅거리는 경향이 있네요...

여러분꼐서 읽어 보시고 부족한 점이나 궁금하신 점이 있으시면 과감히 물어보세요

사사로운것도 좋습니다.

그리고 흉을 보셔도 괜찮습니다. ( 뽀드득 ) 히~~~

다음에는 메모리(RAM)에 대해 간단히 설명해 드리겠습니다.









 [4] 제목 : [초급-기초] 메모리에 대해서 #1
 올린이 : 까망벌레(정태식  )    94/10/24 17:30    읽음 : 627  관련자료 없음

두번째로 메모리에 대해 설명을 드리고자 합니다.

어셈블러 강의를 한답시고 램, 아니면 이상한 것들만 주욱 늘어놓는지에 대해

투덜거리시는 분이 계실 지도 모르지만... 사실 이런것들을 제껴 놓고 어셈블러에

대해 설명한다는 건 불가능 하다고 생각하기 때문 입니다. 다른 어떤 교재를 봐도

다아 이렇게 시작해요...히힛~


그럼 메모리에 대해서...

메모리가 몰까.... 아마 램이 1메가라는 둥 4메가라는 둥.... 이런 말들은 마니

들어보셨을줄로 압니다. 이게 바로 메모리라는 것이지요. 뒤에 '메가'라는 것은

그 메모리가 저장할 수 있는 데이터 양을 말하는 것입니다.

메모리의 구조에 대해 말씀드리겠습니다.


1. 비트, 바이트???

아마도 여러분은 '비트' 또는 '바이트' 라는 용어에 대해 한번쯤은 들어보셨을 줄로

압니다. ( 처음 듣는 거라도 상관은 없지요 ... )

과연 이 비트(bit)라는 것은 무엇일까..... 여러분 2진수에 대해 아시죠?

갑자기 왠 2진수냐고요? 흐...그거에 대해 또 설명을 드리지요.

컴퓨터 ( 이하 PC ) 는 모든 움직임이 전기로 되도록 만들어 졌습니다. 하지만

멍청하게도 이 PC라는 것이 전기가 '흐른다', '안흐른다' 라는 것 밖에는 구분을

못합니다.

사람 같으면 전기의 세기정도를 구분할 수 있지만 아쉽게도 그걸 구분하지는

못합니다. 그래서 PC는 이 '흐른다', '안흐른다' 로 모든 것을 인식할 수 밖에는

없지요. ( 보통 이것을 '스위치'를 '켯다' 또는 '껏다' 라고 비유하기도 합니다.)

여기서 2진수와의 공통점을 발견할 수 있습니다. 2진수에서 역시 0 과 1의

두가지 숫자밖에 사용할 수 없고, 이 두가지 숫자로 모든 수를 표현 할 수 밖에

없지요. 그래서 이 두가지 공통점을 연결해서 '흐른다 = 1', '안흐른다 = 0' 으로

보통 약속되어져 있습니다. 그래서 PC 자체에서 일어나는 과정들을 2진수로 표현하게

됩니다.


다시 '비트' 로 넘어오지요...

우선 10진수 54라는 것이 메모리에 어떤 모양으로  저장 되는지 살펴보겠습니다.

그냥 54라고 저장하면 된다고요? 헤헤헤.... 어떻게요? PC는 0과 1이라는 숫자만

사용할 수 있다고 했는데....

그럼 이 54를 2진수로 바꿔 보겠습니다.

110110

이렇게 바뀌지요. 그럼 이걸 어떻게 저장하지요?

아까 '스위치'라는 걸 예를 들었는데요, 이 각각의 자릿수의 숫자


'1' , '1' , '0' , '1' , '1' , '0'


들을 각각의 스위치로 대비시켜 볼 수 있습니다.

'1' 이라는 숫자는 스위치를 '켜다', '0' 은 '끄다' 로 정한다면


'켜다' , '켜다' , '끄다' , '켜다' , '켜다' , '끄다'   <-- 스위치

  1        1         0       1         1        0     <-- 2진수


라고 이쁘게 바뀌었지요? 그리고 스위치가 전부 6개가 사용 되었습니다.

이렇게 하면 드디어 PC에서 알아 들을 수 있는 방법으로 바뀌게 되었습니다.

메모리에는 이런 스위치가 수도없이 들어 있습니다.

이런 스위치들의 어떤 부분이 위와 같이 고정되어 있다고 한다면, PC는 메모리를

보면서 스위치들의 조합 상태를 보고 언제라도 54라는 숫자를 꺼내올 수가 있는 거죠.

이제 '비트'의 정의를 내리겠습니다.

위의 예에서 스위치가 전부 6개가 사용되었다고 했지요?

이 비트라는 것은 '스위치' 하나를 지칭하는 단어 입니다.

위의 예를 본다면 '10진수 54를 나타내기 위해서 전부 6비트가 사용되었다'

라고 말 할수 있습니다.

메모리라는 것이 바로 이 수 많은 스위치들의 집합체인 것입니다.

그럼 바이트(Byte)는 ?

바로 이 비트 8개를 묶어 1바이트라고 합니다.

예를 들어 연필 12개를 1타스 라고 부르는 것과 동등 합니다.

정리 하자면


8 bit  =  1 byte

1024 byte  =  1 Kbyte

1024 Kbyte  =  1 Mbyte


라고 요약할 수 있겠지요...


2. 메모리 처리방식

PC의 처리 방법은 이 'bit'라는 개념 보다는 'byte'라는 개념을 주로 사용

합니다. 다시 말하면 만약 이 54라는 10진 숫자를 2진수로 바꾸면 , 110110

이라는 6개의 비트만으로 표현이 가능 하지만, PC 자체에서는 00110110 과같이

항상 8개의 비트를 묶어서 저장하게 됩니다.  좀더 구체적으로 만약 54 와 3이라는

숫자를 연달아 메모리에 저장하게 되면

110110 11   이라고 저장하면 비트(메모리)를 조금만 사용해도 되는것을

구지 00110110 00000011 이렇게 항상 각각의 데이터들을 8비트, 즉 1바이트로

저장하게 된다는 뜻입니다.

이유는 원래 생겨먹은게 그렇게 생겨 머거서 그렇죠 머...히히~

(자세한건 다음 기회에... )

그래서 앞으로 모든 수행은 ( 특별한 경우 이외에는 ) 이 byte 개념으로 설명이

됩니다.


3. ASCII 코드에 관해

그럼 과연  영문자 ( abcdefg ) 와 같은 것들은 메모리에 어떻게 저장이

될까.....라고 궁금해 하신다면,

컴퓨터는 숫자밖에 인식을 못합니다. 그래서 이 영문자들도 다른 컴퓨터가 인식할 수

있는 숫자로 대치해 주어야만 합니다.

일단 이것부터 설명을 드리지요... character (문자) 의 개념에 대해서 말이지요.

이부분을 잘 이해하셔야만 합니다. 사실 이해하기 쉽도록 설명하기가 여간 곤란한

내용이 아닐 수 없네요...하지만 용기를 가지고...

PC에서는 모든 문자, 예를 들면

a 부터 z, A 부터 Z, 0 부터 9, 또는 ~!#@$#$%^&*()_+=-\| 와 같은 특수 문자들...

이 모든 문자들에 각기 값이 주어져 있습니다. 이것은 IBM PC 어느 컴퓨터에서나

각 대응되는 값들은 항상 같습니다.

( 참고로 숫자 0 부터 9들도 모두 문자로 취급 합니다 )

예를 들어 ,

a = 97

b = 98

...

2 = 50

A = 65

B = 66

..

$ = 36

...

이와 같이 각 문자들에는 고유 값들이 있습니다.

이것은 전 세계 표준으로 어딜가나 같습니다.

( 대부분 어셈블리어 책에는 이런 ASCII코드 표가 수록되어 있습니다. )

특히 주의 하실 점 두가지는 PC에서는 영문 대문자와 소문자의 대응 값(ASCII 코드

값) 이 다르게 주어진다는 것입니다.

그리고 또 하나는 숫자 5 와 문자 5는 엄연히 종류가 다른 내용입니다.


그럼 문자 'a'라는 것이 메모리에 어떻게 저장 되는지 알아 봅시다.

a 라는 문자의 ACSII코드 값은 97 이라고 정의 되어 있습니다.

그럼 이것을 2진수로 바꾸어 보겠습니다.

01100001  이렇게 바뀌지요...

그럼 끝 입니다. 이걸 바로 메모리에 저장 시키면 되는 거지요...

하지만 여기서 문제가 하나 발생 합니다.

과연 PC가 메모리에서 01100001이라는 값을 받아들일때 과연 이것을 10진수로 97이라는 값으로 받아들이냐... 아니면 이것을 문자 'a'로 받아들이냐가 발생합니다.

97이라는 값으로 받아들이냐... 아니면 이것을 문자 'a'로 받아들이냐가 발생합니다.

하지만 걱정 마시길....사용자 측에서 만약 이것을 문자로 취급해라 하면 문자로

취급하고 숫자(값)으로 취급해라 하면 값으로 취급한다는 것입니다.

다시 말해 만약 97이라는 값을 저장 시켰다고 했을때, 사용자가 이것을 값이 아닌

문자로 취급해라 하면 이 값이 문자로 바뀌어서 취급된다는 점...

반대로 'a'라고 저장을 시키더라도 메모리에는 97이라는 값이 저장되게 되므로 이를

값으로 취급하라고 하면 값으로 취급한다는 것입니다.

이제 문자 'I have 35$' ( 이거 문법에 맞는지는 모르지만 ) 를 메모리에 어떻게

저장 시키는지 알아봅니다.

I = 73, h = 104, a = 97, v = 121, e = 101, 3 = 51, 5 = 53, $ = 36

이렇게 정의 되어 있습니다. 그리고 문자들 사이의 Blank(공백)은 0으로 값이 정해져

있습니다. 이걸 토대로 메모리에 저장 되는 형태를 살펴 보지요...

01001001 00000000 01101000 01100001 01111001 01100101 00000000

   I       공백      h         a        v        e      공백


00110011 00110101 00100100

   3         5        $


이와 같이 메모리에 순서대로 저장되어 집니다.

여기서 하나 중요한 점은 한 문자를 기억 하는데는 8비트, 즉 1바이트의 메모리를

사용한다는 점을 알 수 있습니다.

그리고 영문 모두 합치고 숫자 그리고 특수문자 모두 합쳐바야 문자의 종류가 255개가

되질 않습니다. ( 126까지에서 모든 사용 가능한 문자가 대응될 수 있습니다)

그럼 127번 부터는 어떤 문자가 되응 되어 있을까요?

그건 확장 ACSII코드라고 하는 특수 문자가 대응되어지고 있습니다.

보통 우리들이 키보드 상으로는 입력할 수 없는 문자들.. 예를 들어 하트 모양이나

네모, 또는 직선, 십자표시 등등이 대응되어 있습니다.

만약 이런 문자들을 화면상에 찍어보고 싶다 하신 분들은,

일단 ALT키를 누르고 계세요, 그다음 오른쪽 숫자 키패드에서 228을 친 다음 알트키를

때 보세요...그럼 아마 시그마 기호가 찍힐 겁니다. 이런 식으로 확장 아스키 문자도

사용 할 수 있습니다.


제가 보기에도 설명이 좀 미흡한데....모르시는 점 계시면 물어보시길...

메모리에 관한 설명은 다음에 계속 됩니다.



 [5] 제목 : [초급-기초] 메모리에 대해서 #2
 올린이 : 까망벌레(정태식  )    94/10/24 19:41    읽음 : 540  관련자료 없음

1. 메모리 찾아가기 ...

혹시 1메가의 메모리에는 전부 몇개의 스위치(비트)가 있는지 한번 계산해

봅시다.

겨산해 보면......8388608개의 어마어마한 숫자가 나옵니다.

이걸 byte로 바꾼다고 해도 1048576byte 라는 무시 못할 숫자가 나옵니다.

전에도 말씀 드렸다 시피, PC에서는 대부분의 데이터 처리를 byte로 한다고

말씀드렸습니다.

예를 들어 큰 건물을 생객하 보죠. 여기에는 전부 1048576개라는 갯수의 방이 있

다고 해봅시다. 그리고 각각의 방에 순서대로 1번 부터 2,3,4,5....1048576 이렇게

번호를 매길 수 있겠죠. 그리고 각 방은 크기가 모두 같습니다. 그럼 이 각각의

방들에 어떤 물건들을 넣어 놓습니다. 그리고 모든 방들이 꽈악 찼습니다.

여러분은 만약 여기서 지금 필요로 하는 물건을 꺼내기 위해서는 이렇게 많은

방들 중에 필요로 하는 물건이 어디에 있는지 어떻게 알수 있죠?

당연히 그 물건이 들어있는 방의 번호를 알 고 있다면 아주 쉽게 찾아 올 수

있습니다. 마찬가지로 메모리도 이와 같이 각각의 바이트 마다 번호가 매겨져

있다고 할 수 있습니다. 그리고 각 방의 크기는 1바이트의 크기로 정해져 있는

것이지요.

이것을 3차원 적이 아닌 1차원 적으로 생각하시는 것이 훨씬 편합니다.

다음과 같이 ...

 5번방  6번방  7번방  8번방  9번방  10번방.....

;------------------------------------------

; 사과 ;  배  ; 휴지 ; 담배 ; 성냥 ;  꽃  ; .......

;------------------------------------------

그럼 성냥을 꺼내오기 위해서는 9번방을 찾아가서 꺼내오면 돼겠지요.

그럼 메모리로 시선을 바꾸어 보겠습니다.

만약 메모리에 'IBM PC 486' 이란 문자들을 메모리로 기억 시켰다고 하면,

  15  16  17  18 19  20  21  22  23  24   <-- 방 번호

;----------------------------------------

; I ; B ; M ;   ; P ; C ;   ; 4 ; 8 ; 6 ; ................

;----------------------------------------

 이와 같이 그림으로 나타낼 수 있습니다. 만약 pc에서 지금 4라는 문자를

가져오고 싶다고 한다면 그냥 22번 방에 있는 내용을 읽어오면 되는 겁니다.

보통 위와 같이 15, 16, 17,18 과 같은 숫자들을 주소(Address)라고 명합니다.


-- 참고 -- 16진수에 대해

컴퓨터에서 주로 사용하는 숫자의 진수는 2진수라고 했습니다.

하지만 2진수라는 것은 PC에서 보기에 사용하기 좋지만 사실 사용자가 보기에는

정말 까다롭고 번거롭기 그지없는 진수 입니다. 그럼 이 2진수라는 숫자를 좀 더

사용자 쪽에서 사용하기 편한 숫자로 대치해 보는 것도 좋겠지요. 그럼 이걸 10

진수로 바꾼다고 한다면 물론 사용자 쪽에서는 더이상 바랄것이 없겠지요.

하지만 이 10진수와 2진수와의 상호간 변환은 많은 계산을 필요로 한다는 문제점을

안고 있습니다. 그 대신 사용하는 진수가 바로 16진수라는 것입니다.

이것은 2진수와의 상호 변환이 매우 간단하기 때문 입니다.

가령 11101001 이라는 1바이트 숫자를 16진수로 바꾼다고 한다면, 아마 이걸

10진수로 바꾸어서 다시 16진수로 바꾸는 방법이 있을 수 있겠지요. 하지만

이런 방법 보다 더욱 편한 방법이 있습니다.

바로 이 2진수 숫자들을 오른쪽에서 부터 4개씩 짤라가는 것입니다.

1110, 1001 이렇게 말이지요 그럼 이 뒷부분의 숫자를 16진수로 바꾸면

9 라는 숫자가 나옵니다. 그리고 앞의 것은 14가 나오죠. 이걸 각각 16진수로 하면

9 와 E 가 됩니다. 그럼 이걸 그대로 쓰면 9E 라는 16진수로 쉽게 변환 되는 것이자요


1110 1001   <-- 2진수

  9    E    <-- 16진수

이렇기 때문에 PC에서는 16진수를 많이 사용하게 됩니다.



2. 세그먼트(Segment) 와 오프셋(Offset)

그럼 PC에서는 이러한 주소들을 어떻게 관리하는지에 대해 알아보도록 하겠습니다.

우선 레지스터에 관해 조금만 말씀드리겠습니다.

CPU에는 레지스터라는 장소가 있습니다. 전에 말씀드렸다시피 레지스터라는것은

메모리와 거의 차이가 없다고 했습니다. 그래서 레지스터라는 것은 데이터를 저장

하고 그것을 연산하는 것인데, 역시 데이터를 저장하는데 있어서 그 용량(크기)

의 제한이 있다고 할수 있습니다.

모든 레지스터들은 그 크기가 65535로 제한이 되어 있습니다. 이걸 2진수로 바꾸면

1111111111111111 이 되어 전부 16비트가 됩니다. 즉 바로 2바이트의 크기 제한을

가지고 있습니다. ( 참고로 2바이트를 보통 1워드라고 불릅니다 )

16진수로 바꾸면 FFFF 로 제한이 되어 있다고 할 수 있죠.


메모리 주소를 알아내고 찾고 하기 위해서는 일단 레지스터라는 곳에 메모리

주소를 넣어놓으면 그 주소에 있는 데이터를 가지고 작업을 하게 되는 것이지요.

그럼 여기서 문제가 생갑니다. 그건 바로 1048576개의 주소들을 각각 1대1로

대응되도록 해야 하는데, 레지스터의 제한은 65535로 제한되어 있다는 것입니다.

만약 3450번지에 있는 데이터를 조작하기 위해서는 레지스터에 먼저 3450이라는

수를 넣어주면 되지만, 98500번지에 있는 데이터를 조작하기 위해서는 65535로

제한되어 있는 레지스터에서는 불가능 하게 됩니다.

그럼 어떻게 하면 될까요?

바로 레지스터 2개를 사용하면 됩니다. 즉 2개의 레지스터들이 메모리의 주소를

지시하게 하면 가능하게 되는 것이지요.

그럼 2개의 레지스터, 즉 4바이트의 용량으로 컨트롤 할 수 있는 메모리의 크기는

4294967295바이트, 즉 약 4095메가의 메모리를 컨트롤 할 수 있다는 결론이 나옵니다.

충분하지요? 하지만, 하지만 말이죠 도스 상에서 직접 컨트롤 할 수 있는 메모리의

크기는 위의 숫자와는 다른 약 1메가의 메모리밖에 컨트롤 할 수 없습니다.

왜 그럴까요? 분명 2개의 레지스터가 메모리주소를 지시하는것만은 틀림 없습니다.


여기에 바로 세그먼트와 오프셋이라는 개념이 나옵니다.

바로 위의 두개가 메모리주소를 조작하는데 사용되는 용어 입니다.

잠시,절대주소 라는 것이 있습니다. 각 메모리의 방에는 각각 고유의 숫자가 정해져

있습니다. 이 숫자들을 절대주소 라고 부릅니다. 위에서 보았듯이 이 절대주소를

나타내기 위해서는 하나의 레지스터만으로는 불가능 하겠죠?

세그먼트와 오프셋 모두 각각 1워드(2바이트)의 크기를 가지고 있습니다.

이 두개가 과연 어떻게 메모리의 절대 주소를 나타낼까....

여기선 간단한 계산이 필요 합니다.

예를 들어 만약 세그먼트가 1FE4 이고 오프셋이 43DA 라고 한다면 ,

( 물론 16진수 입니다 )

절대 주소를 구하는 방법은 식으로 나타내자면

세그먼트 * 10(16진수에서의 10입니다) + 오프셋

이렇게 하면 절대 주소가 나오는 것이지요.

위의 예를 가지고 계산해 보면

세그먼트에 우선 16진수 10을 곱해 봅니다

1FE4 * 10 = 1FE40

그리고 여기에 오프셋을 더하면

1FE40 + 43DA = 2421A

그럼 2421A가 바로 절대 주소를 나타내는 값입니다.

10진수로 바꾸면 147994 의 절대주소값이 되는 것입니다.

이것을 보통 1FE4:43DA 이렇게 씁니다.

앞에것이 세그먼트, 뒤에것이 오프셋이 되는 것입니다.

참고로 여기서 주소 지정의 비 유일성이라는것이 있는데,

이것은 다음에 설명을 드리지요...

 그리고 다음에는 레지스터에 관해 대략적으로 설명을 드리지요..




 [6] 제목 : [초급-기초] 레지스터에 대해서
 올린이 : 까망벌레(정태식  )    94/10/25 02:55    읽음 : 524  관련자료 없음

레지스터에 관해...

전에 말씀 드렸듯이 메모리로만 읽어오는 것으로는 프로그램이 수행되어

지는 것은 아니라고 말씀 드렸습니다. 그리고 이 프로그램을 직접 수행하고

처리하는 곳이 바로 레지스터라는 말씀도 드렸습니다.

레지스터라는 것을 따로 설명하기에는  많은 무리가 따르기 때문에 후에

직접 프로그램을 실습해가면서 익히도록 하겠습니다.

그런 의미에서 이 내용들에 너무 매달리지 마시라는 부탁을 드리고 싶습니다.



어셈블리어를 하는데 있어서 가장 중요하고 자주 쓰이는 것이

바로 이 레지스터들 입니다.

'들' 에서도 알 수 있듯이 레지스터라는 것이 하나가 아닌 여러가지가 있다는

걸 알 수 있습니다. 그럼 간단히 모든 레지스터들의 구조를 그림으로 간략히

그려보겠습니다.


EU : Execution Unit

;---------;

; AH ; AL ;   AX

;---------;                       BIU : Bus Interface Unit

; BH ; BL ;   BX                  ;----------;

;---------;                       ;    CS    ;

; CH ; CL ;   CX                  ;----------;

;---------;                       ;    DS    ;

; DH : DL ;   DX                  ;----------;

;---------;                       ;    SS    ;

;   SP    ;                       ;----------;

;---------;                       ;    ES    ;

;   BP    ;                       ;----------;

;---------;

;   SI    ;

;---------;

;   DI    ;

;---------;


;---------;

;   IP    ;

;---------;


Flag Register

;-----------------------------------------------;

;  ;  ;  ;  ;OF;DF;IF;TF;SF;ZF;  ;AF;  ;PF;  ;CF;

;-----------------------------------------------;


<< 참고 서적 : IBM PC Assembly Language and Programming  ( Peter Abel ) >>


대략적 구조를 그려 봤습니다. 이것만 가지구는 전혀 모르시겠지요?

그럼 위의 그림을 가지고 설명을 하겠습니다.


1. 대이타 레지스터 ( Data Registers )

데이타 제리스터란 바로 AX, BX, CX, DX 이 네개의 레지스터들을 한꺼번에

가르키는 용어 입니다.

구지 이렇게 용어를 정해서 나누는 이유는 확실하게 이런 레지스터들의 기능이

구분되기 때문에 주저없이 이렇게 나누고 있는 것입니다.

그럼 과연 데이타 레지스터들의 용도와 구조는 어떻게 되어 있을까...

일단 정리를 해 보면


 AX : 누산기 ( 산술연산, 포트로 부터의 I/O )

    I/O --> ( Input, Output ) 의 약칭

 BX : 베이스 ( 데이타 구조의 시작 주소 )

 CX : 카운트 ( 카운트, 비트 쉬프트 카운트 )

 DX : 데이타 ( 산술연산, I/O주소 )


<< 참고 서적 : Microsoft Macro Assembler Bible ( Barkakati & Hyde ) >>


이렇게 개요적으로 말할 수 있습니다.

일단은 이렇게 넘어가기로 하지요. 자세한 기능들은 하나하나 설명하기엔 너무나

방대하고 또 아직까지 거기까진 좀 무리라는 생각때문 입니다.


그럼 다음으로 이 레지스터들의 생김새에 대해 말하지요.

일단 위의 그림에서 보면 네모칸 오른쪽에 AX, BX와 같이 써 있고 네모칸 안에는

AH AL, BH BL 이라고 써 있는데요, 이건 무엇을 의미 하냐면, 전에 말씀 드렸듯이

레지스터라는 것의 크기가 1워드라는 말씀을 드렸을겁니다. 그리고 또한 PC의

데이터 처리는 1바이트 단위로 이루어 진다고도 말씀 드렸고요.

그런것들을 연결시키면, AX를 예를 들어서, AX 는 2바이트고 이것을 반으로 쪼개

(쪼개면 1바이트씩 짤리겠지요) 서 워드단위 연산뿐이 아닌 바이트 단위의 연산도

가능해게 하여 준다는 것입니다. 다시 말해


             AX = 16bit ( 2byte )

;------------------------------------------;

; AH = 8bit ( 1byte ) ; AL = 8bit (1byte ) ;

;------------------------------------------;


그림과 같이 AX라는 것은 2바이트의 크기로 2바이트 연산도 가능 하지만,

AH, AL 등과 같이 각각 1바이트씩으로 쪼개어 1바이트 연산도 수행한다는 것입니다.

( 여기서 AH 의 H 는 'High' , AL 의 'L'은 'Low' 를 의미합니다 )

그럼 레지스터의 연산 기능을 이용해 예를 보여 드리겠습니다.

만약 04F2 + 2F4A 라는 덧셈을 수행한다고 치면, 이 두 수는 모두 2바이트의 크기로

연산이 되며 결과또한 3447 이라는 2바이트로 나옵니다. 그래서 이와 같은 것은

워드 연산이기 때문에 바로 AX를 사용해 덧셈을 하게 됩니다.

그리고 3447 를 예를 들면, 이것은 2바이트 크기 이므로 AX레지스터에 들어갈 때는

1바이트씩 짤라서 AH에는 상위 34 가 , 그리고 AL에는 하위 47이 들어가게 됩니다.


레지스터의 변화            명령어

    ;---------;

 AX ; 04 ; F2 ;            MOV    AX, 04F2

    ;---------;            ( AX 레지스터에 04F2를 넣어라 )


    ;---------;

 DX ; 2F ; 4A ;            MOV    DX, 2F4A   

    ;---------;            ( DX 레지스터에 2F4A를 넣어라 )


    ;---------;

 AX ; 34 ; 47 ;            ADD    AX, DX

    ;---------;            ( AX 와 DX 를 더하여서 그 결과를 AX에 넣어라 )


다음과 같이 레지스터가 변하게 됩니다. 물론 위에 나온 명령어 들에는 신경쓰지

마시고 그 아래 괄호로 되어있는 설명만 아시면 됩니다.

( 흐..가능하면 명령어는 사용하지 않을려고 했는데... )


그리고 만약 2C + A2 와 같은 연산이라면, 피연산자와 결과 값 모두 1바이트 크기

이므로 ( 결과 : CE ) 이것은 1바이트 연산만으로 가능 합니다.


    ;----;

 AL ; 2C ;                  MOV AL, 2C

    ;----;                  ( AL레지스터에 2C를 넣어라 )


    ;----;

 DL ; A2 ;                  MOV DL, A2

    ;----;                  ( DL 레지스터에 A2를 넣어라 )


    ;----;

 AL ; CE ;                  ADD AL, DL

    ;----;                  ( AL 과 DL 을 더하여 결과를 AL에 넣어라 )


아시겠죠?

그리고 또 한가지.

만약 9B + C3 과 같은 경우는 어떨까요? 이것은 피연산자가 1바이트인것에 대해

결과값이  15E  와 같이 2바이트의 크기가 되어 버립니다.

이런 경우는 다음에 설명 드리도록 하겠습니다.



2. SP, BP, SI, DI, 세그먼트 레지스터 ( CS, DS, SS, ES ), IP


이제 다음 나머지의 레지스터에 관하여 간략히 설명 드리겠습니다.

이것들은 모두 메모리에 관련된 레지스터들로 앞서 배운 메모리 주소에

관한 것들입니다.

AX 등의 레지스터와는 달리 이들은 모두 1워드 단위의 처리밖에 할 수 없

습니다. 그 이유는 메모리의 오프셋 및 세그먼트들의 주소는 모두 1워드를

기본으로 조작이 되어지기 때문 입니다.

그리고 CS, DS, SS, ES 들은 모두 세그먼트를 조작하는 레지스터들입니다.

여기에 한가지 IP 라는 것이 포함 되는데 이것은 CS와 항상 함께 짝을

이루는 레지스터 입니다.

( 마찬가지로 SS 와 SP또한 짝을 이룹니다 )

여기서는 일단 간단한 표만을 그리고 끝내겠습니다.

이유는 이것들을 지금 설명한다고 해 봤자, 더 복잡하고 헥갈리기만 할 뿐

이기 때문 입니다. 이것들에 대한 설명은 차차 프로그램 예를 들어가면서

설명을 해 드리겠습니다.


;-------------------------------------------------------;

; SP : Stack Pointer     : 스텍의 현재 탑(Top)의 오프셋 ;

; BP : Base Pointer      : 스텍으로의 프레임 포인터     ;

; SI : Source Index      : 스트링 명령어                ;

; DI : Destination Index : 스트링 명령어                ;

;-------------------------------------------------------;


;------------------------;

; CS : Code Segment      ;

; DS : Data Segment      ;

; SS : Stack Segment     ;

; ES : Extra Segment     ;

;------------------------;


;--------------------------;

; IP : Instruction Pointer ;

;--------------------------;



3. 플래그 레지스터

플래그 레지스터라는 것은 우리가 어떤 프로그래밍을 하고서 그 프로그램을

수행할 때 PC 내에서 발생되는 눈에 드러나지 않는 여러가지 상태들을

나타내어 주고 또 사용자가 직접 채크할 수 있도록 하여 주는 레지스터 입니다.

이것 또한 간단히 넘어가겠습니다.


Flag Register

;---------------------------;

; OF : Overflow Flag        ;

; DF : Direction Flag       ;

; IF : Interrupt Flag       ;

; TF : Trap Flag            ;

; SF : Sign Flag            ;

; ZF : Zero Flag            ;

; AF : Auxiliary Carry Flag ;

; PF : Parity Flag          ;

; CF : Carry Flag           ;

;---------------------------;


후후...이렇게 쓴 제 자신도 잘 모르겠는데 여러분은 어떻겠습니까...

그냥 부담없이 한번 읽어 보시고 넘어가세요...

차차 차근차근 설명을 할테니까요...

^_^

^

 [7] 제목 : [초급-실습] 프로그램 하나 ( 소스 & 설명 )#1
 올린이 : 까망벌레(정태식  )    94/10/26 00:41    읽음 : 610  관련자료 없음

이제 DEBUG 라는 것으로 간단한 어셈블리어 프로그래밍을 해보겠습니다.


음...DEBUG라는게 과연 어디에 쓰이는 것인지 우선 말하자면,

DEBUG 말그대로 버그를 잡은 데에 쓰이는 것입니다. 도스상에서 지원되는

이런 버그잡는거 말고라도 터보 디버그같은 여러가지 유틸리티가 있습니다.

하나하나 단계별로 실행을 시켜가며 과연 무슨 버그가 있나 하고

레지스터나 플래그 등등을 일일이 살펴보며 확인할 수 있는 유틸중의 하나죠....


헤...혹시 '버그'가 먼지 모르시는 분....을 위해 간단히...

프로그래머가 작성한 프로그램 중에 발생할 수 있는 대표적인 것 중에

ERROR와 WORNING이 있고 또 여기에 가장 골치아픈 버그라는 것이 있지요.

에러가 나면 물론 프로그램 실행이 불가능 하게 되는건 당연 하고요,

WORNNING같은 것은 실행하는데 지장은 없지만 자칫 엉뚱한 결과가 출력될수도

있는 위험성을 안고 있을때 생기는 메시지 입니다. 이 두가지(ERROR,WORNNING)

모두는 컴파일러나 링크 ( 컴파일과 링크에 대해서는 다음 기회에...) 시에

알아서 프로그래머에게 알려 줍니다. 하지만 이 놈(버그)은 위의 두가지 와는

달리 어떠한 출력 메시지도 없이 실행이 되지만 엉뚱한 결과가 나오거나 자칫

시스템이 다운 되어 버리는 경우도 있습니다.

이럴때는 귀찮지만 일일이 프로그램 소스를 검사 하던가, 그래도 안되면

이 DEBUG라는 것을 돌려가지구 버그를 잡게 됩니다.


저희 학교 교수님의 예를 들자면, 이분께서 5000라인 가까이 되는 C프로그램을

작성을 했는데 에러나 경고가 전혀 없이 결과값이 엉뚱하게 나오더랍니다.

그래서 그 5000라인 이나 되는 소스를 일일이 검사하고 검사해도 전혀 이상이

없었답니다. 그것을 1주일 동안 꼬박 밤새가며 찾다가 드디어 찾았는데,

그것이 무엇이냐...하믄


if ( i == 0 ) { 이라고 해야 하는 걸

if ( i = 0 ) { 이라고 했다는 것입니다.


그걸 알았을 때의 그 황당함....

꼬옥 이런걸 버그라고하지는 않습니다만, 이런것도 버그가 될 수 있다는 것이지요.


그럼 다시 원점으로 돌아와서...

아마 여러분들 컴퓨터에는 모두 이 DEBUG라는 것이 있을겁니다.

흐....도스 없이 컴을 사용하시는 분은 없겠지요?


그럼 이 DEBUG를 돌려 봅시다.


C:\DOS>debug

-


이와 같이 화면에는 '-' 표시가 찍힙니다. 이건 바로 디버그를 사용할

준비가 되었다는 것을 의미 합니다. 그럼 다음과 같이 입력을 해 보세요.

( 대문자는 자동으로 표시되는 것이고, 소문자는 직접 입력을 하는 것입니다. )


-a

0A48:0100 mov ax,0123

0A48:0103 add ax,0025

0A48:0106 mov bx,ax

0A48:0108 add bx,ax

0A48:010A mov cx,bx

0A48:010C sub cx,ax

0A48:010E sub ax,ax

0A48:0110                    <--- 여기서는 그냥 엔터만 칩니다.

-


그럼 간단한 예제 프로그램 입력을 마쳤습니다.

여기서 한가지, 앞의 숫자 0A48:0100 에서 ':'앞의 0A48은 시스템 마다 다를

수 있습니다.

( '다를 수 있습니다' 가 아니고 아마 10중 8,9는 다를 것입니다 )

그건 신경쓰지 마시고......

일단 이들이 맞게 입력이 되었는지 확인을 해 바야겠죠.

그럼 또 다음과 같이...




-u100

0A48:0100 B82301           MOV AX,0123

0A48:0103 052500           ADD AX,0025

0A48:0106 89C3             MOV BX,AX

0A48:0108 01C3             ADD BX,AX

0A48:010A 89D9             MOV CX,BX

0A48:010C 29C1             SUB CX,AX

0A48:010E 29C0             SUB AX,AX

........

-

( 바로 위의 '....'은 그 뒤로도 몇가지 비슷한 형태로 주루룩 나온다는 것을

말합니다. 이 부분은 시스템 마다 다른 내용이 출력 되기 때문에 별로 신경쓸

것은 없습니다. 우리가 필요한 부분은 위에 쓴 저 내용뿐입니다. )


제대로 되었는지요? 잘못되었다고요? 그럼 'Q'로 디버그에서 빠져 나온뒤 다시

시작해 보세요...( 다른 방법이 있긴 하지만 일단은 그렇게 하시기를... )

사실 위의 프로그램은 실행 시켜도 아무런 결과 없이 시스템만 다운 됩니다.

절대 실행 시키지 마시길....헤헤~~


그리고 한번 다음과 같이 입력을 해보세요..


-d100

0A48:0100  B8 23 01 05 25 00 89 C3-01 C3 89 D9 29 C1 29 C0   .#..%.......).).

0A48:0110  8B C6 F7 D0 D3 48 DA 2B-D0  .....등등 주루룩~~~


이렇게 출력이 됩니다.

내용은 다를 수 있지만 첫째줄의 모든 내용은 모두 같습니다.

( 설명은 조금 후에... )


그럼 이제 본격적으로 위의 프로그램이 과연 제대로 돌아가는지, 또 어떻게

레지스터들이 변하는지 확인을 해 봐야 겠죠?

그럼 일단 프로그램이 실행되기 전의 레지스터들의 상태를 체크해 봅시다.


-r

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=0100   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        MOV     AX,0123

-


다음과 같이 출력이 될것입니다.

하지만 위에 DS, ES, SS, CS 값들은 모두 컴퓨터 마다 다르게 출력이 될겁니다.

하지만 위의 DS, ES, SS, CS 네개의 값이 모두 동일하다는 것은 어느 PC나

같습니다.

그럼 하나씩 단계적으로 실행을 해 보지요.

( 출력에 대한 설명은 조금 후에 하겠습니다. )

( '()' 안의 숫자는 실제로는 출력이 되지 않는 내용 입니다.

  이 숫자는 설명의 편의상 순서를 정하기 위해 사용 했습니다 )

-t

(1)

AX=0123  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=0103   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        ADD     AX,0025

-t

(2)

AX=0148  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=0106   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        MOV     BX,AX

-t

(3)

AX=0148  BX=0148  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=0108   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        ADD     BX,AX

-t

(4)

AX=0148  BX=0290  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=010A   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        MOV     CX,BX

-t

(5)

AX=0148  BX=0290  CX=0290  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=010C   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        SUB     CX,AX

-t

(6)

AX=0148  BX=0290  CX=0148  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=010E   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        SUB     AX,AX

-t

(7)

AX=0000  BX=0290  CX=0148  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=0110   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        MOV     AX,SI <-- 이부분은 다를 수 있습니다.

-


흐...뭐가 먼지... 정신이 없죠?

그럼 이제 이 결과 값들을 가지고 한번 설명을 해 보겠습니다.


우선 이같이 실행도 안되는 프로그램을 뭐하러 짰는고....라고 생각하신다면,

사실 이 프로그램을 예로 든 이유는 프로그램에 따라서 변하는 레지스터의

값들을 확인해 보기 위해서 입니다.


우선 위에서 보았던 'u'에 대해서 나오는 결과들에 대해 말씀 드리지요...

'u'는 'unassemble'의 약자로 'assemble'의 반대적인 의미를 가집니다.

위에서 보았던 'd'의 결과들과 'u'의 결과들에서 발견할 수 있는 공통점이

있지요? 그건 바로 'u'의 결과중에 보이는 '0A48:0100' 과 ' MOV     AX,0123'

사이에 있는 'B82301'이라는 것이 보이죠?

그럼 바로 이 위치에 있는 문자들을 주욱 모아 보면....


B82301 052500 89C3 01C3 89D9 29C1 29C0


와 같은 내용이 됩니다.

그리고 'd'의 결과 값들의 내용을 보면...


B8 23 01 05 25 00 89 C3-01 C3 89 D9 29 C1 29 C0


와 같습니다. 어때요? 전자의 내용을 두글자씩 분리해 정리하면 후자의 내용과

꼬옥 들어맞는 것을 알 수 있습니다.


그럼 과연 위의 이상한 숫자와 문자들은 무엇을 의미 할까.....

'd'라는 것은 'Dump'의 약자로 즉, 현재 메모리의 내용을 보여주는 명령입니다.

바로 화면에 주루룩 나오는 내용들이 메모리의 현재 내용이라는 것이지요.


그럼 위의 내용을 분석해 보겠습니다.

일단 'B82301'을 시작해 보죠...

이것은 바로 기계어 코드로 'MOV AX,0123'이라는 명령어를 기계어로 바꾼

것입니다. 그럼 왜 이것을 기계어로 바꾸어야 할까요...

그것을 'MOV AX,0123'이라는 명령어는 PC에서는 전혀 알아 듣지 못하는 내용

입니다. 그래서 이것을 PC에서 알아들을 수 있는 내용으로 바꾸기 위해

위와 같은 기계어 코드를 생성하게 된 것입니다. 어셈블러는 이와 같이

사용자가 입력하여준 어셈블리어 언어를 바로 기계어 코드로 변환 하여 주는

역할을 합니다. 사실 사람이 직접 이 기계어 코드를 사용해서 프로그래밍

한다면 더없이 좋겠지요...하지만....그 기계어 코드를 일일이 다아 왜우는

사람은 아마 없을 겁니다. ( 제가 알기로는 말이죠..흐흐 )

그래서 좀 더 사용자가 편리하게 프로그래밍을 할 수 있도록 영문으로 되어진

'MOV' 와 같은 명령어를 만들어 내게 된 것입니다.

C언어나, 베이직 등등 모든 프로그래밍 언어들이 바로 프로그래머들을 위해

좀 더 편리하게 프로그래밍 할 수 있도록 만들어 졌습니다.

결과적으로 모든 언어는 최종적으로 바로 위와 같은 기계어 코드로 변환을

하게 된다는 것입니다.


다시 'B82301'이라는 것을 뚜러지게 봅시다... ( '뻥~' )

모두 3바이트로 된 내용 입니다.

'B8' <-- 요것은 과연 어떤 어셈블리어 명령어를 기계어로 변환한 것일까...

답은 간단...'MOV AX' 라는 것을 변환한 기계어 코드 입니다.

하필 'MOV' 가 아니고 'MOV AX' 를 통채로 변환을 했느냐...하시는 분들을 위해

잠시....

솔직히 'MOV' 따로 'AX'따로 각각 한 바이트의 기계어 코드로 변환을 한다고

하면 알아보기도 쉽고 좋겠죠...하지만 여기에 하나 단점이 발생합니다.

만약 'MOV' 가 기계어 코드로 'C3' 이라고 하고 'AX'는 '25' 라고 칩시다

그럼 'MOV AX'라는 것을 기계어 코드로 바꾸게 되면 결과적으로


C325 라는 결과가 되겠지요?


과연..... 'B8'이라는 코드가 'C325'라는 코드로 변환이 됨에 있어서

원래는 1바이트 만으로 되었던 것이 2바이트로 늘어나 버렸습니다.

이건 쓸데 없는 메모리 낭비만 가져올 뿐입니다.


혹시나 왜, 'MOV' 를 'C' 라는 것으로 하고 'AX'를 '2' 라고 한다면 역시

같은 1바으트로 변환이 가능하지 않냐고 하실 분이 계실 지도 모릅니다.

제 대답은 '벌러덩~' 입니다. 왜냐하면 전에도 말씀 드렸듯이 모든 PC에서

수행되는 기본 단위는 바이트 단위라고 했습니다. 그러므로 'C'라는 것은

완전한 하나의 바이트가 되질 못하기 때문에 처리를 할 수 없게 됩니다.


그럼 혹시 'MOV BX' 같은 것은 또 다른 1바이트의 기계어 코드가 있느냐..

라는 질문에 '예' 입니다. 그럼 그 많은 명령어와 레지스터들의 조합들을

이렇게 따로따로 코드를 정한다면 1바이트 가지고는 부족하게 되는건 아닌지..

하지만 걱정 마세요... 1바이트로 안된다면 2바이트를 써서 하면 되는 거니까..

메모리 낭비? 하지만 일단은 프로그램이 제대로 도라가는게 중요하자나요..

가급적이면 PC에서 메모리를 적게 먹는 방향으로 설계되었을 줄로 압니다.

됐지요?


이제 뒤의 '2301' 에 대해 말씀 드리지요.

이건 가만히 살펴보면 입력하는 데이터 값인 '0123' 과 숫자는 모두 같은데,

대신 순서가 바뀌었다는 것을 알 수 있습니다.

즉 '01' 과 '23' 각각 1바이트의 내용이 서로 자리바꿈을 했음을 알 수

있습니다. 이 이유는 메모리 구조상 원래 이렇게 저장이 되어지도록 됴습니다.

그 '이유'라는 것은 다음으로 넘기기로 하지요...히힛~~

어쨋든 '2301'은 AX레지스터에 넣을 값을 알려주는 것입니다.


휴~~~ 일단 1행은 마쳤습니다.

역시 다음 행들 또한 같은 맥락으로 이해 하시면 되고요...주루룩~~~

왜 어셈블리어 명령어에 대한 설명은 안하냐고요?

후후...이 프로그램을 해보는 목적은 명령어 익히기가 아니구여

바로 메모리의 실제 모습과 레지스터들을 이해하기 위해 해 보는 것이여요..



이제 'd'라는 명령으로 나오는 출력값들이 무엇을 의미 하는지 알겠죠?

바로 프로그램을 기계어 코드로 바꾸어서 메모리로 저장 시킨 모양을

알려주는 것입니다.

그럼 'd'로 나오는 출력들 중에 왼쪽의 숫자 '0A48:0100' 같은 것들은 무엇을

의미 할까요...

이것들은 바로 메모리의 주소값을 표시해 주는 것입니다.

왼쪽이 세그먼트가 되고 오른쪽이 오프셋이 되는 것입니다.

그리고 제일 오른쪽의 알 수 없는 문자들은 무엇인가 하믄...

변환된 기계어 코드는 어디까지나 그냥 숫자들 입니다.

이 숫자들은 전에 말씀드렸던 아스키 코드로 1바이트씩 표시해 준 것입니다.

'23' 같은 것은(16진수) 아스키 코드표를 찾아보면 '#'로 정해져 있습니다.

나머지 들도 마찬가지구요. 그리고 '.'이 상당히 많은데, 이것들은

표준 아스키 코드 이외의 확장 아스키 코드들을 그냥 모두'.' 로 표시한

것입니다.


다음에는  'r' 과 't' 로 나오는 출력들을 가지고 설명을 드리겠습니다.





 







 [8] 제목 : [초급-실습] 프로그램 하나 ( 소스 & 설명 )#2
 올린이 : 까망벌레(정태식  )    94/10/29 20:06    읽음 : 460  관련자료 없음

후.....넘 늦게 올려서 죄송 합니다.

그럼 다시 그 소스를 가지고 설명을 계속 하겠습니다.

이번에는 'r' 명령과 't'명령에 대해 설명한다고 했지요?

그럼 해보겠습니다.


우선 데이타, 스텍, 코드 세그먼트에 대해 설명을 해 드리겠습니다.

여러분도 보시다 시피 소스에서 입력하여준 명령어들 'mov, add, sub'등등

이것들도 우선은 메모리에 저장되어 지는것을 보았지요?

이와 같이 PC에 어떠한 연산 명령을 내려 주는 것들을 저장하는 메모리 영역을

코드 세그먼트라고 부릅니다.

말그대로 메인 프로그램 이라고나 할까요.

전에 말씀드렸다 시피 메모리 구조는 세그먼트와 오프셋으로 구성되어 진다고

했습니다. 그럼 과연 한 세그먼트의 크기는 얼마나 될까요?

그것은 오프셋의 크기와 관련이 됩니다.

다음과 같은 경우를 생각해 보지요.

만약 메모리에 'abcdefg'라고 차례대로 저장을 시킨다고 해 봅시다.

그럼 이것들이 과연 메모리에 어떠한 모양으로 저장이 되어지는지 아시겠지요?


;--------------------------------

; a ; b ; c ; d ; e ; f ; g ;   ;

;--------------------------------

다음과 같습니다.

여기서 만약 'a'라는 문자가 저장된 곳이 바로 0123:53F2 라고 가정을 해 봅시다

그럼 세그먼트는 '0123'이 되고 오프셋은 '53F2' 라고 할 수 있습니다.

그럼 'b'가 저장된 곳은 주소가 어떻게 될까요...

그건 '0123:53F3'으로 세그먼트가 아닌 오프셋이 하나 증가한 메모리 주소를

갖습니다. 'c'역시 차례대로 '0123:53TF4'가 되는건 예측할 수 있죠.

이와 같이 하나의 세그먼트에는 오프셋의 범위가 '0000' 부터 'FFFF' 까지 됩니다.

그러므로 바로 오프셋의 범위가 하나의 세그먼트의 크기라고 할 수 있는 것입니다.]

이걸 계산해 보면 약 64K 바이트의 메모리가 됩니다.

정의해서, 하나의 세그먼트 크기는 64K의 용량을 갖는다는 것을 알 수 있습니다.

그래서 이 세그먼트 내에 이 코드들이 들어간다는 것이지요.

그럼 1메가의 메모리에는 과연 몇개의 세그먼트가 존재 할 수 있는지 계산해 보죠.

1 메가면 1024K바이트라는 것은 알고 있습니다.

그럼 계산은 간단.


1024 / 64 = 64


라는걸 알 수 있습니다. 그러므로 '1메가의 메모리 안에는 64개의 64K바이트를

같은 세그먼트가 존재 한다.' 라고 할 수 있습니다.


그럼 이와 같이 세그먼트들을 나누는데 있어서 왜 데이타 라느니, 코드라느니

이런것들을 나누느냐, 라는 질문에

만약 프로그램이 메모리에 저장되어지는데 있어서 그 크기가 64K를 넘어간다고

생각해 보세요. 그럼 이것을 어떻게 처리해야 하느냐....

물론 두개의 세그먼트를 사용하면 되겠지요.

그럼 이 두개의 세그먼트를 어떻게 사용해야 효율적일까요?

보통 만약 두개의 세그먼트를 사용한다고 하면, 전에 예를 들었던 '3+4'에서

'3'과 '4'는 데이터 라고 했고 '+'는 연산 명령이라고 했지요?

이와 같은 덧셈을 1000개 수행해야 한다고 해 보세요.

덧셈인데, 수 많은 데이터 들을 일일이 다아 더하는 명령을 주어도 되지만

데이타 영역을 따로 만들어 주고, 여기에 이 데이터 영역에 있는 값들을

꺼내다가 계산한다면, 훨씬 효율적이 될 수 있습니다.


여기서 데이터 영역이 저장되어지는 세그먼트를 데이타 세그먼트(Data Segment)

라고 부르고 바로 연산 명령 같은 것이 저장되어지는 세그먼트를 코드 세그먼트

(Code Segment) 라고 부릅니다.

물론 이 두가지(데이타, 연산명령) 를 하나의 세그먼트 안에 넣어 주어도 됩니다.

그리고 위의 내용에선 분명 스텍 세그먼트(Stack Segment) 라는것이 있다고 했습니다.

이 스텍이라는 것은 나중에 설명을 드리겠습니다.


그럼 대충 세그먼트라는 것에 대해 이해가 가셨을 줄로 믿습니다.


그럼 이제 IP ( Instruction Pointer ) 가 무엇인지 설명을 또 드리죠..

PC 상의 레지스터 상태를 보여주는 'r'명령을 내려 보면 알 수 있듯이

모든 레지스터의 저장된 값들이 나옵니다. 거기에 보면 'IP'라는 것이 있습니다.

이것은 보통 초기에는 '100'이라고 나옵니다. 이것은 오프셋 값을 나타낸 것으로,

무엇의 오프셋이냐 하믄, 여러분이 직접 넣어준 프로그램을 'u'명령으로 보면

'MOV AX,0123'이라는 프로그램이 바로 오프셋 100에 저장이 되어 있다고 나옵니다.

그럼  'IP'와 프로그램의 가장 선두 부분의 오프셋 값이 같다는 것을 알 수 있죠.

또 'r' 명령후 나오는 값들중에 제일 마지막 부분에 ' MOV AX,0123'이라고 써 있는

부분이 있죠.. 그것은 지금 상태에서  프로그램을 실행시키면, 다음으로 실행이

되어지는 프로그램을 나타내는 것입니다.


이제 't'명령을 실행시켜 보면 ( 't' 는 'Trace'의 약자로 프로그램을 하나씩

단독적으로 실행시켜 나가면서 레지스터의 상태를 확인해 볼 수 있습니다 )

일단은 'MOV AX,0123'이라는 것을 실행 시킵니다. 그럼 다음으로 실행시키는 명령

이 'ADD AX,0025'라고 표시해 줍니다. (표시된 것은 실행시킨 프로그램이 아닌

앞으로 실행된 프로그램이라는 것을 나타냅니다 )

그럼 이때까지 't'를 한번 해 주었습니다.

소스에서 보면 (1)번에 해당하는 내용 입니다.

그럼 여기서 이제 'IP'값을 보세요.

변했죠?

증가했음을 알 수 있습니다. 그럼 얼마나 증가를 했습니까?

'MOV AX,0123'이 차지하는 메모리는 3바이트라고 했습니다

그리고 그 다음 명령행이 저장되어진 메모리 주소는 'MOV AX,0123'이 저장되어진

오프셋 값에 바로 이 크기인 3바이트 증가한 '103'이라는 것을 알 수 있습니다.

다시 말해 'ADD AX,0025'라는 명령이 저장되어진 메모리 주소가 '103'부터 시작된

다는 것을 알 수 있습니다.

그럼 'IP'레지스터의 '103'과 값이 같지요...

(2) 번 (3) 번 모두 그 'IP'레지스터의 값과 지금까지 실행하고 다음으로 실행되

어질 메모리의 오프셋 값이 동일하다는 것을 알 수 있습니다.


여기서 보듯이 'IP'라는 레지스터는 프로그램 소스를 실행 시키는데 있어서

프로세서 (CPU) 가 과연 그 많은 메모리 중에 어떤곳에 실행시켜야 될 프로그램들이

있는지 프로세서에 알려 주어야 합니다.

그것을 알아야 프로그램이 실행이 되어지지요.

그럼 과연 프로세서가 어느것을 보구서 메모리의 어떤 부분에 실행되어질 프로그램

이 있는지 알 수 있을까요?

그것은 'CS'레지스터에 저장되어진 값으로 프로그램이 들어 있는 세그먼트를 알아

내고 'IP'레지스터에 저장되어진 값으로 프로그램이 들어있는 메모리의 오프셋

값을 알아 냅니다.

그럼 프로그램을 한 라인 실행시키면 그 명령을 수행하고 다시 프로세서는

'CS'값과 'IP'값을 확인 해 보고, 그 다음 명령를 수행하게 됩니다.

예를 들어 보죠.

전의 소스를 보면서....

'IP' 가 100인 상태, 그리고 'CS'가 '0A48', 에서 세그먼트 '0A48' 오프셋

100번지에 있는 'MOV AX,0123'을 실행하게 됩니다.

그럼 다시 그 다음 명령인 'ADD AX,0025'를 실행시키기 위해선 'IP'값이 3바이트

증가된 '103'이 되어야 합니다. 그것은 사용자가 할 필요 없이 PC가 알아서

해 줍니다. 그럼 프로세서는 그 두가지의 레지스터를 확인해 보고 다음으로

실행시켜야 하는 명령을 수행하게 됩니다.

그래서 'CS'와 'IP' 가 항상 짝을 이룬다는 말을 하게 됩니다.


데이타 세그먼트 또한 마찬가지 입니다.

그 예는 다음 프로그램 소스에서 알아보도록 하겠습니다.

















그리고 프로그램 모델이라는 것이 있습니다.

'small' 'compact' 'large' 'huge' 등등의 모델들이 있습니다.



 [9] 제목 : [초급-보충] 스텍?
 올린이 : 까망벌레(정태식  )    94/11/05 06:47    읽음 : 410  관련자료 없음

그럼 스텍 (STACK)에 관해 설명을 드리겠습니다.

스텍은 말 그대로 '쌓다'라는 의미를 지니고 있습니다.

쌓아서 무얼 할까요?

아마 '하노이의 탑' 이라는 유명한 문제를 기억하고 계실 겁니다.

세개의 막대중에 한개의 막대예 크기가 다른 원반들이 여러개 역삼각형 모양으로

꽂혀 있습니다. 이 원반들을 다른 막대에 삼각형 형태( 제일 아래에 가장 큰 원반,

그리고 위로 순서대로 크기가 점점 작은 원반을 쌓는 형태)로 순서대로 나열하는

것인데, 원반은 한번에 하나씩 밖에 옮기지 못합니다.

그리고 원반이 두개 이상 쌓여 있을때는 제일 아래것을 꺼내기 위해서는 위에서

부터 차레차레 하나씩 빼낸 다음 마지막으로 제일 아래것을 꺼낼 수 있는 거죠.


여기서 보듯이 이 구조를 LIFO( LAST-IN-FIRST-OUT ) 이라는 용어를 정의 할 수

있습니다. 우리말로 후입전출 이 됩니다.

이것은 가장 나중에 드러간 것이 가장 먼저 나온다는 뜻입니다.

그러니깐 1,2,3,4,5 번으로 번호가 매겨진 접시가 있다고 생각 해 보죠.

이걸 착착 포개서 제일 아래에 5번 그 위에 4번...이렇게 제일 위에 마지막으로

1번 접시가 오게 됩니다. 그럼 이걸 하나하나 꺼내게 되면 가장 나중에 올려 놓은

1번 접시가 제일 먼저 꺼내어 지게 됩니다.

그리고 제일 처음에 포개어 놓은 5번 접시는 제일 마지막에 꺼내어 지게 되는 것

이지요.

이걸 잘 이해 하셨다면, 스텍이란 것도 별 문제 없습니다.

좀 더 스텍에 가까운 모양을 예를 들면,

딱 동전만한 지름의 원통이 있는데 그 한쪽이 막혀있고 나머지 한 쪽은 뚤려

있습니다.

여기에 동전을 넣게 되면 하나씩 차례로 들어가죠.

그리고 이것을 하나씩 차례로 꺼낼땐 들어갈                                                                                                                痼都求.


이제 이 예를 메모리로 그대로 옴겨 보죠.


;---------------;

;       ;       ; FFFF

;---------------;

;       ;       ; FFFD

;---------------;

;       ;       ; FFFB

;---------------;

;       ;       ; FFF9


메모리의 형태를 그림으로 나타낸 겁니다.

왜 여기서 주소값이 FFFF 에서 그 다음이 FFFE 가 아닌 FFFD 가 되느냐고 의아해

하신다면, 스텍의 처리는 항상 1바이트가 아닌 1워드로 처리 된다는 것입니다.

그림에서 보면 한 칸의 메모리를 1바이트씩 둘로 나눈 것이죠.

그리고 그림의 바로 윗부분이 막혀 있고 아랫 부분이 뚤려 있다고 생각합시다.


여기에 동전 대신에 어떠한 데이터들을 순서대로 넣는다고 해 보죠.

데이터 값을 1034, 325D, 35F3 이렇게 순서대로 넣게 되면,


;---------;

; 10 ; 34 ; FFFF

;---------;

; 32 ; 5D ; FFFD

;---------;

; 35 ; F3 ; FFFB

;---------;

;    ;    ; FFF9


위의 그림과 같이 들어가게 됩니다.

그럼 만약 위의 데이터 중에 1034를 꺼내기 위해서는 ...

물론 깡통이라고 생각하면, 위의 뚜껑을 따고 꺼내는 방법도 있지만...

헤헤...이건 메모리니깐...

일단은 그 아래에 있는 두 워드의 데이터 들을 꺼내고 나서야 비로소 우리가 원하

는 1034 라는 데이터 값을 꺼낼 수 있게 됩니다.


;---------;         ;---------;         ;---------;         ;---------;

; 10 ; 34 ;         ; 10 ; 34 ;         ; 10 ; 34 ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

; 32 ; 5D ;         ; 32 ; 5D ;         ;    ;    ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

; 35 ; F3 ;         ;    ;    ;         ;    ;    ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

;    ;    ;         ;    ;    ;         ;    ;    ;         ;    ;    ;


원래의 메모리       여기서 35F3         그 다음 325D        드디어 1034를

                    을 먼저 꺼낸다.     를 꺼내고,          꺼내게 된다.


잘 이해가 가셨는지요....


그럼 어셈블리 명령어를 가지고 설명을...


        PUSH    AX

        PUSH    DX

        PUSH    CX

        PUSH    BX


일단 이와 같은 명령어를 주었다면, 가장 먼저 있는 AX레지스터의 값을 스텍에

쌓아 놓게 됩니다. 그 다음은 물론 DX이고, 다음은 CX, BX 순서로 스텍에 쌓이

겠죠.


예로 각 레지스터의 값들이 위의 명령어를 하기 전에 다음과 같이 들어있다고

가정 합시다.

AX = 0001

BX = 0002

CX = 0003

DX = 0004


그림을 그려보면


;---------;         ;---------;         ;---------;         ;---------;

; 00 ; 01 ; FFFF    ; 00 ; 01 ;         ; 00 ; 01 ;         ; 00 ; 01 ;

;---------;         ;---------;         ;---------;         ;---------;

;    ;    ; FFFD    ; 00 ; 04 ;         ; 00 ; 04 ;         ; 00 ; 04 ;

;---------;         ;---------;         ;---------;         ;---------;

;    ;    ; FFFB    ;    ;    ;         ; 00 ; 03 ;         ; 00 ; 03 ;

;---------;         ;---------;         ;---------;         ;---------;

;    ;    ; FFF9    ;    ;    ;         ;    ;    ;         ; 00 ; 02 ;


 PUSH AX             PUSH DX             PUSH CX              PUSH BX

 SP = FFFD           SP = FFFB           SP = FFF9            SP = FFF7


다음과 같이 순서대로 드러 갑니다.

그럼 위의 그림에서 SP란 무언가....

SP 란 STACK POINTER의 약자로서 IP와 같은 기능을 합니다.

PUSH AX와 같은 경우 PC는 과연 이 AX의 값을 어디에 쌓아 넣으라는 것인지를

이 SP 를 보고 알게 됩니다. PUSH AX를 하기 전에는 SP 는 FFFF를 가리키게 되므로

pc는 '아, AX레지스터의 값을 FFFF번지에 있는 메모리로 넣으라는 말이구나' 라고

인식을 하게 됩니다. 그리고 저장되는 값은 항상 2바이트 크기 이므로, 다음에

저장할 곳을 지정하게 될 때는 2바이트 하위 쪽인 FFFD 를 가리키게 되는 것이지요

근데 여기서 하나 주목 할 것은 SP 는 IP와는 반대로 갈수록 POINTER 값이 감소

한다는 것입니다. 이것은 스텍의 특성으로 언제나 이와 같이 상위메모리로 부터

하위 메모리로 차근차근 저장을 하게 되도록 만들어져 있습니다.

그럼 이 값들을 꺼내는 것은....


        POP     BX

        POP     CX     

        POP     DX

        POP     AX


이와 같이 했다고 합시다.

POP명령은 스텍에 쌓여 있는 데이터들을 하나씩 순서대로 꺼내오는 명령으로

가장 먼저 POP BX 부터 시작을 하게 됩니다.

여기서 하나 또 관심있게 볼 것은,

PUSH 와는 순서가 반대가 되었다는 것입니다.

PUSH 에서는 AX,DX,CX,BX 순으로 넣었는데, POP에서는 거꾸로

BX,CX,DX,AX 순으로 꺼내 온다는 것입니다.


;---------;         ;---------;         ;---------;         ;---------;

; 00 ; 01 ; FFFF    ; 00 ; 01 ;         ; 00 ; 01 ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

; 00 ; 04 ; FFFD    ; 00 ; 04 ;         ;    ;    ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

; 00 ; 03 ; FFFB    ;    ;    ;         ;    ;    ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

;    ;    ; FFF9    ;    ;    ;         ;    ;    ;         ;    ;    ;


 POP BX              POP CX              POP DX              POP AX

 SP = FFF9           SP = FFFB           SP = FFFD           SP = FFFF


다음과 같이 원래의 레지스터 값들이 다시 각 레지스터에 들어가게 됩니다.

근데 만약 혹시, PUSH 를 위와 같이 한 후에 POP 명령을 다음과 같이 했다면?


        POP     AX

        POP     BX

        POP     CX

        POP     DX


우선 POP 명령은 무조건 스텍에 싸여 있는 데이터 들을 꺼내오게 됩니다.

그리고 그 뒤에 있는 레지스터에 그 값을 무조건 집어 넣게 됩니다.

그래서 결과적으로 각 레지스터에는 다음과 같은 값들이 들어가게 됩니다.


        AX = 0002

        BX = 0003

        CX = 0004

        DX = 0001


이런 결과가 되어 버립니다.


그럼 스텍이라는 것이 왜 필요할까요?

그것에 대한 설명은 다음에 여러개의 프로시져를 사용한 예에서 자세히 설명을

드리겠습니다.



 [10] 제목 : [초급-실습] 프로그램 두울 #1
 올린이 : 까망벌레(정태식  )    94/11/05 06:48    읽음 : 453  관련자료 없음

이번엔 좀 더 실질적인 프로그램을 하나 해 보도록 하겠습니다.

이건 화일의 Attribute(속성)을 바꾸는 것으로 화일을 읽기 전용,

또는 읽기 전용 해제 를 위한 프로그램 입니다.

우선은 소스를 써 보죠.


        .MODEL  SMALL

        .CODE

        ORG     100H


PROTECT PROC    NEAR

        MOV     DX,82H

        MOV     DI,82H

        MOV     AL, 13

        MOV     CX,12

REPNE   SCASB

        MOV     BYTE PTR [DI-1],0

        MOV     AL,1

        MOV     CX,1

        MOV     AH,43H

        INT     21H

        INT     20H

PROTECT ENDP

        END     PROTECT


위의 것은 화일을 읽기 전용으로 세팅 하여 주는 프로그램 소스 입니다.


        .MODEL  SMALL

        .CODE

        ORG     100H

       

RELEASE PROC    NEAR

        MOV     DX,82H

        MOV     DI,82H

        MOV     AL,13

        MOV     CX,12

REPNE   SCASB

        MOV     BYTE PTR [DI-1],0

        MOV     AL,1

        MOV     CX,0

        MOV     AH,43H

        INT     21H

        INT     20H

RELEASE ENDP


        END     RELEASE


그리고 위의 것은 프로텍트(읽기전용)해제 프로그램 입니다.

( 이 소스는 피터 노턴의 ADVANCED ASSEMBLY LANGUAGE 에서 발췌한 것입니다.)


위의 것을 실행시키기 위해 서는 일단은 간단한 에디터로 위와 같이 입력을 하여

줍니다. 그리고 확장자가 ASM인 화일로 만들어 줍니다.

꼬옥 ASM일 필요는 없습니다만, 보통 어셈블리 프로그램의 소스는 ASM이라는

확장자를 쓰는게 일반 입니다.

위의 것을 파일로 저장한다음,

메크로 어셈블러나 터보 어셈블러로 실행 가능한 파일로 만들어야 합니다.

여러분께서 혹시 메크로 어셈블러나 터보 어셈블러가 없다면, 별수 없죠

구해서 해 보는수 밖에....헤헤..


MASM [FILENAME]


이렇게 해 주면 무슨 메시지가 나오는데 , 그냥 엔터를 치세요.

그럼 화일이 하나 생기는데, 확장자가 OBJ인 화일이 생깁니다.

이 화일은 여러분이 짠 프로그램 소스를 기계어 코드로 바꾸어 준 화일 입니다.

이것은 그 자체로는 바로 실행이 불가능 합니다.

실행하기 위해서는 링크라는 것을 시켜 주어야 하는데, 이것은


LINK [FILENAME]


이렇게 해 주면 됩니다.

그럼 EXE화일이 생겼을 겁니다.

( 위의 예는 메크로 어셈블러를 예로 들은 것입니다. 만약 터보어셈블러로 한다면

MASM --> TASM, LINK --> LINK 로 하여 주면 됩니다. )


만약 EXE2BIN 이라는 유틸이 있다면,


EXE2BIN [FILENAME] [FILENAME]


으로 해 주면 EXE화일을 COM화일로 바꾸어 줍니다.

구지 EXE화일을 COM 화일로 바꾸는 이유는 COM화일이 EXE보다 용량도 훨씬 작고

메모리에 로드되는 속도 또한 매우 빠르기 때문 입니다.

그렇다고 해서 모든 소스들이 COM화일로 변환이 가능 한 것은 아닙니다.

그 이유는 다음으로 미루기로 하죠...


그럼 한번 실행을 해 보죠.

만약 만든 화일이름이 'PROTECT'와 'RELEASE'라고 가정하고,


PROTECT [FILENAME]


그리고 한번 그 화일을 DELETE 해 보세요.

지우기 불가능이라는 메시지가 나올 겁니다.

그리고 다시 그 화일을


RELEASE [FILENAME]


이렇게 한 후,

DELETE해 보면 이번엔 지워 지는 것을 볼 수 있습니다.


그럼 프로그램 소스를 다시 보도록 하죠.


여기서 중요한 것은 각 라인의 명령어의 해설이 아닌 어셈블리어 프로그램 작성의

틀을 알아보기 위한 것이므로, 자세한 명령어 소개는 미루도록 합니다.


보면 위에 .MODEL SMALL 이라는 부분이 있습니다.

이것은 프로그램의 모델을 결정하여 주는 것으로, 코드 또는 데이터 같은 것들의

크기에 따라 하나의 세그먼트에서 각각의 코드와 데이터, 및 스텍이 들어갈 수 있는

크기라면 이 모델을 써 주게 됩니다. 즉 코드와 데이터 각각의 크기가 메모리에 로드

됐을때 한개의 세그먼트 크기인 64Kbyte 미만일때는 small모델로 하게 됩니다.

그리고 만약 데이터 영역이 하니의 세그먼트 크기를 초과 하고, 코드의 크기는

그 미만이라고 한다면,  그 모델은 COMPACT모델이 됩니다.

이와 같은 것을 표로 나타내면,


TINY : .COM 화일 형태

SMALL : 데이터와 코드를 각각 하나의 세그먼트에 저장

MEDIUM : 데이터는 64K 이하, 코드는 64K이상 가능

COMPACT : 데이터는 64K 이상(복수 배열 가능), 코드는 64K 이하

LARGE : 데이타와 코드가 64K 이상, 단일 배열이 아닐 경우

HUGE : 데이터, 코드, 데이터 배열이 64K이상


그냥 한번 읽고 넘어 가세요.

그리고 보통 HUGE모델은 거의 쓰이지 않습니다.

그리고 TASM에서는 HUGE모델을 지원하지 않는다고 합니다.


그 다음으로 .CODE 라는 것이 있습니다.

이것은 .CODE 다음에 오는 것이 바로 코드들이라는 것을 알려 줍니다.

즉, 본 프로그램으로 실질적인 명령어들이 온다는 뜻이지요.

그렇다고 데이터 영역이 올 수 없는 것은 아닙니다.

마찬가지로 .DATA라는 것도 따로 있으니깐요...


그 다음 ORG 100H 라는 것이 있는데, 이것은 본 프로그램이 메모리에 로드되는

오프셋 번지를 16진수 100번지부터 하라는 뜻입니다.

구지 이것을 부치는 이유는, 그 소스를 링크 시켜 실행 가능한 화일로 만들때

이 화일을 COM화일로 하기 위해서는 ORG 100H를 써 주게 되고, EXE 화일을

만들때에는 그것을 써 줄 필요가 없습니다.

그 이유는 나중에 COM화일과 EXE 화일의 차이점을 설명할 때 해 드리겠습니다.

참고로 오프셋 번지 0000 부터 0100H 까지의 영역은 COM화일일 경우에 한해서

PSP ( PROGRAM SEGMENT PREFIX ) 영역이라고 해서 프로그램 로드시 필요한

여러가지 정보들을 담게 됩니다.

이 또한 자세한 세부 사항은 다음 기회로...


좀 설명이 많이 어려워 진것 같습니다.

여기서 모르시는 부분이 있다면 질문란에 질문 하여 주시기 바랍니다...

그럼 아주 자세하게 설명을 해 드리지요..


소스 설명은 다음에 계속..... 2 부를 기대해 주세용~~헤헤...









 [11] 제목 : [초급-실습] 프로그램 두울 #2
 올린이 : 까망벌레(정태식  )    94/11/08 04:05    읽음 : 377  관련자료 없음

안냐세요./..

계속 강의를 해 볼꼐요...


바로 전에 올렸던 글을 다시 읽어보니..

훗..저도 잘 모르는 내용을 마구마구 올린거 가타서..

죄송~


다시 마음을 진정하고 좀 더 자세하게 써 보죠.


그럼 그 모델이라는 것에 대해 조금 더 설명을 하겠습니다.

과일 가계를 생각해 보죠.


사과랑, 배랑, 바나나... 세가지만 파는 과일 가계가 있습니다.

근데 이 가계는 너무 영세하고 작아서, 그리 많은 과일을 팔지는

못합니다.

그래서 구지 다른 전문 판매점 처럼 사과 코너, 배 코너..등등

이렇게 코너를 나누어 팔 필요가 없지요.

그래서 그냥 바구니 하나 가따 놓고 이 바구니 않에 사과니 배니 바나나니..

등등 한꺼번에 담아놓고 판다고나 할까요.

그러니깐, 바구니는 하나만 있으면 세가지 모두 가따놓고 팔 수 있게 되는거죠.


그리고 또 조금 큰 상점을 가 봅시다.

여기에선 역시 사과, 배 , 바나나를 파는데, 그 양이 많아서 이 세가지를

한꺼번에 하나의 바구니에 도저히 담을 수 없습니다.

그래서 세개의 바구니를 가져다 놓고 하나는 사과 , 또 하나는 배...

이런 식으로 나누어서 담아놓고 팔고 있습니다.

전부 세개의 바구니를 사용해서 물건을 팔고 있군요.


그리고 마지막으로 아주 큰 상점을 가 봤습니다.

여기에선, 역시 세가지의 과일을 파는데, 사과 하나만 보더라도, 양이 엄청 많아

하나의 바구니만 가지고는 사과를 전부 담을수가 없습니다.

그래서 한 다섯개의 바구니에 사과를 넣고, 또 여섯개의 바구니에 배를 넣고..

이렇게 아주 많은 바구니가 준비되어 있습니다.


그럼 위의 예를 봐서, 각 가계마다 파는 물건의 양에 따라 바구니의 수가

각기 다릅니다. 이게 바로 모델이라는 계념 입니다.


사과는 코드를 말하고

배는 데이터

바나나는 스텍을 말한다고 하면

바구니는 바로 세그먼트라고 할 수 있습니다.

즉 영세한 가계 ( 코드, 데이터, 스텍 모두 양이 적은) 에서는

많은 바구니가 필요치 않고, 오직 하나의 바구니 ( 세그먼트 ) 만 있으면

물건을 팔 수 있습니다.


그리고 좀 더 많은 과일을 파는 가계에서는 그 종류별로 각각 하나씩의

바구니 ( 세그먼트 ) 를 준비해서 팔게 됩니다.


아주 큰 가계는 여러개의 바구니 ( 세그먼트 ) 를 필요로 하는건 당연 하지요.


만약 가계도 조그맣고, 파는 과일도 얼마 없는데 바구니를 여러개 가져다 놓고

텅텅 빈 바구니에 사과 하나 , 배 하나 .. 이런 식으로 판다면, 공간낭비만

가져올 뿐이죠...


이해가 가셨나요?

그리고 아주 영세한 가계 ( 하나의 세그먼트 안에 코드, 데이타, 스텍이

모두 들어가는 ) 가 바로 com화일로 변환이 가능 하다는 것입니다.


바구니의 갯수와 과일의 양이 잘 낭비없이 조화되게 배치하는것이 매우 중요하다는

것은 두말 할 필요도 없죠....


그럼 이제 .code 라는 것에대해 또한 보충 설명을 드리겠습니다.

이것은 프로그램의 코드가 오는 부분이라고 말씀 드렸습니다.

또한 여기에 데이타가 올 수도 있다고 했습니다.

그리고 따로 .data 라는 지시어가 있다는 말도 했구요..

그럼 과연 이 차이가 무엇인지...


'데이타가 올 수 있다' 에서 데이타라는 것은 바로 다른 프로그램에서

보는 바와 같이 '변수'를 선언하여 주는 곳이 바로 데이터 영역이 됩니다.


C 언어에서 보면


int a = 1, b = 4;

char c = 45, d = 64;


이와 간은 기능의 변수들을 선언하여 주는 것이 바로 데이터를 정의 하는 곳입니다.

물론 어셈블러에선 위와 같은 형식으로 쓰지는 않죠.

그냥 어셈블러 에서 사용하는 방법을 써 보자면,


a        dw        1

b        dw        4

c        db        45

d        db        64


이와 같이 선언을 하게 되는 것입니다.

그럼 과연 이 변수 선언을 '.code'안에서 하는 것과

'.data' 에서 하는 것은 무슨 차이가 있을까요


이것은 바로 데이터 영역을 따로 하나의 세그먼트로 분리시키느냐 마느냐

하는 차이 입니다.

'.code' 안에서 변수 선언을 하여 주면 이 변수들이 갖는 데이터는 바로

코드가 들어있는 세그먼트 안에 저장하게 됩니다.

즉 코드와 데이터가 하나의 세그먼트 안에 공존하게 된다는 것입니다.


반면 '.data' 이 안에 선언되어진 변수들은 따로 세그먼트를 하나

만들어서 그곳에 이 변수들의 데이터 값들을 저장하게 되는 것입니다.

즉, 코드가 하나의 세그먼트를 갖고, 또한 데이터 영역이 하나의 세그먼트를

갖게 되어 2개의 세그먼트를 사용하게 되는 것입니다.


스텍도 같은 형식으로 선언이 되어 집니다.


그래서 '.code'안에 모든 변수가 선언이 되면, 그 소스는 바로 하나의 세그먼트

밖에 갖지 못하는 com화일로 변환이 가능하게 되는 것입니다.


그리고 'org 100h'

이것은 com화일로 변화할 때 써 주는 것이라고 했는데요.

프로그램이 돌아가기 위해서는 프로세서에게 프로그램이 읽혀진 상태,

즉, 프로그램이 어떠한 형태로 되어 있나, 또는 그 밖에 여러가지 정보를

미리 알려 주어야 합니다. 이럴 경우 com화일일 경우에는 하나의 세그먼트 밖에

처리를 하지 못하기 때문에 그 하나의 세그먼트 안에 이러한 정보도 들어가게

되는 것이지요.

그래서 그 정보를 저장하기 위해 0000번지 부터 0100h 번지까지 이 정보를 넣

어주게 되는 것입니다.

즉, 본 프로그램은 오프셋 100h 번지부터 시작하라는 뜻이 됩니다.


반면 exe화일 같은 경우에는 여러개의 세그먼트를 사용할 수 있기 때문에,

구지 하나의 세그먼트에 이 PSP영역과 코드, 또는 데이터 영역을 함께 넣을

필요가 없게 되는 것입니다.


이제 다음 단계로 넘어가죠.

가만히 보면 프로그램 내용이 'PROTECT PROC      NEAR' 에 둘러 싸여 있는 것을

볼 수 있습니다. 이것은 C언어에서의 '{' '}' 와 같은 역할을 한다고 생각하면

됩니다. 이것을 프로시져라고 부르는데, C에서의 함수의 개념과 같은

개념이라고 생각하시면 편합니다. 마찬가지로 C에서 여러개의 함수가 사용

되는 것처럼 어셈블리어에서는 이 프로시져 라는 개념으로 여러개의 프로시져가

사용될 수 있다는 뜻입니다. 베이식에서는 서브루틴과 비슷한 개념 입니다.


C언어를 잘 모르시는 분들은 일단은 그냥

'아 어셈블리어의 프로그램 형태는 대충 이런 것이구나' 라고 넘어가세요..

나중에 여러개의 프로시져를 사용하는 예에서 확실히 설명을 드리겠습니다.

그냥 외우세요...이런걸 꼬옥 부쳐 주어야 되는거라고...헤헤~


그리고 여기서 'PROC'다음에 'NEAR'라고 쓰여 있는걸 볼 수 있습니다.

뜻대로 하자면 '가깝다' 라고 되겠지요..

그럼 과연 머가 가까운걸까요...

이건 데이터 정의 및 코드들이 모두 하나의 세그먼트 안에 있을때 씁니다.

그러니깐 세그먼트 하나를 각각 하나의 아파트 건물에 비유 하면,

하나의 세그먼트 안에서 다른 오프셋 주소로 가게 되는건 바로 하나의 건물안에서

이집에서 바로 저 집으로 간다는 것이지요.

반면 하나의 세그먼트 안에서 다른 세그먼트로 간다는건, 즉 다른 건물로 옴겨 간

다는 의미가 됩니다.

생각해 보세요. 하나의 건물안에서 와따가따 하는게 가까운지 , 아니면 다른 건물

로 와따가따 하는게 가까운지...


COM화일 같은 경우에는 모두 이 NEAR 이지만 EXE 화일일 경우에는 FAR를 사용하게

됩니다. 구지 이런걸 미리 해 주는 이유는 프로세서가 주소를 참조할 때

NEAR일 경우에는 OFFSET만 참조하면 되지만, EXE같은 경우 에서는 오프셋 이외의

SEGMENT주소값까지 참조를 해야 하기 때문에 이와 같은 것을 써 주어야만 합니다.


끝에 'PROTECT ENDP'가 있는에 이것은 프로시져가 끝났다는 걸 알려 줍니다.

'END   PROTECT'또한 프로그램이 완전히 끝났다는걸 나타내 주는 것이고요...


그럼 다음 예제 에서는 변수를 선언하여 주는 예를 보이겠습니다.

( 원래 변수라고 하는것 자체가 우습지만... )


참고로 이 예제들 안의 어셈블리 명령어는 따로 하나씩 설명을 하겠습니다.


 [12] 제목 : [초금-명령] MOV 에 대해
 올린이 : 까망벌레(정태식  )    94/11/08 04:06    읽음 : 387  관련자료 없음

MOV 명령에 관해..


이 명령어는 아주 쉽기 때문에 간략히 하겠습니다.

말 그대로 '옮기다' 라는 뜻을 갖는 명령어 입니다.

그럼 MOV 명령어의 사용 예를 주욱 나열해 보겠습니다.


         MOV       DL,41H

         MOV       AH,2

         MOV       CH,41

         MOV       DL,'B'

         MOV       AH,BL

         MOV       AL,01001011B


위의 것들은 모두 1바이트 연산문으로 되어 있습니다.


MOV      DL,41H


이것은 말 그대로 DL레지스터 ( DX 의 하위 바이트 ) 에 16진수 41을

넣으라는 명령 입니다. 그럼 DL 레지스터에는 16진수 41이 들어가게 됩니다.

뒤의 'H'는 바로 16진수 라는 것을 나타냅니다.

아무것도 안부치면 10진수가 되고, 'B'라는 것을 부치게 되면 바로 2진수라는 의미가

됩니다.


MOV      AH,2


이것은 AH 레지스터에 2를 넣으라는 명령 입니다. '2' 라는 숫자는 10진수나

16진수나 같은 값을 나타내기 때문에 구분을 하지 않아도 됩니다.


MOV      CH,41


이것은 CH레지스터에 10진수 41을 넣으라는 명령 입니다.


MOV      DL,'B'


이것은 DL 레지스터에 아스키 코드 'B'의 아스키 값을 넣어주게 됩니다.

즉 DL에 문자 'B'를 넣어주기 위해서는 꼭 'B'의 아스키 값을 찾아서 일일이

넣어줄 필요 없이 그냥 문자를 써어 넣어 주어도 된다는 것을 말합니다.


MOV      AH,BL


이것은 레지스터 AH에 레지스터 BL에 들어있는 값을 넣어 주라는 명령 입니다.

이와 같이 MOV명령은 레지스터에 숫자를 즉치대입 하는것 말고도 바로 레지스터

에 레지스터의 값을 바로 넣을수 있습니다.

( 예외로 이와 같은 것이 될 수 없는 경우도 있습니다...그것은 조금 후에.. )


MOV      AL,01001011B


이것은 AL 레지스터에 2진수 01001011이라는 값을 넣어주라는 명령 입니다.



위와 같이 MOV 명령은 좌변에 우변의 값을 넣어주게 됩니다.


다음과 같은 명령은 잘못된 것입니다.


         MOV       23H,AL

         MOV       AL,DX

         MOV       AL,F3H


위와 같은 명령은 잘몬된 것으로


MOV      23H,AL


이것은 23이라는 상수에 AL레지스터 안의 값을 넣어주라는 뜻이 되므로

틀린 것이 됩니다.


MOV      AL,DX


이것은 AL이 1바이트인데 반하여, DX는 1워드의 크기 이므로 데이터 간의 전송시

데이터 형이 다르므로 이것은 틀린 것이 됩니다.


MOV      AL,F3H


그럼 이건 왜 틀린가...

이건 AL 보다는 F3H라는 것이 잘못 되어서 틀린 것입니다.

F3이라는 16진수를 AL에 넣으라는 것인데, 여기서 5EH 나 32H 같은 것은 되는데

F3H 라는 것은 어셈블러가 이것이 과연 변수를 말하는 것인지..아님 16진수를

말하는 것인지 알 수 없게 됩니다.

그래서 16진수 중 앞에 영문자가 먼저 오는 경우는 항상 '0'을 먼저 써 주어야

됩니다.


즉, 'F3H' 이것을 '0F3H'이렇게 써 주어야만 하는 것입니다.



또 하나 레지스터간의 데이터 전송에 있어서 불가능한 것이 있습니다.


MOV      DS,CS


이와 같은 경우가 있습니다.

DS, CS 모두 세그먼트 주소를를 나타내는 레지스터 입니다.

이런것은 바로 레지스터간의 데이터 전송이 되지 않습니다.

위와 같은 결과를 원할 경우에는


MOV      AX,CS

MOV      DS,AX


위와 같이 두 줄로 늘려서 중간 매개체인 AX레지스터를 사용하여 전송을 하여

주어야만 합니다.

꼬옥 AX일 필요는 없습니다. BX,CX,DX 모두 가능 합니다.



그리고 MOV 명령중엔 주소 지정 방식이라는 것이 있는데...


MOV      AX,[0432]


이와 같은 형태가 있습니다. 이런것을 주소 지정 방식이라고 하는데,

이것에 대해서는 다음 프로그램 소스를 하나 본 후 따로 설명을 드리겠습니다.




 [15] 제목 : [초급-실습] 프로그램 세엣 #1
 올린이 : 까망벌레(정태식  )    94/11/09 05:58    읽음 : 375  관련자료 없음


이번엔 조금 긴 프로그램을 하나 해 보도록 하겠습니다.



    .model tiny

    .code

    org 100h


start:  jmp st1


info    dw  ?

yes db  'exist',13,10,'$'

no  db  'not exist',13
  ,'$'

flo_msg db  'Floppy is $'

co_msg  db  'Coprocessor is $'

mod_msg db  'Video Mode is $'

m1_msg  db  '40X25 Color Mode',13,10,'$'

m2_msg  db  '80X25 Color Mode',13,10,'$'

m3_msg  db  '80X25 Mono Mode',13,10,'$'


st1 proc    near

    int 11h

    mov info,ax

    mov ax,cs

    mov es,ax


    call    floppy

    call    copro

    call    mode

    int 20h

st1 endp



floppy  proc    near

    lea dx,flo_msg

    call    prt

    mov bx,info

    and bx,0000000000000001b

    cmp bx,00

    je  nofloppy

    lea dx,yes

    call    prt

    ret

nofloppy:      

    lea dx,no

    call    prt

    ret

floppy  endp



copro   proc    near

    mov dx,offset co_msg

    call    prt

    mov bx,info

    and bx,0000000000000010b

    cmp bx,00

    je  nocopro

    mov dx,offset yes

    call    prt

    ret

nocopro:

    mov dx,offset no

    call    prt

    ret

copro   endp



mode    proc    near

    lea dx,mod_msg

    call    prt

    mov bx,info

    and bx,0000000000110000b

    cmp bx,0000000000010000b

    je  mod1

    cmp bx,0000000000100000b

    je  mod2

    cmp bx,0000000000110000b

    je  mod3

mod1:

    lea dx,m1_msg

    call    prt

    ret

mod2:

    lea dx,m2_msg

    call    prt

    ret

mod3:

    lea dx,m3_msg

    call    prt

    ret

mode   endp



prt proc    near

    xor ax,ax

    mov ah,09h

    int 21h

    ret

prt endp

    end start




좀 길죠?

이것은 지금 PC의 상태를 표시해 주는 것으로


Floppy 의 유무

Coprocessor 의 유무

Video Mode 의 상태


이 세가지를 채크하여 상태를 나타내어 주는 프로그램 입니다.

( 후...이거 짜느라고 한참 해맸네요. )

여기서는 변수의 정의, 간접주소의 지정 방식, 여러개의 프로시져 호출,

그리고 인터럽트에 대해 배워볼까 합니다.

그리고 프로시져 호출시의 스텍 변화 같은 것도 다루고요...

그럼 이걸 에디터로 짠 후에 info1.asm 으로 저장 하세요

그리고


masm info1.asm


link info1.obj


exe2bin info1.exe info1.com


이렇게 하고서 info1.com을 실행해 보시면 세가지의 정보를 얻을 수 있습니다.


그럼 이 프로그램에 대한 설명은 다음에 하죠..


참고 : 사실 프로시져 호출 후에 push 로 레지스터를 보존해야 하는데,

       짧은 프로그램이기 때문에 그런 과정은 생략 했습니다.




 [16] 제목 : [초급-보충] 변수 선언에 대하여
 올린이 : 까망벌레(정태식  )    94/11/10 12:50    읽음 : 401  관련자료 없음


여러분의 성원(?)에 기픈 감사를 드리며...흐흐..

훌쩍훌쩍~~

감기 조심들 하세요...

( 푸르르~~~ 팽~ )


그럼 우선 변수의 정의에 대해 설명을 드리겠습니다.


변수 정의란 바로


info    dw      ?

yes     db      'exist',13,10,'$'

no      db      'not exist',13,10,'$'

flo_msg db      'Floppy is $'

co_msg  db      'Coprocessor is $'

mod_msg db      'Video Mode is $'

m1_msg  db      '40X25 Color Mode',13,10,'$'

m2_msg  db      '80X25 Color Mode',13,10,'$'

m3_msg  db      '80X25 Mono Mode',13,10,'$'


이 부분을 말하는 것으로,

일단은


yes    db    'exist',13,10,'$'


와 같은 경우 앞의 'yes'가 바로 변수의 이름을 말하는 것입니다.

즉 사람마다 이름이 있는 것과 같이 각 변수에도 각각의 이름이 있어야 하는

것이지요.

( 혹시 변수가 뭔지 모르시는 분은? 흐흐.. 각성을... )


그리고 'db'같은건 변수의 형태를 결정하여 주는 것입니다.

즉 db 란 'define byte'의 약자로 'yes'라는 변수의 형태는 바로 바이트 형이라는

것을 알려 줍니다.


그리고 마지막의 'exist' 하는 마지막 까지의 부분은 바로 변수의 내용을

설정하여 주는 것입니다.


그럼 하나씩...

우선은 변수의 형태를 결정하여 주는 것에 대해 어떻한 종류가 있는지 주욱

나열해 보도록 하지요.


DB      :      DEFINE BYTE      ( 1 BYTE )

DW      :      DEFINE WORD      ( 2BYTE = 1WORD )

DD      :      DOUBLE WORD      ( 4BYTE = 2WORD )

DF      :      DEFINE FARWORD   ( 6BYTE = 3WORD )   < 80386/ 80486 >

DQ      :      DEFINE QUADWORD  ( 8BYTE = 4WORD )

DT      :      DEFINE TENBYTES  ( 10BYTE = 5WORD )


다음과 같은 종류가 있습니다.


일단은 DB에 대한것 부터


               FLD1DB    DB    ?

               FLD2DB    DB    32

               FLD3DB    DB    20H

               FLD4DB    DB    01011001B

               FLD5DB    DB    10 DUP(0)

               FLD6DB    DB    'Personal Computer'

               FLD7DB    DB    '32654'

               FLD8DB    DB    01,'Jan',02,'Feb',03,'Mar'


( 발췌 : IBM PC ASSEMBLY LANGUAGE AND PROGRAMMING ( PETER ABEL ) )


위와 같은 여러가지의 형식으로 DB라는 형식의 변수를 정의하여 줄 수 있습니다.

그럼 첫번째 변수 'FLD1DB'에 대해서 말씀 드리자면,

뒤에 '?' 라는 것이 바로 변수의 값이라고 정의 되어 있습니다.

이것은 이 변수에 '?'가 들어가는 것이 결코 아닙니다.

이것은 '?' 라는 속 뜻 그대로 '모르겠다' 라는 의미의 '?' 가 됩니다.

즉, 'FLD1DB'라는 변수는 형태는 분명 1바이트 형인데, 그곳에다가 무얼

넣을지는 아직은 모르는 상태고, 그냥 'FLD1DB'라는 이름으로 메모리에 1바이트의

변수를 위한 공간을 잡아 달라는 얘기 입니다.

나중에 이 변수에 따로 어떻한 값이 들어갈지 지금 상태는 모르기 때문에

이와 같이 정의를 하여 주는 것입니다.


두번째 'FLD2DB'로 넘어가죠.

이것은 '32'라는 것을 변수에 넣으라는 뜻입니다.

즉, 십진수 32라는 값을 'FLD2DB' 라는 변수에 넣으라는 뜻이 됩니다.

간단하죠?


세번째 것은 'FLD3DB'인데,

이것은 'FLD2DB'와 별다른 것은 없습니다.

'20'뒤에 'H'를 붙여 줌으로서 'FLD3DB'라는 이름의 변수에 16진수 20을

넣으라는 것이 됩니다.


네번째 'FLD4DB' 도 역시 간단히

이 변수 안에 2진수 01011001 이라는 값을 넣으라는 뜻이 됩니다.


그리고 다섯번째 'FLD5DB' 이것은 좀 눈 여겨 볼 필요가 있습니다.

자세히 보면 'DUP'라는 것이 있습니다.

이것은 변수에 넣을 데이터가 아닌 변수에 어떠한 조치를 취해 주는 명령어의

일종입니다.

'10 DUP(0)' 이라고 되어 있는데요, 여기서 '10'이란것은 'FLD5DB'라는 변수의

이름으로 메모리 공간을 1바이트로 잡아 주는데 있어서 1바이트 공간을

10개 잡아 달라는 명령 입니다.

즉, 이것은 배열과 같은 기능을 하는 것입니다.

그리고 괄호 안의 '0'은 열개를 잡는데 있어서 그 10바이트의 내용을 모두

'0'으로 채워
 牝遮 ( 초기화를 하라는 ) 뜻이 됩니다.

만약 '10 DUP(43)' 이라고 한다면 10바이트의 메모리를 잡을때 10바이트 모두

각각 그 안의 내용이 '43'으로 하여 달라는 뜻이 됩니다.

이번것은 특별히 메모리 그림을 그려 드리겠습니다.


;---------------------------------------------------------------

; 00 ; 00 ; 00 ; 00 ; 00 ; 00 ; 00 ; 00 ; 00 ; 00 ;

;---------------------------------------------------------------

10 DUP (0) 의 경우


;--------------------------------------------------------------

; 43 ; 43 ; 43 ; 43 ; 43 ; 43 ; 43 ; 43 ; 43 ; 43 ;

;---------------------------------------------------------------

10 DUP (43) 의 경우


이와 같은 결과를 얻게 됩니다.


그리고 이렇게 저장 되어진 내용을 꺼내어 사용하는 법에 대해서는

메모리 주소 지정 방식을 설명할 때 자세히 설명을 드리도록 하겠습니다.



다음으로 'FLD6DB' 라는 명의 변수가 있습니다.

여기서는 변수에 대입 하여 주는 값이 상수가 아닌 문자열이 됩니다.

근데 여기서 위의 문자열을 보면 1바이트 크기를 훨씬 넘는 크기로

모두 17바이트의 크기를 갖는 문자열 입니다.

그럼 과연 하나의 바이트만 사용하도록 정의 되는 'DB'에서 이 17바이트나

되는 문자열을 집어넣으라는 말은 무슨뜻?

역시 이것은 위에서 설명을 드린 'DUP'의 개념을 생각하시면 됩니다.

즉 1 바이트씩 메모리에 저장을 하여 주는 것입니다.

모두 17바이트의 문자 이므로 메모리 상에도 마찬가지로 17바이트의 연속된

공간을 할당하고 여기에 각각 한 바이트(한 문자) 씩 넣어 주게 됩니다.

메모리 그림을 다시 그려 보죠.


;---------------------------------------------------------------------

; P ; e ; r ; s ; o ; n ; a ; l ;   ; C ; o ; m ; p ; u ; t ; e ; r ;

;----------------------------------------------------------------------


위와 같은 메모리 구조로 저장이 되어 집니다.

사실 엄격히 따지자면 메모리에 위와 같은 문자가 직접 들어가는 것은 아니구요

이 문자들을 모두 아스키 코드 값으로 변환을 하여 저장을 하게 됩니다.


;---------------------------------------------------------------------

; 50 ; 65 ; 72 ; 73 ; 6F ; 6E ; 61 ; 6C ; 20 ; 43 ; ....등등...

;---------------------------------------------------------------------

( 이 값들은 모두 16진수 입니다. )

이와 같이 메모리에 저장을 하게 됩니다.

역시 이 변수를 사용하는 법에 대해서는 메모리 주소지정 방식에 대해 설명을

드릴때 자세히 설명을 드리겠습니다.


다음으로 'FLD7DB' 가 있는데...

들어갈 데이터 값이 '32654'가 됩니다.

이것은 혹 착각을 하실 수도 있습니다만, 이 것은 32654라는 값을 나타내는

것이 아니고 문자 32654를 나타내는 것입니다.

즉 아스키 코드값이 메모리에 들어가게 되는 것이지요.

'FLD6DB'와 같이 하나씩 문자로 처리하여 메모리에 들어가게 되는 것입니다.


마지막으로 'FLD8DB'가 있습니다.

이것은 대입할 데이터가 마구마구 섞여 있습니다.

문자열과 숫자가 막 섞여 있는 예가 됩니다.

간단히 그림을 그려 보면 쉽습니다.


;---------------------------------------------------------

; 01 ; J ; a ; n ; 02 ; F ; e ; b ; 03 ; M ; a ; r ;

;---------------------------------------------------------

위와 같이 들어가는데, 이 역시 문자는 아스키 코드로 들어가게 되겠죠?


;----------------------------------------------------------------------

; 01 ; 4A ; 61 ; 6E ; 02 ; 46 ; 65 ; 62 ; 03 ; 4D ; 61 ; 72 ;

;----------------------------------------------------------------------


이와 같이 들어가게 됩니다.



이제 다음 단계로 'DW'라는 것에 대해서 알아 보겠습니다.

간단한 예를 보이죠.



          FLD1DW    DW    0FFF0H

          FLD2DW    DW    01011001B

          FLD3DW    DW    3,4,7,8,9

          FLD4DW    DW    5 DUP (0)

         

          FLD1DB    DB    '32654'

          FLD5DW    DW    FLD1DB


자...위와 같은 예를 들었습니다.

'DW'는 'DEFINE WORD' 라고 했다시피 2바이트, 즉 1워드의 메모리 공간을 할당하여

줍니다.


그럼 또 순서대로...


'FLD1DW' 이 변수는 메모리에 2바아트 크기의 메모리를 잡아주게 됩니다.

그리고 여기에 16진수 'FFF0' 이라는 2바이트 크기의 값이 들어가게 됩니다.

그림을 그려 보자면,


;------------------------

; 0F ; FF ;

;------------------------

이와 같이 들어가게 되는 것이지요.

( 여기서 또한 1바이트씩 내용이 앞뒤가 바뀌어 들어가는데 그 이유는 마지막에

  간단히 설명을 드기겠습니다. )




( 앞으로 메모리 그림을 직접 그리기 보다는 그냥 나열 식으로 표현을 하겠

  습니다.

  예를 들면 위와 같은 경우는


      FFF0


  이렇게 쓰고 'DB'에서 정의 했던 'Personal Computer' 같은 경우는 그냥


  50 65 72 73 6F 6E 61 6C 20 43 6F 6D 70 75 74 65 72


  와 같이 쓰겠습니다. 여기서는 1바이트씩 앞뒤가 바뀌는 것은 나타내지 않고

  그냥 보기 편한대로 나열하겠습니다.

  왜? ... 피곤하니깐! )


그럼 사실 이 변수를 정의할때 'FLD1DB    DB    0FFH,F0H' 와 같이 해주어도

메모리에 들어가는 형태는 똑같습니다.

하지만 여기서 중요한것 하나.

만약 위에 예에서 'FLD1DB'를 불러오게 되면은 사실 'FF'라는 1 바이트 만을

가져오게 됩니다. 즉 처리가 1바이트 크기로 된다는 것입니다.

하지만 'FLD1DW'같은 경우, 불러올때는 2바이트 크기인 'FFF0'을 한꺼번에

불러와 처리를 하게 되는 것입니다.


그럼 다음으로 'FLD2DW'를 보죠

이것또한 간단히 01011001 이라는 2진수 값을 2바이트 크기의 메모리에 넣으라는

것이 됩니다.


    0059


이와 같이 저장이 되는 것입니다.


'FLD3DW'를 보면,

각 값들을 ','(콤마) 로 구분을 하였습니다.

모두 5개의 다른 데이터 값이 주어 졌습니다.

이럴 경우에는 모두 5개의 워드를 메모리 공간으로 잡아 줍니다.

그리고 각각의 하나의 워드안에 콤마로 구분된 각각의 값들을 넣어 주게 됩니다.


    0003 0004 0007 0008 0009


위와 같은 형태로 저장이 되어 지는 것입니다.


이제 마지막으로 'FLD4DW'를 보도록 하겠습니다.

이 변수를 선언하기 전에 바로 'FLD1DB'라는 변수가 선언 되어 있고

'FLD4DW'에 넣는 변수값이 바로 'FLD1DB'라는 변수를 그대로 넣어주도록

되어 있습니다.

이런 경우는 생각과는 달리 'FLD1DB'의 내용이 들어가는 것이 아니고

바로 'FLD1DB'로 정의되었을때 잡혀지는 메모리의 주소값이 'FLD4DW'에 들어가도록

되어 있습니다.


다시 말해 만약 'FLD1DB'라고 변수를 선언 하여 주었을때, 그 할당되어진 메모리

주소가 '0103'이라고 한다면 'FLD4DW'에는 그 메모리의 주소값인

'0103'이 들어가게 된다는 뜻입니다.




다음으로 'DD', 'DF' , 'DQ', 'DT' 들이 있는데, 이것은 간단히 메모리에 들어가는

형태만을 설명드리고 필요에 따라 간단한 설명을 조금씩만 해 드리겠습니다.

방식은 모두 DB,DW 와 별 다를게 없기 때문입니다.



         FLD1DD    DD        ?

         FLD2DD    DD        32572

         FLD3DD    DD        14,49

         FLD4DD    DD        'PC'

        

         FLD1DB    DB        34

         FLD2DB    DB        65

         FLD5DD    DD        FLD2DB - FLD1DB



1. 'FLD1DD'


  00000000


2. 'FLD2DD'


  00007F3C

 

  32572의 십진수 값이 16진수로 변환되어 메모리에 들어갑니다.


3. 'FLD3DD'

 

  0000000E  00000031


  각 10진수 14 와 49 를 16진수로 변환하여 따로 더블워드에 넣게 됩니다.


4. 'FLD4DD'


  00005043

 

  아스키 문자 'PC'를 각각의 아스키 값으로 변환 하여 더블 워드 하나에 넣습니다.


  'P' = 50

  'C' = 43


5. 'FLD5DD'


  00000001


  이것은 위의 'DW'에서 비슷한 걸 본 적이 있지요?

  마찬가지로 'FLD1DB','FLD2DB'라는 두 개의 바이트형 변수가 'FLD5DD'의 선언

  전에 선언되어 져 있습니다.

  그리고 이 변수값은 1이 저장 되어 있습니다.

  왜 '1'일까요?

  변수에 넣어주는 값은 분명히 'FLD2DB - FLD1DB'라고 되어 있는데...

  65 - 34 의 값인 21이 들어가야 정상이 아닐까요?

  역시 위의 'FLD5DD'에 들어 가는 값은 메모리의 주소값이 되어 지는 것입니다.

  즉 'FLD2DB'와 'FLD1DB'라고 변수가 선언되어질 때의 메모리 주소값들의 그

  차이를 넣어주게 됩니다.

 

  위에서 보는 바와 같이 'FLD1DB'와 'FLD2DB'라는 두 개의 변수선언부는 바로

  연속적으로 되어 있습니다.

  그렇기 때문에 그 메모리 주소의 차이가 1이 되는 것입니다.

  이와 같은 경우의 두가지 예를 더 보여 드리겠습니다.


         FLD1DB    DB     32

         FLD1DD    DD     'PC'

         FLD2DB    DB     53

         FLD2DD    FLD2DB - FLD1DB


  이런 경우 과연 FLD2DD에는 무엇이 들어갈까요?

  답은 5 입니다.


  00000005


  이렇게요..

  즉 'FLD1DB' 와  'FLD2DB'사이에 더블워드형의 변수가 하나 더 선정이 되어

  있습니다. 그래서 'FLD2DB'까지의 변수 선언으로 되어지는 메모리 형태를 보면,


  20 00005043 35


  이런 형태가 됩니다.

  만약 'FLD1DB'라고 주어진 변수의 메모리 주소값이 105라고 가정한다면,

  그 다음에 선언되어진 더블워드 형식의 변수'FLD1DD'의 주소값은 106부터

  시작합니다.  그리고 그 다음에 오는 1바이트 형의 변수 'FLD2DB'가 시작하는

  변수의 주소값은 10A 가 되어집니다 ..

  그래서 결과 적으로는


  10A - 105


  가 되어 5가 들어가게 되는 것입니다.


  다른 예를 하나 더 보여드리겠습니다.


           FLD1DW    DW    'PC'

           FLD2DW    DW    45

           FLD1DD    DD    FLD2DW - FLD1DW


  그럼 위와 같은 경우는 'FLD1DD'에 어떠한 값이 들어가겠습니까?

 

  00000001 이라고요?

  '땡~~' 입니다.

  역시 위의 예에선 'FLD1DW'와 'FLD2DW'의 변수가 연속적으로 선언되어 있긴

  합니다. 하지만 아까와의 차이는 바로 바이트 형이 아닌 워드 형의 변수가

  선언 되어졌다는 것입니다.

  이걸 메모리에 들어가는 구조로 나타내 보죠


  5043 002D


  이렇게 들어 갑니다.

  이런 경우 'FLD1DW'가 잡혀있는 메모리의 주소값이 100 이라고 가정 한다면,

  'FLD2DW'가 시작하는 주소값은 102가 되어 집니다. 왜냐하면 'FLD1DW'의 크기가

  1 바이트가 아닌 2바이트의 크기를 차지하기 때문 입니다.

  결과적으로 'FLD1DD'에는


  00000002


  입니다.



 

DF 라는 형의 변수는 아마도 80386/80486 이상에서만 돌아가는 변수형인것

같습니다. 형식은 위와 같지만 아무래도 시스템의 제약이 따르기 때문에,

가능하면 사용하지 않는편이 좋을듯 싶습니다.



이제 마지막으로 DQ,DT 를 한꺼번에 몰아서 설명을...



0000000000000000        FLD1DQ    DQ    ?

474D000000000000        FLD2DQ    DQ    0474DH

3CF7000000000000        FLD3DQ    DQ    32572



00000000000000000000    FLD1DT    DT    ?

56341200000000000000    FLD2DT    DT    123456

43500000000000000000    FLD3DT    DT    'PC'



위와 같습니다..

왼쪽은 실질적으로 메모리에 들어가는 형태를 쓴 것입니다.


하지만...하나! 중요한 차이점.

이것은 바로 메모리에 들어가는 형태 입니다.


         FLD1DD    DD    'PC'

         FLD1DQ    DQ    'PC'

         FLD1DT    DT    'PC'


다음과 같이 각각 'PC'라는 것에 대해서 정의를 해 주었다고 해 봅시다.

그럼 각각 메모리의 저장 상태를 보면,


         00005043

         5043000000000000

         43500000000000000000


이와 같습니다.

다른점은?

물론 각 각의 크기가 다르다는 것은 당연한 사실이고요,

여기에 또 한가지 다른점은,

'DD'의 선언 같은경우는 형태가 '0000'다음에 '5043'이 들어갔다는 점입니다.

하지만 'DQ'는 5043 다음에 '0000' 이 왔다는 것이지요.

즉 'DD'는 메모리의 하위 바이트 부분에 실제의 값이 써 지지만, 'DQ'나 'DT'같은

것들은 메모리의 상위 바이트부터 채워 진다는 것입니다.

또한 'DQ'와 'DT'의 차이점은,

'DQ' 나 'DD'와 같은 것은 '5043'인 점에 반해

'DT'는 '4350' 과 같이 1 바이트씩 앞뒤가 서로 바뀌어 저장 된다는 점입니다.


   참고 : 전에 메모리의 저장형태에 대해 'DEBUG'를 사용할때 본 적이 있습니다.

          여기서는 가령 ' MOV CX,0123'인 경우 '2301'과 같이 들어가는 것을

          본 적이 있습니다. 이것은 메모리가 아래로 갈 수록 그 주소값이 증가

          하는 형태이기 때문에 실제 넣는 데이터의 앞 부분 '01'은 상위부분의

          메모리로, 뒷 부분'23'은 메모리의 하위부분에 들어가기 때문에 그런

          현상이 발생하게 되는 것입니다.


           ;------------------;

    FFFF   ;                  ;  상위 부분

           ;                  ;

           ;------------------;

           ;        01        ;

           ;------------------;

           ;        23        ;

           ;------------------;

           ;                  ;

    0000   ;   
             ;  하위 부분

           ;------------------;

             하나의 세그먼트


 

 다음에는 주소 지정 방식에 대해 설명을 해 보겠습니다.


                    


 [17] 제목 : [초급-보충] 주소지정방식에 대하여...
 올린이 : 까망벌레(정태식  )    94/11/19 11:07    읽음 : 358  관련자료 없음

후... 넘 늦게 올려서 죄송 합니다.

헐~

사정이 좀 있어가지구여

그럼 이번엔 계속 해서

주소 지정 방식에 대해 배워볼까 합니다.

참...

어떤분이 오타가 좀 있다구....흐흐~

사실 일일이 오타 찾아내기가....읔~~~

제가 넘 게을러서 말이죠...솔직히 그런거 찾아내기가 영 구찮네요...

흐흐...그래도 치명적인 오타는 없도록 할테니깐요..

구냥 세겨 들으세요....헤헤~~~




번지 지정 방식에는 일단은 두 가지가 있다고 봅니다.

그것은 직접 주소 지정 방식과, 간접 주소 지정 방식이라고 불리우고 있습니다.

음...하지만, 직접 주소 지정 방식은 별로 중요한 것은 없습니다.

그 이유는 별로 쓰이지도 않고 비 효율적이기 때문 이죠.

그래도 일단은 직접 주소 지정 방식에 대해 간단히 설명을 해 드리겠습니다.


그럼 과연 주소 지정 방식이란게 무엇인가?

헐~

그럼 그에 해당하는 예를 보이겠습니다.

여러분, 변수 선언을 보셨지요?

그럼 과연 그 변수 선언에서 그 변수들을 직접 가지고 와서 사용할땐

프로세서는 과연 어떻게 처리를 할까요?


         FLD1    DB    34


이와 같은 변수를 선언을 했다고 가정을 합시다.

그럼 메모리에는 이 변수를 위한 하나의 공간을 잡아 줍니다.

즉 메모리에 FLD1이라는 이름으로 1바이트 크기의 방을 잡고서, 거기에다 데이터

값인 '34'라는 값을 16진수로 변환 하여 22라는 값으로 넣게 됩니다.

그럼 이 34라는 숫자가 들어가 있는 곳도 메모리 이기 때문에 역시나 그 메모리의

고유의 주소값이 있는것은 당연 합니다.

( 모든 메모리는 각각의 고유의 주소값을 가지고 있기 때문이죠. )

그럼 그 메모리로 저장된 값이 다음과 같다고 가정을 해 봅시다.


         SEGMENT : 03A2

         OFFSET  : 0106


이와 같다고 가정을 하면,

 ( 사실 프로그램 소스가 실행 화일로 변환이 되어서 메모리로 로드될 때는 항상

   이 주소값이 변합니다. 즉, 실행할 때 마다 그 시스템의 상태에 따라 로드되는

   메모리의 값이 항상 일정하지는 않다는 것입니다. )

그림으로 나타내 보면


;---------------------------------------------------

; ?? ; ?? ; ?? ; 22 ; ?? ; ?? ; ?? ;

;---------------------------------------------------

 0103 0104 0105 0106 0107 0108 0109


여기서 '??' 란 어떠한 다른 ( 우리가 알 수 없는, 미리 들어 있던 쓰레기 값들 )

수가 들어 있다는 뜻이고, 우리가 원하는 값은 바로 106 번지의 22 입니다.


이와 같이 정의 되어진 변수를 사용하는 하나의 예를 잠깐 봅시다.


       MOV      AX, FLD1


이와 같은 명령어가 프로그램 내에 있다고 가정을 해 보면,

이 명령어가 들어있는 소스를 실행 가능한 화일로 만들고서 이를 메모리에 로드하게

되면 다음과 같이 변환이 됩니다.


       MOV      AX, [106]


위와 같이 변환이 됩니다.

즉,원래의 변수명은 사실 프로세서에게는 아무 의미도 없는 쓰레기에 불과 합니다.

전에 말씀 드렸던, 모든 명령어는 그에 해당하는 숫자로 바뀌어서 프로세서에게

인식을 시켜 준다고 말씀을 드렸습니다. 그래서 여기에서도 FLD1 이라는 영문자가

아닌 프로세서가 직접 인식을 할 수 있는 언어인 기계어 ( 숫자 ) 로 변환을 시켜

주게 됩니다.

그래서 위와 같은 경우는 바로 FLD1의 내용인 34라는 값이 메모리 주소인 106번지에

들어 있기 때문에 직접 이를 가져오도록 하여 주는 것입니다.

음...( 흐..넘 어렵당 )


'[]'로 둘러 싸여 있는 숫자인 '106'은 메모리의 주소값을 나타내는 것으로 '[]'

로 인해 프로세서는 이것이 바로 '106번지에 있는 데이터 값을 가져오라!' 라는

것으로 인식을 하게 됩니다.

즉, 프로세서는 변수명을 인식하지 못하기 때문에 어떠한 변수가 선언되어 지면,

그 변수이름으로 잡혀져 있는 메모리의 주소값을 프로세서에게 직접 알려 주어

그 메모리에 있는 데이터 내용을 인식하고, 가져오고, 여러가지 작업을 하게 되는

것입니다.


만약 프로그램 소스에서


      MOV      AX,FLD1


대신에


      MOV      AX,[106]


이라고 한다면 같은 결과가 나올꺼라고 한다면?

땡~~~

후자와 같은 경우를 보통 직접 주소 지정 방식이라고 하는데, 여기서는 위와같은

방식으로 프로그래밍을 하여 직접 실행을 하여 보면, 간혹 운 좋게 원래의 원하는

값인 34가 들어갈 수도 있지만, 대부분은 아마도 틀린 값이 들어가게 될겁니다.

이유는 간단.

앞서 말씀 드렸듯이, 프로그램이 메모리로 실행을 위해서 로드되면 그 로드되는

메모리의 위치가 항상 같지가 않다는 것입니다.

프로그램이 로드될때는 프로세서가 알아서 그 프로그램을 로드시키기에 알맞은

빈 메모리를 임의적으로 결정하기 때문에 그 위치는 수시로 변하게 됩니다.

그래서 위와 같은 표현은 결코 원하는 값을 기대하기는 어렵습니다.

그래서 이 보다는 간접주소 지정방식이 주로 사용됩니다.


하지만 직접 주소지정방식이 전혀 쓰이지 않는 것은 아닙니다.

그것은 가령 메모리의 어떻한 부분은 항상 MS-DOS를 사용하는 PC에서는 거의 같은

메모리 주소에 같은 정보가 들어가는 것도 있습니다.

하드 디스크의 크기라든가, 메모리의 크기, 날짜. 등등 이와 같은 것들은 시스템이

부팅이 될 때 메모리로 읽혀 들어가는 부분이 항상 같습니다.

그렇기 때문에 만약, 위와 같은 데이터들을 참조하기를 원할때는 직접 메모리 주소를

써서 이 들을 참조 할 필요가 있습니다.


그럼 이제 간접 주소 지정 방식에 대해 자세히 들어가 보도록 하겠습니다.


         FLD1      DB      34

         FLD2      DB      'R'

         FLD3      DB      'COMPUTER'


다음과 같이 변수를 선언 하였다고 해 봅시다.

그럼 메모리에는 다음과 같이 들어가겠죠?


      22 52 43 4F 4E 50 55 54 45 52


위와 같은 순서로 메모리에 들어가게 됩니다.

여기서 메모리의 주소값은 아직 정해지지 않은 상태 입니다.

주소들은 실행을 위해 메모리에 로드될때 정해 지게 되는 것입니다.

그럼 일단은


         MOV      AL,FLD1


과 같이 정의를 하였다고 해 봅시다.

이것은 아시다 시피 FLD1이라는 변수의 값인 34라는 값이 AL레지스터에 들어가게

됩니다.


         MOV      AL,FLD2


이 것도 마찬가지로 문자 'R'의 아스키 코드 값인 52라는 16진수가 AL 레지스터에

들어가게 됩니다.


         MOV      AL,FLD3


과연 위와 같은 것은 레지스터 AL에 어떻한 것이 들어갈까요?

답은 'C'라는 문자의 아스키 코드 값인 '43'이라는 16진수 숫자만이 들어가게 됩니다.

그 이유는 FLD3라는 변수는 1바이트 형 변수이기 때문에 'COMPUTER'이라는 8바이트의

내용을 모두 가져 오지는 못합니다.

그럼 여기서 여러분은 뭣하러 FLD3 에 'C'하나만 넣지 왜 쓸데없이 'COMPUTER'이라고

할 필요가 있느냐?

라고 질문 하신다면.

위에서 메모리에 들어가 있는 형태를 보면 알 수 있듯이 'COMPUTER'은 결코 따로

각각 한 문자 ( 1 바이트 ) 씩 떨어져 들어가는 법이 없이 모두 연속적으로 메모리에

들어가게 됩니다. 그래서 그 다음 문자인 'O'를 가져오고 싶다면 FLD3로 정의 되어진

변수의 그 다음 메모리 번지를 검색하면 'O'라는 문자를 가져올 수 있게 됩니다.


      FLD3      : 'C'

      FLD3 + 1  : 'O'

      FLD3 + 2  : 'M'

      FLD3 + 3  : 'P'

      .....


과 같이 사용한다면, 모든 문자를 참조 할 수 있겠죠.


      MOV      AL, FLD3 + 2


위와 같이 명령을 한다면, FLD3가 지정하고 있는 메모리의 데이터 내용인 'C'다음의

세번째 문자 'P'를 AL 레지스터에 넣게 되는 것입니다.

하지만 위와 같이 하면 'COMPUTER'의 전체 내용을 각각이 가져오는데 불편한 점이

있습니다.

그래서 FLD3라는 변수로 지정되어진 메모리의 주소값을 직접 가지고 와서 조작할 수

있는 기능이 있습니다.


      MOV      BX, OFFSET FLD3


라고 하면 BX레지스터에 FLD3로 정의 되어진 메모리의 주소값이 들어가게 됩니다.

그래서 이 BX레지스터의 값을 직접 변화 시켜가며 훨씬 빠르고 손쉽게 그 문자열을

조작 할 수 있게 됩니다.

 여기서 하나, BX 대신에 BL로 하면 안되는가....

안됩니다.

그 이유는 메모리의 주소값 ( 여기서는 오프셋 ) 이 1 바이트값이 아닌 항상 1워드

값을 가지는 이유 때문에 FLD3가 1바이트 형 변수이기는 하지만 그 변수의 데이터

값을 가져오는 것이 아닌 바로 그 변수의 ( 메모리의 ) OFFSET 값을 가져오기 때문에

항상 1워드 크기의 레지스터에 넣어야만 합니다.


위에서의 'OFFSET'이라는 의사 명령이 바로 위와 같은 역할을 하는 명령이 되는 것이지요.

이것이 좀 길다 하면


      LEA      BX,FLD3


라고 해도 결과는 똑같습니다.

가능하다면 'LEA'명령을 사용하는 것이 보기에 좋기 때문에 'LEA'명령을 사용하는 것이

바람직 하다고 할 수 있겠지요?


      LEA      BX,FLD3

      MOV      AL,[BX]


이와 같이 하면 우선은 BX 레지스터에는 FLD3의 주소값이 들어가게 됩니다.

그 다음 AL 레지스터에는 바로 BX레지스터가 지정하는 메모리의 주소에 들어있는

데이터 내용, 즉 'C'라는 문자가 들어가게 되는 것입니다.


      LEA      BX,FLD3

      INC      BX

      MOV      AL,[BX]


위와 같은 경우에는 역시 BX레지스터에 FLD3의 주소값이 들어가고

그 다음의 명령인 'INC'에 의해 BX레지스터의 값이 하나 증가하게 됩니다.

( INC 명령은 레지스터의 값에 1을 더하는 효과를 봅니다.

  만약 BX레지스터의 값이 0231 이라고 한다면 ' INC    BX' 에 의해 BX레지스터의

  값은 1이 증가한 0232가 되는 것입니다. )

그 다음에는 AL레지스터에 FLD3가 지정하고 있는 문자인 'C' 다음의 문자 'O'가

들어가게 됩니다.

이런 식으로 변수를 조작 하게 됩니다.


         FLD1      DB      34

         FLD2      DB      'R'

         FLD3      DB      'COMPUTER'


그럼 위와 같이 지정 되어진 프로그램에서 다음과 같은 경우를 생각해 보죠.


         LEA      BX,FLD1

         INC      BX

         MOV      AL,[BX]


위와 같이 명령을 한다면 과연 AX레지스터에는 어떻한 값이 들어갈까요.

FLD1이라는 변수는 오직 하나의 내용인 '34'라는 값을 지정하고 있습니다.

거기서 34라는 값이 저장되어진 메모리의 번지값 다음의 주소값에 있는 내용을

AL 레지스터에 넣으라고 되어 있는데요.

모순이죠?

하지만 프로그램상으로는 어떻한 에러 메시지도 출력하지 않고 잘 돌아 갑니다.

위와 같이 변수를 선언하여 주면 메모리에는 다음과 같이 들어가죠.


      22 52 43 4F 4E 50 55 54 45 52


위에서 FLD1이 저장되어진 곳이 첫번째인 '22'라는 값입니다.

( 34를 16진수로 바꾸면 22 가 되죠 )

여기서 FLD1이 가리키는 메모리의 주소값이 하나 증가하게 되면 바로 그 옆의 내용인

'52'를 가리키게 됩니다.

즉 FLD2가 지정하는 메모리의 주소값이 되어 버리게 됩니다.

그래서 위와 같은 프로그램에서는 AL 레지스터에 바로 'FLD2'의 데이터 값인

'C' 즉 '52'라는 값이 들어가게 되는 것입니다.

마찬가지로 'FLD1 + 3'을 하게 되면 '4F'를 지정하게 됩니다.

이것은 'FLD3'의 내용중의 하나인 'O'를 가리키게 됩니다.

이와 같이 되는 이유는 변수선언을 하게 되면 주로 연속되어서 여러개의 변수들이

메모리로 잡히기 때문에 위와 같이 해게 되면 그러한 값들을 얻게 되는 것입니다.

     

이제 선언되어진 변수값에 어떻한 값을 넣는 방법에 대해 설명을 하겠습니다.


      FLD1      DB      ?


위와 같이 선언되어진 변수는 메모리에 FLD1이라는 이름으로 메모리의 1바이트의

크기를 잡아주고 여기에는 어떻한 값이 들어갈지는 아직 모른다고 전에 말씀드렸습니다.

그럼 위와 같이 변수를 잡아 주는 이유는 무엇일까요?

그것은 어떻한 연산을 한 후에 그 결과 값들을 넣어주기 위한 것입니다.

물론 레지스터에 보관을 하면 됩니다만, 레지스터의 갯수가 제한 되어져 있기 때문에

저장할 데이터들이 매우 많을 경우에는 레지스터들이 감당을 할 수 없게 됩니다.

이럴때 남아도는 메모리를 이용하여 일시적으로 저장을 하여 다시 꺼내거나 조작을

하면 됩니다.

그런 의미에서 위와 같은 변수 선언을 아마 자주 사용하게 될겁니다.


그럼 이제 이와 같이 선언되어진 변수에 어떻한 값을 넣는 방법을 알아보죠.

방법은 간단 합니다.


           MOV       FLD1, AL


이와 같이 하여 주면 FLD1이라고 잡혀져 있는 메모리에 AL레지스터의 값을 넣어주게

됩니다. 다시 말해 메모리의 어떻한 부분 ( FLD1 이라고 이름지어진 ) 에 AL레지스터

값을 넣어 주게 됩니다.

그래서 다음에 FLD1을 참조하게 되면 위에서 넣어준 AL 레지스터의 값이 그대로 오게

되는 효과를 보게 됩니다.


           FLD1      DB      7 DUP (0)


위와 같이 선언되어진 변수가 있다고 칩시다.

그럼 메모리에는


  00 00 00 00 00 00 00


위와 같은 장소가 잡히게 됩니다.

이런 경우 이 변수는 배열과 같은 효과를 본다고 했습니다.

그럼 이 장소에 다음과 같은 값들을 넣는다면,


     FLD1      : 'A'

     FLD1 + 1  : 'B'

     FLD1 + 2  : 'C'

     FLD1 + 5  : 'D'


위와 같은 값들을 넣는다면 메모리의 구조는


  41 42 43 00 00 44 00


이렇게 들어 가겠죠?

이를 직접 어셈블리어 명령어로 하면


      LEA      BX,FLD1

      MOV      [BX],'A'

      MOV      [BX + 1], 'B'

      MOV      [BX + 2], 'C'

      MOV      [BX + 5], 'D'


위와 같이 하여 주면 우리가 원하는 결과를 얻을 수 있습니다.

여기서 하나 눈여겨 볼 것은

주소를 지정하는 방식에서 '[]'안에 직접적인 연산 명령을 줄 수도 있다는 점입니다

즉 '[BX + 5]' 와 같이 직접 그 안에 '+' 라는 연산 명령을 줄 수도 있다는 사실이죠.


마지막으로 다음과 같은 경우를 보죠.


      FLD1DB      DB      ?

      FLD2DB      DB      43


      MOV      FLD1DB, FLD2DB


이런 경우는 여지없이 꽝~~~ 'ERROR'메시지를 출력합니다.

그 이유는


      MOV      [105], [106]


위와 같은 명령어는 사용이 불가능 하기 때문 입니다.

이와 같은 맥락에서 그런 명령어는 사용이
   합니다.

그럼 위와 같은 결과를 얻기 위해서는 어떻게 해야 할까요?

그것은 다음과 같이 하면 됩니다.


      MOV      AL,FLD1DB

      MOV      FLD2DB,AL


위와 같이 하여 주면 원하는 결과를 얻을 수 있습니다.




에고...역시나 버벅...헐~

사실 주소지정 방식에 대해서 모두 설명 한 것은 아닙니다.

그 이유는 일단은 전에 올린 프로그램 소스를 분석하고, 이해하기 위해서 이기 때문에

분석 이외의 내용에 대해서는 나중에 따로 조금씩 추가로 설명을 해 드리겠습니다

앞으로의 설명을 모두 이런 맥락에서 해 나가겠습니다.


그럼 다음에는 여러개의 프로시져를 처리하고, 그 때의 스택의 변화등을 살펴 보겠

습니다.




 [18] 제목 : [초급-보충] 프로시져 및 스텍의 변화에 대해...
 올린이 : 까망벌레(정태식  )    94/11/24 00:09    읽음 : 326  관련자료 없음

음....이번에는 여러개의 프로시져에 대한 설명과 그에 따른 스텍의 변화 등을

설명 드리겠습니다.


일단은 프로시져에 대해 설명을 드리겠습니다.

음...프로시져란, C언어 에서의 '함수'와 같은 기능을 한다고 전에 말씀 드렸습니다.

즉, 어떠한 특수한 기능을 하는 명령어의 집합이라고 할 수 있죠.

다음 간단한 예제를 보이겠습니다.




    .MODEL SMALL

    .CODE

   

    ORG 100H


START      PROC    NEAR 

    MOV    DL,41H

    MOV    AH,02H

    INT    21H

START      ENDP

    END


위와 같은 프로그램을 하나 생각해 봅시다.

이 프로그램은 문자 'A'를 하나 출력하여 주는 프로그램 입니다.

또 다른 프로그램을 하나 보죠.



    .MODEL SMALL

    .CODE

   

    ORG 100H


START      PROC    NEAR 

    MOV    DL,41H

    CALL   PRINT_CHAR

    INT    20H

START      ENDP


PRINT_CHAR PROC    NEAR

    PUSH   AX

    MOV    AH,02H

    INT    21H

    POP    AX

    RET

PRINT_CHAR ENDP

    END


앞에것 보단 좀 길죠?

하지만 이 역시 위와 똑같은 결과, 즉 문자 'A'를 하나 출력하여 주는 프로그램 입니다.

그럼 이 두 프로그램의 차이는 무엇일까요?

위에것은 바로 프로시져를 사용하지 않고 최대한 짧고 간결하게 문자 'A'를 찍어주는

프로그램 입니다. 그리고 그 다음 후자의 것은 이와 같은 기능을 '프로시져'라는 것을

사용하여 문자를 찍어 주는 프로그램 입니다.


( 앞으로 전자의 프로그램을 1번 프로그램, 후자를 2번 프로그램 이라고 부르겠습니다.)


여기서 보듯 프로시져를 이용한 프로그래밍이 훨씬 더 길고 보기에 복잡해 보입니다.

그래서 구지 프로시져를 사용할 필요성이 있을까...라는 생각을 할 수도 있습니다.

하지만, 프로시져란 기능은 생각과는 달리 아주 막강한 기능을 발휘하여 주는

아주 편리한 것입니다.


2번 프로그램을 한번 보죠.


    MOV    DL,41


이것은 문자의 아스키 코드 값을 DL 레지스터에 넣어주는 것입니다.

그 이유는 프로세서가 DL 레지스터에 있는 아스키 코드값 ( 여기서는 16진수 41이고,

이 값을 문자로 하면 대문자 'A'가 됩니다.)을 보구서 여기에 해당하는 문자를 찍게

하기 위해 넣어주게 됩니다.


    CALL    PRINT_CHAR


이것에 의해 DL레지스터에 들어있는 값을 보고서 그 값에 해당하는 아스키 코드 문자

를 하나 찍어 주게 됩니다.


( 프로시져 : 흐~ 혹시 2번 프로그램에서 어떤게 프로시져인지 모르시는 분을 위해..

  2번 프로그램에서


      PRINT_CHAR    PROC    NEAR

      .....


      PRINT_CHAR    ENDP


  이 부분으로 싸여 있는 부분이 하나의 프로시져를 나타내는 것입니다.

  그러니깐 이 프로시져를 'PRINT_CHAR 프로시져' 라고 말 할 수 있는 것입니다.)


CALL을 사용하여 프로시져를 호출하게 되면 프로그램의 흐름은 바로 호출되어진

프로시져 내로 옮겨가게 되는 것입니다.

2번 프로그램을 한번 전체적으로 실행되어지는 순서를 보죠.


      MOV    DL,41H

     

      PUSH   AX       --;

      MOV    AL,02H     ;---> 이 부분이 바로 PRINT_CHAR이라는 이름의 프로시져

      INT    21H        ;     부분이 됩니다.

      POP    AX       --;     ( CALL PRINT_CHAR 에 의해 실행되는 부분입니다. )


      INT    20H


이를 보면 알 수 있듯이 PUSH,POP 을 제외하고는 1번 프로그램이랑 완전히 같은 순서로

실행이 되어진다는 것을 알 수 있습니다.


PRINT_CHAR 프로시져는 DL레지스터에 들어 있는 문자를 출력하여 주는 기능만을 하는

것입니다. 즉, 문자를 하나 찍는 명령어를 PRINT_CHAR이라는 이름으로 프로그래머가

직접 만들어 주는 경우와 같습니다.

MOV 라는 것이 어떠한 값을 옮겨 주는 것과 같이 PRINT_CHAR라는 명령어로 문자를

하나 출력하여 주는 명령어를 만들었다고 할 수 있습니다.

단지 이 명령어를 실행하기 위해서는 CALL이라는 명령어를 앞에 해 주어야 한다는

차이 뿐이라고 생각하시면 되겠지요.


그럼 이 같이 프로시져를 만들어서 사용하면 무슨 잇점이 있을까요?

만약 메인 프로그램 ( 2번 프로그램에서는 'START    PROC    NEAR' 부분으로 되어있는

곳이 됩니다. ) 에서 하나의 문자를 출력하는 기능을 여러군데에서 필요로 하게

된다면, 원래는 2줄의 명령문 집합을 하나의 명령으로 줄일 수 있게 됩니다.

다시 말해, 만약 프로그램에서 이와 같은 기능을 10군데에서 필요로 하게 된다면,

1번 프로그램과 같이 프로시져 없이 그냥 프로그래밍을 한다면 20라인이 더 추가되는

반면, 2번프로그램과 같이 프로시져를 사용하게 되면, 10라인으로 줄일 수 있게 됩니다.


    MOV    AH,02H

    INT    21H


이과 같이 두줄의 명령문을 필요할 때 마다 넣어주어야 하는 반면,


    CALL   PRINT_CHAR


이 같이 단 한줄의 명령문으로 똑 같은 기능을 기대할 수 있게 되는 것입니다.



이와 같이 생각한다면, 만약 어떠한 특수한 기능을 하는 명령어의 집합

 ( 예를 들어 진수를 변환하여 준다던가, 키보드로 부터 어떠한 문자를 하나 입력

   받는 기능이라든가.. ) 이 10라인 이상이 넘어가게 된다면, 그리고 이와 같은

필요로 하는 기능이 프로그램 여기저기 에서 자주 필요로 하게 된다면, 프로시져를

사용하지 않고서는 너무나 길어지고, 메모리 소비 또한 매우 많아 지게 됩니다.


위의 프로그램 예에서 'COMPUTER'라는 문자열을 출력한다고 하면,

1번 프로그램 에서는


    MOV    DL,43H

    MOV    AH,02H

    INT    21H

   

    MOV    DL,4FH

    MOV    AH,02H

    INT    21H


    MOV    DL,4EH

    MOV    AH,02H

    INT    21H

    ......등등


인 반면,

2번 프로그램 에서는


    MOV    DL,43H

    CALL   PRINT_CHAR

   

    MOV    DL,4FH

    CALL   PRINT_CHAR

  

    MOV    DL,4EH

    CALL   PRINT_CHAR

    ......등등


이 같이 꽤 간결하고, 알아 보기 쉽게 변환이 됩니다.

PRINT_CHAR 이라는 프로시져가 단순히


    MOV    AH,02H

    INT    21H

 

의 단 두줄이기 때문에 1번 프로그램과 같이 해도 해 볼만 하겠지만, 만약 50라인이

훨씬 넘어가는 프로시져라고 한다면 그 차이는 엄청나게 되는 것이지요.




그럼 이제 여기에 스텍이라는 개념을 끼워 넣어 보겠습니다.

스텍은 push, pop등에 의해 스텍에 넣거나 빼 올 수도 있습니다.

하지만 프로시져를 호출할 때나 인터럽트 ( 다음에 설명 ) 호출시에 프로세서가

스텍을 조작하게 됩니다.


이 프로그램을 실행 가능 화일로 만들어서 메모리에 읽어온 후에 이를 실행하게 되면,

우선은 IP 레지스터에 실행되어질 오프셋의 메모리 주소 값이 들어가게 됩니다.

전에도 말씀 드렸다 시피, 프로세서는 항상 IP 레지스터를 조사하여 다음에 실행되어질

프로그램의 내용이 있는 메모리의 오프셋 주소를 알아내어 찾아 가서 실행을 한다고

말씀 드렸습니다. 그럼 위의 프로그램 2번과 같은 경우 이것이 메모리로 읽어 들여

실행될 때의 메모리에 읽어들인 모양을 보겠습니다.


0A8B:0200 B241               MOV      DL,41

0A8B:0202 E80200             CALL     0207

0A8B:0205 CD20               INT      20

0A8B:0207 50                 PUSH     AX

0A8B:0208 B402               MOV      AH,02

0A8B:020A CD21               INT      21

0A8B:020C 58                 POP      AX

0A8B:020D C3                 RET


위와 같은 형태로 메모리에 로드 되어 집니다.

( 확인을 위해서는 DEBUG [FILENAME] 으로 읽어서 디버그의 프롬프트 '-'가 나왔을떠

  'U 200' 명령으로 확인을 해 볼 수 있습니다. )


우선 이것을 실행하기 전에 IP 레지스터는 200 을 가리키고 있습니다. 즉, 프로그램이

시작되어질 위치가 오프셋 200번지라는 뜻이 됩니다.

그리고 실행을 하면 우선은 첫번째 명령인 'MOV      DL,41'을 실행하게 됩니다.

이것을 실행한 후에 IP레지스터의 값은 그 다음 실행 명령이 들어 있는 메모리의

 오프셋 번지인 202 번지를 가리키게 됩니다.


그 다음 'CALL    0207' 이 것이 매우 중요한데, 이것은 바로


 'CALL      PRINT_CHAR' 부분이 컴파일 된 상태가 됩니다. 즉, 프로시져를 호출하게

되는데, 그 호출되는 프로시져의 이름 대신에 프로세서가 알 수 있는 메모리의 번지를

직접 가리키도록 변환이 되어 졌습니다.

이것을 실행하게 되면, IP레지스터는 바로 '207'번지를 가리키게 됩니다.

즉, 프로그램의 흐름을 중간에서 바꾸어 버리게 되는 것으로, 207번지로 바로 점프를

하게 되는 것입니다.


그럼 프로세서는 역시 IP레지스터를 보고서, 실행될 위치를 찾고서 207번지에 있는

'PUSH     AX'를 실행하게 됩니다. 그럼 역시 IP레지스터에는 그 다음 명령이 들어있는

'208'번지를 가리키게 됩니다.


이런 식으로 '20C'번지 까지 실행을 하면, IP레지스터는 그 다음 명령인 'RET'명령이

들어있는 '20D'를 가리키게 됩니다. 이 'RET'명령은 아까 'CALL    0207'로 프로시져를

호출하는 명령 그 다음 명령 부분으로 돌아가도록 하는 명령 입니다.

즉, 205번지인 'INT    20'으로 돌아가도록 하는 명령이 됩니다. 그럼 프로그램의

흐름은 아까 202번지 까지 실행이 되어 지다가 'CALL' 명령에 의해 207번지로 건너 뛴

다음, 'RET'명령에 의해 위치상으로 'CALL'명령 다음에 있는 명령으로 순서를 옮겨 가게

됩니다.


그럼 한번 생각을 해 봅시다.

CALL명령으로 207번지로 점프를 하게 됴습니다. 그 상태에서 'RET'명령을 만나게 되면,

'CALL'다음에 있는 'INT    20'이 있는 곳으로 IP레지스터를 바꾸어 주어야 프로그램의

흐름이 원래 상태로 돌아오게 됩니다. 즉,'RET'명령을 만나게 되면 IP레지스터에 205를

다시 넣어 주어야만 합니다.

다시 좀 더 자세히...

아래에 프로그램이 실행되어 짐에 따라 IP레지스터의 변화를 보여 보겠습니다.



         
                                             [200]   

0A8B:0200 B241               MOV      DL,41             [202]    (1)

0A8B:0202 E80200             CALL     0207              [207]    (2)

0A8B:0205 CD20               INT      20                         (8) 

0A8B:0207 50                 PUSH     AX                [208]    (3)

0A8B:0208 B402               MOV      AH,02             [20A]    (4)

0A8B:020A CD21               INT      21                [20C]    (5)

0A8B:020C 58                 POP      AX                [20D]    (6)

0A8B:020D C3                 RET                        [205]    (7)


'[]'안의 숫자가 바로 IP레지스터의 값을 나타냅니다.

여기서는  'MOV    DL,41'을 실행하면 실행한 다음 IP레지스터에 '202'가 들어가게

된다는 뜻입니다.

그리고 제일 오른쪽'()'안의 숫자는 순서대로 실행이 되어 지는 순서를 나타냅니다.


위에서 보면 'CALL' 명령을 실행 하면 IP 값이 207으로 변환이 됩니다. 만약 'CALL'

명령이 아니고 다른명령 ( 프로그램 실행 순서와 관계없는 명령들 )이 있었다고 한다면,

IP레지스터에는 205가 들어가는게 정상이지만, 여기서는 프로그램의 실행 순서를 바꾸게

되는 명령인 'CALL'이 있기 때문에 IP레지스터에는 '207'이 들어가게 됩니다.

그럼 프로세서는 'CALL'명령의 다음 실행 시킬 명령으로 'INT   20' 이 아닌

바로 207 번지에 있는 'PUSH    AX'를 실행하게 됩니다.

그 다음은 물론 ' MOV    AH,02'가 되겠죠.

그러다가 바로 'RET'명령을 만나면 다시 원래의 프로그램 실행 순서를 찾아게게 됩니다.

즉 순서가 뒤게뀌게 된 곳인 'CALL'이 있는곳, 그 다음의 위치인 205번지 'INT    20'을

실행하도록 하기 위해 IP레지스터에 205라는 값을 넣게 됩니다.


그럼 여기서 프로세서는 'RET'명령을 만난 후 돌아갈 위치를 어떻게 올바로 찾을 수

있는지...프로그램 상으로는 어떻한 부분에서도 직접 그 IP레지스터를 어느 다른 곳에

보관을 시키는 명령은 없습니다.

하지만 'RET'명령을 만나면 프로세서는 정확히 원래의 위치로 찾아가게 됩니다.

즉, 'CALL'명령을 만날 때의 그 위치( IP 레지스터의 값 )를 어디 다른 안전한

곳에 보관을 하였다가 'RET'명령을 만나게 되면, 다시 꺼내어 와서 IP레지스터에 다시

넣어주게 되는 것입니다.

그럼 역시 일시적으로 IP레지스터의 값을 보관하는 곳은 메모리가 되겠죠..

그 메모리 영역을 바로 스텍이라고 부릅니다.

스텍의 구조는 전에 설명을 드렸었죠.


CALL    207 을 실행하게 되면 프로세서는 스스로 스텍에 원래의 그 다음 오프셋

값인 205를 보관하게 됩니다. 즉


    PUSH      IP


를 스스로 하게 되는 것입니다.

그리고 다시 RET명령을 만나게 되면 스텍에 프쉬하였던 IP레지스터의 값을 다시 팝을

하여서 IP레지스터에 넣어주게 됩니다.


    POP       IP


와 같은 기능을 스스로 하게 됩니다.



그럼 프로그램 소스와 스텍 세그먼트의 변화등을 직접 살펴 보겠습니다.

보통 프로그램을 실행하게 되면 최초의 스텍 포인터 값은 'FFFF'번지를 가리키게 됩니다.

즉 COM화일인 경우 하나의 세그먼트만을 사용하게 되는데 그 세그먼트 내에서 가장

최 상위 번지인 'FFFF'번지를 스텍 포인터가 가리키게 됩니다.

그래서 제일 처음 스텍에 푸쉬하는 값은 메모리 번지 'FFFF'에 저장이 되어 집니다.

그 다음은 물론 스텍 포인터가 'FFFD'를 가리키는건 아시는 것이고요...


프로그램을 실행하게 되면, 프로세서는 일단 스텍에 '0000'이라는 값을 푸쉬하게

됩니다. 이것은 프로그래머가 프로그래밍을 하지 않아도 프로세서가 스스로 하는 작업

입니다. 이 이유는 인터럽트를 설명드릴때 간단히 설명을 드리겠습니다.

그래서 그럼 '0000'이라는 값이 푸쉬된 상태에서 프로그래머가 프로그래밍한 프로그램을

실행하게 됩니다. 하나의 워드값이 푸쉬 되었기 때문에 SP( 스텍 포인터 )의 값은

'FFFD' 를 가지게 됩니다.

그럼 프로그램 소스를 보면서...


                                                        [FFFD]   

0A8B:0200 B241               MOV      DL,41             [FFFD]    (1)

0A8B:0202 E80200             CALL     0207              [FFFB]    (2)

0A8B:0205 CD20               INT      20                [FFFD]    (8) 

0A8B:0207 50                 PUSH     AX                [FFF9]    (3)

0A8B:0208 B402               MOV      AH,02             [FFF9]    (4)

0A8B:020A CD21               INT      21                [FFF9]    (5)

0A8B:020C 58                 POP      AX                [FFFB]    (6)

0A8B:020D C3                 RET                        [FFFD]    (7)



그림으로 마지막 정리를 해 보겠습니다.

 


        (1)         (2)         (3)         (4)         (5)


  SP =  FFFD        FFFB        FFF9        FFFB        FFFD

      

      ;------;    ;------;    ;------;    ;------;    ;------;

 FFFF ; 0000 ;    ; 0000 ;    ; 0000 ;    ; 0000 ;    ; 0000 ;

      ;------;    ;------;    ;------;    ;------;    ;------;

 FFFD ;      ;    ; 0205 ;    ; 0205 ;    ; 0205 ;    ;      ;

      ;------;    ;------;    ;------;    ;------;    ;------;

 FFFB ;      ;    ;      ;    ;  AX  ;    ;      ;    ;      ;

      ;------;    ;------;    ;------;    ;------;    ;------;

 FFF9 ;      ;    ;      ;    ;      ;    ;      ;    ;      ;

      ;------;    ;------;    ;------;    ;------;    ;------;

 FFF7 ;      ;    ;      ;    ;      ;    ;      ;    ;      ;


     

(1)

   이것은 프로그램이 시작 되기 전에 스텍에 0000을 푸쉬한 상태를 나타냅니다.


(2)

   이것은 CALL명령을 한 후에 그 다음 메모리의 오프셋 값인 205 가 스텍에

   푸쉬된 상태를 나타냅니다.


(3) 

   이것은 'PUSH    AX'라는 명령에 의해 AX레지스터의 값이 스텍에 푸쉬되어진

   상태를 나타내고 있습니다.


(4)

   이것은 'POP    AX'에 의해 스텍에 들어있던 AX레지스터의 값을 다시 AX레지스터에

   넣어 주는 명령을 실행 한 후를 나타냅니다.


(5)

   이것은 'RET'명령에 의해 IP레지스터에 'CALL'명령 다음으로 실행되어질 프로그램의

   메모리 주소값을 다시 IP레지스터에 넣어준 후 그 상태가 됩니다.







참고 : 'PUSH    AX',  'POP     AX' 라는 부분에 대해서...


    이것은 프로그램을 실행하기 전에 미리 AX레지스터의 값을 보관하기 위해

    하여 주는 것입니다. 즉 AX레지스터의 값이 변하는 것을 방지하기 위해서지요.

    다음과 같은 경우를 보죠.




    MOV      AX,0123

    CALL     EXT1

    MOV      CX,AX

    ......


EXT1    PROC    NEAR

    MOV      AX,0206

    INT      21H

    RET

EXT1    ENDP

    ....


위와 같은 프로그램이 있다고 한다면, CALL명령에 의해 EXT1이라는 프로시져가 호출 되었

습니다. 여기서 하나, CALL명령이 실행되기 전의 AX레지스터의 값은 0123으로 되어

있음을 알 수 있습니다. ( MOV      AX,0123 에 의해서.. )

그럼 프로시저를 호출하게 되면 바로 'MOV      AX,0206'이라는 명령어를 실행하게

됩니다. 그럼 원래 AX레지스터에 있던 값인 '0123'이 날아가 버리고 다시 새로운

값인 '0206'이라는 값이 들어가게 됩니다.

그 후 프로시져를 끈낸 후 다시 본 프로그램으로 돌아와서 다음 명령을 실행하게

되는데, 여기서는 'MOV      CX,AX'라는 것이 됩니다. 그럼 과연 CX레지스터에는

어떻한 값이 들어가겠습니까? 원래 우리가 원했던 값은 '0123'이었는데 엉뚱한 값인

'0206'이 들어가 버리게 됩니다.

즉 프로시져 호출을 해서 그 안에서 AX레지스터를 사용하였기 때문에 원래의 AX레지스터

안에 가지고 있던 값을 잃어 버리게 되는 것입니다. 이것을 방지 하기 위해서

PUSH, POP 으로 레지스터의 값을 잃어 버리는 일 없이 보관을 할 수 있습니다.

수정된 프로그램을 보죠.



    MOV      AX,0123

    CALL     EXT1

    MOV      CX,AX

    ......


EXT1    PROC    NEAR

    PUSH     AX                  <--- AX 레지스터의 값을 스텍에 저장

    MOV      AX,0206

    INT      21H

    POP      AX                  <--- 스텍에 저장된 원래의 AX레지스터의 값을

    RET                               다시 AX레지스터로 복원

EXT1    ENDP

    ....


이와 같이 하면 CX레지스터에는 우리가 원했던 '0123'이라는 값이 들어갈 수 있게

됩니다.




음....솔직히 제가 다시 읽어 보아도 되게 답답 하네요...

좀 더 명쾌하게 설명을 하고 싶었는데...헐헐~

어셈을 배우시고자 하는 분들에게 이 부분은 좀 상당부분 이해하기 힘들거 같아

보입니다. 근데 솔직히 이거 읽으시는 분들이 과연 어떤 부분이 이해가 잘 안가시는지

제가 모르기 때문에, 정말 이해가 한 군데라도 안가는 부분이 있다면 질문 하세요...

자세히 다시 설명을 드리겠습니다.



그럼 다음에는 '프로그램 세엣'의 분석을 하기 위해 필요한 명령어 설명을 간단히

드린 후 세번째 프로그램 소스를 자세히 분석해 보도록 하겠습니다.

'C/C++언어' 카테고리의 다른 글

함수포인터란 ?  (0) 2007.09.06
[본문스크랩] 파일 입.출력  (0) 2007.09.06
[본문스크랩] VC++ Article  (0) 2007.09.06
[강좌] MASM 6.0 사용법  (0) 2007.09.06
어셈블리 초급 기초 #1(정태식님)  (0) 2007.09.06
어셈블리 초급 기초 #2(정태식님)  (0) 2007.09.06

+ Recent posts