New timer code

This commit is contained in:
edmccard 2012-04-19 01:23:08 -04:00
parent 22f4327c0d
commit bf117ac730
9 changed files with 342 additions and 234 deletions

View File

@ -96,12 +96,12 @@ class Paddles
if (onTrigger[i] == 0) onTrigger[i] = 1;
switchVal[i] = 0x80;
}
timer.new Counter(onTrigger[0], &pdl_0_expired);
timer.new Counter(onTrigger[1], &pdl_1_expired);
timer.addCounter(onTrigger[0], &pdl_0_expired);
timer.addCounter(onTrigger[1], &pdl_1_expired);
if (numPaddles > 2)
{
timer.new Counter(onTrigger[2], &pdl_2_expired);
timer.new Counter(onTrigger[3], &pdl_3_expired);
timer.addCounter(onTrigger[2], &pdl_2_expired);
timer.addCounter(onTrigger[3], &pdl_3_expired);
}
}

View File

@ -103,8 +103,8 @@ class Speaker
void update()
{
uint elapsedSinceToggle = cycle.currentVal() - lastToggleTick;
lastToggleTick = cycle.currentVal();
uint elapsedSinceToggle = cycle.val() - lastToggleTick;
lastToggleTick = cycle.val();
elapsedSinceToggle = processExtraBuffer(elapsedSinceToggle);
uint samples = elapsedSinceToggle / sampleTicks;

View File

@ -70,28 +70,34 @@ ubyte[256] controllerRom = [
class StopTimer
{
Timer timer;
Timer.Counter stopCounter;
bool stopCounter;
void delegate() notifyExpired;
ulong t_tick;
size_t t_idx;
void startCountdown()
{
if (stopCounter is null)
stopCounter = timer.new Counter(1_020_484, &expire);
if (!stopCounter)
{
t_idx = timer.addCounter(1_020_484, &expire);
t_tick = timer.totalTicks;
stopCounter = true;
}
}
void stopCountdown()
{
if (stopCounter !is null)
if (stopCounter)
{
stopCounter.discard();
stopCounter = null;
timer.removeCounter(t_tick, t_idx);
stopCounter = false;
}
}
bool expire()
{
notifyExpired();
stopCounter = null;
stopCounter = false;
return false;
}
}

View File

@ -40,17 +40,15 @@ import ioummu;
class SystemBase
{
Video video_;
Timer timer;
abstract void reboot();
abstract void reset();
abstract uint checkpoint();
abstract uint sinceCheckpoint(uint cp);
abstract void execute();
}
class System(string chip) : SystemBase
{
Timer timer;
Timer.Cycle deviceCycle;
AddressDecoder decoder;
SoftSwitchPage switches;
@ -138,9 +136,9 @@ class System(string chip) : SystemBase
void initTimer()
{
// XXX constants? variables?
timer = new Timer(10_205, 1_020_484);
timer = new Timer(10_205, 1_020_484, &primaryStop);
deviceCycle =
timer.startCycle(timer.primaryCounter.startLength * 2);
timer.new Cycle(timer.primaryLength * 2);
}
void initMemory(ubyte[] romDump)
@ -177,7 +175,7 @@ class System(string chip) : SystemBase
resetLow = &cpu.resetLow;
debug(disassemble) cpu.memoryName = &decoder.memoryReadName;
timer.onPrimaryStop(&primaryStop);
// timer.onPrimaryStop(&primaryStop);
}
void initIO(ubyte[] vidRom)
@ -218,18 +216,6 @@ class System(string chip) : SystemBase
*resetLow = true;
}
override uint checkpoint()
{
return timer.primaryCounter.currentLength;
}
override uint sinceCheckpoint(uint cp)
{
uint currentLength = timer.primaryCounter.currentLength;
return ((currentLength == timer.primaryCounter.startLength) ?
cp : (cp - currentLength));
}
override void execute()
{
cpu.run(true);

View File

@ -1,7 +1,7 @@
/+
+ timer.d
+
+ Copyright: 2007 Gerald Stocker
+ Copyright: 2012 Ed McCardell, 2007 Gerald Stocker
+
+ This file is part of twoapple-reboot.
+
@ -22,222 +22,318 @@
module timer;
class Timer
final class Timer
{
class Cycle
{
int delta;
uint rollOver;
this(uint maxVal)
{
rollOver = maxVal;
restart();
}
void restart()
{
delta = 0 - currentCounter.elapsed();
}
uint currentVal()
{
return (currentCounter.elapsed() + delta) % rollOver;
}
void update()
{
delta = currentVal();
}
}
class Counter
private:
static struct Counter
{
uint start, curr;
bool active;
size_t next = -1, nextFree;
bool delegate() expiry;
uint startLength, currentLength;
int ticks;
bool shouldContinue;
ulong creationTick;
this(uint start)
this(uint length, bool delegate() expiry, ulong creationTick)
{
shouldContinue = true;
startLength = currentLength = ticks = start;
addCounter(this);
}
this(uint start, bool delegate() expiration)
{
this(start);
initCounter(this);
expiry = expiration;
}
final uint elapsed()
{
return currentLength - ticks;
}
final void tick()
{
--ticks;
if (ticks == 0)
{
reset();
}
}
final void forceExpire()
{
ticks = 1;
tick();
}
final void discard()
{
expiry = &nullExpiry;
forceExpire();
}
private final void resume()
{
currentLength = ticks;
}
private final bool expire()
{
ticks = currentLength = startLength;
return expiry();
}
private bool nullExpiry() { return false; }
}
class DelayedCounter : Counter
{
uint realStart;
bool delegate() realExpiry;
this(uint start, bool delegate() expiration, uint delay)
{
realStart = start;
realExpiry = expiration;
super(delay, &becomeReal);
}
private bool becomeReal()
{
ticks = currentLength = startLength = realStart;
expiry = realExpiry;
bool retval = expiry();
initCounter(this);
return retval;
start = curr = length;
this.expiry = expiry;
this.creationTick = creationTick;
active = true;
}
}
Cycle[] cycles;
ulong _totalTicks;
uint start = uint.max, curr = uint.max, minCurr = uint.max;
uint balance;
size_t head, tail, nextFree;
Counter[] counters;
Counter primaryCounter, currentCounter;
uint hertz;
uint _hertz;
this(uint primaryStart, uint hz)
final void setNextFree()
{
hertz = hz;
cycles.length = 10;
counters.length = 10;
cycles.length = 0;
counters.length = 0;
currentCounter = primaryCounter = new Counter(primaryStart);
for (size_t i = nextFree; i < counters.length; i++)
counters[i].nextFree = i + 1;
}
final void onPrimaryStop(bool delegate() expiration)
final void deleteCounter(size_t idx, size_t prev)
{
primaryCounter.expiry = expiration;
}
auto tmp = nextFree;
nextFree = idx;
counters[idx].nextFree = tmp;
Cycle startCycle(uint maxVal)
{
cycles.length = cycles.length + 1;
cycles[$-1] = new Cycle(maxVal);
return cycles[$-1];
}
void tick()
{
currentCounter.tick();
}
private void deleteCounters()
{
int numCounters = cast(int)counters.length;
int lastCounter;
main: for (int counter = 0; counter < counters.length; ++counter)
auto next = counters[idx].next;
if (idx == head)
{
lastCounter = counter;
while (!counters[counter].shouldContinue)
{
numCounters--;
if (++counter >= counters.length) break main;
}
currentCounter = counters[lastCounter] = counters[counter];
}
if (numCounters < counters.length)
{
counters.length = numCounters;
}
}
private void addCounter(Counter newCounter)
{
counters.length = counters.length + 1;
counters[$-1] = newCounter;
}
private void initCounter(Counter newCounter)
{
if (newCounter.ticks < currentCounter.ticks)
{
reset(newCounter);
head = next;
}
else
{
newCounter.ticks += currentCounter.elapsed();
counters[prev].next = next;
}
if (idx == tail)
{
tail = prev;
counters[tail].next = -1;
}
counters[idx].active = false;
}
private void reset(Counter newCounter = null)
public:
this(uint primaryLength, uint hertz, bool delegate() primaryStop)
{
// update cycle counts
for (int cycle = 0; cycle < cycles.length; ++cycle)
{
cycles[cycle].update();
}
counters = new Counter[50];
setNextFree();
_hertz = hertz;
addCounter(primaryLength, primaryStop);
}
// update counter counts
for (int counter = 0; counter < counters.length; ++counter)
{
if (counters[counter] !is currentCounter &&
counters[counter] !is newCounter)
counters[counter].ticks -= currentCounter.elapsed();
}
final @property uint primaryRemaining()
{
return counters[0].curr;
}
// check for expired counters
for (int counter = 0; counter < counters.length; ++counter)
{
if (counters[counter].ticks <= 0)
counters[counter].shouldContinue = counters[counter].expire();
else
counters[counter].resume();
}
final @property uint primaryLength()
{
return counters[0].start;
}
//delete counters that should be deleted
deleteCounters();
final @property uint hertz()
{
return _hertz;
}
// set current counter
for (int counter = 0; counter < counters.length; ++counter)
final @property ulong totalTicks()
{
return _totalTicks;
}
final void tick()
{
_totalTicks++;
curr--;
if (!curr)
{
if (counters[counter].ticks < currentCounter.ticks)
currentCounter = counters[counter];
minCurr = uint.max;
size_t idx = head;
size_t prev = -1;
while (idx != -1)
{
if (counters[idx].active)
{
counters[idx].curr -= start;
if (counters[idx].curr) counters[idx].curr -= balance;
if (!counters[idx].curr)
{
if (counters[idx].expiry())
counters[idx].curr = counters[idx].start;
else
deleteCounter(idx, prev);
}
if (counters[idx].active && counters[idx].curr < minCurr)
minCurr = counters[idx].curr;
}
else
{
deleteCounter(idx, prev);
}
prev = idx;
idx = counters[idx].next;
}
start = curr = minCurr;
balance = 0;
}
}
final size_t addCounter(uint length, bool delegate() expiry)
{
if (nextFree == counters.length)
{
counters.length += 20;
setNextFree();
}
auto idx = nextFree;
nextFree = counters[nextFree].nextFree;
counters[idx] = Counter(length, expiry, _totalTicks);
counters[tail].next = idx;
tail = idx;
counters[tail].next = -1;
if (curr == 0)
{
counters[idx].curr += start;
}
else
{
if (counters[idx].curr < curr)
{
balance = start - curr;
start = curr = counters[idx].curr;
}
else
{
counters[idx].curr += (start - curr);
}
}
return idx;
}
final void removeCounter(ulong creationTick, size_t idx)
{
assert(counters[idx].creationTick == creationTick);
counters[idx].active = false;
}
final class Cycle
{
ulong startTick;
uint rollOver;
this(uint rollOver)
{
this.rollOver = rollOver;
restart();
}
final void restart()
{
startTick = _totalTicks;
}
final uint val()
{
return (_totalTicks - startTick) % rollOver;
}
}
}
unittest
{
bool primary() { return true; }
auto t = new Timer(10205, 1020484, &primary);
int c1 = 0, c2 = 0;
t.addCounter(10, (){assert(t._totalTicks == 10); c1++; return true;});
foreach (i; 0..9) t.tick();
t.addCounter(5, (){assert(t._totalTicks == 14); c2++; return true;});
foreach (i; 0..5) t.tick();
assert (c1 == 1 && c2 == 1);
}
unittest
{
bool primary() { return true; }
auto t = new Timer(10205, 1020484, &primary);
int c1 = 0, c2 = 0;
struct Dummy
{
bool exp1()
{
auto ticks = t._totalTicks;
assert((c1 == 0 && ticks == 10) || (c1 == 1 && ticks == 30));
c1++;
t.addCounter(10, &exp2);
return false;
}
bool exp2()
{
auto ticks = t._totalTicks;
assert((c2 == 0 && ticks == 20) || (c2 == 1 && ticks == 40));
c2++;
t.addCounter(10, &exp1);
return false;
}
}
Dummy d;
t.addCounter(10, &d.exp1);
foreach (i; 0..40) t.tick();
assert(c1 == 2 && c2 == 2);
}
unittest
{
bool primary() { return true; }
auto t = new Timer(10205, 1020484, &primary);
int c1 = 0, c2 = 0, c3 = 0;
void addExtra()
{
t.addCounter(4, (){auto ticks = t._totalTicks;
assert((c1 == 0 && ticks == 14) ||
(c1 == 1 && ticks == 24));
c1++;
return false;});
t.addCounter(5, (){auto ticks = t._totalTicks;
assert((c2 == 0 && ticks == 15) ||
(c2 == 1 && ticks == 25));
c2++;
return false;});
t.addCounter(6, (){auto ticks = t._totalTicks;
assert((c3 == 0 && ticks == 16) ||
(c3 == 1 && ticks == 26));
c3++;
return false;});
}
t.addCounter(10, (){addExtra(); return true;});
foreach (i; 0..30) t.tick();
assert (c1 == 2 && c2 == 2 && c3 == 2);
}
unittest
{
bool primary() { return true; }
auto t = new Timer(10205, 1020484, &primary);
auto c1 = t.addCounter(10, (){assert(false); return false;});
auto c1_tick = t._totalTicks;
foreach (i; 0..5) t.tick();
t.removeCounter(c1_tick, c1);
foreach (i; 0..5) t.tick();
}
unittest
{
bool primary() { return true; }
auto t = new Timer(10205, 1020484, &primary);
int c1 = 0, c2 = 0;
t.addCounter(10, (){c1++; return true;});
auto idx = t.addCounter(15, (){c2++; return true;});
auto tick = t._totalTicks;
foreach (i; 0..20) t.tick();
assert(c1 == 2 && c2 == 1);
t.removeCounter(tick, idx);
foreach (i; 0..20) t.tick();
assert(c1 == 4 && c2 == 1);
}
unittest
{
bool primary() { return true; }
auto t = new Timer(10205, 1020484, &primary);
bool t1() { assert(!(t.totalTicks % 5000)); return true; }
bool t2() { assert(!(t.totalTicks % 4500)); return true; }
bool t3() { assert(!(t.totalTicks % 6500)); return true; }
bool junk() { assert(!((t.totalTicks - 7000) % 1500)); return true; }
t.addCounter(5000, &t1);
t.addCounter(4500, &t2);
t.addCounter(6500, &t3);
foreach (i; 0..7000) t.tick();
t.addCounter(1500, &junk);
t.addCounter(1500, &junk);
t.addCounter(1500, &junk);
t.addCounter(1500, &junk);
foreach (i; 0..7000) t.tick();
}

View File

@ -210,6 +210,8 @@ class Input
void onJoystickEvent()
{
import std.stdio;
writeln("QWERTY joyevent");
static const float scale = 65535.0 / 2760.0;
static const int shift = 32768;
bool buttonDown = false;

View File

@ -143,6 +143,13 @@ class TwoappleMainWindow : MainWindow
return super.windowDelete(event, widget);
}
uint sinceCheckpoint(uint cp)
{
uint currentLength = system.timer.primaryRemaining;
return ((currentLength == system.timer.primaryLength) ?
cp : (cp - currentLength));
}
bool configChanged;
bool runOnce;
bool stayOpen;
@ -189,9 +196,9 @@ class TwoappleMainWindow : MainWindow
if (shouldRun)
{
willElapse = system.checkpoint();
willElapse = system.timer.primaryRemaining;
system.execute();
didElapse = system.sinceCheckpoint(willElapse);
didElapse = sinceCheckpoint(willElapse);
input.processEvents();
// XXX do something about typeahead?
if (!runOnce)

View File

@ -222,7 +222,7 @@ class TextPatternGenerator_II : TextPatternGenerator
// XXX XXX INIT
void init(Timer timer)
{
timer.new Counter(timer.hertz / 2, &toggleFlash);
timer.addCounter(timer.hertz / 2, &toggleFlash);
}
}
@ -307,7 +307,7 @@ class TextPatternGenerator_IIe : TextPatternGenerator
// XXX XXX INIT
void init(Timer timer)
{
timer.new Counter(32 * 262 * 65, &toggleFlash); // XXX PAL
timer.addCounter(32 * 262 * 65, &toggleFlash); // XXX PAL
}
void altCharsetOn()

View File

@ -28,12 +28,14 @@ import memory;
import timer;
import device.base;
class Scanner : ScannerBase
{
uint page;
bool graphicsTime, textSwitch, mixedSwitch, hiresSwitch, oldTextSwitch;
Mode mode;
Timer timer;
Timer.Cycle vidCycle;
uint frameSkip, frameCount;
@ -47,14 +49,23 @@ class Scanner : ScannerBase
void init(Timer timer)
{
int frameLen = 262 * 65; // XXX PAL: 312 * 65
vidCycle = timer.startCycle(frameLen);
this.timer = timer;
vidCycle = timer.new Cycle(frameLen);
graphicsTime = true;
timer.new Counter(frameLen, &graphicsTimeOn);
timer.new DelayedCounter(frameLen, &frameComplete, frameLen - 1);
timer.new DelayedCounter(frameLen, &graphicsTimeOff, 160 * 65);
timer.new DelayedCounter(frameLen, &graphicsTimeOn, 192 * 65);
timer.new DelayedCounter(frameLen, &graphicsTimeOff, 224 * 65);
timer.addCounter(frameLen, &graphicsTimeOn);
timer.addCounter(frameLen - 1,
(){frameComplete();
timer.addCounter(frameLen, &frameComplete); return false;});
timer.addCounter(160 * 65,
(){graphicsTimeOff();
timer.addCounter(frameLen, &graphicsTimeOff); return false;});
timer.addCounter(192 * 65,
(){graphicsTimeOn();
timer.addCounter(frameLen, &graphicsTimeOn); return false;});
timer.addCounter(224 * 65,
(){graphicsTimeOff();
timer.addCounter(frameLen, &graphicsTimeOff); return false;});
}
void forceFrame()
@ -199,12 +210,12 @@ class Scanner : ScannerBase
uint currentLine()
{
return vidCycle.currentVal() / 65;
return vidCycle.val() / 65;
}
uint currentCol()
{
return vidCycle.currentVal() % 65;
return vidCycle.val() % 65;
}
ubyte* getData(uint vidClock)
@ -243,7 +254,7 @@ class Scanner_II : Scanner
ubyte floatingBus(ushort addr)
{
uint clock = vidCycle.currentVal();
uint clock = vidCycle.val();
if (((clock % 65) < 25) && (mode != Mode.HIRES))
return decoder.read(
cast(ushort)(0x1400 + (page * 0x400) + scanOffset(clock, mode)));
@ -263,7 +274,7 @@ class Scanner_IIe : Scanner
{
ubyte floatingBus(ushort addr)
{
return displayMem[mode][page].data[scanOffset(vidCycle.currentVal(),
return displayMem[mode][page].data[scanOffset(vidCycle.val(),
mode)];
// equivalent to getData()[0];
}
@ -280,7 +291,7 @@ class Scanner_IIe : Scanner
bool readVBL()
{
return (vidCycle.currentVal() >= (192 * 65));
return (vidCycle.val() >= (192 * 65));
}
ubyte readLowVBL()