어셈블리

어셈블리 강좌5

RSMaster 2019. 7. 9. 20:05

제6장 비교 분기와 서브루틴
--------------------------------------------------------------------------------
디버그(DEBUG)의 커맨드의 요약
-------------+----------------------------------------+-------------------------
커맨드       | 목적(Purpose)                          |명령
COMMAND      |                                        |형식(Format)
-------------+----------------------------------------+-------------------------
Assemble     |문장을 어셈블 한다.                        A [번지]
Compare      |메모리를 비교한다.                        C범위 번지
Dump         |메모리를 표시한다.                        D [번지]이거나
             |                                          D [범위]
Enter        |메모리를 변경한다.                        E 번지 [리스트]
Fill         |메모리 블록을 변경한다.                   F범위 리스트
Go           |선택적 브레이크 포인트를 갖고 실행한다.   G [=번지]
             |                                          [번지
             |                                          [번지...]]
Hexarithmetic|16진수 덧셈, 뺄셈                           H 값 값
Input        |입력 바이트를 읽거나 표시한다.            I 포트 번지
Load         |파일이나 절대 디스켓 섹터를 메모리에 로드한다. L [번지[드라이브
             |                                             섹터 섹터]]
Move         |메모리 블록을 전송한다.                   M 범위 번지
Name         |파일이나 패러미터를 정의한다.             [d:]
             |                                          [패스] 파일 이름[. 확장자]
Output       |출력 바이트를 내보낸다.                    O 포트 번지 바이트
Quit         |디버그 프로그램을 끝낸다.                 Q
Register     |레지스터나 플레그를 표시한다.             R [레지스터 이름]
Search       |문자를 찾는다.                            S 범위 리스트
Trace        |집행하고 표시한다.                        T [=번지][값]
Unassemble   |명령을 디어셈블 한다.                      U [번지]이거나
             |                                          U [범위]
Write        |파일이나 절대 디스켓 섹터를 기록한다.     W [번지][드라이브
             |                                          섹터 섹터]
-------------+------------------------------------------------------------------

 


6.1 반복 처리와 조건 분기 명령(conditional branch instruction)
--------------------------------------------------------------------------------
조건 분기 명령:반복을 할 때 필요 불가결한 요소

CMP     AX,0  ; AX레지스터의 값을 0과 비교하라
JE      100   ; 바로 위의 비교 결과가 같다면 100번지로 분기하라

CMP(compare) : 레지스터나 메모리 및 숫자의 크기를 비교합니다.
JE(Jump if Equal) 같다면 분기하라
JNE(Jump if Nor Equal) 같지 않다면 분기하라
JA(Jump if Above)  ~ 이상이라면 분기하라
JC(Jump if Carry)  캐리 플래그가 1이라면 분기하라

CMP +- 범용 레지스터(8/16비트)-+   +-레지스터(8/16비트)
    +- 메모리       (8/16비트)-+   +-메모리  (8/16비트)
                                   +-숫자    (8/16비트)
예제) JMP1.ASM
1부터 100까지 의 수를 더하는 프로그램
CODE    SEGMENT
        ASSUME  CS:CODE, DS:CODE
        MOV     AX, CODE    ; DS설정
        MOV     DS, AX
;
        MOV     CX,100   ;CX에 100을 지정
        MOV     AX,0     ;AX에 0을 지정
MEXT:   ADD     AX, CX
        SUB     CX,1
        CMP     CX,0     ; CX의 내용이 0인가 비교
        JNE     NEXT     ;0이면 밑으로 아니면 NEXT라는 라벨로 이동
        MOV     TOTAL, AX
        MOV     AH,4CH   ;끝내고 MS-DOS로 돌아간다.
        INT     21H
;
TOTAL   DW?
;
CODE    ENDS
        END

라벨(label)의 사용:
라벨이라는 것은 명령 등이 있는 번지에 붙여진 이름입니다.
라벨 이름의 직후에 콜론" : "을 넣는다

INC, DEC 명령:
레지스터나 메모리의 내용을 +/-1 하기 위한 명령으로서 INC(increment. 증가)
DEC(decrement. 감소) 명령이 준비되어 있습니다.
        SUB     CX,1
대신    DEC     CX로 쓸 수가 있습니다.

INC +- 범용 레지스터(8/16비트)
    +- 메모리       (8/16비트)
DEC +- 범용 레지스터(8/16비트)
    +- 메모리       (8/16비트)

또한 여산 직후에 그 결과가 0 인지 아닌지를 비교하여 분기하는 경우에는  비교 명령을
생략할 수가 있습니다.  플레그 레지스터의 동작에 관한 절에서 설명하겠지만 , ADD나
SUB, INC, DEC 등의 연산 명령 다음에 는 직접 조건 분기 명령을 넣을 수가 있습니다.
( 이 경우에 는 결과가 0으로 비교된 것으로 간주됩니다.)
따라서
NEXT:   ADD     AX, CX
        DEC     CX
        CMP     CX,0
        JNE     NEXT

NEXT:   ADD     AX, CX
        DEC     CX
        JNE     NEXT
가 됩니다.


반복을 끝내는 조건은 , 처음의 하나 , 마지막의 하나에 주의
 L1:    ADD     AX, CX
        INC     CX
        CMP     CX,100
        JBE
CX=100으로 해 버린 면 AX 에는 100 이 더해지지 않습니다.
그래서 반복하는 조건으로 CX < 100의 사이에서가 아니라  CX <=100의 조건 분기 명령
에는 JBE(Jump if Below or Equal)를 사용합니다.

DUP의사 명령과 블록 전송:
----------------------
        DB      16 DUP (?)
                   |
                   +---> DUP는 duplication의 약자
여기에서 행한 정의 중에 사용되고 있는 DUP는 () 속에 것을 () 왼쪽의 개수만큼 반복하
라는 의미입니다.

예제) JMP3.ASM
MSG1부터 저장된 10바이트를 MSG2로 전송하는 프로그램
CODE    SEGMENT
        ASSUME  CS:CODE, DS:DATA
;
        MOV     AX, DATA    ; DS설정
        MOV     DS, AX
        MOV     SI, OFFSET MSG1 ;SI에 MSG1 번지 값을 지정한다.
        MOV     DI, OFFSET MSG2
        MOV     CX,10
CONT:   MOV     AL, BYTE PTR [SI]  ;MSG1부터 MSG2로 1바이트 전송
        MOV     BYTE PTR [DI], AL
        INC     SI  ;전송하는 번지를 하나씩 증가시킨다.
        INC     DI
        DEC     CX   ;전송해야 할 개수의 카운트를 하나씩 줄인다.
        JNE     CONT
        MOV     AH,4CH  ; 끝내는 시스템 호출
        INT     21H
;
CODE    ENDS
;----------------
DATA    SEGMENT
MSG1    DB      'ABCDEFGHIJKLMNOP'
MSG2    DB      16 DUP (?)  ;16바이트만큼의 비초 기화 메모리를 확보
DATA    ENDS
        END

데이터 세그먼트(DS;data segment) 값을 아는 방법:
위 프로그램의 실행결과를 확인하려면 , MSG1 및 MSG2에 있는 번지를 알지 않으면  안 될
니다. 그러나 프로그램을 실행하기 전에는 DS의 값은  PSP(program segment prefix)의 
선두를  가리키고 있기 때문에 데이터를 읽을 수가 없습니다.

                     +--------------------------------------------------+
DS:데이터 세그먼트   |    +---------------+---> DS, ES                    |
   레지스터          |    |    PSP        |                             |
ES:엑스트라 세그먼트 |    +---------------+---> CS, SS                    |
   레지스터          |    |코드 세그먼트  |                             |
                     |    +---------------+<-- 여기에 세그먼트 번지를   |
                     |    |데이터 세그먼트|     알고 싶다                |
                     |    +---------------+                             |
                     +--------------------------------------------------+
                      메모리에 로드되었을 때의 EXE 파일의 예

실제로 데이터가 저장되어 있는 세그먼트 번지를 알 필요가 있습니다.
MOV     AX, DATA
MOV     DS, AX에서 DATA의 내용을 대치하는
MOV     AX,2244로 역 어셈블 됩니다. 2244는 각 PC의 상황에 따라 다릅니다.

-D2244:0,1F
2244:0000  41 42 43 44 45 46 47 48-49 4A 4B 4C 4D 4E 4F 50 ABCDEFGHIJKLMNOP

레지스터 읽는 R 커맨드에 의해 데이터 세그먼트의 값을 세트 한 다음, 다음과 같이  볼 수
도입입니다.
-RDS
DS 2232
:2244
-D0,1F
2244:0000 41 42 43 44 45 46 47 48-49 4A 4B 4C 4D 4F 50

조건 분기 명령에 대한 일람표를 만들었습니다.
+-----------------+----------------+---------------------+
|분기하기 위한 A, B |부호 없는 숫자인 |부호 있는 숫자인      |
|의 대소 관계      |경우            |경우                 |
+-----------------+----------------+---------------------+
|A> B              |JA/JNBE         |JG/JNLE              |
|A>=B             |JNB/JAE         |JGE/JNL              |
|A=B              |JZ/JE           |JZ/JE                |
|A <=B             |JBE/JNA         |JLE/JNG              |
|A <B              |JB/JNAE         |JL/JNGE              |
|A=\=B            |JNZ/JNE         |JNZ/JNE              |
+-----------------+----------------+---------------------+


6.2 무조건 분기 명령
--------------------------------------------------------------------------------
JMP 명령:
예제) JMP4.ASM
MSG1번지부터 시작하는 문자열을 MSG2번지 이후로 옮기는데 " $" 자가
나타날 때까지 행하는 프로그램

CODE    SEGMENT
        ASSUME  CS:CODE, DS:DATA
;
        MOV     AX, DATA       ;DS 설정
        MOV     DS, AX
        MOV     SI, OFFSET MSG1 ;MSG1번지를 SI에 지정
        MOV     DI, OFFSET MSG2
        MOV     DX,0
CONT:   MOV     AL, BYTE PTR [SI]
 |      CMP     AL, "$"          ;'$' 이면 종료
 |      JE      EXITP
 |      MOV     BYTE PTR [DI]
 |      INC     SI
 |      INC     DI
 |      INC     DX              ;전송 문자수의 카운트
 +---   JMP     CONT            ;무조건 반복
EXITP:  MOV     NUMBER, DX
        MOV     AH,4CH
        INT     21H
;
CODE    ENDS
;-------------------
DATA    SEGMENT
MSG1    DB      'ABCDEFGHIJKLMNO'
MSG2    DB      16 DUP (?)
NUMBER  DW?
DATA    ENDS
        END

종료를 나타내는 문자 (여기서는 "$" 자 )가 나타날 때까지 전송을 반복한다.


대문자 - 소문자 변환 루틴:
영어 대문자는 41H(=A)부터 5AH(=Z)까지 연속적으로 할당되어있습니다.
CODE    SEGMENT
        ASSUME  CS:CODE
MEXT:   MOV     AH,1   ; 1 문자 입력 시스템 호출 명령 사용
        INT     21H
        CMP     AL,1AH  ; CTRL+Z 이면 종료
        JE      EXITP
        CMP     AL, 'A'  ; 아스키코드 'A' 미만이면 L1으로
        JB      L1
        CMP     AL, 'Z'  ; 아스키코드 'Z' 보다 크면 L1으로
        JA      L1
        ADD     AL, 'a'-'A' ;상응하는 소문자를 찾기 위해'a'-'A'=20H를 더한다.
L1:     MOV     AH,2     ; 1 문자 출력 시스템 호출 명령 사용
        MOV     DL, AL
        INT     21H
        JMP     NEXT
EXITP:  MOV     AH,4CH
        INT     21H
CODE    ENDS
        END


6.3 비교 분기와 플레그 레지스터
--------------------------------------------------------------------------------
조건 분기 명령은 비교 명령이나 연산 명령의 결과에 의해 세트 된 플레그 레지스터의  값
에 따라서 분기할 것인지 아닌지를 결정하고 있는 것입니다.

플레그 레지스터의 각 비트의 의미
플레그 레지스터는 이와 같이 일반적인 레지스터와 달라서 , 여러 가지 상태를 기억하기
의한 레지스터입니다.
비트마다 독립된 의미를 가지고 있어 독립되어 작동한다.

CF:캐리 플레그
연산한 결과, 최상의 비트로부터의 높은 자리로 자리올림(캐리 CARRY) 혹은 최상의 비
트로부터의 빌림(borrow)이 발생한 경우에 1로 세트 되고 그 이외의 경우 0으로 리셋
된다. 그 밖의 목적으로 사용될 때도 있다.

PF:패리티 플레그
연산한 결과 , 1로 된 비트의 수가 짝수개(even number) 일 때 1로 세트도 고, 홀수
(odd number) 일 때 0으로 리셋된다.

AF:보조 캐리 플레그
8(16) 비트 연산에서 , 하위 4(8) 비트로부터 상위 4(8) 비트로 자리올림 혹은 빌림이 발
상한 경우에 1로 세트 되고 그 이외의 경우 0으로 리셋된다. 10 진 보정명령에  있어서
도 사용된다.

ZF:제로 플레그
연산한 결과 가 0으로 되었을 때에 1로 세트 되고, 그 이외일 때에 0으로 리셋된다.

SF: 사인 플레그
연산한 결과, 최상의 비트가 1 이 되었을 때 (즉 , 보수 표현으로 음수가 되었을 때)
1로 세트 되고, 그 이외일 때에 0으로 리셋된다.

OF:오버플로우 플레그(overflow flag)
연산을 부호가 달린 숫자로 했을 때 , 오버플로우 혹은 언더 플로우(under flow)가  발
상한 경우에 1로 세트 되고, 그 이외 일 때에는 0으로 리셋된다.

이상의 6가지 플레그는 연산의 결과에 따라서 세트 혹은 리셋되는 플레그로써 스테이터
스 플레그(status flag, 상태 플레그)라고 부릅니다.
플레그에는 그 이외에도 3 가지 더 있어서 , 그들은 CPU의 동작 상태를 제어하는 것으로
서 컨트롤 플레그(control flag, 제어 플레그)라고 부릅니다.

DF:디렉션 플레그(direction flag)
스트링 조작을 할 때에 이 플레그가 0  이면 번지를 나타내는 레지스터 값이  자동 적응
로 증가하고 1 이면 레지스터 값은 자동적으로 감소한다.

IF:인터럽트 플레그(interrupt. enable. flag)
이 플레그가 0일 때 INTR 단자로부터의 외부 인터럽트 요구는 무시되고 , 1일 때에는
외부 인터럽트 요구를 받아들일 수 있게 된다.

TF:트랩 플레그(trap flag)
이 플레그가 0 일 때 CUP는 보통대로 명령을 실행한다.
이 플레그가 1 일 때  CUP는 한 명령 시행할 때마다 자동적으로 내부 인터럽트(INT1)
를 발생하고 , 인터럽트 처리 루틴으로 들어간다.(단, 이 처리 루틴의 실행 중에는 트랩플
래그는 0으로 클리어 된다.) 프로그램의 추적(trace)에 사용된다.

캐리 플레그는 프로그램에 의해서도 세트 혹은 리세트 할 수 있습니다.

플레그 레지스터 제어 명령

STC(Set Carry flag)            캐리 플레그를  1로 세트 한다.
CLC(CLdar Carry flag)          캐리 플레그를 0으로 리셋한다.
CMC(CoMplement Carry flag)     캐리 플레그가 0이면 1로 , 1이면 0으로 세트 한다.
STD(Set Direction flag)        디렉션 플레그를 1로 세트 한다.(번지를 자동적으로
                               감소시키는 방향)
CLD(CLear Direction flag)      디렉션 플레그를 0으로 리셋한다.(번지를 자동 적응
                               로 증가 시티는 방향)
STI(Set Interrupt-enable flag) 인터럽트 플레그를 1로 세트 하고 인터럽트를 받아
                               들일수 있는 상태로 한다.
CLI(CLear Interrupt-enable flag) 인터럽트 플레그를 0으로 하고, 인터럽트를 걸지 못하
                               게 금지 상태로 한다.
LAHF(Load AH from Flags)       플래그 레지스터의 하위 8비트를 AH레지스터로 전송한
                               다. 사용하지 않는 비트의 값은 정해져 있지 않다.
SAHF(Store AH into Flags)      AH레지스터의 내용을 플래그 레지스터의 하위 8비트
                               에 전송한다.
PUSHF(PUSH Flags onto stack)  플레그 레지스터의 내용을 스택(stag)상에 푸시(PUSH)
                               한다.
POPF(POP Flags off stack)     스택 위의 워드 데이터를 플레그 레지스터에 POP 한다.

비교 연산 명령과 조건 분기 명령의 관계:
비교 연산 명령은 뺄셈 명령과 같은 것입니다. 다른 점은 비교한 결과 비교를 행한 레지스
터나 메모리의 내용은 전혀 변하지 않고 단지 플레그 레지스터만을 변화시킨다.
CMP     AX, BX
라고 했을 때, AX 레지스터로부터 BX 레지스터의 내용을 (가상적으로 ) 뺀 다음, 그 결과
에 따라서 플레그 레지스터 만을 변화시키고 AX, BX의 내용은 보존됩니다.
SUB     AX, BX
라고 하면 플레그 레지스터는 완전히 똑같게 세트 되지만, AX 레지스터의 값도 변화되어 
AX에서 BX를 뺀 결과가  AX의 값이 되어 버립니다.
플레그 레지스터의 값은 산술 논리 연산을 행할 때에 세트 리셋되고, 전송 명령이나 분기
명령을 실행해서는 변화하지 않습니다.

조건 분기 명령의 정리

+--------+---------+---------------+--------+---------------+
|A, B의  |부호 없는 |조건을 만족하는|부호 있는|조건을 만족하는|
|대소 관 |숫자의   |플레그의 상태 |숫자의   |플레그의 상태 |
|계      |경우     |CF             | 경 우  |SFⓧOF |  ZF   |
+--------+---------+-------+-------+--------+-------+-------+
|A> B     |JA/JNBE  | 0     | 0     |JG/JNLE | 0     | 0     |
|A>=B    |JNB/JAE  | 0     | *     |JGE/JNL | 0     | *     |
|A=B     |JZ/JE    | *     | 1     |JZ/JE   | *     | 1     |
|A <=B    |JBE/JNA  | 1     | 1     |JLE/JNG | 1     | 1     |
|A <B     |JB/JNAE  | 1     | *     |JL/JNGE | 1     | *     |
|A=\=B   |JNZ/JNE  | *     | 0     |JNZ/JNE | *     | 0     |
+--------+---------+-------+-------+--------+-------+-------+
A, B의 대소 관계는 , CMP A, B 즉(A-B)의 결과로 표시된다.
ⓧ는 배타적 논 이합(exclusive OR)을 구하는 연산이다.
* 표시란의 값은 0이라도 1이라도 조건 판단에 영향을 주지 않는다.

JA/JNBE 와 같이 / 로 구분된 2개의 표현은 , 완전히 같은 관계를 다른 말로 표현한
것으로서 , 어느 쪽을 사용해도 상관이 없다.
단, 디버그에 의한 디스 어셈블 결과는 왼쪽의 표현으로 된다.
조건 분기 명령에서는 쇼트 점프(short jump) 즉, 명령이 있는 다음 번지로부터 +127 ~
-128바이트 이내로 만 분 기할수 있다.
+------+------------+------------+------------------------------------------+
|플레그|1일 때의 분기|0일 때의 분기|플레그가 1일 때의 의미                     |
+------+------------+------------+------------------------------------------+
|SF    |JS          |JNS         |보수 표현으로 음수이다.                    |
|ZF    |JZ/JE       |JNZ/JNE     |결과가 0이다.(위표의 A=B와 같다)         |
|PF    |JP/JPE      |JNP/JAE     | 1이 된 비트의 수가 짝수이다.              |
|CF    |JB/JNAE     |JNB/JAE     |캐리 플레그가 1이다(연산한 결과 자리올림    |
|      |            |            | 혹은 빌림이 발생한 경우 등)              |
|OF    |JO          |JNO         |보수 표현을 사용한 연산에서 결과가 범위를  |
|      |            |            |초과했다.                                 |
+------+------------+------------+------------------------------------------+
|레지스터           |            | 명령의 의미                              |
+------+------------+------------+------------------------------------------+
|CX    |  ------    |   JCXZ     | CX레지스터가 0일 때만 분기한다.           |
+------+------------+------------+------------------------------------------+

조건 분기 명령 약자를 읽는 법
A:Above     위(부호 없음)        O:Overflow    오버플로우
B:Below     아래(부호 없음)        Odd         짝수
E:Equal     0 혹은 같음         P:Parity      패리티
G:Greater   위(부호 있음)        S:Sign        부호가 음수
L:Less      아래(보호 있음)      Z:Zero  0     혹은 같음
N:Not       ~ 가아니다

JNGE는
Jump if Not Greater or Equal
이므로(A>=B), 즉 A <B라는 의미가 됩니다.

플레그 레지스터 표시를 보는 법:
DEBUG에서는 , R 커맨드로  레지스터의 내용을 표시했을  때 그리고 추적(trace)  고우
(GO)인 T, G 커맨드로 실행을 추적했을 때에, 각종 레지스터의 내용과 함께 플레그 레지
스터의 내용도 표시됩니다. 플레그 레지스터의 표시는 숫자나 1,0에 의한 것이  아니
라 각 플레그의 명칭을 사용하여 표시됩니다.
각 플레그의 0,1의 상태와 표시를 대응시킨 것은 다음과 같습니다.
+-----------+---+---+---+---+---+---+---+---+
|플레그 이름|OF |DF |IF |SF |ZF |AF |PF |CF |
+-----------+---+---+---+---+---+---+---+---+
| 0         |NV |UP |DI |PL |NZ |NA |PO |NC |
+-----------+---+---+---+---+---+---+---+---+
| 1         |OV |DN |EI |NG |ZR |AC |PE |CY |
+-----------+---+---+---+---+---+---+---+---+


+--------------------+--------------------+-----------------+
|플레그 이름         |  0                 |   1             |
+--------------------+--------------------+-----------------+
|Overflow flag       | Not oVerflow       |OVerflow         |
|Direction flag      | UP                 |DowN             |
|Interrupt flag      | Disable Interrupt  |Enable Interrrupt|
|Sign flag           | PLus               |NeGative         |
|Zero flag           | Not Auxiliary carry|ZeRo             |
|Auxiliary Carry flag| Parity Odd         |Auxiliary Carry  |
|Parity flag         | Parity Odd         |Parity Even      |
|Carry flag          | Not Carry          |Carry            |
+--------------------+--------------------+-----------------+

MOV     BX,8888H
CMP     BX,0
0과 비교한 결과 8888H - 0H의 결과에 따라서 플래그 레지스터가 세트 됩니다.
뺄셈의 결과는 8888H  이므로 최상위 비트가 1 이 되고 , 사인 플래그는 NG(음수)가
됩니다. 그러나 빌림은 발생하지 않았으므로 , 캐리 플래그는 NC (0)가 되어 있습니다.
오버플로우 플래그가 NV(0)입니다.
따라서 부호 없는 비교 JB는 조건을 만족하지 않으므로, 다음 명령으로 진행합니다.
여기에서 부호 있는 비교 JL은 조건을 만족하고 있으므로 , 지정한 번지로 분기합니다.

주의 )
1을 더하는 명령을 ADD명령으로 한경우와 , INC 명령으로 한 경우
덧셈 결과는 양쪽 다 같지만 플래그 레지스터의 변화는 달라집니다.
보통의 산술 연산 명령에서는 자리 올림 혹은 빌림이 발생한 경우는 캐리 플래그가  세
트 되지만 , INC 명령과 DEC 명령만은 특수해서, 이 2 종류의 명령만은 자리올림이나 빌
림이 발생해도 캐리 플래그에 영향을 주지 않습니다.
이 때문에 자리올림()을 사용하여 조건 판단을 하는 명령 앞에 INC 명령, DEC 명령을 놓을
때는 주의가 필요합니다.  캐리 플래그는 , 이들 명령보다 앞의 상태를 계속  가지고 
있습니다.
INC , DEC 명령이 이와 같은 성질을 갖고 있다는 것은   결코 불편한 것이 아니라 , 오히
려 실제 프로그램 상에서 느 도움이 되는 경우가 많습니다.
캐리 플래그를 제외한 다른 플래그 레지스터는 INC, DEC 명령의 결과에 따라서 세트 혹
은 리셋됩니다.

6.4 서브루틴의 사용법
--------------------------------------------------------------------------------
서브루틴(subroutine)이란
프로그램 중에서 여러 번 되풀이 사용되는 부분을 메인 루틴으로부터 독립하여 작성
프로그램을 효율적으로 하는 효과가 있다.
프로그램을 처리 내용에 따라 불럭으로 나눔으로써 전체를 보기 쉽도록 해 주고 , 보수
를 쉽게 만들어 주는 이점이 있습니다.
어셈블러의 서브루틴은 BASIC과 비숫한 사용방법이다.
서브루틴의 끝을 나타내는 RET 명령으로 끝납니다.
RET (return command )는 서브 루딘으로부터 메인 루틴으로 돌아오는 명령입니다.
서브루틴을 부를 때는 CALL 다음에 서브루틴의 시작에 붙여진 라벨을 사용합니다.
예) CALL1.ASM
0부터 9까지의 숫자를 출력하는 프로그램이다. 단 한 자 한자 출력하는 서브루틴을 만
들어  호출한다.
CODE    SEGMENT
        ASSUME  CS:CODE
;
        MOV     CX,0
NEXT:   CALL    SUB1   ;CALL명령으로 서브루틴을 호출
        INC     CX
        CMP     CX,10
        JB      NEXT
        MOV     AH,4CH
        INT     21H
;
SUB1:   MOV     DX, CX
        ADD     DL, '0'
        MOV     AH,2
        INT     21H
        RET         ;리턴 명령
CODE    ENDS
        END
서브루틴 SUB1속에서는 , CX 레지스터의 값을 변경시키지 않도록 CX 레지스터의 값을
DX 레지스터로 전송한다
16 진수 값의 화면상에 출력 법:
기본구상 _ AL레지스터의 내용을 표시하려고 한다면, AL=3FH 일 경우
"3"와 "F" 와의 2가지 문자를 출력하지 않으면 안 된다.
AL레지스터의  내용을 상위 4비트와 하위 4비트로 나누어서 , 2번에 걸쳐 출력해야 한
다.
+--------------------------------------------------------+
|  AL         DL                                         |
|+--+--+    +--+--+                                      |
||3 |F |--->|0 |F | ---> 아스키코드로 변환 --> 출력      |
|+--+--+    +--+--+                                      |
|             DL                                         |
|  |        +--+--+                                      |
|  +------->|0 |3 | ---> 아스키코드로 변환 -->  출력     |
|           +--+--+                                      |
+--------------------------------------------------------+
 레지스터의 비트 단위로 데이터를 취급하려면, 보통 논리 연산 명령이나, 쉬프트(이동)
로테이트(rotate, 회전 ) 명령을 사용한다.
여기서는 학습을 위해 곱셈과 나눗셈을 사용한다.

                     AL          3F
                                 \/ * 10H
                     AX         03F0
                                / |
          상위 자리 보존 <--- 03   |
                     AH      00   |
                                \ \/
                     AX         00F0
                                 \/  %10H
                     AX         000F
                                  \/
                    <--------     0F


예제) PUTAL.ASM
AL레지스터 내의 값을 ASCII코드 16 진수 값으로 화면에 표시하는 프로그램
(서브루틴을 사용한다.) AL 내의 값은 3FH값을 예로 들었다.
CODE    SEGMENT
        ASSUME  CS:CODE, DS:CODE
;
        MOV     AX, CODE  ; DS의 초기치 설정
        MOV     DS, AX
;
        MOV     AL,3FH   ;처음 AL에 3FH가 들어 있다고 하자
        CALL    PUTAL    ;서브루틴을 호출한다.
;
        MOV     AH,4CH
        INT     21H
;-----------------------
PUTAL:  MOV     BL,10H    ;AL을 10H 하면 AH, AL                 -----+상위 자리
        MUL     BL        ;               03 F0 가 된다.            |하위 자리로
        MOV     LEVEL 2, AH ;                                         |분할하는
        MOV     AH,0      ;AX를 10H 분의 1로 나누면 AH AL          |서브루틴
        MOV     BL,10H                               00 0F 가 된다 |
        DIV     BL                                                  |
        MOV     LEVEL 1, AL  ; 하위 자리 보존                          |
        MOV     DL, LEVEL 2  ; -+ 상위 자리 표시                       |
        CALL    PUTHEX       -+                                     |
        MOV     DL, LEVEL 1  ; -+  하위 자리 표시                      |
        CALL    PUTHEX       -+                                     |
        RET                                                     ----+
;---------------------------
PUTHEX: CMP     DL,0AH
        JAE     HEX2
        ADD     DL, '0'    ; 0의 ASCII 코드 값이 30H이다.   ----+수치에 대한
        JMP     HEX3          (0에서 9 인경우)                  |아스키코드를
HEX2:   ADD     DL, 'A'-0AH ;   A에서 F 인경우                   |산출, 표시하는
HEX3:   MOV     AH,2           'A'의 ASCII코드값은 41이다       |서브루틴
        INT     21H            'B'는 42가 표시 도기 위해서는      |
        RET                    41+1이 되어야 하는데 이것은       |
                              'A'+(BH-AH)의 계산으로 가능하다. --+
LEVEL 2  DB?
LEVEL 1  DB?
;
CODE    ENDS
        END

서브루틴 PUTAL 속에서 다시 서브루틴 PUTHEX를 호출할 수도 있습니다.
여기에서 출력 시스템 INT 21은 DL 레지스터에 ASCII코드값을 넣을 때 그에 해당하는
ASCII 문자가 화면에 출력된다.
0에서 9까지의 숫자에 대응하는 아스키코드와 A에서 F까지 대응하는 아스키코드
가 연속하여 있지 않으므로 , 숫자가 0AH 미만인지 아닌지 에 따라 변환을 위해서 더하
는 숫자가 달라집니다.
+-------------+--------------+--------------+
| 문자        |    0 ~  9    |  A ~  F      |
| 아스키코드 |   30 ~ 39    | 41 ~ 46      |
+-------------+--------------+--------------+
숫자를 표시할 경우에는 윗자리를 먼저, 아랫자리를 나중에 표시하지 않으면 안 됩
니다. 구한 순서와 표시한 순서가 일치하지 않을 때도 있으므로 , 그 와 같은 경우에는
각 자리의 숫자를 보존해 두는 것이 필요합니다.

PROC에 의하 서브루틴의 구조화:
서브루틴을 보다 구조적으로 표현하기 위해서 프로시듀어(procedure) 의사 명령에  의해 
서브루틴의 앞뒤를 명시하도록 합니다.

SUB1    PROC
    서브루틴 본체
SUB1    ENDP

16 진수 값의 입력법
예제) GETAL.ASM
키보드로부터 ASCII코드를 입력하여 이 코드에 해당하는 ASCII 문자 출력

CODE    SEGMENT
        ASSUME  CS:CODE
CR      EQU     0DH
LF      EQU     0AH
;
NEXT:   CALL    GETAL  ; 키보드로부터 두 개의 숫자 입력
        JC      EXITP  ;에러이면 끝냄
        MOV     DL.AL
        CALL    PUTASC  ;아스키코드의 출력
        JMP     NEXT
EXITP:  MOV     AH,4CH  ;종료하여 MS-DOS로 돌아간다.
        INT     21H
;-----------------------
GETAL   PROC
        MOV     AH,1   ; 우선 한자를 입력한다.
        INT     21H
        CALL    CVTAL   ; 아스키코드 --->  숫자
        JC      GETEND  ;에러이면 되돌아 간다.
        MOV     DH, AL   ; 보존
        MOV     AH,1    ;두 번째 글자 입력
        INT     21H
        CALL    CVTAL  --- 아스키코드 ---> 숫자
        JC      GETEND  ;에러이면 되돌아간다.
        MOV     DL, AL
        MOV     BH,10H  ;상위 자리를 10배
        MOV     AL, DH
        MUL     BH
        ADD     AL, DL   ;하위 자리와 더한다
GETEND: RET
GETAL   ENDP
;--------------------------
CVTAL   PROC
        CMP      AL, '0'
        JB      ERR1      ;ASCII 코드값으로부터
        CMP     AL, '9'     30H보다 적을 경우 ERR1
        JA      SKIP1
        SUB     AL, '0'
        JMP     SKIP2
SKIP1:  CMP     AL, 'A'    ---+ 'A'~'F'이면
        JB      ERR1         | 아스키코드의
        CMP     AL, 'F'       | 'A'를 빼고
        JA      ERR1         | 0AH를 더한다.
        SUB     AL, 'A'-OAH  -+
SKIP2:  CLC                  ;에러가 없으면
        JMP     CVTEND         캐리를 클리어
ERR1:   STC                  ;에러이면 캐리를 세트
CVTEND: RET
CVTAL   ENDP
;---------------------
PUTASC  PROC
        MOV     DH, DL        ; DL 에있는 ASCII코드 16진 값을 잠깐 대피시킨다.
        MOV     DL, ' ' -+    ; DL에 있는 아스키코드 16진 값을 잠깐 대피시킨다.
        MOV     AH,2    |공백 출력
        INT     21H    -+
        MOV     DL, DH  -+   ; 대피시켰던 것을 되찾는다
        MOV     AH,2    |아스키코드 출력
        INT     21H    -+
        MOV     DL, CR  -+    ; carriage return을 출력
        MOV     AH,2    | 복귀 개행
        INT     21H     |
        MOV     DL, LF   |     ; line feed
        MOV     AH,2    |
        INT     21H    -+
        RET
PUTASC  ENDP
CODE    ENDS
        END

CVTAL은 , GETAL로부터 호출된 서브루틴에서 아스키코드를 숫자로 변환하는 것입니다.
만일 숫자 이외에 입력이 있을 때에는 캐리 플래그가 세트 되어 돌아옵니다.
PUTASC는 1 문자를 띄고 지정된 아스키코드에 상당하는 문자를 출력하고 , 줄을  바꿔
는 것입니다.
GETAL에서는 캐리 플레그가 세트 되어 있는지 어떤지를 체크하여, 세트 되어있으면 에러로 
간주 메인 루틴으로 돌아옵니다.
메인 루틴에서도, 역시 캐리 플래그가 세트 되어 있다면 에러로 간주하고 처리를 끝 냅니
다. 캐리 플래그에 의해서 분기하는 명령이 JC명령입니다.

'어셈블리' 카테고리의 다른 글

어셈블리 강좌7  (0) 2019.07.09
어셈블리 강좌6  (0) 2019.07.09
어셈블리 강좌4  (0) 2019.07.09
어셈블리 강좌3  (0) 2019.07.09
어셈블리 강좌2  (0) 2019.07.09