;************************************************************************ ;* * ;* 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