1 /** 2 Copyright: Copyright (c) 2013-2014 Andrey Penechko. 3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 8 module emulator.dcpu.devices.genericclock; 9 10 import std.stdio; 11 12 import anchovy.graphics.bitmap; 13 14 import emulator.dcpu.devices.idevice; 15 import emulator.dcpu.emulator; 16 import emulator.dcpu.dcpu; 17 import emulator.utils.undoproxy; 18 19 @trusted nothrow: 20 21 /++ 22 + Generic Clock (compatible) v1.0 23 + See 'docs/generic clock.txt' for specification. 24 +/ 25 26 private struct ClockRegisters 27 { 28 ulong initialCycles; 29 ushort ticks; 30 ushort divider; 31 ulong tickPeriod; 32 ushort interruptMessage; 33 } 34 35 class GenericClock(Cpu) : IDevice!Cpu 36 { 37 protected: 38 Cpu* _dcpu; 39 Emulator!Cpu _emulator; 40 41 UndoableStruct!(ClockRegisters, ushort) regs; 42 43 public: 44 /// Saves dcpu reference internally for future use. 45 override void attachEmulator(Emulator!Cpu emulator) 46 { 47 _emulator = emulator; 48 _dcpu = &emulator.dcpu; 49 50 regs.ticks = 0; 51 regs.divider = 0; 52 regs.tickPeriod = 0; 53 regs.interruptMessage = 0; 54 } 55 56 /// Handles hardware interrupt and returns a number of cycles. 57 override uint handleInterrupt() 58 { 59 ushort aRegister = _emulator.dcpu.regs.a; 60 ushort bRegister = _emulator.dcpu.regs.b; 61 62 switch(aRegister) 63 { 64 case 0: 65 if (regs.divider != 0) 66 { 67 _dcpu.updateQueue.removeQueries(this); 68 } 69 70 regs.divider = bRegister; 71 72 if (regs.divider != 0) 73 { 74 regs.tickPeriod = cast(ulong)(_dcpu.clockSpeed / (60.0 / regs.divider)); 75 _dcpu.updateQueue.addQuery(this, regs.tickPeriod); 76 } 77 regs.ticks = 0; 78 regs.initialCycles = _dcpu.regs.cycles; 79 return 0; 80 case 1: 81 _dcpu.regs.b = regs.ticks; 82 return 0; 83 case 2: 84 regs.interruptMessage = bRegister; 85 return 0; 86 default: 87 break; 88 } 89 90 return 0; 91 } 92 93 /// Called every application frame. 94 /// Can be used to update screens. 95 override void updateFrame() 96 { 97 } 98 99 override void handleUpdateQuery(ref ulong delay) 100 { 101 ulong diff = _dcpu.regs.cycles - regs.initialCycles; 102 ulong totalTicks = diff / regs.tickPeriod; 103 if (totalTicks > regs.ticks) 104 { 105 foreach(i; 0..totalTicks - regs.ticks) 106 { 107 regs.inc!"ticks"; 108 if (regs.interruptMessage > 0) 109 { 110 triggerInterrupt(_emulator.dcpu, regs.interruptMessage); 111 } 112 } 113 } 114 115 delay = regs.tickPeriod; 116 } 117 118 /// Returns: 32 bit word identifying the hardware id. 119 override uint hardwareId() @property 120 { 121 return 0x12d0b402; 122 } 123 124 /// Returns: 16 bit word identifying the hardware version. 125 override ushort hardwareVersion() @property 126 { 127 return 1; 128 } 129 130 /// Returns: 32 bit word identifying the manufacturer 131 override uint manufacturer() @property 132 { 133 return 0; 134 } 135 136 override void commitFrame(ulong frameNumber) 137 { 138 regs.commitFrame(frameNumber); 139 } 140 141 override void discardFrame() 142 { 143 regs.discardFrame(); 144 } 145 146 override void undoFrames(ulong numFrames) 147 { 148 regs.undoFrames(numFrames); 149 } 150 151 override void discardUndoStack() 152 { 153 regs.discardUndoStack(); 154 } 155 156 override size_t undoStackSize() @property 157 { 158 return regs.undoStackSize; 159 } 160 }