! Addition
! The fixnum case is done in line, so if the operands are fixnums, we had
! an overflow and must box the result in a bignum.

EXTNAME(m_generic_add):
	and	%RESULT, TAGMASK, %TMP0
	and	%ARGREG2, TAGMASK, %TMP1
	cmp	%TMP0, BVEC_TAG
	be,a	Ladd_bvec
	cmp	%TMP1, BVEC_TAG
	or	%RESULT, %ARGREG2, %TMP2
	andcc	%TMP2, 3, %g0
	be,a	Ladd_fix
	nop
	cmp	%TMP0, VEC_TAG
	be,a	Ladd_vec
	cmp	%TMP1, VEC_TAG
	b	_contagion
	mov	MS_GENERIC_ADD, %TMP2
Ladd_bvec:
	be,a	Ladd_bvec2
	ldub	[ %RESULT + 3 - BVEC_TAG ], %TMP0
	b	_contagion
	mov	MS_GENERIC_ADD, %TMP2
Ladd_bvec2:
	ldub	[ %ARGREG2 + 3 - BVEC_TAG ], %TMP1
	cmp	%TMP0, FLONUM_HDR
	be,a	Ladd_flo
	cmp	%TMP1, FLONUM_HDR
	cmp	%TMP0, COMPNUM_HDR
	be,a	Ladd_comp
	cmp	%TMP1, COMPNUM_HDR
	cmp	%TMP0, BIGNUM_HDR
	be,a	Ladd_big
	cmp	%TMP1, BIGNUM_HDR
	b	Lnumeric_error
	mov	EX_ADD, %TMP0
Ladd_flo:
	be,a	Ladd_flo2
	ldd	[ %RESULT + 8 - BVEC_TAG ], %f2
	! Got a flonum and something; check for compnum with 0i.
	cmp	%TMP1, COMPNUM_HDR
	bne	_contagion
	mov	MS_GENERIC_ADD, %TMP2
	ldd	[ %ARGREG2 - BVEC_TAG + 16 ], %f2
	fcmpd	%f0, %f2
	nop
	fbe,a	Ladd_flo2
	ldd	[ %RESULT + 8 - BVEC_TAG ], %f2
	b	_contagion
	mov	MS_GENERIC_ADD, %TMP2
Ladd_flo2:
	ldd	[ %ARGREG2 + 8 - BVEC_TAG ], %f4
	b	_box_flonum
	faddd	%f2, %f4, %f2
Ladd_comp:
	be,a	Ladd_comp2
	ldd	[ %RESULT + 8 - BVEC_TAG ], %f2
	! op1 is a compnum, but perhaps op2 is a flonum and op1 has 0i.
	cmp	%TMP1, FLONUM_HDR
	bne	_contagion
	mov	MS_GENERIC_ADD, %TMP2
	ldd	[ %RESULT - BVEC_TAG + 16 ], %f2
	fcmpd	%f0, %f2
	nop
	fbe,a	Ladd_flo2
	ldd	[ %RESULT + 8 - BVEC_TAG ], %f2
	b	_contagion
	mov	MS_GENERIC_ADD, %TMP2
Ladd_comp2:
	ldd	[ %ARGREG2 + 8 - BVEC_TAG ], %f4
	ldd	[ %RESULT + 16 - BVEC_TAG ], %f6
	faddd	%f2, %f4, %f2
	ldd	[ %ARGREG2 + 16 - BVEC_TAG ], %f8
	b	_box_compnum
	faddd	%f6, %f8, %f4
Ladd_big:
	be,a	Ladd_big2
	mov	2, %TMP1
	b	_contagion
	mov	MS_GENERIC_ADD, %TMP2
Ladd_big2:
	b	internal_scheme_call
	mov	MS_BIGNUM_ADD, %TMP2
Ladd_vec:
	be,a	Ladd_vec2
	ldub	[ %RESULT - VEC_TAG + 3 ], %TMP0
	b	_contagion
	mov	MS_GENERIC_ADD, %TMP2
Ladd_vec2:
	ldub	[ %RESULT - VEC_TAG + 3 ], %TMP1
	cmp	%TMP0, RATNUM_HDR
	be,a	Ladd_rat
	cmp	%TMP1, RATNUM_HDR
	cmp	%TMP0, RECTNUM_HDR
	be,a	Ladd_rect
	cmp	%TMP1, RECTNUM_HDR
	b	Lnumeric_error
	mov	EX_ADD, %TMP0
Ladd_rat:
	be,a	Ladd_rat2
	mov	2, %TMP1
	b	_contagion
	mov	MS_GENERIC_ADD, %TMP2
Ladd_rat2:
	b	internal_scheme_call
	mov	MS_RATNUM_ADD, %TMP2
Ladd_rect:
	be,a	Ladd_rect2
	mov	2, %TMP1
	b	_contagion
	mov	MS_GENERIC_ADD, %TMP2
Ladd_rect2:
	b	internal_scheme_call
	mov	MS_RECTNUM_ADD, %TMP2
Ladd_fix:
	sra	%RESULT, 2, %TMP0
	sra	%ARGREG2, 2, %TMP1
	b	_box_single_bignum
	add	%TMP0, %TMP1, %TMP0

! ...

! The following subroutines are common.
!
! Box the double in %f2/f3 as a flonum. Return tagged pointer in RESULT.
! Scheme return address in %o7.

_box_flonum:
	st	%o7, [ %GLOBALS + G_RETADDR ]
	call	EXTNAME(mem_internal_alloc)
	mov	16, %RESULT
	ld	[ %GLOBALS + G_RETADDR ], %o7
	std	%f2, [ %RESULT + 8 ]
	set	(12 << 8) | FLONUM_HDR, %TMP1
	st	%TMP1, [ %RESULT ]
	jmp	%o7 + 8
	add	%RESULT, BVEC_TAG, %RESULT


! Box the two doubles in %f2/%f3 and %f4/%f5 as a compnum.
! Return tagged pointer in RESULT.
! Scheme return address in %o7.

_box_compnum:
	st	%o7, [ %GLOBALS + G_RETADDR ]
	call	EXTNAME(mem_internal_alloc)
	mov	24, %RESULT
	ld	[ %GLOBALS + G_RETADDR ], %o7
	std	%f2, [ %RESULT + 8 ]
	std	%f4, [ %RESULT + 16 ]
	set	(20 << 8) | COMPNUM_HDR, %TMP0
	st	%TMP0, [ %RESULT ]
	jmp	%o7+8
	add	%RESULT, BVEC_TAG, %RESULT

! Box an integer in a bignum with one digit. The integer is passed in %TMP0.
! %o7 has the Scheme return address.

_box_single_bignum:
	cmp	%TMP0, 0
	bge,a	_box_single_positive_bignum
	mov	0, %TMP2
	mov	1, %TMP2
	neg	%TMP0

! Sign (0 or 1) is in %TMP2, untagged, positive number in %TMP0.

_box_single_positive_bignum:
	st	%TMP0, [ %GLOBALS + G_GENERIC_NRTMP1 ]
	st	%TMP2, [ %GLOBALS + G_GENERIC_NRTMP2 ]
	st	%o7, [ %GLOBALS + G_RETADDR ]
	call	EXTNAME(mem_internal_alloc)
	mov	12, %RESULT
	ld	[ %GLOBALS + G_RETADDR ], %o7
	ld	[ %GLOBALS + G_GENERIC_NRTMP1 ], %TMP0
	ld	[ %GLOBALS + G_GENERIC_NRTMP2 ], %TMP2
	sll	%TMP2, 16, %TMP2
	add	%TMP2, 1, %TMP1
	st	%TMP1, [ %RESULT + 4 ]		! store sign, length
	st	%TMP0, [ %RESULT + 8 ]		! store number
	set	(8 << 8) | BIGNUM_HDR, %TMP0
	st	%TMP0, [ %RESULT ]
	jmp	%o7+8
	or	%RESULT, BVEC_TAG, %RESULT