설마 어셈블러까지 건드리겠어 라고 생각하시는 분들이 많으시겠지만,
실무에서 타사랑 경쟁하고 있다면 어셈블러를 쓰는게 더 낳은경우가 많습니다. (아무래도 기계 성능을 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
 [19] 제목 : [초급-보충] 논리연산에 대해서...
 올린이 : 까망벌레(정태식  )    94/12/12 23:53    읽음 : 303  관련자료 없음

강좌가 늦어진 점에 대해 사과 드립니다.

제가 곧 군대를 가기 때문에 이런 저런 일 좀 처리하다 보니

2주일 정도 전혀 못들어 오게 됬었습니다.
그럼 계속 해서...


이번에는 논리 연산 명령에 대해 알아 보도록

하겠습니다.


아마 대부분 여러분들은 고등학교 과정에서 논리 연산에 대해 간략히 배워

보셨을 줄로 압니다.

즉 논리 연산에는 다음과 같은 명령들이 있는데...


    AND

    OR

    NOT

    XOR

  


위와 같은 네개가 컴퓨터에서 대표적으로 쓰이는 논리연산 명령어들 입니다.

그럼 우선 하나하나 자세하게 설명을 해 나가겠습니다.

학교에서 배울때는 'T,F'라는 것을 많이 사용 하였는데요, PC에서는 이 'T,F'

라는 것 대신에 프로세서가 알아 먹을 수 있는 내용인 숫자로 바꾸어 연산이

되어 집니다. 즉 프로세서가 알 수 있는 숫자는 2진수 밖에 없으므로 이

'T,F'를 2진수로 대치시켜 사용 하도록 합니다.


논리 연산에서는 'T,F'두개인 것과 같이 2진수 또한 '0,1'이라는 두개의 숫자만이

존재 합니다. 그럼 쉽죠?


  T ( TRUE )  = 1

  F ( FULSE ) = 0


위와 같이 정의를 하면 논리 연산도 식은죽 먹기...






첫째로 AND에 대해서...

우선 AND연산에 대한 진리표를 작성해 보도록 하겠습니다.


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

 ; X ; Y ; X AND Y ;

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

 ; F ; F ;    F    ;

 ; F ; T ;    F    ;

 ; T ; F ;    F    ;

 ; T ; T ;    T    ;

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


위와 같은 진리표를 얻습니다.

( 헤..다 아신다구 해도 혹시나 까먹으셨을지도 모르시는 분들을 위해... )


AND 라는 것은 위에서 보면 알 수 있듯이 두개의 오퍼랜드 ( 피연산자 )를 필요로

하는 논리 연산 명령어 입니다. 위의 진리표에서는 두 개의 피 연산자가 바로

'X' 와 'Y'가 되는 것이겠죠. 이 AND라는 것은 두 개의 오퍼랜드 모두 참 값

( 'T'라는 값 ) 을 가져야만 결과값이 참( T )이 되는 것을 알 수 있습니다.

이제 기억이 조금씩 되살아 나실라나 모르겠네요.

그럼 이 진리표를 조금 바꾸어서 이를 2진수의 숫자 0 과 1 이라는 숫자로 바꾸어

다시 그려 보도록 하겠습니다.


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

 ; X ; Y ; X AND Y ;

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

 ; 0 ; 0 ;    0    ;

 ; 0 ; 1 ;    0    ;

 ; 1 ; 0 ;    0    ;

 ; 1 ; 1 ;    1    ;

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


위와 같이 변환이 되어 집니다. 즉 전에 말씀 드렸다 시피


    T 는 1

    F 는 0


으로 각각 바꾸어 주기만 하면 됩니다.

그럼 이제 이 AND 연산 명령이 PC 상에서 어떻게 사용 되며 어떠한 처리를 하는지,

그리고 어느때 이러한 연산 명령이 필요한지 간단히 알아 보도록 하겠습니다.


이제까지 연산 처리 명령은 모두 1비트 크기의 연산 명령으로 제한된 예를 보였

습니다. 하지만 이 AND 연산이라는 것이 1비트 연산만을 하기 위한 것이라고

한다면... '푸~~~' 하고 한숨만 나오겠죠...

그럼 다음과 같은 것을 봅시다.


    X = 00101101

    Y = 10110110

( 이 숫자들의 값은 모두 2진수 입니다.

  즉, X 의 값은 원래 45 이고 Y는 182 인데, 이 값들을 모두 2진수로 바꾸어

  논리 연산을 하는데 알아보기 편하도록 했습니다. )


위와 같이 X,Y 의 값을 정의 하였다고 해 보죠.

그럼 학교 수학 시간에 배운 개념을 날려 버리고 새롭게 PC차원의 논리 연산으로

들어가서 위의 두 변수의 값들을 AND연산을 해 보도록 하겠습니다.


    AND    X,Y


앗! 위의 것은 무슨 뜻이냐고요?

원래의 의미는 'X   AND   Y'와 같은 의미를 지닙니다. 하지만 PC상에서 사용하는

문법을 따르기 위해 위와 같은 표기를 하게 된 것입니다.

즉 'X 값과 Y 두개의 값을 서로 AND 연산 시켜라'라는 명령어가 되는 것입니다.


그럼 결과 값은 어떤게 나오는지 살펴 보죠.


        00101101   <-- X 값

        10110110   <-- Y 값

   AND )________

         결과값


( 후후...이거 결과 값 찾는데 어지간히 뜸을 들이는군요.. )

위와 같이 자리수를 서로 맞추어 다시 써 보았습니다.


좀 더 편한 설명을 위해 각각의 자리수에 번호를 메겨 부르기 편하도록 해 보죠

위의 변수들의 값들은 모두 1바이트의 크기를 갖습니다. 즉 비트수로 따지면

8개의 비트수를 갖는 숫자라고 할 수 있습니다.

그럼 각각의 비트마다 번호 ( 순번 )을 매겨 보도록 하겠습니다.


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

 ;  0  ;  0  ;  1  ;  0  ;  1  ;  1  ;  0  ;  1  ; <---  X값

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

   7번   6번   5번   4번   3번   2번   1번   0번    비트


위와 같이 매길 수 있습니다. ( 매길 수 있습니다가 아니고 사실 위와 같이

매기도록 되어 있습니다.)

조금 핵갈리실지도 모르지만 가장 죄측의 비트 번호가 '8'이 아닌 '7'이 된다는

사실에 주의를 해 주세요. 사실 비트수가 모두 8개 이어서 가장 높은 자리의 비트

번호가 '8'이어야 할 꺼 같지만 ( 1 부터 8 까지 ) PC 상에서는 이 '0' 이라는

숫자가 더 끼어들게 되어 가장 낮은 자리의 비트 번호는 '0'이 되고 가장 높은

자리의 비트수가 바로 '7'이 되어 집니다. ( 0 부터 7 까지 모두 8개의 비트 )


그럼 위의 연산 결과값을 하나하나 찾아 나가보도록 하겠습니다.

X,Y 두개의 변수에서 각각의 자리수를 이미 맞추어 놓았습니다. 즉, X의 7번 비트는

Y의 7번 비트, X의 5번 비트는 Y의 5번 비트....등등 각각의 비트자릿수를 맞추어

놓습니다.

그리고 이 맞추어진 것에 따라 하나씩 (1 비트씩) 따로따로 연산을 하여 결과값도

마찬가지로 각각 비트의 자릿수를 맞추어서 써 주면 됩니다.

알아 보기 쉽도록 표를 하나 그려 보도록 하겠습니다.


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

 ; 비트 번호 ; X ; Y ; AND    X,Y ; 

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

 ;     0     ; 1 ; 0 ;     0      ;

 ;     1     ; 0 ; 1 ;     0      ;

 ;     2     ; 1 ; 1 ;     1      ;

 ;     3     ; 1 ; 0 ;     0      ;

 ;     4     ; 0 ; 1 ;     0      ;

 ;     5     ; 1 ; 1 ;     1      ;

 ;     6     ; 0 ; 0 ;     0      ;

 ;     7     ; 0 ; 1 ;     0      ;

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


위와 같이 됩니다. 각각의 비트번호 대로 1비트씩 따로 짤라내어 하나씩 연산을

한 결과를 얻게 되는 것이지요.

그래서 위와 같은 예의 결과 값은 바로


  00100100 


이 되는 것입니다.


그럼 이와 같은 것을 직접 어셈블리 명령어를 사용해서 프로그래밍을 해 보도록

하겠습니다.


    X = 00101101

    Y = 10110110


    X  AND  Y    = ??


'??'라는 값을 얻기위한 어셈블리 프로그래밍은,


    MOV    AX, 00101101B

    MOV    BX, 10110110B

    AND    AX, BX


간단하죠?

MOV 에 의해 AX에는 원래의 X값을, BX에는 원래의 Y값을 넣어주었습니다.

그리고 다음의 'AND'명령에 의해 AX와 BX의 값을 서로 AND 시켜 주었습니다.

앗! 근데 연산의 결과 값이 과연 어디에 들어가나요?

그건 역시나 'AX'레지스터에 들어가게 됩니다.

즉 'AND    AX,BX'는 AX레지스터와 BX레지스터에 들어 있는 값들을 서로 논리연산

'AND'를 수행 한 후에 그 결과 값을 다시 AX레지스터에 넣어라! 라는 뜻이 됩니다.

만약 'AND    BX,AX'라고 한다면 똥 같은 값이지만 그 결과 값을 AX레지스터가 아닌

BX레지스터에 넣게 되는 것이지요.


그럼 과연 이러한 연산이 어디에 쓰이는가.

간단한 한 예를 보도록 하겠습니다.

인터럽트 11(16진수 11임)을 호출할 때를 보도록 하겠습니다.

( 인터럽트에 관한 설명은 나중에 따로 하겠습니다. 여기서는 그냥 그려러니...하구

  넘어 가세요 )

이 인터럽트를 호출하게 되면 프로세서는 지금 PC의 주변기기의 연결상태를

체크하여 그 정보를 넘겨 줍니다. 그 정보는 AX레지스터로 넘겨 주게 됩니다.

사실 연결장치의 정보는 한가지가 아닌 여러가지의 연결장치들의 상태를

넘겨주게 되는데, AX레지스터에 이 모든 정보들이 다아 담겨져 있습니다.


  플로피 드라이브의 설치 유무

  코프로 세서의 유무

  16K RAM 블럭의 수

  비디오 모드

  플로피 드라이브 갯수

  DMA 칩(CHIP)의 사용

  RS - 232 카드의 수

  게임 포트 수

  직렬 프린터의 설치

  모뎀 설치

  설치된 프린터의 수


위와 같은 11개에 대한 정보가 하나의 레지스터인 AX레지스터에 모두 담겨져 나오게

됩니다. 그럼 과연 그것이 어떻게 가능 할까요? 그것은 바로 위와 같은 정보들이

따로따로 각각의 지정된 고유의 비트들을 차지하여 정보를 전달하게 됩니다.



비트 번호

    0     플로피 드라이브의 설치 유무

    1     코프로 세서의 유무

  2 - 3   16K RAM 블럭의 수

  4 - 5   비디오 모드

  6 - 7   플로피 드라이브 갯수

    8     DMA 칩(CHIP)의 사용

  9 - 11  RS - 232 카드의 수

    12    게임 포트 수

    13    직렬 프린터의 설치

    13    모뎀 설치

 14 - 15  설치된 프린터의 수


위와 같이 각각의 비트들에 그 주변 기기들의 상태들에 대한 정보를 넣게 됩니다.

가령 인터럽트 11번을 사용하여 얻어낸 AX레지스터의 값이 다음과 같다고 해 보죠.


    0110000010101111 ( 2진수 입니다. 16진수로는 60AF 가 됩니다. )


이걸 좀 더 알아보기 편하도록 박스를 그려 구분해 보도록 하겠습니다.


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

; 0 ; 1 ; 1 ; 0 ; 0 ; 0 ; 0 ; 0 ; 1 ; 0 ; 1 ; 0 ; 1 ; 1 ; 1 ; 1 ;

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

  F   E   D   C   B   A   9   8   7   6   5   4   3   2   1   0   <- 비트 번호

( 위의 비트 번호에서 영문자는 16진수 표기를 따랐기 때문 입니다. )


위와 같이 정리 될 수 있습니다.

그럼 이제 이 값들에서 필요한 정보를 알아 내도록 해 보겠습니다.


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

;비트번호;     내           용        ; 값 ;   결과의 내용     ;

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

;   0    ;플로피 드라이브의 설치 유무 ;  1 ; 있음              ; 

;   1    ;코프로 세서의 유무          ;  1 ; 있음              ;

; 2 - 3  ;16K RAM 블럭의 수           ; 11 ; 3개               ;

; 4 - 5  ;비디오 모드                 ; 10 ; 80X25 COLOR MODE  ;

; 6 - 7  ;플로피 드라이브 갯수        ; 10 ; 2개               ;

;   8    ;DMA 칩(CHIP)의 사용         ;  0 ; 없음              ;

; 9 - B  ;RS - 232 카드의 수          ;  0 ; 없음              ;

;   C    ;게임 포트 수                ;  0 ; 없음              ; 

;   D    ;직렬 프린터의 설치          ;  1 ; 있음              ;

;   D    ;모뎀 설치                   ;  1 ; 있음              ;

; E - F  ;설치된 프린터의 수          ; 01 ; 1개               ;

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


이와 같습니다.

즉, 비트 단위로 그 결과를 알아 낼 수 있는 것입니다.

하지만 여기서 만약 프로피 드라이브의 갯수를 알고 싶은데, 과연 어떻게

이리저리 섞여 있는 정보들 중에 원하는 정보를 따로 분리해 뽑아 낼수 있을까요.

다시 말해 AX레지스터는 전부 16개의 비트로 구성이 되어 있는데 이 중에서

우리가 원하는 비트 번호인 6번 부터 7번 까지의 비트안에 있는 내용을 뽑아 올 수

있겠습니까?

여기서 바로 AND라는 연산 명령이 사용 됩니다.

다음과 같이 해 보도록 하죠.


    AND      AX, 0000000011000000B


그럼 원래의 AX레지스터에는 0110000010101111 이라는 2진수 값이 들어 있었습니다.


        0110000010101111   <-- 인터럽트 호출로 얻은 정보 값

        0000000011000000  

   AND )________________

            결과 값


이와 같이 하게 되면 결과 값은


         0000000010000000


이 되는 것을 쉽게 알 수 있습니다. 이것은 바로 플로피 드라이브의 갯수를

담고있는 비트 번호만이 그대로 남아 있고 나머지는 모두 0으로 세트 되는 현상을

가져 오게 되는 것이지요. 이렇게 따로 비트의 내용을 뽑아 올때 AND연산을 많이

사용하게 됩니다.






둘째로 OR에 대해 알아 보도록 하겠습니다.


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

 ; X ; Y ; X  OR Y ;

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

 ; 0 ; 0 ;    0    ;

 ; 0 ; 1 ;    1    ;

 ; 1 ; 0 ;    1    ;

 ; 1 ; 1 ;    1    ;

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

.

진리 표는 위와 같습니다.

간단히 연산 결과만을 보여 드리고 다음으로 넘어 가겠습니다.


        X = 01001011

        Y = 10011010


        X   OR   Y   =   11011011


위와 같이 됩니다. 즉 두개의 오퍼랜드 중 하나라도 '1'이 라는 값이 있으면

그 결과는 1이 되는 것입니다. ( 물론 1비트씩 따로 연산이 되어 집니다. )


이 OR이라는 명령어는 어떨때 쓰이느냐...하믄

바로 비트 중에 나머지 부분은 그냥 놔 두고 원하는 부분만을 '1'로 세트하고

싶을 때에 사용하게 됩니다.


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

; 0 ; 1 ; 1 ; 0 ; 0 ; 0 ; 0 ; 0 ; 1 ; 0 ; 1 ; 0 ; 1 ; 1 ; 1 ; 1 ;

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

  F   E   D   C   B   A   9   8   7   6   5   4   3   2   1   0   <- 비트 번호


위의 예에서 만약 비트 번호 9번이랑 8번을 모두 1로 세트하고 싶다면,


    OR      AX, 0000001100000000B


이와 같이 하면 비트 번호 9번이랑 8번은 모두 1로 세트가 되고 나머지는 변화없이

그대로 남게 됩니다.






다음은 NOT 인가요?

이건 너무 간단해서 ....

부정 한다는 뜻입니다.

단 이것은 오퍼랜드가 하나밖에 올 수 없다는 것입니다.

생각해 보면 당연한 얘기지만 말이죠...


       X = 01001101

 

       NOT    X  =  10110010


각 비트들의 값이 모두 거꾸로 전환이 된 것을 알 수 있습니다.

즉, '0'은 '1'로

    '1'은 '0'으로


각각 변환이 된 것을 알 수 있습니다.

이렇게 NOT는 각각의 비트별로 그 값을 '0'이면 '1', '1'이면 '0' 으로 바꾸는

기능을 하여 줍니다.







이제 XOR로 넘어오죠..


이 XOR이라는 것은 EXCLUSIVE - OR  뜻으로 다음 진리표를 보면,

 

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

 ; X ; Y ; X XOR Y ;

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

 ; 0 ; 0 ;    0    ;

 ; 0 ; 1 ;    1    ;

 ; 1 ; 0 ;    1    ;

 ; 1 ; 1 ;    0    ;

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

  

이와 같은 진리표가 됩니다.

또 예를 보도록 하죠.


      X = 01101101

      Y = 10111001


      X    XOR    Y   =   11010100


즉 다시 말해 두 개의 오퍼랜드를 비교 하여 각 비트 별로 그 값이 같으면 ( 모두

'1' 또는 '0' 이면 ) 결과 값은 0이 되고, 두 비교 대상의 값이 서로 다른 값이면

( '0'과 '1', 또는 '1'과 '0' 이면 ) 그 결과값은 1이 되는 것입니다.


이것은 생각보다 꽤 많이 사용되어 지는 연산 명령어 입니다.

그 이유는 어떠한 레지스터의 값을 깨끝이 ( 값을 0으로 해 주는것 ) 하고자 할 때

일단은 다음과 같은 방법이 있음을 알 수 있습니다.


1,    MOV    AX, 00  <-- AX 에 0 을 대입


2,    SUB    AX, AX  <-- AX값에 AX값을 빼어 AX에 넣어줌 ( AX - AX = 0 )


3,    AND    AX, 00  <-- AND연산으로 AX레지스터의 값을 0으로 만들어 줌


4,    OR     AX,0FFH <-- AX 값을 모두 1로 세트한 다음 ( 비트단위 )

      NOT    AX          이것의 부정(NOT)을 취하여 0으로 세트


5,    XOR    AX, AX  <-- AX 값을 AX와 XOR연산 시킴


일단은 위의 다섯 가지를 보도록 하죠.

이 다섯 가지 경우 모두 AX레지스터의 값을 0으로 세트하여 주는 어셈블리

명령어들 입니다.

사실 이 다섯 가지 중, 어떤것을 사용 해도 별 상관은 없습니다.

하지만 가장 많이 사용 되고, 또한 가장 권장할 만한 방법은 바로 다섯번째의 경우인


    XOR    AX, AX


입니다. 그 이유는 바로 어셈블리 프로그래밍을 하는 가장 중요한 이유중의 하나인

속도 차원의 문제 입니다.

대충 비교를 하자면, 덧셈이나 뺄셈 명령은 곱셈이나 나눗셈 명령보단 훨씬 속도가

빠릅니다.

그리고 여기에 논리 연산명령은 덧셈, 뺄셈 명령보다, 그리고 이동 명령인 'MOV'보다

훨씬 빠른 속도를 나타냅니다.


물론 쉬프트 명령이라는 것 또한 못지 않게 빠른 속도를 나타냅니다만, 이 것은

아직 설명을 하지 않았기 때문에 넘어 가기로 하고...


그런 속도 문제 때문에 XOR은 어떠한 레지스터의 값을 0으로 세팅 하는데 아주

많이 사용 됩니다.



 [23] 제목 : 디버그(DEBUG.EXE) 정리 -전편-
 올린이 : 영원의별(이세원  )    95/01/10 18:58    읽음 : 455  관련자료 없음

안녕하세요.


디버그에관해 자세히 그리고 분명히 언급된 책이 별로 없더군요.

그래서 한번 꾸며보았습니다.

너무 길어서 둘로 나누어 올립니다.

많은 도움이 되시길...



                      

                       디버그(DEBUG.EXE)의 사용법.



        디버그라는 뜻은 프로그램의 버그  즉 오류를 찾아낸다는 뜻이다.프로그

램을 완성하고 실행시키면 예상치도 않은  버그가 발생하는수가 간혹있다.  이런

경우에 프로그램을 부분적으로 실행해보고  오류를 찾아내야 하는데 그런 작용을

전문적으로 해주는 프로그래머에게는 필수의 도구가 바로 도스의 디버그(DEBUG.E

XE) 이다. 정확한 명칭은 디버거(DEBUGER)  라고 해야하나 도스의 화일로서 제공

되는 이름은 이름은 디버그이다.


        그러나 지금은 프로그램의 덩치가  워낙 커지고 또한 고급언어로 작성되

는지라 프로그램의 정확한 흐름을 아는것은  대단히 어렵다. 프로그래머 조차 라

이브러리의 내용을 모른다는 이야기다.  따라서 고급언어로 만들어진 프로그램을

디버거로 오류를 찾아내는것은 어렵다. 그런 이유로 고급언어에서는 통합환경 내

에서 자체적으로 오류를 찾도록해주는 자체 디버거를 가지고 있는 형편이다.


        이 디버거는 예전의 어셈블리어로 프로그램을 작성하던때에 진가를 발휘

하던 프로그램이었으나 지금은 그런  목적보다도 컴퓨터의 시스템을 직접 조작하

는데 많이 쓰인다.


        디버거를 기동하고 디버거 프롬프트 상에서  '?' 를 입력하면 다음과 같

은 도움말이 출력된다.



+-------------------------------------------------------------------------+

|       -?                                                               |

|       assemble     A [address]                                          |

|       compare      C range address                                      |

|       dump         D [range]                                            |

|       enter        E address [list]                                     |

|       fill         F range list                                         |

|       go           G [=address] [addresses]                             |

|       hex          H value1 value2                                      |

|       input        I port                                               |

|       load         L [address] [drive] [firstsector] [number]           |

|       move         M range address                                      |

|       name         N [pathname] [arglist]                               |

|       output       O port byte                                          |

|       proceed      P [=address] [number]                                |

|       quit         Q                                                    |

|       register     R [register]                                         |

|       search       S range list                                         |

|       trace        T [=address] [value]                                 |

|       unassemble   U [range]                                            |

|       write        W [address] [drive] [firstsector] [number]           |

|       allocate expanded memory        XA [#pages]                       |

|       deallocate expanded memory      XD [handle]                       |

|       map expanded memory pages       XM [Lpage] [Ppage] [handle]       |

|       display expanded memory status  XS                                |

|       -                                                                 |

+-------------------------------------------------------------------------+



        1.      A (어셈블)

A 명령은 어셈블하여 메모리에 직접 기계어 코드를 만들다. 다만 1행마다 어셈블

하는 기능이므로 레이블을 사용할 수는 없다.


형식)   A [어드레스]


어드레스는 메모리내의 오프셋번지 또는 세그먼트와 오프셋의 번지이다.

세그먼트번지를 생략하면 현재의  CS 레지스터가 세그먼트값이되고 IP레지스터의

값이 오프셋 번지가 된다.



-A 100 

154D:0100 MOV AH,4C 

154D:0102 INT 21 

154D:0104

    .

    .




        2.      C (비교)

C 명령은 메모리의 내용을 비교하고 달라져 있는 부분을 표시한다.

세그먼트가 생략된 경우에는 DS 세그먼트의 값으로 정해진다.


형식)   C [개시어드레스 종료어드레스]

        C [개시어드레스] L [길이]


1234:0000 ∼ 1234:000F 까지를 현재의 DS:0010 부터 시작해서

비교한다.


-c 1234:0 f 10 

1234:0000  ED  B1  154D:0010

1234:0001  75  0F  154D:0011

1234:0002  05  17  154D:0012

1234:0003  E8  03  154D:0013

1234:0004  26  B1  154D:0014

1234:0005  00  0F  154D:0015

1234:0006  EB  FA  154D:0016

1234:0007  03  0E  154D:0017

1234:0008  E8  01  154D:0018

1234:0009  B0  01  154D:0019

1234:000B  72  00  154D:001B

1234:000C  08  02  154D:001C

1234:000D  5A  FF  154D:001D

1234:000E  E8  FF  154D:001E

1234:000F  05  FF  154D:001F

-


1234:0000 부터 10H개 만큼 현재의 DS:0010 부터 비교한다.

이것은 결과적으로 위와 같은 내용을 출력한다.


-c 1234:0 l10 10 

1234:0000  ED  B1  154D:0010

1234:0001  75  0F  154D:0011

1234:0002  05  17  154D:0012

1234:0003  E8  03  154D:0013

1234:0004  26  B1  154D:0014

1234:0005  00  0F  154D:0015

1234:0006  EB  FA  154D:0016

1234:0007  03  0E  154D:0017

1234:0008  E8  01  154D:0018

1234:0009  B0  01  154D:0019

1234:000B  72  00  154D:001B

1234:000C  08  02  154D:001C

1234:000D  5A  FF  154D:001D

1234:000E  E8  FF  154D:001E

1234:000F  05  FF  154D:001F

-




        3.      D (덤프)

D 명령은 메모리의 내용을 16진수와 아스키문자로 나타낸다.

디폴트 세그먼트는 DS세그먼트이고 한번에 128바이트를 출력한다. 또한 덤프후에

또다시 D 명령을 치면 연속해서 메모리의 내용을 출력한다.

덤프명령은 아스키 문자도 출력하는데 아스키 128번 이후의 문자는 출력하지 못

하고 '.' 으로 표현한다. 그런 이유로 한글은 출력해도 알아볼수가 없다.


형식)   D

        D [어드레스]


-D 

154D:0100  0F 00 B9 8A FF F3 AE 47-61 03 1F 8B C3 48 12 B1   .......Ga....H..

154D:0110  04 8B C6 F7 0A 0A D0 D3-48 DA 2B D0 34 00 3C 15   ........H.+.4.<.

154D:0120  00 DB D2 D3 E0 03 F0 8E-DA 8B C7 16 C2 B6 01 16   ................

154D:0130  C0 16 F8 8E C2 AC 8A D0-00 00 4E AD 8B C8 46 8A   ..........N...F.

154D:0140  C2 24 FE 3C B0 75 05 AC-F3 AA A0 0A EB 06 3C B2   .$.<.u........<.

154D:0150  75 6D 6D 13 A8 01 50 14-74 B1 BE 32 01 8D 8B 1E   umm...P.t..2....

154D:0160  8E FC 12 A8 33 D2 29 E3-13 8B C2 03 C3 69 02 00   ....3.)......i..

154D:0170  0B F8 83 FF FF 74 11 26-01 1D E2 F3 81 00 94 FA   .....t.&........


오프셋번지 FFH 이후의 메모리의 내용을 출력한다.

-D FF 

154D:00F0                                               00                  .

154D:0100  0F 00 B9 8A FF F3 AE 47-61 03 1F 8B C3 48 12 B1   .......Ga....H..

154D:0110  04 8B C6 F7 0A 0A D0 D3-48 DA 2B D0 34 00 3C 15   ........H.+.4.<.

154D:0120  00 DB D2 D3 E0 03 F0 8E-DA 8B C7 16 C2 B6 01 16   ................

154D:0130  C0 16 F8 8E C2 AC 8A D0-00 00 4E AD 8B C8 46 8A   ..........N...F.

154D:0140  C2 24 FE 3C B0 75 05 AC-F3 AA A0 0A EB 06 3C B2   .$.<.u........<.

154D:0150  75 6D 6D 13 A8 01 50 14-74 B1 BE 32 01 8D 8B 1E   umm...P.t..2....

154D:0160  8E FC 12 A8 33 D2 29 E3-13 8B C2 03 C3 69 02 00   ....3.)......i..

154D:0170  0B F8 83 FF FF 74 11 26-01 1D E2 F3 81 00 94      .....t.&.......


특정 메모리번지인 1100:0000 의 메모리의 내용을 출력한다.(세그먼트 값과

오프셋 값을 설정한다.)

-D 1100:0 

1100:0000  C3 C6 06 22 4A 00 E8 D0-FF 33 DB BF 07 57 E8 E8   ..."J....3...W..

1100:0010  EE 74 1F 8B 2E 1C 4A 57-53 E8 B3 F2 5B 5F 89 15   .t....JWS...[_..

1100:0020  89 45 02 83 C7 05 43 83-FB 0B 75 E2 BA F4 4B E9   .E....C...u...K.

1100:0030  D8 F5 89 1E AC 56 8B CB-E3 12 BF 07 57 1E 26 C5   .....V......W.&.

1100:0040  35 83 C7 04 A4 C6 44 FF-CC E2 F3 1F 8B 16 8E 4A   5.....D........J

1100:0050  89 16 1C 4A 8B 16 90 4A-89 16 1E 4A C7 06 AE 56   ...J...J...J...V

1100:0060  01 00 E9 EC FD B4 37 32-C0 CD 21 2E 88 16 75 56   ......72..!...uV

1100:0070  AC E8 C4 F6 74 05 E8 D2-F6 75 F5 4E C3 E8 08 F0   ....t....u.N....

-




        4.     E (엔터)

메모리의 내용을 세트한다.

이는 새로이 특정 메모리의 내용을 설정하는것을 말한다.


형식)   E[개시어드레스 리스트]


-e B800:0100                           ----- ①

B800:0100 20.31                        ----- ②


① EB800:0100 의 내용을 출력시킨다.

② 20H 의 값을 '31' 로 바꾼다.


이명령은 또한 메모리에 문자열의 입력도 가능하다.

EB800:100 의 번지에 문자열을 입력한다. 중간중간의 '7' 은 문자의 속성이다.

결과적으로 화면 우측 상단에 'I AM A BOY' 라고 출력된다.


-e B800:100 'I' 7 ' ' 7 'A' 7 'M' 7 ' ' 7 'A' 7 ' ' 7 'B' 7 'O' 7 'Y' 

-                                               I AM A BOY




        5.     F (채움)

F 명령은 메모리를 지정한 데이타로 채운다.


형식)   F[어드레스 리스트]


-f b800:0 ff 31                         ----- ①

-f b800:0 ff 31,32,33                   ----- ②

-f b800:0 ff 'i am a boy'               ----- ③

-


① B800:0000 ∼ B800:00FF 까지 31H 로 채운다.

② B800:0000 ∼ B800:00FF 까지 31H,32H,33H 로 채운다.

③ B800:0000 ∼ B800:00FF 까지 'i am a boy' 로 채운다.


또한 이 명령을 이용해서 메모리의 특정 부분의 데이타를 소거하는데 쓸수있다.


-fb800:0 ffff 0         -----  B800:0000 ∼ B800:FFFF 까지를 0H 로 채운다.




        6.     G (실행개시)

G 명령은 메모리에 로드된 프로그램의 실행을 개시한다.


형식)   G                               ---- ①

        G [=시작점 중지점]              ---- ②


① 현재의 CS:IP 로부터 프로그램을 시작한다.

② 시작번지에서 중지번지 바로 앞번지까지 프로그램이 진행된다.


C:\ASM>debug small.com 

-g 

Ex) small file1 file2

Program terminated normally

-


C:\ASM>debug small.com 

-g=100 215 

Ex) small file1 file2

AX=0924  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=0000  SI=0000  DI=0000

DS=155E  ES=155E  SS=155E  CS=155E  IP=0215   NV UP EI PL ZR NA PE NC

155E:0215 B44C          MOV     AH,4C

-g 


Program terminated normally

-




다음은 디버거로 웜부팅을 해보는 재미있는 예이다.

부트 스트랩 로더를 기동하는 프로그램을 CS:100H 로 복사하고 G 명령으로

실행 시켜본다.


C:\ASM>debug 

-m ffff:0 f 100 

-d 100 

154D:0100  EA F4 04 A6 02 30 34 2F-33 30 2F 39 30 00 FC 00   .....04/30/90...

154D:0110  04 8B C6 F7 0A 0A D0 D3-48 DA 2B D0 34 00 3C 15   ........H.+.4.<.

154D:0120  00 DB D2 D3 E0 03 F0 8E-DA 8B C7 16 C2 B6 01 16   ................

154D:0130  C0 16 F8 8E C2 AC 8A D0-00 00 4E AD 8B C8 46 8A   ..........N...F.

154D:0140  C2 24 FE 3C B0 75 05 AC-F3 AA A0 0A EB 06 3C B2   .$.<.u........<.

154D:0150  75 6D 6D 13 A8 01 50 14-74 B1 BE 32 01 8D 8B 1E   umm...P.t..2....

154D:0160  8E FC 12 A8 33 D2 29 E3-13 8B C2 03 C3 69 02 00   ....3.)......i..

154D:0170  0B F8 83 FF FF 74 11 26-01 1D E2 F3 81 00 94 FA   .....t.&........

-g 




        7.     H (합과차)

H 명령은 16진수끼리의  합과 차를 계산하여 표시해준다.

좌측의 값이 합이고 우측의 값이 차이다.


형식)   H[값1 값2]


-H 12 11 

0023  0001

-H FF00 EF12 

EE12  0FEE

-H 23 12 

0035  0011

-




        8.     I (인풋)

I 명령은 I/O PORT 에서 1 바이트의 데이타를 입력하여 표시한다.

8086의 포트 어드레스는 0H ∼ 0FFFFH 의 범위에 있기때문에 포트 어드레스의

지정도 16비트로 한다.


형식)   I[포트어드레스]




        9.     L (로드)

L 명령은 화일이나 디스크의 내용을 메모리에 로드한다.


형식)   L [어드레스]                                    ---- ①

        L [어드레스 드라이브 레코드번호 섹터수]         ---- ②


① 화일을 로드할때 쓰인다. 단 FCB 에 세트된 이름이 로드 되므로 사전에

   N 명령을 하여야한다.

   어드레스를 생략하면 CS:100H 에서 로드된다.

② 디스크의 특정 섹터를 로드할때 쓰인다.


+--------------------+-------------------------------------------+

|       어드레스     |       로드 시키는 메모리의 번지           |

+--------------------+-------------------------------------------+

|       드라이브     |       0=A,1=B,2=C,3=D 드라이브            |

+--------------------+-------------------------------------------+

|       레코드번호   |       읽는 디스크의 레코드 번호           |

+--------------------+-------------------------------------------+

|       섹터수       |       읽는 디스크의 섹터수                |

+--------------------+-------------------------------------------+


다음은 디버거 내에서 'small.com' 을 로드하는 예이다.

C:\ASM>debug 

-n small.com 

-l 

-d 

155E:0100  A0 80 00 3C 00 75 22 EB-16 45 78 29 20 73 6D 61   ...<.u"..Ex) sma

155E:0110  6C 6C 20 66 69 6C 65 31-20 66 69 6C 65 32 24 B4   ll file1 file2$.

155E:0120  09 BA 09 01 CD 21 E9 EC-00 8A 0E 80 00 B5 00 49   .....!.........I

155E:0130  49 BE 82 00 BF AC 02 AC-3C 20 74 03 AA E2 F8 B0   I.......< t.....

155E:0140  00 AA BF D5 02 F3 A4 B0-00 AA B4 3D BA AC 02 B0   ...........=....

155E:0150  00 CD 21 72 5E A3 A8 02-B4 3D BA D5 02 B0 00 CD   ..!r^....=......

155E:0160  21 72 1B EB 0F 41 6C 72-65 61 64 79 20 65 78 69   !r...Already exi

155E:0170  73 74 2E 24 B4 09 BA 65-01 CD 21 E9 97 00 B4 3C   st.$...e..!....<

-


다음은 하드 디스크(C 드라이브) 의 부트 영역을 로드한 예이다.

C:\ASM>debug 

-l 100 2 0 1 

-d 

154D:0100  EB 3C 90 4D 53 44 4F 53-35 2E 30 00 02 08 01 00   .<.MSDOS5.0.....

154D:0110  02 00 02 00 00 F8 A2 00-2E 00 08 00 2E 00 00 00   ................

154D:0120  72 10 05 00 80 00 29 79-59 E8 1C 20 20 20 20 20   r.....)yY..

154D:0130  20 20 20 20 20 20 46 41-54 31 36 20 20 20 FA 33         FAT16   .3

154D:0140  C0 8E D0 BC 00 7C 16 07-BB 78 00 36 C5 37 1E 56   .....|...x.6.7.V

154D:0150  16 53 BF 3E 7C B9 0B 00-FC F3 A4 06 1F C6 45 FE   .S.>|.........E.

154D:0160  0F 8B 0E 18 7C 88 4D F9-89 47 02 C7 07 3E 7C FB   ....|.M..G...>|.

154D:0170  CD 13 72 79 33 C0 39 06-13 7C 74 08 8B 0E 13 7C   ..ry3.9..|t....|

-




        10.    M (무브)

M 명령은 메모리의 내용을 다른 위치에 복사한다.


형식)   M [범위 번지]


다은은 'small.com' 의 내용중에 'Ex) small file1 file2' 의 부분을

다른 번지로 복사시키는 예이다.


C:\ASM>debug small.com 

-d 

155E:0100  A0 80 00 3C 00 75 22 EB-16 45 78 29 20 73 6D 61   ...<.u"..Ex) sma

155E:0110  6C 6C 20 66 69 6C 65 31-20 66 69 6C 65 32 24 B4   ll file1 file2$.

155E:0120  09 BA 09 01 CD 21 E9 EC-00 8A 0E 80 00 B5 00 49   .....!.........I

155E:0130  49 BE 82 00 BF AC 02 AC-3C 20 74 03 AA E2 F8 B0   I.......< t.....

155E:0140  00 AA BF D5 02 F3 A4 B0-00 AA B4 3D BA AC 02 B0   ...........=....

155E:0150  00 CD 21 72 5E A3 A8 02-B4 3D BA D5 02 B0 00 CD   ..!r^....=......

155E:0160  21 72 1B EB 0F 41 6C 72-65 61 64 79 20 65 78 69   !r...Already exi

155E:0170  73 74 2E 24 B4 09 BA 65-01 CD 21 E9 97 00 B4 3C   st.$...e..!....<

-m 109 11d 140 

-d 100 

155E:0100  A0 80 00 3C 00 75 22 EB-16 45 78 29 20 73 6D 61   ...<.u"..Ex) sma

155E:0110  6C 6C 20 66 69 6C 65 31-20 66 69 6C 65 32 24 B4   ll file1 file2$.

155E:0120  09 BA 09 01 CD 21 E9 EC-00 8A 0E 80 00 B5 00 49   .....!.........I

155E:0130  49 BE 82 00 BF AC 02 AC-3C 20 74 03 AA E2 F8 B0   I.......< t.....

155E:0140  45 78 29 20 73 6D 61 6C-6C 20 66 69 6C 65 31 20   Ex) small file1

155E:0150  66 69 6C 65 32 A3 A8 02-B4 3D BA D5 02 B0 00 CD   file2....=......

155E:0160  21 72 1B EB 0F 41 6C 72-65 61 64 79 20 65 78 69   !r...Already exi

155E:0170  73 74 2E 24 B4 09 BA 65-01 CD 21 E9 97 00 B4 3C   st.$...e..!....<

-



 [25] 제목 : 디버그(DEBUG.EXE) 정리 -후편-
 올린이 : 영원의별(이세원  )    95/01/10 20:33    읽음 : 293  관련자료 없음



        11.    N (네임)

N 명령은 실행파일의 이름을 수정하거나 파라메터를 지정한다.

결과로 FCB가 세트된다.


+----------------------------------------------------------+

|       FCB                                                |

+----------------------------------------------------------+

|       CS:5C   첫 번째 파일을 위한 파일 제어블럭          |

|       CS:6C   두 번째 파일을 위한 파일 제어블럭          |

|       CS:80   파라메터 길이                              |

|       CS:81   파라메터 시작 위치                         |

+----------------------------------------------------------+


형식)   N [패스네임 파라메터]


다음은 디버거 내에서 'small.asm' 을 어셈블하고 링크하는 과정이다.


C:\ASM>debug 

-n masm.exe 

-l 

-n small; 

-g 

Microsoft (R) Macro Assembler Version 5.00

Copyright (C) Microsoft Corp 1981-1985, 1987.  All rights reserved.



  51448 + 383976 Bytes symbol space free


      0 Warning Errors

      0 Severe  Errors


Program terminated normally

-n link.exe 

-l 

-n small; 

-g 


Microsoft (R) Overlay Linker  Version 3.60

Copyright (C) Microsoft Corp 1983-1987.  All rights reserved.


LINK : warning L4021: no stack segment


Program terminated normally

-n c:\dos\exe2bin.exe 

-l 

-n small.exe 

-g 


Program terminated normally

-




        12.    O (아웃풋)

O 명령은 I/O 포트에 데이타를 출력한다.


형식)   O [포트어드레스 바이트데이타]




        13.    P (진행)

P 명령은 프로그램을 로드한 상태에서 실제로 프로그램을 한명령씩 실행 시키

는 것이다. 따라서 오히려 T 명령보다 정확하게 프로그램을 추적할수 있다.


형식)   P                               ---- ①

        P [=어드레스]                   ---- ②

        P [=어드레스,넘버]              ---- ③

        P [넘버]                        ---- ④


① 현재의 CS:IP 에서 실행 시킨다.

② IP 의 값을 지정해준 번지에서부터 실행한다.

③ IP 의 값과 실행시킬 명령의 갯수만큼 실행한다.

④ 실행시킬 명령의 갯수만큼 실행한다.


C:\ASM>debug small.com 

-p 


AX=0000  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000

DS=155E  ES=155E  SS=155E  CS=155E  IP=0103   NV UP EI PL NZ NA PO NC

155E:0103 3C00          CMP     AL,00

-p=110 


AX=0000  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0002

DS=155E  ES=155E  SS=155E  CS=155E  IP=0115   NV UP EI PL ZR NA PE NC

155E:0115 6C            DB      6C

-p=100 


AX=0900  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=C640  SI=0000  DI=0003

DS=155E  ES=155E  SS=155E  CS=155E  IP=0103   NV UP EI PL ZR NA PE NC

155E:0103 3C00          CMP     AL,00

-


-p=110,4 

AX=0000  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0002

DS=155E  ES=155E  SS=155E  CS=155E  IP=0115   NV UP EI PL ZR NA PE NC

155E:0115 6C            DB      6C


AX=0000  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0003

DS=155E  ES=155E  SS=155E  CS=155E  IP=0119   NV UP EI PL ZR NA PE NC

155E:0119 66            DB      66


AX=0000  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=C640  SI=0000  DI=0003

DS=155E  ES=155E  SS=155E  CS=155E  IP=0121   OV UP EI PL NZ NA PE CY

155E:0121 BA0901        MOV     DX,0109


AX=0000  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=C640  SI=0000  DI=0003

DS=155E  ES=155E  SS=155E  CS=155E  IP=0124   OV UP EI PL NZ NA PE CY

155E:0124 CD21          INT     21

-


-p4 


AX=0900  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=C640  SI=0000  DI=0003

DS=155E  ES=155E  SS=155E  CS=155E  IP=0105   NV UP EI PL ZR NA PE NC

155E:0105 7522          JNZ     0129


AX=0900  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=C640  SI=0000  DI=0003

DS=155E  ES=155E  SS=155E  CS=155E  IP=0107   NV UP EI PL ZR NA PE NC

155E:0107 EB16          JMP     011F


AX=0900  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=C640  SI=0000  DI=0003

DS=155E  ES=155E  SS=155E  CS=155E  IP=011F   NV UP EI PL ZR NA PE NC

155E:011F B409          MOV     AH,09


AX=0900  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=C640  SI=0000  DI=0003

DS=155E  ES=155E  SS=155E  CS=155E  IP=0121   NV UP EI PL ZR NA PE NC

155E:0121 BA0901        MOV     DX,0109

-




        14.    Q (종료)

Q 명령은 디버거를 종료하고 도스로 빠져나간다.

Ctrl-C 또는 Ctrl-Break 으로는 빠져나갈 수 없다.


형식)   Q




        15.     R (레지스터)

R 명령은 현재의 레지스터의 내용을 보여준다.


형식)   R                               ---- ①

        R [레지스터]                    ---- ②

        R [F]                           ---- ③

① 단지 레지스터의 내용을 나타낸다.

② 레지스터의 내용을 나타내고 새로이 설정할 수 있다.

   레지스터는 AX,BX,CX,DX,SP,BP,SI,DI,DS,ES,SS,CS,IP,이다.

③ F 는 프래그 레지스터이다.

   프래그
 治뵀痼 각 프래그가 나타내는것은 다음과 같다.

   만약 프래그를 변경하려면 새로이 코드를 입력한다.


+----------------------------+----------------+---------------+

|       Flag name            |      설정      |      해제     |

+----------------------------+----------------+---------------+

|       Overflow             |       OV       |       NV      |

|       Drection             |       DN       |       UP      |

|       Interrupt            |       EL       |       DL      |

|       Sign                 |       NG       |       PL      |

|       Zero                 |       ZR       |       NZ      |

|       Auxiliary Carry      |       AC       |       NA      |

|       Parity               |       PE       |       PO      |

|       Carry                |       CY       |       NC      |

+----------------------------+----------------+---------------+



C:\ASM>debug small.com 

-r 

AX=0000  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000

DS=155E  ES=155E  SS=155E  CS=155E  IP=0100   NV UP EI PL NZ NA PO NC

155E:0100 A08000        MOV     AL,[0080]                          DS:0080=00

-r ax            ---- ①

AX 0000           ---- ②

:100             ---- ③

-r 

AX=0100  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000

DS=155E  ES=155E  SS=155E  CS=155E  IP=0100   NV UP EI PL NZ NA PO NC

155E:0100 A08000        MOV     AL,[0080]                          DS:0080=00

-r f             ---- ④

NV UP EI PL NZ NA PO NC  -ov cy          ---- ⑤

-r f 

OV UP EI PL NZ NA PO CY  -

-r 

AX=0100  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000

DS=155E  ES=155E  SS=155E  CS=155E  IP=0100   OV UP EI PL NZ NA PO CY

155E:0100 A08000        MOV     AL,[0080]                          DS:0080=00

-


① AX 레지스터의 내용을 보인다.

② AX 레지스터의 내용

③ 새로이 AX 레지스터의 내용을 설정한다.

④ 프래그 레지스터의 내용을 보인다.

⑤ 오버플로우 프래그와 캐리 플래그의 내용을 변경한다.




        16.     S (검색)

S 명령은 메모리내의 특정한 데이타를 찾아내고 발견된 어드레스를 표시한다.


형식)   S ['문자,문자열']


앞에서 만들어 보았던 SMALL.COM 의 메시지인 'Ex) small file1 file2' 중에서

'small' 을 찾아내보자.


C:\ASM>small 

Ex) small file1 file2                   ---- ①

C:\ASM>debug small.com                 ---- ②

-r                                     ---- ③

AX=0000  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000

DS=155E  ES=155E  SS=155E  CS=155E  IP=0100   NV UP EI PL NZ NA PO NC

155E:0100 A08000        MOV     AL,[0080]                          DS:0080=00

-s 155e:100 'small'                    ---- ④

155E:010D                               ---- ⑤

-


① small.com 의 메시지이다.

② small.com 을 디버거로 로드한다.

③ R 명령으로 CS:IP 의 값을 알아낸다.

④ 155E:100(CS:IP) 에서부터 'small' 의 문자열을 검색한다.

⑤ 문자열을 찾아낸 메모리의 번지




        17.     T (트레이스)

T 명령은 프로그램을 1 명령씩 실행하고 그때의 레지스터의 값,프래그의 상태

,명령을 표시해준다.


형식)   T                                ---- ①

        T [=개시어드레스]                ---- ②

        T [=개시어드레스 횟수]           ---- ③


① 현재의 CS:IP 의 값에서 1 명령씩 수행한다.

② 개시 어드레스에서 부터 1 면령씩 수행한다.

③ 개시 어드레스에서 부터 횟수만큼 수행한다.


C:\ASM>debug small.com 

-t 


AX=0000  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000

DS=155E  ES=155E  SS=155E  CS=155E  IP=0103   NV UP EI PL NZ NA PO NC

155E:0103 3C00          CMP     AL,00

-t 


AX=0000  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000

DS=155E  ES=155E  SS=155E  CS=155E  IP=0105   NV UP EI PL ZR NA PE NC

155E:0105 7522          JNZ     0129

-t 


AX=0000  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000

DS=155E  ES=155E  SS=155E  CS=155E  IP=0107   NV UP EI PL ZR NA PE NC

155E:0107 EB16          JMP     011F

-t 


AX=0000  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000

DS=155E  ES=155E  SS=155E  CS=155E  IP=011F   NV UP EI PL ZR NA PE NC

155E:011F B409          MOV     AH,09

-



-t=110 


AX=0900  BX=0000  CX=01FF  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0002

DS=155E  ES=155E  SS=155E  CS=155E  IP=0115   NV UP EI PL ZR NA PE NC

155E:0115 6C            DB      6C

-



-t=110 5 


AX=0900  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=0000  SI=0000  DI=0001

DS=155E  ES=155E  SS=155E  CS=155E  IP=0111   NV UP EI PL ZR NA PE NC

155E:0111 6C            DB      6C


AX=0900  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=0000  SI=0000  DI=0002

DS=155E  ES=155E  SS=155E  CS=155E  IP=0112   NV UP EI PL ZR NA PE NC

155E:0112 206669        AND     [BP+69],AH                         SS:0069=00


AX=0900  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=0000  SI=0000  DI=0002

DS=155E  ES=155E  SS=155E  CS=155E  IP=0115   NV UP EI PL ZR NA PE NC

155E:0115 6C            DB      6C


AX=0900  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=0000  SI=0000  DI=0003

DS=155E  ES=155E  SS=155E  CS=155E  IP=0116   NV UP EI PL ZR NA PE NC

155E:0116 65            DB      65


AX=0900  BX=0000  CX=01FF  DX=0109  SP=FFFE  BP=0000  SI=0000  DI=0003

DS=155E  ES=155E  SS=155E  CS=155E  IP=0119   NV UP EI NG NZ NA PE NC

155E:0119 66            DB      66

-




        18.     U (역 어셈블)

U 명령은 기계어를 역 어셈블해준다.

이 명령에 의해 역 어셈블된 코드는 정확한 프로그램의 코드가 아니다.

디버거는 명령코드인지 아니면 데이타 인지를 가리지 않고 무조건 역어셈블

하기 때문이다. 따라서 대게 점프 명령이후의 역어셈코드는 데이타인 경우가

많다.


형식)   U                               ---- ①

        U [범위]                        ---- ②

        U [개시어드레스]                ---- ③


① CS:IP 로부터 32바이트를 역어셈블한다.

② 범위로 정해진만큼 역어셈블한다.

③ 개시 어드레스로부터 이후 32바이트를 역어셈블한다.



C:\ASM>debug small.com 


CS:IP 로부터 32바이트를 역 어셈블한다.

-U 

155E:0100 A08000        MOV     AL,[0080]

155E:0103 3C00          CMP     AL,00

155E:0105 7522          JNZ     0129

155E:0107 EB16          JMP     011F

155E:0109 45            INC     BP

155E:010A 7829          JS      0135

155E:010C 20736D        AND     [BP+DI+6D],DH

155E:010F 61            DB      61

155E:0110 6C            DB      6C

155E:0111 6C            DB      6C

155E:0112 206669        AND     [BP+69],AH

155E:0115 6C            DB      6C

155E:0116 65            DB      65

155E:0117 3120          XOR     [BX+SI],SP

155E:0119 66            DB      66

155E:011A 69            DB      69

155E:011B 6C            DB      6C

155E:011C 65            DB      65

155E:011D 3224          XOR     AH,[SI]

155E:011F B409          MOV     AH,09

-


CS:100 ∼ 110 의 내용을 역 어셈블한다.

-u 100 110 

155E:0100 A08000        MOV     AL,[0080]

155E:0103 3C00          CMP     AL,00

155E:0105 7522          JNZ     0129

155E:0107 EB16          JMP     011F

155E:0109 45            INC     BP

155E:010A 7829          JS      0135

155E:010C 20736D        AND     [BP+DI+6D],DH

155E:010F 61            DB      61

155E:0110 6C            DB      6C

-


CS:200 이후 32바이트를 역 어셈블한다.

-u 200 

155E:0200 73E4          JNB     01E6

155E:0202 B43E          MOV     AH,3E

155E:0204 8B1EA802      MOV     BX,[02A8]

155E:0208 CD21          INT     21

155E:020A B43E          MOV     AH,3E

155E:020C 8B1EAA02      MOV     BX,[02AA]

155E:0210 CD21          INT     21

155E:0212 E85300        CALL    0268

155E:0215 B44C          MOV     AH,4C

155E:0217 CD21          INT     21

155E:0219 EB0C          JMP     0227

155E:021B 52            PUSH    DX

155E:021C 65            DB      65

155E:021D 61            DB      61

155E:021E 64            DB      64

155E:021F 206572        AND     [DI+72],AH

-




        19.     W (쓰기)

W 명령은 메모리의 내용을 화일이나 임의의 섹터에 써 넣는다.


형식)   W [어드레스]

        W [어드레스 드라이브번호 레코드번호 섹터수]


다음은 디버그를 이용해서 컴퓨터를 리부팅 시켜주는 잘 알려진 프로그램을

만들어본다. 이 부팅은 웜 부팅이고 메모리를 검사하지 않는다.


C:\ASM>debug reboot.com 

File not found


-a 100 

154D:0100 mov   ax,40                  ---- ①

154D:0103 mov   ds,ax                  ---- ②

154D:0105 mov   ax,1234                ---- ③

154D:0108 mov   [72],ax                ---- ④

154D:010B jmp   ffff:0                 ---- ⑤

154D:0110 

-r cx                                  ---- ⑥

CX 0000

:10                                    ---- ⑦

-w 

Writing 00010 bytes

-q 


① AX 에 40H 를 넣는다.

② DS 에 AX 를 복사한다.

   이것은 결국 DS 를 설정하는 작업이다. (DS=40H)

③ AX 에 1234H 를 넣는다.

④ DS:72H 에 1234H 를 넣는다.

   이것은 DS:72H 에 워드 1234H 를 넣으면 웜부팅시에 메모리 검사를 생략하기

   때문이다.

⑤ 부트 스트랩 프로그램으로 분기

⑥ CX 레지스터를 새로 작성하기 위함이다.

⑦ CX 에 10H 를 넣는다.

   이것은 W 명령시에 CX 레지스터의 값 만큼 쓰기 때문이다.




이상입니다.

어땠어요? 괜찮았죠?

워드로 뽑아두시면 좋은 교재가 될것입니다.

안녕히계세요.



 [26] 제목 : [예고] 강좌를 시작하기 전에...
 올린이 : natali  (박선근  )    95/01/20 02:18    읽음 : 165  관련자료 없음


 안녕하세요.. 나탈리 박선근입니다.

 어셈블리 강좌를 시작하기 전에 알려드릴 것과 부탁하고픈 것이
 있어서 그 예고편을 올립니다.

 이번에 하게될 어셈블리 강좌는 어셈블리 소스를 분석하는 형식으로
 진행하게 됩니다. 그런데 강좌에 쓸 재료가 너무 부족한 형편입니다.
 구상하기는 바이러스 코드를 분석해 보려고 했는데 그 소스를 얻기가
 그리 쉬운 일이 아니군요.
 바이너리라도 있다면 좋겠지만 그것도 없어서 오늘 자료실을 이곳 저곳
 뒤졌습니다만 한 건도 못올렸습니다.

 강좌에 쓰일 만한 재료감을 갖고 계신 분은 제게 좀 보내 주세요.
 바이러스 코드가 아니라도 괜찮습니다. 다만, 그래픽 프로그램이나
 상업용 프로그램의 락 같은 것은 사절하겠습니다.

 보내주신 코드가 강좌에 쓰일지는 모릅니다. 그건 제가 코드를 보고
 강좌에 쓰일 만한 내용인가를 결정합니다.
 그럼 관심있는 분의 도움을 바라면서 이만...


 [28] 제목 : [강좌] 어셈블리 소스분석 <1>
 올린이 : natali  (박선근  )    95/01/24 19:14    읽음 : 451  관련자료 없음

=============================================================================

어셈블리 < 1 >  - Source 분석을 중심으로 -


                                             작성: 박선근(NATALI), 1995.01.

* 간편화를 위해 경어를 사용하지 않았습니다.

* TASM 1.0 이상 또는 MASM 5.0이상의 어셈블러가 필요합니다.

-----------------------------------------------------------------------------

<> 어셈블리가 필요한 이유 <>

=============================================================================


[ 어셈블리의 효용성 ]


어셈블리어는 인간이 이해할 수 있는 유일한(?) 저수준 언어이다.

예전의 거의 대부분의 우수한 프로그램들은 어셈블리로 제작되었었고, 지금도 어셈

블리는 여전히 중요한 언어임에 틀림없다.

그러나 어셈블리어가 표현과 판독의 어려움이 있고 프로그램 개발에 투자해야 하는

노력의 비대함으로 점차적으로 그 소용이 미진해 지고 있는 것 또한 사실이다.

현재  고급언어 중에서 가장 최적의 코드를 생성하는 것으로 평가되는  C언어 역시

궁극에는 어셈블리의 범주에 포함됨에도 불구하고 마치 어셈블리가  C를 위해 있는

듯한 착각을 일으킬 만큼 어셈블리의 사용층은 엷어졌고,  대신 C의 사용층은 그만

큼 두터워 졌다.  그러나, 전혀 새로운 유형의 시스템과 언어가 나오지 않는 한 어

셈블리는 여전히 최적의 코드를 생성하는 언어로 존재할 것이고, 또 사용될 것이다

(현재의 모든 시스템은 0과 1의 표현에 의존하는 기계어를 그
 긆瓮 삼고있다

그런 점에서 어셈블리는 기계어를 대신하는 가장 간결한 코드를 생성한다. 그러나

만약 시스템이 인간의 말을 바로 이해하는 이른 바 '인공지능'이 현실화 된다면 더

이상 어셈블리를 사용하여 복잡한 프로그래밍을 할 이유는 없을 것이다. 말하는 것

이 곧 프로그램이 될테니까...)


많은 프로그램들이 C를 통해 구현되고 있다. 그러나 전문적인 프로그래머는 결코

C 하나에 의존하지 않는다. C가 편리하기는 하지만 결코 최적의 코드를 생성하지는

못한다는 것을 잘 알고 있기 때문이다. 그렇다고 해서 어셈블리만을 사용하지도 않

는다. 그건 너무나 비경제적이다.

가장 좋은 방법은, C와 어셈블리를 병용하는 것이다. 이런 것이 있다.

데이타베이스를 구축하는데 굳이 C나 어셈블리를 사용할 필요는 없다. 이미 데이타

베이스를 위한 전문적인 언어들이 있다.  클리퍼나 폭스프로 등의 쉽게 데이타베이

스를 구축할 수 있는 언어들이 개발되어 있는 것이다. 쉽게 할 수 있는데  굳이 어

렵게 할 이유는 없는 것이다.  그런데, 기계를 제어하는 프로그램을 만드는데 클리

퍼나 폭스프로 만을 사용할 수 있겠는가? 어려운 일이다. 이럴 땐 오히려 C나 어셈

블리를 사용하는 것이 경제적이다. 그렇다면, 데이타베이스내에서 기계를 제어해야

한다면 어떻게 해야 하는가?  데이타베이스 구축 부분은 데이타베이스를 위한 전용

언어를 사용하고,  기계제어하는  부분은 또 그에 적합한 언어를 사용하여 그 둘을

결합한다면 일은 보다 수월해 질 것이다.


C와 어셈블리를 병용하는 것도 이러한 내용과 맥락을 같이 한다.

보다 빠른 실행 속도가 요구되는 부분은 빠른 처리가 가능한 언어로,  그다지 속도

는 요구되지 않지만 어셈블리와 같은 복잡한 언어로는 구현하기 까다로운 부분은

표현이 보다 쉬운 언어로 작성하여 그 둘을 결합하는 것이다.


그런 점에서, 어셈블리는 어떤 언어보다도 빠른 처리를 가능하게 한다.  이것이 어

셈블리가 구현이 어려운 중에도 꾸준히 사용되고 있는 가장 유력한 이유이며, 어셈

블리의 효용성이 여기에 있는 것이다.



[ 어셈블리를 배우는데 필요한 것들 ]


본 강좌를 보시는 데는 아래의 사항을 준비하고 있으면 도움이 된다.


        - 디버거(Debugger)

          프로그래밍을 하는데 있어서 필수적인 것이다.

          자신의 손에 익은 디버거가 있다면 좋겠지만 그렇지 못하다면 먼저 디버

          거의 사용을 익히는 것이 좋겠다.

          디버거는 여러 종류가 있는데 가장 간단한 형태로는 도스에 함께 제공되

          는 debug가 있고, 볼랜드의 Turbo Debugger와 MS의 Code View 등이 가장

          많이 사용되고 있다. 이 강좌는 Turbo Debugger와 Code View가 구입하기

          어려운 점을 감안하여 도스의 debug를 표준 디버거로 사용할 것이다.

          debug의 상세한 사용 방법은 메뉴얼을 참고하시기 바라고,  이 강좌에서

          는 debug의 명령어 사용에 관한 일체의 설명을 하지 않는다.


        - 소서(Sourcer)

          이 프로그램은 어셈블리 source를 얻을 수 있도록 하는 역어셈블러이다.

          'V Communication'이라는 곳 (회사 이름이 정확한지 모르겠다)에서 나온

          프로그램인데 아마 쉐어일 것이다. 이 프로그램이 있다면 좋은 일이겠으

          나 없어도 상관없다. 역어셈블 코드는 도스의 debug를 통해서도 얻을 수

          있기 때문이다. debug로 역어셈블 코드를 얻는 방법은 따로 설명을 드리

          겠다.


        - 프린터(Printer)

          프린터가 없다면 상당히 곤란하다. 수백, 수천 line의 어셈블리 코드를

          화면만 통해서 보기는 무척 힘든 까닭이다. 한  화면에 나타낼 수 있는

          line의 수가 너무 작기 때문에 전체적인 윤곽을 잡기가 무척 어렵다.

          이 때는 프린트해 놓고 보아야 한다. 프린터가 없다면 비상한 기억력(?)

          과 어떤 유혹도 뿌리치고 컴퓨터 모니터에 집중할 수 있는 무서운(?) 인

          내력이 요구될 것이다.



[ debug를 통해 역어셈블 코드를 얻는 방법 ]


debug는 간이 어셈블러라고도 불릴 만큼 나름대로 독특한 기능을 보유하고 있다.

직접 어셈블리 코드를 써넣을 수 있을 뿐만 아니라 메모리의 내용을 역어셈블해 볼

수도 있다. 역어셈블된 코드를 화일이나 프린터로 출력하기 위해서는 도스의 필터

기능을 이용하면 된다.


        우선 다음과 같은 간단한 내용의 텍스트 화일을 작성해 두자.


        u 100 200               <- 모든 .COM프로그램은 100h번지에서 시작된다.

        q                       <- 반드시 'q(Q)'가 있어야 한다.

                                   그렇지 않으면 필터에서 빠져 나오질 못한다.


        이 화일의 이름을 "dump.dat"라고 하자.

        이제 명령행 상에서 다음과 같이 입력한다.


        C:\>debug command.com < dump.dat > command.a


        명령을 입력하고 잠시 기다렸다가 디렉토리를 보면 'command.a'가 새로

        생성되었을 것이다.

        이 화일의 내용을 보면 어셈블리 코드가 들었을 텐데, 이는 위의 일련의

        동작을 통해 필터처리된 command.com의 CS:0100h~CS:0200h 범위의 역어셈

        블리 코드이다. command.com의 모든 내용을 역어셈블 하자면 전체 프로그

        램의 크기 만큼 처리하면 될 것이다(하지만 이런 일은 하지 말기 바란다.

        아래에 실험을 위한 보다 작은 프로그램을 예로 들었다)


        이런 방법으로, 다소 답답한 감이 있지만 sourcer없이 debug를 통해 어셈

        블리 코드를 얻게 된다.



[ debug를 통해 역어셈블리 코드를 얻는 간단한 실험 ]


궁금증을 해소하기 위해 간단한 실험을 해보도록 하자. debug를 기동시키고 다음과

같이 입력한다.


        C:\>debug

        -a

        jmp     110

        db      'Hello, World!$'

        mov     dx,102

        mov     ah,9

        int     21

        mov     ax,4c00

        int     21

        <ENTER>

        -nhello.com

        -rcx

        1c

        -w

        -q


위의 내용을 모두 입력했다면 debug에서 빠져 나와 있을 것이다.

디렉토리를 보면 'hello.com'이 새로이 생성되어 있을 것인데, 이 프로그램은 실행

할 수 있다. 실행하면 화면에 'Hello, World!'를 출력한다.  위의 프로그램을 이젠

거꾸로 역어셈블 해보자.


        다음과 같은 내용의 'dump.dat'화일을 작성한다.


        u 100 11c

        q


        이제 도스상에서 다음을 입력하자.


        C:\>debug hello.com < dump.dat > hello.asm


        생성된 'hello.asm'을 보면 위에서 입력할 때와는 조금 다름을 알 수 있다

        다른 부분은  'Hello, World!$'를 찾을 수 없는 것인데 대신 'DB ...'라는

        내용이나 입력하지 않았던 명령등을 볼 수 있을 것이다.


        CS:0100 JMP     0110            ; 데이타 부분을 건너뜀      ----+

                                        ;                               |

        CS:0102 DEC     AX              ; 이 부분이 'Hello, World!$'    |

                DB      65              ;       "          "            |

                DB      6C              ;       "          "            |

                DB      6C              ;       "          "            |

                DB      6F              ;       "          "            |

                SUB     AL,20           ;       "          "            |

                PUSH    DI              ;       "          "            |

                DB      6F              ;       "          "            |

                JB      0179            ;       "          "            |

                DB      64              ;       "          "            |

                AND     [SI],SP         ;       "          "            |

                                        ;                               |

        CS:0110 MOV     DX,0102         ; 문자열 출력 부분          <---+

                MOV     AH,09

                INT     21


        CS:0117 MOV     AX,4C00         ; 종료처리

                INT     21


        이 프로그램의 경우는 직접 작성한 것이므로 정확히 데이타가 어디에서 어

        디까지라는 것을 알 수 있었지만 자신이 작성하지 않은 어떤 임의의  프로

        그램을 역어셈블한다면 그런 것들을 어떻게 알 수 있을까?


        힘든 일이지만, 그런 내용을 파악하는 것이 바로 '분석'의 묘미이다. 많은

        경우에 그러한 내용을 판단하는 것은  분석자의 직관(?)이나  경험에 의해

        파악된다. 위의 프로그램의 예에서는 'jmp'에 의해 아예 데이타 부분을 건

        너 뛰고 있다. 그리고, 도스의 문자열 출력 함수인 09h는 DX에  출력할 문

        자열의 옵셋을 가지며 그 문자열의  끝은 반드시 '$'이다.  이러한 점으로

        미루어 볼 때, 옵셋 102h에서 10Fh까지는 실행할 수 없는  데이타 임을 짐

        작하여 알 수 있다(정말일까?)


        물론, 현재로썬 필자가 이렇게 말하니 '그런것 같다..' 하는 생각을 할 것

        이다.  하지만 실전(?)에서는 간단한 문제가 아니다. 수천라인(어셈블리에

        서 수백라인 넘기기는 쉬운 일이다)의 코드에서  어떻게 간단할 수 있겠는

        가? 어려운 일이다.


[ 강좌를 시작하기 전의 문제점들... ]


본 강좌에서는 지금 그렇게 어려운 일을 해보려고 한다. 강좌가 어떻게 진행될지는

글을 쓰고 있는 본인으로써도 예측할 수 없다.


예고편에서 알려 드렸듯이, 분석해 볼만한 어셈블리 소스를 구하는 일이 그다지 쉬

운 일이 아니다(분석하기도 어려운데 재료마저 구하기 어렵다).  가장 구히기 쉬운

코드는  '바이러스'라고 생각되서(이건 저작권 문제에 크게 신경쓰지 않아도 된다)

몇분이 보내주신 바이러스 코드를 살펴 봤다. 그런데 필자가 바이러스에 대해 아는

게 없어서 인지는 모르겠으나 다음과 같은 생각을 하게 되었다.


        - 바이러스 코드들은 한결 같이 쓰잘데 없는(?) 기교를  많이 부려서 눈을

          어지럽게 했다(물론 숨기려는 의도가 있는 것들이니 그럴 것이다).


        - 우연인지,  아니면 바이러스 코드의 정통성(?)인지는 모르겠으나 비슷한

          형태가 많았다 (어떤 모범적인(?) 바이러스의 변종들이었던지...). 나름

          대로 조금씩 변화를  가지기는 했으나 그건 조금 장난친 것에 지나지 않

          았다(예를 들자면,  상주하는 코드의 크기를 조금 다르게 한다던가 내부

          적으로 사용하는 바이러스 인식 코드를 다르게 한 내용들.... 만약 모든

          바이러스가 이런 형편이라면 바이러스  만든 사람들은 더 늦기 전에  바

          이러스에 대한 생각일랑 싹 지우고 그  시간에 좀 더 유용한 프로그램을

          구상하는게 좋지 않을까?)


        - 크기가 적어도  2,000~3,000 바이트 정도가 되니 역어셈블  코드로는 약

          20~30페이지(프린트했을 때) 정도의  제법 큰 코드가 되었다 (이 정도면

          하나 분석하는데 몇일이 걸려야 할까? 그것도 온라인 강좌로...)


어떤 코드는 약 2~3페이지 정도의 작은 분량도 있었는데 그건 바이러스가 아니었다

바이러스라고 설명되어 있기는 했으나 일반적인  바이러스 코드가 가지는 특성들을

구비하지 못한 것들이었고, 분석할 만한 내용이 못되었다(너무 간단해서...)


강좌를 시작하기 전에 간추려 본 문제점들은 다음과 같다.


        - 바이러스 코드의 기교가 너무 혼란스러워서, 이런 것들을 강좌에서 다룰

          만큼 가치가 있을까 하는 의문이 생기며,  그런 기교들이 어셈블리에 대

          한 이해를 목적으로 하는 본 강좌의 목적과 어느 정도  부합되는가 하는

          당위성에 대한 회의(?).


        - 이런 바이러스 코드의 분석이 반드시 좋은 결과만을 줄 수 있을까?

          바이러스 코드를 살펴본 바로는 변종이 많은 것 같은데  이 강좌가 그런

          변종의 발생을 촉진하는 결과가 되지는 않을까?


        - 이 강좌가 온라인인 관계로, 20~30 페이지나 되는 분량의 어셈블리 코드

          가 너무 부담스럽다.


바이러스 코드를 분석한다고 할 때, 가장 우려되는 점은 두번째의 경우이다.

'해커'라는 말이 공공연히 나쁜 의미로 해석되고 말해지는 요즈음의 추세이고 보면

예측할 수 있는 가장 최악의 경우는  또다른 변종 바이러스가 나올 수 있다는 것이

다. 필자는 그런 결과를 원치 않는다.


그래서 본 강좌에서는 바이러스 코드에 대해서 만큼은 전체  소스를 소개하는 대신

부분적으로 사용된 기교들을 분석하는 방법을 취한다.  물론 바이러스 코드가 아니

라면 모든 내용에 대한 분석을 시도할 것이다.

본 강좌의 목적이 어셈블리를 배우고자 하는데 있으므로 부분적으로 발췌된 내용을

분석하더라도 그곳에서 얻을 것은 많다. 물론 이러한 방법은 전체적인 흐름에 대한

이해면에서는 다소의 부족한 점이 있을 것이다. 이점은 다른 소스를 분석하는 것으

로 대신할 것이다. 본 강좌를 보시는 분들은 이같은 필자의 고충(?)을 이해해 주셨

으면 고맙겠다.



 [29] 제목 : [목록] 어셈블리 강좌 목록입니다..
 올린이 : 아꾸    (하성욱  )    95/02/05 00:51    읽음 : 126  관련자료 없음


                  ASSEMBLER 강좌 목록
                ━━━━━━━━━━━━━━━━


     아이디   이름    제목                              게시판 번호
 ─────────────────────────────────
    까망벌레 정태식   [초급-기초] 맛보기...                    3
    까망벌레 정태식   [초급-기초] 메모리에 대해서           4~ 5
    까망벌레 정태식   [초급-기초] 레지스터에 대해서            6
    까망벌레 정태식   [초급-실습] 프로그램 하나(소스&설명   7~ 8
    까망벌레 정태식   [초급-보충] 스텍?                        9 
    까망벌레 정태식   [초급-실습] 프로그램 두울            10~11
    까망벌레 정태식   [초금-명령] MOV 에 대해                 12 
    까망벌레 정태식   [초급-실습] 프로그램 세엣               15 
    까망벌레 정태식   [초급-보충] 변수 선언에 대하여          16 
    까망벌레 정태식   [초급-보충] 주소지정방식에 대하여...    17 
    까망벌레 정태식   [초급-보충] 프로시져 및 스텍의 변화에   18
    까망벌레 정태식   [초급-보충] 논리연산에 대해서...        19 
    영원의별 이세원   디버그(DEBUG.EXE) 정리               23,25
    natali   박선근   [강좌] 어셈블리 소스분석 <1>            28
 ─────────────────────────────────

 2/4일짜 까지의 어셈블리 강좌란 목록입니다.


 [30] 제목 : [강좌] 어셈블리 소스분석 <2>
 올린이 : natali  (박선근  )    95/02/08 20:48    읽음 : 308  관련자료 없음

=============================================================================

어셈블리 < 2 >  - Source 분석을 중심으로 -


                                             작성: 박선근(NATALI), 1995.02.

* 간편화를 위해 경어를 사용하지 않았습니다.

* TASM 1.0 이상 또는 MASM 5.0이상의 어셈블러가 필요합니다.

-----------------------------------------------------------------------------

<> 첫번째 프로그램 - Disk Monitor <>

=============================================================================


[ 변명 ... ]


서비스 이용료가 연체된 줄 모르고 있다가 설연휴 지나고서야 알았습니다. 나우는

그 달 25일까지 이용료를 내지 않으면 다음 달 1일에 사용중지 된다는 군요.

음.. 전 그 사실을 전혀 모르고 있다가 연휴 끝나는 날 접속해보니... 흘~


이래저래 첫 강좌 올리고 근 2주일이 지나버렸군요.  이에 죄송한 말씀을 드리고,

강좌를 시작하기 전에 여러분께 한가지 당부하고 싶은 것이 있습니다.


근래 며칠 동안 이번 강좌의 내용에 대해 많은 생각을 했었습니다. '바이러스분석'

이라는 것 때문이었죠.  강좌의 서두에서 분명히 본 강좌의 목적이 '바이러스분석'

에 있는 것이 아니라  '다양한 어셈블리 표현을 익히는'데 있다는 것을 밝혔었습니

다만 많은 분들이 강좌의 목적에 대한 부분을 지나치신 것 같습니다.  바이러스 코

드를 분석하는 일은 본 강좌에서 주요 줄거리가 못됩니다.  제가 강좌를 위해서 프

로그램을 직접 작성하고 그 내용을  소개할 수도  있겠지만 그건 너무 많은 시간을

소모하게 되기 때문에 조금 편리한  방향에서 저작권 문제에 지장이 없는 바이러스

이야기가 나온 것입니다.  그런데 많은 분들이 제가 한번도 들어 본적이 없는 수많

은 바이러스에 대한 질문을 하셨고 또 그것들을 분석해 달라고 자료를 보내 주셨습

니다. 이번 일로 많은 분들이 바이러스에 대한 관심이 크다는 것과 저 또한 여러가

지를 알게 된 것도 사실이지만 그 보다는 '변형바이러스'에 대한 걱정이 더 큽니다

제가  살펴 본 바로는 우리나라에서 제작된 거의 대부분의 바이러스들이 외국의 것

을 변형한 것이었습니다. 모방은 창조의 어머니라지만 글쎄요... 이런 경우에도 그

런 말이 정당할 것 같지는 않습니다.

그래서 이런 저런 고민 끝에 본 강좌에서는 바이러스 코드를 분석하더라도 전체 내

용은 소개하지 않습니다. 특별하거나 독특한 표현이 있다면 그런 부분 만을 개별적

으로 소개할 것입니다. 바이러스 코드가 아닐 때는 전체 소스를 소개할 것입니다.


본 강좌의 목적이 '바이러스 분석'에 있지 않음을 다시 한번 분명히 밝히고 오해없

기를 바라며 두번째 강좌를 시작하겠습니다.



[ 어셈블리 프로그래밍 시의 주의점들 ]


어셈블리 프로그래밍은 여타의 고급 언어 프로그래밍에서는 신경쓰지 않아도 될 많

은 내용을 프로그래머가 일일이 신경써 주어야 한다. 이러한 내용은 보다 섬세하고

강력한 프로그램을 작성할 수 있는 요소가 되지만,  다른 면으로는 매우 성가신 문

제가 아닐 수 없다. 어셈블리 프로그래밍에서 발생할 수 있는 중요하면서도 성가신

몇가지 문제점들을 정리해 보았다.


- 스택(Stack)

  스택의 크기는 '적당한 것'이 좋다. 그러나 적당한 크기의 스택을 설정하기란 상

  당히 신경쓰이는 일이다. 그래서 일반적으로 어느 정도의 여유를 두고 스택을 결

  정하게 된다. 어떤 경우에, 스택이 모자란다고 해도 프로그램이 정상적으로 수행

  될 수가 있는데 이는 스택의 넘친 부분이 다행히 프로그램의 수행에 지장이 없었

  기 때문이다. 그러나 이것은 명백한 버그이며, 프로그램이 언제 심각한 에러를

  발생할지 모르는 잠재적인 폭주 가능성을 안고 있는 것이다. 이런 이유로 스택은

  '어느 정도 여유를 갖고' 결정되는 것이 안전하다고 할 수 있다. COM파일의 경우

  코드,데이타,스택이 모두 하나의 세그먼트 안에 있으므로 스택의 사용은 더욱 조

  심스럽게 행해져야 한다. 코드나 데이타 세그먼트의 크기가 커짐에  따라 그만큼

  스택의 크기가 줄어 들기 때문이다.


- 레지스터(Register)

  어셈블리 프로그래밍에서 레지스터의 사용은 빼놓을 수 없는 부분인 만큼 중요성

  또한 크다.  그러나 레지스터는 사용 빈도나 중요성에 비해 크기가 너무 작고 일

  시적이다.  그러므로 레지스터의 내용이 보관될 필요가 있고 다른 일을 수행하는

  과정에서 그 내용이 변경될 가능성이 있다면 그 전에 적절한 장소에 보관해 두어

  야 한다.


- 변수/데이타의 참조

  다음에 나타낸 어셈블리 표현들에서 DX가 가지는 값은 무엇일까?


        VALUE   DW      1234H


    (1) MOV     DX, VALUE

    (2) MOV     DX, [VALUE]

    (3) MOV     DX, OFFSET VALUE


  (1)과 (2)는 결과가 동일하고 (3)은 다르다.


    (1), (2) : DX = 1234H

         (3) : DX = 1234H가 있는 메모리상의 주소(Offset Address)


  표현 (1), (2), (3)은 지루한(?) 어셈블리 프로그래밍에서 때때로  혼동할 수 있

  는 소지를 갖고 있다. 고급언어 프로그래밍에서도 마찬가지 이겠지만, 훌륭한 프

  로그래머가 되기 위해서는  어중간한 표현을 삼가하고 명확한 표현방법을 익히는

  데 힘써는 것이 중요하다. 더우기 VALUE와 [VALUE]의 경우는 동일한 결과를 갖지

  만 전자 보다는 후자가 보다 명백하게 '값'의 의미를 강조하고 있다.


- LABEL의 사용과 함수 구성

  어셈블리 프로그램에 있어서 함수로 처리해야  할 부분과  분기 명령을 사용해서

  강제적으로 이동해야 할 부분을 구분하는 일은  프로그램의  최적화에 큰 영향을

  끼칠 수 있으므로 신중히 처리해야 한다.


보다 기본적인 문제들이 더 있지만 여기서 모든 내용을 언급하지는 않는다. 강좌가

진행되면서 그런 내용들이 부분적으로 다루어 질 것이다.



[ 첫번째 프로그램 - DISKMON.ASM ]


다음의 어셈블리 코드는 이세원(영원의별)님이 보내주신 프로그램을 역어셈블하고,

TASM과 MASM에서 어셈블될 수 있도록 필자가 함수/라벨등을 적절히 구분한 것이다.

프로그램의 내용이 신선하고, 본 강좌에서 처음으로 분석하게 될 프로그램인 만큼

가벼운 마음으로 풀어 볼 수 있는 내용으로 생각되어 소개한다.

참고로, 이 프로그램은 램상주 프로그램인데 이세원님이 보내주신 원래의 프로그램

인 DISKMON.COM의 크기는 783바이트였으며 램에 상주하면 624바이트의 메모리를 차

지하게 되어 있었다. 그러나, 여기에 소개된 소스를 어셈블해보면 아시겠지만 파일

의 크기는 803바이트(MASM=803바이트, TASM=802바이트), 램상주시의 메모리 차지는

640바이트로 변경되었다. 변경(추가)된 부분은 뒤에서 알려드리겠다.


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

; DISKMON.COM, Disk Monitor

;

; To build      MASM /ML diskmon;

;               LINK diskmon;

;               EXE2BIN diskmon.exe diskmon.com

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

;               TASM /ml diskmon

;               TLINK /t diskmon       <- '/t'는 링크하고 바로 COM파일을 생성

;

; 이 프로그램은 INT 21h의 31h함수를 이용해서 메모리에 상주하며, 바이오스의 디

; 스크 인터럽트인 INT 13h를 가로채고 디스크 작업의 상태를 화면상단, 우측에 표

; 시한다. 디스크 작업의 상태는 '디스크ID-작업-진행과정'의 형태로 표시한다.

;

;       디스크ID = 'A', 'B', 'C', 'D'중의 하나

;       작업     = 'R'ead, 'W'rite, 'V'erify, 'F'ormat중의 하나

;       진행과정 = 바람개비의 형태를 표시('|', '/', '-', '\')

;

; 처음 프로그램을 실행하면 메모리에 상주하며, 다시 한번 프로그램을 실행하면

; 상주를 해제한다. 별도의 옵션은 없다.

;

; 각 소스라인에 대한 상세한 설명은 별도로 하도록 하고 우선 소스를 살펴보자.

; 라인번호는 설명을 위해 편의상 붙인 것이므로 어셈블할 때는 라인번호를 제거

; 해야 한다.

;


0001    PSP_ENV_SEG     EQU     2CH

0002    VIDEO_OFF       EQU     9AH

0003   

0004    ;----------------------------------------------------------------------

0005                    .MODEL  SMALL

0006   

0007                    .CODE

0008   

0009                    ORG     100h

0010   

0011    ;----------------------------------------------------------------------

0012   

0013    DiskMonitor     PROC    FAR

0014                    JMP     InstallCheck

0015   

0016    Author          DB      'Copyright (C) Yi,se-won.'

0017    KeepInt13h      DD      ?

0018    KeepInt60h      DD      ?

0019    VideoSeg        DW      0B000H                  ; 칼라이면 800h를 더함

0020    SiteBuffer      DB      0, 0, 0, 0, 0, 0

0021    DriveID         DB      0, 70H                  ; 70H는 문자 색상값이다

0022    CurrentJob      DB      0, 70H                  ;      "       "

0023    CurrentSite     DB      7CH, 70H                ;      "       "

0024    DiskMonitor     ENDP

0025   

0026    ;----------------------------------------------------------------------

0027    ; New handler for INT 60h

0028   

0029    NewInt60h       PROC    FAR

0030                    PUSHF

0031                    CALL    DWORD PTR CS:[KeepInt60h]

0032                    IRET

0033    NewInt60h       ENDP

0034   

0035    ;----------------------------------------------------------------------

0036    ; New handler for INT 13h

0037   

0038    NewInt13h       PROC    FAR

0039                    PUSHF

0040                    CLI                     ; 이 작업을 진행하는 동안의

0041                                            ; 인터럽트를 금지한다

0042                    PUSH    SS              ; 레지스터들을 대피시킨다

0043                    PUSH    SP

0044                    PUSH    BP

0045                    PUSH    AX

0046                    PUSH    BX

0047                    PUSH    CX

0048                    PUSH    DX

0049                    PUSH    SI

0050                    PUSH    DI

0051                    PUSH    ES

0052                    PUSH    DS

0053   

0054                    PUSH    CS                      ; CS를 DS에 복사한다

0055                    POP     DS

0056   

0057                    CALL    CheckDriveID

0058                    CALL    CheckCurJob

0059                    CALL    CheckCurSite

0060                    CALL    StoreCurSite

0061                    CALL    PutDriveID

0062   

0063                    POP     DS

0064                    POP     ES

0065                    POP     DI

0066                    POP     SI

0067                    POP     DX

0068                    POP     CX

0069                    POP     BX

0070                    POP     AX

0071                    POP     BP

0072                    POP     SP

0073                    POP     SS

0074                    POPF

0075   

0076                    PUSHF

0077                    CALL    DWORD PTR CS:[KeepInt13h]

0078   

0079                    PUSHF

0080                    CLI

0081   

0082                    PUSH    SS

0083                    PUSH    SP

0084                    PUSH    BP

0085                    PUSH    AX

0086                    PUSH    BX

0087                    PUSH    CX

0088                    PUSH    DX

0089                    PUSH    SI

0090                    PUSH    DI

0091                    PUSH    ES

0092                    PUSH    DS

0093   

0094                    CALL    PutCurSite

0095   

0096                    POP     DS

0097                    POP     ES

0098                    POP     DI

0099                    POP     SI

0100                    POP     DX

0101                    POP     CX

0102                    POP     BX

0103                    POP     AX

0104                    POP     BP

0105                    POP     SP

0106                    POP     SS

0107                    POPF

0108   

0109                    RETF    2                       ; 아직 POP되지 않은

0110    NewInt13h       ENDP                            ; FLAG 레지스터를 위해

0111   

0112    ;----------------------------------------------------------------------

0113   

0114    CheckDriveID    PROC    NEAR

0115                    CMP     DL,0

0116                    JE      DRIVE_A

0117                    CMP     DL,1

0118                    JE      DRIVE_B

0119                    CMP     DL,80H

0120                    JE      DRIVE_C

0121                    CMP     DL,81H

0122                    JE      DRIVE_D

0123                    RET

0124    DRIVE_A:

0125                    MOV     [DriveID],41H           ; 'A'

0126                    RET

0127    DRIVE_B:

0128                    MOV     [DriveID],42H           ; 'B'

0129                    RET

0130    DRIVE_C:

0131                    MOV     [DriveID],43H           ; 'C'

0132                    RET

0133    DRIVE_D:

0134                    MOV     [DriveID],44H           ; 'D'

0135                    RET

0136    CheckDriveID    ENDP

0137   

0138    ;----------------------------------------------------------------------

0139   

0140    CheckCurJob     PROC    NEAR

0141                    CMP     AH,2

0142                    JE      READ_DISK

0143                    CMP     AH,3

0144                    JE      WRITE_DISK

0145                    CMP     AH,4

0146                    JE      VERIFY_DISK

0147                    CMP     AH,5

0148                    JE      FORMAT_DISK

0149                    CMP     AH,6

0150                    JE      FORMAT_DISK

0151                    CMP     AH,7

0152                    JE      FORMAT_DISK

0153                    CMP     AH,0AH

0154                    JE      READ_DISK

0155                    CMP     AH,0BH

0156                    JE      WRITE_DISK

0157                    RET

0158    READ_DISK:

0159                    MOV     [CurrentJob],52H        ; 'R'

0160                    RET

0161    WRITE_DISK:

0162                    MOV     [CurrentJob],57H        ; 'W'

0163                    RET

0164    VERIFY_DISK:

0165                    MOV     [CurrentJob],56H        ; 'V'

0166                    RET

0167    FORMAT_DISK:

0168                    MOV     [CurrentJob],46H        ; 'F'

0169                    RET

0170    CheckCurJob     ENDP

0171   

0172    ;----------------------------------------------------------------------

0173    ; CurrentSite의 최초값은 '|'이다.

0174    CheckCurSite    PROC    NEAR

0175                    CMP     CurrentSite,7CH         ; '|'

0176                    JE      SITE_2

0177                    CMP     CurrentSite,2FH         ; '/'

0178                    JE      SITE_3

0179                    CMP     CurrentSite,2DH         ; '-'

0180                    JE      SITE_4

0181                    MOV     CurrentSite,7CH         ; '|'

0182                    RET

0183    SITE_2:

0184                    MOV     [CurrentSite],2FH       ; '/'

0185                    RET

0186    SITE_3:

0187                    MOV     [CurrentSite],2DH       ; '-'

0188                    RET

0189    SITE_4:

0190                    MOV     [CurrentSite],5CH       ; '\'

0191                    RET

0192    CheckCurSite    ENDP

0193   

0194    ;----------------------------------------------------------------------

0195   

0196    PutDriveID      PROC    NEAR

0197                    PUSH    CS

0198                    POP     DS

0199                    MOV     SI,OFFSET DriveID

0200                    MOV     AX,CS:[VideoSeg]

0201                    MOV     ES,AX           ; ES = 0B800h또는 0B000h

0202                    MOV     DI,VIDEO_OFF    ; DI = 9Ah(=154)

0203                    MOV     CX,3

0204                    REP     MOVSW           ; Copy to ES:DI from DS:SI

0205                    RET                     ; '문자+속성'을 위해 워드복사

0206    PutDriveID      ENDP

0207   

0208    ;----------------------------------------------------------------------

0209   

0210    StoreCurSite    PROC    NEAR

0211                    PUSH    CS

0212                    POP     ES

0213                    MOV     DI,OFFSET SiteBuffer

0214                    MOV     AX,CS:[VideoSeg]

0215                    MOV     DS,AX

0216                    MOV     SI,VIDEO_OFF

0217                    MOV     CX,3

0218                    REP     MOVSW

0219                    RET

0220    StoreCurSite    ENDP

0221   

0222    ;----------------------------------------------------------------------

0223   

0224    PutCurSite      PROC    NEAR

0225                    PUSH    CS

0226                    POP     DS

0227                    MOV     SI,OFFSET SiteBuffer

0228                    MOV     AX,CS:[VideoSeg]

0229                    MOV     ES,AX

0230                    MOV     DI,VIDEO_OFF

0231                    MOV     CX,3

0232                    REP     MOVSW

0233                    RET

0234    PutCurSite      ENDP

0235   

0236    TSR_BLOCK       EQU     $               ; TSR_BLOCK = 이곳의 옵셋

0237   

0238    ;----------------------------------------------------------------------

0239   

0240    InstallMsg      DB      0DH, 0AH

0241                    DB      '旼컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴 '

0242                    DB      0DH, 0AH

0243                    DB      ' Disk monitor.                            '

0244                    DB      0DH, 0AH

0245                    DB      ' Copyright (C) Yi, se - won.  1994.11     '

0246                    DB      0DH, 0AH

0247                    DB      '읕컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴 '

0248                    DB      0DH, 0AH

0249                    DB      ' Installed.'

0250                    DB      7, 0DH, 0AH, '$'

0251    RemoveMsg       DB      0DH, 0AH, ' Disk monitor'

0252                    DB      0DH, 0AH, ' Removed.'

0253                    DB      7, 0DH, 0AH, '$'

0254    NotRemoveMsg    DB      0DH, 0AH, ' Disk monitor'

0255                    DB      0DH, 0AH

0256                    DB      ' Not removed.'

0257                    DB      7, 0DH, 0AH, '$'

0258   

0259    ;----------------------------------------------------------------------

0260   

0261    InstallCheck:

0262                    MOV     AX,3560H                ; 인터럽트 벡터 얻기

0263                    INT     21H                     ; ES:BX = Seg:Ofs

0264                    CMP     WORD PTR ES:[Author],6F43H      ; 'Co'인가?

0265                    JNE     InstallTSR                      ; Author의 옵셋

0266                                                            ; 은 103h이다

0267                    MOV     AX,3513H

0268                    INT     21H

0269                    CMP     WORD PTR ES:[Author],6F43H      ; 한번 더 확인

0270                    JE      RemoveTSR

0271   

0272                    MOV     DX,OFFSET NotRemoveMsg

0273                    MOV     AH,9

0274                    INT     21H

0275   

0276                    INT     20H                             ; 프로그램 종료

0277    RemoveTSR:

0278                    PUSH    DS

0279   

0280                    MOV     AX,2513H                ; 인터럽트 벡터 설정

0281                    MOV     DX,WORD PTR ES:[KeepInt13h]     ; 옵셋

0282                    MOV     DS,WORD PTR ES:[KeepInt13h+2]   ; 세그먼트

0283                    INT     21H

0284   

0285                    MOV     AX,2560H

0286                    MOV     DX,WORD PTR ES:[KeepInt60h]     ; 옵셋

0287                    MOV     DS,WORD PTR ES:[KeepInt60h+2]   ; 세그먼트

0288                    INT     21H

0289   

0290                    POP     DS

0291   

0292                    MOV     AH,49H          ; 메모리 해제, ES=해제할 메모리

0293                    INT     21H             ; 블럭의 세그먼트

0294   

0295                    MOV     DX,OFFSET RemoveMsg

0296                    MOV     AH,9

0297                    INT     21H

0298   

0299                    INT     20H             ; 프로그램 종료

0300   

0301    InstallTSR:

0302                    MOV     AH,0FH          ; 비디오 모드 얻기

0303                    INT     10H

0304                    CMP     AL,7            ; 흑백인가?

0305                    JE      Settings        ; 그렇다면 VideoSeg=0B000h

0306                    ADD     CS:[VideoSeg],800H   ; 아니면 VideoSeg=0B800h

0307    Settings:

0308                    MOV     AX,3560H

0309                    INT     21H

0310   

0311                    MOV     WORD PTR CS:[KeepInt60h],BX     ; 옵셋 보관

0312                    MOV     WORD PTR CS:[KeepInt60h+2],ES   ; 세그먼트 보관

0313   

0314                    MOV     AX,2560H

0315                    MOV     DX,OFFSET NewInt60h     ; 새로운 핸들러 설치

0316                    INT     21H

0317   

0318                    MOV     AX,3513H

0319                    INT     21H

0320   

0321                    MOV     WORD PTR CS:[KeepInt13h],BX     ; 옵셋 보관

0322                    MOV     WORD PTR CS:[KeepInt13h+2],ES   ; 세그먼트 보관

0323   

0324                    MOV     AX,2513H

0325                    MOV     DX,OFFSET NewInt13h     ; 새로운 핸들러 설치

0326                    INT     21H

0327   

0328                    MOV     AH,49H

0329                    MOV     ES,CS:[PSP_ENV_SEG]

0330                    INT     21H

0331   

0332                    MOV     AH,9

0333                    MOV     DX,OFFSET InstallMsg

0334                    INT     21H

0335   

0336                    LEA     DX,TSR_BLOCK

0337                    ADD     DX, 0Fh

0338                    MOV     CL, 4

0339                    SHR     DX, CL

0340   

0341                    MOV     AX,3100H

0342                    INT     21H

0343   

0344    ;----------------------------------------------------------------------

0345   

0346                    END     DiskMonitor             ; Entry point


이 프로그램의 전체적인 흐름을 살펴보자.


                      ┌───────┐

                      │프로그램 시작 │

                      └───┬───┘

                              │

                              ▽

              ┌───────────────┐Yes

              │      이미 설치되었는가?      ├──┐

              └───────┬───────┘    │

                              │No                  │

                              ▽                    │

              ┌───────────────┐    │

              │ 원래의 인터럽트 핸들러 저장  │    │

        ┌──┤ 새로운 인터럽트 핸들러 설치  │    │

        │    │       메모리 할당/램상주     │    │

        │    └───────────────┘    │

        │    ┌───────────────┐    │

        │    │ 원래의 인터럽트 핸들러 복구  │◁─┘

        │    │   메모리 해제/램상주 해제    │

        │    └───────┬───────┘

        │                    │

        │                    ▽

        │            ┌───────┐

        └─────▷│프로그램 종료 │

                      └───────┘


전체 흐름도에서 보는 바와 같이 어셈블리 소스가 약 300여 라인이나 되는 것에 비

해 하는 일은 매우 간단하다.  하지만 이런 일을 하는 프로그램을 고급언어로 작성

한다면 최적화 된다고 해도 메모리에 상주하는 크기가 적어도  5~6,000바이트는 될

것이다(예를 들어서 C와 같은 언어). 그러나 어셈블리로 작성된 이 프로그램은 640

바이트 만을 차지할 뿐이다.


위의 흐름도를 통해 프로그램이 전체적인 윤곽을 알 수 있었을 것이다.  이제 프로

그램의 세부적인 내용을 보자. 각 라인 단위로 설명한다.


0001      : PSP의 옵셋 2Ch에는 프로그램의 환경 블럭에 대한 세그먼트 주소가 저

            장되어 있다(Word).  프로그램이 메모리에 상주하기 전에 자신에게 할

            당되었던 환경블럭을 메모리에서 해제하게 되는데  이 부분은 다시 설

            명된다.


0002      : 이 프로그램에서는 현재의 디스크 작업 상태를 화면의 최상단, 최우측

            에 표시한다.  화면상의 임의의 위치 (X,Y)에 대응되는 비디오 메모리

            를 계산하기 위해서는 다음과 같이 한다.


                임의의 위치 (X, Y)에 대응되는 비디오 메모리 주소


                문자주소 = (Y*160)+(X*2)

                속성주소 = (Y*160)+(X*2)+1


            이 계산에 따르면 9Ah(154)는 화면상의 (X, Y) = (77, 0)의 좌표를 지

            정하는 것임을 알 수 있다. 이 값은 뒤에서 비디오 메모리의 세그먼트

            주소에 더해진다.


0005      : 메모리 모델을 SMALL로 설정하도록 한다. SMALL 모델은 코드 세그먼트

            와 데이타 세그먼트를 각각 한 개씩 가질 수 있다.


0007      : 코드세그먼트의 경계를 설정한다.  간이 코드 세그먼트 지시어에 의해

            생성된 세그먼트의 이름은 기본적으로 '_TEXT'가 된다.


0009      : 초기 명령 포인터(Instruction Point, IP)를 100h로 설정한다.

            모든 COM 프로그램은 100h번지로 부터 시작된다.


0014      : 초기 CS:IP 위치에는 반드시 실행 가능한 코드가 있어야 한다.

            만약 여기에 실행 코드가 아닌 어떤 데이타가 오더라도 CPU는 그것이

            데이타임을 알아보지 못한다.


0016~0023 : 메모리 상주 후에 사용하게 될 변수들을 정의한다.


        Author      : 프로그램 제작자에 대한 정보

        KeepInt13h  : 원래의 INT 13h의 주소 보관용

        KeepInt60h  : 원래의 INT 60h의 주소 보관용

        VideoSeg    : 비디오 메모리의 세그먼트 주소(Default=0B000H, 흑백화면)

        SiteBuffer  : 디스크 동작 상태를 저장할 버퍼

                      CurrentJob과 CurrentSite를 합친 문자열

                      예) R|, R/, R-, R\        <- 읽기 작업중...

                          F|, F/, F-, F\        <- 초기화 작업중...

        DriveID     : 드라이브 ID = 'A', 'B', 'C', or 'D'

        CurrentJob  : 현재의 작업 = 읽기('R'), 쓰기('W')

                                    검사('V'), 초기화('F')

        CurrentSite : 바람개비 모양(Default='|')


0029~0033 : INT 60h에 대한 새로운 핸들러.

            프로그램이 메모리에 상주한 후 새로운 핸들러의  세그먼트 주소는 메

            모리에 상주한 프로그램의 CS와 동일하다.   이 점을 이용해서 메모리

            상주 여부를 확인한다.  라인 0031의 표현은 원래,


                CALL    CS:[KeepInt60h]


            이었으나 뜻을 명확히 하기 위해 변경되었다.


0038~0110 : INT 13h에 대한 새로운 핸들러.

            바이오스를 통한 디스크 입출력이 있을 때 마다 이 핸들러가 호출되고

            핸들러는 INT 13h가 호출될 때의 레지스터를 검사해서 어떤 작업을 할

            것인지를 파악하게 된다. 라인 0054~0055는 DS의 값을 CS의 값으로 대

            치하는 작업인데 이는 다음과 같이 표현될 수 있다.


                MOV     DX,CS

                MOV     DS,DX


            즉,  세그먼트 레지스트 끼리의 직접적인 교환은 할 수 없으므로 범용

            레지스터를 통해 CS를 DS로 복사하는 것이다. 그러나 이 방법은 PUSH,

            POP에 의한 복사방법 보다 코드의 크기가 크며 CPU 사이클도 늦다.


0114~0136 : 작업이 진행될 디스크의 ID를 검사하고 DriveID에 보관한다.


0140~0170 : INT 13h로 넘겨진 레지스터에서 AH를 검사하면 어떤 작업을 하게될 지

            알 수 있다.  이를 검사하고 각 작업에 따라 'R', 'W', 'V', 'F' 중의

            한가지를 CurrentJob에 보관한다.


0174~0192 : 바람개비의 종류를 결정한다.  바람개비는 '|', '/', '-', '\'를 차례

            대로 출력하여 구현한다. 이 값은 CurrentSite에 저장된다.


0196~0206 : 드라이브 ID('A', 'B',..)를 화면의 최상단 우측(Y=0, X=77)에 출력한

            한다. ES:DI = 비디오세그먼트:009Ah


0210~0220 : 작업의 유형과 선택된 바람개비 모양을 SiteBuffer에 저장한다.


0224~0234 : SiteBuffer에 저장된 문자열을 비디오세그먼트:009Ah에 출력한다.


0236      : 메모리에 상주시킬 영역의 크기(바이트 단위)를 계산하기  편리하도록

            하기 위해 사용되었다. '$'은 한 세그먼트 내에서 '그곳 까지의 거리'

            인 옵셋의 개념과 동일하므로 그 값은 16비트 범위(0~64KB)에 있다.

            즉, 이 프로그램은 CS:0000~CS:TSR_BLOCK 영역을 메모리에 상주시키게

            된다. 이 부분은 역어셈블한 코드에서는 찾을 수 없다. 필자가 프로그

            램의 앞뒤를 맞추어 보고 임의로 삽입했다.


0240~0257 : 프로그램의 설치/제거시에 보야줄 메세지를 정의한다.


0261~0276 : 프로그램이 이미 메모리에 설치되어 있는지를 검사하고 이미 설치되어

            있다면 상주를 해제하는 곳으로, 그렇지 않다면 설치하는 곳으로 분기

            한다. 프로그램의 설치 여부는 앞서 이미 언급한 바 있지만 INT 60h의

            새로운 핸들러의 세그먼트 주소를 v얻고 그 세그먼트의 103h옵셋을 조

            사하는 것이다. 103h옵셋에는 'Copyright..'라는 문자열이 있고 이 문

            자열의 머릿부분인 'Co'는 워드 단위로 읽었을 때 6F43h가  되므로 이

            를 확인한다. 만약 6F42h가 아니라면 아직 설치되지 않은 것으로 간주

            하여 설치처리를 하게되며, 6F43h라면 확인을 위해 다시 한번 INT 13h

            의 세그먼트를 얻고 그 세그먼트의 103h옵셋과 6F43h를 비교하여 동일

            하다면 이미 설치된 것으로 간주하여 상주 해제 부분으로 분기한다.

            INT 13h의 새로운 핸들러 세그먼트는 INT 60h의 새로운 핸들러 세그먼

            트와 동일하다.


0277~0299 : 메모리 상주를 해제하기 위해 보관해 두었던 원래의  인터럽트 핸들러

            벡터를 복구하고 상주를 위해 할당되었던 메모리를 해제한다.


0301~0342 :  비디오 메모리에 디스크의 작업 상태를 써야 하므로  비디오 메모리의

            세그먼트를 계산하기 위해(칼라는 0B800h, 흑백은 0B000h로 다르다)

            비디오 모드를 얻고 칼라이면  VideoSeg(Default=0B000h)에 800h를 더

            한 후 메모리에 상주하기 위해 INT 13h,INT 60h의 새로운 핸들러를 설

            치하고 상주하게될 메모리 블럭 (바이트 단위를 패러그래프 단위로 환

            산)을 할당한다. 라인 0328~0330의 표현,


                MOV     AH,49H

                MOV     ES,CS:[PSP_ENV_SEG]

                INT     21H


            은 이 프로그램이 실행되면서 생성된 환경영역(Default=384바이트)을

            해제한다. 프로그램이 실행되면 도스는 그 프로그램을 위한 환경영역과

            프로그램 자체를 위한 프로그램영역을 할당한다. 메모리 상주 프로그램

            의 경우 상주를 해제할 때는 프로그램영역과 환경영역을 모두 해제해

            주어야 하는데 이 프로그램에서는 환경영역을 미리 해제하고 있다.


            라인 0336~0342는 메모리 상주를 위해 상주할 코드의 크기를 계산한다.


                LEA     DX,TSR_BLOCK    ; TSR_BLOCK의 유효주소(옵셋)을 얻는다.

                                        ; 상주하게 될 영역은 옵셋 0에서

                                        ; TSR_BLOCK까지의 바이트 크기와 PSP의

                                        ; 256바이트를 합해서 계산된다. 그러나

                                        ; COM 프로그램은 같은 세그먼트 내에

                                        ; PSP를 가지고 있으므로(옵셋 0~FFh)

                                        ; 단순히 TSR_BLOCK까지의 바이트 길이만

                                        ; 계산된다. EXE프로그램이라면 PSP의 크

                                        ; 기인 100h가 더해져야할 것이다.


                ADD     DX, 0Fh         ; DX는 메모리에 상주할 프로그램의 크기

                                        ; 가 바이트 단위로 저장되어 있다.

                                        ; 이 값을 패러그래프 단위로 변환하기

                                        ; 위해서는 4로 나누어 주게 되는데  그

                                        ; 때의 소실되는 나머지 값을 보정하기

                                        ; 위해서 0Fh를 더해 주었다.

                                        ; 이 부분은 원래의 원래 코드에는 없는

                                        ; 내용이다. 원래의 프로그램에서는 DX에

                                        ; 바로 패러그래프 단위의 메모리 크기가

                                        ; 저장되었었는데 이는 아마도  EQU를 사

                                        ; 용해서 앞부분에서 미리 계산되었기 때

                                        ; 문일 것으로 추측된다.


                MOV     CL, 4

                SHR     DX, CL          ; 바이트 단위의 길이를 패러그래프 단위

                                        ; 로 환산하기 위해 DX를 우측으로 4비트

                                        ; 시프트한다(DX / 4).

                MOV     AX,3100H        ; 메모리 상주후 종료 서비스, 복귀코드=0

                INT     21H



0346      : 프로그램의 진입점(Entry point)을 명시한다.


어셈블리 소스와 설명 부분을 따로 프린트한 후 라인번호를 비교하면서 보는 것이 편

리할 것이다.


프로그램은 비교적 간단한 내용이므로 이제 막 어셈블리를 배우기 시작하는 초보가

아니라면 이해하는데는 별 어려움이 없을 것으로 생각된다.



 [31] 제목 : 소스분석<2>에서분석된프로그램의제작자입니다.
 올린이 : 영원의별(이세원  )    95/02/17 00:38    읽음 : 223  관련자료 없음

       안녕하세요.


         박선근님(ID:natali)의 [강좌] 어셈블리 소스분석 <2> 에서  분석된

       프로그램 (diskmon.com)의 제작자 이세원 입니다.


         먼저 선근님의 놀라운 코드 분석력에 경탄을 마지  않습니다.  물론

       코드는 소서로 뽑아낼 수 있으나 제작자의 코드 의도를 정확히 파악한

       다는것은 결코 쉬운것이 아니기 때문입니다.


         밑의 강좌 내용중에 극히 일부 제작자의 본래 의도와 약간 다른  해

       석이 있었는데 단지, 제작자가 살아있는 이유로 이를  보충할까  합니

       다.(하는게 낫겠죠? 선근님?)


         이 프로그램을 만들게된 동기는 iomon.com 이나 노턴 유티리티의 di

       skmon.exe가 디스크의 움직임을 화면에 표시해 주지만 뭔가 부족한 점

       이 있어 보다 확실한 디스크의 움직임을 구현해보기 위해서 였습니다.

       그것은 단지, 재미있는 화면 출력으로 끝나는것이 아니라 디스크의 섬

       세한 움직임을 포착하여 디스크 작동상의 오류나 어떤 변화를  찾아내

       기 위해서 였습니다.


         제작기간은 총 7일 가량 소요 되었습니다. 처음에 화면의 구성과 프

       로그램의 흐름을 잡아 몇시간만에 완성된 프로그램을 제작하여 시험하

       는데, 알 수 없는 이유로 A나 B드라이브를 읽을때 디스켓이 끼워져 있

       지 않았는데도 A나 B드라이브로 넘어갔습니 다. 저로서는 도무지 이유

       를 알 수가 없었습니다. 이 문제는 선근님의 코드 분석에도  밝혀지지

       가 않았습니다.(선근님 소스 109 라인) 결국 이 문제로  오랜  고민을

       하게 되었는데 우연히 디스크 인터럽트의 복귀과정에서 스택의 워드를

       하나 더 pop 해야함을 알게 되었습니다. (보통의 사용자 인터럽트  핸

       들러에서의 복귀는 iret 으로 스택의 3워드를 비워야하나 디스크 인터

       럽트만 유난히 4워드를 비워야 합니다.) 이것이 109 라인의 'retf  2'

       인 이유입니다.


         그리고 이것은 기술적인 문제이지만, 프로그램을 메모리에 상주시키

       기전에 먼저 환경블럭을 해제하는것이 좋습니다.  왜냐하면  가능한한

       램상주 프로그램은 메모리를 덜 차지해야하기 때문입니다.


         그리고 이것은 보충입니다만, 램상주 프로그램의  경우에  메모리에

       상주시킬 프로그램 일부분의 크기를 계산하는 방법은  매우  간단합니

       다. 그것은 상주시킬 부분까지의 레이블에서 start 레이블을 빼고  다

       시 256byte를 더합니다.(256은 psp의 크기) 거기에 15바이트를 더합니

       다. (패러그래프 단위로 나눌때 완전히 포함되게하기 위해서)  그리고

       패러그래프 단위인 16으로 나눕니다. - 이것은 도스인터럽트 21h 함수

       번호 31h인 경우 입니다.


         그러나 더욱 간단히 프로그램을 메모리에 상주시키고자 한다면 도스

       인터럽트 27h를 쓰십시오. 위의 방법(int  21h,ah=31h)이  프로그램의

       크기를 위에서부터 측정하는 방법이라면 int 27h는 반대로 프로그램의

       크기를 밑에서부터 측정합니다. 즉 메모리에서  제거하고자  하는부분

       이하를 dx에 넣고 호출만하면 됩니다. 이 차이는 방법의 차이일뿐이고

       기능에서의 차이는 없습니다. (int 21h,ah=31h는  패러그래프  단위로

       램상주시키고, int 27h는 바이트 단위로 램상주 시키기 때문에 방법의

       차이가 생겨난것 입니다.) 그리고 이러한 계산은  어셈블러가  어셈블

       시(pass 1)에 계산하기 때문에 프로그래머는 신경쓰지 않아도  됩니다

       .(하나의 세그먼트 단위로 절대번지를 계산해서 자동으로 넣어주므로)


         그리고 int 60h를 후킹한 이유는, 이 프로그램을 상주시킨후에 다른

       int 13h를 후킹하는 프로그램이 있었다고 할때, 이 프로그램을 메모리

       에서 제거하면 디스크 인터럽트는 아작납니다.(매우 큰 사고임)  따라

       서 이 프로그램의 제거시에 이 프로그램 이후에 디스크 인터럽트를 후

       킹한 프로그램이 있었는가를 알아보기 위해서 인식자를 int 60h에  숨

       겨 두었던것 입니다. (다른 프로그램에서 int 60h를 후킹할 이유가 없

       으므로...)


         끝으로 이 프로그램의 원 소스(흔히 하는말로 오리지날 쏘쓰~)를 공

       개합니다. 그리고 별 볼일 없는 프로그램임에도 불구하고 분석을 해주

       신 선근님에게 다시한번 감사 드립니다.



;===========================================================================

;       DISKMON

;       디스크의 움직임을 관찰한다.

;==========================================================================

include macro.inc


color           =       01110000b                 ;흰색바탕에 검은색글자


code    segment

        assume  cs:code


org     100h

start:

        jmp     begin


copyright       db      'Copyright (C) Yi,se-won.' ;인식자는 Co 이다.

old_13h         dd      ?

old_60h         dd      ?

video_buffer    dw      0b000h                    ;칼라 비디오 버퍼

old_screen      db      6       dup(0)            ;본래의 화면 저장소


drive           db      ?

                db      color

status          db      ?

                db      color

speed           db      '|'                       ;바람개비 초기치

                db      color


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

; user interrupt handler

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

new_60h:                                          ;인식자 검사를 위한

        pushf                                     ;사용자 인터럽트 핸들러

        call    cs:old_60h

        iret


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

; disk interrupt handler

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

new_13h:

        pushf

        cli

        push    ss

        push    sp

        push    bp

        push_register

        push    es

        push    ds


        push    cs

        pop     ds


perform:

        call    drive_check                       ;드라이브 체크

        call    status_check                      ;디스크 체크

        call    rotation_count                    ;바람개비 상태 체크

        call    screen_save                       ;본래화면 저장

        call    monitor                           ;화면 출력


        pop     ds

        pop     es

        pop_register

        pop     bp

        pop     sp

        pop     ss

        popf


        pushf

        call    cs:old_13h


next:

        pushf

        cli

        push    ss

        push    sp

        push    bp

        push_register

        push    es

        push    ds


        call    screen_recover


        pop     ds

        pop     es

        pop_register

        pop     bp

        pop     sp

        pop     ss

        popf


        retf    2                                 ;스택의 4바이트를 비운다.


drive_check:                                      ;드라이브를 체크한다.

        cmp     dl,0

        je      a_drive

        cmp     dl,1

        je      b_drive

        cmp     dl,80h

        je      c_drive

        cmp     dl,81h

        je      d_drive


drive_check_exit:

        ret


a_drive:

        mov     ds:drive,'A'

        ret

b_drive:

        mov     ds:drive,'B'

        ret

c_drive:

        mov     ds:drive,'C'

        ret

d_drive:

        mov     ds:drive,'D'

        ret



status_check:

        cmp     ah,2

        je      read

        cmp     ah,3

        je      write

        cmp     ah,4

        je      verify

        cmp     ah,5

        je      format

        cmp     ah,6

        je      format

        cmp     ah,7

        je      format

        cmp     ah,0ah

        je      read

        cmp     ah,0bh

        je      write


status_check_exit:

        ret


read:

        mov     ds:status,'R'

        ret

write:

        mov     ds:status,'W'

        ret

verify:

        mov     ds:status,'V'

        ret

format:

        mov     ds:status,'F'

        ret


rotation_count:

        cmp     ds:speed,'|'

        je      rotation_1

        cmp     ds:speed,'/'

        je      rotation_2

        cmp     ds:speed,'-'

        je      rotation_3


        mov     ds:speed,'|'

        ret


rotation_1:

        mov     ds:speed,'/'

        ret

rotation_2:

        mov     ds:speed,'-'

        ret

rotation_3:

        mov     ds:speed,'\'

        ret



monitor:                                          ;디스크의 상태를 화면

        push    cs                                ;출력한다.

        pop     ds

        mov     si,offset drive


        mov     ax,cs:video_buffer

        mov     es,ax

        mov     di,154


        mov     cx,3

        rep     movsw


        ret



screen_save:

        push    cs

        pop     es

        mov     di,offset es:old_screen


        mov     ax,cs:video_buffer

        mov     ds,ax

        mov     si,154


        mov     cx,3

        rep     movsw


        ret


screen_recover:                                   ;본래의 화면을 복구한다.

        push    cs

        pop     ds

        mov     si,offset ds:old_screen


        mov     ax,cs:video_buffer

        mov     es,ax

        mov     di,154


        mov     cx,3

        rep     movsw


        ret


install_msg     db      13,10,'┌────────────────────┐'

                db      13,10,'│ Disk monitor.                          │'

                db      13,10,'│ Copyright (C) Yi, se - won.  1994.11   │'

                db      13,10,'└────────────────────┘'

                db      13,10,' Installed.',7

                db      13,10,'$'


removed_msg     db      13,10,' Disk monitor'

                db      13,10,' Removed.',7

                db      13,10,'$'


no_remove_msg   db      13,10,' Disk monitor'

                db      13,10,' Not removed.',7

                db      13,10,'$'


begin:

        mov     ax,3560h                          ;인식자를 위한 인터럽트에서

        int     21h                               ;인식자를 검사한다.

        cmp     word ptr es:copyright,6f43h       ;만약 인식자가 없다면

        jne     install                           ;인스톨한다.


disk_vector_check:

        mov     ax,3513h                          ;디스크 인터럽트에서

        int     21h                               ;인식자를 검사한다.

        cmp     word ptr es:copyright,6f43h       ;인식자가 있다면 프로그램을

        je      remove                            ;메모리에서 제거한다.


no_remove:                                        ;만약 인식자가 없다면 다른

        mov     dx,offset no_remove_msg           ;프로그램이 디스크 인터럽트를

        mov     ah,9                              ;후킹한 것이므로 프로그램을

        int     21h                               ;메모리에서 제거할 수 없다.

                                                  ;

        int     20h



remove:                                           ;이미 메모리에 상주하므로

        push    ds                                ;프로그램을 제거한다.


        mov     ax,2513h

        mov     dx,word ptr es:old_13h

        mov     ds,word ptr es:old_13h[2]

        int     21h


        mov     ax,2560h

        mov     dx,word ptr es:old_60h

        mov     ds,word ptr es:old_60h[2]

        int     21h


        pop     ds


        mov     ah,49h

        int     21h


        mov     dx,offset removed_msg

        mov     ah,9

        int     21h


        int     20h


install:                                          ;프로그램을 인스톨한다.

        mov     ah,0fh                            ;현재의 비디오모드를 검사하고

        int     10h                               ;모노크롬이면 color_m 로

                                                  ;점프한다.

        cmp     al,7

        jne     color_m


hooking:

        mov     ax,3560h                          ;인식자를 위한 사용자

        int     21h                               ;인터럽트를 후킹한다.

        mov     word ptr old_60h,bx

        mov     word ptr old_60h[2],es

        mov     ax,2560h

        mov     dx,offset new_60h

        int     21h


        mov     ax,3513h                          ;디스크 인터럽트 후킹

        int     21h

        mov     word ptr old_13h,bx

        mov     word ptr old_13h[2],es

        mov     ax,2513h

        mov     dx,offset new_13h

        int     21h


        mov     ah,49h

        mov     es,cs:[2ch]

        int     21h


        mov     ah,9

        mov     dx,offset install_msg

        int     21h


        mov     ah,31h                            ;메모리 상주 종료.

        mov     dx,(install_msg-start+256+15)/16

        int     21h


color_m:                                          ;모노크롬 모니터를 위한

        add     cs:video_buffer,800h              ;비디오 버퍼 설정

        jmp     hooking


code    ends


        end     start



 [32] 제목 : [참고] 어셈블리 참고 책들..
 올린이 : onestep (류창균  )    95/02/18 13:19    읽음 : 325  관련자료 없음

Fri 29 Jan 93  8:34
By: Yousuf Khan
To: All
Re: FAQ: Books on Assembler
------------------------------------------------------------------------

Q2) I want to learn assembler, what books should I read?

                                  ***

A2) Various people have found these books useful to them:


                                General
                                =======

Beginner:
---------
-"Assembly Language from Square One", Jeff Duntemann, Scott Foresman IBM
Comptuter Books. ISBN 0-673-38590-6.
-"Assembly Language for the IBM PC", Kip R. Irvine, ISBN 0-02-359840-9
-"Mastering Turbo Assembler", by Tom Swan, Hayden Books, 1989.
ISBN 0-672-48435-8.
-"Assembly Language and Systems Programming for the IBM PC and
Compatables", Karen A. Lemone, Little, Brown, & Co. ISBN 0-316-52069-1.
-"Assembly Language Primer for the IBM PC/XT", Robert Lafore,
Plume/Waite.
-"Using Assembly Language", Allen L. Wyatt Sr., Que 1990.
ISBN 0-88022-464-9.

Intermediate:
-------------
-"The Zen of Assembly", Michael Abrash, Scott Foresman Publ.
-"Assembly Language Primer for the IBM PC/XT"
-"IBM Microcomputers: A Programmer's Handbook", Julio Sanchez and Maria
P. Canton, McGraw-Hill. ISBN 0-07-054594-4.
-"Programmer's Problem Solver for the IBM PC, XT, and AT", Robert
Jourdain, Prentice Hall 1986. ISBN 0-89303-787-7.
-"IBM PC ASSEMBLER LANGUAGE AND PROGRAMMING", Peter Abel, 1987,
Prentice-Hall, hardcover (college text). ISBN 0-13-448143-7.

Advanced:
---------
-"80386: A Programming and Design Handbook", 2nd ed., Penn & Don Brumm,
TAB Books. ISBN 0-8306-3237-9.
-"80486 Programming", Penn & Don Brumm and Leo J. Scanlon, McGraw-Hill.
ISBN 0-8306-3577-7.
-"ADVANCED ASSEMBLY LANGUAGE", Steven Holzner and Peter Norton Computing,
Inc., Brady Books/Simon and Schuster. ISBN 0-13-658774-7.


                             Video Graphics
                             ==============

Intermediate:
-------------
-"Programmer's Guide to PC & PS/2 Video Systems", Richard Wilton,
Microsoft Press. ISBN 1-55615-103-9.

Advanced:
---------
-"Power Graphics Programming", Michael Abrash, Que Corporation. ISBN
0-88022-500-9
-"Programmers Guide to the EGA and VGA cards", 2nd Ed., Richard F.
Ferraro, Addison-Wesley Publishing Co. ISBN 0-201-57025-4.
-"Advanced Programmers Guide to the EGA/VGA", George Sutty and Steve
Blair, Brady Books/Prentice Hall Trade. ISBN 0-13-729039-X.


                         References/Specialized
                         ======================

Intermediate:
-------------
-"Undocumented DOS", Andrew Schulman, Raymond J. Michels, Jim Kyle, Tim
Paterson, David Maxey, and Ralf Brown, Addison-Wesley. ISBN
0-201-57064-5.
-"DOS Programmer's Reference", 2nd Edition, Terry Dettmann, QUE.
ISBN 0-88022-458-4.

Advanced:
---------
-"386SX Microprocessor Programmer's Reference Manual", Intel Corp.,
McGraw-Hill. ISBN 0-07-881673-4.
-"i486 Microprocessor Programmer's Reference Manual", Intel Corporation,
McGraw-Hill. ISBN 0-07-881674-2.
-"The Programmer's PC Sourcebook", Thom Hogan, Microsoft Press. ISBN
1-55615-321-X.
-"System BIOS for IBM PCs, Compatables, and EISA Computers", 2nd Ed.,
Phoenix Technologies Ltd., Addison Wesley. ISBN 0-201-57760-7.
-"PC Magazine Programmers Technical Reference: The Processor and
Coprocessor", Robert L. Hummel, Ziff-Davis Press. ISBN 1-56276-016-5.
-"Mastering Serial Communications", Peter W. Gofton, Sybex 1986.  ISBN
0-89588-180-2.
-"DOS Programmer's Reference", 2nd Ed.
-"MS-DOS Programmer's Reference", MS Press. ISBN 1-555615-329-5.

from CG.


 [33] 제목 : 스택(STACK)에 관한 연구
 올린이 : 영원의별(이세원  )    95/03/22 23:11    읽음 : 135  관련자료 없음

                스택 (Stack)


        1.      스택이란.


        스택은 프로그램에서 사용하는  조금 특별한 데이타를 저

장하는 아주 특별한 영역이다.   그 영역은 스택 세그먼트이고 스

택 세그먼트 레지스터가 그곳의 위치를 가리킨다.


        스택이라는 이름은  식당에서 접시를  쌓아두는 장치에서

유래했다고 한다.  이 장치의 특징을 한가지  예로 들어 설명하겠

다. 커피 전문점에 가면  커피잔을 전기열로 말리는 장치가 있다.

이곳에 커피잔을 씻어 올려 놓는다. 그리고 다음에 씻은잔은 먼저

올려놓은 잔 위에 올려놓는다. 이런 동작들을 반복한다면 가장 먼

저 씻은잔이 가장 밑에  놓여 있게 된다. 반대로 이번에는 손님이

와서 커피잔이 나가게 되었을때는 맨 위에 놓인잔이 가장 먼저 손

님에게 나가게 된다. 물론 이 방법은 손님에게는 합리적이지 못하

다. 맨 밑의 잔이 손님에게 나간다면 그 잔은 얼마나 먼지를 덮어

쓰고 있는지 종업원 조차 알수 없기 때문이다.


        이런 식으로 가장 먼저  들어온 데이타가 가장 늦게 나가

는 다시말해 가장 최근에 저장된 데이타가 가장 먼저 사용되는 그

런 형태이다. 이러한 구조를 가지고 있으므로 당연히 가장 최근에

들어온 데이타의 위치를 알수 있는 지시자를 가지고 있다. 그것이

바로 스택 포인터이다. 스택 포인터는 스택 영역에서 다음에 저장

할 위치를 가리키고 있다. 즉  PUSH 를 하게 되면 현재의 스택 포

인터가 가리키는 위치에 데이타가 저장되는것이다. 스택에 데이타

를 저장했으면 다음에 저장할 데이타의 스택에서의 위치를 알려주

기 위해서 스택 포인터의 값은 2 감소한다. 스택에서 데이타를 꺼

내는 명령은  POP 이다. 또한 POP  명령으로 데이타를 거낸후에는

스택 포인터는 2 증가한다. 2씩 감소하고 2씩 증가하는 이유는 스

택에는 워드단위(2 BYTE)로 저장하기 때문이다.



        2.      스택을 만든 이유


        일반적으로 데이타는 별도의  데이타 영역을 두어 그곳에

저장한다. 스택에 저장할 이유가 없다. 그러나 스택에 저장을하면

여러가지 이득을 볼 수  있다. 프로그램에는 프로그램을 종료할때

까지 보존해야할 데이타가 있는가 하면 일시적으로 사용하고는 버

리는 데이타가 있다. 이런  일시적인 데이타조차 메모리의 데이타

영역에 보존한다면 메모리를 낭비하는 결과를 초래할수도있다. 따

라서 이런 데이타는 스택에 저장한다. 또한 스택을 이용한 데이타

의 이동은 매우 효과적이다. PUSH  와 POP 은 한쌍으로 쓰여서 데

이타를 아주 빠르게 복사할수 있다.


        고급언어의 경우 전자의 경우 즉 일시적으로 쓰이는 데이

타를 스택에 저장하는 것은  필수적이다. 고급언어는 구조상 라이

브러리를 써야만하고 이과정에서  프로그램에 소용되는 변수의 갯

수가 많아진다. 그런 이유로 지역변수는 스택을 이용한 조작이 불

가피하기 때문이다. 그러나 어셈블리어는 데이타의 이용이란 측면

에서는 아주 자유롭다.   프로그래머가 데이타의 저장되는 위치와

그 양을 모두 알기 때문이다. 반면에 고급언어에서는 그것을 아는

것이 거의 불가능하다.


**************************************************************

주석)

지역변수 (Local variable, Internal variable)

지역변수는 하나의  독립된 서브 프로그램  내에서만 쓰는 변수를

말한다. 그러므로 이 변수는 서브 프로그램의 종료와 함께 제거된

다.


전역변수 (Global variable, External variable)

전역 변수는 주 프로그램 서브 프로그램 모두 사용할수 있는 변수

를 말한다. 따라서 전체  프로그램이 종료할때가지 메모리에 남아

있는다.


        * 위의 두개념은 고급언에서만 존재한다.


**************************************************************



        3.      스택 포인터의 감소


        위에서 잠깐 언급한 스택 포인터의 값이 감소하는 이유는

무엇일까 ?  스택에 데이타가 저장될때마다  메모리에서의 번지는

오히려 2바이트씩 감소한다. 정상적인 경우 즉 메모리에 데이타가

저장되는 경우에 메모리의  번지는 증가해야함에도 불구하고 스택

에서는 반대의 현상을 보인다. 그 이유는 프로그램의 형태와 무관

하지 않을 것이다. 컴퓨터에서  실행화일이라 부르는 화일은 확장

명이 COM 아니면  EXE 이다. 즉 컴퓨터는  확장명이 COM 또는 EXE

이면 무조건 메모리에 로드하고 실행해버린다. 이중 COM 프로그램

이 스택과 관련이 깊다. 앞에서도 언급이 되었지만 COM 형의 프로

그램에는 세그먼트 재배치에관한  헤더가 없다. 다시말해 .COM 형

의 프로그램은 하나의 세그먼트만을 사용하고 그에 따라서 프로그

램과 관계되는 네개의 세그먼트가 시작하는곳이 모두 같은 번지이

다. 그런데 임시적인 데이타를  스택에 저장한다고하여 스택 세그

먼트의 시작점에서부터 데이타를  저장한다면 프로그램 코드와 데

이타가 들어있는 메모리에  모두 '덮어써버리는' 결과를 초래하고

말것이다. 물론 그렇게 되면 프로그램은 엉망이 되어버린다. 그러

므로 프로그램에서 사용하는 한정된  64KB 의 메모리 영역중에 그

래도 비교적 사용가능성이  드문 64 KB 의  끝부분 부터 데이타를

저장해 내려온다면 프로그램의 코드나 데이타와 중복 사용될 가능

성이 적어질것이다. 이런 연유에서  스택 포인터는 프로그램이 시

작할때부터 스택 세그먼트의 가장 끝부분을 가리키고 있는것이다.

그러나 만약 프로그램의  크기가 커서 거의 64  KB 의 끝부분까지

도달한다면 문제는 달라진다. 이경우에는 프로그램에서 저절로 설

정되는 스택 포인터를 사용하면  않되고 별도의 스택 영역을 확보

하고 스택 포인터도 이에 맞게 고쳐써야 할것이다.



        4.      스택의 호출번지 저장


        스택의 데이타  저장구조 즉 가장  늦게 저장된 데이타가

가장먼저 사용되는 구조를 이용해 서브 프로그램의 호출시에 호출

한 프로그램에로의  복귀번지를 저장하는데 사용한다.  즉 현재의

프로그램에서 서브 프로그램을  호출하는 경우에 호출하려는 서브

프로그램이 현재의 세그먼트 내에  있으면 IP 값만 저장하고 현재

의 세그먼트 밖에 있으면 CS 와 IP 의 값을 스택에 저장한다.


        이러한 스택에 저장하는  방법은 생각해보면 합리적이다.

서브 프로그램으로 제어가 넘어가기전에 현재의 CS 와 IP 의 값을

만약에 데이타 영역에  저장했다고하고 제어를 서브 프로그램으로

넘겼다고 하자. 그리고 서브 프로그램의 수행을 마치고 본래의 프

로그램으로 돌아가려 한다면  어떻게 CS 와 IP  를 저장한 번지를

찾을수 있을까 ? 물론  서브 프로그램이 어셈블리어로 제작된다면

별로 문제 될것은 없다.  어셈블리어는 데이타에 관한한 자유롭기

때문에 프로그래머가 데이타의 위치를 임의로 설정 하는것이 가능

하다. 그러나 고급언어에서는 문제가 전혀 다르다. 일단 고급언어

로 작성된 프로그램내로  들어오게되면 메모리의 번지를 알아내는

것이 결코 쉽지않다. 많은  라이브러리가 결합된 형태의 고급언어

프로그램은 프로그래머 조차 레지스터의  움직임을 알 수 없기 때

문이다. 그러나 이러한 프로그램도  스택 영역은 변함이 없으므로

(스택은 하나이므로) 이곳에  데이타를 저장한다면 안전할것이다.

마찬가지로 메인 프로그램에서  서브 프로그램으로 인수를 전달할

때에도 스택을 이용한다. 이것은  일종의 약속이다. 즉 언어를 막

론하고 스택의 영역은 변함이  없으므로 이곳을 통해 데이타를 교

환한다는것이다. 물론 반대로도  해석이 가능하다 '프로그램과 프

로그램간의 데이타 교환은 스택을  이용한다. 그러므로 스택 영역

은 변경하지 않는다.' 스택에 관하여 마치 닭이 먼저냐 알이 먼저

냐 하는식의 얘기가 되었으나 결론은 분명하다. 즉 '프로그램간의

데이타 교환이나 호출한  프로그램에로의 복귀번지는 스택에 저장

한다' 하는것이 약속이라는 것이다.




            FFFFH      +--------------------+

                       |         0H         |

            FFFEH      +--------------------+      최초의 SP

                       |       DATA 1       |

            FFFCH      +--------------------+

                       |       DATA 2       |

            FFFAH      +--------------------+

                       |       DATA 3       |

            FFF8H      +--------------------+

                       |       DATA 4       |

            FFF6H      +--------------------+      현재의 SP

                       |                    |

                       |                    |



                           .COM 형의 스택


        최초의 SP 는 FFFEH 이고 SS  의 값은 CS 와 동일하다. 이

것은 .COM형의 프로그램은 하나의 세그먼트  값만을 가질수 없다는

이유에서 비롯된것이다. 그후 스택에 데이타가 하나씩 저장 될  때

마다 SP 의 값은 2 바이트 씩 감소하여 FFFCH,FFFAH,FFF8H 로 떨어

졌으며 데이타가 4개  저장될때 FFF6H 의 값을 가지게 되었다.





            0000H      +--------------------+      최초의 SP

                       |       DATA 1       |

            FFFEH      +--------------------+

                       |       DATA 2       |

            FFFCH      +--------------------+

                       |       DATA 3       |

            FFFAH      +--------------------+

                       |       DATA 4       |

            FFF8H      +--------------------+      현재의 SP

                       |                    |

                       |                    |



               스택영역을 설정하지 않은 .EXE 형의 스택



       최초의 SP 값은 0000H 이고 데이타가 하나씩 저장될때마다

역시 2바이트씩 감소한다. 0000H 에서 FFFEH 로 바뀌는 것에 의아

해 할수도 있다. 그 이유는 다음과 같다.   .EXE 형의 프로그램이

시작될때 이때는 SS와 CS의 값은 같다.  따라서 SS:SP 는 PSP:100

H 를 가리키고 있다.  그러나 데이타가 스택에  저장되기 전에 SS

의 값은 다시 설정되고 그후부터는  정상적으로 SP는 SS  의 끝에

서부터 내려로기 때문에 어느 세그먼트의 영역과도 겹치지 않는다.




 [34] 제목 : [소스] 보호모드 진입코드
 올린이 : yunix   (유경상  )    95/03/25 02:41    읽음 : 160  관련자료 없음

안녕하세요 yunix 입니다.

제가 32비트 프로그래밍 강좌를 하고 있는데.....
과연 386 보호모드로 진입하기 위해서는 어떠한 일들을 해야 하는 지
궁금해 하는 분들이 있으신것 같아 여기 간단한(?) 프로그램을 올려봅니다.
소스가 약간 길군요. 잘 분석해 보시고 질문을 하시면 성의껏 답변해 드리겠읍니다.
스스로 부딛혀보고 생각해 보는 것이 실력향상에 도움이... :)
참고로 Loner는 제 call-sign 입니다.

컴파일러는 tasm을 썻구요. 아마 masm으로도 되리라 믿습니다.
링크하실때는 /3 옵션을 꼭 주셔야 합니당...

주석이 모두 영어군요... 짭...
제 콩글리쉬 실력을 마음껏 감상하십시요. :)
(   제길... 영어를 10년 넘게 공부했는데 ... 이게 뭐야 !
    우리나라 교육.... 문제야 ! )

    ---------------------------------------------------------------
;*
;*  File : enter.asm
;*
;*  enter.asm : entering protected mode code.
;*
;*  This code is used in [booting]. It will set up gdt and idt, and
;*  print some message.
;*  Once entered protected mode, we cannot go back to dos because we
;*  destory gdt,idt etc., which is used by dos memory manager like
;*  EMM386, QEMM386, 386MAX ....
;*  After execution this, you should power down your computer.
;*  This NEVER harm your computer.
;*
;*  [Extention]
;*  I will write a new code that read gnu program in real mode and enter
;*  protected mode using this code for running the gnu program.
;*  the gnu program is written with djgpp or gpp on the linux, which
;*  is allowed no dos function call, no bios call. This will be primitive
;*  operating system of mine. Isn't it wonderful?
;*
;*  Assembler : any assembler compatiable with MASM 5.0
;*
;*
;*      Copyright (C) 1994, Yu, Kyoung Sang
;*
;*              Wed  11-02-1994  02:07:30 written by Loner
;*

.386p

text        segment use16
        assume  cs:text,ds:data,ss:stk

;*
;* Let's DO IT !!!!
;*
enter_prot:
;*
;* check if operation mode is protected mode.
;*
        smsw    ax
        and ax,1
        jz  ready_to_enter
;*
;* hum... operation mode is already protected mode. memory manager like
;* EMM386 or QEMM may control 386. if we touch cr0(msw),idt, or gdt to enter
;* protected mode, the memory manager will complain and kill our program.
;* let's find other way. how about go back to real mode ?
;* but it need touch cr0(msw) and it is not allowed.
;* when processor is reset, it will operate in real mode. ok... let's reset
;* processor ! and jump to ready_to_enter. how ?
;* there is back door to go back in real mode. write 5 or 10 to shutdown
;* status byte of CMOS and set seg:off to jump at 040:0067 after reset.
;* then, when processor is reset, POST of BIOS will jump to address
;* where we write at 0040:0067. How funny it is !
;*
;* NOTE :
;*      after reset, you may not use some interrupt which memory
;*      manager have interceptted.
;*
        cli
        mov al,0fh      ; offset of shutdown status byte of CMOS
        out 70h,al      ; write offset
        mov al,10       ; after reset, jump where 40:67 point.
        out 71h,al
        mov ax,40h      ; set magic address(40:67)
        mov es,ax
        mov bx,67h
        mov ax,offset ready_to_enter
        mov es:[bx],ax  ; write address to jump after reset
        mov ax,cs
        mov es:[bx+2],ax
        mov cx,10       ; for safe reset, repeat 10 times
_reset:
        mov al,0feh     ; reset cmd ....
        out 64h,al      ; reset !!
        call    wait_8042   ; program control will never reach here

__die:      jmp __die

;*
;* now.... do some initialization to enter...
;*
ready_to_enter:
        mov ax,data
        mov ds,ax
;*
;* disabled interrupt will not be enabled until everything(idt) is ready.
;*
        cli
        mov ax,stk
        mov ss,ax
        mov sp,offset stk_ptr

;*
;* ok. segment initialzation is done. now, let's disable NMI.
;*
        mov al,80h      ; disable NMI
        out 70h,al
;*
;* all right. let's enable A20 line.
;* if A20 line is not enabled, we cannot access high memory area (10000h)
;*
        call    wait_8042   ; wait until 8042 is ready to read
        mov al,0d1h     ; d1h = write a byte to output port
        out 64h,al
        call    wait_8042   ; wait until 8042 reads a command
        mov al,0dfh     ; Gate A20 enable !
        out 60h,al
        call    wait_8042   ; wait until 8042 reads a data
;*
;* now, set gdt pointer.
;* gdt base is needed in format of 32bit linear address. so we should change
;* 20bit address of gdt to 32bit linear address.
;* save 32bit data segment pointer for later use (message ouput)
;*
        mov ax,ds
        shl ax,4
        mov bx,ax
        mov ax,ds
        shr ax,12
        push    ax
        push    bx      ; ax:bx=32bit data segment base
        add bx,offset gdt
        adc ax,0
        mov word ptr gdt_ptr+2,bx
        mov word ptr gdt_ptr+4,ax
;*
;* set far jump address as 32bit form into si,di register
;*
        mov ax,text_32
        shl ax,4
        mov bx,ax
        mov ax,text_32
        shr ax,12
        add bx,offset start_32
        adc ax,0            ; ax:bx => 32 bit linear pointer
        mov cs:jmp_addr,bx
        mov cs:jmp_addr+2,ax
;*
;* we shoud save stack pointer as 32bit form. because once entering protected
;* mode, stack pointer should be initialized again.
;*
        mov ax,ss
        shl ax,4
        mov dx,ax
        mov ax,ss
        shr ax,12
        add dx,sp
        adc ax,0
        mov cx,ax

;*
;* load gdt and idt onto descriptor table registers (gdtr,idtr)
;*
           lidt    pword ptr idt_ptr
           lgdt    pword ptr gdt_ptr
;*
;* ok. everythings are ready. now we are entering protected mode !
;*
        mov ax,1        ; set proctected mode bit
        lmsw    ax      ; now !!!
        jmp $+2     ; flush instruction prefetch queue.
;*
;* now, we are in protected mode. before do somthing, initialzing segment
;* register and do far jump to change cs register
;*
        mov bx,18h
        mov ds,bx
        mov ss,bx
        mov es,bx

        db  66h,0eah    ;   jmp far 10h:start_32
jmp_addr    dw  0h
        dw  0h
        dw  10h
;*
;* if input buffer is full, wait until 8042 reads from it.
;*
wait_8042:  in  al,064h     ; read 8042 status register
        test    al,02h      ; 8042 reads a byte from input buffer?
        jnz wait_8042
        ret

text        ends

.386p
text_32     segment use32
        assume  cs:text_32
;*
;* OK... we have succeeded !
;* you can do somthing in protected mode!
;* I will just output 'Hello ...' on the screen.
;*
start_32:
        mov ax,cx       ; cx:dx= 32bit stack pointer
        shl eax,16
        mov ax,dx
        mov esp,eax
;*
;* foolish check is done. check that A20 is REALLY enabled.
;*
        xor eax,eax
        mov ebx,0
        mov ecx,100000h
check_a20:  inc eax
        mov ebx,eax
        cmp eax,ecx
        jz  check_a20
;*
;* clear screen
;*
        mov ecx,80*25   ; clear screen
        mov ax,0820h    ; blank with black backgournd
        mov ebx,0b8000h ; text vram address
clear_screen:   mov [ebx],ax
        inc ebx
        inc ebx
        loop    clear_screen
;*
;* output message. message is in data segment. so we needs 32bit pointer of
;* message. we have already saved 32bit pointer of data segment on the stack.
;*
        pop esi     ; get 32bit data segment base
        mov eax,0       ; clear upper word
        mov ax,offset data:message  ; message offset
        add esi,eax     ; esi<- 32bit data segment
        mov ebx,0b8820h+54  ; screen position
        mov ah,1eh      ; yellow/blue attribute
output_msg: mov al,[esi]
        or  al,al       ; null character ?
        jz  end_msg
        mov [ebx],ax    ; output character
        inc esi     ; next pointer
        inc ebx     ; next screen position
        inc ebx
        jmp output_msg
end_msg:
;*
;* everything is done. we can go back to real mode but
;* we can't go back to dos. so...
;*
die:        jmp short die   ; die...

text_32     ends


data        segment use16
gdt_ptr     dw  4*8-1       ; limit of gdt
        dw  0,0
idt_ptr     dw  0,0,0       ; empty idt

gdt     dw  0,0,0,0     ; null descriptor
        dw  0,0,0,0     ; unused

        dw  07ffh       ; limit 8MB ((2047+1)*4096)
        dw  0000h       ; base = 00000000h
        dw  9a00h       ; present,dpl=0,exec/read code segment
        dw  00c0h       ; granularity=4KB, 32bit

        dw  07ffh       ; limit 8MB
        dw  0000h       ; base = 00000000h
        dw  9200h       ; present,dpl=0,read/write data segment
        dw  00c0h       ; graularity=4KB, 32bit

message     db  'Hello 386 world !'
        db  0

data        ends

stk     segment use16
stack_area  db  512 dup (0) ; stack area
stk_ptr     label
        db  16  dup (0) ; for dummy byte
stk     ends

        end


 [38] 제목 : MASM 6.1의 조건문 정리.
 올린이 : 영원의별(이세원  )    95/04/22 18:26    읽음 :  64  관련자료 없음


MASM 6.1 의 조건문 입니다.

이 조건문들을 통해서 고급언어 흉내를 낼 수 가 있지요.

예전에 만들어 두었던 것인데 그때가 그립군요.

이제 다시는 프로그램 만들기가 쉽지 않을듯 합니다.

프린터로 뽑아서 고이 간직하세요...




MASM 6.x 는 고급언어에서나 볼 수 있었던 여러가지 조건문을 사용할 수 있게

되었다. 블럭 구조의 이들 조건문의 연산자는 다음과 같다.


┌────────┬──────────────────┐

│    연산자      │                 의미               │

├────────┼──────────────────┤

│      ==        │             같으면                 │

│      !=        │             같지 않으면            │

│      >         │             크면                   │

│      >=        │             크거나 같으면          │

│      <         │             작으면                 │

│      <=        │             작거나 같으면          │

│      &         │             BIT TEST               │

│      !         │             NOT                    │

│      &&        │             AND                    │

│      ||        │             OR                     │

└────────┴──────────────────┘



        1        .IF,.IFELSE,.ELSE,.ENDIF


이 블럭 구조문은 .IF 에서 시작하여 .ENDIF 에서 끝난다. 따라서 조건을 만족

했으면 .ENDIF 이후의 명령을 수행하게된다.


형식)

        .IF     조건식

                명령어

        .ELSE

                명령어

        .ENDIF


먼저 조건을주고 조건에 맞으면 다음 문장을 수행하고 빠져나간다. 그러나 조건과

맞지 않으면 .ELSEIF 이후의 문장을 수행하고 빠져나간다.



        .IF     조건식

                명령어

        .ELSEIF

                명령어

        .ELSEIF

                명령어

        .ELSEIF

                명령어

               .

               .

               .

        .ENDIF


이 구조식도 위와 위와 별 차이가 없다. 먼저 조건을 비교하고 만족하면 밑의

명령을 수핸하고 빠져나가고 만족하지 않으면 다음 조건과 비교한다. 다음조건

과 비교해서 만족하면 밑의 명령을 수행하고 빠져나가고 만족하지 않으면 다음

조건과 비교한다.....



code    segment

        assume  cs:code


org     100h


start:

        mov     cx,100

        .if     cx==10                 ;─── ①

        mov     ah,9                   ;─┐

        mov     dx,offset msg1         ;  ├─ ②

        int     21h                    ;─┘


        .else                          ;─── ③

        mov     ah,9

        mov     dx,offset msg2

        int     21h


        .endif                         ;─── ③


        mov  ah,4ch                    ;─── ⑤

        int  21h


msg1    db     'msg1$'

msg2    db     'msg2$'


code    ends


        end     start


① 만약 CX 가 10이면 ② 를 수행하고 그렇지 않다면 ③으로 간다.

② 'msg1' 을 출력하고 ⑤ 로 간다.

③ 'msg2' 를 출력하고 ⑤ 로 간다.

④ 이 블럭 구조문의 끝

⑤ 프로그램을 종료한다.


이 프로그램을 어셈블하여 디버깅을 해본다.


C:\MASM61\BIN>debug sam2.com 

-u 

186F:0100 B96400        MOV     CX,0064

186F:0103 83F964        CMP     CX,+64

186F:0106 7509          JNZ     0111

186F:0108 B409          MOV     AH,09

186F:010A BA1C01        MOV     DX,011C

186F:010D CD21          INT     21

186F:010F EB07          JMP     0118

186F:0111 B409          MOV     AH,09

186F:0113 BA2101        MOV     DX,0121

186F:0116 CD21          INT     21

186F:0118 B44C          MOV     AH,4C

186F:011A CD21          INT     21

186F:011C 6D            DB      6D

186F:011D 7367          JNB     0186

186F:011F 3124          XOR     [SI],SP

-


막상 디버깅을 해보명 역시 우리의 소스 프로그램과 약간 다르게 기계어로

번역 되었음을 알 수 있다. 하지만 우리가 의도했던대로의 결과를 나타내고 있다.



        2   .WHILE,.ENDW


이 블럭 구조문은 .WHILE 에서 시작하여 .ENDW 로 끝난다.

먼저 조건을 주고 그 조건이 만족될 때까지 밑의 명령을 반복 수행한다.


형식)   .WHILE  조건식

                명령어

        .ENDW



code    segment

        assume  cs:code


org     100h


start:

        mov     dx,10                   ;①

        .while  dx > 0                  ;②

        dec     dx                      ;③

        .endw                           ;④


        mov     ah,4ch

        int     21h


code    ends


        end     start


① DX 에 10을 넣는다.

② DX 가 10보다 큰 동안 밑의 명령을 수행한다.

③ DX 의 값을 1 감소한다.

④ .WHILE 블럭 구조문의 끝


다음은 이 프로그램을 어셈블링하여 디버깅한것이다.


C:\MASM61\BIN>debug sam3.com 

-u 

186F:0100 BA0A00        MOV     DX,000A

186F:0103 EB01          JMP     0106     ─┐

186F:0105 4A            DEC     DX         ├─ ①

186F:0106 83FA00        CMP     DX,+00     │

186F:0109 77FA          JA      0105     ─┘

186F:010B B44C          MOV     AH,4C

186F:010D CD21          INT     21

186F:010F 7C4E          JL      015F

186F:0111 8700          XCHG    AX,[BX+SI]

186F:0113 004E92        ADD     [BP-6E],CL

186F:0116 4E            DEC     SI

186F:0117 9D            POPF

186F:0118 4E            DEC     SI

186F:0119 A84E          TEST    AL,4E

186F:011B B34E          MOV     BL,4E

186F:011D BE4EC9        MOV     SI,C94E

-


① 의 내용이 .WHILE 블럭 구조문에 의해 생성된 기계어 코드이다.



        3   .REPEAT,.UNTIL,.UNTILCXZ



형식)   .REPEAT

                명령어

        .UNTIL  조건식


.UNTIL 의 조건이 만족 될때까지 .REPEAT 밑의 명령을 반복 수행한다.


        .REPEAT

                명령어

        .UNTILCXZ

        .UNTIL  조건식


.UNTIL 의 조건이 만족 될때까지 .REPEAT 밑의 멸영을 반복수행 하다가 만약

CX 의 값이 0이되면 빠져나간다.


code    segment

        assume  cs:code


org     100h


start:

        .repeat                     ----①

        inc     si


        .until  si == 10            ----②


        mov     cx,10               ----③

        .repeat                     ----④

        inc     di

        dec     cx                  ----⑤


        .untilcxz                   ----⑥


        mov     ah,4ch

        int     21h


code    ends


        end     start


① .REPEAT 블럭문의 시작

② SI 가 10 이 뚜때까지 반복 수행한다.

③ CX 의 초기값으로 10을 넣는다.

④ .REPEAT 블럭문의 시작

⑤ CX 의 값을 1 감소 시킨다.

⑥ 만약 CX 의 값이 0이면 빠져 나간다.



C:\MASM61\BIN>debug sam4.com 

-u 

186F:0100 46            INC     SI         ──┐

186F:0101 83FE0A        CMP     SI,+0A         ├─ ①

186F:0104 75FA          JNZ     0100       ──┘

186F:0106 B90A00        MOV     CX,000A    ──┐

186F:0109 47            INC     DI             ├─ ②

186F:010A 49            DEC     CX             │

186F:010B E2FC          LOOP    0109       ──┘

186F:010D B44C          MOV     AH,4C

186F:010F CD21          INT     21

186F:0111 8700          XCHG    AX,[BX+SI]

186F:0113 004E92        ADD     [BP-6E],CL

186F:0116 4E            DEC     SI

186F:0117 9D            POPF

186F:0118 4E            DEC     SI

186F:0119 A84E          TEST    AL,4E

186F:011B B34E          MOV     BL,4E

186F:011D BE4EC9        MOV     SI,C94E

-


① .REPEAT 문에 의해서 SI 가 10이 될때까지 반복한다.

② .REPEAT 문에 .UNTILCXZ 가 삽입된 관계로 LOOP 명령어가 출현하였다.



        4   .BREAK,.CONTINUE


.BREAK 은 .WHILE/.ENDW 나 .REPEAT/.UNTIL/.UNTILCXZ 문의 루프를 도중에

빠져나오게 하고 .CONTINUE 은 .WHILE/.ENDW 나 .REPEAT/.UNTIL/.UNTILCXZ 문의

루프를 계속 실행시키게한다.


형식)

        .BREAK

        .BREAK          .IF 조건식

        .CONTINUE

        .CONTINUE       .IF 조건식



code    segment

        assume  cs:code


org     100h


start:

        .while  dx!=100              ---①

        inc     ax

        .if     ax==100              ---②

        .break                       ---③

        inc     si

        .endif

        inc     dx

        .endw


        mov     ah,4ch

        int     21h


code    ends


        end     start


① DX 가 100이 아닌동안 밑의 명령을 반복 수행한다.

② 만약 AX 가 100 이면 밑의 명령을 수행한다.

③ 이 명령에 의해 .WHILE 블럭 루프문을 완전히 빠져나간다.


C:\MASM61\BIN>debug sam5.com 

-u 

1A7C:0100 EB0A          JMP     010C

1A7C:0102 40            INC     AX

1A7C:0103 83F864        CMP     AX,+64

1A7C:0106 7503          JNZ     010B

1A7C:0108 EB07          JMP     0111            ----①

1A7C:010A 46            INC     SI

1A7C:010B 42            INC     DX

1A7C:010C 83FA64        CMP     DX,+64

1A7C:010F 75F1          JNZ     0102

1A7C:0111 B44C          MOV     AH,4C

1A7C:0113 CD21          INT     21

1A7C:0115 92            XCHG    DX,AX

1A7C:0116 4E            DEC     SI

1A7C:0117 9D            POPF

1A7C:0118 4E            DEC     SI

1A7C:0119 A84E          TEST    AL,4E

1A7C:011B B34E          MOV     BL,4E

1A7C:011D BE4EC9        MOV     SI,C94E

-


① .BREAK 문이 나타난 명령코드이다.




 [40] 제목 : [소스] 부팅시간을 기록하는 bootlog.asm
 올린이 : 영원의별(이세원  )    95/04/22 19:20    읽음 :  45  관련자료 없음

;┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓

;┃     bootlog.asm                                                        ┃

;┃     날짜및 시각을 화일에 순차로 입력하는 프로그램.                     ┃

;┠────────────────────────────────────┨

;┃     자기 컴퓨터의 부팅시간을 기록하는 프로그램은 수 없이 많습니다.     ┃

;┃     하지만 이 프로그램 만큼 작고 그리고 빠르게 기록하는                ┃

;┃     프로그램은 없을 겁니다.                                            ┃

;┃     왜냐구요? 어셈블리로 만들었으니까요...                             ┃

;┃     어셈블리를 공부하시는 분에게 도움이 되길 빌며...                   ┃

;┃     앞으로 가끔 그동안 제가 만들었던 어셈유틸의 소스를 올리겠습니다.   ┃

;┃     마지막으로 .EXE 형의 프로그램입니다.                               ┃

;┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛


        dosseg


data    segment


year            dw      ?

month_date      dw      ?

a_day           dw      ?


time            dw      ?

second          dw      ?


temp_1          db      ?

temp_2          db      ?

temp_3          db      ?

temp_4          db      ?


now_year        db      4       dup('y')

                db      '.'

now_month       db      2       dup('m')

                db      '.'

now_date        db      2       dup('d')

                db      '  '


now_time        db      2       dup('t')

                db      ':'

now_minute      db      2       dup('m')

                db      ':'

now_second      db      2       dup('s')

                db      13,10


write_error_msg         db      'Write error.$'

open_error_msg          db      'Open error.$'

pointer_error_msg       db      'Pointer error.$'


path_name       db      'c:\bootlog',0             ;화일이름(아스키즈 문자열)

handle          dw      ?


data    ends




stack   segment stack

        db      100h dup(0)

stack   ends       




code    segment

        assume  cs:code,ds:data,ss:stack



start:

        mov     ax,data

        mov     ds,ax

       

        push    ds                              ;ES 를  DS 에 맞춘다.

        pop     es

       

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

;날짜및 요일 읽어내기

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

read_date:

        mov     ah,2ah                          ;년,월,일을 구한다.

        int     21h


        mov     year,cx                         ;year

        mov     month_date,dx                   ;month & date


        mov     ax,year

        call    div_1                           ;나누기 수행


        mov     cx,4                            ;year

        mov     si,offset temp_1                ;년도를 저장한다.

        mov     di,offset now_year

        rep     movsb


        mov     ax,month_date

        call    div_2                           ;나누기 수행


        mov     cx,2                            ;month

        mov     si,offset temp_1                ;월을 저장한다.

        mov     di,offset now_month

        rep     movsb


        mov     cx,2                            ;date

        mov     si,offset temp_3                ;일을 저장한다

        mov     di,offset now_date

        rep     movsb


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

; 시각 읽어내기

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

        mov     ah,2ch                          ;시각 구하기

        int     21h


        mov     time,cx                         ;time

        mov     second,dx                       ;second


        mov     ax,time

        call    div_2                           ;나누기 수행


        mov     cx,2                            ;시간저장

        mov     si,offset temp_1

        mov     di,offset now_time

        rep     movsb


        mov     cx,2                            ;분저장

        mov     si,offset temp_3

        mov     di,offset now_minute

        rep     movsb


        mov     ax,second

        call    div_2                           ;나누기 수행


        mov     cx,2                            ;초저장

        mov     si,offset temp_1

        mov     di,offset now_second

        rep     movsb


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

; 화일 오픈 및 쓰기

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

file_open:

        mov     ah,3dh                          ;화일오픈

        mov     dx,offset path_name             ;화일명

        mov     al,1                            ;쓰기전용

        int     21h


        jc      file_creat                      ;오픈에 실패하면 화일을 만든다.

        mov     handle,ax                       ;핸들을 저장한다.


        mov     ah,42h                          ;화일 포인터의 설정

        mov     al,2                            ;이동 오프셍을 화일의

                                                ;끝에다 더한다.

        mov     bx,handle                       ;핸들번호

        mov     cx,0                            ;이동 바이트 상위 값

        mov     dx,0                            ;이동 바이트 하위 값

        int     21h


        jc      pointer_error                   ;캐리발생하면 포인터에라


        mov     ah,40h                          ;화일쓰기

        mov     dx,offset now_year              ;출력버퍼 번지

        mov     cx,22                           ;써넣을 바이트수

        mov     bx,handle                       ;핸들번호

        int     21h


        jnc     file_close                      ;캐리가 발생안하면

                                                ;화일을 닫아라.

        mov     ah,9                            ;쓰기에라 메시지출력

        mov     dx,offset write_error_msg

        int     21h


        jmp     file_close

       


file_creat:

        mov     ah,3ch                          ;화일생성

        mov     dx,offset path_name             ;화일명

        mov     cx,0                            ;정상속성

        int     21h


        jnc     file_open                       ;캐리가 발생안하면 오픈하라


        mov     ah,9                            ;오픈에라 메시지 출력

        mov     dx,offset open_error_msg

        int     21h


        jmp     exit



pointer_error:

        mov     ah,9                            ;포인터 에라 메시지출력

        mov     dx,offset pointer_error_msg

        int     21h



file_close:

        mov     ah,3eh                          ;화일닫기.

        mov     bx,handle                       ;핸들번호

        int     21h



exit:

        mov     ah,4ch

        int     21h



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

; 연결된 숫자 변환

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

div_1:

        mov     bl,100                          ;젯수 100

        div     bl                              ;100으로 나누기


        push    ax                              ;100으로 나눗값 보존


        mov     bl,10                           ;젯수 10


        mov     ah,0                            ;몫을 다시 10으로 나눈다.

        div     bl


        add     al,30h

        mov     temp_1,al                       ;1000단위 저장

        add     ah,30h

        mov     temp_2,ah                       ;100단위 저장


        pop     ax                              ;100으로 나눈값 복구


        xchg    ah,al                           ;100으로 나눈값의 나머지를

        mov     ah,0                            ;다시 나누기위해


        div     bl                              ;다시 10으로 나눈다


        add     al,30h

        mov     temp_3,al                       ;10단위 저장

        add     ah,30h

        mov     temp_4,ah                       ;1단위 저장


        ret


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

; 분리된 숫자 계산

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

div_2:

        push    ax


        xchg    ah,al                           ;ah,al을 따로 나누기위해


        mov     ah,0

        mov     bl,10                           ;젯수 10

        div     bl                              ;10으로 나눈다


        add     al,30h

        mov     temp_1,al                       ;몫 저장

        add     ah,30h

        mov     temp_2,ah                       ;나머지 저장


        pop     ax


        mov     ah,0

        div     bl


        add     al,30h

        mov     temp_3,al                       ;몫 저장

        add     ah,30h

        mov     temp_4,ah                       ;나머지 저장.


        ret

       

code    ends       



        end     start                           ;진입접




 [41] 제목 : [새내기 연재] 어셈블러가 도대체 뭐길래
 올린이 : hanuly님(조광현  )    95/04/23 21:19    읽음 : 114  관련자료 있음(TL)

어셈블러?? 어셈블러?! 어셈블러!? 어셈블러!
─────────────────────
      Copyright(c)1995,.Sir hanuly        ┍━┯━━━━━━━━━━━━━┑
   어셈 새내기를 위한 어셈 기초 연재...   │Ⅰ│어셈블러가 도대체 뭐길래..│
                                          ┕━┷━━━━━━━━━━━━━┙
 ★ 기억하세요!
  * 저의 비주얼 베이직 연재를 읽어보신 분들은 아시겠지만, 내용이 '잡담 형식'
  으로 꾸며지다가, 때로는, 교습 형식으로 꾸며지는 등의 이상한 진행 형태를 취
  하고 있습니다.
  * 이 연재는 어셈블러를 처음 배우시는 분들을 위해 마련되었습니다.
  * 개인적 질문 편지는, 되도록 '삼가'해주시고, '묻고 답하기' 란을 이용해주셨
  으면 합니다.
  * 내용상의 오류가 발견된 경우는 언제든지 편지로 연락주시기 바랍니다. 제 아
  이디는 아시죠? 'hanuly님'입니다.
  * 본 내용을 제 허가 없이 베끼거나(!), 복사(?)하는 행위를 하시면, 제가 화낼
  거예요.. 그리고, 저작권법에 의거(?)해 고소할거예요.. (히히.. 돈버는거 좋아
  하는 리님..)
  * 본 내용의 소스들은 거의 다 Macro Assembler 5.x를 기준으로 작업합니다. 공
  개자료실에 등록되어 있습니다.
  * 필요한 파일들..
      masm.exe
      link.exe
   exe2bin.??? <-이름을 까먹었어요...
                 그리고, 즐기는 에디터...('산'이나, 'UEdit'나, 도스의 'edit'
                 같은..)
─────────────────────────────어셈블러는 어렵다?

 안녕하세요? 하누리님입니다.
 
 어셈블러는 어렵다는 말씀을 많이 하시더군요... 게다가...많은 분들이 공부하다
가 중간에 포기하십니다. 물론, 어셈블러는 어려워요...(...) 인터럽트를 직접 건
드리고 놀아야(?) 합니다. 하드웨어, CPU 원리...

 ..맞아요.. 어셈블러를 배우는 것은 영어를 배우는 것과 비슷하다고 해야겠군요.
영어를 배울때는 단어와 숙어를 열심히 외움과 동시에, 문법도 연구합니다.. 게다
가, 사고 방식, 환경까지...

 어셈블러는 그렇지만, 영어보다 쉬워요. printf...int.. 엄청나게 많은 단어들..
(C나, Basic, Pascal 같은 고급 언어들은 다 그렇죠..뭐..) 언제 외우노.. 그쵸?

 그렇지만, 어셈블러는 mov, int, add등의 몇개의 명령어만 배우면 됩니다. 문화
방식 이해하기도 쉽고(미국이나, 영어의 문화 방식 같은..)..

 어셈블러는 아는대로 써먹을 수 있다는 것이 장점일수도 있겠군요. 물론, 이 연
재와, 앞으로 올라올 연재들을 학습한다고 해서, 다 깨닫는 것은 절대 아니겠죠!

 그렇지만, 어셈블러로 쬐끄만 프로그램은 짤 수 있을거예요(저도 바라고.. 당신
도 바라고.. 우리도 바라는 목표입니다).

 영어를 배울려면, "영어에 대해서, 학교에서 배우는 것" 보다는, "미국에 가서,
직접 경험하고 느끼는 것"이 훨씬 효과적입니다. 어셈블러도... 시스템이 다운되
고, CMOS가 날아가고..(우리는 그정도로 극단적인 기법은 학습하지 않아요... 그
러한 것들을 하려면, 다른 분들의 강좌를 읽으셔야 겠죠..) 아무튼, 실제로 느껴
서 하는 것이 가장 중요하다는 거.. 아시죠? 어셈은 그럴때 더욱 더 나의 친구로
다가오게 되는 것입니다.

 자, 우리 어셈 새내기들... 화이팅을 외치며.. 조그만 시작을.. 하자구요..
 
 ¤ 명언 : 시작이 반이랍니다.

─────────────────────────────── 어셈이 뭐꼬?

 자, 여기서 처음부터 어셈블러(이하, '어셈'이라고 부릅니다)를 배우려고, 하시
는 분들은 손 들어보시겠어요?
 
 당신도, 남대문에서 과일 파시는 아저씨도, 손을 내리고 계시는 군요..어! 그런
데.. 저쪽에서는 국민학생 같아 보이는 학생들이 손을 들고 있네요... 아.. 학교
에서 벌을 받고 있었군요..

 어셈을 처음부터 배우려는 분들은 아주 극소수입니다. 물론이죠! 극소수죠.
 
 베이직이나, 씨나, 파스칼이나.. 다른 언어를 먼저 배우고, 어셈을 배우는 것이
보통입죠. 네..

 원래 컴퓨터는 고철덩어리죠. 2진수만 받아들이죠.. 꺼졌다 켜졌다... 아시리라
생각되는 군요.. 세게.. 약하게는 해당되지 않습죠(디지탈 형식이다..).

 게다가, 컴퓨터의 머리는 꼭 자기가 기계인 것을 티를 낸답니다, 기계어만을 받
아들일줄 안답니다.

 그래서, 수치 비교도 사실 컴의 머리(CPU:중앙처리장치)로서는 엄청난 작업을..
거치는 겁니다.

 그래서, 고급언어는 하나의 문장으로 처리되는 것이... 어셈-기계어와 1대 1로
대응하는 언어-에서는 복잡해지고.. 기계어는... 꼬로로로록..(거품내는 소리)

 예를 들어서.. 1+2+3을 계산합니다.
 Basic은요..
    abc = 1+2+3
 C는요.....
    abc = 1+2+3;
 어셈은요...
    mov abc,1
    add abc,2
    add abc,3
 기계어는요...
    (저도 잘모릅니다. ... ★... 하지만, 엄청 복잡하다는 것은 아시리라.. 악!
    돌이,.,.,.)
 
 고급언어(씨나, 베이직)은 한줄이면 끝나죠. 어셈은요.. 에고.. 석줄씩이나..
기계어는... 아시리라...

 그렇지만, CPU는 한번에 하나만 더할줄 압니다. 즉,
 a=1+2
  ┌─┐
  │1│→ 넣어요.. CPU에..
  └─┘           ┌─┐
                   │1│이 CPU에 기억됩니다.
                   └─┘
  ┌─┐ 
  │2│->넣어요.. 더하라는 신호도..
  └─┘           ┌┬┐
                   │3┤이 기억됩니다.
                   └─┘
  이런 구조이기에.. 1+2+3을 계산할때 석줄이 필요했던 것입니다.
 
─────────────────────────────────어셈이 뭐꼬?

 이 정도로만 해 두죠... 어셈에대해서 이해하셨으리라 믿습니다.
 
 1+2를 더했던 것과 같이, CPU의 계산 방식처럼, 한번에 하나를 더하는... 기계어와
1대 1로 대응하는.. (모 학습지처럼, 기계와 눈높이를 맞추고 있는) 언어가 바로 어
셈입니다.

 숫자를 더하는 예를 들어서 설명드렸고요..
 
 다음에는... 맛보기 예제를 하나 만들어보도록 하죠..
 
 그럼 안녕히! 노력이 있으면, 결과가 있기 마련입니다.


 [42] 제목 : [새내기 연재] 어셈으로 만든 첫 프로그램
 올린이 : hanuly님(조광현  )    95/05/02 00:15    읽음 :  88  관련자료 있음(TL)

어셈블러?? 어셈블러?! 어셈블러!? 어셈블러!
─────────────────────
      Copyright(c)1995,.Sir hanuly        ┍━┯━━━━━━━━━━━━━┑
   어셈 새내기를 위한 어셈 기초 연재...   │Ⅱ│어셈으로 만든 첫 프로그램.│
              1995. 5. 2                  ┕━┷━━━━━━━━━━━━━┙
             
 ★ 기억하세요!
  * 이 연재는 어셈블러를 처음 배우시는 분들을 위해 마련되었습니다.
  * 개인적 질문 편지는, 되도록 '삼가'해주시고, '묻고 답하기' 란을 이용해주셨
  으면 합니다.
  * 내용상의 오류가 발견된 경우는 언제든지 편지로 연락주시기 바랍니다. 제 아
  이디는 아시죠? 'hanuly님'입니다.
  * 본 내용을 제 허가 없이 베끼거나(!), 복사(?)하는 행위를 하시면, 제가 화낼
  거예요.. 그리고, 저작권법에 의거(?)해 고소할거예요.. (히히.. 돈버는거 좋아
  하는 리님..)
  * 본 내용의 소스들은 거의 다 Macro Assembler 5.x를 기준으로 작업합니다. 매
  크로 어셈블러 5.x는 공개 자료실에 등록되어 있습니다.

────────────────────────────────어셈블이라..

 안녕하세요? 하누리님입니다.
 
 우리는 지난번에, 어셈블러가 도대체 뭔지에 대해서, 이야기를 했었죠. 어셈블러
를 배우는 것은 영어를 배우는 것, 영어를 배울때는 단어, 숙어, 문법.. 미국놈들
의 사고 방식, 환경 등까지 배워야 이해를 할 수 있다고 했었죠!

 오늘은 간단한 프로그램(?)을 만들어봄으로서, 어셈블러의 프로그래밍 개념에 대
해서 조금만 맛 보도록 할 것입니다. 아무튼, 노력만 한다면, 결과는 분명히 좋을
거라고 생각해요...

 씨나, 파스칼의 컴파일이라는 작업을 아시겠지요. 그러나, 어셈으로 소스를 코딩
하고, obj로 변환시키는 건, 컴파일한다고 하면 망신살이 뻗친다는 사실을 기억하
세요~!

 어셈블에 대해서 들어보셨는지요.  어셈블리어 소스를 기계어로 바꿔주는 과정이
라고 생각하시면 쉽습니다. 컴파일 정도에 견주어보시면 됩니다. 그리고, 이 어셈
블 과정을 해주는 것이 바로 이름하여 '어셈블러' 입니다.

────────────────────────어셈으로 프로그램을 짜는데..

 어셈 프로그램을 만들때는 보통 다음과 같이 하게 됩니다.

 1> 프로그램의 작성해야겠다!
  우선 시작을 위해서는 어떤 프로그램을 짤 것인지 목표를 정해야 겠죠?
 2> 에디터로 소스 프로그램을..
  산이나, Uedit등으로 소스를 작성합니다. → 저장함으로 ?.ASM이 만들어졌겠죠.
 3> masm이나, tasm으로 어셈블합니다. IF 실패?=True 그렇다면.. 1,2번으로..
    성공이라면.. obj파일이 생성되었겠죠!
 4> link나 tlink로 링크! If 실패?=True 그렇다면 1,2,3번...
    성공이면 .EXE.COM파일 생성!
 5> 실행테스트! if 실패?=True 그렇다면 2번...(논리적 오류이므로, 제작자의
    알고리듬 실수일 가능성이 제일 높습니다.)
 6> 성공?! -> 완성이닷!

────────────────────────────── 나의 첫 프로그램

 자, 어셈에 대한 개괄적 이해를 하게 되셨으리라 믿고(광현아! 설명도 x떡같이 해
놓고는 무슨 소리얏!! 끄아아~!), 간단한 다음과 같은 소스를 준비해 보았습니다.
 우리가, 터보씨를 배웠을때 맨처음 작성했던 프로그램이죠..
 "Hello, World!"라는 문자열을 출력하는 것입니다.  main안에서 printf만 써도 될
것 같은데.. 어셈이라는 점을 명심하시고..
┌hello.asm ───────────────────────────────┐
│  DOSSEG                                                                │
│  .MODEL SMALL               ;메모리 모델을 SMALL로!                    │
│                                                                        │
│  .STACK 100h                ;스택을 잡아둡니다.                        │
│                                                                        │
│  .DATA                                                                 │
│message db 'Hello, World!',13,10,'$' ;메세지는 바로...이것!             │
│                                                                        │
│.CODE                                                                   │
│                                                                        │
│mov ax,@data                  ;DS 초기화...                             │
│mov ds,ax                                                               │
│                                                                        │
│mov ah,9                      ;메세지를 화면에 찍는 부분                │
│mov dx,offset message                                                   │
│int 21h                                                                 │
│                                                                        │
│mov ah,4ch                    ;끝내는 부분..                            │
│int 21h                                                                 │
│                                                                        │
│END                                                                     │
└────────────────────────────────────┘  
 
                                                                             
 자, 다 작성하셨다면 (전문가 같이 보이게 하는 말로)어셈블링 시켜볼까요?
 masm이 준비되셨겠죠? 저는 분명히 5.0정도로도 충분하다고 말씀드렸습니다~!
┌────────────────────────────────────┐
│                                                                        │
│C:\LANG\MASM>masm hello←                                               │
│Microsoft (R) Macro Assembler Version 5.10                              │
│Copyright (C) Microsoft Corp 1981, 1988,   All right reserved.          │
│                                                                        │
│Object filename [hello.obj] : ← ;엔터로 그냥 넘어가세요.. 신경쓰지말고.│
│Source Listing  [NUL.LST] : ← ;소스가 어쩌구저쩌군요..그냥 무시합시다~!│
│Cross-reference [NUL.CRF] : ← ; 냠냠.. 그냥 넘어갑시다~!               │
│                                                                        │
│ ????? + ?????? Bytes symbol space free                                 │
│                                                                        │
│  0 Warning Errors                                                      │
│  0 Severe  Errors                                                      │
│                                                                        │
└────────────────────────────────────┘

 이렇게 되었다면 성공~! 그런데, 여기서 끝나면 바보.. 링킹해야죠..(연결??)
┌─────────────────────────────────────┐
│C:\LANG\MASM>link hello←                                                 │
│Microsoft (R) Overlay Linker. .... 어쩌구...                              │
│                                                                          │
│Run File [HELLO.EXE] : ←                                                 │
│                                                                          │
│List ...  : ←                                                            │
│Library...: ←                                                            │
│                                                                          │
 
└─────────────────────────────────────┘
 이런식으로 넘기면 hello.exe가 만들어집니다.
 자, 실행해보시죠?!
 
 그리고.. 결과는??
 
 간단한 맛배기 였습니다.
 
─────────────────────────────────────결론

 우리는 이 소스를 구경하고 입력해봄으로써, 어셈블러에 대해서 어느정도의 파악은
할수 있었습니다.

 다음에는 뭘할까요? 좀 따분한 내용을 다루도록 하죠. 그럼 이만...

-------------------------------
함께누리는 넓은 하늘아래 우리를 모으는 한울타리..  그리고, 상대방을 존중
할줄아는 그러한 말가짐새... 하누리님은 그러한 뜻을 가지고 있습니다.
 
출력이 끝났습니다. [Enter]를 누르십시오.

'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