mirror of
https://github.com/zydeco/minivmac4ios.git
synced 2024-11-22 19:31:47 +00:00
562 lines
9.8 KiB
C
Executable File
562 lines
9.8 KiB
C
Executable File
/*
|
|
PROGMAIN.c
|
|
|
|
Copyright (C) 2009 Bernd Schmidt, Philip Cummins, Paul C. Pratt
|
|
|
|
You can redistribute this file and/or modify it under the terms
|
|
of version 2 of the GNU General Public License as published by
|
|
the Free Software Foundation. You should have received a copy
|
|
of the license along with this file; see the file COPYING.
|
|
|
|
This file is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
license for more details.
|
|
*/
|
|
|
|
/*
|
|
PROGram MAIN.
|
|
*/
|
|
|
|
#ifndef AllFiles
|
|
#include "SYSDEPNS.h"
|
|
|
|
#include "MYOSGLUE.h"
|
|
#include "EMCONFIG.h"
|
|
#include "GLOBGLUE.h"
|
|
#include "M68KITAB.h"
|
|
#include "MINEM68K.h"
|
|
#include "VIAEMDEV.h"
|
|
#if EmVIA2
|
|
#include "VIA2EMDV.h"
|
|
#endif
|
|
#include "IWMEMDEV.h"
|
|
#include "SCCEMDEV.h"
|
|
#if EmRTC
|
|
#include "RTCEMDEV.h"
|
|
#endif
|
|
#include "ROMEMDEV.h"
|
|
#include "SCSIEMDV.h"
|
|
#include "SONYEMDV.h"
|
|
#include "SCRNEMDV.h"
|
|
#if EmVidCard
|
|
#include "VIDEMDEV.h"
|
|
#endif
|
|
#if EmClassicKbrd
|
|
#include "KBRDEMDV.h"
|
|
#elif EmPMU
|
|
#include "PMUEMDEV.h"
|
|
#else
|
|
#include "ADBEMDEV.h"
|
|
#endif
|
|
#if EmASC
|
|
#include "ASCEMDEV.h"
|
|
#else
|
|
#if MySoundEnabled && (CurEmMd != kEmMd_PB100)
|
|
#include "SNDEMDEV.h"
|
|
#endif
|
|
#endif
|
|
#include "MOUSEMDV.h"
|
|
#endif
|
|
|
|
|
|
#include "PROGMAIN.h"
|
|
|
|
LOCALPROC EmulatedHardwareZap(void)
|
|
{
|
|
Memory_Reset();
|
|
ICT_Zap();
|
|
IWM_Reset();
|
|
SCC_Reset();
|
|
SCSI_Reset();
|
|
VIA1_Zap();
|
|
#if EmVIA2
|
|
VIA2_Zap();
|
|
#endif
|
|
Sony_Reset();
|
|
Extn_Reset();
|
|
m68k_reset();
|
|
}
|
|
|
|
LOCALPROC DoMacReset(void)
|
|
{
|
|
Sony_EjectAllDisks();
|
|
EmulatedHardwareZap();
|
|
}
|
|
|
|
LOCALPROC InterruptReset_Update(void)
|
|
{
|
|
SetInterruptButton(falseblnr);
|
|
/*
|
|
in case has been set. so only stays set
|
|
for 60th of a second.
|
|
*/
|
|
|
|
if (WantMacInterrupt) {
|
|
SetInterruptButton(trueblnr);
|
|
WantMacInterrupt = falseblnr;
|
|
}
|
|
if (WantMacReset) {
|
|
DoMacReset();
|
|
WantMacReset = falseblnr;
|
|
}
|
|
}
|
|
|
|
LOCALPROC SubTickNotify(int SubTick)
|
|
{
|
|
#if 0
|
|
dbglog_writeCStr("ending sub tick ");
|
|
dbglog_writeNum(SubTick);
|
|
dbglog_writeReturn();
|
|
#endif
|
|
#if MySoundEnabled && (CurEmMd != kEmMd_PB100)
|
|
MacSound_SubTick(SubTick);
|
|
#else
|
|
UnusedParam(SubTick);
|
|
#endif
|
|
}
|
|
|
|
#define CyclesScaledPerTick (130240UL * kMyClockMult * kCycleScale)
|
|
#define CyclesScaledPerSubTick (CyclesScaledPerTick / kNumSubTicks)
|
|
|
|
LOCALVAR ui4r SubTickCounter;
|
|
|
|
LOCALPROC SubTickTaskDo(void)
|
|
{
|
|
SubTickNotify(SubTickCounter);
|
|
++SubTickCounter;
|
|
if (SubTickCounter < (kNumSubTicks - 1)) {
|
|
/*
|
|
final SubTick handled by SubTickTaskEnd,
|
|
since CyclesScaledPerSubTick * kNumSubTicks
|
|
might not equal CyclesScaledPerTick.
|
|
*/
|
|
|
|
ICT_add(kICT_SubTick, CyclesScaledPerSubTick);
|
|
}
|
|
}
|
|
|
|
LOCALPROC SubTickTaskStart(void)
|
|
{
|
|
SubTickCounter = 0;
|
|
ICT_add(kICT_SubTick, CyclesScaledPerSubTick);
|
|
}
|
|
|
|
LOCALPROC SubTickTaskEnd(void)
|
|
{
|
|
SubTickNotify(kNumSubTicks - 1);
|
|
}
|
|
|
|
LOCALPROC SixtiethSecondNotify(void)
|
|
{
|
|
#if dbglog_HAVE && 0
|
|
dbglog_StartLine();
|
|
dbglog_writeCStr("begin new Sixtieth");
|
|
dbglog_writeReturn();
|
|
#endif
|
|
Mouse_Update();
|
|
InterruptReset_Update();
|
|
#if EmClassicKbrd
|
|
KeyBoard_Update();
|
|
#endif
|
|
#if EmADB
|
|
ADB_Update();
|
|
#endif
|
|
|
|
Sixtieth_PulseNtfy(); /* Vertical Blanking Interrupt */
|
|
Sony_Update();
|
|
|
|
#if EmLocalTalk
|
|
LocalTalkTick();
|
|
#endif
|
|
#if EmRTC
|
|
RTC_Interrupt();
|
|
#endif
|
|
#if EmVidCard
|
|
Vid_Update();
|
|
#endif
|
|
#if EmASC
|
|
ASC_Update();
|
|
#endif
|
|
|
|
SubTickTaskStart();
|
|
}
|
|
|
|
LOCALPROC SixtiethEndNotify(void)
|
|
{
|
|
SubTickTaskEnd();
|
|
Mouse_EndTickNotify();
|
|
Screen_EndTickNotify();
|
|
#if dbglog_HAVE && 0
|
|
dbglog_StartLine();
|
|
dbglog_writeCStr("end Sixtieth");
|
|
dbglog_writeReturn();
|
|
#endif
|
|
}
|
|
|
|
LOCALPROC ExtraTimeBeginNotify(void)
|
|
{
|
|
#if 0
|
|
dbglog_writeCStr("begin extra time");
|
|
dbglog_writeReturn();
|
|
#endif
|
|
VIA1_ExtraTimeBegin();
|
|
#if EmVIA2
|
|
VIA2_ExtraTimeBegin();
|
|
#endif
|
|
}
|
|
|
|
LOCALPROC ExtraTimeEndNotify(void)
|
|
{
|
|
VIA1_ExtraTimeEnd();
|
|
#if EmVIA2
|
|
VIA2_ExtraTimeEnd();
|
|
#endif
|
|
#if 0
|
|
dbglog_writeCStr("end extra time");
|
|
dbglog_writeReturn();
|
|
#endif
|
|
}
|
|
|
|
GLOBALPROC EmulationReserveAlloc(void)
|
|
{
|
|
ReserveAllocOneBlock(&RAM,
|
|
kRAM_Size + RAMSafetyMarginFudge, 5, falseblnr);
|
|
#if EmVidCard
|
|
ReserveAllocOneBlock(&VidROM, kVidROM_Size, 5, falseblnr);
|
|
#endif
|
|
#if IncludeVidMem
|
|
ReserveAllocOneBlock(&VidMem,
|
|
kVidMemRAM_Size + RAMSafetyMarginFudge, 5, trueblnr);
|
|
#endif
|
|
#if SmallGlobals
|
|
MINEM68K_ReserveAlloc();
|
|
#endif
|
|
}
|
|
|
|
LOCALFUNC blnr InitEmulation(void)
|
|
{
|
|
#if EmRTC
|
|
if (RTC_Init())
|
|
#endif
|
|
if (ROM_Init())
|
|
#if EmVidCard
|
|
if (Vid_Init())
|
|
#endif
|
|
if (AddrSpac_Init())
|
|
{
|
|
EmulatedHardwareZap();
|
|
return trueblnr;
|
|
}
|
|
return falseblnr;
|
|
}
|
|
|
|
LOCALPROC ICT_DoTask(int taskid)
|
|
{
|
|
switch (taskid) {
|
|
case kICT_SubTick:
|
|
SubTickTaskDo();
|
|
break;
|
|
#if EmClassicKbrd
|
|
case kICT_Kybd_ReceiveEndCommand:
|
|
DoKybd_ReceiveEndCommand();
|
|
break;
|
|
case kICT_Kybd_ReceiveCommand:
|
|
DoKybd_ReceiveCommand();
|
|
break;
|
|
#endif
|
|
#if EmADB
|
|
case kICT_ADB_NewState:
|
|
ADB_DoNewState();
|
|
break;
|
|
#endif
|
|
#if EmPMU
|
|
case kICT_PMU_Task:
|
|
PMU_DoTask();
|
|
break;
|
|
#endif
|
|
case kICT_VIA1_Timer1Check:
|
|
VIA1_DoTimer1Check();
|
|
break;
|
|
case kICT_VIA1_Timer2Check:
|
|
VIA1_DoTimer2Check();
|
|
break;
|
|
#if EmVIA2
|
|
case kICT_VIA2_Timer1Check:
|
|
VIA2_DoTimer1Check();
|
|
break;
|
|
case kICT_VIA2_Timer2Check:
|
|
VIA2_DoTimer2Check();
|
|
break;
|
|
#endif
|
|
default:
|
|
ReportAbnormal("unknown taskid in ICT_DoTask");
|
|
break;
|
|
}
|
|
}
|
|
|
|
LOCALPROC ICT_DoCurrentTasks(void)
|
|
{
|
|
int i = 0;
|
|
uimr m = ICTactive;
|
|
|
|
while (0 != m) {
|
|
if (0 != (m & 1)) {
|
|
if (i >= kNumICTs) {
|
|
/* shouldn't happen */
|
|
ICTactive &= ((1 << kNumICTs) - 1);
|
|
m = 0;
|
|
} else if (ICTwhen[i] == NextiCount) {
|
|
ICTactive &= ~ (1 << i);
|
|
#ifdef _VIA_Debug
|
|
fprintf(stderr, "doing task %d, %d\n", NextiCount, i);
|
|
#endif
|
|
ICT_DoTask(i);
|
|
|
|
/*
|
|
A Task may set the time of
|
|
any task, including itself.
|
|
But it cannot set any task
|
|
to execute immediately, so
|
|
one pass is sufficient.
|
|
*/
|
|
}
|
|
}
|
|
++i;
|
|
m >>= 1;
|
|
}
|
|
}
|
|
|
|
LOCALFUNC ui5b ICT_DoGetNext(ui5b maxn)
|
|
{
|
|
int i = 0;
|
|
uimr m = ICTactive;
|
|
ui5b v = maxn;
|
|
|
|
while (0 != m) {
|
|
if (0 != (m & 1)) {
|
|
if (i >= kNumICTs) {
|
|
/* shouldn't happen */
|
|
m = 0;
|
|
} else {
|
|
ui5b d = ICTwhen[i] - NextiCount;
|
|
/* at this point d must be > 0 */
|
|
if (d < v) {
|
|
#ifdef _VIA_Debug
|
|
fprintf(stderr, "coming task %d, %d, %d\n",
|
|
NextiCount, i, d);
|
|
#endif
|
|
v = d;
|
|
}
|
|
}
|
|
}
|
|
++i;
|
|
m >>= 1;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
LOCALPROC m68k_go_nCycles_1(ui5b n)
|
|
{
|
|
ui5b n2;
|
|
ui5b StopiCount = NextiCount + n;
|
|
do {
|
|
ICT_DoCurrentTasks();
|
|
n2 = ICT_DoGetNext(n);
|
|
#if dbglog_HAVE && 0
|
|
dbglog_StartLine();
|
|
dbglog_writeCStr("before m68k_go_nCycles, NextiCount:");
|
|
dbglog_writeHex(NextiCount);
|
|
dbglog_writeCStr(", n2:");
|
|
dbglog_writeHex(n2);
|
|
dbglog_writeCStr(", n:");
|
|
dbglog_writeHex(n);
|
|
dbglog_writeReturn();
|
|
#endif
|
|
NextiCount += n2;
|
|
m68k_go_nCycles(n2);
|
|
n = StopiCount - NextiCount;
|
|
} while (n != 0);
|
|
}
|
|
|
|
LOCALVAR ui5b ExtraSubTicksToDo = 0;
|
|
|
|
LOCALPROC DoEmulateOneTick(void)
|
|
{
|
|
#if EnableAutoSlow
|
|
{
|
|
ui5r NewQuietTime = QuietTime + 1;
|
|
|
|
if (NewQuietTime > QuietTime) {
|
|
/* if not overflow */
|
|
QuietTime = NewQuietTime;
|
|
}
|
|
}
|
|
#endif
|
|
#if EnableAutoSlow
|
|
{
|
|
ui5r NewQuietSubTicks = QuietSubTicks + kNumSubTicks;
|
|
|
|
if (NewQuietSubTicks > QuietSubTicks) {
|
|
/* if not overflow */
|
|
QuietSubTicks = NewQuietSubTicks;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SixtiethSecondNotify();
|
|
|
|
m68k_go_nCycles_1(CyclesScaledPerTick);
|
|
|
|
SixtiethEndNotify();
|
|
|
|
if ((ui3b) -1 == SpeedValue) {
|
|
ExtraSubTicksToDo = (ui5b) -1;
|
|
} else {
|
|
ui5b ExtraAdd = (kNumSubTicks << SpeedValue) - kNumSubTicks;
|
|
ui5b ExtraLimit = ExtraAdd << 3;
|
|
|
|
ExtraSubTicksToDo += ExtraAdd;
|
|
if (ExtraSubTicksToDo > ExtraLimit) {
|
|
ExtraSubTicksToDo = ExtraLimit;
|
|
}
|
|
}
|
|
}
|
|
|
|
LOCALFUNC blnr MoreSubTicksToDo(void)
|
|
{
|
|
blnr v = falseblnr;
|
|
|
|
if (ExtraTimeNotOver() && (ExtraSubTicksToDo > 0)) {
|
|
#if EnableAutoSlow
|
|
if ((QuietSubTicks >= 16384)
|
|
&& (QuietTime >= 34)
|
|
&& ! WantNotAutoSlow)
|
|
{
|
|
ExtraSubTicksToDo = 0;
|
|
} else
|
|
#endif
|
|
{
|
|
v = trueblnr;
|
|
}
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
LOCALPROC DoEmulateExtraTime(void)
|
|
{
|
|
/*
|
|
DoEmulateExtraTime is used for
|
|
anything over emulation speed
|
|
of 1x. It periodically calls
|
|
ExtraTimeNotOver and stops
|
|
when this returns false (or it
|
|
is finished with emulating the
|
|
extra time).
|
|
*/
|
|
|
|
if (MoreSubTicksToDo()) {
|
|
ExtraTimeBeginNotify();
|
|
do {
|
|
#if EnableAutoSlow
|
|
{
|
|
ui5r NewQuietSubTicks = QuietSubTicks + 1;
|
|
|
|
if (NewQuietSubTicks > QuietSubTicks) {
|
|
/* if not overflow */
|
|
QuietSubTicks = NewQuietSubTicks;
|
|
}
|
|
}
|
|
#endif
|
|
m68k_go_nCycles_1(CyclesScaledPerSubTick);
|
|
--ExtraSubTicksToDo;
|
|
} while (MoreSubTicksToDo());
|
|
ExtraTimeEndNotify();
|
|
}
|
|
}
|
|
|
|
LOCALVAR ui5b CurEmulatedTime = 0;
|
|
/*
|
|
The number of ticks that have been
|
|
emulated so far.
|
|
|
|
That is, the number of times
|
|
"DoEmulateOneTick" has been called.
|
|
*/
|
|
|
|
LOCALPROC RunEmulatedTicksToTrueTime(void)
|
|
{
|
|
/*
|
|
The general idea is to call DoEmulateOneTick
|
|
once per tick.
|
|
|
|
But if emulation is lagging, we'll try to
|
|
catch up by calling DoEmulateOneTick multiple
|
|
times, unless we're too far behind, in
|
|
which case we forget it.
|
|
|
|
If emulating one tick takes longer than
|
|
a tick we don't want to sit here
|
|
forever. So the maximum number of calls
|
|
to DoEmulateOneTick is determined at
|
|
the beginning, rather than just
|
|
calling DoEmulateOneTick until
|
|
CurEmulatedTime >= TrueEmulatedTime.
|
|
*/
|
|
|
|
si3b n = OnTrueTime - CurEmulatedTime;
|
|
|
|
if (n > 0) {
|
|
DoEmulateOneTick();
|
|
++CurEmulatedTime;
|
|
|
|
DoneWithDrawingForTick();
|
|
|
|
if (n > 8) {
|
|
/* emulation not fast enough */
|
|
n = 8;
|
|
CurEmulatedTime = OnTrueTime - n;
|
|
}
|
|
|
|
if (ExtraTimeNotOver() && (--n > 0)) {
|
|
/* lagging, catch up */
|
|
|
|
EmVideoDisable = trueblnr;
|
|
|
|
do {
|
|
DoEmulateOneTick();
|
|
++CurEmulatedTime;
|
|
} while (ExtraTimeNotOver()
|
|
&& (--n > 0));
|
|
|
|
EmVideoDisable = falseblnr;
|
|
}
|
|
|
|
EmLagTime = n;
|
|
}
|
|
}
|
|
|
|
LOCALPROC MainEventLoop(void)
|
|
{
|
|
for (; ; ) {
|
|
WaitForNextTick();
|
|
if (ForceMacOff) {
|
|
return;
|
|
}
|
|
|
|
RunEmulatedTicksToTrueTime();
|
|
|
|
DoEmulateExtraTime();
|
|
}
|
|
}
|
|
|
|
GLOBALPROC ProgramMain(void)
|
|
{
|
|
if (InitEmulation())
|
|
{
|
|
MainEventLoop();
|
|
}
|
|
}
|