본문 바로가기
어셈블리어 공부용/x86 어셈블리어(ASSEMBLY)

emu8086으로 어셈블리어연습 [99 Bottles of Beer]

by Redoutable 2019. 8. 18.
반응형

 

Hello World 다음으로 예제로 많이 사용되고있는 99 Bottles of Beer 노래 가사 출력하기 입니다.

 

.MODEL SMALL
.CODE
ORG 100h

PROG:

    JMP MAIN
;------------------------------
BEERS DB 63h  ;맥주의 갯수 99개.

BEER1 DB ' BOTTLES OF BEER ON THE WALL, $'
BEER2 DB ' BOTTLES OF BEER.$'
BEER3 DB 'TAKE ONE DOWN AND PASS IN AROUND ,$'
BEER4 DB ' BOTTLES OF BEER ON THE WALL.$'

NOBEER DB 'no more bottles of beer on the wall.$'

NOBEER1 DB  'No more bottles of beer on the wall, no more bottles of beer.$'
NOBEER2 DB  'Go to the store and buy some more, 99 bottles of beer on the wall.$'

CRLF DB 0Dh,0Ah,'$' ;다음줄로 넘어가기

;----------------------------- 
MAIN PROC NEAR
    
  
TOP:

   CMP BEERS,0        ;BEERS에 있는 데이터(맥주 갯수)가 0인지 아닌지 확인.
   JE ENND            ;0이라면 ENND로 분기.
   
HEYBEERS:             ;맥주 갯수 표시하는 곳.
   
   LEA SI,BEERS      ;BEERS가 존재하는 주소를 SI 레지스터에 저장.
   MOV AX,[SI]       ;SI 레지스터가 가르키고 있는 주소의 데이터를 AX 레지스터에 저장.
   AND AX,000000011111111b ; 하위바이트만 필요하므로 상위 바이트는 00으로 만들어줍니다.

   MOV CX,0          ;진수변환 앞서 청소.
    
PUSHDECA:            ;
    MOV DX,0         ;진수변환 앞서 청소
    MOV BX,10        ;10으로 나누어야 하므로 BX레지스터 10 수록.
    
    DIV BX           ;AX 레지스터를 BX로 나눔.
    
    PUSH DX          ;DX레지스터값(나머지)을 스택에 저장.
    INC CX           ;CX 레지스터 1 증가시킴.
    CMP AL,0         ;AX레지스터(몫)이 0인가?
    JNE PUSHDECA      ;0이 아니라면 진수변환 계속.
    
POPDECA:
    POP DX           ;스택에 있는 값 DX 레지스터로 가져오기.
    ADD DX,30h       ;DX레지스터에 30h 더해서 아스키코드화 하기.
    MOV AH,02        ;1글자를 출력하는 기능번호.
    INT 21h          ;실행
    
    DEC CX           ;CX값 1 감소시키기.
    CMP CX,0         ;CX값이 0인가?
    JNE POPDECA      ;0이 아니라면 출력 계속함.    
;-----------------
    POP CX           ;CX값 가져오기 (가사 옮겨다닐때 필요한 값임.)
    
    CMP CX,1         ;CX값이 1인가?
    JE NEXT          ;1이라면 다음 가사로.
    
    CMP CX,2         ;CX값이 2인가?
    JE NEXT2         ;2라면 다다음 가사로.
;-----------------    
    LEA DX,BEER1     ;BEER1 데이터영역 주소 DX레지스터로 가져오기.
    MOV AH,9         ;문자열을 출력하는 기능번호
    INT 21h          ;실행
    
    MOV CX,1         ;CX레지스터에 1 수록. (그 다음 가사로 넘어가게 하기위하여.)
    PUSH CX          ;CX값 스택에 저장.
    
    JMP HEYBEERS     ; 맥주 갯수 표시하기.
    
NEXT:

    LEA DX,BEER2    ;BEER2 데이터영역 주소 DX레지스터로 가져옴.
    MOV AH,9        ;문자열 출력
    INT 21h         ;실행
    
    LEA DX, CRLF    ;CRLF데이터영역 주소 DX로 가져옴. (줄바꾸기)
    MOV AH,9        ;문자열 출력
    INT 21h         ;실행
    
    LEA DX,BEER3    ;BEER2 데이터영역 주소 DX레지스터로 가져옴.
    MOV AH,9        ;문자열 출력
    INT 21h         ;실행
    
    DEC BEERS       ;BEERS데이터영역 값 1 감소시키기. (맥주가 하나 줄었다.)
    
    MOV CX,2        ;다다음 가사로 출력하게 하기 위하여 CX레지스터에 2 수록.
    PUSH CX         ;CX값 스택에 저장.
    
    OR BEERS,00000000b ;BEERS 데이터영역값이 0인가?
    JZ NO              ;0 이라면 NO로 분기. (작동메커니즘은 플래그와 관련있음.)

   ;CMP BEERS.0        ;위 코드는 이 코드와 완벽하게 똑같이 동작합니다.         
   ;JE NO         
    
    JMP HEYBEERS       ;맥주 갯수를 출력한다.
    
NEXT2:

    LEA DX,BEER4    ;BEER2 데이터영역 주소 DX레지스터로 가져옴.
    MOV AH,9        ;문자열 출력
    INT 21h         ;실행

    LEA DX, CRLF    ;CRLF데이터영역 주소 DX로 가져옴. (줄바꾸기)
    MOV AH,9        ;문자열 출력
    INT 21h         ;실행
    INT 21h         ;실행 (엔터키 두 번 누른 효과.)
        
    JMP HEYBEERS    ;맥주 갯수 출력
    
NO:                 ;맥주가 1개 남았었고 이제 0개일 때.
    LEA DX,NOBEER   ;NOBEER 데이터영역 주소 DX레지스터로 가져옴.
    MOV AH,9        ;문자열 출력
    INT 21h         ;실행

    LEA DX, CRLF    ;CRLF데이터영역 주소 DX로 가져옴. (줄바꾸기)
    MOV AH,9        ;문자열 출력
    INT 21h         ;실행
    INT 21h         ;실행 (엔터키 두 번 누른 효과.)
        
    JMP TOP         ;맨 위로 올라가서 맥주갯수가 0인지 아닌지 확인.
;--------------------    
ENND:

    LEA DX,NOBEER1  ;NOBEER1 데이터영역 주소 DX레지스터로 가져옴.
    MOV AH,9        ;문자열 출력
    INT 21h         ;실행
    
    LEA DX, CRLF    ;CRLF데이터영역 주소 DX로 가져옴. (줄바꾸기)
    INT 21h         ;실행
    
    LEA DX,NOBEER2  ;NOBEER2 데이터영역 주소 DX레지스터로 가져옴.
    INT 21h         ;실행
    
    HLT             ;프로그램 중단.
   
MAIN ENDP

END PROG

 

 

 

반응형

댓글