home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
pcmag
/
vol8n08.arc
/
MOUSECTL.ASM
< prev
next >
Wrap
Assembly Source File
|
1989-01-14
|
30KB
|
606 lines
;------------------------------------------------------------------------;
; MOUSE.CTL - Enhances mouse sensitivity control. ;
; PC Magazine - Michael J. Mefford ;
;------------------------------------------------------------------------;
_TEXT SEGMENT PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:_TEXT,ES:_TEXT,SS:_TEXT
ORG 100H
START: JMP INITIALIZE
; DATA AREA
; ---------
SIGNATURE DB CR,SPACE,SPACE,SPACE,CR,LF
COPYRIGHT DB "MOUSECTL 1.0 (C) 1988 Ziff Communications Co.",CR,LF
PROGRAMMER DB "PC Magazine ",BOX," Michael J. Mefford",CR,LF,LF,"$"
DB CTRL_Z
CR EQU 13
LF EQU 10
CTRL_Z EQU 26
SPACE EQU 32
BELL EQU 7
BOX EQU 254
IRET_CODE EQU 0CFH
OLD_INT_33 DW ?,?
OLD_INT_10 DW ?,?
VIDEO_MODE DB ?
DISPLAY_MODE EQU 49H ;BIOS data.
TEXT_MODE EQU 0 ;Text video mode flag.
GRAPHICS EQU 1 ;Graphics video mode flag.
CHAR_WIDTH DW ? ;Adjustment for text cell size.
GRAPHICS_WIDTH DW ? ;Adjustment for graphics cell.
MOUSE_VARIABLES LABEL BYTE
HORZ_LAST DW 0 ;Mouse driver mickey count total.
VERT_LAST DW 0
HORZ_ADJUSTED DW 0 ;Mickey total application sees.
VERT_ADJUSTED DW 0
HORZ_REMAINDER DW 0 ;Mickey adjustment remainder.
VERT_REMAINDER DW 0
HORZ_RATIO_REMAIN DW 0 ;Mickey per pixel remainder.
VERT_RATIO_REMAIN DW 0
HORZ_CURS_REMAIN DW 0 ;Text cursor mickey remainder.
VERT_CURS_REMAIN DW 0
CALL_MASK DW 0 ;Subroutine call mask.
INTERRUPT_SUB DW 0,0 ;Subroutine address.
VARIABLES_LENGTH EQU ($ - MOUSE_VARIABLES) / 2
HORZ_RATIO DW 8 ;Mickeys / 8 pixel ratio.
VERT_RATIO DW 16
RANGE DW 3, 5, 7, 9, 12, 23, 32; and above
MULTIPLIER DW 5, 6, 10, 15, 22, 30, 50, 70
MULTIPLIER_END EQU $ - 2
MULTIPLIER_LENGTH EQU ($ - MULTIPLIER) / 2
; CODE AREA
;************* INTERRUPT HANDLERS *************;
NEW_INT_33 PROC FAR
OR AX,AX ;Is it Reset and Status?
JNZ READ_MOUSE
CALL MOUSE_DRIVER ;If yes, call the driver.
PUSH AX ;Save output.
PUSH BX
PUSH CX
PUSH DX
CALL RESET ;Zero out our variables.
MOV CS:HORZ_RATIO,8 ;Restore mickey/pixel ratio.
MOV CS:VERT_RATIO,16
MOV CX,32767 ;Disable mouse driver cursor
MOV DX,CX ; control by setting Mickey
MOV AX,15 ; to Pixel ratio very large.
CALL MOUSE_DRIVER
MOV DX,10000 ;Disable Double-speed by setting
MOV AX,19 ; threshold very large.
CALL MOUSE_DRIVER
MOV CX,1 ;Install our interrupt subroutine
CALL SET_INTERRUPT
POP DX ;Restore returned values.
POP CX
POP BX
POP AX
IRET ;Return to caller.
READ_MOUSE: CMP AX,11 ;Is it Read Mouse
JNZ SUBROUTINE ; Motion Counters?
CALL MOUSE_DRIVER ;If yes, call driver.
CLI ;No interrupts, especially mouse.
MOV CX,CS:HORZ_ADJUSTED ;Return adjusted mickey count.
MOV DX,CS:VERT_ADJUSTED
MOV CS:HORZ_LAST,0 ;Reset driver mickey count.
MOV CS:VERT_LAST,0
MOV CS:HORZ_ADJUSTED,0 ;Reset adjusted mickey count.
MOV CS:VERT_ADJUSTED,0
IRET
SUBROUTINE: CMP AX,12 ;Is it Set Interrupt Subroutine
JNZ SET_RATIO ; Call Mask and Address?
MOV CS:CALL_MASK,CX ;If yes, store call mask.
MOV CS:INTERRUPT_SUB[0],DX ;Store address.
MOV CS:INTERRUPT_SUB[2],ES
PUSH CX ;Save call mask.
OR CX,1 ;Add Cursor position changes
CALL SET_INTERRUPT ; and install subroutine.
POP CX ;Restore CX.
IRET
SET_RATIO: CMP AX,15 ;Is it Set Mickey/Pixel Ratio?
JNZ SWAP_INT
MOV CS:HORZ_RATIO,CX ;If yes, store requested values.
MOV CS:VERT_RATIO,DX
IRET
SWAP_INT: CMP AX,20 ;Is it Swap Interrupt
JNZ DOUBLE_SPEED ; Subroutines?
PUSH CS:CALL_MASK ;If yes, save old call mask
PUSH CS:INTERRUPT_SUB[0] ; and subroutine address.
PUSH CS:INTERRUPT_SUB[2]
MOV CS:CALL_MASK,CX ;Store new call mask and
MOV CS:INTERRUPT_SUB[0],DX ; subroutine address.
MOV CS:INTERRUPT_SUB[2],BX
OR CX,1 ;Set our subroutine with
CALL SET_INTERRUPT ; new call mask.
POP BX ;Return old call mask and
POP DX ; subroutine address.
POP CX
IRET
DOUBLE_SPEED: CMP AX,19 ;Skip Double-Speed Threshold.
JZ INT_33_END
CMP AX,26 ;Skip Sensitivity.
JZ INT_33_END
JMP CS:DWORD PTR OLD_INT_33 ;Pass all other calls as is.
INT_33_END: IRET
NEW_INT_33 ENDP
;----------------------------------------------;
RESET: PUSH DI ;Preserve registers.
PUSH ES
PUSH CS
POP ES
XOR AX,AX ;Store zeros in appropriate
MOV DI,OFFSET MOUSE_VARIABLES ; mouse variables.
MOV CX,VARIABLES_LENGTH
CLD ;Forward direction.
CLI ;No interrupts.
REP STOSW
POP ES ;Restore registers.
POP DI
RET
;----------------------------------------------;
; INPUT: CX = Call Mask ;
;----------------------------------------------;
SET_INTERRUPT: PUSH AX ;Preserve registers.
PUSH DX
PUSH ES
PUSH CS
POP ES
MOV DX,OFFSET MOUSE_CONTROL ;Install our subroutine.
MOV AX,12
CALL MOUSE_DRIVER
POP ES ;Restore registers.
POP DX
POP AX
RET
;----------------------------------------------;
MOUSE_DRIVER: PUSHF
CLI
CALL CS:DWORD PTR OLD_INT_33
RET
;----------------------------------------------;
NEW_INT_10 PROC FAR
OR AH,AH ;Video mode change?
JZ INT_UPDATE ;If yes, update variables.
CMP AH,11H ;Is it User Alpha Load?
JAE INT_UPDATE ;If yes, update variables.
JMP CS:DWORD PTR OLD_INT_10 ;Else, no business.
INT_UPDATE: PUSHF ;Update by calling INT 10
CALL CS:DWORD PTR OLD_INT_10
CALL GET_BIOS_DATA ;Then retrieve video data.
IRET
NEW_INT_10 ENDP
;----------------------------------------------;
GET_BIOS_DATA: PUSH AX ;Preserve registers.
PUSH DS
MOV AX,40H ;Point to BIOS data.
MOV DS,AX
MOV AL,DS:DISPLAY_MODE ;Retrieve display mode.
PUSH CS
POP DS ;Point to our data.
MOV AH,TEXT_MODE ;Assume text mode.
MOV CHAR_WIDTH,16 ;Assume low resolution.
CMP AL,1 ;Is it 40 column?
JBE BIOS_END ;If yes, assumed right.
MOV CHAR_WIDTH,8 ;Else, assume high resolution.
CMP AL,3 ;Is it 80 column color or
JBE BIOS_END
CMP AL,7 ; MONO? If yes, assumed right.
JZ BIOS_END
MOV AH,GRAPHICS ;Else, gotta be graphics mode.
MOV GRAPHICS_WIDTH,1 ;Assume low resolution.
CMP AL,5 ;Is it mode 4, 5, D, or 13?
JBE BIOS_END ;If yes, assumed right?
CMP AL,0DH
JZ BIOS_END
CMP AL,13H
JZ BIOS_END
MOV GRAPHICS_WIDTH,0 ;Else gotta be high resolution.
BIOS_END: MOV DS:VIDEO_MODE,AH ;Store video mode.
POP DS ;Restore registers.
POP AX
RET
;----------------------------------------------;
MOUSE_CONTROL PROC FAR
PUSH AX ;Condition mask.
PUSH BX ;Button state.
PUSH DX ;Vertical cursor coordinate.
PUSH DI ;Vertical Mickeys.
MOV BP,CX ;Horizontal cursor coordinate.
PUSH CS ;Point to our data
POP DS
PUSH CS ; and extra segment.
POP ES
CLD ;All strings forward.
MOV AX,HORZ_LAST ;Retrieve last mickey count.
MOV HORZ_LAST,SI ;Last gets new count.
SUB SI,AX ;SI gets difference since last.
JZ VERTICAL ;If no change, done here.
MOV AX,HORZ_REMAINDER ;Else, retrieve remainder.
CALL LOOKUP ;Calculate adjusted mickeys.
ADD HORZ_ADJUSTED,AX ;Add to our mickey count.
MOV HORZ_REMAINDER,DX ;Store remainder for next time.
MOV CX,8 ;Multiply count by 8.
IMUL CX
MOV SI,HORZ_RATIO_REMAIN ;Last ratio remainder.
MOV CX,HORZ_RATIO ;Mickeys per 8 pixels.
CALL SIGN_ADD_DIV ;Calculate Mickey/pixels.
MOV HORZ_RATIO_REMAIN,DX ;Store remainder for next time.
CMP VIDEO_MODE,GRAPHICS ;Graphics video mode?
JNZ HORZ_TEXT ;If no, do text cursor.
CMP AX,0 ;Else, are there any mickeys?
JZ VERTICAL ;If no, done here.
MOV CX,GRAPHICS_WIDTH ;Retrieve resolution adjustment.
JG ADD_WIDTH ;Is it positive mickeys?
NEG CX ;If no, negate adjustment.
ADD_WIDTH: ADD AX,CX ;Add adjustment value.
ADD BP,AX ;Add result to cursor position.
JMP SHORT VERTICAL ;Done here.
HORZ_TEXT: ADD AX,HORZ_CURS_REMAIN ;Add last remainder.
CWD ;Convert to double word.
MOV SI,AX ;Store in SI.
MOV CX,CHAR_WIDTH ;Divide by mouse
IDIV CX ; pixels/character (8 or 16).
MOV HORZ_CURS_REMAIN,DX ;Store remainder for next time.
SUB SI,DX ;Subtract remainder to get pixels
ADD BP,SI ;Add result to cursor position.
;----------------------------------------------;
VERTICAL: POP SI ;Retrieve vertical Mickeys.
POP BX ;Retrieve vertical coordinate.
MOV AX,VERT_LAST
MOV VERT_LAST,SI ;Calculate vertical same as
SUB SI,AX ; horizontal.
JZ MOVE_CURSOR
MOV AX,VERT_REMAINDER
CALL LOOKUP
ADD VERT_ADJUSTED,AX
MOV VERT_REMAINDER,DX
MOV CX,8
IMUL CX
MOV SI,VERT_RATIO_REMAIN
MOV CX,VERT_RATIO
CALL SIGN_ADD_DIV
MOV VERT_RATIO_REMAIN,DX
CMP VIDEO_MODE,GRAPHICS
JNZ VERT_TEXT
ADD BX,AX
JMP SHORT MOVE_CURSOR
VERT_TEXT: ADD AX,VERT_CURS_REMAIN
CWD
MOV SI,AX
MOV CX,8
IDIV CX
MOV VERT_CURS_REMAIN,DX
SUB SI,DX
ADD BX,SI
;----------------------------------------------;
MOVE_CURSOR: MOV CX,BP ;New horizontal cursor coordinate
MOV DX,BX ;New vertical cursor coordinate.
MOV AX,4 ;Set Mouse Cursor.
CALL MOUSE_DRIVER
POP BX ;Retrieve Button state
POP AX ;Retrieve Condition mask.
TEST AX,CALL_MASK ;Is there a subroutine installed?
JZ MOUSE_END ;If no, done here.
MOV SI,HORZ_ADJUSTED ;Else, pass on vertical mouse
MOV DI,VERT_ADJUSTED ; counts and horizontal mouse
CALL DWORD PTR INTERRUPT_SUB ; counts to the subroutine.
MOUSE_END: RET
MOUSE_CONTROL ENDP
;--------------------------------------------------------------------;
;INPUT: SI = MICKEYS; AX = Last remainder; Flags = sign of MICKEYS. ;
;OUTPUT: AX = Adjusted MICKEYS, DX = New remainder. ;
;--------------------------------------------------------------------;
LOOKUP: PUSHF ;Save sign.
JNS EXCHANGE
NEG SI ;Absolute value of Mickeys.
EXCHANGE: XCHG AX,SI ;Store in AX for scan.
MOV CX,MULTIPLIER_LENGTH - 1 ;Length of table.
MOV DI,OFFSET RANGE ;Point to table.
NEXT_ACCEL: SCASW ;Scan until Mickeys exceeds
JB GOT_ACCEL ; range.
LOOP NEXT_ACCEL
GOT_ACCEL: POPF ;Retrieve sign.
JNS MULTIPLY
NEG AX ;Return sign to Adjusted Mickeys.
MULTIPLY: MOV DI,OFFSET MULTIPLIER_END ;Point to multipliers.
SHL CX,1 ;Retrieve multiplier for
SUB DI,CX ; appropriate range.
MOV CX,[DI]
IMUL CX ;Multiply.
MOV CX,10 ;Divide to achieve implied
CALL SIGN_ADD_DIV ; decimal point.
RET
;----------------------------------------------------------------------;
; Calculates: signed double word DX:AX + SI / CX; OUTPUT: DX:AX=Result ;
;----------------------------------------------------------------------;
SIGN_ADD_DIV: XCHG AX,SI ;Place SI in AX to sign extend.
MOV DI,DX ;DX:AX now in DI:SI.
CWD ;Sign extend AX to DX:AX.
ADD AX,SI ;Add to DX:DX, DI:SI.
ADC DX,DI
IDIV CX ;Divide DX:AX result by CX.
RET ; and return.
; DISPOSABLE DATA
; ---------------
SYNTAX LABEL BYTE
DB "Syntax: MOUSECTL [n ... n | filename | /U]",CR,LF
DB "n = up to 8 mouse multipliers with values between 0-32767"
DB CR,LF,"defaults = 5,6,10,15,22,30,50,70",CR,LF
DB "Implied decimal in values; ie. 5 = 0.5, 18 = 1.8",CR,LF
DB "filename = file with multiplier values",CR,LF
DB "/U = Uninstall"
CR_LF DB CR,LF,LF,"$"
NOT_ENOUGH DB "Not enough memory",CR,LF,"$"
NOT_FOUND DB "File not found",CR,LF,"$"
NO_MOUSE DB "Mouse driver not installed so "
NOT_INSTALLED DB "MOUSECTL not installed",CR,LF,"$"
UNLOAD_MSG DB "MOUSECTL can't be uninstalled",CR,LF
DB "Uninstall resident programs in reverse order",CR,LF,"$"
ALLOCATE_MSG DB "Memory allocation error",CR,LF,BELL,"$"
INSTALL_MSG DB "Installed",CR,LF,"$"
UNINSTALL_MSG DB "Uninstalled",CR,LF,"$"
;--------------------------------------------------------------------;
; Search memory for a copy of our code, to see if already installed. ;
;--------------------------------------------------------------------;
INITIALIZE PROC NEAR
CLD ;All string operations forward.
MOV BX,OFFSET START ;Point to start of code.
NOT BYTE PTR [BX] ;Change a byte so no false match.
XOR DX,DX ;Start at segment zero.
MOV AX,CS ;Store our segment in AX.
NEXT_PARAG: INC DX ;Next paragraph.
MOV ES,DX
CMP DX,AX ;Is it our segment?
JZ ANNOUNCE ;If yes, search is done.
MOV SI,BX ;Else, point to our signature
MOV DI,BX ; and offset of possible match.
MOV CX,16 ;Check 16 bytes for match.
REP CMPSB
JNZ NEXT_PARAG ;If no match, keep looking.
;----------------------------------------------;
ANNOUNCE: MOV DX,OFFSET SIGNATURE ;Display our signature.
CALL PRINT_STRING
MOV DX,OFFSET SYNTAX ;And syntax.
CALL PRINT_STRING
;----------------------------------------------;
PARSE: MOV SI,81H ;Point to command line again.
NEXT_PARSE: LODSB ;Get a byte.
CMP AL,CR ;Is it carriage return?
JZ INSTALL ;If yes, done here.
CMP AL,"/" ;Is it switch character?
JNZ CK_NUMBER ;If no, check if number.
LODSB ;Else, get the switch.
DEC SI ;Adjust pointer.
AND AL,5FH ;Capitalize.
CMP AL,"U" ;Is it "U"?
JNZ NEXT_PARSE ;If no, continue parsing.
CALL CK_INSTALLED ;Else, are we installed?
MOV DX,OFFSET NOT_INSTALLED ;If no, exit with error message.
JZ MSG_EXIT
JMP SHORT UNINSTALL ;Else, attempt to uninstall.
CK_NUMBER: CMP AL,"9" ;Is it a number?
JA CK_FILENAME ;If no, must be a filename.
CMP AL,"0"
JB CK_FILENAME
DEC SI ;Else, adjust pointer.
JMP SHORT LOAD_TABLE ;Load the table with multipliers.
CK_FILENAME: CMP AL,SPACE ;Is it a delimiting character?
JBE NEXT_PARSE ;If yes, continue parsing.
PUSH SI ;Else, save start.
FIND_END: LODSB ;Find the end and convert
CMP AL,SPACE ; to ASCIIZ.
JA FIND_END
MOV BYTE PTR [SI-1],0
POP DX ;Retrieve start.
DEC DX ;Adjust.
MOV AX,3D00H ;Open file for reading.
INT 21H
MOV DX,OFFSET NOT_FOUND ;Exit with error message if
JC MSG_EXIT ; not found.
MOV BX,AX ;Else, save handle.
MOV DX,OFFSET BUFFER ;Point to buffer and
MOV CX,100 ; read up to 100 characters.
MOV AH,3FH
INT 21H
MOV AH,3EH ;Close the file.
INT 21H
MOV SI,DX ;Point to buffer.
MOV BYTE PTR [SI+100],CR ;Add ending carriage return.
LOAD_TABLE: MOV DI,OFFSET MULTIPLIER ;Point to multiplier table.
MOV CX,MULTIPLIER_LENGTH ;Number of entries.
NEXT_MULT: LODSB ;Read a byte.
CMP AL,CR ;Ending carriage return?
JZ INSTALL ;If yes, done here.
CMP AL,"0" ;Is it delimiter?
JB NEXT_MULT ;If yes, next byte.
DEC SI ;Else, adjust pointer.
CALL DECIMAL_HEX ;Convert decimal to hex.
STOSW ;Store the value.
LOOP NEXT_MULT ;Retrieve all values.
;----------------------------------------------;
INSTALL: CALL CK_INSTALLED ;Check if already installed.
JZ CK_MOUSE ;If no, see if mouse installed.
OR AL,AL ;Else, done.
JMP SHORT EXIT ;Exit with ERRORLEVEL = 0.
;-------------------------------------------------------------------;
; Exit. Return ERRORLEVEL code 0 if successful, 1 if unsuccessful. ;
;-------------------------------------------------------------------;
MSG_EXIT: CALL PRINT_STRING
ERROR_EXIT: MOV AL,1 ;ERRORLEVEL = 1.
EXIT: MOV AH,4CH ;Terminate.
INT 21H
;-------------------------------------------------------;
; This subroutine uninstalls the resident MOUSECTL.COM. ;
;-------------------------------------------------------;
UNINSTALL: MOV CX,ES ;Save segment in CX.
MOV AX,3533H ;Get interrupt 33.
INT 21H
CMP BX,OFFSET NEW_INT_33 ;Has it been hooked by another?
JNZ UNINSTALL_ERR ;If yes, exit with error message.
MOV BX,ES
CMP BX,CX ;Is the segment vector same?
JNZ UNINSTALL_ERR ;If no, exit with error message.
MOV AX,3510H ;Get interrupt 10.
INT 21H
CMP BX,OFFSET NEW_INT_10 ;Has it been hooked by another?
JNZ UNINSTALL_ERR ;If yes, exit with error message.
MOV BX,ES
CMP BX,CX ;Is the segment vector same?
JNZ UNINSTALL_ERR ;If no, exit with error message.
MOV AH,49H ;Return memory to system pool.
INT 21H
MOV DX,OFFSET ALLOCATE_MSG
JC MSG_EXIT ;Display message if problem.
MOV DX,ES:OLD_INT_33[0] ;Restore old INT 33.
MOV DS,ES:OLD_INT_33[2]
MOV AX,2533H
INT 21H
MOV DX,ES:OLD_INT_10[0] ;Restore old INT 10.
MOV DS,ES:OLD_INT_10[2]
MOV AX,2510H
INT 21H
XOR AX,AX ;Mouse Reset.
INT 33H
PUSH CS
POP DS ;Point to our data.
MOV DX,OFFSET UNINSTALL_MSG ;Display uninstall message.
CALL PRINT_STRING
XOR AL,AL ;Exit with ERRORLEVEL = 0.
JMP EXIT
UNINSTALL_ERR: MOV ES,CX ;If error, exit
MOV DX,OFFSET UNLOAD_MSG ; with error message.
JMP MSG_EXIT
;--------------------------------;
; This is the install procedure. ;
;--------------------------------;
CK_MOUSE: MOV AX,3533H ;Else, get INT 33 vector.
INT 21H
MOV AX,ES
OR AX,AX ;If vector zero, no mouse driver.
MOV DX,OFFSET NO_MOUSE
JZ MSG_EXIT
MOV AL,ES:[BX]
CMP AL,IRET_CODE
JZ MSG_EXIT
MOV OLD_INT_33[0],BX ;Save old interrupt.
MOV OLD_INT_33[2],ES
MOV DX,OFFSET NEW_INT_33 ;Install new interrupt.
MOV AX,2533H
INT 21H
XOR AX,AX
INT 33H ;Initialize driver.
CALL GET_BIOS_DATA ;Get video mode.
MOV AX,3510H ;Get INT 10 vector.
INT 21H
MOV OLD_INT_10[0],BX ;Save old interrupt.
MOV OLD_INT_10[2],ES
MOV DX,OFFSET NEW_INT_10 ;Install new interrupt.
MOV AX,2510H
INT 21H
MOV AX,DS:[2CH] ;Get environment segment.
MOV ES,AX
MOV AH,49H ;Free up environment.
INT 21H
MOV DX,OFFSET INSTALL_MSG ;Display install message.
CALL PRINT_STRING
MOV DX,OFFSET SYNTAX ;End of resident portion.
ADD DX,15 ;Round up.
MOV CL,4
SHR DX,CL ;Convert to paragraphs.
MOV AX,3100H ;Return error code of zero.
INT 21H ;Terminate but stay resident.
INITIALIZE ENDP
;-------------------------------------------------------;
; OUTPUT: ZR = 1 if not installed; ZR = 0 if installed. ;
;-------------------------------------------------------;
CK_INSTALLED: MOV AX,ES
MOV BX,CS
CMP AX,BX ;Compare segments.
RET
;-------------------------------------------------------------;
; INPUT: SI points to decimal number; OUTPUT: AX = hex number ;
;-------------------------------------------------------------;
DECIMAL_HEX: XOR BX,BX
NEXT_DECIMAL: LODSB ;Get a character.
SUB AL,"0" ;ASCII to binary.
JC DECIMAL_END ;If not between 0 and 9, skip.
CMP AL,9
JA DECIMAL_END
CBW ;Convert to word.
XCHG AX,BX ;Swap old and new number.
MOV DX,10 ;Shift to left by multiplying
MUL DX ; last entry by ten.
ADD BX,AX ;Add new number and store in BX.
JMP NEXT_DECIMAL
DECIMAL_END: MOV AX,BX ;Return in AX.
DEC SI ;Adjust pointer.
RET
;----------------------------------------------;
PRINT_STRING: MOV AH,9
DOS_INT: INT 21H ;Print string via DOS.
RET
BUFFER EQU $
_TEXT ENDS
END START