/* 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 } GLOBALFUNC 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; GLOBALPROC 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; } GLOBALPROC 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(); } }