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