어셈블러에 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 저장장소의 주소가 유동적으로 늘어나기 때문에
복잡하게 프로그램을 끝내는 과정을 만들 필요가 없다는것에 있습니다.
'어셈블리어 공부용 > x86 어셈블리어(ASSEMBLY)' 카테고리의 다른 글
DOS DEBUG로 어셈블리어 초간단 예제. [S.D.J. 출력하기] (0) | 2019.08.18 |
---|---|
emu8086으로 어셈블리어 연습 [ROL 명령어로 2의 거듭제곱 단위로 수 나열하기] (5) | 2019.08.18 |
emu8086으로 어셈블리어연습 [99 Bottles of Beer] (0) | 2019.08.18 |
emu8086으로 어셈블리어연습 [어셈블리어 구구단] (2) | 2019.08.18 |
emu8086으로 어셈블리어연습 [비디오램에 직접 접근 하여 테트리스 미노 그리기] (0) | 2019.08.18 |
댓글