diff --git a/src/device/paddle.d b/src/device/paddle.d index 0fdbb33..769a92d 100644 --- a/src/device/paddle.d +++ b/src/device/paddle.d @@ -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); } } diff --git a/src/device/speaker.d b/src/device/speaker.d index 33a2b04..32ab56d 100644 --- a/src/device/speaker.d +++ b/src/device/speaker.d @@ -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; diff --git a/src/peripheral/diskii.d b/src/peripheral/diskii.d index e2d234f..7c3fc4d 100644 --- a/src/peripheral/diskii.d +++ b/src/peripheral/diskii.d @@ -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; } } diff --git a/src/system/base.d b/src/system/base.d index 844cc77..2b4543a 100644 --- a/src/system/base.d +++ b/src/system/base.d @@ -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); diff --git a/src/timer.d b/src/timer.d index a0c0b51..7b5e6c3 100644 --- a/src/timer.d +++ b/src/timer.d @@ -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(); } diff --git a/src/ui/inputevents.d b/src/ui/inputevents.d index 5efffe8..17838d0 100644 --- a/src/ui/inputevents.d +++ b/src/ui/inputevents.d @@ -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; diff --git a/src/ui/mainwindow.d b/src/ui/mainwindow.d index 6d0e083..6e3dd45 100644 --- a/src/ui/mainwindow.d +++ b/src/ui/mainwindow.d @@ -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) diff --git a/src/video/patterns.d b/src/video/patterns.d index bb2bac1..b741227 100644 --- a/src/video/patterns.d +++ b/src/video/patterns.d @@ -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() diff --git a/src/video/scanner.d b/src/video/scanner.d index bdef541..13243a4 100644 --- a/src/video/scanner.d +++ b/src/video/scanner.d @@ -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()