Fri, 04 Mar 2011 00:44:36 +0000
more verbose bus error logging
1 #include <stdint.h>
2 #include <stdbool.h>
3 #include <malloc.h>
4 #include "musashi/m68k.h"
5 #include "wd279x.h"
7 #ifndef WD279X_DEBUG
8 #define NDEBUG
9 #endif
10 #include "utils.h"
12 /// WD2797 command constants
13 enum {
14 CMD_MASK = 0xF0, ///< Bit mask to detect command bits
15 CMD_RESTORE = 0x00, ///< Restore (recalibrate, seek to track 0)
16 CMD_SEEK = 0x10, ///< Seek to given track
17 CMD_STEP = 0x20, ///< Step
18 CMD_STEP_TU = 0x30, ///< Step and update track register
19 CMD_STEPIN = 0x40, ///< Step In
20 CMD_STEPIN_TU = 0x50, ///< Step In and update track register
21 CMD_STEPOUT = 0x60, ///< Step Out
22 CMD_STEPOUT_TU = 0x70, ///< Step Out and update track register
23 CMD_READ_SECTOR = 0x80, ///< Read Sector
24 CMD_READ_SECTOR_MULTI = 0x90, ///< Read Multiple Sectors
25 CMD_WRITE_SECTOR = 0xA0, ///< Write Sector
26 CMD_WRITE_SECTOR_MULTI = 0xB0, ///< Write Multiple Sectors
27 CMD_READ_ADDRESS = 0xC0, ///< Read Address (IDAM contents)
28 CMD_FORCE_INTERRUPT = 0xD0, ///< Force Interrupt
29 CMD_READ_TRACK = 0xE0, ///< Read Track
30 CMD_FORMAT_TRACK = 0xF0 ///< Format Track
31 };
34 void wd2797_init(WD2797_CTX *ctx)
35 {
36 // track, head and sector unknown
37 ctx->track = ctx->head = ctx->sector = 0;
39 // no IRQ pending
40 ctx->irq = false;
42 // no data available
43 ctx->data_pos = ctx->data_len = 0;
44 ctx->data = NULL;
46 // Status register clear, not busy; type1 command
47 ctx->status = 0;
48 ctx->cmd_has_drq = false;
50 // Clear data register
51 ctx->data_reg = 0;
53 // Last step direction = "towards zero"
54 ctx->last_step_dir = -1;
56 // No disc image loaded
57 ctx->disc_image = NULL;
58 ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = ctx->geom_tracks = 0;
59 }
62 void wd2797_reset(WD2797_CTX *ctx)
63 {
64 // track, head and sector unknown
65 ctx->track = ctx->head = ctx->sector = 0;
67 // no IRQ pending
68 ctx->irq = false;
70 // no data available
71 ctx->data_pos = ctx->data_len = 0;
73 // Status register clear, not busy
74 ctx->status = 0;
76 // Clear data register
77 ctx->data_reg = 0;
79 // Last step direction
80 ctx->last_step_dir = -1;
81 }
84 void wd2797_done(WD2797_CTX *ctx)
85 {
86 // Reset the WD2797
87 wd2797_reset(ctx);
89 // Free any allocated memory
90 if (ctx->data) {
91 free(ctx->data);
92 ctx->data = NULL;
93 }
94 }
97 bool wd2797_get_irq(WD2797_CTX *ctx)
98 {
99 return ctx->irq;
100 }
103 bool wd2797_get_drq(WD2797_CTX *ctx)
104 {
105 return (ctx->data_pos < ctx->data_len);
106 }
109 WD2797_ERR wd2797_load(WD2797_CTX *ctx, FILE *fp, int secsz, int spt, int heads)
110 {
111 size_t filesize;
113 // Start by finding out how big the image file is
114 fseek(fp, 0, SEEK_END);
115 filesize = ftell(fp);
116 fseek(fp, 0, SEEK_SET);
118 // Now figure out how many tracks it contains
119 int tracks = filesize / secsz / spt / heads;
120 // Confirm...
121 if (tracks < 1) {
122 return WD2797_ERR_BAD_GEOM;
123 }
125 // Allocate enough memory to store one disc track
126 if (ctx->data) {
127 free(ctx->data);
128 }
129 ctx->data = malloc(secsz * spt);
130 if (!ctx->data)
131 return WD2797_ERR_NO_MEMORY;
133 // Load the image and the geometry data
134 ctx->disc_image = fp;
135 ctx->geom_tracks = tracks;
136 ctx->geom_secsz = secsz;
137 ctx->geom_heads = heads;
138 ctx->geom_spt = spt;
140 return WD2797_ERR_OK;
141 }
144 void wd2797_unload(WD2797_CTX *ctx)
145 {
146 // Free memory buffer
147 if (ctx->data) {
148 free(ctx->data);
149 ctx->data = NULL;
150 }
152 // Clear file pointer
153 ctx->disc_image = NULL;
155 // Clear the disc geometry
156 ctx->geom_tracks = ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = 0;
157 }
160 uint8_t wd2797_read_reg(WD2797_CTX *ctx, uint8_t addr)
161 {
162 uint8_t temp = 0;
164 switch (addr & 0x03) {
165 case WD2797_REG_STATUS: // Status register
166 // Read from status register clears IRQ
167 ctx->irq = false;
169 // Get current status flags (set by last command)
170 // DRQ bit
171 if (ctx->cmd_has_drq) {
172 temp = ctx->status & ~0x03;
173 temp |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
174 LOG("\tWDFDC rd sr, has drq, pos=%lu len=%lu, sr=0x%02X", ctx->data_pos, ctx->data_len, temp);
175 } else {
176 temp = ctx->status & ~0x01;
177 }
178 // FDC is busy if there is still data in the buffer
179 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!
180 // TODO: also if seek delay / read delay hasn't passed (but that's for later)
181 return temp;
183 case WD2797_REG_TRACK: // Track register
184 return ctx->track;
186 case WD2797_REG_SECTOR: // Sector register
187 return ctx->sector;
189 case WD2797_REG_DATA: // Data register
190 // If there's data in the buffer, return it. Otherwise return 0xFF.
191 if (ctx->data_pos < ctx->data_len) {
192 // set IRQ if this is the last data byte
193 if (ctx->data_pos == (ctx->data_len-1)) {
194 // Set IRQ
195 ctx->irq = true;
196 }
197 // return data byte and increment pointer
198 return ctx->data[ctx->data_pos++];
199 } else {
200 // command finished
201 return 0xff;
202 }
204 default:
205 // shut up annoying compilers which don't recognise unreachable code when they see it
206 // (here's looking at you, gcc!)
207 return 0xff;
208 }
209 }
212 void wd2797_write_reg(WD2797_CTX *ctx, uint8_t addr, uint8_t val)
213 {
214 uint8_t cmd = val & CMD_MASK;
215 size_t lba;
216 bool is_type1 = false;
217 int temp;
219 m68k_end_timeslice();
221 switch (addr) {
222 case WD2797_REG_COMMAND: // Command register
223 // write to command register clears interrupt request
224 ctx->irq = false;
226 // Is the drive ready?
227 if (ctx->disc_image == NULL) {
228 // No disc image, thus the drive is busy.
229 ctx->status = 0x80;
230 return;
231 }
233 // Handle Type 1 commands
234 switch (cmd) {
235 case CMD_RESTORE:
236 // Restore. Set track to 0 and throw an IRQ.
237 is_type1 = true;
238 ctx->track = 0;
239 break;
241 case CMD_SEEK:
242 // Seek. Seek to the track specced in the Data Register.
243 is_type1 = true;
244 if (ctx->data_reg < ctx->geom_tracks) {
245 ctx->track = ctx->data_reg;
246 } else {
247 // Seek error. :(
248 ctx->status = 0x10;
249 }
251 case CMD_STEP:
252 // TODO! deal with trk0!
253 // Need to keep a copy of the track register; when it hits 0, set the TRK0 flag.
254 is_type1 = true;
255 break;
257 case CMD_STEPIN:
258 case CMD_STEPOUT:
259 // TODO! deal with trk0!
260 // Need to keep a copy of the track register; when it hits 0, set the TRK0 flag.
261 if (cmd == CMD_STEPIN) {
262 ctx->last_step_dir = 1;
263 } else {
264 ctx->last_step_dir = -1;
265 }
266 is_type1 = true;
267 break;
269 case CMD_STEP_TU:
270 case CMD_STEPIN_TU:
271 case CMD_STEPOUT_TU:
272 // if this is a Step In or Step Out cmd, set the step-direction
273 if (cmd == CMD_STEPIN_TU) {
274 ctx->last_step_dir = 1;
275 } else if (cmd == CMD_STEPOUT_TU) {
276 ctx->last_step_dir = -1;
277 }
279 // Seek one step in the last direction used.
280 ctx->track += ctx->last_step_dir;
281 if (ctx->track < 0) ctx->track = 0;
282 if (ctx->track >= ctx->geom_tracks) {
283 // Seek past end of disc... that'll be a Seek Error then.
284 ctx->status = 0x10;
285 ctx->track = ctx->geom_tracks - 1;
286 }
287 is_type1 = true;
288 break;
290 default:
291 break;
292 }
294 if (is_type1) {
295 // Terminate any sector reads or writes
296 ctx->data_len = ctx->data_pos = 0;
298 // No DRQ bit for these commands.
299 ctx->cmd_has_drq = false;
301 // Type1 status byte...
302 ctx->status = 0;
303 // S7 = Not Ready. Command executed, therefore the drive was ready... :)
304 // S6 = Write Protect. TODO: add this
305 // S5 = Head Loaded. For certain emulation-related reasons, the heads are always loaded...
306 ctx->status |= 0x20;
307 // S4 = Seek Error. Not bloody likely if we got down here...!
308 // S3 = CRC Error. Not gonna happen on a disc image!
309 // S2 = Track 0
310 ctx->status |= (ctx->track == 0) ? 0x04 : 0x00;
311 // S1 = Index Pulse. TODO -- need periodics to emulate this
312 // S0 = Busy. We just exec'd the command, thus we're not busy.
313 // TODO: Set a timer for seeks, and ONLY clear BUSY when that timer expires. Need periodics for that.
315 // Set IRQ
316 ctx->irq = true;
317 return;
318 }
320 // That's the Type 1 (seek) commands sorted. Now for the others.
322 // All these commands return the DRQ bit...
323 ctx->cmd_has_drq = true;
325 // If drive isn't ready, then set status B7 and exit
326 if (ctx->disc_image == NULL) {
327 ctx->status = 0x80;
328 return;
329 }
331 // If this is a Write command, check write protect status too
332 // TODO!
333 if (false) {
334 // Write protected disc...
335 if ((cmd == CMD_WRITE_SECTOR) || (cmd == CMD_WRITE_SECTOR_MULTI) || (cmd == CMD_FORMAT_TRACK)) {
336 // Set Write Protect bit and bail.
337 ctx->status = 0x40;
339 // Set IRQ
340 ctx->irq = true;
342 return;
343 }
344 }
346 // Disc is ready to go. Parse the command word.
347 switch (cmd) {
348 case CMD_READ_ADDRESS:
349 // Read Address
350 ctx->head = (val & 0x02) ? 1 : 0;
352 // reset data pointers
353 ctx->data_pos = ctx->data_len = 0;
355 // load data buffer
356 ctx->data[ctx->data_len++] = ctx->track;
357 ctx->data[ctx->data_len++] = ctx->head;
358 ctx->data[ctx->data_len++] = ctx->sector;
359 switch (ctx->geom_secsz) {
360 case 128: ctx->data[ctx->data_len++] = 0; break;
361 case 256: ctx->data[ctx->data_len++] = 1; break;
362 case 512: ctx->data[ctx->data_len++] = 2; break;
363 case 1024: ctx->data[ctx->data_len++] = 3; break;
364 default: ctx->data[ctx->data_len++] = 0xFF; break; // TODO: deal with invalid values better
365 }
366 ctx->data[ctx->data_len++] = 0; // TODO: IDAM CRC!
367 ctx->data[ctx->data_len++] = 0;
369 ctx->status = 0;
370 // B6, B5 = 0
371 // B4 = Record Not Found. We're not going to see this... FIXME-not emulated
372 // B3 = CRC Error. Not possible.
373 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
374 // B1 = DRQ. Data request.
375 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
376 break;
378 case CMD_READ_SECTOR:
379 case CMD_READ_SECTOR_MULTI:
380 ctx->head = (val & 0x02) ? 1 : 0;
381 LOG("WD279X: READ SECTOR cmd=%02X chs=%d:%d:%d", cmd, ctx->track, ctx->head, ctx->sector);
382 // Read Sector or Read Sector Multiple
384 // Check to see if the cyl, hd and sec are valid
385 if ((ctx->track > (ctx->geom_tracks-1)) || (ctx->head > (ctx->geom_heads-1)) || (ctx->sector > ctx->geom_spt) || (ctx->sector == 0)) {
386 LOG("*** WD2797 ALERT: CHS parameter limit exceeded! CHS=%d:%d:%d, maxCHS=%d:%d:%d",
387 ctx->track, ctx->head, ctx->sector,
388 ctx->geom_tracks-1, ctx->geom_heads-1, ctx->geom_spt);
389 // CHS parameters exceed limits
390 ctx->status = 0x10; // Record Not Found
391 break;
392 // Set IRQ
393 ctx->irq = true;
394 }
396 // reset data pointers
397 ctx->data_pos = ctx->data_len = 0;
399 // Calculate number of sectors to read from disc
400 if (cmd == CMD_READ_SECTOR_MULTI)
401 temp = ctx->geom_spt;
402 else
403 temp = 1;
405 for (int i=0; i<temp; i++) {
406 // Calculate the LBA address of the required sector
407 // LBA = (C * nHeads * nSectors) + (H * nSectors) + S - 1
408 lba = (((ctx->track * ctx->geom_heads * ctx->geom_spt) + (ctx->head * ctx->geom_spt) + ctx->sector) + i) - 1;
409 // convert LBA to byte address
410 lba *= ctx->geom_secsz;
411 LOG("\tREAD lba = %lu", lba);
413 // Read the sector from the file
414 fseek(ctx->disc_image, lba, SEEK_SET);
415 // TODO: check fread return value! if < secsz, BAIL! (call it a crc error or secnotfound maybe? also log to stderr)
416 ctx->data_len += fread(&ctx->data[ctx->data_len], 1, ctx->geom_secsz, ctx->disc_image);
417 LOG("\tREAD len=%lu, pos=%lu, ssz=%d", ctx->data_len, ctx->data_pos, ctx->geom_secsz);
418 }
420 ctx->status = 0;
421 // B6 = 0
422 // B5 = Record Type -- 1 = deleted, 0 = normal. We can't emulate anything but normal data blocks.
423 // B4 = Record Not Found. Basically, the CHS parameters are bullcrap.
424 // B3 = CRC Error. Not possible.
425 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
426 // B1 = DRQ. Data request.
427 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
428 break;
430 case CMD_READ_TRACK:
431 // Read Track
432 // TODO! implement this
433 ctx->head = (val & 0x02) ? 1 : 0;
434 ctx->status = 0;
435 // B6, B5, B4, B3 = 0
436 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
437 // B1 = DRQ. Data request.
438 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
439 break;
441 case CMD_WRITE_SECTOR:
442 case CMD_WRITE_SECTOR_MULTI:
443 // Write Sector or Write Sector Multiple
445 ctx->head = (val & 0x02) ? 1 : 0;
446 // reset data pointers
447 ctx->data_pos = ctx->data_len = 0;
449 // TODO: set "write pending" flag, and write LBA, and go from there.
451 ctx->status = 0;
452 // B6 = Write Protect. FIXME -- emulate this!
453 // B5 = 0
454 // B4 = Record Not Found. We're not going to see this... FIXME-not emulated
455 // B3 = CRC Error. Not possible.
456 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
457 // B1 = DRQ. Data request.
458 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
459 break;
461 case CMD_FORMAT_TRACK:
462 // Write Track (aka Format Track)
463 ctx->head = (val & 0x02) ? 1 : 0;
464 ctx->status = 0;
465 // B6 = Write Protect. FIXME -- emulate this!
466 // B5, B4, B3 = 0
467 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
468 // B1 = DRQ. Data request.
469 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
470 break;
472 case CMD_FORCE_INTERRUPT:
473 // Force Interrupt...
474 // Terminates current operation and sends an interrupt
475 // TODO!
476 ctx->status = 0;
477 ctx->data_pos = ctx->data_len = 0;
478 // Set IRQ
479 ctx->irq = true;
480 break;
481 }
482 break;
484 case WD2797_REG_TRACK: // Track register
485 ctx->track = val;
486 break;
488 case WD2797_REG_SECTOR: // Sector register
489 ctx->sector = val;
490 break;
492 case WD2797_REG_DATA: // Data register
493 // Save the value written into the data register
494 ctx->data_reg = val;
496 // If we're processing a write command, and there's space in the
497 // buffer, allow the write.
498 if (ctx->data_pos < ctx->data_len) {
499 // set IRQ if this is the last data byte
500 if (ctx->data_pos == (ctx->data_len-1)) {
501 // Set IRQ
502 ctx->irq = true;
503 }
505 // store data byte and increment pointer
506 ctx->data[ctx->data_pos++] = val;
507 }
508 break;
509 }
510 }