executor/src/vbl.c

236 lines
5.6 KiB
C

/* Copyright 1989, 1990, 1995 by Abacus Research and
* Development, Inc. All rights reserved.
*/
#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_vbl[] =
"$Id: vbl.c 63 2004-12-24 18:19:43Z ctm $";
#endif
/* Forward declarations in VRetraceMgr.h (DO NOT DELETE THIS LINE) */
#include "rsys/common.h"
#include "OSUtil.h"
#include "TimeMgr.h"
#include "VRetraceMgr.h"
#include "DeviceMgr.h"
#include "SoundDvr.h"
#include "OSEvent.h"
#include "ToolboxEvent.h"
#include "rsys/pstuff.h"
#include "rsys/vbl.h"
#include "rsys/time.h"
#include "rsys/hook.h"
#include "rsys/soundopts.h"
#include "rsys/prefs.h"
typedef enum
{
CLOCKOFF = 0,
CLOCKTEMPON,
CLOCKON,
CLOCKTEMPONSOUND,
CLOCKONSOUND,
}
clockstatus_t;
PUBLIC int ROMlib_clock = CLOCKOFF;
/*
* ROMlib_vcatch is called by the time manager, hence it doesn't need to
* block signals
*/
/*
* NOTE: we put a shim in the VBL queue, because Prince of Persia
* has a bug in it that overlooks the first element in the queue
* when it's looking for itself! We put in a magic ptr value we
* can recognize in case some bozo actually ever tries to call it.
* Note, however, that VInstall fills this in with a pointer to
* a m68k RTS instruction, just to be safe.
*/
VBLTask vblshim = { 0, CWC (vType), (ProcPtr) CLC (0xAEDCBA98), 0, 0 };
PRIVATE TMTask vbltm;
#define VBUSY (1 << 6)
A0 (PUBLIC, void, C_ROMlib_vcatch)
{
VBLTaskPtr vp, next;
ULONGINT old_ticks, new_ticks, ticks_elapsed;
LONGINT msecs_to_next_tick;
CCRElement saved_ccnz, saved_ccn, saved_ccc, saved_ccv, saved_ccx;
M68kReg saved_regs[16];
/* We save the 68k registers away. This isn't necessary if we can
* only be called from the time manager, since it has already saved
* all of the registers.
*/
memcpy (saved_regs, &cpu_state.regs, sizeof saved_regs);
saved_ccnz = cpu_state.ccnz;
saved_ccn = cpu_state.ccn;
saved_ccc = cpu_state.ccc;
saved_ccv = cpu_state.ccv;
saved_ccx = cpu_state.ccx;
EM_A7 = (EM_A7 - 32) & ~3; /* Might as well long-align it. */
/* Save the old Ticks value & compute new value. */
old_ticks = CL (Ticks);
new_ticks = C_TickCount ();
ticks_elapsed = new_ticks - old_ticks;
/* Compute the milliseconds until Ticks changes again.
* Here we rely on the fact that 1000 / 60 == 50 / 3, to avoid overflow.
*/
msecs_to_next_tick = (((new_ticks + 1) * 50 + 2) / 3) - msecs_elapsed ();
if (msecs_to_next_tick <= 0)
msecs_to_next_tick = 1;
PrimeTime ((QElemPtr) &vbltm, msecs_to_next_tick);
for (vp = (VBLTaskPtr) MR (VBLQueue.qHead); vp; vp = next)
{
INTEGER old_vbl_count, new_vbl_count;
next = (VBLTaskPtr) MR (vp->qLink);
/* Blow off the Prince of Persia shim. */
if (vp == &vblshim)
/*-->*/ continue;
/* Account for possible missed ticks by possibly subtracting
* off more than one tick from the VBL count. */
old_vbl_count = CW (vp->vblCount);
new_vbl_count = old_vbl_count - ticks_elapsed;
if (old_vbl_count > 0 && new_vbl_count < 0)
new_vbl_count = 0; /* Only compensate for zero crossings. */
vp->vblCount = CW (new_vbl_count);
if (new_vbl_count == 0)
{
VBLQueue.qFlags |= CWC (VBUSY);
ROMlib_hook (vbl_number);
/* No need to save/restore syn68k regs here; we do that
* outside the loop.
*/
EM_A0 = (LONGINT) (long) US_TO_SYN68K_CHECK0(vp);
EM_A1 = (LONGINT) CL ((long) vp->vblAddr);
CALL_EMULATOR ((syn68k_addr_t) EM_A1);
VBLQueue.qFlags &= CWC (~VBUSY);
if (vp->vblCount == CWC (0))
Dequeue ((QElemPtr) vp, &VBLQueue);
}
}
memcpy (&cpu_state.regs, saved_regs, sizeof saved_regs);
cpu_state.ccnz = saved_ccnz;
cpu_state.ccn = saved_ccn;
cpu_state.ccc = saved_ccc;
cpu_state.ccv = saved_ccv;
cpu_state.ccx = saved_ccx;
}
PRIVATE void
startclock (void)
{
vbltm.tmAddr = RM ((ProcPtr) P_ROMlib_vcatch);
InsTime ((QElemPtr) &vbltm);
PrimeTime ((QElemPtr) &vbltm, 17);
/* don't adjust ROMlib_clock; it could be temporary */
}
PRIVATE void
stopclock (void)
{
if (!VBLQueue.qTail)
{
RmvTime ((QElemPtr) &vbltm);
ROMlib_clock = CLOCKOFF;
}
else
ROMlib_clock = CLOCKTEMPON;
}
A1 (PUBLIC, void, ROMlib_clockonoff, LONGINT, onoroff)
{
if (onoroff)
{
if (ROMlib_clock == CLOCKOFF)
startclock ();
ROMlib_clock = CLOCKON;
}
else
stopclock ();
}
A2 (PUBLIC trap, OSErrRET, SlotVInstall,
VBLTaskPtr, vtaskp,
INTEGER, slot)
{
return VInstall (vtaskp);
}
A1 (PUBLIC trap, OSErrRET, VInstall, VBLTaskPtr, vtaskp)
{
static unsigned short m68k_rts = CWC (0x4E75); /* RTS */
vtaskp->vblCount = CW (CW (vtaskp->vblCount) + CW (vtaskp->vblPhase));
if (vtaskp->qType == CWC ((INTEGER) vType))
{
if (!VBLQueue.qHead)
{
vblshim.vblAddr = (ProcPtr) RM (&m68k_rts); /* legal 68k code. */
Enqueue ((QElemPtr) &vblshim, &VBLQueue);
}
Enqueue ((QElemPtr) vtaskp, &VBLQueue);
if (ROMlib_clock == CLOCKOFF)
{
ROMlib_clock = CLOCKTEMPON;
startclock ();
}
/*-->*/ return noErr;
}
else
return vTypErr;
}
A2 (PUBLIC trap, OSErrRET, SlotVRemove,
VBLTaskPtr, vtaskp,
INTEGER, slot)
{
return VRemove (vtaskp);
}
A1 (PUBLIC trap, OSErrRET, VRemove, VBLTaskPtr, vtaskp)
{
if (vtaskp->qType == CWC ((INTEGER) vType))
{
Dequeue ((QElemPtr) vtaskp, &VBLQueue);
if ((VBLTaskPtr) MR (VBLQueue.qHead) == &vblshim
&& (VBLTaskPtr) MR (VBLQueue.qTail) == &vblshim)
{
Dequeue ((QElemPtr) &vblshim, &VBLQueue);
if (ROMlib_clock == CLOCKTEMPON)
stopclock ();
}
/*-->*/ return noErr;
}
else
return vTypErr;
}
A0 (PUBLIC, QHdrPtr, GetVBLQHdr)
{
return &VBLQueue;
}