								      COMMENT ~
BCD.ASM -- BCD Conversion Routines

  From `BLUEBOOK of ASSEMBLY ROUTINES for the IBM PC & XT'
        by Christopher L. Morgan
        Copyright (C) 1984 by The Waite Group, Inc.

        >>>>>  See BCD.DOC for complete descriptions of routines.  <<<<<  
 _____________________________________________________________________________ 

   Contents:
   ---------
   BCDIN	--  Convert from ASCII Decimal to BCD
   BCDOUT	--  Convert from BCD to ASCII Decimal
   BCD2I16	--  Convert from BCD to 16-bit Binary
   I162BCD	--  Convert from 16-bit Binary to BCD
 _____________________________________________________________________________ 
                                                                              ~
EXTRN	STDIN:FAR,STDOUT:FAR
;_____________________________________________________________________________ 
;		
DATAS	SEGMENT	PUBLIC
	BCDBUFF	DB	18 DUP(?)
DATAS	ENDS 
;_____________________________________________________________________________ 
;		
CODES	SEGMENT
	PUBLIC BCDIN,BCDOUT,BCD2I16,I162BCD
ASSUME CS:CODES,DS:DATAS
;______________________________ BCD ROUTINES _________________________________ 
;Routine to convert from ASCII decimal to internal BCD format.
;
BCDIN	  PROC	 FAR

	  PUSH	 DS		       ;Save registers
	  PUSH	 DI
	  PUSH	 DX
	  PUSH	 CX
	  PUSH	 AX
;
;Set up data segment
	  MOV	 DX,DATAS	       ;Point to data segment
	  MOV	 DS,AX
;
;Clear BCD buffer
	  LEA	 DI,BCDBUFF	       ;Point to BCD buffer
	  MOV	 AL,0		       ;zero byte
	  MOV	 CX,18		       ;buffer SIze
BCDIN1:
	  MOV	 [DI],AL	       ;zero the byte
	  INC	 DI		       ;Point to next byte
	  LOOP	 BCDIN1
BCDIN2:
;
; get the digit
	  CALL	 STDIN		       ;Digit comes in AL
	  SUB	 AL,30H 	       ;Subract 30H
	  JL	 BCDIN4 	       ;Check if too low
	  CMP	 AL,9
	  JG	 BCDIN4 	       ;Check if too high
;
;Multiply buffer by 10 and add new digit
	  LEA	 DI,BCDBUFF	       ;Point to BCD buffer
	  MOV	 CX,18		       ;buffer SIze
BCDIN3:
	  PUSH	 CX		       ;Save counter
	  MOV	 DL,AL		       ;Save previous digit
	  MOV	 AL,[DI]	       ;Pick up byte from buffer
	  MOV	 CL,4		       ;for a count of 4
	  SAL	 AX,CL		       ;Shift byte left
	  OR	 AL,DL		       ;Combine with previous digit
	  MOV	 [DI],AL	       ;Result goes back
	  MOV	 AL,AH		       ;Prepare for next byte
	  AND	 AL,0FH 	       ;Strip to byte
	  INC	 DI		       ;Point to next byte
	  POP	 CX		       ;Restore count
	  LOOP	 BCDIN3
	  JMP	 BCDIN2
BCDIN4:
	  POP	 AX		       ;Restore registers
	  POP	 CX
	  POP	 DX
	  POP	 DI
	  POP	 DS
	  RET			       ;Return
BCDIN	  ENDP
;------------------------------------------------------------------------------
;Routine to convert from internal BCD to ASCII decimal
;
BCDOUT	  PROC	 FAR
	  PUSH	 DS		       ;Save registers
	  PUSH	 SI
	  PUSH	 DX
	  PUSH	 CX
	  PUSH	 AX
;
;Set up data segment
	  MOV	 AX,DATAS	       ;Point to data segment
	  MOV	 DS,AX
	  MOV	 CX,18		       ;for a count of 18
	  LEA	 SI,BCDBUFF	       ;Point ot BCD buffer
	  ADD	 SI,17		       ;Point ot end of BCD buffer
	  MOV	 DH,0		       ;Clear flag for leading zeros
BCDOUT1:
	  PUSH	 CX		       ;Save loop count
	  MOV	 AL,[SI]	       ;get BCD byte
	  DEC	 SI		       ;And point to next
	  MOV	 DL,AL		       ;Save it
;
;Upper digit
	  MOV	 CL,4		       ;for a count of 4
	  ROL	 AL,CL		       ;  rotate byte
	  AND	 AL,0FH 	       ;just the digit
	  OR	 DH,AL		       ;Leading zeros
	  JZ	 BCDOUT2	       ;if so skip digit
	  ADD	 AL,30H 	       ;make ASCII
	  CALL	 STDOUT 	       ;Send it out
BCDOUT2:
	  POP	 CX		       ;Restore count
	  CMP	 CX,1		       ;Last digit ?
	  JNZ	 BCDOUT3	       ;Skip in not
	  MOV	 DH,0FFH	       ;  set flag if so
BCDOUT3:
	  PUSH	 CX		       ;Save count
	  MOV	 AL,DL		       ;byte back
;
;Lower digit
	  AND	 AL,0FH 	       ;just the digit
	  OR	 DH,AL		       ;Leading zeros ?
	  JZ	 BCDOUT4	       ;if so skip digit
	  ADD	 AL,30H 	       ;make ASCII
	  CALL	 STDOUT 	       ;Send it out
BCDOUT4:
	  POP	 CX		       ;Restore loop count
	  LOOP	 BCDOUT1
	  POP	 AX		       ;Restore registers
	  POP	 CX
	  POP	 DX
	  POP	 SI
	  POP	 DS
	  RET			       ;Return
BCDOUT	  ENDP
;------------------------------------------------------------------------------
;Routine to convert from internal BCD to internal 16-bit binary.
;
BCD2I16   PROC	 FAR
	  PUSH	 DS		       ;Save registers
	  PUSH	 SI
	  PUSH	 CX
	  PUSH	 AX
;
;Set up data segment
	  MOV	 AX,DATAS	       ;Point of data segment
	  MOV	 DS,AX
;
;Set up loop
	  MOV	 CX,18		       ;initialize counter
	  LEA	 SI,BCDBUFF	       ;Point to buffer
	  ADD	 SI,17		       ;Point to end of BCD buffer
	  MOV	 DX,0		       ;Set DX to zero
BCD2I161:
	  PUSH	 CX		       ;Save loop count
	  MOV	 AL,[SI]	       ;get BCD byte
	  DEC	 SI		       ;And point to next
	  MOV	 BL,AL		       ;Save it
;
;Upper digit
	  MOV	 CL,4		       ;for a count of 4
	  ROL	 AL,CL		       ;  rotate byte
	  AND	 AL,0FH 	       ;just the digit
	  CBW
	  PUSH	 AX		       ;Save digit
	  MOV	 AX,DX
	  MOV	 CX,10		       ;multiplier of 10
	  MUL	 CX		       ;multiply
	  MOV	 DX,AX		       ;Result in DX
	  POP	 AX		       ;Restore digit
	  ADD	 DX,AX		       ;Add in digit
	  MOV	 AL,BL		       ;byte back
;
;Lower digit
	  AND	 AL,0FH 	       ;just digit
	  CBW
	  PUSH	 AX		       ;Save digit
	  MOV	 AX,DX
	  MOV	 CX,10		       ;miltiplier of 10
	  MUL	 CX		       ;multiply
	  MOV	 DX,AX		       ;Result in DX
	  POP	 AX		       ;Restore digit
	  ADD	 DX,AX		       ;Add in digit
	  POP	 CX		       ;Restore loop count
	  LOOP	 BCD2I161
	  POP	 AX		       ;Restore registers
	  POP	 CX
	  POP	 SI
	  POP	 DS
	  RET			       ;Return
BCD2I16   ENDP
;-----------------------------------------------------------------------------
;Routine to convert from internal 16-bit binary to internal BCD.
;
I162BCD   PROC	 FAR
;
;A binary number is in DX
	  PUSH	 DS		       ;Save registers
	  PUSH	 DI
	  PUSH	 DX
	  PUSH	 CX
	  PUSH	 AX
;
;Set up data segment
	  MOV	 AX,DATAS	       ;Point to data segment
	  MOV	 DS,AX
;
;Clear BCD buffer
	  LEA	 DI,BCDBUFF	       ;Point to buffer
	  MOV	 AL,0		       ;zero byte
	  MOV	 CX,18		       ;buffer SIze
I162BCD1:
	  MOV	 [DI],AL	       ;Clear byte
	  INC	 DI		       ;next byte
	  LOOP	 I162BCD1
;
;Put the digits in a buffer
	  LEA	 DI,BCDBUFF	       ;Point to buffer
I162BCD2:
	  MOV	 AX,DX		       ;numerator
	  MOV	 DX,0		       ;Clear upper hALf
	  MOV	 CX,10		       ;Divisor of 10
	  DIV	 CX		       ;Divide
	  XCHG	 AX,DX		       ;get quotient
	  MOV	 BL,AL		       ;Save digit
	  MOV	 AX,DX		       ;numerator
	  MOV	 DX,0		       ;Clear upper hALf
	  MOV	 CX,10		       ;Divisor of 10
	  DIV	 CX		       ;Divide
	  XCHG	 AX,DX		       ;get quotient
	  MOV	 CL,4		       ;for a count of 4
	  ROL	 AL,CL		       ;  rotate digit
	  AND	 AL,0F0H	       ;just the digit
	  OR	 AL,BL		       ;Combine digits
	  MOV	 [DI],AL	       ;Put in buffer
	  INC	 DI		       ;next byte
	  CMP	 DX,0		       ;done ?
	  JNZ	 I162BCD2
	  POP	 AX		       ;Restore registers
	  POP	 CX
	  POP	 DX
	  POP	 DI
	  POP	 DS
	  RET			       ;Return
I162BCD   ENDP
;-----------------------------------------------------------------------------
CODES	ENDS
;
	END
;_____________________________________________________________________________ 
;>>>>> Physical EOF BCD.ASM <<<<<
