있읍니다.
오버플로우 플래그가 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 LEVEL2,AH ;
|분할하는
MOV AH,0 ;AX를 10H 분의 1 로 나누면 AH AL
|서브루틴
MOV BL,10H 00 0F 가 된다 |
DIV BL |
MOV LEVEL1,AL ; 하위자리 보존 |
MOV DL,LEVEL2 ; -+ 상위자리 표시 |
CALL PUTHEX -+ |
MOV DL,LEVEL1 ; -+ 하위자리 표시 |
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)의 계산으로 가능하다. --+
LEVEL2 DB ?
LEVEL1 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명령입니다.
제 목:[강좌] 어셈블리 강좌 [6/7]
올린이:유씨가족(유용수 ) 96/11/02 19:35 읽음:3295 관련자료 없음
-----------------------------------------------------------------------------
6.5 프로시듀어의 배치
----------------------------------------------------------------------
----------
PROC ~ ENDP 는 사람이 보기 편하기 위한 구분에 불과하다.어셈블러는
실제로 프로시
듀어를 구별하지 않는다.
예제)CALL2.ASM
CODE SEGMENT
ASSUME CS:CODE
EOF EQU 1AH
START: CALL GETCH
JC EXITP ;--+여기에서 프로시듀어를 호출하고 있다.
MOV DL,AL |
CALL PUTCH ;--+
JMP START
EXITP: MOV AH,4CH
INT 21H
GETCH PROC
MOV AH,8
INT 21H
CMP AL,EOF
JNE GETCHEND
STC
RET
GETCHEND: ;긴이름의 라벨도 사용가능 그때라벨만의
행으로 해도좋다
CLC
RET
GETCH ENDP
PUTCH PROC
MOV AH,2
INT 21H
RET
PUTCH ENDP
CODE ENDS
END START ;실행시작을 알리는 문장
END 문이 가지는 기능: 프로그램의 시작번지를 지정한다.
이와같은것은 여러개의 코드 세그먼트를 가진 프로그램을 작성한다든지
,프로시듀어와 메인 루틴의 순서를 바꾼 프로그램을 작성하는 경우에는
절대로 필요하다.
예제)CALL2-2.ASM
CODE SEGMENT
ASSUME CS:CODE
EOF EQU 1AH
GETCH PROC
MOV AH,8
INT 21H
CMP AL,EOF
JNE GETCHEND
STC
RET
GETCHEND: ;긴이름의 라벨도 사용가능 그때라벨만의
행으로 해도좋다
CLC
RET
GETCH ENDP
PUTCH PROC
MOV AH,2
INT 21H
RET
PUTCH ENDP
START: CALL GETCH ; 메인 루틴의 시작
<--------------------+
JC EXITP ;--+여기에서 프로시듀어를 호출하고 있다.
|
MOV DL,AL |
|
CALL PUTCH ;--+
|
JMP START
|
EXITP: MOV AH,4CH
|
INT 21H
|
|
CODE ENDS
|
END START ;실행시작을 알리는 문장
>------------------+
제7장 모듈별 프로그램의 작성법
----------------------------------------------------------------------
----------
7.1 INCLUDE 의 방법
----------------------------------------------------------------------
----------
모듈화의 이점: 프로그램을기능 별로 분활하여 알기쉽게 하는 것과
걔발효율을
향상시킬수있다
아무리 같은 루틴을 사용할수있다고 하더라도 ,다른 프로그램을
만들때마다. 같은
루틴을 키보드로 부터 입력하지 않으면 안된다는 것은 ,역시귀찮은
작업입니다.
그래서 완성된 루틴의 부분을 미리 다른 화일 로 부터 작성해두고,메인
루틴만을 에디터로 부터 입력하여,서브루틴인 부분은 나중에 화일 단위로
결합시키는 방법을 생각해
냈읍니다.이것이 어셈블러에 있는 인클루드(INCLUDE) 기능입니다.
이기능을 사용할 때에는 우선 ,서브루틴 등의 부분을 독립시킨 화일로서
작성해두고 ,메인 루틴을 작성할때에 그중 필요한 부분으로 INCLUDE
의사명령을 사용하여
"INCLUDE 화일 이름 " 으로 한다음 ,결합하는 화일 이름을
지정합니다.이렇게 해두면
메인 루틴을 어셈블할때에 자동적으로 필요한 화일이 결합되어
어셈블됩니다.
CODE SEGMENT
ASSUME CS:CODE,DS:CODE
;
INCLUDE PUTAL.SUB ; PUTAL.SUB 화일의 내용을 인용한다.
;
START: MOV AX,CODE
MOV DS,AX
MOV AL,3FH
CALL PUTAL
MOV AH,4CH
INT 21H
CODE ENDS
END START
B 드라이브에 PUTAL.SUB 화일이 들어 있을때
INCLUDE B:PUTAL.SUB
INCLUDE 로 전개된 부분의 어셈블된리스트 화일은 "C " 가 붙어있다.
동일 변수에 ,여러개의 데이터를 나열할때에는 다음 행에 변수이름을
가지지않는
DB(define byte) 선언 등을 계속한다.
NUM DB 12H,34H
는
NUM DB 12H
DB 34H
라고 한것과 같다.
레지스터의 대피:
MOV BXSAVE,BX --+
CALL PUTAL | BX 레지스터의 값을 대피했다가 복귀한다.
MOV BX,BXSAVE --+
..........
BXSAVE DW ? ; 변수 선언
7.2 PUSH,POP 명령
----------------------------------------------------------------------
----------
PUSH,POP 명령은 ,스택 영역이라고 부르는 특별한 영역에 대하여 데이터를
기록,읽어내는 명령입니다.
스택동작(stack operation)
마지막에 넣은 것을 처음에 꺼내는 동작(LIFO,Last In First Out)을
스택동작
메모리는 순서대로 나열되어 있으므로 ,현재어디까지 데이터가
쌓여있는지를 나타내기
위하여,스택 포인터(stack pointer,ST 레지스터의 일종)에 마지막에
놓여진 데이터가 있는 메모리의 번지를 기억하도록 되어있읍니다.
새로운 데이터를 쌓아올리면 SP 값은 감소 하고,데이터를 꺼내면 SP 값은
증가
번지 메모리
하위번지 | |
FFFA +-------+
FFFB +-------+
FFFC +-------+ <-- SP(스택포인터)에 의해
FFFD +-------+ 지시되는 메모리(마지막에 넣어진 데이터가
FFFE +-------+ 들어 있다.)
FFFF +-------+
주의) 보통의 경우와 ,데이터의 저장법과 순서가 거꾸로 되어 있으므로
주의가 요함
8086의 PUSH, POP 명령에서는 반드시 2 바이트의 데이터가 한번에
읽어들여지고 꺼내
어지므로, SP 의 값은 항상 2씩 증감합니다.
스택영역은 보통 메모리의 최상위 번지로 설정되지만 이것은 8비트 CPU
와 같이 메모리공간이 좁은 시스템에 있어서 프로그램과 스택상의
데이터가 충돌하지 않게 하기위
해서 였읍니다.
8086에서는 보다 넓은 메모리 공간을 사용할수 가 있으므로 ,스택 동작을
위해서 스택 세그먼트를 독립하여 설정하고 있읍니다.
SP 가 가리키는번지는 실제는 스택 세그먼트(SS)가 가리키는 베이스
번지로 부터의 오프셋 번지입니다.
스택 세그먼트의 설정법에 의해 프로그램이나 데이터와 스택롸를 독립하여
배치할수도 있고,또한 8지트 CPU와 같이 프로그램과 스택을 동일한 메모리
공간에 배치할 메모리 공간에 배치할수도 있읍니다.
어느경우에서도 스택 포인터의 초기치를 적당하게 설정함으로써 적당한
번지를 스택영역의 시작 번지로 할수가 있는데 ,보통은 최상위 번지로
부터시작합니다.
CS -->+--------+--- 프로그램 | |
+--------+ CS,SS ->+------------|
+--------+ | |프로그램
SS -->+--------+ +------------+
+--------+ SP스택영역 +------------+<-- SP
+--------+ +------------+ 스택영역
CS와 SS 를 독립시킨 경우 CS와 SS를 일치시킨 경우
( 8비트 CPU 와 같은 사용법 )
+-----------------------------------------+
| PUSH +- 16비트 범용 레지트터 -+ |
| | 세그먼트 레지스터 | |
| +- 16비트 메모리 -+ |
| POP +- 16비트 범용 레지스터 -+ |
| | 세그먼트 레지스터 | |
| +- 16비트 메모리 -+ |
+-----------------------------------------+
*.POP CS 명령만은 존재하지 않는다.
IP를 PUSH, POP 하는 명령은 없다.
플레그 레지스터를 PUSH, POP 하는 명령으로는 위와 별도로 PUSHF,
POPF명령을 사용
PUSH, POP 명령의 실제 사용 예:
레지스터의 대피가 필요 하게 된것은 서브루틴 중에서 레지스터의 내용이
파괴 되기 때문입니다. 어느 레지스터의 내용이 파괴되는지는 서브루틴의
내용을 보지않고서는 알수없읍니다. 그래서 레지스터의 대피, 복귀는 메인
루틴쪽이 아니라 서브루틴 쪽에서 하는 것이 적절 합니다.
+--------------+ +--------------+
| 메인루틴 | |메인루틴 |
| ........ | | ..... |
| PUSH BX | |CALL PUTAL |
| CALL PUTAL | <--+ | ..... |
| POP BX | | | |
+--------------+ | +--------------+
+--------------+ | +---------------+
|서브루틴 +-----+ |서브루틴 |
+--------------+ |PUSH BX |
PUSH ,POP 를 메인루틴쪽에서 | ..... |
행한경우의 프로그램 |POP BX |
+---------------+
PUSH,POP를 서브루틴쪽에서 행한
경우의 프로그램
완전히 같은 동작을 하지만 오른쪽이 더 좋은 프로그램입니다.
메인 루틴쪽에서 어느 레지스터가 서브루틴 내에서 변경이나 파괴되는가를
생각하지
않아도 되기 때문입니다.
서브루틴을 완전히 블랙 박스 로서 사용할수가 있기 때문입니다.
이와 같이 함으로써 서브루틴의 모듈화가 점점 효율적으로 됩니다.
예제)PUSH1.ASM
BL에 초기치로 주어진 어떤 8비트 값()을 1배,2배,..., F배 한수치를
출력한다.
CODE SEGMENT
ASSUME CS:CODE,DS:CODE
;
INCLUDE PUTAL2.SUB
INCLUDE CRLF.SUB
;
ENTRY: MOV AX,CODE -+
MOV DS,AX -+DS설정
;
MOV BL,SEED ;초기 데이터 21H를 BL 에 지정한다.
MOV CL,1
NEXT: CMP CL,10H ;10H배이면 끝남.
JE EXITP
MOV AL,CL -+초기 데이터의 BL배를 구한다.
MUL BL, -+
PUSH AX --+
MOV AL,AH -+-+---- 상위 바이트 출력
CALL PUTAL -+ |
POP AX --+-- AX레지스터를 보존(출력에 AL 레지스터를
사용하기
CALL PUTAL ;하위 바이트 출력
때문 )
CALL CRLF ;개행
INC CL
UMP NEXT
EXITP: MOV AH,4CH -+
INT 21H -+끝내고MS-DOS 로 귀환
;
SEED DB 21H ;초기 데이터
CODE ENDS
END ENTRY
PUTAL2.SUB
PUTAL:
+--- PUSH AX -+;서브루틴 속에서 레지스터
|+-- PUSH BX | AX,BX,DX 를 대피하고 있다
||+- PUSH DX -+
||| MOV BL,10H
||| MUL BL
||| MOV DL,AH
||| CALL PUTHEX ; 상위자리를 출력
||| MOV AH,0
||| MOV BL,10H
||| DIV BL
||| MOV DL,AL
||| CALL PUTHEX ;하위자리를 출력
||+- POP DX -+ AX,BX,DX 를 복귀
|+-- POP BX | (PUSH와 의 역순)
+--- POP AX -+
RET
;
PUTHEX: PUSH AX
PUSH DX
CMP DL,0AH
JAE HEX2
ADD DL,'0'
JMP HEX3
HEX2: ADD DL,'A'-0AH
HEX3: MOV AH,2
INT 21H
POP DX
POP AX
RET
CRLF.SUB
CRLF:
+-- PUSH AX
|+- PUSH DX
|| MOV DL,0DH ;CR(복귀)을 출력
|| MOV AH,2 -+
|| INT 21H -+
|| MOV DL,0AH
|| MOV AH,2 -+ ;LF(개행)을 출력
|| INT 21H -+
|+- POP DX
+- POP AX
RET
PUSH,POP 명령은 16비트로 된 레지스터에 ,혹은 메모리에 대피,복귀밖에
하지 못하므로 ,서브루틴 내의 DL레지스터 밖에 사용하지 않는경우에도
DX레지스터로서 대피한다.
PUSH AX ; AX의 내용을 BX에 전송
POP BX
직접 전송을 허용하지 못하는 세그먼트간의 값을 전송하는데 유효합니다.
예) PUSH CS
POP DS
가장주의 해야할점:
하나의 서브루틴 중에서 PUSH 한것은 그 루틴 내에서 반드시 POP 한다.
7.3 프로그램의 모듈화와 링클방법
----------------------------------------------------------------------
----------
모듈화된 프로그램을 따로 별도로 어셈블하고 ,LINK에 의해 결합 할수도
있읍니다.
+---------------------------------------------------------------------
--------+
|메인 프로그램 서브프로그램 메인 프로그램 서브프로그램
|
| | | \/ \/
|
| +-> MASM <-+ MASM MASM
|
| \/ \/ \/
|
| 오브젝트 프로그램 오브젝트 프로그램 오브젝트
프로그램 |
| \/ \/
|
| LINK LINK
|
| \/ \/
|
| 실행가능 프로그램 실행가능 화일
|
|
|
| 어셈블단계에서 결합하는경우 링크 단계에서 결합하는
경우 |
+---------------------------------------------------------------------
--------+
오브젝트 단계에서 링크가 가능하기 때문에 ,MS-DOS에서느 공동형식ㅇ의
오브젝트 화일을 작성하는 어셈블러나컴파일러의 출력을 합쳐서 하나의
실행가능 프로그램하는것이 가능하다.
이때문에 어셈블러와 C 언어와 의 링크 등이 가능하게 됩니다.
그러나 링크의 기능을 잘 사용하기 위해서는 ,많은 형식사의 약속이나
링커의 동작을 이해하지 않으면 안되아서 그 개요를 설명하는 것만해도
상당한 분량을 차지하게 됩니다.
프로그램의 개발에 대해서는 메인 프로그램을 포함하는 모듈의 소스
프로그램과 서브
루틴으로 이루어지는 소스 프로그램으로 작성합니다.
예제)MODULE1.ASM
문자열(string) We are the world 를 출력한다.
CODE SEGMENT PUBLIC
ASSUME CS:CODE,DS:DATA
;
EXTRN PUTSTR:NEAR 외부에서 참조하는 서브루틴의 선언
;
START: MOV AX,DATA
MOV DS,AX
MOV BX,OFFSET MSG
CALL PUTSTR
MOV AH,4CH
INT 21H
;
CODE ENDS
;
DATA SEGMENT PUBLIC
MSG DB 'We are the world',00H
; |
DATA ENDS +-->끝내기 위해 삽입된 0 이다.
END START
PUTSTR.ASM
문자열을 출력하는 서브루틴
CODE SEGMENT PUBLIC
ASSUME CS:CODE
;
PUBLIC PUSTR ;다른 모듈에서 참조되는 서브루틴 이름을 PUBLIC
의사명령으로
; 선언한다. 즉 다른데서 PUTSTR을 참조할것이라고
선언함.
PUTSTR PROC NEAR --+
PUSH AX |
PUSH DX |
PUSH SI |
MOV SI,0 |
PUT_NET: |
MOV DL,[BX+SI] |
CMP DL,0 -+-+----->문자열중 0 이 나타나면 끝낸다.
JZ PUT_EXIT -+ |
MOV AH,2 |반드시 서브루틴
INT 21H | PROC ~ ENDP 로
INC SI | 선언한다.
JMP PUT_NEXT |
PUT_EXIT: |
POP SI |
POP DX |
POP AX |
RET |
PUTSTR ENDP ----------+
;
CODE ENDS 형식상 하나의 세그먼트에 있는 걸로한다.
END
서브루틴 PUTSTR은 ,BX에 의해 간접지정되는 번지를 선두로 한데이터 열을
문자열로서 출력하고 ,0 이 나타나면 종료하는 것입니다.
PUBLIC 이라는 지정은 : 다른 모듈과 링크할 때에 같은 이름의 세그먼트가
있다면
그것을 합쳐서 하나로 하라, 라는 지정입니다.
EXTRN(external) :
열거된 서브루틴이 외부의 모듈의 모듈 속에 들어있다는 것을 나타내고
있읍니다.
어셈블러는 더셈블 단계에서 같은 모듈 내의 서브루틴이나 라벨이
존재하지 않는 경우에는 Symbol not defined 에러를 발생하는데, EXTRN
의사명령에 의해 선언되어 있게되면 ,그서브루틴이나 라벨은 다른 모듈에
있어서 링크할때 결합할수있다고 간주되어 에러를 발생하지 않고 어셈블을
계속합니다.
라벨이름이 동일세그먼트 내에 배치되는 것인 경우에는 ,
: 에 바로 이어서 NER을 붙이고, 라벨의 속성을 선언하지 않으면
안됩니다.
만일동일 세그먼트내에 존재하지 않을 때에는 ,
: FAR 을 붙이지 않으면 안됩니다.
서브루틴 :
서브루틴에 놓이는 모듈도 하나의 세그먼트로서 형식을 갖추기위해 그
앞뒤를
SEGMENT ~ ENDS 로 둘러 쌉니다.여기 에서 세그먼트선언은 메인루틴과
같지않으면 안
됩니다.(동일한세그먼트 이름이어야 한다) 데이터 세그먼트를
사용하지않을 때에는
ASSUME 문장으로 DS 선언을 하지 않도록 합니다.
PUBLIC :
의사명령 으로 PUTSTR 이라는 이름의 서브루틴(라벨 이름)이 다른 외부의
모듈에서 참조 가능하다는 것을 선언하고 있읍니다.이것은 이 루틴 중
어느 라벨 이름니 외부참조가 가능 하다는 것을 나타내는 것 입니다.
여기서 사용하고 있느 PUBLC 과 세그먼트 선언중에 사용되고 있느
PUBLC과는 다른 것입니다. 그리고 서브루틴 전후를 PROC ~ ENDP 에
의해 둘러싸고, 구조를 명확하게 합니다. 이전과 마찬가지로 단순히 라벨
이름만을 서브루틴으로 할수도 있지만, 별로 추천할만 하지하지는
않습니다. 그리고 PROC 의 다음의 NEAR 은 없어도 상관이 없지만,이것도
동일한 세그먼트 내에서 참조 되는 라벨이라는 것을 나타내고
있읍니다.
이상의 것을 정리하여 프로그램에 필요한 구조를 나타내고면 다음과 같이
됩니다.
(세그먼트 이름을 CODE , 서브루틴 이름을 PUTSTR 로 한다.)
+------------- 메인루틴 -------------------------------------+
| CODE SEGMENT PUBLC |
| ASSUME CS:CODE....... |
| EXTRN PUTSTR:NEAR |
| START: |
| +---------+ |
| | 본 문 | |
| +---------+ |
| CODE ENDS |
| +----------------------------------+ |
| | 만일 있다면 데이터 세그먼트 등 | |
| +----------------------------------+ |
| END START |
+----------------------------------------------------------------+
+------- 서브루틴 -------------------------+
| CODE SEGMENT PUBLC |
| ASSUME CS:CODE..... |
| PUBLC PUTSTR |
| PUTSTR PROC NEAR |
| +---------+ |
| | 본 문 | |
| +---------+ |
| PUTSTR ENDP |
| CODE ENDS |
| END |
+----------------------------------------------+
오브젝트 화일을 링크할 때에는 화일 이름을 " + " 로 연결하여
지정합니다.
반드시 메인 루틴을 포함하는 모듈의 화일 이름을 선두에 지정합니다.
A>LINK MODULE1+PUTSTR,,MODULE1;
이것으로 실행가능 화일이 완성됩니다.
자기가 모듈을 만들어 나갈때에는 위레서 설명한 방법에 따라서 모듈으
개발하면 되는데, 아른 참고서 로 부터 인용할 경우에 반드시 위와 같은
대로 되어 있지 않은 경우가 있읍니다. 그와 같은 경우에는 세그먼트
이름을 고쳐 쓰는 방법 등으로 대처해 주십시오. 그래도 안되는겨우에는
원인을 여러가지로 생각 할수가 있지만,자세하게는 나중에 다루도록 하고
,할수없이 서브루틴 자체를 맞도록 자기가 다시 베열 하여 고쳐 작성하는
등 시행착오를 해 보십시요.
제 목:[강좌] 어셈블리 강좌 [7/7]
올린이:유씨가족(유용수 ) 96/11/02 19:36 읽음:4196 관련자료 없음
-----------------------------------------------------------------------------
제 8 장 . 반복 기법
----------------------------------------------------------------------
----------
8.1 루프(loop)명령
----------------------------------------------------------------------
----------
비교와 분기를 하나로 합쳐서 한명령으로 할수 있도록 된
루프(loop),스트링(string)
LOOP 명령
LOOP 명령은 CX레지스터를 뺄셈식 카운터로서 사용하고 ,CX 레지스터가 0
이 될때까지 어떤 문장들을 되풀이 반복한 뒤 지정한 라벨로 분기 하는
명령입니다.즉, CX 에 설정
한 횟수만큼 반복을 하기위한 명령인것 입니다.
LOOP명령은 오퍼랜드에 분기하는 곳의 라벨을 지정하여,
LOOP NEXT
와 같은 식으로 사용합니다.이명령은 지금까지 알고 있는 명령을 조합하여
실행하려고
하면
DEC CX
CMP CX,0
JNE NEXT
와 같이 하면 됩니다.즉 , LOOP 명령은 CX 레지스터 뺄셈명령과
비교명령과 조건분기 명령을 한 명령으로 실행하는 명령입니다. 이경우
뺄셈 카운터로서 사용되는 것은 CX레지스터 라고 정해져
있읍니다.루프명령을 실행하면 CX 레지스터 값을 하나 빼고,
0 이아니면 지정된 라벨로 분기 합니다.
LOOP명령에 의해 CX레지스터에 지정된 횟수만큼 반복할수가 있읍니다.
CX 레지스터에는 LOOP명령에서 지정하는 반복범위보다 앞서서 반복하는
횟수를 설정해주지 않으면 안됩니다.
예제)LOOP1.ASM
1부터 10H까지의 수치를 화면에 출력하는 프로그램
CODE SEGMENT
ASSUME CS:CODE,DS:CODE
;
INCLUDE PUTAL2.SUB - INCLUDE화일의 전개부분의 리스트는 생략하였다.
;
START: PUSH CS -+DS 에 CS의 값을 지정함
POP DX -+
;
MOV AX,1
MOV CX,10H
L1: MOV DS,AL -+
CALL PUTAL |
CALL SPACE | 이범위를 10H회 반복한다.
INC AX |
LOOP L1 -+
MOV AH,4CH
INT 21H
;
SPACE PROC NEAR - NEAR지정은 없어도 좋다.
PUSH AX -+ 레지스터를 퇴피
PUSH DX -+
MOV DL,' ' -+공백문자를 출력
MOV AH,2 |
INT 21H -+
POP DX -+퇴피했던 레지스터값을 회복
POP AX -+
RET
SPACE ENDP
;
CODE ENDS
END START
위의 프로그램은 리스트는 INCLUDE 화일 이 전개된 부분은 생략하고
나타낸것 입니다
CX레지스터의 초기 설정은 루프밖에서 세트
LOOP명령은 ,그자신 속에 CX의 값을 빼는 명령을 포함하고 있다
LOOP : 무조건 지정된 횟수를 반복
LOOPE(loop while equal) ,LOOPNE(loop while not equal):
어떤 조건이 지정된 횟수 이내에 서 반복을 끝냄
이 명령 앞에 비교명령(또는 연산명령)을 놓고 그 결과가 같고 ,또한 CX
레지스터의 1씩을 값을 뺀 결과가 0 이 아니면 반복하는 명령입니다.
상한을 CX 회로 설정해 놓고 같지않은 것이 나타날 때까지 반복하는
명령입니다.
이것은 데이터의 열중에서 서로 다른 데이터를 찾는데에 사용할수
있읍니다.
예제)LOOP2.ASM
문자열 "_ _ _ _ _HELLO!" 의 공백의 갯수를 세고,문자열 "HELLO!_ _
_"중의 연속된 문자갯수를 세어 출력한다.
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
INCLUDE PUTAL2.SUB -+INCLUDE화일의 전개부분은 생략한다.
INCLUDE CRLF.SUB -+
;
SPACE EQU ' '
;
START: MOV AX,DATA
MOV DS,AX
;
;COUNT SPACE LENGTH 공백길이를 센다.
MOV CX,10H 상한 회수를 10H회로 결정한다.
MOV DL,0 공백수를 DL레지스터에카운트
MOV SI,0 선두번지부터 몇번째인가를 나타내는 SI
MOV BX,OFFSET STR1
NEXT1: INC DL
INC SI SI 가 INC SI 에 의해서 1부터
시작되므로
MOV AL,[BX+SI-1] -- 하나를 빼야한다.
CMP AL,SPACE CX내용에서 1을 뺀 결과 0이아닌 동안
여기서 AL과
LOOPE NEXT1 SPACE와 비교한 결과 같으면
NEXT1으로분기한다.
DEC DL -----+ 같지않으면 아래로 계속한다.
MOV AL,DL --+ +--- 카운트에 더 더한 만큼 뺀다
CALL PUTAL --+------ 표시
CALL CRLF --------- 줄을 바꾼다.
;
;COUNT STRING LENGTH 문자열겟수를 센다
MOV CX,10H
MOV DL,-1 초기값을 -1로 하는것은
MOV SI,-1 0FFFFH세트하는 것
NEXT2: INC DL
INC SI
MOV AL,[BX+SI]
CMP AL,SPACE -+ CX를 1감소시켰을 때 0이 아닌범위 내에서
AL와
LOOPNE NEXT2 -+ SPACE가 같지 않으면 NEXT2로 분기하여
문자 갯수
MOV AL,DL 를 센다.
CALL PUTAL --- 표시
CALL CRLF 줄을 바꾼다.
;
MOV AH,4CH
INT 21H
;
CODE ENDS
DATA SEGMENT
STR1 DB HELLO!' ;선두가 공백인 문자
STR2 DB 'HELLOW! ' ;선두부터 문자가 들어있는 문자
DATA ENDS
END START
LOOP 명령과 플레그 레지스터:
LOOP 명령, LOOPE명령,LOOPNE명령은 모두 CX 레지스터를 카운터로 하고
한번 실행할때
마다 CX 레지스터 값을 하나 씩 값을 빼고 있읍니다.그러나 여기에서 CX
레지스터 값
을 뺄떼는 ,결과가 무엇이든 상관없이 플래그 레지스터의 값은 변화하지
않습니다.
즉, LOOP명령등의 실행 후 플래그 레지스터의 값은 앞의 연산결과를
유지하고 있읍니
다.따라서 LOOP 명령 중의 CX 레지스터를 빼는 명령은 단순한 DEC 명령과
치환할수는
없읍니다.(DEC 명령에서는 CF이외의 레지스터가 변화한다.).
LOOP명령,LOOPE명령,LOOPNE 명령을 이해를 돕기 위해서 다른 명령으로
치환 한다면 다음과같이 됩니다.
LOOP명령 LOOPE명령, LOOPNE명령들의 똑같은 기능을 다른 명령으로 치환
+-------------------------------------------------------+
| LOOP L1 LOOPE L1 LOOPNE L1 |
| |
| PUSHF PUSHF PUSHF |
| DEC CX JNZ EXIT JZ EXIT |
| JE EXIT DEC CX DEC CX |
| JZ EXIT JZ EXIT |
| POPF POPF POPF |
| JMP L1 JMP L1 JMP L1 |
| EXIT:POPF EXIT:POPF EXIT:POPF |
+-------------------------------------------------------+
여기서 PUSHF 는 플래그 레지스터(flag register)를 스택에 퇴피시킵니다.
8.2 스트링(string) 명령
----------------------------------------------------------------------
----------
연속한 데이터를 전송하는 명령(5종류)
문자열과 같은 연속한 데이터를 전송하는 명령입니다.
리피트 프리픽스(repeat prefix) 명령과 조합하여 사용함으로써
,한명령으로 대량의 데이터를 전송할수 있는 이점이 있읍니다.
스트링 명령
+-----------+-------+-------------------+-------+---------------------
---+
| | | 바이트 단위 | | 워드 단위
|
+-----------+-------+-------------------+-------+---------------------
---+
|전송 명령군| LODSB | 메모리 ->AL | LODSW | 메모리 -> AX
|
| | STOSB | AL ->메모리 | STOSW | AX -> 메모리
|
| | MOVSB | 메모리 ->메모리 | MOVSW | 메모리 -> 메모리
|
+-----------+-------+-------------------+-------+---------------------
---+
|비교 명령군| SCASB | 메모리 AL의 비교 | SCASW | 메모리와 AX의 비교
|
| | CMPSB | 메모리끼리의 비교 | CMPSW | 메모리와 메모리의
비교 |
+-----------+-------+-------------------+-------+---------------------
---+
리피트 프리픽스 명령
+-------------+-------+-----------------------------------------------
---------+
| 전송 명령군 | REP | CX 레지스터에 주어진 횟만큼,다음의 스트링
명령을 반복 |
+-------------+-------+-----------------------------------------------
---------+
| 비교 명령군 | REPE | 비교결과가 0 및 CX레지스터에 주어진 횟수
이내이면 다 |
| | REPZ | 음의 스트링 명령을 반복한다.
|
|
+-------+--------------------------------------------------------+
| | PEPNE | 비교결과가 0이 아니고 CX레지스터에 주어진
횟수 이내이|
| | PEPNZ | 내 이면 다음의 스트링 명령을 반복한다.
|
+-------------+-------+-----------------------------------------------
---------+
스트링 명령에서는 메모리의 번지를 지정하는 방법이
정해져 있고,또한 데이터를 읽는 방법과 쓰는 방법이 차이가 있읍니다.
*.데이터를 읽어낼때...DS 내의 [SI]로 표시되는 번지의 내용을 읽어낸다.
*.데이터를 써넣을때...ES 내의 [DI]로 표시되는 번지의 데이터를
써넣는다.
+----------+
| DS:[SI] +-----+ LODS명령
+----------+ \|/
+-----------------+
MOVS명령 | AX 혹은 AL |
(CMP명령) +-----------------+
|
+----------+ | STOS명령
| ES:[DI] +<----+ ( SCAS 명령)
+----------+
사용하는 번지도 바이트 단위면 AL, 워드 단위이면 AX 로정해져
있읍니다.따라서 스트링 명령을 사용하기 전에는 필요한
레지스터(DS,ES,SI,DI,CX)의 초기설정을 하지않으면 안되는
대신에,명령자체는 오퍼랜드를 가지지 않고 단순히
LODSB
MOVSW
와 같은 식으로 단순하게 사용할수가 있읍니다.
단,스트링 명령은 전송이나 비교 등을 행한 다음,번지지정에 사용되어
SI혹은 DI레지스터의 값을 자동적으로 1(바이트 단위일때) 또는 2(워드
단위일때) 만큼 증가시킵니다. 이것은 다음번지의 데이터를 전송 또는
비교하기위한 준비이고,스트링 명령을 연속하여 사용 함으로써 연속된
번지에 놓여진 데이터를 전송 또는 비교할수있읍니다.
(디렉션 플레그값이 1 일때는 ,SI, DI 갑은 감소 된다.)
LOOP 명령을 사용하기 위해,편의상 미리 CX 에 문자열의 길이를 넣어두지
않으면 안됩니다. 문자열의 길이를 계산하기위하여 원하는 문자열의
다음에 더미(dummy) 변수 MSG2를 놓아서 두 개의 오프셋 번지의 차를
취합니다.
OFFSET MSG2-OFFSET MSG
이와 같이 매크로 어셈블에서는 번지등의 덧셈 뺄셈을 소스 프로그램 중에
쓸수가 있읍니다. 이 방법은 정석으로 되어 있읍니다.
LOODSB명령은 ,데이터를 전송한다음 SI 값을 하나 증가 시키므로, 한 문자
출력한 다음 LOOP명령으로 돌아와서 다시 LODSB 명령을 실행하게 되면, SI
는 앞에 출력한 문자가 있는 번지의 다음 번지를 나타내고
있읍니다.(연속문자 출력가능)
STOS명령
메모리로 부터 메모리로 데이터 전송
LODS 와 STOS를 조합하는 방법 ,MOVS 를 사용하는 방법,
예제)MOVS2.ASM
MSG1에들어 있는 스트링 FUNCTION 09H:DISPLAY STRING 을
MSG2로 전송하는 펑션 9 를 호출하여 출력한다.
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,ES:DATA
;
START: MOV AX,DATA
MOV DS,AX
MOV ES,AX
;
MOV CX,OFFSET MSG2-OFFSET MSG1
MOV SI,OFFSET MSG1
MOV DI,OFFSET MSG2
CLD ;DI의 증가 설정
NEXT: LODSB ;DS:[SI]로 정해지는 번지메모리 내용을 ES:[DI]로
STOSB ;정해지는 메모리에 전송한다.단,문자열의 길이(CX)
만큼
LOOP NEXT
MOV BYTE PTR ES:[DI],'$'
MOV DX,OFFSET MSG2
MOV AH,9H
INT 21H
;
CODE ENDS
DATA SEGMENT
MSG1 DB 'FUNCTION 09H:DISPLAY STRING'
MSG2 DB 20H DUP (?)
DATA ENDS
END START
LODSB의 실행후 SI의 값이 하나 증가하고,STOSB의 실행후 DI 의 값이 하나
증가,
2 개의 명령에 LOOP 명령을 더함으로써 연속한 데이터를 전송할수가
있읍니다.
MOVS 명령:
위의 LODS 명령과 STOS 명령을 MOVS 명령으로 치환
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,ES:EXTRA
;
START: MOV AX,DATA
MOV DS,AX ;DS설정
MOV AX,EXTRA ;독립된 ES의 설정이 필요
MOV ES,AX
;
CLD ;DI의 증가 설정
MOV DX,5 ;5번 되풀이
MOV DI,OFFSET MSG2
NEXT2: MOV CX,OFFSET DMSG - OFFSET MSG1
MOV SI,OFFSET MSG1
NEXT: MOVSB ;CX의 값이 0이 될때 까지 MSG1번지부터
시작하는 스트링
LOOP NEXT ;을 MSG2 번지위치부터 옮겨 놓는다.
DEC DX ;DX 에서 1을 빼라
JNZ NEXT2 ;0 이 아니면 NEXT2로 가라.
MOV BYTE PTR ES:[DI],'$' ;메모리에 $(문자열 끝표시)를
넣는다.
MOV AX,ES
MOV DS,AX ;펑션호출 9번의 앞에는 DS의 설정이 필요
MOV DX,OFFSET MSG2
MOV AH,9H ;DX에 지정된 번지부터 저장된 스트링을 $
자가 나타
INT 21H ;날때까지 출력하는 시스템 펑션 호출.
MOV AH,4CH
INT 21H
;
CODE ENDS
;
DATA SEGMENT
MSG1 DB 'REPEAT SAME MESSAGE 5 TIMES!'
DMSG DB ?
DATA ENDS
;
EXTRA DB 100H DUP (?)
EXTRA ENDS
END START
8.3 리피트 프리픽스(1)
----------------------------------------------------------------------
----------
리피트 프리픽스 명령은 스트링 명령과 조합하여 사용하고 ,스트링 명령을
일정 횟수 연속하여 사용 하는것 입니다.
REP MOVSB
-+-
+----->이것이 리피트 프리픽스이다.
CX 에 지정된 횟수만큼 MOVSB를 되풀이 하라는 의사명령
리피트 프리픽스는 스트링 명령 앞에 놓고,스트링 명령의 기능
확장을 지정
여기서 STOSB -->B는 바이트를 나타냄
STOSW -->W는 워드를 나타냄
따라서 AX 에 AB 가 들어 있어도 STOSB는 B 만을 나타낸다.
AX 레지스터에 AB'를 대입하고 있으므로, AL 레지스터의 내용은
'B'=42H가 됩니다.
워드단위의 스트링 명령을 사용한 경우에는 20H 개의 바이트 의 데이터가
전송됩니다.
이것을 실행하면
A>MOVS5
BABABABABABABABABABA
AX레지스터의 값 AB' 가 메모리 상에 전송돌 경우에는 역워드(reverse
word)형식으로
되기 때문에 ,'BA' 순서로 되어버린 점에 주의 하십시요.
REP 명령은 조합하는 스트링 명령이 바이트 형인가 워드 형인가에 따라서
전송되는 바
이트 수는 달라집니다.
예를들면 20H 바이트의 데이터를 전송할때에 MOVSB명령을 사용A한다고
하면 CX 레지
스터의 초기치를 20H로 ,MOVSW명령을 사용한다고 하면 CX 레지스터의
초기치를 10H
로하지 않으면 안될것입니다.
전송하는 데이터의 수가 홀수 바이트 일때에는 워드형의 스트링명령을
사용하면 마지
막 데이터가 전송되니 않는다든지 ,여분의 데이터 까지 전송 된다든지
하는 일이 있으
므로 주의가 필요합니다. 미리 데이터수를 알과있는 경우는 괜찬지만
경우에따라 전
송하는 데이터수가 변화 할때에는 바이트 형의 스트링 명령을 사용하는
쪽이 안전 합니다.
8.4 비교명령군의 사용법
----------------------------------------------------------------------
----------
메모리와 레지스터의 값을 비교하는 SCAS명령,
메모리끼리의 값을 비교하는 CMPS명령
SCAS명령
AL 레지스터의 값과 ES:[DI]에 의해 표시되는 메모리의 내용을 비교
몇번 반복했는가를 알기위해서 ,
원래의 CX값을 보존해두어,루프에서 빠져나온 후의 CX 값을 빼면 된다.
문자열길이의 계산 : STR2 - STR1
OFFSET STR2 - OFFSET STR1
과 완전히 같은 역활을 합니다. 이것도 하나의 정석으로서 외워두십시요
CODE SEGMENT
ASSUME CS:CODE,DS:CODE,ES:CODE
;
INCLUDE PUTAL2.SUB
START: MOV AX,CODE ;
MOV DS,AX
MOV ES,AX ;DS와 ES 의 설정
CLD ;DI 의 증가 설정
MOV AL,'&' ;문자열 중에서 찾는문자
MOV DI,OFFSET STR1
MOV CX,STR2-STR1 ; 문자열의 길이를 계산하려면
PUSH CX ;OFFSET 을 생략해도 좋다.
NEXT: SCASB------------+일치하는 문자가 있는가 찾는다.
JE FOUND |(AL의값과 ES:[DI]로 지정된 메모리 내용과
내용을 비교한
LOOP NEXT --+ 다,그리고 자동적으로 DI는 증가 CX 는
감소한다.)
JMP NOTFOUND
FOUND: DEC DI
MOV DX,OFFSET FOUNDMSG1
MOV AH,9
INT 21H
MOV AX,DI --+- 발견한 번지를 표시
MOV AL,AH |
CALL PUTAL |
MOV AX,DI |
CALL PUTAL --+
MOV DX,OFFSET FOUNDMSG2
MOV AH,9
INT 21H
POP AX --+문자열의 길이-뺄셈 카운터에 의해 몇번째
문자에서
SUB AX,CX | 발견되었는지를 계산하여 표시
CALL PUTAL --+
JMP EXITP
NOTFOUND:
MOV DX,OFFSET NOTFOUNDMSG
MOV AH,9
INT 21H
EXITP: MOV AH,4CH
INT 21H
;
FOUNDMSG1 DB 'FOUND ADDRESS = $'
FOUNDMSG2 DB 'H COUND = $'
NOTFOUNDMSG DB 'NOT FOUND $'
STR1 DB 'ABCDEFG&ABC'
STR2 DB ?
CODE ENDS
END STRAT
CMPS 명령 :
2개의 데이터열이 같은가 다른가를 비교하는데 사용합니다.
DS:[SI]가 지정하는 메모리 한 바이트 내용과
ES:[DI]가 지정하는 메모리 한 바이트 내용을 비교하는 명령이다.
CMPSB명령을 하면 SI,DI의값이 자동적으로 하나만큼 증가 한다는
점입니다.
예제)CMPS2.ASM
메모리 번지 CMP-STRING 부터 저장되어 있는 ABC" 문자와 키보드로 부터
입력하여
메모리 번지 BUFF부터 저장된 문자 스트링 속에 같은 "ABC" 가 몇 번째
글자부터 있
는가 알아 낸다.
CODE SEGMENT
ASSUME CS:CODE,DS:CODE,ES:CODE
;
INCLUDE CRLF.SUB
INCLUDE PUTAL2.SUB
START: MOV AX,CS
MOV DS,AX
MOV ES,AX
CLD
MOV DX,OFFSET OPENING_MSG
CALL DISP
CALL CRLF
MOV DX,OFFSET MAX_CHARS -+리터키가 눌릴때까지
키보드로부터 입력된
MOV AH,0AH |스트링을 연속적으로 BUFF속에
저장하고 총
INT 21H -+입력된 문자갯수를
CHARS-ENTERED에 지정하
CALL CRLF 는 시스템 펑션 A 번이다.
MOV CH,0
MOV CL,CHARS_ENTERED
MOV BP,OFFSET BUFF
DEC BP
INC CX
NEXT: DEC CX
CMP CX,3
JB NOTFOUND
INC BP
MOV DI,BP
MOV SI,OFFSET CMP_STRINGS
CMPSB <---+DS:[SI]로 지정되는 메모리내용과 ES:[DI] 로
지정되는
JNE NEXT |메모리 내용을 비교한다.비교한후,자동적으로
DI,SI값
CMPSB <---+을 증가시켜 다음것을 비교한다.
JNE NEXT |
CMPSB <---+
JNE NEXT
MOV DX,OFFSET FOUND_MSG1
CALL DISP -+몇번째 문자에서
발견되었는지를계산표시
MOV AL,CHARS_ENTERED |
SUB AL,CL -+
CALL PUTAL
JMP EXITP
NOTFOUND:
MOV DX,OFFSET NOTFOUND_MSG
CALL DISP
EXITP: MOV AH,4CH
INT 21H
DISP PROC
MOV AH,09H
INT 21H
RET
DISP ENDP
OPENING_MSG DB 'INPUP STRINGS INCLUDE'
$'
FOUND_MSG1 DB 'FOUND CHARACTERS AT $'
NOTFOUND_MSG DB 'NOT FOUND $'
MAX_CHARS DB 80
CHARS_ENTERED DB ?
BUFF DB 80H DUP (0)
+-->최대 입력가능 문자 갯수(CR포함):1바이트
+--+---------+---------+----------------------+
| | | 버퍼 --+-->입력된 실지 문자가
들어가는곳
+------------+--+------+----------------------+
+---->실제로 입력된 문자갯수(CR포함하지 않음) :
1바이트
CMP_STRINGS DB 'ABC'------>비교대상이되는 문자열
CODE ENDS
END START
위문제는 상당히 논리가 어려운 문제입니다.그러나 꾸준히 추적해나가면
곧 이해할수있을 것입니다.그러나 이 프로그램을 완전히 이해했다면
어셈블리 언어의 상당한 수준을 파악하게 된셈이며 앞으로 어떤 복잡한
어셈블리 언어 프로그램도 이해 할수있는 능력이 생긴 것입니다.