Assembly2010. 1. 5. 01:36



.model large
.386
.stack 100h
.data
    num  word 0f3f2h
.code
L_start:
    mov ax, @data
    mov ds, ax
   
    mov dx, num
    call printDecimal
   
    mov ah, 4ch
    int 21h

;---------------------------------
; 헥스값을 10진수로 변환
; 입력값 dx : 16-bit unsigned
;
; F3F2라는 숫자를 10진수로 출력하려면 F3F2라는 숫자를 10으로 나누어
; 나머지를 스택에 차례로 저장했다가 복원하면서 하나씩 아스키문자로
; 변환해서 출력해 주면 됩니다.
; F3F2 / 10 = 1865....0
; 1865 / 10 = 270.....5
; 270 / 10  = 3E......4
; 3E / 10   = 6.......2
; 6 / 10    = 0.......6
; 각 자리를 푸쉬하는 이유는 각 연산의 결과 05426이 됩니다. 그렇기
; 때문에 자리를 뒤집어서 찍어줘야 합니다. 이런 경우 스택을 사용하면
; 간단히 해결할 수 있습니다. 스택의 구조는 후입선출로서 나중에 들어
; 간 것이 먼저 나오므로 각 연산의 결과를 스택에 푸쉬해 두고 차례로
; 팝하면서 찍어주면 62450이 정상적으로 찍히게 됩니다.
;
; 각 자리를 10으로 나누기 때문에 9이상의 숫자가 나머지로 나올 수
; 없습니다. 10이 나온다면 10으로 나누어 떨어져 결국 나머지는 0이기
; 때문입니다.
; 0~9는 10진수와 16진수가 공통으로 가지고 있는 부분이기
; 때문에 다른 변환없이 바로 사용할 수 있습니다. 때문에 16진수를
; 출력하는 프로시져로 출력해도 정상적인 10 진수를 출력할 수 있습니다.
printDecimal proc
        pusha                  ; 레지스터 보호
        mov ax, dx             ; 피젯수를 ax에 저장
        mov si, 10             ; 젯수를 si에 저장
        xor cx, cx             ; cx를 0으로 초기화
NONE_ZERO:
        xor dx, dx             ; dx를 0으로 초기화
        div si                 ; ax의 내용을 si의 값으로 나눔
        push dx                ; 나머지 dx를 스택에 저장
        inc cx                 ; cx 증가시킴
        or ax, ax              ; ax 가 0인가 ?
        jne NONE_ZERO          ; 0이 아니면 NONE_ZERO로 이동
WRITE_DIGIT_LOOP:
        pop dx                 ; 푸쉬한 수만큼 스택에서 꺼내면서 출력하기
        call printHex             ; 16진수 출력 루틴
        loop WRITE_DIGIT_LOOP
END_DECIMAL:
        popa                   ; 레지스터 보호 해제

        ret                    ; 프로시져 리턴
printDecimal endp

;--------------------------------
; 헥스값이나 10진수 값을 화면에 출력
; 입력값 dl : 8-bit 의 출력하고자하는 니블의 값
printHex proc
        push dx         ; 레지스터 보호
        push ax        

        cmp dl, 10              ; dl을 10과 비교  
        jae HEX_LETTER    ; 10보다 크거나 같으면 16진수 A~F에 해당하는 아스키 출력
        add dl, '0'              ; 10보다 작으면 '0'의 아스키코드 30h를 더해 실제 아스키코드 만듦
                        ; 가령 dl이 9이면 30+9=39 39는 아스키코드 '9'임                                      
                   
        jmp short WRITE_DIGIT   ; 아스키코드 출력 루틴으로 이동
HEX_LETTER:
        add dl, 'A'-10         ; 10이상인 경우 10~15까지의 숫자가 A~F로 할당된다. dl이 11이라면
                    ; 'A'에 해당하는 아스키코드 41h + 0Bh = 4Ch가 되어 'L'에 해당하는
                    ; 아스키코드가 되어 버린다. 여기서 변환하는 목적은 실제 수를 변환
                    ; 하거나 연산하는 것이 아니고, 단순히 그 숫자에 해당하는 아스키
                    ; 코드를 찍어주는 것이다. 그러므로 10~15의 숫자중 1의 자리에 해당
                    ; 하는 0~5만 있으면 A~F의 아스키코드에 짝지울 수 있다.
                    ; 그래서 아스키코드'A'에서 10을 빼주는 것이다. dl이 11이면 10을
                    ; 빼서 1로 만들고 거기에 아스키코드'A'를 더해주면 아스키코드'B'
                    ; 가 나와서 정확한 수치표현을 할 수 있다. 여기서 sub dl, 0Ah를
                    ; 하고 add dl, 'A'를 하지 않는 이유는 불필요한 코드를 줄이기 위해서
                    ; 이다. 'A'-10은 컴파일시에 자동으로 41h-0Ah=37h 하여 37h값으로
                    ; 대치하여 컴파일 해준다. 어째됐건 우리의 목표인 10을 빼는데 문제
                    ; 가 없으므로 필요없는 2줄의 코드를 한줄로 줄여주는 효과가 있다.
WRITE_DIGIT:
        mov ah, 2         ; 도스기능 호출 2번으로 dl에 저장된 하나의 문자를 출력
        int 21h
                
        pop ax             ; 레지스터 복원
        pop dx

        ret
printHex endp

          end L_start   

Posted by houdinist