Wed, 29 Dec 2010 01:38:54 +0000
add first cut keyboard driver
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->irql = ctx->irqe = false;
42 // no data available
43 ctx->data_pos = ctx->data_len = 0;
44 ctx->data = NULL;
46 // Status register clear, not busy
47 ctx->status = 0;
49 // Clear data register
50 ctx->data_reg = 0;
52 // Last step direction
53 ctx->last_step_dir = -1;
55 // No disc image loaded
56 ctx->disc_image = NULL;
57 ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = ctx->geom_tracks = 0;
58 }
61 void wd2797_reset(WD2797_CTX *ctx)
62 {
63 // track, head and sector unknown
64 ctx->track = ctx->head = ctx->sector = 0;
66 // no IRQ pending
67 ctx->irql = ctx->irqe = false;
69 // no data available
70 ctx->data_pos = ctx->data_len = 0;
72 // Status register clear, not busy
73 ctx->status = 0;
75 // Clear data register
76 ctx->data_reg = 0;
78 // Last step direction
79 ctx->last_step_dir = -1;
80 }
83 void wd2797_done(WD2797_CTX *ctx)
84 {
85 // Reset the WD2797
86 wd2797_reset(ctx);
88 // Free any allocated memory
89 if (ctx->data) {
90 free(ctx->data);
91 ctx->data = NULL;
92 }
93 }
96 bool wd2797_get_irq(WD2797_CTX *ctx)
97 {
98 // If an IRQ is pending, clear it and return true, otherwise return false
99 if (ctx->irqe) {
100 ctx->irqe = false;
101 return true;
102 } else {
103 return false;
104 }
105 }
108 bool wd2797_get_drq(WD2797_CTX *ctx)
109 {
110 return (ctx->data_pos < ctx->data_len);
111 }
114 WD2797_ERR wd2797_load(WD2797_CTX *ctx, FILE *fp, int secsz, int spt, int heads)
115 {
116 size_t filesize;
118 // Start by finding out how big the image file is
119 fseek(fp, 0, SEEK_END);
120 filesize = ftell(fp);
121 fseek(fp, 0, SEEK_SET);
123 // Now figure out how many tracks it contains
124 int tracks = filesize / secsz / spt / heads;
125 // Confirm...
126 if (tracks < 1) {
127 return WD2797_ERR_BAD_GEOM;
128 }
130 // Allocate enough memory to store one disc track
131 if (ctx->data) {
132 free(ctx->data);
133 }
134 ctx->data = malloc(secsz * spt);
135 if (!ctx->data)
136 return WD2797_ERR_NO_MEMORY;
138 // Load the image and the geometry data
139 ctx->disc_image = fp;
140 ctx->geom_tracks = tracks;
141 ctx->geom_secsz = secsz;
142 ctx->geom_heads = heads;
143 ctx->geom_spt = spt;
145 return WD2797_ERR_OK;
146 }
149 void wd2797_unload(WD2797_CTX *ctx)
150 {
151 // Free memory buffer
152 if (ctx->data) {
153 free(ctx->data);
154 ctx->data = NULL;
155 }
157 // Clear file pointer
158 ctx->disc_image = NULL;
160 // Clear the disc geometry
161 ctx->geom_tracks = ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = 0;
162 }
165 uint8_t wd2797_read_reg(WD2797_CTX *ctx, uint8_t addr)
166 {
167 uint8_t temp = 0;
169 switch (addr & 0x03) {
170 case WD2797_REG_STATUS: // Status register
171 // Read from status register clears IRQ
172 ctx->irql = false;
173 ctx->irqe = false;
175 // Get current status flags (set by last command)
176 // DRQ bit
177 if (ctx->cmd_has_drq) {
178 temp = ctx->status & ~0x03;
179 temp |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
180 LOG("\tWDFDC rd sr, has drq, pos=%lu len=%lu, sr=0x%02X", ctx->data_pos, ctx->data_len, temp);
181 } else {
182 temp = ctx->status & ~0x01;
183 }
184 // FDC is busy if there is still data in the buffer
185 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!
186 // TODO: also if seek delay / read delay hasn't passed (but that's for later)
187 return temp;
189 case WD2797_REG_TRACK: // Track register
190 return ctx->track;
192 case WD2797_REG_SECTOR: // Sector register
193 return ctx->sector;
195 case WD2797_REG_DATA: // Data register
196 // If there's data in the buffer, return it. Otherwise return 0xFF.
197 if (ctx->data_pos < ctx->data_len) {
198 // set IRQ if this is the last data byte
199 if (ctx->data_pos == (ctx->data_len-1)) {
200 // Set IRQ only if IRQL has been cleared (no pending IRQs)
201 ctx->irqe = ctx->irql ? ctx->irqe : true;
202 ctx->irql = true;
203 }
204 // return data byte and increment pointer
205 return ctx->data[ctx->data_pos++];
206 } else {
207 // command finished
208 return 0xff;
209 }
211 default:
212 // shut up annoying compilers which don't recognise unreachable code when they see it
213 // (here's looking at you, gcc!)
214 return 0xff;
215 }
216 }
219 void wd2797_write_reg(WD2797_CTX *ctx, uint8_t addr, uint8_t val)
220 {
221 uint8_t cmd = val & CMD_MASK;
222 size_t lba;
223 bool is_type1 = false;
224 int temp;
226 m68k_end_timeslice();
228 switch (addr) {
229 case WD2797_REG_COMMAND: // Command register
230 // write to command register clears interrupt request
231 ctx->irql = false;
233 // Is the drive ready?
234 if (ctx->disc_image == NULL) {
235 // No disc image, thus the drive is busy.
236 ctx->status = 0x80;
237 return;
238 }
240 // Handle Type 1 commands
241 switch (cmd) {
242 case CMD_RESTORE:
243 // Restore. Set track to 0 and throw an IRQ.
244 is_type1 = true;
245 ctx->track = 0;
246 break;
248 case CMD_SEEK:
249 // Seek. Seek to the track specced in the Data Register.
250 is_type1 = true;
251 if (ctx->data_reg < ctx->geom_tracks) {
252 ctx->track = ctx->data_reg;
253 } else {
254 // Seek error. :(
255 ctx->status = 0x10;
256 }
258 case CMD_STEP:
259 // TODO! deal with trk0!
260 // Need to keep a copy of the track register; when it hits 0, set the TRK0 flag.
261 is_type1 = true;
262 break;
264 case CMD_STEPIN:
265 case CMD_STEPOUT:
266 // TODO! deal with trk0!
267 // Need to keep a copy of the track register; when it hits 0, set the TRK0 flag.
268 if (cmd == CMD_STEPIN) {
269 ctx->last_step_dir = 1;
270 } else {
271 ctx->last_step_dir = -1;
272 }
273 is_type1 = true;
274 break;
276 case CMD_STEP_TU:
277 case CMD_STEPIN_TU:
278 case CMD_STEPOUT_TU:
279 // if this is a Step In or Step Out cmd, set the step-direction
280 if (cmd == CMD_STEPIN_TU) {
281 ctx->last_step_dir = 1;
282 } else if (cmd == CMD_STEPOUT_TU) {
283 ctx->last_step_dir = -1;
284 }
286 // Seek one step in the last direction used.
287 ctx->track += ctx->last_step_dir;
288 if (ctx->track < 0) ctx->track = 0;
289 if (ctx->track >= ctx->geom_tracks) {
290 // Seek past end of disc... that'll be a Seek Error then.
291 ctx->status = 0x10;
292 ctx->track = ctx->geom_tracks - 1;
293 }
294 is_type1 = true;
295 break;
297 default:
298 break;
299 }
301 if (is_type1) {
302 // Terminate any sector reads or writes
303 ctx->data_len = ctx->data_pos = 0;
305 // No DRQ bit for these commands.
306 ctx->cmd_has_drq = false;
308 // Type1 status byte...
309 ctx->status = 0;
310 // S7 = Not Ready. Command executed, therefore the drive was ready... :)
311 // S6 = Write Protect. TODO: add this
312 // S5 = Head Loaded. For certain emulation-related reasons, the heads are always loaded...
313 ctx->status |= 0x20;
314 // S4 = Seek Error. Not bloody likely if we got down here...!
315 // S3 = CRC Error. Not gonna happen on a disc image!
316 // S2 = Track 0
317 ctx->status |= (ctx->track == 0) ? 0x04 : 0x00;
318 // S1 = Index Pulse. TODO -- need periodics to emulate this
319 // S0 = Busy. We just exec'd the command, thus we're not busy.
320 // TODO: Set a timer for seeks, and ONLY clear BUSY when that timer expires. Need periodics for that.
322 // Set IRQ only if IRQL has been cleared (no pending IRQs)
323 ctx->irqe = ctx->irql ? ctx->irqe : true;
324 ctx->irql = true;
325 return;
326 }
328 // That's the Type 1 (seek) commands sorted. Now for the others.
330 // All these commands return the DRQ bit...
331 ctx->cmd_has_drq = true;
333 // If drive isn't ready, then set status B7 and exit
334 if (ctx->disc_image == NULL) {
335 ctx->status = 0x80;
336 return;
337 }
339 // If this is a Write command, check write protect status too
340 // TODO!
341 if (false) {
342 // Write protected disc...
343 if ((cmd == CMD_WRITE_SECTOR) || (cmd == CMD_WRITE_SECTOR_MULTI) || (cmd == CMD_FORMAT_TRACK)) {
344 // Set Write Protect bit and bail.
345 ctx->status = 0x40;
347 // Set IRQ only if IRQL has been cleared (no pending IRQs)
348 ctx->irqe = ctx->irql ? ctx->irqe : true;
349 ctx->irql = true;
351 return;
352 }
353 }
355 // Disc is ready to go. Parse the command word.
356 switch (cmd) {
357 case CMD_READ_ADDRESS:
358 // Read Address
359 ctx->head = (val & 0x02) ? 1 : 0;
361 // reset data pointers
362 ctx->data_pos = ctx->data_len = 0;
364 // load data buffer
365 ctx->data[ctx->data_len++] = ctx->track;
366 ctx->data[ctx->data_len++] = ctx->head;
367 ctx->data[ctx->data_len++] = ctx->sector;
368 switch (ctx->geom_secsz) {
369 case 128: ctx->data[ctx->data_len++] = 0; break;
370 case 256: ctx->data[ctx->data_len++] = 1; break;
371 case 512: ctx->data[ctx->data_len++] = 2; break;
372 case 1024: ctx->data[ctx->data_len++] = 3; break;
373 default: ctx->data[ctx->data_len++] = 0xFF; break; // TODO: deal with invalid values better
374 }
375 ctx->data[ctx->data_len++] = 0; // TODO: IDAM CRC!
376 ctx->data[ctx->data_len++] = 0;
378 ctx->status = 0;
379 // B6, B5 = 0
380 // B4 = Record Not Found. We're not going to see this... FIXME-not emulated
381 // B3 = CRC Error. Not possible.
382 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
383 // B1 = DRQ. Data request.
384 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
385 break;
387 case CMD_READ_SECTOR:
388 case CMD_READ_SECTOR_MULTI:
389 ctx->head = (val & 0x02) ? 1 : 0;
390 LOG("WD279X: READ SECTOR cmd=%02X chs=%d:%d:%d", cmd, ctx->track, ctx->head, ctx->sector);
391 // Read Sector or Read Sector Multiple
393 // Check to see if the cyl, hd and sec are valid
394 if ((ctx->track > (ctx->geom_tracks-1)) || (ctx->head > (ctx->geom_heads-1)) || (ctx->sector > ctx->geom_spt) || (ctx->sector == 0)) {
395 LOG("*** WD2797 ALERT: CHS parameter limit exceeded! CHS=%d:%d:%d, maxCHS=%d:%d:%d",
396 ctx->track, ctx->head, ctx->sector,
397 ctx->geom_tracks-1, ctx->geom_heads-1, ctx->geom_spt);
398 // CHS parameters exceed limits
399 ctx->status = 0x10; // Record Not Found
400 break;
401 // Set IRQ only if IRQL has been cleared (no pending IRQs)
402 ctx->irqe = ctx->irql ? ctx->irqe : true;
403 ctx->irql = true;
404 }
406 // reset data pointers
407 ctx->data_pos = ctx->data_len = 0;
409 // Calculate number of sectors to read from disc
410 if (cmd == CMD_READ_SECTOR_MULTI)
411 temp = ctx->geom_spt;
412 else
413 temp = 1;
415 for (int i=0; i<temp; i++) {
416 // Calculate the LBA address of the required sector
417 // LBA = (C * nHeads * nSectors) + (H * nSectors) + S - 1
418 lba = (((ctx->track * ctx->geom_heads * ctx->geom_spt) + (ctx->head * ctx->geom_spt) + ctx->sector) + i) - 1;
419 // convert LBA to byte address
420 lba *= ctx->geom_secsz;
421 LOG("\tREAD lba = %lu", lba);
423 // Read the sector from the file
424 fseek(ctx->disc_image, lba, SEEK_SET);
425 // TODO: check fread return value! if < secsz, BAIL! (call it a crc error or secnotfound maybe? also log to stderr)
426 ctx->data_len += fread(&ctx->data[ctx->data_len], 1, ctx->geom_secsz, ctx->disc_image);
427 LOG("\tREAD len=%lu, pos=%lu, ssz=%d", ctx->data_len, ctx->data_pos, ctx->geom_secsz);
428 }
430 ctx->status = 0;
431 // B6 = 0
432 // B5 = Record Type -- 1 = deleted, 0 = normal. We can't emulate anything but normal data blocks.
433 // B4 = Record Not Found. Basically, the CHS parameters are bullcrap.
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_READ_TRACK:
441 // Read Track
442 // TODO! implement this
443 ctx->head = (val & 0x02) ? 1 : 0;
444 ctx->status = 0;
445 // B6, 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_WRITE_SECTOR:
452 case CMD_WRITE_SECTOR_MULTI:
453 // Write Sector or Write Sector Multiple
455 ctx->head = (val & 0x02) ? 1 : 0;
456 // reset data pointers
457 ctx->data_pos = ctx->data_len = 0;
459 // TODO: set "write pending" flag, and write LBA, and go from there.
461 ctx->status = 0;
462 // B6 = Write Protect. FIXME -- emulate this!
463 // B5 = 0
464 // B4 = Record Not Found. We're not going to see this... FIXME-not emulated
465 // B3 = CRC Error. Not possible.
466 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
467 // B1 = DRQ. Data request.
468 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
469 break;
471 case CMD_FORMAT_TRACK:
472 // Write Track (aka Format Track)
473 ctx->head = (val & 0x02) ? 1 : 0;
474 ctx->status = 0;
475 // B6 = Write Protect. FIXME -- emulate this!
476 // B5, B4, B3 = 0
477 // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated
478 // B1 = DRQ. Data request.
479 ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00;
480 break;
482 case CMD_FORCE_INTERRUPT:
483 // Force Interrupt...
484 // Terminates current operation and sends an interrupt
485 // TODO!
486 ctx->status = 0;
487 ctx->data_pos = ctx->data_len = 0;
488 // Set IRQ only if IRQL has been cleared (no pending IRQs)
489 ctx->irqe = ctx->irql ? ctx->irqe : true;
490 ctx->irql = true;
491 break;
492 }
493 break;
495 case WD2797_REG_TRACK: // Track register
496 ctx->track = val;
497 break;
499 case WD2797_REG_SECTOR: // Sector register
500 ctx->sector = val;
501 break;
503 case WD2797_REG_DATA: // Data register
504 // Save the value written into the data register
505 ctx->data_reg = val;
507 // If we're processing a write command, and there's space in the
508 // buffer, allow the write.
509 if (ctx->data_pos < ctx->data_len) {
510 // set IRQ if this is the last data byte
511 if (ctx->data_pos == (ctx->data_len-1)) {
512 // Set IRQ only if IRQL has been cleared (no pending IRQs)
513 ctx->irqe = ctx->irql ? ctx->irqe : true;
514 ctx->irql = true;
515 }
517 // store data byte and increment pointer
518 ctx->data[ctx->data_pos++] = val;
519 }
520 break;
521 }
522 }