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 }