uvmac/src/PROGMAIN.c

704 lines
13 KiB
C
Raw Normal View History

2019-07-22 22:50:34 -04:00
/*
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.
*/
2020-03-03 15:13:19 -05:00
#include <string.h>
2020-03-03 13:31:11 -05:00
#include "SYSDEPNS.h"
#include "UI/MYOSGLUE.h"
#include "EMCONFIG.h"
#include "GLOBGLUE.h"
#include "HW/M68K/M68KITAB.h"
#include "HW/M68K/MINEM68K.h"
#include "HW/VIA/VIAEMDEV.h"
#include "HW/VIA/VIA2EMDV.h"
#include "HW/DISK/IWMEMDEV.h"
#include "HW/SCC/SCCEMDEV.h"
#include "HW/RTC/RTCEMDEV.h"
#include "PATCHES/ROMEMDEV.h"
#include "HW/SCSI/SCSIEMDV.h"
#include "HW/DISK/SONYEMDV.h"
#include "HW/SCREEN/SCRNEMDV.h"
#include "HW/VIDCARD/VIDEMDEV.h"
#include "HW/KBRD/KBRDEMDV.h"
#include "HW/POWERMAN/PMUEMDEV.h"
#include "HW/ADB/ADBEMDEV.h"
#include "HW/SOUND/ASCEMDEV.h"
#include "HW/SOUND/SNDEMDEV.h"
#include "HW/MOUSE/MOUSEMDV.h"
2019-07-22 22:50:34 -04:00
#include "PROGMAIN.h"
/*
ReportAbnormalID unused 0x1002 - 0x10FF
*/
2020-03-03 13:31:11 -05:00
const bool _EmVIA2 = false;
const bool _EmRTC = true;
const bool _EmVidCard = false;
const bool _EmClassicKbrd = true;
const bool _EmPMU = false;
const bool _EmMMU = false;
const bool _EmASC = false;
const bool _EmADB = false;
2020-03-03 15:13:19 -05:00
// Let's define a bunch of function structure thingies
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array))
typedef struct DevMethods {
bool (*init)(void);
void (*reset)(void);
void (*starttick)(void);
void (*endtick)(void);
void (*timebegin)(void);
void (*timeend)(void);
} DevMethods_t;
const DevMethods_t DEVICES[] = {
// RTC
{
.init = EmRTC ? RTC_Init : NULL,
.reset = NULL,
.starttick = NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// ROM
{
.init = ROM_Init,
.reset = NULL,
.starttick = NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// Memory
{
.init = AddrSpac_Init,
.reset = Memory_Reset,
.starttick = NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// ICT
{
.init = NULL,
.reset = ICT_Zap,
.starttick = NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// IWM
{
.init = NULL,
.reset = IWM_Reset,
.starttick = NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// SCC
{
.init = NULL,
.reset = SCC_Reset,
.starttick = NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// SCSI
{
.init = NULL,
.reset = SCSI_Reset,
.starttick = NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// VIA1
{
.init = NULL,
.reset = VIA1_Zap,
.starttick = NULL,
.endtick = NULL,
.timebegin = VIA1_ExtraTimeBegin,
.timeend = VIA1_ExtraTimeEnd,
},
// VIA2
{
.init = NULL,
.reset = EmVIA2 ? VIA2_Zap : NULL,
.starttick = NULL,
.endtick = NULL,
.timebegin = EmVIA2 ? VIA2_ExtraTimeBegin : NULL,
.timeend = EmVIA2 ? VIA2_ExtraTimeEnd : NULL,
},
// Sony disk drive
{
.init = NULL,
.reset = Sony_Reset,
.starttick = Sony_Update,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// Extn
{
.init = NULL,
.reset = Extn_Reset,
.starttick = NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// m68k
{
.init = NULL,
.reset = m68k_reset,
.starttick = NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// Mouse
{
.init = NULL,
.reset = NULL,
.starttick = Mouse_Update,
.endtick = Mouse_EndTickNotify,
.timebegin = NULL,
.timeend = NULL,
},
// Classic Keyboard
{
.init = NULL,
.reset = NULL,
.starttick = EmClassicKbrd ? KeyBoard_Update : NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// ADB
{
.init = NULL,
.reset = NULL,
.starttick = EmADB ? ADB_Update : NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// LocalTalk
/*{
.init = NULL,
.reset = NULL,
.starttick = EmLocalTalk ? LocalTalkTick : NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},*/
// Video card
{
.init = EmVidCard ? Vid_Init : NULL,
.reset = NULL,
.starttick = EmVidCard ? Vid_Update : NULL,
.endtick = NULL,
.timebegin = NULL,
.timeend = NULL,
},
// Screen
{
.init = NULL,
.reset = NULL,
.starttick = Sixtieth_PulseNtfy, // VBlank interrupt
.endtick = Screen_EndTickNotify,
.timebegin = NULL,
.timeend = NULL,
},
};
2019-07-22 22:50:34 -04:00
LOCALPROC EmulatedHardwareZap(void)
{
2020-03-03 15:13:19 -05:00
int i;
for ( i = 0; i < ARRAY_SIZE(DEVICES); i++ ) {
if (DEVICES[i].reset != NULL) { DEVICES[i].reset(); }
}
2019-07-22 22:50:34 -04:00
}
LOCALPROC DoMacReset(void)
{
Sony_EjectAllDisks();
EmulatedHardwareZap();
}
LOCALPROC InterruptReset_Update(void)
{
2020-02-11 00:34:32 -05:00
SetInterruptButton(false);
2019-07-22 22:50:34 -04:00
/*
in case has been set. so only stays set
for 60th of a second.
*/
if (WantMacInterrupt) {
2020-02-11 00:34:32 -05:00
SetInterruptButton(true);
WantMacInterrupt = false;
2019-07-22 22:50:34 -04:00
}
if (WantMacReset) {
DoMacReset();
2020-02-11 00:34:32 -05:00
WantMacReset = false;
2019-07-22 22:50:34 -04:00
}
}
LOCALPROC SubTickNotify(int SubTick)
{
#if 0
dbglog_writeCStr("ending sub tick ");
dbglog_writeNum(SubTick);
dbglog_writeReturn();
#endif
#if EmASC
2020-03-03 13:31:11 -05:00
ASC_SubTick(SubTick);
2019-07-22 22:50:34 -04:00
#else
#if SoundEnabled && (CurEmMd != kEmMd_PB100)
2019-07-22 22:50:34 -04:00
MacSound_SubTick(SubTick);
#else
UnusedParam(SubTick);
#endif
#endif
}
#define CyclesScaledPerTick (130240UL * ClockMult * kCycleScale)
2019-07-22 22:50:34 -04:00
#define CyclesScaledPerSubTick (CyclesScaledPerTick / kNumSubTicks)
LOCALVAR uint16_t SubTickCounter;
2019-07-22 22:50:34 -04:00
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)
{
2020-03-03 15:13:19 -05:00
int i;
2019-07-22 22:50:34 -04:00
#if dbglog_HAVE && 0
dbglog_WriteNote("begin new Sixtieth");
#endif
InterruptReset_Update();
2020-03-03 15:13:19 -05:00
for ( i = 0; i < ARRAY_SIZE(DEVICES); i++ ) {
if (DEVICES[i].starttick != NULL) { DEVICES[i].starttick(); }
}
2019-07-22 22:50:34 -04:00
SubTickTaskStart();
}
LOCALPROC SixtiethEndNotify(void)
{
2020-03-03 15:13:19 -05:00
int i;
2019-07-22 22:50:34 -04:00
SubTickTaskEnd();
2020-03-03 15:13:19 -05:00
for ( i = 0; i < ARRAY_SIZE(DEVICES); i++ ) {
if (DEVICES[i].endtick != NULL) { DEVICES[i].endtick(); }
}
2019-07-22 22:50:34 -04:00
#if dbglog_HAVE && 0
dbglog_WriteNote("end Sixtieth");
#endif
}
LOCALPROC ExtraTimeBeginNotify(void)
{
#if 0
dbglog_writeCStr("begin extra time");
dbglog_writeReturn();
#endif
VIA1_ExtraTimeBegin();
2020-03-03 13:31:11 -05:00
if (_EmVIA2) { VIA2_ExtraTimeBegin(); }
2019-07-22 22:50:34 -04:00
}
LOCALPROC ExtraTimeEndNotify(void)
{
VIA1_ExtraTimeEnd();
2020-03-03 13:31:11 -05:00
if (_EmVIA2) { VIA2_ExtraTimeEnd(); }
2019-07-22 22:50:34 -04:00
#if 0
dbglog_writeCStr("end extra time");
dbglog_writeReturn();
#endif
}
GLOBALPROC EmulationReserveAlloc(void)
{
ReserveAllocOneBlock(&RAM,
2020-02-11 00:34:32 -05:00
kRAM_Size + RAMSafetyMarginFudge, 5, false);
2019-07-22 22:50:34 -04:00
#if EmVidCard
2020-03-03 13:31:11 -05:00
ReserveAllocOneBlock(&VidROM, kVidROM_Size, 5, false);
2019-07-22 22:50:34 -04:00
#endif
#if IncludeVidMem
ReserveAllocOneBlock(&VidMem,
2020-02-11 00:34:32 -05:00
kVidMemRAM_Size + RAMSafetyMarginFudge, 5, true);
2019-07-22 22:50:34 -04:00
#endif
#if SmallGlobals
MINEM68K_ReserveAlloc();
#endif
}
2020-02-11 00:34:32 -05:00
LOCALFUNC bool InitEmulation(void)
2019-07-22 22:50:34 -04:00
{
2020-03-03 15:13:19 -05:00
int i;
for ( i = 0; i < ARRAY_SIZE(DEVICES); i++ ) {
if (DEVICES[i].init != NULL) {
bool retval = DEVICES[i].init();
if (retval == false) { return false; }
}
}
2020-03-03 13:31:11 -05:00
2020-03-03 15:13:19 -05:00
EmulatedHardwareZap();
return true;
2019-07-22 22:50:34 -04:00
}
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:
ReportAbnormalID(0x1001, "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 uint32_t ICT_DoGetNext(uint32_t maxn)
2019-07-22 22:50:34 -04:00
{
int i = 0;
uimr m = ICTactive;
uint32_t v = maxn;
2019-07-22 22:50:34 -04:00
while (0 != m) {
if (0 != (m & 1)) {
if (i >= kNumICTs) {
/* shouldn't happen */
m = 0;
} else {
uint32_t d = ICTwhen[i] - NextiCount;
2019-07-22 22:50:34 -04:00
/* 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(uint32_t n)
2019-07-22 22:50:34 -04:00
{
uint32_t n2;
uint32_t StopiCount = NextiCount + n;
2019-07-22 22:50:34 -04:00
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 uint32_t ExtraSubTicksToDo = 0;
2019-07-22 22:50:34 -04:00
LOCALPROC DoEmulateOneTick(void)
{
#if EnableAutoSlow
{
uint32_t NewQuietTime = QuietTime + 1;
2019-07-22 22:50:34 -04:00
if (NewQuietTime > QuietTime) {
/* if not overflow */
QuietTime = NewQuietTime;
}
}
#endif
#if EnableAutoSlow
{
uint32_t NewQuietSubTicks = QuietSubTicks + kNumSubTicks;
2019-07-22 22:50:34 -04:00
if (NewQuietSubTicks > QuietSubTicks) {
/* if not overflow */
QuietSubTicks = NewQuietSubTicks;
}
}
#endif
SixtiethSecondNotify();
m68k_go_nCycles_1(CyclesScaledPerTick);
SixtiethEndNotify();
if ((uint8_t) -1 == SpeedValue) {
ExtraSubTicksToDo = (uint32_t) -1;
2019-07-22 22:50:34 -04:00
} else {
uint32_t ExtraAdd = (kNumSubTicks << SpeedValue) - kNumSubTicks;
uint32_t ExtraLimit = ExtraAdd << 3;
2019-07-22 22:50:34 -04:00
ExtraSubTicksToDo += ExtraAdd;
if (ExtraSubTicksToDo > ExtraLimit) {
ExtraSubTicksToDo = ExtraLimit;
}
}
}
2020-02-11 00:34:32 -05:00
LOCALFUNC bool MoreSubTicksToDo(void)
2019-07-22 22:50:34 -04:00
{
2020-02-11 00:34:32 -05:00
bool v = false;
2019-07-22 22:50:34 -04:00
if (ExtraTimeNotOver() && (ExtraSubTicksToDo > 0)) {
#if EnableAutoSlow
if ((QuietSubTicks >= 16384)
&& (QuietTime >= 34)
&& ! WantNotAutoSlow)
{
ExtraSubTicksToDo = 0;
} else
#endif
{
2020-02-11 00:34:32 -05:00
v = true;
2019-07-22 22:50:34 -04:00
}
}
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
{
uint32_t NewQuietSubTicks = QuietSubTicks + 1;
2019-07-22 22:50:34 -04:00
if (NewQuietSubTicks > QuietSubTicks) {
/* if not overflow */
QuietSubTicks = NewQuietSubTicks;
}
}
#endif
m68k_go_nCycles_1(CyclesScaledPerSubTick);
--ExtraSubTicksToDo;
} while (MoreSubTicksToDo());
ExtraTimeEndNotify();
}
}
LOCALVAR uint32_t CurEmulatedTime = 0;
2019-07-22 22:50:34 -04:00
/*
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.
*/
int8_t n = OnTrueTime - CurEmulatedTime;
2019-07-22 22:50:34 -04:00
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 */
2020-02-11 00:34:32 -05:00
EmVideoDisable = true;
2019-07-22 22:50:34 -04:00
do {
DoEmulateOneTick();
++CurEmulatedTime;
} while (ExtraTimeNotOver()
&& (--n > 0));
2020-02-11 00:34:32 -05:00
EmVideoDisable = false;
2019-07-22 22:50:34 -04:00
}
EmLagTime = n;
}
}
LOCALPROC MainEventLoop(void)
{
for (; ; ) {
WaitForNextTick();
if (ForceMacOff) {
return;
}
RunEmulatedTicksToTrueTime();
DoEmulateExtraTime();
}
}
GLOBALPROC ProgramMain(void)
{
if (InitEmulation())
{
MainEventLoop();
}
}