;************************************************************************
;*                                                                      *
;* Atari Mathbox emulator	                                        *
;*                                                                      *
;* Copyright 1997, Zonn Moore						*
;* Guest appearance by Neil Bradley                                     *
;* Version 1.1								*
;*                                                                      *
;* This code is free to distribute and use in unmodified form for your  *
;* own projects requiring the Atari Tempest, Battlezone, and Red Baron  *
;* mathbox hardware. This emulator is designed to give the best 	*
;* emulation performance on a PC as possible.             		*
;*                                                                      *
;* This code, and no part of this code, may not be used in any          *
;* commercial or for-profit venture without the express written         *
;* permission of Zonn Moore. Credit must be given within the program    *
;* that uses this emulator OR in accompanying documentation.            *
;*                                                                      *
;* Contact information:                                                 *
;*                                                                      *
;* Zonn Moore (zonn@concentric.net) -					*
;*      Architect, reverse engineering, design, and optimization	*
;*									*
;* Neil Bradley (neil@synthcom.com) -					*
;*      Assembler interface, entry points - making it pretty ;-)	*
;*                                                                      *
;************************************************************************
;*                                                                      *
;* Release history:                                                     *
;*                                                                      *
;* 11/21/97 - Release 1.1						*
;* 09/16/97 - Release 1.0						*
;*									*
;* Initial release							*
;*									*
;************************************************************************
;*									*
;* This emulator is designed to be used with NASM:			*
;*									*
;* nasm mathbox.asm							*
;*									*
;* is all that's required for mathbox assembly. If you are using	*
;* Microsoft's Visual C++ or other stack based calling conventions,	*
;* assemble with the following:						*
;*									*
;* nasm /DUSE_STACK mathbox.asm						*
;*									*
;************************************************************************

; For assembly by NASM only
bits 32
%define offset
%define ptr
; Using register calling conventions

		extern	_cyclesRemaining
		extern	_dwElapsedTicks

		section	.data

; Mathbox register storage

r00		dw	0
r01		dw	0
r02		dw	0
r03		dw	0
r04		dw	0
r05		dw	0
r06		dw	0
r07		dw	0
r08		dw	0
r09		dw	0
r10		dw	0
r11		dw	0
r12		dw	0
r13		dw	0
r14		dw	0
r15		dw	0
Qre		dw	0

; Don't change following order otherwise it will corrupt everything!

Mout		equ	$
MathboxLow	db	0
MathboxHigh	db	0

		section	.text

;************************************************************************
;									*
; Name	     : MathboxHigh_						*
; 									*
; Entry	     : Nothing							*
;									*
; Exit	     : EAX=Upper 8 bits of mathbox's result			*
;									*
; Registers  : EAX Only							*
;									*
; Description:								*
;									*
; This routine will return the upper 8 bits of the mathbox's result	*
; value (normally mapped as MBHI).					*
;									*
;************************************************************************

		global	MathboxHigh_
		global	_MathboxHigh

MathboxHigh_:
_MathboxHigh:
		xor	eax, eax	; Zero our result code
		mov	al, [MathboxHigh] ; High 8 bits of mathbox result code
		ret


;************************************************************************
;									*
; Name	     : MathboxLow_						*
; 									*
; Entry	     : Nothing							*
;									*
; Exit	     : EAX=Lower 8 bits of mathbox's result			*
;									*
; Registers  : EAX Only							*
;									*
; Description:								*
;									*
; This routine will return the lower 8 bits of the mathbox's result	*
; value (normally mapped as MBLO).					*
;									*
;************************************************************************

		global	MathboxLow_
		global	_MathboxLow

MathboxLow_:
_MathboxLow:
		xor	eax, eax	; Zero our result code
		mov	al, [MathboxLow] ; Low 8 bits of mathbox result code
		ret

; Jump table

mbJumpTbl:
		dd	L20
		dd	L21
		dd	L22
		dd	L23
		dd	L24
		dd	L25
		dd	L26
		dd	L27

		dd	L28
		dd	L29
		dd	L2A
		dd	L41
		dd	L2C
		dd	L34
		dd	L35
		dd	L36

		dd	L37
		dd	L42
		dd	L7E
		dd	LB8
		dd	LBD
		dd	L2E
		dd	L2F
		dd	L17

		dd	L19
		dd	L18
		dd	L30
		dd	L31
		dd	LD9
		dd	LEB
		dd	LF4
		dd	L00

		global	MathboxGo_
		global	_MathboxGo

MathboxGo_:
_MathboxGo:

%ifdef USE_STACK
		mov	eax, [esp+4]		; Get our data off the stack
%endif

		and	eax, 01fh		; 32 Addresses are valid
		jmp	dword ptr [mbJumpTbl+eax*4] ; Go do it!

; Selftest dummy return

L00:		ret			; Selftest never returns, so add this

; Read registers

L17:		mov	ax,[r07]		; read r07
		mov	[Mout],ax
		ret

L18:		mov	ax,[r08]		; read r08
		mov	[Mout],ax
		ret

L19:		mov	ax,[r09]		; read r09
		mov	[Mout],ax
		ret

; Set register, then read them to 'Mout'

L20:		mov	[r00],dl	; Set r00 Low
		mov	ax,[r00]
		mov	[Mout],ax
		ret

L21:		mov	[r00+1],dl	; Set r00 High
		mov	ax,[r00]
		mov	[Mout],ax
		ret

L22:		mov	[r01],dl	; Set r01 Low
		mov	ax,[r01]
		mov	[Mout],ax
		ret

L23:		mov	[r01+1],dl	; Set r01 High
		mov	ax,[r01]
		mov	[Mout],ax
		ret

L24:		mov	[r02],dl	; Set r02 Low
		mov	ax,[r02]
		mov	[Mout],ax
		ret

L25:		mov	[r02+1],dl	; Set r02 High
		mov	ax,[r02]
		mov	[Mout],ax
		ret

L26:		mov	[r03],dl	; Set r03 Low
		mov	ax,[r03]
		mov	[Mout],ax
		ret

L27:		mov	[r03+1],dl	; Set r03 High
		mov	ax,[r03]
		mov	[Mout],ax
		ret

L28:		mov	[r04],dl	; Set r04 Low
		mov	ax,[r04]
		mov	[Mout],ax
		ret

L29:		mov	[r04+1],dl	; Set r04 High
		mov	ax,[r04]
		mov	[Mout],ax
		ret

L2A:		mov	[r05],dl	; Set r05 Low
		mov	ax,[r05]
		mov	[Mout],ax
		ret

L2C:		mov	byte ptr [r06],dl	; Set r06 Low
		mov	byte ptr [r06+1],0	; Zero upper byte of r06
		mov	word ptr [Qre],-1		; set to all ones
		mov	ax,[r06]
		mov	[Mout],ax
		ret

L2E:		mov	[r07],dl	; Set r07 Low
		mov	ax,[r07]
		mov	[Mout],ax
		ret

L2F:		mov	[r07+1],dl	; Set r07 High
		mov	ax,[r07]
		mov	[Mout],ax
		ret

L30:		mov	[r08],dl	; Set r08 Low
		mov	ax,[r08]
		mov	[Mout],ax
		ret

L31:		mov	[r08+1],dl	; Set r08 High
		mov	ax,[r08]
		mov	[Mout],ax
		ret

L34:		mov	[r10],dl	; Set r10 Low
		mov	ax,[r10]
		mov	[Mout],ax
		ret

L35:		mov	[r10+1],dl	; Set r10 High
		mov	ax,[r10]
		mov	[Mout],ax
		ret

L36:		mov	[r11],dl	; Set r11 Low
		mov	ax,[r11]
		mov	[Mout],ax
		ret

L37:		mov	[r11+1],dl	; Set r11 High
		mov	ax,[r11]
		mov	[Mout],ax
		ret

; Start of Mathbox guts...

; *** Entry point
;
; Register on entry:
;    r05L, r00, r01, r02, r03, r04, r05, r06
;
; Returns:
;    

L42:		mov	[r05+1],dl	; set high byte
		mov	word ptr [r15],0			; clear r15
		jmp	Short L48

; *** Entry point
;
; Register on entry:
;    r00, r01, r02, r03, r04, r05L
;
; Modifies:
;    r04, r05, r07, r08, r09

L41:		mov	[r05+1],dl	; set high byte
		mov	word ptr [r15],-1		; set to all ones
		mov	ax,[r02]		; r04 = r04 - r02
		sub	[r04],ax
		mov	ax,[r03]		; r05 = r05 - r03
		sub	[r05],ax

L48:		mov	ax,[r00]		; r00 * r04
		imul	word ptr [r04]
		mov	[r12],dx		; upperword
		mov	[r14],ax		; lowerword

		mov	ax,[r01]		; -r01 * r05
		neg	ax
		imul	word ptr [r05]
		add	dx,[r12]
		mov	[r07],dx		; upperword
		and	al,0FEh			; bottom bit doesn't count
		add	ax,[r14]		; add lowerwords
		adc	word ptr [r07],0			; add carry
		cmp	word ptr [r15],0
		je	L7D
		mov	ax,[r07]
		mov	[Mout],ax
		ret

L7D:		mov	ax,[r02]		; r07 = r07 + r02
		add	[r07],ax

; *** Entry point

L7E:		mov	ax,[r01]		; r01 * r04
		imul	word ptr [r04]
		mov	[r12],dx		; upperword
		mov	[r09],ax		; lowerword

		mov	ax,[r00]		; r00 * r05
		imul	word ptr [r05]
		add	dx,[r12]
		mov	[r08],dx		; upperword + r12
		shr	word ptr [r09],1
		shr	ax,1
		add	[r09],ax
		shl	word ptr [r09],1			; check for carry
		adc	word ptr [r08],0			; add carry
		cmp	word ptr [r15],0
		je	LB5
		mov	ax,[r08]		; Mout = r08, HLT
		mov	[Mout],ax
		ret

LB5:		mov	ax,[r03]		; r08 = r08 + r03
		add	[r08],ax
		mov	word ptr [r09],0	; r09 = r09 & 0xFF00

; *** Entry point

LB8:		push	bx
		push	cx
		mov	bx,[r09]		; r12 = r09
		mov	cx,[r08]		; Qre = r08
		jmp	Short LBF

; *** Entry point

LBD:		push	bx
		push	cx
		mov	bx,[r10]		; r12 = r10
		mov	cx,[r11]		; Qre = r11

; Determine the resulting sign and save it as the hi bit of r14.
		
LBF:		mov	ax,cx			; r14 = r07 ^ Qre
		xor	ax,[r07]
		mov	[r14],ax		; save the resulting sign

; At this point we should be finding the absolute value of the long word
; that's being divided by r07, but because of limitations of the Mathbox
; hardware, a perfect absolute value cannot be done.
;
; This algorithm emulates the same limitations:
;
; cx:bx = abs( Qre:r12)

		test	cx,cx			; negative?
		jns	LC8			; no, continue
		not	cx			; invert upper word
		neg	bx
		sub	bx,1			; simulate the MB NOT inst.
		jge	LC7			; jump on MB overflow
		add	bx,1			; do a negate
		jge	LC8			; jump on MB overflow flags
		inc	cx			; adjust upper word
		dec	bx			; instead of jumping around
LC7:		inc	bx			; adjust incase of skip

; ax = abs( r07)

LC8:		mov	ax,[r07]		; get divisor
		cwd				; dx = FFFF if r07 negative
		xor	ax,dx			; invert r07 if negative
		sub	ax,dx			; add 1 to r07 if negative

		mov	dx,[r06]		; get shift counter
		
; Divide:  bx = cx:bx / ax

		cmp	dx,16			; proper loop count?
		jne	LCD			; act like mathbox

		cmp	cx,ax			; overflow?
		ja	LCD			; act like mathbox
		je	testDiv1		; if eq, more testing needed

		; use DIV instruction

		xchg	bx,ax			; AX=Low, BX=r07
		mov	dx,cx			; DX=High
		div	bx			; do divide
		mov	bx,ax			; get results in BX

		; adjust sign of results

		mov	ax,[r14]		; get sign
		cwd
		xor	bx,dx			; adjust results
		sub	bx,dx
		mov	[Mout],bx
		pop	cx
		pop	bx
		ret

testDiv1:	test	bx,bx			; overflow?
		jnz	LCD			; yes, act like mathbox
		test	ax,ax			; divide by 0?
		jz	LCD			; yes, act like mathbox
		mov	word ptr [Mout],0		; else, always 0
		pop	cx
		pop	bx
		ret

; Division loop

LCD:		sub	cx,ax
		jns	LD2			; if no sign, shift in a 1
		add	cx,ax			; else, fix subtract
		shl	bx,1			; shift in a zero
		rcl	cx,1
		dec	dx
		jns	LCD			; no, loop

		; adjust sign of results

		mov	ax,[r14]		; get sign
		cwd
		xor	bx,dx			; adjust results
		sub	bx,dx
		mov	[Mout],bx
		pop	cx
		pop	bx
		ret

LD2:		shl	bx,1			; shift left
		rcl	cx,1
		inc	bx			; shift in a 1
		dec	dx			; done?
		jns	LCD			; no, loop

		; adjust sign of results

		mov	ax,[r14]		; get sign
		cwd
		xor	bx,dx			; adjust results
		sub	bx,dx
		mov	[Mout],bx
		pop	cx
		pop	bx
		ret
		
; *** Entry point ***

LD9:		push	bx
		mov	[r05+1],dl	; Set high byte of r05

; AX = r14
; BX = r15

LDA:		mov	ax,[r04]		; r14 = (r04 + r07) / 2
		add	ax,[r07]
		setl	dh			; do mathbox OSR instruction
		shr	dh,1
		rcr	ax,1

		mov	bx,[r05]		; r15 = (r05 + r08) / 2
		add	bx,[r08]
		setl	dh			; do mathbox OSR instruction
		shr	dh,1
		rcr	bx,1

		cmp	[r11],ax		; if r11 >= r14, jmp LE8
		jge	LE8

		cmp	bx,ax			; if r15 >= r14, jmp LE8
		jge	LE8

		mov	dx,ax			; if -(r14 + r15) >= 0, jmp LE8
		add	dx,bx
		neg	dx
		jge	LE8

		mov	[r07],ax		; r07 = r14
		mov	[r08],bx		; r08 = r15
		sub	word ptr [r06],1			; r06 = r06 - 1, >=0, jmp LDA
		jge	LDA

		mov	ax,[r08]		; Mout = r08, HLT
		mov	[Mout],ax
		pop	bx
		ret

LE8:		mov	[r04],ax		; r04 = r14
		mov	[r05],bx		; r05 = r15
		sub	word ptr [r06],1			; r06 = r06 - 1, >=0, jmp LDA
		jl	notLess
		jmp	LDA

notLess:
		mov	ax,[r08]		; Mout = r08, HLT
		mov	[Mout],ax
		pop	bx
		ret

; *** Entry point ***

LEB:		mov	[r03+1],dl	; Set high byte of r03
		mov	ax,[r02]		; r02 = abs( r02 - r00)
		sub	ax,[r00]
		cwd				; dx = 0 or FFFF
		xor	ax,dx			; if dx == FFFF, invert
		sub	ax,dx			; if dx == FFFF, add 1
		mov	[r02],ax

		mov	ax,[r03]		; r03 = abs( r03 - r01)
		sub	ax,[r01]
		cwd				; dx = 0 or FFFF
		xor	ax,dx			; if dx == FFFF, invert
		sub	ax,dx			; if dx == FFFF, add 1
		mov	[r03],ax

; *** Entry point ***

LF4:		mov	ax,[r02]		; r12 = r02
		mov	dx,[r03]		; r13 = r03
		cmp	ax,dx			; if r02 <= r03, skip
		jbe	LFA	
		xchg	ax,dx			; else swap r12 and r13
LFA:		shr	ax,2			; r12 >>= 2
		add	dx,ax			; r13 += r12
		shr	ax,1			; r12 >>= 1
		add	dx,ax			; r13 += r12
		mov	[Mout],dx		; exit with r13
		ret
		
		end
