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

emu 8086으로 어셈블리어 연습 [16진수 음수(보수) 10진수로 출력하기]

by Redoutable 2019. 8. 18.
반응형

어셈블러에 10진수를 입력하게되면 메모리에는 16진수 형태로 저장되어 집니다.

때문에 10진수로 데이터를 입력했다고해도 출력할 때는 10진수로 변환해줄 필요가 있습니다.

그 중에서도 십진수 음수는 2의 보수형태의 16진수로 저장되어 지는데,

이 2의 보수를 10진수로 그대로 변환하면 전혀 엉뚱한 값이 나옵니다.

 

때문에 음수는 특별한 처리를 해주어야 하는데 그 예제를 살펴보겠습니다.

 

예제에서는 1135와 -1135를 입력받았다고 가정했습니다.

 

 

.MODEL SMALL
.CODE
ORG 100h
;---------------
PROG:


CALL MAIN           ;전체적인 흐름입니다. 메인 프로시저가 끝나면 HLT로 프로그램을 중단합니다.
HLT


;---------------
NUM1 DW 1135        ; DW 단위 데이터영역을 만들고 십진수 1135를 저장합니다.
NUM2 DW -1135       ; DW 단위 데이터영역을 만들고 십진수 -1135를 저장합니다.
ENDMARK DB '$$'     ; DB 단위 데이터영역을 만들고 '$$'를 수록합니다. 아래쪽에서 용도를 설명하겠습니다. 
;---------------
MAIN PROC NEAR

LEA SI,NUM1         ; SI 레지스터에 NUM1데이터 영역의 주소를 수록합니다.

HEXA:               ; 맨 처음.

MOV AX,[SI]         ; SI 레지스터가 가르키고있는 주소의 데이터를 AX 레지스터에 가져옵니다.
CMP AX,'$$'         ; AX 레지스터값이 '&&'인지 확인합니다. 16진수로 2424h
JE ENND             ; 맞다면 프로그램을 ENND 라벨로 보냅니다.

CMP AX,0            ; AX 레지스터가 음수인지 아닌지 확인하기 위하여 0과 비교합니다.
JGE GO              ; 0보다 크거나 같다면 음수변환 과정을 거치지 않도록 바로 진수변환 하는곳으로 보냅니다.


NEG AX              ; AX 레지스터값을 반전합니다. 음수값이 양수값으로 바뀝니다. (반대도 가능)
PUSH AX             ; AX 레지스터값을 일단 스택에 넣어서 보존시킵니다.
MOV DL, '-'         ; DL레지스터에 '-' 기호 값을 넣습니다.
MOV AH,02           ; AH 레지스터에 기능번호 2번을 넣습니다.
INT 21h             ; 실행
POP AX              ; 보존된 AX 레지스터값을 가져옵니다.

GO:
MOV CX,0            ; 진수변환 앞서 레지스터 청소
MOV DX,0            ; 진수변환 앞서 레지스터 청소
MOV BX,10           ; 10로 진수변환 하기위하여 BX 레지스터에 10 수록. 

PUSHDECA:

MOV DX,0            ; 나눗셈 후 나머지값이 나눗셈에 이용되지 않도록 청소

DIV BX              ; AX 레지스터를 BX 레지스터로 나눔 (몫 : AX / 나머지 : DX)
PUSH DX             ; 나머지값을 스택에 보존.
INC CX              ; CX 레지스터에 1을 더함. (출력부분에서 사용됨)
CMP AX,0            ; AX 레지스터(몫)이 0인지 아닌지 확인
JG PUSHDECA         ; 몫이 남아있다면 진수변환 계속함.

POPDECA:
POP DX              ; 스택에서 DX레지스터로 값 가져옴. 
ADD DX,30h          ; 아스키코드화 시키기 위하여 30h를 더해줌.
MOV AH,2            ; AH 레지스터에 기능번호 2번 수록
INT 21h             ; 실행
DEC CX              ; 위에서 증가시켜두었던 CX값을 1 빼기.
CMP CX,0            ; CX 레지스터값이 0인지 아닌지 비교.
JNE POPDECA         ; CX 값이 0이 될 때 까지 출력 계속함.

ADD SI,2            ; SI 레지스터에 2를 더하여 그 다음 데이터 영역으로 주소 옮김 (DW단위2씩움직.2칸차지하)

                    ; 캐리지리턴, 라인피드 실행하는 곳.
MOV AH,02           
MOV DL,0Dh
INT 21h

MOV DL,0Ah
INT 21h

JMP HEXA           ; 맨 처음(HEXA)으로 돌아간다.

ENND:
RET                ; 메인루틴으로 나가기. (메인루틴으로 나가면 HLT를 만나게 됩니다.)

MAIN ENDP
END PROG

그러니까 쉽게 말하자면,

보수를 NEG 처리해 주고, 그 앞에다 '-'만 붙혀서

출력해주는 프로그램입니다.

 

CMP AX,0            ; AX 레지스터가 음수인지 아닌지 확인하기 위하여 0과 비교합니다.
JGE GO              ; 0보다 크거나 같다면 음수변환 과정을 거치지 않도록 바로 진수변환 하는곳으로 보냅니다.


NEG AX              ; AX 레지스터값을 반전합니다. 음수값이 양수값으로 바뀝니다. (반대도 가능)
PUSH AX             ; AX 레지스터값을 일단 스택에 넣어서 보존시킵니다.
MOV DL, '-'         ; DL레지스터에 '-' 기호 값을 넣습니다.
MOV AH,02           ; AH 레지스터에 기능번호 2번을 넣습니다.
INT 21h             ; 실행
POP AX              ; 보존된 AX 레지스터값을 가져옵니다.

이 부분이 레지스터에 들어있는

보수를 NEG 처리해주는 부분입니다.

 

 

정상적으로 처리되면 1135와 -1135를 출력합니다.

 

 

하지만 NEG 를 생략하게되면

전혀 엉뚱한 수가 출력되는데...

 

이것은 16진수 FB91을 그대로 10진수로

변환했기 때문입니다.

그래서 음수를 나타내고자 할 때에는

위와 같은 별도처리를 해주셔야 합니다.

 

MOV AX,[SI]         ; SI 레지스터가 가르키고있는 주소의 데이터를 AX 레지스터에 가져옵니다.
CMP AX,'$$'         ; AX 레지스터값이 '&&'인지 확인합니다. 16진수로 2424h
JE ENND             ; 맞다면 프로그램을 ENND 라벨로 보냅니다.

그리고 이 부분의 용도는 문자열 종결문자'$'와 비슷한 기능을 합니다.

 

'ADD SI, 2' 이 코드로

SI레지스터가 데이터 저장영역을 옮겨다니게 됩니다.

 

그러다가 ENDMARK라는 데이터 저장영역의 값을 가져오게되면 ('$$')

AX 레지스터에 24 24 가 실리게 되는데

CMP AX, '$$' 명령으로 24 24라는 값이 실렸는지 판단하고,

그 값이 실려있으면 끝으로 보내는 구조입니다. (JE ENND)

 

즉 SI 레지스터가 가르키는 주소에 있는 데이터는 2씩 옮겨다니며,

차례대로 1135 ,-1135, '$$' 순으로 레지스터에 전송하게 됩니다.

이 방법의 장점은 추후에 DW단위 저장영역을 추가해서 어셈블해도

ENDMARK 저장장소의 주소가 유동적으로 늘어나기 때문에

복잡하게 프로그램을 끝내는 과정을 만들 필요가 없다는것에 있습니다.

반응형

댓글