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 }