; ; CPASM05S.ASM ; written by Keith Fenske ; Sunday, 10 March 2002 ; Copyright (c) 2002 by Keith Fenske. All rights reserved. ; ; This is an Intel 80x86 assembly language program to read an unsigned ; decimal number from the user (keyboard), and then print the same number ; in hexadecimal. ; ; The program stops when the input is null (empty), or when the number is ; zero. ; ; This program is smaller than CPASM04S.ASM. Why? Because, the hard part ; about this program is deciding what to do. CPASM04S.ASM was for writing ; code in similar ways to do similar jobs. ; [BITS 16] ; set 16-bit code generation [ORG 0100H] ; set address of first instruction in COM file CR equ 0Dh ; carriage return character LF equ 0Ah ; line feed character MAXINPUT equ 72 ; maximum number of bytes to read from user [SECTION .text] ; start code (instruction) section start: ; ; Prompt the user and then read a line of input. Quit this program if the ; input line is null (empty = zero bytes of input). ; get_input: mov bx,1 ; DOS file handle 1 is standard output mov cx,prompt_size ; length of prompt string in bytes mov dx,prompt ; address of first byte in prompt string mov ah,40h ; DOS function code to write string int 21h ; call DOS ; mov [input],byte MAXINPUT+1 ; maximum number of bytes to read, including the ; CR (or new line) character mov [input+1],byte 0 ; clear number of bytes read mov dx,input ; address of input buffer (two sizes + text) mov ah,0Ah ; DOS function code for buffered input int 21h ; call DOS ; cmp [input+1],byte 0 ; was there any input? jle near done ; no, quit ; ; Check that the input has only decimal digits. Any other characters (even ; a space) will result in an error message. ; mov cl,[input+1] ; get number of bytes read before CR (new line) mov ch,0 ; clear high-order byte of CX register mov si,input+2 ; starting address of user's input check_loop: cmp [si],byte '0' ; is input byte less than a zero? jb check_error ; jump if below (characters are unsigned) cmp [si],byte '9' ; is input byte greater than a nine? ja check_error ; jump if above (unsigned) inc si ; address of next input byte loop check_loop ; repeat for all input bytes (until CX=0) jmp convert ; if everything is okay ; check_error: mov bx,1 ; DOS file handle 1 is standard output mov cx,error_1_size ; length of error message in bytes mov dx,error_1 ; address of first byte in error message mov ah,40h ; DOS function code for write string int 21h ; call DOS jmp get_input ; and get new input ; ; Convert decimal digits from ASCII to an unsigned binary number. We can ; now assume that all of the input bytes are ASCII digits from '0' to '9'. ; The only error we can get during conversion if the number gets too big ; for a 16-bit unsigned word (overflow). ; convert: mov [number],word 0 ; clear unsigned binary number mov cl,[input+1] ; get number of bytes read before CR (new line) mov ch,0 ; clear high-order byte of CX register mov si,input+2 ; starting address of user's input convert_loop: mov ax,[number] ; get running total for unsigned binary number mov bx,10 ; new total = (10 x old total) + new digit mul bx ; multiply previous total in AX by 10 jo convert_error ; jump if overflow: number is now too big ; mov bl,[si] ; copy input byte into low-order byte of BX register and bl,0Fh ; convert ASCII char code to unsigned binary number mov bh,0 ; clear high-order byte add ax,bx ; add new digit to running total jc convert_error ; jump if carry: number is now too big mov [number],ax ; save the unsigned binary number ; inc si ; address of next input byte loop convert_loop ; repeat for all input bytes (until CX=0) cmp [number],word 0 ; is the converted number equal to zero? je done ; yes, quit jmp print ; no, go print it ; convert_error: mov bx,1 ; DOS file handle 1 is standard output mov cx,error_2_size ; length of error message in bytes mov dx,error_2 ; address of first byte in error message mov ah,40h ; DOS function code for write string int 21h ; call DOS jmp get_input ; and get new input ; ; Print the unsigned binary number in hexadecimal. We do this by removing ; the low-order 4 bits (or "nibble"), and converting from an unsigned binary ; "nibble" to ASCII characters. The conversion is done backwards because ; it is easier to strip off the low-order part of numbers. ; print: mov ax,[number] ; get unsigned binary number in AX register mov cx,4 ; how many hex digits to produce mov di,output_hex+3 ; where *last* hex digit goes print_loop: mov bx,ax ; copy binary number into BX register and bx,000Fh ; save only low-order nibble (4 bits) mov bl,[hex_string+bx] ; convert binary nibble to ASCII character mov [di],bl ; and save ASCII character in output string shr ax,4 ; drop low-order nibble from binary number dec di ; address of next output byte loop print_loop ; repeat for all hex digits (until CX=0) ; mov bx,1 ; DOS file handle 1 is standard output mov cx,output_size ; length of output string in bytes mov dx,output ; address of first byte in output string mov ah,40h ; DOS function code for write string int 21h ; call DOS ; ; Now go get the next line of input from the user. ; jmp get_input ; repeat until input is null (empty) ; ; The user gave us an empty input line (or the number 0), so exit from this ; program after printing a goodbye message. ; done: mov bx,1 ; DOS file handle 1 is standard output mov cx,goodbye_size ; length of goodbye string in bytes mov dx,goodbye ; address of first byte in goodbye string mov ah,40h ; DOS function code for write string int 21h ; call DOS ; mov ah,4Ch ; DOS function code to exit program mov al,0 ; pass this value back as ERRORLEVEL int 21h ; call DOS (does not return) [SECTION .data] ; start initalized data section ; ; The following data section contains only those data regions that have ; initial values. ; error_1 db CR, LF, db "Sorry, your input is not an unsigned decimal number from 0 " db "to 65535. Please", CR, LF db "type only digits from 0 to 9 and then press the Enter key. " db "Please do not type", CR, LF db "spaces or other punctuation characters.", CR, LF error_1_size equ $ - error_1 ; length of error string in bytes error_2 db CR, LF, "Sorry, your number is too big.", CR, LF error_2_size equ $ - error_2 ; length of error string in bytes goodbye db CR, LF, "See you later!", CR, LF goodbye_size equ $ - goodbye ; length of goodbye string in bytes hex_string db "0123456789ABCDEF" output db CR, LF db "Your number is " output_hex db "FFFF" db " in hexadecimal.", CR, LF output_size equ $ - output ; length of output string in bytes prompt db CR, LF db "Please type an unsigned decimal number from 1 to 65535, " db CR, LF, "or the number 0 to quit: " prompt_size equ $ - prompt ; length of prompt string in bytes [SECTION .bss] ; start uninitialized data section ; ; The following data sections contains buffers (etc) that do not have ; initial values. The data must be created while the program is running. ; ; Note that the following three lines must be kept together to make a valid ; description of a DOS input buffer. ; input resb 1 ; maximum number of bytes to read from user resb 1 ; gets set to actual bytes read resb MAXINPUT+1 ; where to put bytes from user, plus CR number resw 1 ; unsigned binary number after conversion ; ; End of CPASM05S.ASM ;