Mon, 06 Dec 2010 08:27:21 +0000
move edge-sensitive FDC IRQ to main()
1 #include <stdint.h>
2 #include <stdbool.h>
3 #include <malloc.h>
4 #include "musashi/m68k.h"
5 #include "wd279x.h"
7 /// WD2797 command constants
8 enum {
9 CMD_MASK = 0xF0, ///< Bit mask to detect command bits
10 CMD_RESTORE = 0x00, ///< Restore (recalibrate, seek to track 0)
11 CMD_SEEK = 0x10, ///< Seek to given track
12 CMD_STEP = 0x20, ///< Step
13 CMD_STEP_TU = 0x30, ///< Step and update track register
14 CMD_STEPIN = 0x40, ///< Step In
15 CMD_STEPIN_TU = 0x50, ///< Step In and update track register
16 CMD_STEPOUT = 0x60, ///< Step Out
17 CMD_STEPOUT_TU = 0x70, ///< Step Out and update track register
18 CMD_READ_SECTOR = 0x80, ///< Read Sector
19 CMD_READ_SECTOR_MULTI = 0x90, ///< Read Multiple Sectors
20 CMD_WRITE_SECTOR = 0xA0, ///< Write Sector
21 CMD_WRITE_SECTOR_MULTI = 0xB0, ///< Write Multiple Sectors
22 CMD_READ_ADDRESS = 0xC0, ///< Read Address (IDAM contents)
23 CMD_FORCE_INTERRUPT = 0xD0, ///< Force Interrupt
24 CMD_READ_TRACK = 0xE0, ///< Read Track
25 CMD_FORMAT_TRACK = 0xF0 ///< Format Track
26 };
29 void wd2797_init(WD2797_CTX *ctx)
30 {
31 // track, head and sector unknown
32 ctx->track = ctx->head = ctx->sector = 0;
34 // no IRQ pending
35 ctx->irq = false;
37 // no data available
38 ctx->data_pos = ctx->data_len = 0;
39 ctx->data = NULL;
41 // Status register clear, not busy; type1 command
42 ctx->status = 0;
43 ctx->cmd_has_drq = false;
45 // Clear data register
46 ctx->data_reg = 0;
48 // Last step direction = "towards zero"
49 ctx->last_step_dir = -1;
51 // No disc image loaded
52 ctx->disc_image = NULL;
53 ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = ctx->geom_tracks = 0;
54 }
57 void wd2797_reset(WD2797_CTX *ctx)
58 {
59 // track, head and sector unknown
60 ctx->track = ctx->head = ctx->sector = 0;
62 // no IRQ pending
63 ctx->irq = false;
65 // no data available
66 ctx->data_pos = ctx->data_len = 0;
68 // Status register clear, not busy
69 ctx->status = 0;
71 // Clear data register
72 ctx->data_reg = 0;
74 // Last step direction
75 ctx->last_step_dir = -1;
76 }
79 void wd2797_done(WD2797_CTX *ctx)
80 {
81 // Reset the WD2797
82 wd2797_reset(ctx);
84 // Free any allocated memory
85 if (ctx->data) {
86 free(ctx->data);
87 ctx->data = NULL;
88 }
89 }
92 bool wd2797_get_irq(WD2797_CTX *ctx)
93 {
94 return ctx->irq;
95 }
98 bool wd2797_get_drq(WD2797_CTX *ctx)
99 {
100 return (ctx->data_pos < ctx->data_len);
101 }
104 WD2797_ERR wd2797_load(WD2797_CTX *ctx, FILE *fp, int secsz, int spt, int heads)
105 {
106 size_t filesize;
108 // Start by finding out how big the image file is
109 fseek(fp, 0, SEEK_END);
110 filesize = ftell(fp);
111 fseek(fp, 0, SEEK_SET);
113 // Now figure out how many tracks it contains
114 int tracks = filesize / secsz / spt / heads;
115 // Confirm...
116 if (tracks < 1) {
117 return WD2797_ERR_BAD_GEOM;
118 }
120 // Allocate enough memory to store one disc track
121 if (ctx->data) {
122 free(ctx->data);
123 }
124 ctx->data = malloc(secsz * spt);
125 if (!ctx->data)
126 return WD2797_ERR_NO_MEMORY;
128 // Load the image and the geometry data
129 ctx->disc_image = fp;
130 ctx->geom_tracks = tracks;
131 ctx->geom_secsz = secsz;
132 ctx->geom_heads = heads;
133 ctx->geom_spt = spt;
135 return WD2797_ERR_OK;
136 }
139 void wd2797_unload(WD2797_CTX *ctx)
140 {
141 // Free memory buffer
142 if (ctx->data) {
143 free(ctx->data);
144 ctx->data = NULL;
145 }
147 // Clear file pointer
148 ctx->disc_image = NULL;
150 // Clear the disc geometry
151 ctx->geom_tracks = ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = 0;
152 }
155 uint8_t wd2797_read_reg(WD2797_CTX *ctx, uint8_t addr)
156 {
157 uint8_t temp = 0;
159 switch (addr & 0x03) {
160 case WD2797_REG_STATUS: // Status register
161 // Read from status register clears IRQ
162 ctx->irq = false;
164 // Get current status flags (set by last command)
165 // DRQ bit
166 if (ctx->cmd_has_drq) {
167 printf("\tWDFDC rd sr, has drq, pos=%lu len=%lu\n", ctx->data_pos, ctx->data_len);
168 temp = ctx->status & ~0x03;
169 temp |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
170 } else {
171 temp = ctx->status & ~0x01;
172 }
173 // FDC is busy if there is still data in the buffer
174 temp |= (ctx->data_pos < ctx->data_len) ? 0x01 : 0x00; // if data in buffer, then DMA hasn't copied it yet, and we're still busy!
175 // TODO: also if seek delay / read delay hasn't passed (but that's for later)
176 return temp;
178 case WD2797_REG_TRACK: // Track register
179 return ctx->track;
181 case WD2797_REG_SECTOR: // Sector register
182 return ctx->sector;
184 case WD2797_REG_DATA: // Data register
185 // If there's data in the buffer, return it. Otherwise return 0xFF.
186 if (ctx->data_pos < ctx->data_len) {
187 // set IRQ if this is the last data byte
188 if (ctx->data_pos == (ctx->data_len-1)) {
189 // Set IRQ
190 ctx->irq = true;
191 }
192 // return data byte and increment pointer
193 return ctx->data[ctx->data_pos++];
194 } else {
195 // command finished
196 return 0xff;
197 }
199 default:
200 // shut up annoying compilers which don't recognise unreachable code when they see it
201 // (here's looking at you, gcc!)
202 return 0xff;
203 }
204 }
207 void wd2797_write_reg(WD2797_CTX *ctx, uint8_t addr, uint8_t val)
208 {
209 uint8_t cmd = val & CMD_MASK;
210 size_t lba;
211 bool is_type1 = false;
212 int temp;
214 m68k_end_timeslice();
216 switch (addr) {
217 case WD2797_REG_COMMAND: // Command register
218 // write to command register clears interrupt request
219 ctx->irq = false;
221 // Is the drive ready?
222 if (ctx->disc_image == NULL) {
223 // No disc image, thus the drive is busy.
224 ctx->status = 0x80;
225 return;
226 }
228 // Handle Type 1 commands
229 switch (cmd) {
230 case CMD_RESTORE:
231 // Restore. Set track to 0 and throw an IRQ.
232 is_type1 = true;
233 ctx->track = 0;
234 break;
236 case CMD_SEEK:
237 // Seek. Seek to the track specced in the Data Register.
238 is_type1 = true;
239 if (ctx->data_reg < ctx->geom_tracks) {
240 ctx->track = ctx->data_reg;
241 } else {
242 // Seek error. :(
243 ctx->status = 0x10;
244 }
246 case CMD_STEP:
247 // TODO! deal with trk0!
248 // Need to keep a copy of the track register; when it hits 0, set the TRK0 flag.
249 is_type1 = true;
250 break;
252 case CMD_STEPIN:
253 case CMD_STEPOUT:
254 // TODO! deal with trk0!
255 // Need to keep a copy of the track register; when it hits 0, set the TRK0 flag.
256 if (cmd == CMD_STEPIN) {
257 ctx->last_step_dir = 1;
258 } else {
259 ctx->last_step_dir = -1;
260 }
261 is_type1 = true;
262 break;
264 case CMD_STEP_TU:
265 case CMD_STEPIN_TU:
266 case CMD_STEPOUT_TU:
267 // if this is a Step In or Step Out cmd, set the step-direction
268 if (cmd == CMD_STEPIN_TU) {
269 ctx->last_step_dir = 1;
270 } else if (cmd == CMD_STEPOUT_TU) {
271 ctx->last_step_dir = -1;
272 }
274 // Seek one step in the last direction used.
275 ctx->track += ctx->last_step_dir;
276 if (ctx->track < 0) ctx->track = 0;
277 if (ctx->track >= ctx->geom_tracks) {
278 // Seek past end of disc... that'll be a Seek Error then.
279 ctx->status = 0x10;
280 ctx->track = ctx->geom_tracks - 1;
281 }
282 is_type1 = true;
283 break;
285 default:
286 break;
287 }
289 if (is_type1) {
290 // Terminate any sector reads or writes
291 ctx->data_len = ctx->data_pos = 0;
293 // No DRQ bit for these commands.
294 ctx->cmd_has_drq = false;
296 // Type1 status byte...
297 ctx->status = 0;
298 // S7 = Not Ready. Command executed, therefore the drive was ready... :)
299 // S6 = Write Protect. TODO: add this
300 // S5 = Head Loaded. For certain emulation-related reasons, the heads are always loaded...
301 ctx->status |= 0x20;
302 // S4 = Seek Error. Not bloody likely if we got down here...!
303 // S3 = CRC Error. Not gonna happen on a disc image!
304 // S2 = Track 0
305 ctx->status |= (ctx->track == 0) ? 0x04 : 0x00;
306 // S1 = Index Pulse. TODO -- need periodics to emulate this
307 // S0 = Busy. We just exec'd the command, thus we're not busy.
308 // TODO: Set a timer for seeks, and ONLY clear BUSY when that timer expires. Need periodics for that.
310 // Set IRQ
311 ctx->irq = true;
312 return;
313 }
315 // That's the Type 1 (seek) commands sorted. Now for the others.
317 // All these commands return the DRQ bit...
318 ctx->cmd_has_drq = true;
320 // If drive isn't ready, then set status B7 and exit
321 if (ctx->disc_image == NULL) {
322 ctx->status = 0x80;
323 return;
324 }
326 // If this is a Write command, check write protect status too
327 // TODO!
328 if (false) {
329 // Write protected disc...
330 if ((cmd == CMD_WRITE_SECTOR) || (cmd == CMD_WRITE_SECTOR_MULTI) || (cmd == CMD_FORMAT_TRACK)) {
331 // Set Write Protect bit and bail.
332 ctx->status = 0x40;
334 // Set IRQ
335 ctx->irq = true;
337 return;
338 }
339 }
341 // Disc is ready to go. Parse the command word.
342 switch (cmd) {
343 case CMD_READ_ADDRESS:
344 // Read Address
345 ctx->head = (val & 0x02) ? 1 : 0;
347 // reset data pointers
348 ctx->data_pos = ctx->data_len = 0;
350 // load data buffer
351 ctx->data[ctx->data_len++] = ctx->track;
352 ctx->data[ctx->data_len++] = ctx->head;
353 ctx->data[ctx->data_len++] = ctx->sector;
354 switch (ctx->geom_secsz) {
355 case 128: ctx->data[ctx->data_len++] = 0; break;
356 case 256: ctx->data[ctx->data_len++] = 1; break;
357 case 512: ctx->data[ctx->data_len++] = 2; break;
358 case 1024: ctx->data[ctx->data_len++] = 3; break;
359 default: ctx->data[ctx->data_len++] = 0xFF; break; // TODO: deal with invalid values better
360 }
361 ctx->data[ctx->data_len++] = 0; // TODO: IDAM CRC!
362 ctx->data[ctx->data_len++] = 0;
364 ctx->status = 0;
365 // B6, B5 = 0
366 // B4 = Record Not Found. We're not going to see this... FIXME-not emulated
367 // B3 = CRC Error. Not possible.
368 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
369 // B1 = DRQ. Data request.
370 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
371 break;
373 case CMD_READ_SECTOR:
374 case CMD_READ_SECTOR_MULTI:
375 ctx->head = (val & 0x02) ? 1 : 0;
376 printf("WD279X: READ SECTOR cmd=%02X chs=%d:%d:%d\n", cmd, ctx->track, ctx->head, ctx->sector);
377 // Read Sector or Read Sector Multiple
378 // reset data pointers
379 ctx->data_pos = ctx->data_len = 0;
381 // Calculate number of sectors to read from disc
382 if (cmd == CMD_READ_SECTOR_MULTI)
383 temp = ctx->geom_spt;
384 else
385 temp = 1;
387 for (int i=0; i<temp; i++) {
388 // Calculate the LBA address of the required sector
389 lba = ((((ctx->track * ctx->geom_heads) + ctx->head) * ctx->geom_spt) + ((ctx->sector + i - 1) % ctx->geom_spt)) * ctx->geom_secsz;
390 printf("\tREAD lba = %lu\n", lba);
392 // Read the sector from the file
393 fseek(ctx->disc_image, lba, SEEK_SET);
394 ctx->data_len += fread(&ctx->data[ctx->data_len], 1, ctx->geom_secsz, ctx->disc_image);
395 printf("\tREAD len=%lu, pos=%lu, ssz=%d\n", ctx->data_len, ctx->data_pos, ctx->geom_secsz);
396 // TODO: check fread return value! if < secsz, BAIL! (call it a crc error or secnotfound maybe? also log to stderr)
397 }
399 ctx->status = 0;
400 // B6 = 0
401 // B5 = Record Type -- 1 = deleted, 0 = normal. We can't emulate anything but normal data blocks.
402 // B4 = Record Not Found. We're not going to see this... FIXME-not emulated
403 // B3 = CRC Error. Not possible.
404 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
405 // B1 = DRQ. Data request.
406 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
407 break;
409 case CMD_READ_TRACK:
410 // Read Track
411 // TODO! implement this
412 ctx->head = (val & 0x02) ? 1 : 0;
413 ctx->status = 0;
414 // B6, B5, B4, B3 = 0
415 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
416 // B1 = DRQ. Data request.
417 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
418 break;
420 case CMD_WRITE_SECTOR:
421 case CMD_WRITE_SECTOR_MULTI:
422 // Write Sector or Write Sector Multiple
424 ctx->head = (val & 0x02) ? 1 : 0;
425 // reset data pointers
426 ctx->data_pos = ctx->data_len = 0;
428 // TODO: set "write pending" flag, and write LBA, and go from there.
430 ctx->status = 0;
431 // B6 = Write Protect. FIXME -- emulate this!
432 // B5 = 0
433 // B4 = Record Not Found. We're not going to see this... FIXME-not emulated
434 // B3 = CRC Error. Not possible.
435 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
436 // B1 = DRQ. Data request.
437 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
438 break;
440 case CMD_FORMAT_TRACK:
441 // Write Track (aka Format Track)
442 ctx->head = (val & 0x02) ? 1 : 0;
443 ctx->status = 0;
444 // B6 = Write Protect. FIXME -- emulate this!
445 // B5, B4, B3 = 0
446 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
447 // B1 = DRQ. Data request.
448 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
449 break;
451 case CMD_FORCE_INTERRUPT:
452 // Force Interrupt...
453 // Terminates current operation and sends an interrupt
454 // TODO!
455 ctx->status = 0;
456 ctx->data_pos = ctx->data_len = 0;
457 // Set IRQ
458 ctx->irq = true;
459 break;
460 }
461 break;
463 case WD2797_REG_TRACK: // Track register
464 ctx->track = val;
465 break;
467 case WD2797_REG_SECTOR: // Sector register
468 ctx->sector = val;
469 break;
471 case WD2797_REG_DATA: // Data register
472 // Save the value written into the data register
473 ctx->data_reg = val;
475 // If we're processing a write command, and there's space in the
476 // buffer, allow the write.
477 if (ctx->data_pos < ctx->data_len) {
478 // set IRQ if this is the last data byte
479 if (ctx->data_pos == (ctx->data_len-1)) {
480 // Set IRQ
481 ctx->irq = true;
482 }
484 // store data byte and increment pointer
485 ctx->data[ctx->data_pos++] = val;
486 }
487 break;
488 }
489 }