First Commit of my working state
[simh.git] / AltairZ80 / wd179x.c
1 /*************************************************************************
2 * *
3 * $Id: wd179x.c 1907 2008-05-21 07:04:17Z hharte $ *
4 * *
5 * Copyright (c) 2007-2008 Howard M. Harte. *
6 * http://www.hartetec.com *
7 * *
8 * Permission is hereby granted, free of charge, to any person obtaining *
9 * a copy of this software and associated documentation files (the *
10 * "Software"), to deal in the Software without restriction, including *
11 * without limitation the rights to use, copy, modify, merge, publish, *
12 * distribute, sublicense, and/or sell copies of the Software, and to *
13 * permit persons to whom the Software is furnished to do so, subject to *
14 * the following conditions: *
15 * *
16 * The above copyright notice and this permission notice shall be *
17 * included in all copies or substantial portions of the Software. *
18 * *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND *
22 * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY *
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, *
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE *
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
26 * *
27 * Except as contained in this notice, the name of Howard M. Harte shall *
28 * not be used in advertising or otherwise to promote the sale, use or *
29 * other dealings in this Software without prior written authorization *
30 * Howard M. Harte. *
31 * *
32 * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. *
33 * *
34 * Module Description: *
35 * Generic WD179X Disk Controller module for SIMH. *
36 * *
37 * Environment: *
38 * User mode only *
39 * *
40 *************************************************************************/
41
42 /*#define DBG_MSG */
43
44 #include "altairz80_defs.h"
45
46 #if defined (_WIN32)
47 #include <windows.h>
48 #endif
49
50 #include "sim_imd.h"
51 #include "wd179x.h"
52
53 #ifdef DBG_MSG
54 #define DBG_PRINT(args) printf args
55 #else
56 #define DBG_PRINT(args)
57 #endif
58
59 #define SEEK_MSG 0x01
60 #define CMD_MSG 0x04
61 #define RD_DATA_MSG 0x08
62 #define WR_DATA_MSG 0x10
63 #define STATUS_MSG 0x20
64 #define VERBOSE_MSG 0x80
65
66 #define WD179X_MAX_DRIVES 4
67 #define WD179X_SECTOR_LEN 8192
68
69 #define CMD_PHASE 0
70 #define EXEC_PHASE 1
71 #define DATA_PHASE 2
72
73 /* Status Bits for Type I Commands */
74 #define WD179X_STAT_NOT_READY (1 << 7)
75 #define WD179X_STAT_WPROT (1 << 6)
76 #define WD179X_STAT_HLD (1 << 5)
77 #define WD179X_STAT_SEEK_ERROR (1 << 4)
78 #define WD179X_STAT_CRC_ERROR (1 << 3)
79 #define WD179X_STAT_TRACK0 (1 << 2)
80 #define WD179X_STAT_INDEX (1 << 1)
81 #define WD179X_STAT_BUSY (1 << 0)
82
83 /* Status Bits for Type II, III Commands */
84 #define WD179X_STAT_REC_TYPE (1 << 5)
85 #define WD179X_STAT_NOT_FOUND (1 << 4)
86 #define WD179X_STAT_LOST_DATA (1 << 2)
87 #define WD179X_STAT_DRQ (1 << 1)
88
89
90 typedef union {
91 uint8 raw[WD179X_SECTOR_LEN];
92 } SECTOR_FORMAT;
93
94 typedef struct {
95 UNIT *uptr;
96 DISK_INFO *imd;
97 uint8 ntracks; /* number of tracks */
98 uint8 nheads; /* number of heads */
99 uint32 sectsize; /* sector size, not including pre/postamble */
100 uint8 track; /* Current Track */
101 uint8 ready; /* Is drive ready? */
102 } WD179X_DRIVE_INFO;
103
104 typedef struct {
105 PNP_INFO pnp; /* Plug-n-Play Information */
106 uint8 intrq; /* WD179X Interrupt Request Output (EOJ) */
107 uint8 hld; /* WD179X Head Load Output */
108 uint8 drq; /* WD179X DMA Request Output */
109 uint8 ddens; /* WD179X Double-Density Input */
110 uint8 fdc_head; /* H Head Number */
111 uint8 sel_drive; /* Currently selected drive */
112 uint8 drivetype; /* 8 or 5 depending on disk type. */
113 uint8 fdc_status; /* WD179X Status Register */
114 uint8 verify; /* WD179X Type 1 command Verify flag */
115 uint8 fdc_data; /* WD179X Data Register */
116 uint8 fdc_read; /* TRUE when reading */
117 uint8 fdc_write; /* TRUE when writing */
118 uint8 fdc_read_addr; /* TRUE when READ ADDRESS command is in progress */
119 uint8 fdc_multiple; /* TRUE for multi-sector read/write */
120 uint16 fdc_datacount; /* Read or Write data remaining transfer length */
121 uint16 fdc_dataindex; /* index of current byte in sector data */
122 uint8 index_pulse_wait; /* TRUE if waiting for interrupt on next index pulse. */
123 uint8 fdc_sector; /* R Record (Sector) */
124 uint8 fdc_sec_len; /* N Sector Length */
125 int8 step_dir;
126 WD179X_DRIVE_INFO drive[WD179X_MAX_DRIVES];
127 } WD179X_INFO;
128
129 static SECTOR_FORMAT sdata;
130 extern uint32 PCX;
131 extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc);
132 extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc);
133 extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type,
134 int32 (*routine)(const int32, const int32, const int32), uint8 unmap);
135
136 t_stat wd179x_svc (UNIT *uptr);
137
138 /* These are needed for DMA. PIO Mode has not been implemented yet. */
139 extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value);
140 extern uint8 GetBYTEWrapper(const uint32 Addr);
141 static uint8 Do1793Command(uint8 cCommand);
142
143 #define UNIT_V_WD179X_WLK (UNIT_V_UF + 0) /* write locked */
144 #define UNIT_WD179X_WLK (1 << UNIT_V_WD179X_WLK)
145 #define UNIT_V_WD179X_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
146 #define UNIT_WD179X_VERBOSE (1 << UNIT_V_WD179X_VERBOSE)
147 #define WD179X_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */
148 #define WD179X_CAPACITY_SSSD (77*1*26*128) /* Single-sided Single Density IBM Diskette1 */
149 #define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */
150 #define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */
151 #define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */
152
153 /* WD179X Commands */
154 #define WD179X_RESTORE 0x00 /* Type I */
155 #define WD179X_SEEK 0x10 /* Type I */
156 #define WD179X_STEP 0x20 /* Type I */
157 #define WD179X_STEP_U 0x30 /* Type I */
158 #define WD179X_STEP_IN 0x40 /* Type I */
159 #define WD179X_STEP_IN_U 0x50 /* Type I */
160 #define WD179X_STEP_OUT 0x60 /* Type I */
161 #define WD179X_STEP_OUT_U 0x70 /* Type I */
162 #define WD179X_READ_REC 0x80 /* Type II */
163 #define WD179X_READ_RECS 0x90 /* Type II */
164 #define WD179X_WRITE_REC 0xA0 /* Type II */
165 #define WD179X_WRITE_RECS 0xB0 /* Type II */
166 #define WD179X_READ_ADDR 0xC0 /* Type III */
167 #define WD179X_FORCE_INTR 0xD0 /* Type IV */
168 #define WD179X_READ_TRACK 0xE0 /* Type III */
169 #define WD179X_WRITE_TRACK 0xF0 /* Type III */
170
171 static int32 trace_level = 0xff; /* Disable all tracing by default. */
172 static int32 bootstrap = 0;
173
174 static int32 wd179xdev(const int32 port, const int32 io, const int32 data);
175 static t_stat wd179x_reset(DEVICE *dptr);
176 int32 find_unit_index (UNIT *uptr);
177
178 WD179X_INFO wd179x_info_data = { { 0x0, 0, 0x30, 4 } };
179 WD179X_INFO *wd179x_info = &wd179x_info_data;
180
181 static UNIT wd179x_unit[] = {
182 { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 },
183 { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 },
184 { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 },
185 { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 }
186 };
187
188 static REG wd179x_reg[] = {
189 { HRDATA (TRACELEVEL, trace_level, 16), },
190 { DRDATA (BOOTSTRAP, bootstrap, 10), },
191 { NULL }
192 };
193
194 static MTAB wd179x_mod[] = {
195 { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL },
196 { UNIT_WD179X_WLK, 0, "WRTENB", "WRTENB", NULL },
197 { UNIT_WD179X_WLK, UNIT_WD179X_WLK, "WRTLCK", "WRTLCK", NULL },
198 /* quiet, no warning messages */
199 { UNIT_WD179X_VERBOSE, 0, "QUIET", "QUIET", NULL },
200 /* verbose, show warning messages */
201 { UNIT_WD179X_VERBOSE, UNIT_WD179X_VERBOSE, "VERBOSE", "VERBOSE", NULL },
202 { 0 }
203 };
204
205 DEVICE wd179x_dev = {
206 "WD179X", wd179x_unit, wd179x_reg, wd179x_mod,
207 WD179X_MAX_DRIVES, 10, 31, 1, WD179X_MAX_DRIVES, WD179X_MAX_DRIVES,
208 NULL, NULL, &wd179x_reset,
209 NULL, &wd179x_attach, &wd179x_detach,
210 &wd179x_info_data, (DEV_DISABLE | DEV_DIS), 0,
211 NULL, NULL, NULL
212 };
213
214 /* Unit service routine */
215 /* Used to generate INDEX pulses in response to a FORCE_INTR command */
216 t_stat wd179x_svc (UNIT *uptr)
217 {
218
219 if(wd179x_info->index_pulse_wait == TRUE) {
220 wd179x_info->index_pulse_wait = FALSE;
221 wd179x_info->intrq = 1;
222 }
223
224 return SCPE_OK;
225 }
226
227
228 /* Reset routine */
229 static t_stat wd179x_reset(DEVICE *dptr)
230 {
231 PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt;
232
233 if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */
234 sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &wd179xdev, TRUE);
235 } else {
236 /* Connect I/O Ports at base address */
237 if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &wd179xdev, FALSE) != 0) {
238 printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base);
239 return SCPE_ARG;
240 }
241 }
242 return SCPE_OK;
243 }
244
245 extern int32 find_unit_index (UNIT *uptr);
246
247 void wd179x_external_restore(void)
248 {
249 WD179X_DRIVE_INFO *pDrive;
250 pDrive = &wd179x_info->drive[wd179x_info->sel_drive];
251
252 if(pDrive->uptr == NULL) {
253 TRACE_PRINT(SEEK_MSG,
254 ("WD179X: " ADDRESS_FORMAT " No drive selected, cannot restore." NLP, PCX))
255 return;
256 }
257
258 TRACE_PRINT(SEEK_MSG,
259 ("WD179X[%d]: " ADDRESS_FORMAT " External Restore drive to track 0" NLP, wd179x_info->sel_drive, PCX))
260
261 pDrive->track = 0;
262
263 }
264
265 /* Attach routine */
266 t_stat wd179x_attach(UNIT *uptr, char *cptr)
267 {
268 char header[4];
269 t_stat r;
270 int32 i = 0;
271
272 r = attach_unit(uptr, cptr); /* attach unit */
273 if ( r != SCPE_OK) /* error? */
274 return r;
275
276 /* Determine length of this disk */
277 uptr->capac = sim_fsize(uptr->fileref);
278
279 i = find_unit_index(uptr);
280
281 if (i == -1) {
282 return (SCPE_IERR);
283 }
284
285 DBG_PRINT(("Attach WD179X%d\n", i));
286 wd179x_info->drive[i].uptr = uptr;
287
288 /* Default to drive not ready */
289 wd179x_info->drive[i].ready = 0;
290
291 if(uptr->capac > 0) {
292 fgets(header, 4, uptr->fileref);
293 if(!strcmp(header, "IMD")) {
294 uptr->u3 = IMAGE_TYPE_IMD;
295 } else if(!strcmp(header, "CPT")) {
296 printf("CPT images not yet supported\n");
297 uptr->u3 = IMAGE_TYPE_CPT;
298 wd179x_detach(uptr);
299 return SCPE_OPENERR;
300 } else {
301 printf("DSK images not yet supported\n");
302 uptr->u3 = IMAGE_TYPE_DSK;
303 wd179x_detach(uptr);
304 return SCPE_OPENERR;
305 }
306 } else {
307 /* creating file, must be DSK format. */
308 printf("Cannot create images, must start with a WD179X IMD image.\n");
309 uptr->u3 = IMAGE_TYPE_DSK;
310 wd179x_detach(uptr);
311 return SCPE_OPENERR;
312 }
313
314 if (uptr->flags & UNIT_WD179X_VERBOSE)
315 printf("WD179X%d: attached to '%s', type=%s, len=%d\n", i, cptr,
316 uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK",
317 uptr->capac);
318
319 if(uptr->u3 == IMAGE_TYPE_IMD) {
320 if(uptr->capac < WD179X_CAPACITY_SSSD) { /*was 318000 but changed to allow 8inch SSSD disks*/
321 printf("IMD file too small for use with SIMH.\nCopy an existing file and format it with CP/M.\n");
322 }
323
324 if (uptr->flags & UNIT_WD179X_VERBOSE)
325 printf("--------------------------------------------------------\n");
326 wd179x_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_WD179X_VERBOSE));
327 wd179x_info->drive[i].ready = 1;
328 if (uptr->flags & UNIT_WD179X_VERBOSE)
329 printf("\n");
330 } else {
331 wd179x_info->drive[i].imd = NULL;
332 }
333
334 wd179x_info->fdc_sec_len = 0; /* 128 byte sectors, fixme */
335 wd179x_info->sel_drive = 0;
336
337 return SCPE_OK;
338 }
339
340
341 /* Detach routine */
342 t_stat wd179x_detach(UNIT *uptr)
343 {
344 t_stat r;
345 int8 i;
346
347 i = find_unit_index(uptr);
348
349 if (i == -1) {
350 return (SCPE_IERR);
351 }
352
353 DBG_PRINT(("Detach WD179X%d\n", i));
354 diskClose(wd179x_info->drive[i].imd);
355 wd179x_info->drive[i].ready = 0;
356
357 r = detach_unit(uptr); /* detach unit */
358 if ( r != SCPE_OK)
359 return r;
360
361 return SCPE_OK;
362 }
363
364
365 static int32 wd179xdev(const int32 port, const int32 io, const int32 data)
366 {
367 DBG_PRINT(("WD179X: " ADDRESS_FORMAT " %s, Port 0x%02x Data 0x%02x" NLP,
368 PCX, io ? "OUT" : " IN", port, data));
369 if(io) {
370 WD179X_Write(port, data);
371 return 0;
372 } else {
373 return(WD179X_Read(port));
374 }
375 }
376
377 static uint8 floorlog2(unsigned int n)
378 {
379 /* Compute log2(n) */
380 uint8 r = 0;
381 if(n >= 1<<16) { n >>=16; r += 16; }
382 if(n >= 1<< 8) { n >>= 8; r += 8; }
383 if(n >= 1<< 4) { n >>= 4; r += 4; }
384 if(n >= 1<< 2) { n >>= 2; r += 2; }
385 if(n >= 1<< 1) { r += 1; }
386 return ((n == 0) ? (0xFF) : r); /* 0xFF is error return value */
387 }
388
389 uint8 WD179X_Read(const uint32 Addr)
390 {
391 uint8 cData;
392 WD179X_DRIVE_INFO *pDrive;
393 unsigned int flags;
394 unsigned int readlen;
395 int status;
396
397 pDrive = &wd179x_info->drive[wd179x_info->sel_drive];
398
399 if(pDrive->uptr == NULL) {
400 return 0xFF;
401 }
402
403 cData = 0x00;
404
405 switch(Addr & 0x3) {
406 case WD179X_STATUS:
407 cData = (pDrive->ready == 0) ? WD179X_STAT_NOT_READY : 0;
408 cData |= wd179x_info->fdc_status; /* Status Register */
409 TRACE_PRINT(STATUS_MSG,
410 ("WD179X: " ADDRESS_FORMAT " RD STATUS = 0x%02x" NLP, PCX, cData))
411 wd179x_info->intrq = 0;
412 break;
413 case WD179X_TRACK:
414 cData = pDrive->track;
415 TRACE_PRINT(STATUS_MSG,
416 ("WD179X: " ADDRESS_FORMAT " RD TRACK = 0x%02x" NLP, PCX, cData))
417 break;
418 case WD179X_SECTOR:
419 cData = wd179x_info->fdc_sector;
420 TRACE_PRINT(STATUS_MSG,
421 ("WD179X: " ADDRESS_FORMAT " RD SECT = 0x%02x" NLP, PCX, cData))
422 break;
423 case WD179X_DATA:
424 cData = 0xFF; /* Return High-Z data */
425 if(wd179x_info->fdc_read == TRUE) {
426 if(wd179x_info->fdc_dataindex < wd179x_info->fdc_datacount) {
427 cData = sdata.raw[wd179x_info->fdc_dataindex];
428 if(wd179x_info->fdc_read_addr == TRUE) {
429 TRACE_PRINT(STATUS_MSG,
430 ("WD179X[%d]: " ADDRESS_FORMAT " READ_ADDR[%d] = 0x%02x" NLP, wd179x_info->sel_drive, PCX, wd179x_info->fdc_dataindex, cData))
431 }
432
433 wd179x_info->fdc_dataindex++;
434 if(wd179x_info->fdc_dataindex == wd179x_info->fdc_datacount) {
435 if(wd179x_info->fdc_multiple == FALSE) {
436 wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */
437 wd179x_info->drq = 0;
438 wd179x_info->intrq = 1;
439 wd179x_info->fdc_read = FALSE;
440 wd179x_info->fdc_read_addr = FALSE;
441 } else {
442
443 /* Compute Sector Size */
444 wd179x_info->fdc_sec_len = floorlog2(
445 pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7;
446 if(wd179x_info->fdc_sec_len == 0xF8) { /*Error calculating N*/
447 printf("Invalid sector size!\n");
448 }
449
450 wd179x_info->fdc_sector ++;
451 TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " MULTI_READ_REC, T:%d/S:%d/N:%d, %s, len=%d" NLP,
452 wd179x_info->sel_drive,
453 PCX,
454 pDrive->track,
455 wd179x_info->fdc_head,
456 wd179x_info->fdc_sector,
457 wd179x_info->ddens ? "DD" : "SD",
458 128 << wd179x_info->fdc_sec_len));
459
460 status = sectRead(pDrive->imd,
461 pDrive->track,
462 wd179x_info->fdc_head,
463 wd179x_info->fdc_sector,
464 sdata.raw,
465 128 << wd179x_info->fdc_sec_len,
466 &flags,
467 &readlen);
468
469 if(status != -1) {
470 wd179x_info->fdc_status = (WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Set DRQ, BUSY */
471 wd179x_info->drq = 1;
472 wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len;
473 wd179x_info->fdc_dataindex = 0;
474 wd179x_info->fdc_read = TRUE;
475 wd179x_info->fdc_read_addr = FALSE;
476 } else {
477 wd179x_info->fdc_status = 0; /* Clear DRQ, BUSY */
478 wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND;
479 wd179x_info->drq = 0;
480 wd179x_info->intrq = 1;
481 wd179x_info->fdc_read = FALSE;
482 wd179x_info->fdc_read_addr = FALSE;
483 }
484 }
485 }
486 }
487 }
488 break;
489 }
490
491 return (cData);
492 }
493
494 /*
495 * Command processing happens in three stages:
496 * 1. Flags and initial conditions are set up based on the Type of the command.
497 * 2. The execution phase takes place.
498 * 3. Status is updated based on the Type and outcome of the command execution.
499 *
500 * See the WD179x-02 Datasheet available on www.hartetechnologies.com/manuals/
501 *
502 */
503 static uint8 Do1793Command(uint8 cCommand)
504 {
505 uint8 result = 0;
506 WD179X_DRIVE_INFO *pDrive;
507 unsigned int flags;
508 unsigned int readlen;
509
510 pDrive = &wd179x_info->drive[wd179x_info->sel_drive];
511
512 if(pDrive->uptr == NULL) {
513 return 0xFF;
514 }
515
516 if(wd179x_info->fdc_status & WD179X_STAT_BUSY) {
517 if((cCommand & 0xF0) != WD179X_FORCE_INTR) {
518 printf("WD179X[%d]: ERROR: Command 0x%02x ignored because controller is BUSY\n", wd179x_info->sel_drive, cCommand);
519 }
520 return 0xFF;
521 }
522
523 /* Extract Type-specific command flags, and set initial conditions */
524 switch(cCommand & 0xF0) {
525 /* Type I Commands */
526 case WD179X_RESTORE:
527 case WD179X_SEEK:
528 case WD179X_STEP:
529 case WD179X_STEP_U:
530 case WD179X_STEP_IN:
531 case WD179X_STEP_IN_U:
532 case WD179X_STEP_OUT:
533 case WD179X_STEP_OUT_U:
534 wd179x_info->fdc_status |= WD179X_STAT_BUSY; /* Set BUSY */
535 wd179x_info->fdc_status &= ~(WD179X_STAT_CRC_ERROR | WD179X_STAT_SEEK_ERROR | WD179X_STAT_DRQ);
536 wd179x_info->intrq = 0;
537 wd179x_info->hld = cCommand && 0x08;
538 wd179x_info->verify = cCommand & 0x04;
539 break;
540 /* Type II Commands */
541 case WD179X_READ_REC:
542 case WD179X_READ_RECS:
543 case WD179X_WRITE_REC:
544 case WD179X_WRITE_RECS:
545 wd179x_info->fdc_status = WD179X_STAT_BUSY; /* Set BUSY, clear all others */
546 wd179x_info->intrq = 0;
547 wd179x_info->hld = 1; /* Load the head immediately, E Flag not checked. */
548 break;
549 /* Type III Commands */
550 case WD179X_READ_ADDR:
551 case WD179X_READ_TRACK:
552 case WD179X_WRITE_TRACK:
553 /* Type IV Commands */
554 case WD179X_FORCE_INTR:
555 default:
556 break;
557 }
558
559 switch(cCommand & 0xF0) {
560 /* Type I Commands */
561 case WD179X_RESTORE:
562 TRACE_PRINT(CMD_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=RESTORE" NLP, wd179x_info->sel_drive, PCX));
563 pDrive->track = 0;
564 wd179x_info->intrq = 1;
565 break;
566 case WD179X_SEEK:
567 TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=SEEK, track=%d, new=%d" NLP, wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_data));
568 pDrive->track = wd179x_info->fdc_data;
569 break;
570 case WD179X_STEP:
571 TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP" NLP, wd179x_info->sel_drive, PCX));
572 break;
573 case WD179X_STEP_U:
574 TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_U dir=%d" NLP, wd179x_info->sel_drive, PCX, wd179x_info->step_dir));
575 if(wd179x_info->step_dir == 1) {
576 if(pDrive->track < 255) pDrive->track++;
577 } else if (wd179x_info->step_dir == -1) {
578 if(pDrive->track > 0) pDrive->track--;
579 } else {
580 printf("WD179X[%d]: Error, undefined direction for STEP\n", wd179x_info->sel_drive);
581 }
582 break;
583 case WD179X_STEP_IN:
584 TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_IN" NLP, wd179x_info->sel_drive, PCX));
585 break;
586 case WD179X_STEP_IN_U:
587 if(pDrive->track < 255) pDrive->track++;
588 wd179x_info->step_dir = 1;
589 TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_IN_U, Track=%d" NLP,
590 wd179x_info->sel_drive, PCX, pDrive->track));
591 break;
592 case WD179X_STEP_OUT:
593 TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_OUT" NLP,
594 wd179x_info->sel_drive, PCX));
595 break;
596 case WD179X_STEP_OUT_U:
597 TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_OUT_U" NLP,
598 wd179x_info->sel_drive, PCX));
599 if(pDrive->track > 0) pDrive->track--;
600 wd179x_info->step_dir = -1;
601 break;
602 /* Type II Commands */
603 case WD179X_READ_REC:
604 case WD179X_READ_RECS:
605 /* Compute Sector Size */
606 wd179x_info->fdc_sec_len = floorlog2(
607 pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7;
608 if(wd179x_info->fdc_sec_len == 0xF8) { /*Error calculating N*/
609 printf("Invalid sector size!\n");
610 }
611
612 wd179x_info->fdc_multiple = (cCommand & 0x10) ? TRUE : FALSE;
613 TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_REC, T:%d/S:%d/N:%d, %s, %s len=%d" NLP,
614 wd179x_info->sel_drive,
615 PCX, pDrive->track,
616 wd179x_info->fdc_head,
617 wd179x_info->fdc_sector,
618 wd179x_info->fdc_multiple ? "Multiple" : "Single",
619 wd179x_info->ddens ? "DD" : "SD",
620 128 << wd179x_info->fdc_sec_len));
621
622 sectRead(pDrive->imd,
623 pDrive->track,
624 wd179x_info->fdc_head,
625 wd179x_info->fdc_sector,
626 sdata.raw,
627 128 << wd179x_info->fdc_sec_len,
628 &flags,
629 &readlen);
630
631 if(IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens)) {
632 printf("Sector not found\n");
633 wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; /* Sector not found */
634 wd179x_info->intrq = 1;
635 } else {
636 wd179x_info->fdc_status |= (WD179X_STAT_DRQ); /* Set DRQ */
637 wd179x_info->drq = 1;
638 wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len;
639 wd179x_info->fdc_dataindex = 0;
640 wd179x_info->fdc_write = FALSE;
641 wd179x_info->fdc_read = TRUE;
642 wd179x_info->fdc_read_addr = FALSE;
643 }
644 break;
645 case WD179X_WRITE_RECS:
646 printf("-->> Error: WRITE_RECS not implemented." NLP);
647 case WD179X_WRITE_REC:
648 /* Compute Sector Size */
649 wd179x_info->fdc_sec_len = floorlog2(
650 pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7;
651 if(wd179x_info->fdc_sec_len == 0xF8) { /*Error calculating N*/
652 printf("Invalid sector size!\n");
653 }
654
655 TRACE_PRINT(WR_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=WRITE_REC, T:%d/S:%d/N:%d, %s." NLP,
656 wd179x_info->sel_drive,
657 PCX,
658 pDrive->track,
659 cCommand&&0x08,
660 wd179x_info->fdc_sector,
661 (cCommand & 0x10) ? "Multiple" : "Single"));
662 wd179x_info->fdc_status |= (WD179X_STAT_DRQ); /* Set DRQ */
663 wd179x_info->drq = 1;
664 wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len;
665 wd179x_info->fdc_dataindex = 0;
666 wd179x_info->fdc_write = TRUE;
667 wd179x_info->fdc_read = FALSE;
668 wd179x_info->fdc_read_addr = FALSE;
669
670 sdata.raw[wd179x_info->fdc_dataindex] = wd179x_info->fdc_data;
671 break;
672 /* Type III Commands */
673 case WD179X_READ_ADDR:
674 TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_ADDR, T:%d/S:%d, %s" NLP,
675 wd179x_info->sel_drive,
676 PCX,
677 pDrive->track,
678 wd179x_info->fdc_head,
679 wd179x_info->ddens ? "DD" : "SD"));
680
681 /* Compute Sector Size */
682 wd179x_info->fdc_sec_len = floorlog2(
683 pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7;
684 if(wd179x_info->fdc_sec_len == 0xF8) { /*Error calculating N*/
685 printf("Invalid sector size!\n");
686 }
687
688 if(IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens)) {
689 wd179x_info->fdc_status = WD179X_STAT_NOT_FOUND; /* Sector not found */
690 wd179x_info->intrq = 1;
691 } else {
692 wd179x_info->fdc_status = (WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Set DRQ, BUSY */
693 wd179x_info->drq = 1;
694 wd179x_info->fdc_datacount = 6;
695 wd179x_info->fdc_dataindex = 0;
696 wd179x_info->fdc_read = TRUE;
697 wd179x_info->fdc_read_addr = TRUE;
698
699 sdata.raw[0] = pDrive->track;
700 sdata.raw[1] = wd179x_info->fdc_head;
701 sdata.raw[2] = wd179x_info->fdc_sector;
702 sdata.raw[3] = wd179x_info->fdc_sec_len;
703 sdata.raw[4] = 0xAA; /* CRC1 */
704 sdata.raw[5] = 0x55; /* CRC2 */
705
706 wd179x_info->fdc_sector = pDrive->track;
707 wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */
708 wd179x_info->intrq = 1;
709 }
710 break;
711 case WD179X_READ_TRACK:
712 TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_TRACK" NLP, wd179x_info->sel_drive, PCX));
713 printf("-->> Error: READ_TRACK not implemented." NLP);
714 break;
715 case WD179X_WRITE_TRACK:
716 TRACE_PRINT(WR_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=WRITE_TRACK" NLP, wd179x_info->sel_drive, PCX));
717 printf("-->> Error: WRITE_TRACK not implemented." NLP);
718 break;
719 /* Type IV Commands */
720 case WD179X_FORCE_INTR:
721 TRACE_PRINT(CMD_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=FORCE_INTR" NLP, wd179x_info->sel_drive, PCX));
722 if((cCommand & 0x0F) == 0) { /* I0-I3 == 0, no intr, but clear BUSY and terminate command */
723 wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */
724 wd179x_info->drq = 0;
725 wd179x_info->fdc_write = FALSE;
726 wd179x_info->fdc_read = FALSE;
727 wd179x_info->fdc_read_addr = FALSE;
728 wd179x_info->fdc_datacount = 0;
729 wd179x_info->fdc_dataindex = 0;
730 } else {
731 if(wd179x_info->fdc_status & WD179X_STAT_BUSY) { /* Force Interrupt when command is pending */
732 } else { /* Command not pending, clear status */
733 wd179x_info->fdc_status = 0;
734 }
735
736 if(cCommand & 0x04) {
737 wd179x_info->index_pulse_wait = TRUE;
738 sim_activate (wd179x_unit, wd179x_info->drivetype == 8 ? 48500 : 58200); /* Generate INDEX pulse */
739 } else {
740 wd179x_info->intrq = 1;
741 }
742 wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */
743 }
744 break;
745 default:
746 printf("WD179X[%d]: Unknown WD179X command 0x%02x.\n", wd179x_info->sel_drive, cCommand);
747 break;
748 }
749
750 /* Post processing of Type-specific command */
751 switch(cCommand & 0xF0) {
752 /* Type I Commands */
753 case WD179X_RESTORE:
754 case WD179X_SEEK:
755 case WD179X_STEP:
756 case WD179X_STEP_U:
757 case WD179X_STEP_IN:
758 case WD179X_STEP_IN_U:
759 case WD179X_STEP_OUT:
760 case WD179X_STEP_OUT_U:
761 if(wd179x_info->verify) { /* Verify the selected track/head is ok. */
762 TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " Verify ", wd179x_info->sel_drive, PCX));
763 if(sectSeek(pDrive->imd, pDrive->track, wd179x_info->fdc_head) != 0) {
764 TRACE_PRINT(SEEK_MSG, ("FAILED" NLP));
765 wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND;
766 } else {
767 TRACE_PRINT(SEEK_MSG, ("Ok" NLP));
768 }
769 }
770
771 if(pDrive->track == 0) {
772 wd179x_info->fdc_status |= WD179X_STAT_TRACK0;
773 } else {
774 wd179x_info->fdc_status &= ~(WD179X_STAT_TRACK0);
775 }
776
777 wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */
778 wd179x_info->intrq = 1;
779 break;
780 /* Type II Commands */
781 case WD179X_READ_REC:
782 case WD179X_READ_RECS:
783 case WD179X_WRITE_REC:
784 case WD179X_WRITE_RECS:
785 /* Type III Commands */
786 case WD179X_READ_ADDR:
787 case WD179X_READ_TRACK:
788 case WD179X_WRITE_TRACK:
789 /* Type IV Commands */
790 case WD179X_FORCE_INTR:
791 default:
792 break;
793 }
794
795
796 return result;
797 }
798
799 uint8 WD179X_Write(const uint32 Addr, uint8 cData)
800 {
801 WD179X_DRIVE_INFO *pDrive;
802 unsigned int flags;
803 unsigned int writelen;
804
805 pDrive = &wd179x_info->drive[wd179x_info->sel_drive];
806
807 if(pDrive->uptr == NULL) {
808 return 0xFF;
809 }
810
811 switch(Addr & 0x3) {
812 case WD179X_STATUS:
813 TRACE_PRINT(STATUS_MSG,
814 ("WD179X: " ADDRESS_FORMAT " WR CMD = 0x%02x" NLP, PCX, cData))
815 wd179x_info->fdc_read = FALSE;
816 wd179x_info->fdc_write = FALSE;
817 wd179x_info->fdc_datacount = 0;
818 wd179x_info->fdc_dataindex = 0;
819
820 Do1793Command(cData);
821 break;
822 case WD179X_TRACK:
823 TRACE_PRINT(STATUS_MSG,
824 ("WD179X: " ADDRESS_FORMAT " WR TRACK = 0x%02x" NLP, PCX, cData))
825 pDrive->track = cData;
826 break;
827 case WD179X_SECTOR: /* Sector Register */
828 TRACE_PRINT(STATUS_MSG,
829 ("WD179X: " ADDRESS_FORMAT " WR SECT = 0x%02x" NLP, PCX, cData))
830 wd179x_info->fdc_sector = cData;
831 break;
832 case WD179X_DATA:
833 TRACE_PRINT(STATUS_MSG,
834 ("WD179X: " ADDRESS_FORMAT " WR DATA = 0x%02x" NLP, PCX, cData))
835 if(wd179x_info->fdc_write == TRUE) {
836 if(wd179x_info->fdc_dataindex < wd179x_info->fdc_datacount) {
837 sdata.raw[wd179x_info->fdc_dataindex] = cData;
838
839 wd179x_info->fdc_dataindex++;
840 if(wd179x_info->fdc_dataindex == wd179x_info->fdc_datacount) {
841 wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */
842 wd179x_info->drq = 0;
843 wd179x_info->intrq = 1;
844
845 sectWrite(pDrive->imd,
846 pDrive->track,
847 wd179x_info->fdc_head,
848 wd179x_info->fdc_sector,
849 sdata.raw,
850 128 << wd179x_info->fdc_sec_len,
851 &flags,
852 &writelen);
853
854 wd179x_info->fdc_write = FALSE;
855 }
856 }
857 }
858 wd179x_info->fdc_data = cData;
859 break;
860 }
861
862 return 0;
863 }
864