; (C) 1993 Peter Thompson
; Base [2..36] to int32 conversion routines.
; TODO
; Back up on str ptr.

	INCLUDE	"intdefs.i"
	INCLUDE	"to32lib.i"

	IFD	ASM_ENTRY
	INCLUDE	"intconv32.i"
	ENDIF

	IFD	STKARGS
	PUBLIC	_strtou32
_strtou32:				;C stkargs entry
	move.l	4(sp),a0		;C char *
	move.l	8(sp),a1		;C char **
	move.l	12(sp),d1		;C int
	ENDIF

	IFD	REGARGS
	PUBLIC	@strtou32
@strtou32:				;C regargs entry
	ENDIF

str2u32:
	push	a1/Digit/Base/Errno/a6
	lea	Tou32(PC),a1
	bra	tostr

	IFD	STKARGS
	PUBLIC	_strtoi32
_strtoi32:				;C stkargs entry
	move.l	4(sp),a0		;C char *
	move.l	8(sp),a1		;C char **
	move.l	12(sp),d1		;C int
	ENDIF

	IFD	REGARGS
	PUBLIC	@strtoi32
@strtoi32:				;C regargs entry
	ENDIF
str2i32:
	push	a1/Digit/Base/Errno/a6
	lea	Toi32(PC),a1
tostr:
	move.l	d1,Base
	lea	su32(PC),a6
	jsr	0(a1)
	move.l	Errno,d1
	pop	a1/Digit/Base/Errno/a6
	cmp.l	#0,a1		; take care of char **
	beq	zz
	move.l	a0,(a1)
zz:	rts

	; su32: converts ASCII string in base [2..36] to an unsigned int32.
	; Requires:
	; - All sorts of stuff.. too much to list.
	; Provides:
	; - Conversion of a string of digits into binary form.
 
su32:	move.l	Base,d1
	bne	defd		; If base not predefined
	moveq	#10,Base	; base 10 is default
	cmp.b	#'0',Digit	; unless first digit is a 0
	bne	GotBase0
	moveq	#8,Base		; in which case it's base 8
	GetCh	Digit		; unless the next digit
	cmp.b	#'X',Digit	; is X
	beq	Base16
	cmp.b	#'x',Digit	; or x
	bne	GotBase
Base16:	moveq	#16,Base	; in which case it is base 16.
	GetCh	Digit		; and get the next digit.
GotBase:
	bsr	Digify
	move.l	Digit,d0
	cmp.l	Base,Digit
	bcs	su32a
	moveq	#0,d0		; number was just a zero digit
	lsr.b	#3,Base		; over by 1=8>>3 for octal, 2=16>>3 for hex.
	sub.l	Base,String	; adjust.
	rts

defd:	subq.l	#2,d1
	bcs	dflt
	cmp.l	#36+1,Base
	bcs	GotBase0
dflt:	moveq	#10,Base

GotBase0:
	bsr	Digify		; Get first digit in number.
	cmp.l	Base,Digit	;X Check at least one digit in number.
	bcc	Derr32		;X
	move.l	Digit,d0	; And initialise number to it.
su32a:
	GetCh	Digit		; get next char
	bsr	Digify		; convert to digit
	cmp.l	Base,Digit	; End it if non-digit
	bcc	su32x
	move.l	d0,d1		; multiply number by Base
	swap	d1		; lower 32 bits.
	mulu	Base,d0
	mulu	Base,d1
	swap	d1
	tst.w	d1		;V bits 32-49 must be 0.
	bne	Verr32		;V
	add.l	d1,d0		; add partial results.
	bcs	Verr32		;V
	add.l	Digit,d0	; Add digit.
	bcc	su32a		;V SPECIAL - continue if not ok.
Verr32:	move.l	#ERR_RANGE,Errno	;V Overflow
su32x:	UnGetCh			; put back character that ended fetch.
	rts

Derr32:	move.l	#ERR_NONUM,Errno	; No digits in number
	bra	su32x			; Put back cause of error.

Digify:	; Turn an alphanumeric ASCII character into a digit in [0..35]
	cmp.b	#'9'+1,Digit
	bcs	num
	and.b	#%11011111,Digit	; toupper(Digit)
	cmp.b	#'A',Digit
	bcs	GDerr
	sub.b	#'A'-'0'-10,Digit
num:	sub.b	#'0',Digit
	bcc	GotDigit
GDerr:	move.l	Base,Digit	; not a digit, so return illegal value.
GotDigit:
	rts

	END

