; Routine for converting a 64 bit number to an ASCII string in any base.
; X-Calibre, Phrozen Crew 2000

Divide64		MACRO
; Input:	64 bit dividend in EBX::ECX, 32 bit divisor in ESI
; Output:	64 bit result in EBX::EAX, 32 bit remainder in EDX
; Uses:		EAX, EBX, ECX, EDX, ESI

	; Divide high dword by divisor.
	mov		eax, ebx
	xor		edx, edx
	div		esi
	; Put remainder as high dword of the original dividend.
	mov		ebx, eax
	mov		eax, ecx
	div		esi

	; Note that this is a 'long division' algorithm. It can easily be expanded to
	; be able to divide any number by 32 bits. I only use it for 64 bits here to
	; keep the CPU from getting an exception on overflow when the input is larger
	; than ((2^32)-1)*divisor, so that printing any 64 bit number with any radix
	; is possible.
ENDM

PrintRadixUnsigned	PROC
; Input:	64 bit unsigned number in EBX::ECX, radix in ESI, pointer to output buffer in EDI
; Output:	Zero-terminated ASCII string in output buffer, length of string in EAX
; Uses:		EAX, EBX, ECX, EDX, ESI, EDI, EBP

	xor		ebp, ebp	; StringLength counter

	; If the high dword of the number is larger than the divisor, we
	; have to do a 'long division' to prevent overflow.
	cmp		ebx, esi
	jb		smallDiv

longDiv:
	Divide64

	; Convert the remainder to an ASCII char.
	add		edx, 30h
	dec		esp
	.IF	edx > 39h
		add		edx, 7
	.ENDIF

	; Store char on stack.
	inc		ebp
	; While result is not 0, we loop.
	test	eax, eax
	mov		ecx, eax
	mov		[esp], dl
	jz		lowDWORDIsZero

	cmp		ebx, esi
	jae		longDiv

smallDiv:
	; Set EBX::ECX to EDX::EAX for a normal 64->32 division.
	mov		edx, ebx
	mov		eax, ecx

radixLoopSmall:
	div		esi

	; Convert the remainder to an ASCII char.
	add		edx, 30h
	dec		esp
	.IF	edx > 39h
		add		edx, 7
	.ENDIF

	; Store char on stack.
	inc		ebp
	mov		[esp], dl
	; Clean out high dword for next division.
	xor		edx, edx
	; While result is not 0, we loop.
	test	eax, eax
	jnz		radixLoopSmall

toBuffer:
	mov		eax, ebp	; Return stringlength (not including 0-terminator)

toBufferLoop:
	; Copy the string from stack to the destination buffer.
	inc		edi
	mov		dl, [esp]
	inc		esp
	dec		ebp
	mov		[edi-1], dl
	jnz		toBufferLoop

	; Zero terminate the string.
	mov		byte ptr [edi], 0

	ret

lowDWORDIsZero:
	test	ebx, ebx
	jnz		longDiv

	; We have the final string, time to copy it to the destination buffer.
	jmp		toBuffer
PrintRadixUnsigned	ENDP

PrintRadixSigned	PROC
; Input:	64 bit signed number in EBX::ECX, radix in ESI, pointer to output buffer in EDI
; Output:	Zero-terminated ASCII string in output buffer, length of string in EAX
; Uses:		EAX, EBX, ECX, EDX, ESI, EDI, EBP

	; If number is non-negative, use the normal PrintRadix
	test	ebx, ebx
	jns		PrintRadixUnsigned

	; Prefix the number with a - sign
	mov		byte ptr [edi], '-'
	inc		edi

	; Negate the 64 bit number
	neg		ebx
	neg		ecx
	sbb		ebx, 0

	; Do a normal PrintRadix
	call	PrintRadixUnsigned
	inc		eax
	ret
PrintRadixSigned	ENDP