AppleWin/source/SynchronousEventManager.cpp
ThorstenB 4543117f81
More clean-up, portability and another debugger fix (PR #847)
Fixed debugger main "data" window behaviour:
. When activating the main data display ("data" command) the cursor keys wouldn't work until the minidump ("md1") was also enabled. NB. The cursor keys should work in the main data window, independently of whether the minidump is active.
2020-10-25 17:27:59 +00:00

169 lines
4.4 KiB
C++

/*
AppleWin : An Apple //e emulator for Windows
Copyright (C) 1994-1996, Michael O'Brien
Copyright (C) 1999-2001, Oliver Schmidt
Copyright (C) 2002-2005, Tom Charlesworth
Copyright (C) 2006-2020, Tom Charlesworth, Michael Pohoreski, Nick Westgate
AppleWin is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
AppleWin 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
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with AppleWin; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Description: Synchronous Event Manager
*
* This manager class maintains a linked-list of ordered timer-based event,
* where only the head of the list needs updating after every opcode.
*
* The Nth event in the list will expire in: event[1] + ... + event[N] cycles time.
* (So each event has a cycle delta expiry time relative to the previous event.)
*
* A synchronous event is used for a deterministic event that will occur in N cycles' time,
* eg. 6522 timer & Mousecard VBlank. (As opposed to async events, like SSC Rx/Tx interrupts.)
*
* Events that are active in the list can be removed before they expire,
* eg. 6522 timer when the interval changes.
*
* Author: Various
*
*/
#include "StdAfx.h"
#include "Applewin.h"
#include "SynchronousEventManager.h"
void SynchronousEventManager::Insert(SyncEvent* pNewEvent)
{
pNewEvent->m_active = true; // add always succeeds
if (!m_syncEventHead)
{
m_syncEventHead = pNewEvent;
return;
}
// walk list to find where to insert new event
SyncEvent* pPrevEvent = NULL;
SyncEvent* pCurrEvent = m_syncEventHead;
int newEventExtraCycles = pNewEvent->m_cyclesRemaining;
while (pCurrEvent)
{
if (newEventExtraCycles >= pCurrEvent->m_cyclesRemaining)
{
newEventExtraCycles -= pCurrEvent->m_cyclesRemaining;
pPrevEvent = pCurrEvent;
pCurrEvent = pCurrEvent->m_next;
if (!pCurrEvent) // end of list
{
pPrevEvent->m_next = pNewEvent;
pNewEvent->m_cyclesRemaining = newEventExtraCycles;
}
continue;
}
// insert new event
if (!pPrevEvent)
m_syncEventHead = pNewEvent;
else
pPrevEvent->m_next = pNewEvent;
pNewEvent->m_next = pCurrEvent;
pNewEvent->m_cyclesRemaining = newEventExtraCycles;
// update cycles for next event
if (pCurrEvent)
{
pCurrEvent->m_cyclesRemaining -= newEventExtraCycles;
_ASSERT(pCurrEvent->m_cyclesRemaining >= 0);
}
return;
}
}
bool SynchronousEventManager::Remove(int id)
{
SyncEvent* pPrevEvent = NULL;
SyncEvent* pCurrEvent = m_syncEventHead;
while (pCurrEvent)
{
if (pCurrEvent->m_id != id)
{
pPrevEvent = pCurrEvent;
pCurrEvent = pCurrEvent->m_next;
continue;
}
// remove event
if (!pPrevEvent)
m_syncEventHead = pCurrEvent->m_next;
else
pPrevEvent->m_next = pCurrEvent->m_next;
int oldEventExtraCycles = pCurrEvent->m_cyclesRemaining;
pPrevEvent = pCurrEvent;
pCurrEvent = pCurrEvent->m_next;
pPrevEvent->m_active = false;
pPrevEvent->m_next = NULL;
// update cycles for next event
if (pCurrEvent)
pCurrEvent->m_cyclesRemaining += oldEventExtraCycles;
return true;
}
_ASSERT(0);
return false;
}
extern bool g_irqOnLastOpcodeCycle;
void SynchronousEventManager::Update(int cycles, ULONG uExecutedCycles)
{
SyncEvent* pCurrEvent = m_syncEventHead;
if (!pCurrEvent)
return;
pCurrEvent->m_cyclesRemaining -= cycles;
if (pCurrEvent->m_cyclesRemaining <= 0)
{
if (pCurrEvent->m_cyclesRemaining == 0)
g_irqOnLastOpcodeCycle = true; // IRQ occurs on last cycle of opcode
int cyclesUnderflowed = -pCurrEvent->m_cyclesRemaining;
pCurrEvent->m_cyclesRemaining = pCurrEvent->m_callback(pCurrEvent->m_id, cycles, uExecutedCycles);
m_syncEventHead = pCurrEvent->m_next; // unlink this event
pCurrEvent->m_active = false;
pCurrEvent->m_next = NULL;
// Always Update even if cyclesUnderflowed=0, as next event may have cycleRemaining=0 (ie. the 2 events fire at the same time)
Update(cyclesUnderflowed, uExecutedCycles); // update (potential) next event with underflow cycles
if (pCurrEvent->m_cyclesRemaining)
Insert(pCurrEvent); // re-add event
}
}