NINTV-DS/arm9/source/emucore/ProcessorBus.cpp
2021-09-02 17:32:31 -04:00

318 lines
8.5 KiB
C++

#include "ProcessorBus.h"
ProcessorBus::ProcessorBus()
: processorCount(0),
startQueue(NULL),
endQueue(NULL)
{}
ProcessorBus::~ProcessorBus()
{
for (UINT32 i = 0; i < processorCount; i++) {
if (processors[i]->scheduleQueue)
delete processors[i]->scheduleQueue;
}
}
void ProcessorBus::addProcessor(Processor* p)
{
processors[processorCount] = p;
processorCount++;
p->processorBus = this;
p->scheduleQueue = new ScheduleQueue(p);
}
void ProcessorBus::removeProcessor(Processor* p)
{
for (UINT32 i = 0; i < processorCount; i++) {
if (processors[i] == p) {
for (UINT32 j = i; j < (processorCount-1); j++)
processors[j] = processors[j+1];
processorCount--;
delete p->scheduleQueue;
p->scheduleQueue = NULL;
return;
}
}
}
void ProcessorBus::removeAll()
{
while (processorCount)
removeProcessor(processors[0]);
}
void ProcessorBus::reset()
{
if (processorCount == 0)
return;
UINT64 totalClockSpeed = 1;
startQueue = endQueue = NULL;
//reorder the processor queue so that it is in the natural (starting) order
for (UINT32 i = 0; i < processorCount; i++) {
Processor* p = processors[i];
totalClockSpeed = lcm(totalClockSpeed, ((UINT64)p->getClockSpeed()));
ScheduleQueue* nextQueue = p->scheduleQueue;
nextQueue->tick = 0;
if (startQueue == NULL) {
startQueue = nextQueue;
nextQueue->previous = NULL;
nextQueue->next = NULL;
}
else {
endQueue->next = nextQueue;
nextQueue->previous = endQueue;
nextQueue->next = NULL;
}
endQueue = nextQueue;
}
//pre-cache the multiplication factor required to convert each processor's clock speed to
//the common clock speed, and reset each processor
for (UINT32 i = 0; i < processorCount; i++) {
Processor* p = processors[i];
p->scheduleQueue->tickFactor = (totalClockSpeed / ((UINT64)p->getClockSpeed()));
p->resetProcessor();
}
}
void ProcessorBus::run()
{
running = true;
while (running) {
// TODO: jeremiah sypult, saw crash when NULL
if (startQueue->next == NULL) {
break;
}
//tick the processor that is at the head of the queue
int minTicks = (int)((startQueue->next->tick / startQueue->tickFactor) + 1);
startQueue->tick = ((UINT64)startQueue->processor->tick(minTicks)) * startQueue->tickFactor;
//now reschedule the processor for later processing
ScheduleQueue* tmp1 = startQueue;
while (tmp1->next != NULL && startQueue->tick > tmp1->next->tick) {
startQueue->tick -= tmp1->next->tick;
tmp1 = tmp1->next;
}
//reorganize the scheduling queue
ScheduleQueue* queueToShuffle = startQueue;
startQueue = startQueue->next;
queueToShuffle->previous = tmp1;
queueToShuffle->next = tmp1->next;
tmp1->next = queueToShuffle;
if (queueToShuffle->next != NULL) {
queueToShuffle->next->tick -= queueToShuffle->tick;
queueToShuffle->next->previous = queueToShuffle;
}
else
endQueue = queueToShuffle;
}
}
void ProcessorBus::stop()
{
running = false;
}
void ProcessorBus::halt(Processor*)
{
}
void ProcessorBus::unhalt(Processor*)
{
}
void ProcessorBus::pause(Processor* p, int ticks)
{
ScheduleQueue* q = p->scheduleQueue;
UINT64 commonTicks = ((UINT64)ticks) * q->tickFactor;
if (q->next == NULL)
q->tick += commonTicks;
else if (commonTicks <= q->next->tick) {
q->tick += commonTicks;
q->next->tick -= commonTicks;
}
else {
//pull this processor out of the scheduling stream and reschedule it
//add my current ticks to the next guy's delta ticks
q->next->tick += q->tick;
//add the common ticks to my ticks
q->tick += commonTicks;
//have the previous q point to the next q
if (q->previous != NULL) {
q->previous->next = q->next;
q->next->previous = q->previous;
}
reschedule(p->scheduleQueue);
}
}
void ProcessorBus::reschedule(ScheduleQueue* queueToShuffle)
{
//look for where to put this processor in the scheduling queue
ScheduleQueue* tmp1 = queueToShuffle;
while (tmp1->next != NULL && queueToShuffle->tick > tmp1->next->tick) {
queueToShuffle->tick -= tmp1->next->tick;
tmp1 = tmp1->next;
}
//reorganize the scheduling queue
if (queueToShuffle == startQueue)
startQueue = queueToShuffle->next;
queueToShuffle->previous = tmp1;
queueToShuffle->next = tmp1->next;
tmp1->next = queueToShuffle;
if (queueToShuffle->next != NULL) {
queueToShuffle->next->tick -= queueToShuffle->tick;
queueToShuffle->next->previous = queueToShuffle;
}
else
endQueue = queueToShuffle;
}
UINT64 lcm(UINT64 a, UINT64 b) {
//This is an implementation of Euclid's Algorithm for determining
//the greatest common denominator (gcd) of two numbers.
UINT64 m = a;
UINT64 n = b;
UINT64 r = m % n;
while (r != 0) {
m = n;
n = r;
r = m % n;
}
UINT64 gcd = n;
//lcm is determined from gcd through the following equation
return (a/gcd)*b;
}
/*
ProcessorBus::ProcessorBus()
{
processorDebugOn = FALSE;
processorCount = 0;
memset(processors, 0, sizeof(Processor*) * MAX_PROCESSORS);
memset(processorTickFactors, 0, sizeof(INT64) * MAX_PROCESSORS);
memset(processorTicks, 0, sizeof(INT64) * MAX_PROCESSORS);
memset(processorsIdle, 0, sizeof(BOOL) * MAX_PROCESSORS);
}
void ProcessorBus::setAudioMixer(AudioMixer* am)
{
this->audioMixer = am;
}
void ProcessorBus::init()
{
UINT64 totalClockSpeed = 1;
UINT32 i;
for (i = 0; i < processorCount; i++) {
totalClockSpeed = lcm(totalClockSpeed,
((UINT64)processors[i]->getClockSpeed()));
}
for (i = 0; i < processorCount; i++) {
processorsIdle[i] = FALSE;
processorTicks[i] = 0;
processorTickFactors[i] = (totalClockSpeed /
((UINT64)processors[i]->getClockSpeed()));
processors[i]->initProcessor();
}
}
void ProcessorBus::release()
{
for (UINT32 i = 0; i < processorCount; i++)
processors[i]->releaseProcessor();
}
void ProcessorBus::addProcessor(Processor* p)
{
processors[processorCount] = p;
processorCount++;
}
void ProcessorBus::removeProcessor(Processor* p)
{
for (UINT32 i = 0; i < processorCount; i++) {
if (processors[i] == p) {
for (UINT32 j = i; j < (processorCount-1); j++)
processors[j] = processors[j+1];
processorCount--;
return;
}
}
}
void ProcessorBus::removeAll()
{
processorCount = 0;
}
void ProcessorBus::tick()
{
//determine the next tick delta
#ifdef _MSC_VER
UINT64 tickDelta = 0xFFFFFFFFFFFFFFFF;
#else
UINT64 tickDelta = 0xFFFFFFFFFFFFFFFFll;
#endif
UINT32 i;
for (i = 0; i < processorCount; i++) {
//wake up any processors that want to wake up
processorsIdle[i] = (processorsIdle[i] && processors[i]->isIdle());
//if the processor is not idle by this point, use it
//to calculate the tick delta
if (!processorsIdle[i] && (UINT64)processorTicks[i] < tickDelta)
tickDelta = processorTicks[i];
}
//move the audio mixer clock forward by the tick delta amount
audioMixer->clock += tickDelta;
for (i = 0; i < processorCount; i++) {
//skip this processor if it has been idled
if (!processorsIdle[i]) {
processorTicks[i] -= tickDelta;
//if the clock just caught up to the processor, and
//if the processor is not about to go idle, then run it
if (processorTicks[i] == 0) {
if (processors[i]->isIdle())
processorsIdle[i] = TRUE;
else {
processorTicks[i] = (((UINT64)processors[i]->tick()) *
processorTickFactors[i]);
}
}
}
}
}
UINT64 lcm(UINT64 a, UINT64 b) {
//This is an implementation of Euclid's Algorithm for determining
//the greatest common denominator (gcd) of two numbers.
UINT64 m = a;
UINT64 n = b;
UINT64 r = m % n;
while (r != 0) {
m = n;
n = r;
r = m % n;
}
UINT64 gcd = n;
//lcm is determined from gcd through the following equation
return (a/gcd)*b;
}
*/