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.floppydrive;
9 
10 import std.stdio;
11 import std.math : abs;
12 
13 import emulator.dcpu.devices.idevice;
14 import emulator.dcpu.emulator;
15 import emulator.dcpu.dcpu;
16 
17 @trusted nothrow:
18 
19 /++
20  + Mackapar 3.5"
21  + Floppy Drive (M35FD)
22  + See 'docs/floppy drive.txt' for specification.
23  +/
24 
25 struct Sector
26 {
27 	ushort[512] data;
28 }
29 
30 struct Floppy
31 {
32 	Sector[1440] sectors;
33 	bool isWriteProtected;
34 }
35 
36 class FloppyDrive(Cpu) : IDevice!Cpu
37 {
38 protected:
39 	Emulator!Cpu _emulator;
40 
41 	Floppy* _floppy;
42 
43 	StateCode _state = StateCode.ready;
44 	ErrorCode _error = ErrorCode.none;
45 
46 	ushort interruptMessage;
47 
48 	ushort curentTrack; // Read-write head position.
49 	ushort targetSector; // Floppy sector number to read/write.
50 	ushort ramAddress; // DCPU ram address.
51 
52 	// reading if true, writing otherwise.
53 	bool isReading;
54 
55 	//enum seekingTime = 2.4 / 1000.0; // seconds per track.
56 	//enum sectorReadWriteSpeed = 512.0 / 30700.0; // seconds per sector.
57 	enum seekingTime = 0;
58 	enum sectorReadWriteSpeed = 0;
59 	enum sectorSize = 512; // 512 words.
60 	enum sectorsPerTrack = 18;
61 
62 public:
63 	this()
64 	{
65 
66 	}
67 
68 	Floppy* floppy(Floppy* newFloppy) @property
69 	{
70 		if (newFloppy == _floppy) return _floppy;
71 
72 		if (newFloppy)
73 		{
74 			_floppy = newFloppy;
75 			setStateError(_floppy.isWriteProtected ? StateCode.readyWp : StateCode.ready);
76 		}
77 		else
78 		{
79 			_floppy = newFloppy;
80 
81 			if (_state == StateCode.busy)
82 			{
83 				_emulator.dcpu.updateQueue.removeQueries(this);
84 
85 				setStateError(StateCode.noMedia, ErrorCode.eject);
86 
87 				return _floppy;
88 			}
89 
90 			setStateError(StateCode.noMedia);
91 		}
92 
93 		return _floppy;
94 	}
95 
96 	override void attachEmulator(Emulator!Cpu emulator)
97 	{
98 		_emulator = emulator;
99 	}
100 
101 	/// Handles hardware interrupt and returns a number of cycles.
102 	override uint handleInterrupt()
103 	{
104 		ushort aRegister = _emulator.dcpu.regs.a; // A register
105 		ushort bRegister = _emulator.dcpu.regs.b; // B register
106 
107 		switch(aRegister)
108 		{
109 			case 0: // Poll device
110 				_emulator.dcpu.regs.b = _state;
111 				_emulator.dcpu.regs.c = _error;
112 				_error = ErrorCode.none;
113 
114 				return 0;
115 
116 			case 1: // Set interrupt message
117 				interruptMessage = _emulator.dcpu.regs.x;
118 
119 				return 0;
120 
121 			case 2: // Read sector
122 				return setupReadWrite!true;
123 
124 			case 3: // Write sector
125 				return setupReadWrite!false;
126 
127 			default:
128 				break;
129 		}
130 
131 		return 0;
132 	}
133 
134 	override void updateFrame()
135 	{
136 
137 	}
138 
139 	override void handleUpdateQuery(ref ulong delay)
140 	{
141 		assert(_floppy);
142 
143 		if (isReading)
144 		{
145 			auto ram = _emulator.dcpu.mem;
146 			auto sector = _floppy.sectors[targetSector].data;
147 
148 			foreach(i; 0..sectorSize)
149 			{
150 				ram[(ramAddress + i) & 0xFFFF] = sector[i];
151 			}
152 
153 			delay = 0;
154 		}
155 		else
156 		{
157 			auto ram = _emulator.dcpu.mem;
158 			auto sector = _floppy.sectors[targetSector].data;
159 
160 			foreach(i; 0..sectorSize)
161 			{
162 				sector[i] = ram[(ramAddress + i) & 0xFFFF];
163 			}
164 
165 			delay = 0;
166 		}
167 
168 		setStateError(_floppy.isWriteProtected ? StateCode.readyWp : StateCode.ready, ErrorCode.none);
169 	}
170 
171 	/// Returns: 32 bit word identifying the hardware id.
172 	override uint hardwareId() @property
173 	{
174 		return 0x4fd524c5;
175 	}
176 
177 	/// Returns: 16 bit word identifying the hardware version.
178 	override ushort hardwareVersion() @property
179 	{
180 		return 0x000b;
181 	}
182 
183 	/// Returns: 32 bit word identifying the manufacturer
184 	override uint manufacturer() @property
185 	{
186 		return 0x1eb37e91;
187 	}
188 
189 	override void commitFrame(ulong frameNumber)
190 	{
191 
192 	}
193 
194 	override void discardFrame()
195 	{
196 
197 	}
198 
199 	override void undoFrames(ulong numFrames)
200 	{
201 
202 	}
203 
204 	override void discardUndoStack()
205 	{
206 		
207 	}
208 
209 	override size_t undoStackSize() @property
210 	{
211 		return 0;
212 	}
213 
214 protected:
215 
216 	uint setupReadWrite(bool read)()
217 	{
218 		bool validState;
219 
220 		static if (read)
221 			validState = _state == StateCode.ready || _state == StateCode.readyWp;
222 		else
223 			validState = _state == StateCode.ready;
224 
225 		if (validState)
226 		{
227 			isReading = read;
228 			targetSector = _emulator.dcpu.regs.x;
229 			ramAddress = _emulator.dcpu.regs.y;
230 
231 			uint distance = abs(targetSector/sectorsPerTrack - curentTrack);
232 
233 			uint ticksToWait = cast(uint)(((distance * seekingTime) + sectorReadWriteSpeed) * _emulator.dcpu.clockSpeed);
234 
235 			_emulator.dcpu.updateQueue.addQuery(this, ticksToWait);
236 
237 			_emulator.dcpu.regs.b = 1;
238 			setStateError(StateCode.busy);
239 
240 			return 0;
241 		}
242 		
243 		_emulator.dcpu.regs.b = 0;
244 
245 		if (_state == StateCode.readyWp)
246 			setStateError(ErrorCode.floppyProtected);
247 		else if (_state == StateCode.noMedia)
248 			setStateError(ErrorCode.noMedia);
249 		else
250 			setStateError(ErrorCode.busy);
251 
252 		return 0;
253 	}
254 
255 	void setStateError(StateCode state, ErrorCode error)
256 	{
257 		_state = state;
258 		_error = error;
259 		stateErrorUpdated();
260 	}
261 
262 	void setStateError(ErrorCode error)
263 	{
264 		_error = error;
265 		stateErrorUpdated();
266 	}
267 
268 	void setStateError(StateCode state)
269 	{
270 		_state = state;
271 		stateErrorUpdated();
272 	}
273 
274 	void stateErrorUpdated()
275 	{
276 		if (interruptMessage)
277 		{
278 			triggerInterrupt(_emulator.dcpu, interruptMessage);
279 		}
280 	}
281 }
282 
283 enum StateCode : ushort
284 {
285 	noMedia,
286 	ready,
287 	readyWp,
288 	busy,
289 }
290 
291 enum ErrorCode : ushort
292 {
293 	none,
294 	busy,
295 	noMedia,
296 	floppyProtected,
297 	eject,
298 	badSector,
299 	broken = 0xFFFF
300 }