mirror of
https://github.com/InvisibleUp/uvmac.git
synced 2024-06-09 22:29:32 +00:00
be71368cf7
Non-windows systems are probably broken right now. Color display in general is probably broken. But, soon, you will be able to change the screen size and color depth without recompiling. That would be nice.
641 lines
12 KiB
C
641 lines
12 KiB
C
/*
|
|
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.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#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"
|
|
|
|
#include "PROGMAIN.h"
|
|
|
|
// Temporary location for config variables
|
|
bool EnableAutoSlow = true;
|
|
uint16_t vMacScreenHeight = 342;
|
|
uint16_t vMacScreenWidth = 512;
|
|
uint16_t vMacScreenDepth = 0;
|
|
|
|
// 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 (*subtick)(int);
|
|
void (*timebegin)(void);
|
|
void (*timeend)(void);
|
|
} DevMethods_t;
|
|
|
|
const DevMethods_t DEVICES[] = {
|
|
// RTC
|
|
{
|
|
.init = EmRTC ? RTC_Init : NULL,
|
|
.reset = NULL,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// ROM
|
|
{
|
|
.init = ROM_Init,
|
|
.reset = NULL,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// Memory
|
|
{
|
|
.init = AddrSpac_Init,
|
|
.reset = Memory_Reset,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// ICT
|
|
{
|
|
.init = NULL,
|
|
.reset = ICT_Zap,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// IWM
|
|
{
|
|
.init = NULL,
|
|
.reset = IWM_Reset,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// SCC
|
|
{
|
|
.init = NULL,
|
|
.reset = SCC_Reset,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// SCSI
|
|
{
|
|
.init = NULL,
|
|
.reset = SCSI_Reset,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// VIA1
|
|
{
|
|
.init = NULL,
|
|
.reset = VIA1_Zap,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = VIA1_ExtraTimeBegin,
|
|
.timeend = VIA1_ExtraTimeEnd,
|
|
},
|
|
// VIA2
|
|
{
|
|
.init = NULL,
|
|
.reset = EmVIA2 ? VIA2_Zap : NULL,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = EmVIA2 ? VIA2_ExtraTimeBegin : NULL,
|
|
.timeend = EmVIA2 ? VIA2_ExtraTimeEnd : NULL,
|
|
},
|
|
// Sony disk drive
|
|
{
|
|
.init = NULL,
|
|
.reset = Sony_Reset,
|
|
.starttick = Sony_Update,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// Extn
|
|
{
|
|
.init = NULL,
|
|
.reset = Extn_Reset,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// m68k
|
|
{
|
|
.init = NULL,
|
|
.reset = m68k_reset,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// Mouse
|
|
{
|
|
.init = NULL,
|
|
.reset = NULL,
|
|
.starttick = Mouse_Update,
|
|
.endtick = Mouse_EndTickNotify,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// Classic Keyboard
|
|
{
|
|
.init = NULL,
|
|
.reset = NULL,
|
|
.starttick = EmClassicKbrd ? KeyBoard_Update : NULL,
|
|
.endtick = NULL,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// ADB
|
|
{
|
|
.init = NULL,
|
|
.reset = NULL,
|
|
.starttick = EmADB ? ADB_Update : NULL,
|
|
.endtick = NULL,
|
|
.subtick = 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,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// Screen
|
|
{
|
|
.init = NULL,
|
|
.reset = NULL,
|
|
.starttick = Sixtieth_PulseNtfy, // VBlank interrupt
|
|
.endtick = Screen_EndTickNotify,
|
|
.subtick = NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL,
|
|
},
|
|
// ASC
|
|
{
|
|
.init = NULL,
|
|
.reset = NULL,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = EmASC ? ASC_SubTick : NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL
|
|
},
|
|
// Sound (non-ASC)
|
|
{
|
|
.init = NULL,
|
|
.reset = NULL,
|
|
.starttick = NULL,
|
|
.endtick = NULL,
|
|
.subtick = (SoundEnabled && (CurEmMd != kEmMd_PB100)) ? MacSound_SubTick : NULL,
|
|
.timebegin = NULL,
|
|
.timeend = NULL
|
|
}
|
|
};
|
|
|
|
LOCALPROC EmulatedHardwareZap(void)
|
|
{
|
|
int i;
|
|
for ( i = 0; i < ARRAY_SIZE(DEVICES); i++ ) {
|
|
if (DEVICES[i].reset != NULL) { DEVICES[i].reset(); }
|
|
}
|
|
}
|
|
|
|
LOCALPROC DoMacReset(void)
|
|
{
|
|
Sony_EjectAllDisks();
|
|
EmulatedHardwareZap();
|
|
}
|
|
|
|
LOCALPROC InterruptReset_Update(void)
|
|
{
|
|
SetInterruptButton(false); // don't keep held over 1/60 sec
|
|
|
|
if (WantMacInterrupt) {
|
|
SetInterruptButton(true);
|
|
WantMacInterrupt = false;
|
|
}
|
|
if (WantMacReset) {
|
|
DoMacReset();
|
|
WantMacReset = false;
|
|
}
|
|
}
|
|
|
|
LOCALPROC SubTickNotify(int SubTick)
|
|
{
|
|
int i;
|
|
for ( i = 0; i < ARRAY_SIZE(DEVICES); i++ ) {
|
|
if (DEVICES[i].subtick != NULL) { DEVICES[i].subtick(SubTick); }
|
|
}
|
|
}
|
|
|
|
#define CyclesScaledPerTick (130240UL * ClockMult * kCycleScale)
|
|
#define CyclesScaledPerSubTick (CyclesScaledPerTick / kNumSubTicks)
|
|
|
|
LOCALVAR uint16_t 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)
|
|
{
|
|
int i;
|
|
// Begin new frame
|
|
InterruptReset_Update();
|
|
for ( i = 0; i < ARRAY_SIZE(DEVICES); i++ ) {
|
|
if (DEVICES[i].starttick != NULL) { DEVICES[i].starttick(); }
|
|
}
|
|
|
|
SubTickTaskStart();
|
|
}
|
|
|
|
LOCALPROC SixtiethEndNotify(void)
|
|
{
|
|
int i;
|
|
SubTickTaskEnd();
|
|
for ( i = 0; i < ARRAY_SIZE(DEVICES); i++ ) {
|
|
if (DEVICES[i].endtick != NULL) { DEVICES[i].endtick(); }
|
|
}
|
|
// End frame
|
|
}
|
|
|
|
LOCALPROC ExtraTimeBeginNotify(void)
|
|
{
|
|
int i;
|
|
for ( i = 0; i < ARRAY_SIZE(DEVICES); i++ ) {
|
|
if (DEVICES[i].timebegin != NULL) { DEVICES[i].timebegin(); }
|
|
}
|
|
}
|
|
|
|
LOCALPROC ExtraTimeEndNotify(void)
|
|
{
|
|
int i;
|
|
for ( i = 0; i < ARRAY_SIZE(DEVICES); i++ ) {
|
|
if (DEVICES[i].timeend != NULL) { DEVICES[i].timeend(); }
|
|
}
|
|
}
|
|
|
|
GLOBALPROC EmulationReserveAlloc(void)
|
|
{
|
|
ReserveAllocOneBlock(&RAM,
|
|
kRAM_Size + RAMSafetyMarginFudge, 5, false);
|
|
#if EmVidCard
|
|
ReserveAllocOneBlock(&VidROM, kVidROM_Size, 5, false);
|
|
#endif
|
|
#if IncludeVidMem
|
|
ReserveAllocOneBlock(&VidMem,
|
|
kVidMemRAM_Size + RAMSafetyMarginFudge, 5, true);
|
|
#endif
|
|
#if SmallGlobals
|
|
MINEM68K_ReserveAlloc();
|
|
#endif
|
|
}
|
|
|
|
LOCALFUNC bool InitEmulation(void)
|
|
{
|
|
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; }
|
|
}
|
|
}
|
|
|
|
EmulatedHardwareZap();
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
uimr m;
|
|
|
|
// For each ICT...
|
|
for (m = ICTactive, i = 0; m != 0; m >>=1, i += 1) {
|
|
// If no ICT here, continue
|
|
if (0 == (m & 1)) { continue; }
|
|
if (ICTwhen[i] != NextiCount) { continue; }
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
ICTactive &= ~(1 << i); // Clear active bit
|
|
ICT_DoTask(i);
|
|
}
|
|
}
|
|
|
|
LOCALFUNC uint32_t ICT_DoGetNext(uint32_t maxn)
|
|
{
|
|
int i;
|
|
uimr m;
|
|
uint32_t v = maxn;
|
|
|
|
for (m = ICTactive, i = 0; m != 0; m >>=1, i += 1) {
|
|
uint32_t d = ICTwhen[i] - NextiCount;
|
|
if (0 == (m & 1)) { continue; }
|
|
if (d < v) { v = d; }
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
LOCALPROC m68k_go_nCycles_1(uint32_t n)
|
|
{
|
|
uint32_t n2;
|
|
uint32_t StopiCount = NextiCount + n;
|
|
do {
|
|
ICT_DoCurrentTasks();
|
|
n2 = ICT_DoGetNext(n);
|
|
NextiCount += n2;
|
|
m68k_go_nCycles(n2);
|
|
n = StopiCount - NextiCount;
|
|
} while (n != 0);
|
|
}
|
|
|
|
LOCALVAR uint32_t ExtraSubTicksToDo = 0;
|
|
|
|
LOCALPROC DoEmulateOneTick(void)
|
|
{
|
|
if (EnableAutoSlow) {
|
|
uint32_t NewQuietTime = QuietTime + 1;
|
|
uint32_t NewQuietSubTicks = QuietSubTicks + kNumSubTicks;
|
|
|
|
if (NewQuietTime > QuietTime) {
|
|
/* if not overflow */
|
|
QuietTime = NewQuietTime;
|
|
}
|
|
if (NewQuietSubTicks > QuietSubTicks) {
|
|
/* if not overflow */
|
|
QuietSubTicks = NewQuietSubTicks;
|
|
}
|
|
}
|
|
|
|
SixtiethSecondNotify();
|
|
m68k_go_nCycles_1(CyclesScaledPerTick);
|
|
SixtiethEndNotify();
|
|
|
|
if ((uint8_t) -1 == SpeedValue) {
|
|
ExtraSubTicksToDo = (uint32_t) -1;
|
|
} else {
|
|
uint32_t ExtraAdd = (kNumSubTicks << SpeedValue) - kNumSubTicks;
|
|
uint32_t ExtraLimit = ExtraAdd << 3;
|
|
|
|
ExtraSubTicksToDo += ExtraAdd;
|
|
if (ExtraSubTicksToDo > ExtraLimit) {
|
|
ExtraSubTicksToDo = ExtraLimit;
|
|
}
|
|
}
|
|
}
|
|
|
|
LOCALFUNC bool MoreSubTicksToDo(void)
|
|
{
|
|
bool v = false;
|
|
|
|
if (ExtraTimeNotOver() && (ExtraSubTicksToDo > 0)) {
|
|
if (
|
|
EnableAutoSlow
|
|
&& (QuietSubTicks >= 16384)
|
|
&& (QuietTime >= 34)
|
|
&& ! WantNotAutoSlow
|
|
) {
|
|
ExtraSubTicksToDo = 0;
|
|
} else {
|
|
v = true;
|
|
}
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
/*
|
|
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).
|
|
*/
|
|
LOCALPROC DoEmulateExtraTime(void)
|
|
{
|
|
if (MoreSubTicksToDo()) {
|
|
ExtraTimeBeginNotify();
|
|
do {
|
|
if (EnableAutoSlow) {
|
|
uint32_t NewQuietSubTicks = QuietSubTicks + 1;
|
|
|
|
if (NewQuietSubTicks > QuietSubTicks) {
|
|
/* if not overflow */
|
|
QuietSubTicks = NewQuietSubTicks;
|
|
}
|
|
}
|
|
m68k_go_nCycles_1(CyclesScaledPerSubTick);
|
|
--ExtraSubTicksToDo;
|
|
} while (MoreSubTicksToDo());
|
|
ExtraTimeEndNotify();
|
|
}
|
|
}
|
|
|
|
/*
|
|
The number of ticks that have been emulated so far.
|
|
That is, the number of times "DoEmulateOneTick" has been called.
|
|
*/
|
|
LOCALVAR uint32_t CurEmulatedTime = 0;
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
LOCALPROC RunEmulatedTicksToTrueTime(void)
|
|
{
|
|
int8_t lag = OnTrueTime - CurEmulatedTime;
|
|
|
|
if (lag > 0) {
|
|
DoEmulateOneTick();
|
|
CurEmulatedTime += 1;
|
|
DoneWithDrawingForTick();
|
|
|
|
if (lag > 8) {
|
|
/* emulation not fast enough */
|
|
lag = 8;
|
|
CurEmulatedTime = OnTrueTime - lag;
|
|
} else {
|
|
lag -= 1;
|
|
}
|
|
|
|
while (ExtraTimeNotOver() && (lag > 0)) {
|
|
/* Missed vblank due to lag; catch up */
|
|
EmVideoDisable = true;
|
|
DoEmulateOneTick();
|
|
CurEmulatedTime += 1;
|
|
lag -= 1;
|
|
EmVideoDisable = false;
|
|
}
|
|
|
|
EmLagTime = lag;
|
|
}
|
|
}
|
|
|
|
LOCALPROC MainEventLoop(void)
|
|
{
|
|
while (true) {
|
|
WaitForNextTick();
|
|
if (ForceMacOff) { return; }
|
|
RunEmulatedTicksToTrueTime();
|
|
DoEmulateExtraTime();
|
|
}
|
|
}
|
|
|
|
GLOBALPROC ProgramMain(void)
|
|
{
|
|
if (InitEmulation() == false) {return;}
|
|
MainEventLoop();
|
|
}
|