|
|
1.1 root 1: /*
1.1.1.3 root 2: Hatari - hdc.c
1.1 root 3:
1.1.1.15 root 4: This file is distributed under the GNU General Public License, version 2
5: or at your option any later version. Read the file gpl.txt for details.
1.1 root 6:
1.1.1.3 root 7: Low-level hard drive emulation
1.1 root 8: */
1.1.1.10 root 9: const char HDC_fileid[] = "Hatari hdc.c : " __DATE__ " " __TIME__;
1.1 root 10:
11: #include "main.h"
1.1.1.3 root 12: #include "configuration.h"
1.1.1.6 root 13: #include "debugui.h"
1.1.1.7 root 14: #include "file.h"
1.1 root 15: #include "fdc.h"
16: #include "hdc.h"
1.1.1.14 root 17: #include "ioMem.h"
1.1.1.7 root 18: #include "log.h"
1.1 root 19: #include "memorySnapShot.h"
20: #include "mfp.h"
21: #include "stMemory.h"
1.1.1.6 root 22: #include "tos.h"
1.1.1.9 root 23: #include "statusbar.h"
24:
1.1 root 25:
26: /*
27: ACSI emulation:
28: ACSI commands are six byte-packets sent to the
29: hard drive controller (which is on the HD unit, not in the ST)
30:
31: While the hard drive is busy, DRQ is high, polling the DRQ during
32: operation interrupts the current operation. The DRQ status can
33: be polled non-destructively in GPIP.
1.1.1.7 root 34:
1.1.1.15 root 35: (For simplicity, the operation is finished immediately,
1.1 root 36: this is a potential bug, but I doubt it is significant,
37: we just appear to have a very fast hard drive.)
38:
39: The ACSI command set is a subset of the SCSI standard.
40: (for details, see the X3T9.2 SCSI draft documents
41: from 1985, for an example of writing ACSI commands,
42: see the TOS DMA boot code)
43: */
44:
1.1.1.7 root 45: // #define DISALLOW_HDC_WRITE
46: // #define HDC_VERBOSE /* display operations */
1.1 root 47:
1.1.1.14 root 48: #define HDC_ReadInt16(a, i) (((unsigned) a[i] << 8) | a[i + 1])
49: #define HDC_ReadInt24(a, i) (((unsigned) a[i] << 16) | ((unsigned) a[i + 1] << 8) | a[i + 2])
50: #define HDC_ReadInt32(a, i) (((unsigned) a[i] << 24) | ((unsigned) a[i + 1] << 16) | ((unsigned) a[i + 2] << 8) | a[i + 3])
51:
52: /**
1.1.1.16! root 53: * Information about a ACSI/SCSI drive
1.1.1.14 root 54: */
55: typedef struct {
1.1.1.16! root 56: bool enabled;
! 57: FILE *image_file;
! 58: Uint32 nLastBlockAddr; /* The specified sector number */
! 59: bool bSetLastBlockAddr;
! 60: Uint8 nLastError;
! 61: unsigned long hdSize; /* Size of the hard disk in sectors */
! 62: } SCSI_DEV;
! 63:
! 64: /**
! 65: * Status of the ACSI/SCSI bus/controller including the current command block.
! 66: */
! 67: typedef struct {
! 68: int target;
! 69: int byteCount; /* number of command bytes received */
! 70: Uint8 command[16];
! 71: Uint8 opcode;
! 72: bool bDmaError;
! 73: short int returnCode; /* return code from the HDC operation */
! 74: SCSI_DEV devs[8];
! 75: } SCSI_CTRLR;
1.1.1.14 root 76:
1.1 root 77: /* HDC globals */
1.1.1.16! root 78: SCSI_CTRLR AcsiBus;
1.1 root 79: int nPartitions = 0;
1.1.1.11 root 80: bool bAcsiEmuOn = false;
1.1.1.7 root 81:
1.1 root 82:
83: /* Our dummy INQUIRY response data */
1.1.1.7 root 84: static unsigned char inquiry_bytes[] =
1.1 root 85: {
1.1.1.7 root 86: 0, /* device type 0 = direct access device */
87: 0, /* device type qualifier (nonremovable) */
1.1.1.16! root 88: 1, /* ACSI/SCSI version */
1.1.1.7 root 89: 0, /* reserved */
1.1.1.16! root 90: 31, /* length of the following data */
! 91: 0, 0, 0, /* Vendor specific data */
! 92: 'H','a','t','a','r','i',' ',' ', /* Vendor ID */
! 93: 'E','m','u','l','a','t','e','d', /* Product ID 1 */
! 94: 'H','a','r','d','d','i','s','k', /* Product ID 2 */
! 95: '0','1','8','0', /* Revision */
1.1 root 96: };
97:
1.1.1.7 root 98:
1.1 root 99: /*---------------------------------------------------------------------*/
1.1.1.8 root 100: /**
1.1.1.16! root 101: * Return the LUN (logical unit number) specified in the current
! 102: * ACSI/SCSI command block.
1.1.1.14 root 103: */
1.1.1.16! root 104: static unsigned char HDC_GetLUN(SCSI_CTRLR *ctr)
1.1.1.14 root 105: {
1.1.1.16! root 106: return (ctr->command[1] & 0xE0) >> 5;
1.1.1.14 root 107: }
108:
109: /**
1.1.1.16! root 110: * Return the start sector (logical block address) specified in the
! 111: * current ACSI/SCSI command block.
1.1.1.8 root 112: */
1.1.1.16! root 113: static unsigned long HDC_GetLBA(SCSI_CTRLR *ctr)
1.1.1.4 root 114: {
1.1.1.14 root 115: /* offset = logical block address * 512 */
1.1.1.16! root 116: if (ctr->opcode < 0x20) /* Class 0? */
! 117: return HDC_ReadInt24(ctr->command, 1) & 0x1FFFFF;
! 118: else
! 119: return HDC_ReadInt32(ctr->command, 2); /* Class 1 */
1.1.1.14 root 120: }
1.1 root 121:
1.1.1.14 root 122: /**
123: * Return the count specified in the current ACSI command block.
124: */
1.1.1.16! root 125: static int HDC_GetCount(SCSI_CTRLR *ctr)
1.1.1.14 root 126: {
1.1.1.16! root 127: if (ctr->opcode < 0x20)
! 128: return ctr->command[4]; /* Class 0 */
! 129: else
! 130: return HDC_ReadInt16(ctr->command, 7); /* Class 1 */
1.1.1.14 root 131: }
1.1.1.7 root 132:
1.1.1.14 root 133: /**
134: * Return the control byte specified in the current ACSI command block.
135: */
1.1.1.16! root 136: static inline Uint8 HDC_GetControl(SCSI_CTRLR *ctr)
1.1.1.14 root 137: {
1.1.1.16! root 138: if (ctr->opcode < 0x20)
! 139: return ctr->command[5]; /* Class 0 */
! 140: else
! 141: return ctr->command[9]; /* Class 1 */
! 142: }
! 143:
! 144: /**
! 145: * Get info string for SCSI/ACSI command packets.
! 146: */
! 147: static inline char *HDC_CmdInfoStr(SCSI_CTRLR *ctr)
! 148: {
! 149: static char str[80];
! 150:
! 151: snprintf(str, sizeof(str), "t=%i, lun=%i, opc=%i, cnt=0x%x, ctrl=0x%x",
! 152: ctr->target, HDC_GetLUN(ctr), ctr->opcode, HDC_GetCount(ctr),
! 153: HDC_GetControl(ctr));
! 154:
! 155: return str;
1.1 root 156: }
157:
1.1.1.7 root 158:
1.1.1.8 root 159: /**
160: * Seek - move to a sector
161: */
1.1.1.16! root 162: static void HDC_Cmd_Seek(SCSI_CTRLR *ctr)
1.1 root 163: {
1.1.1.16! root 164: SCSI_DEV *dev = &ctr->devs[ctr->target];
! 165:
! 166: dev->nLastBlockAddr = HDC_GetLBA(ctr);
! 167:
! 168: LOG_TRACE(TRACE_SCSI_CMD, "HDC: SEEK (%s), LBA=%i.\n",
! 169: HDC_CmdInfoStr(ctr), dev->nLastBlockAddr);
1.1 root 170:
1.1.1.16! root 171: if (dev->nLastBlockAddr < dev->hdSize &&
! 172: fseeko(dev->image_file, (off_t)dev->nLastBlockAddr * 512L, SEEK_SET) == 0)
1.1.1.7 root 173: {
1.1.1.16! root 174: ctr->returnCode = HD_STATUS_OK;
! 175: dev->nLastError = HD_REQSENS_OK;
1.1.1.7 root 176: }
177: else
178: {
1.1.1.16! root 179: ctr->returnCode = HD_STATUS_ERROR;
! 180: dev->nLastError = HD_REQSENS_INVADDR;
1.1.1.7 root 181: }
1.1 root 182:
1.1.1.16! root 183: dev->bSetLastBlockAddr = true;
1.1 root 184: }
185:
1.1.1.7 root 186:
1.1.1.8 root 187: /**
188: * Inquiry - return some disk information
189: */
1.1.1.16! root 190: static void HDC_Cmd_Inquiry(SCSI_CTRLR *ctr)
1.1.1.7 root 191: {
1.1.1.16! root 192: SCSI_DEV *dev = &ctr->devs[ctr->target];
1.1.1.12 root 193: Uint32 nDmaAddr;
194: int count;
195:
1.1.1.14 root 196: nDmaAddr = FDC_GetDMAAddress();
1.1.1.16! root 197: count = HDC_GetCount(ctr);
1.1.1.12 root 198:
1.1.1.16! root 199: LOG_TRACE(TRACE_SCSI_CMD, "HDC: INQUIRY (%s) to 0x%x.\n",
! 200: HDC_CmdInfoStr(ctr), nDmaAddr);
1.1.1.7 root 201:
1.1.1.12 root 202: if (count > (int)sizeof(inquiry_bytes))
203: count = sizeof(inquiry_bytes);
204:
1.1.1.16! root 205: /* For unsupported LUNs set the Peripheral Qualifier and the
! 206: * Peripheral Device Type according to the SCSI standard */
! 207: inquiry_bytes[0] = HDC_GetLUN(ctr) == 0 ? 0 : 0x7F;
! 208:
! 209: inquiry_bytes[4] = count - 5;
1.1.1.12 root 210:
1.1.1.13 root 211: if (STMemory_SafeCopy(nDmaAddr, inquiry_bytes, count, "HDC DMA inquiry"))
1.1.1.16! root 212: {
! 213: ctr->returnCode = HD_STATUS_OK;
! 214: }
1.1.1.13 root 215: else
1.1.1.16! root 216: {
! 217: ctr->bDmaError = true;
! 218: ctr->returnCode = HD_STATUS_ERROR;
! 219: }
1.1.1.13 root 220:
1.1.1.12 root 221: FDC_WriteDMAAddress(nDmaAddr + count);
1.1.1.7 root 222:
1.1.1.16! root 223: dev->nLastError = HD_REQSENS_OK;
! 224: dev->bSetLastBlockAddr = false;
1.1.1.7 root 225: }
226:
227:
1.1.1.8 root 228: /**
229: * Request sense - return some disk information
230: */
1.1.1.16! root 231: static void HDC_Cmd_RequestSense(SCSI_CTRLR *ctr)
1.1 root 232: {
1.1.1.16! root 233: SCSI_DEV *dev = &ctr->devs[ctr->target];
1.1.1.7 root 234: Uint32 nDmaAddr;
235: int nRetLen;
236: Uint8 retbuf[22];
237:
1.1.1.16! root 238: nRetLen = HDC_GetCount(ctr);
1.1.1.7 root 239:
1.1.1.16! root 240: LOG_TRACE(TRACE_SCSI_CMD, "HDC: REQUEST SENSE (%s).\n", HDC_CmdInfoStr(ctr));
1.1.1.7 root 241:
242: if ((nRetLen < 4 && nRetLen != 0) || nRetLen > 22)
243: {
1.1.1.9 root 244: Log_Printf(LOG_WARN, "HDC: *** Strange REQUEST SENSE ***!\n");
1.1.1.7 root 245: }
246:
247: /* Limit to sane length */
248: if (nRetLen <= 0)
249: {
250: nRetLen = 4;
251: }
252: else if (nRetLen > 22)
253: {
254: nRetLen = 22;
255: }
256:
1.1.1.14 root 257: nDmaAddr = FDC_GetDMAAddress();
1.1.1.7 root 258:
259: memset(retbuf, 0, nRetLen);
260:
261: if (nRetLen <= 4)
262: {
1.1.1.16! root 263: retbuf[0] = dev->nLastError;
! 264: if (dev->bSetLastBlockAddr)
1.1.1.7 root 265: {
266: retbuf[0] |= 0x80;
1.1.1.16! root 267: retbuf[1] = dev->nLastBlockAddr >> 16;
! 268: retbuf[2] = dev->nLastBlockAddr >> 8;
! 269: retbuf[3] = dev->nLastBlockAddr;
1.1.1.7 root 270: }
271: }
272: else
273: {
274: retbuf[0] = 0x70;
1.1.1.16! root 275: if (dev->bSetLastBlockAddr)
1.1.1.7 root 276: {
277: retbuf[0] |= 0x80;
1.1.1.16! root 278: retbuf[4] = dev->nLastBlockAddr >> 16;
! 279: retbuf[5] = dev->nLastBlockAddr >> 8;
! 280: retbuf[6] = dev->nLastBlockAddr;
1.1.1.7 root 281: }
1.1.1.16! root 282: switch (dev->nLastError)
1.1.1.7 root 283: {
284: case HD_REQSENS_OK: retbuf[2] = 0; break;
285: case HD_REQSENS_OPCODE: retbuf[2] = 5; break;
286: case HD_REQSENS_INVADDR: retbuf[2] = 5; break;
287: case HD_REQSENS_INVARG: retbuf[2] = 5; break;
1.1.1.16! root 288: case HD_REQSENS_INVLUN: retbuf[2] = 5; break;
1.1.1.7 root 289: default: retbuf[2] = 4; break;
290: }
291: retbuf[7] = 14;
1.1.1.16! root 292: retbuf[12] = dev->nLastError;
! 293: retbuf[19] = dev->nLastBlockAddr >> 16;
! 294: retbuf[20] = dev->nLastBlockAddr >> 8;
! 295: retbuf[21] = dev->nLastBlockAddr;
1.1.1.7 root 296: }
297:
298: /*
299: fprintf(stderr,"*** Requested sense packet:\n");
300: int i;
301: for (i = 0; i<nRetLen; i++) fprintf(stderr,"%2x ",retbuf[i]);
302: fprintf(stderr,"\n");
303: */
304:
1.1.1.13 root 305: if (STMemory_SafeCopy(nDmaAddr, retbuf, nRetLen, "HDC request sense"))
1.1.1.16! root 306: {
! 307: ctr->returnCode = HD_STATUS_OK;
! 308: }
1.1.1.13 root 309: else
1.1.1.16! root 310: {
! 311: ctr->bDmaError = true;
! 312: ctr->returnCode = HD_STATUS_ERROR;
! 313: }
1.1.1.13 root 314:
1.1.1.12 root 315: FDC_WriteDMAAddress(nDmaAddr + nRetLen);
1.1.1.7 root 316: }
317:
318:
1.1.1.8 root 319: /**
320: * Mode sense - Get parameters from disk.
321: * (Just enough to make the HDX tool from AHDI 5.0 happy)
322: */
1.1.1.16! root 323: static void HDC_Cmd_ModeSense(SCSI_CTRLR *ctr)
1.1.1.7 root 324: {
1.1.1.16! root 325: SCSI_DEV *dev = &ctr->devs[ctr->target];
1.1.1.7 root 326: Uint32 nDmaAddr;
327:
1.1.1.16! root 328: LOG_TRACE(TRACE_SCSI_CMD, "HDC: MODE SENSE (%s).\n", HDC_CmdInfoStr(ctr));
1.1.1.7 root 329:
1.1.1.14 root 330: nDmaAddr = FDC_GetDMAAddress();
1.1.1.7 root 331:
1.1.1.13 root 332: if (!STMemory_ValidArea(nDmaAddr, 16))
333: {
334: Log_Printf(LOG_WARN, "HCD mode sense uses invalid RAM range 0x%x+%i\n", nDmaAddr, 16);
1.1.1.16! root 335: ctr->bDmaError = true;
! 336: ctr->returnCode = HD_STATUS_ERROR;
1.1.1.13 root 337: }
1.1.1.16! root 338: else if (ctr->command[2] == 0 && HDC_GetCount(ctr) == 0x10)
1.1.1.7 root 339: {
340: size_t blocks;
1.1.1.16! root 341: blocks = dev->hdSize;
1.1.1.7 root 342:
343: STRam[nDmaAddr+0] = 0;
344: STRam[nDmaAddr+1] = 0;
345: STRam[nDmaAddr+2] = 0;
346: STRam[nDmaAddr+3] = 8;
347: STRam[nDmaAddr+4] = 0;
348:
349: STRam[nDmaAddr+5] = blocks >> 16; // Number of blocks, high (?)
350: STRam[nDmaAddr+6] = blocks >> 8; // Number of blocks, med (?)
351: STRam[nDmaAddr+7] = blocks; // Number of blocks, low (?)
352:
353: STRam[nDmaAddr+8] = 0;
354:
355: STRam[nDmaAddr+9] = 0; // Block size in bytes, high
356: STRam[nDmaAddr+10] = 2; // Block size in bytes, med
357: STRam[nDmaAddr+11] = 0; // Block size in bytes, low
358:
359: STRam[nDmaAddr+12] = 0;
360: STRam[nDmaAddr+13] = 0;
361: STRam[nDmaAddr+14] = 0;
362: STRam[nDmaAddr+15] = 0;
363:
1.1.1.12 root 364: FDC_WriteDMAAddress(nDmaAddr + 16);
365:
1.1.1.16! root 366: ctr->returnCode = HD_STATUS_OK;
! 367: dev->nLastError = HD_REQSENS_OK;
1.1.1.7 root 368: }
369: else
370: {
1.1.1.9 root 371: Log_Printf(LOG_TODO, "HDC: Unsupported MODE SENSE command\n");
1.1.1.16! root 372: ctr->returnCode = HD_STATUS_ERROR;
! 373: dev->nLastError = HD_REQSENS_INVARG;
1.1.1.7 root 374: }
375:
1.1.1.16! root 376: dev->bSetLastBlockAddr = false;
1.1.1.7 root 377: }
378:
379:
1.1.1.8 root 380: /**
381: * Format drive.
382: */
1.1.1.16! root 383: static void HDC_Cmd_FormatDrive(SCSI_CTRLR *ctr)
1.1.1.7 root 384: {
1.1.1.16! root 385: SCSI_DEV *dev = &ctr->devs[ctr->target];
! 386:
! 387: LOG_TRACE(TRACE_SCSI_CMD, "HDC: FORMAT DRIVE (%s).\n", HDC_CmdInfoStr(ctr));
1.1.1.7 root 388:
389: /* Should erase the whole image file here... */
390:
1.1.1.16! root 391: ctr->returnCode = HD_STATUS_OK;
! 392: dev->nLastError = HD_REQSENS_OK;
! 393: dev->bSetLastBlockAddr = false;
1.1 root 394: }
395:
1.1.1.16! root 396:
1.1.1.14 root 397: /**
398: * Read capacity of our disk.
399: */
1.1.1.16! root 400: static void HDC_Cmd_ReadCapacity(SCSI_CTRLR *ctr)
1.1.1.14 root 401: {
1.1.1.16! root 402: SCSI_DEV *dev = &ctr->devs[ctr->target];
1.1.1.14 root 403: Uint32 nDmaAddr = FDC_GetDMAAddress();
404:
1.1.1.16! root 405: LOG_TRACE(TRACE_SCSI_CMD, "HDC: READ CAPACITY (%s) to 0x%x.\n",
! 406: HDC_CmdInfoStr(ctr), nDmaAddr);
1.1.1.14 root 407:
408: /* seek to the position */
409: if (STMemory_ValidArea(nDmaAddr, 8))
410: {
1.1.1.16! root 411: int nSectors = dev->hdSize - 1;
1.1.1.14 root 412: STRam[nDmaAddr++] = (nSectors >> 24) & 0xFF;
413: STRam[nDmaAddr++] = (nSectors >> 16) & 0xFF;
414: STRam[nDmaAddr++] = (nSectors >> 8) & 0xFF;
415: STRam[nDmaAddr++] = (nSectors) & 0xFF;
416: STRam[nDmaAddr++] = 0x00;
417: STRam[nDmaAddr++] = 0x00;
418: STRam[nDmaAddr++] = 0x02;
419: STRam[nDmaAddr++] = 0x00;
420:
421: /* Update DMA counter */
1.1.1.16! root 422: FDC_WriteDMAAddress(nDmaAddr);
1.1.1.14 root 423:
1.1.1.16! root 424: ctr->returnCode = HD_STATUS_OK;
! 425: dev->nLastError = HD_REQSENS_OK;
1.1.1.14 root 426: }
427: else
428: {
429: Log_Printf(LOG_WARN, "HDC capacity read uses invalid RAM range 0x%x+%i\n", nDmaAddr, 8);
1.1.1.16! root 430: ctr->bDmaError = true;
! 431: ctr->returnCode = HD_STATUS_ERROR;
! 432: dev->nLastError = HD_REQSENS_NOSECTOR;
1.1.1.14 root 433: }
434:
1.1.1.16! root 435: dev->bSetLastBlockAddr = false;
1.1.1.14 root 436: }
437:
1.1.1.7 root 438:
1.1.1.8 root 439: /**
440: * Write a sector off our disk - (seek implied)
441: */
1.1.1.16! root 442: static void HDC_Cmd_WriteSector(SCSI_CTRLR *ctr)
1.1 root 443: {
1.1.1.16! root 444: SCSI_DEV *dev = &ctr->devs[ctr->target];
! 445: Uint32 nDmaAddr = FDC_GetDMAAddress();
1.1.1.11 root 446: int n = 0;
447:
1.1.1.16! root 448: dev->nLastBlockAddr = HDC_GetLBA(ctr);
! 449:
! 450: LOG_TRACE(TRACE_SCSI_CMD, "HDC: WRITE SECTOR (%s) with LBA 0x%x from 0x%x.\n",
! 451: HDC_CmdInfoStr(ctr), dev->nLastBlockAddr, nDmaAddr);
1.1 root 452:
1.1.1.7 root 453: /* seek to the position */
1.1.1.16! root 454: if (dev->nLastBlockAddr >= dev->hdSize ||
! 455: fseeko(dev->image_file, (off_t)dev->nLastBlockAddr * 512L, SEEK_SET) != 0)
1.1.1.7 root 456: {
1.1.1.16! root 457: ctr->returnCode = HD_STATUS_ERROR;
! 458: dev->nLastError = HD_REQSENS_INVADDR;
1.1.1.7 root 459: }
460: else
461: {
462: /* write - if allowed */
1.1 root 463: #ifndef DISALLOW_HDC_WRITE
1.1.1.16! root 464: if (STMemory_ValidArea(nDmaAddr, 512 * HDC_GetCount(ctr)))
1.1.1.13 root 465: {
466: n = fwrite(&STRam[nDmaAddr], 512,
1.1.1.16! root 467: HDC_GetCount(ctr), dev->image_file);
1.1.1.13 root 468: }
469: else
470: {
471: Log_Printf(LOG_WARN, "HDC sector write uses invalid RAM range 0x%x+%i\n",
1.1.1.16! root 472: nDmaAddr, 512 * HDC_GetCount(ctr));
! 473: ctr->bDmaError = true;
1.1.1.13 root 474: }
1.1 root 475: #endif
1.1.1.16! root 476: if (n == HDC_GetCount(ctr))
1.1.1.11 root 477: {
1.1.1.16! root 478: ctr->returnCode = HD_STATUS_OK;
! 479: dev->nLastError = HD_REQSENS_OK;
1.1.1.11 root 480: }
481: else
482: {
1.1.1.16! root 483: ctr->returnCode = HD_STATUS_ERROR;
! 484: dev->nLastError = HD_REQSENS_WRITEERR;
1.1.1.11 root 485: }
486:
1.1.1.9 root 487: /* Update DMA counter */
1.1.1.13 root 488: FDC_WriteDMAAddress(nDmaAddr + 512*n);
1.1.1.7 root 489: }
1.1 root 490:
1.1.1.16! root 491: dev->bSetLastBlockAddr = true;
1.1 root 492: }
493:
1.1.1.7 root 494:
1.1.1.8 root 495: /**
496: * Read a sector off our disk - (implied seek)
497: */
1.1.1.16! root 498: static void HDC_Cmd_ReadSector(SCSI_CTRLR *ctr)
1.1 root 499: {
1.1.1.16! root 500: SCSI_DEV *dev = &ctr->devs[ctr->target];
! 501: Uint32 nDmaAddr = FDC_GetDMAAddress();
1.1.1.11 root 502: int n;
503:
1.1.1.16! root 504: dev->nLastBlockAddr = HDC_GetLBA(ctr);
1.1 root 505:
1.1.1.16! root 506: LOG_TRACE(TRACE_SCSI_CMD, "HDC: READ SECTOR (%s) with LBA 0x%x to 0x%x.\n",
! 507: HDC_CmdInfoStr(ctr), dev->nLastBlockAddr, nDmaAddr);
1.1 root 508:
1.1.1.7 root 509: /* seek to the position */
1.1.1.16! root 510: if (dev->nLastBlockAddr >= dev->hdSize ||
! 511: fseeko(dev->image_file, (off_t)dev->nLastBlockAddr * 512L, SEEK_SET) != 0)
1.1.1.7 root 512: {
1.1.1.16! root 513: ctr->returnCode = HD_STATUS_ERROR;
! 514: dev->nLastError = HD_REQSENS_INVADDR;
1.1.1.7 root 515: }
516: else
517: {
1.1.1.16! root 518: if (STMemory_ValidArea(nDmaAddr, 512 * HDC_GetCount(ctr)))
1.1.1.13 root 519: {
520: n = fread(&STRam[nDmaAddr], 512,
1.1.1.16! root 521: HDC_GetCount(ctr), dev->image_file);
1.1.1.13 root 522: }
523: else
524: {
525: Log_Printf(LOG_WARN, "HDC sector read uses invalid RAM range 0x%x+%i\n",
1.1.1.16! root 526: nDmaAddr, 512 * HDC_GetCount(ctr));
! 527: ctr->bDmaError = true;
1.1.1.13 root 528: n = 0;
529: }
1.1.1.16! root 530: if (n == HDC_GetCount(ctr))
1.1.1.11 root 531: {
1.1.1.16! root 532: ctr->returnCode = HD_STATUS_OK;
! 533: dev->nLastError = HD_REQSENS_OK;
1.1.1.11 root 534: }
535: else
536: {
1.1.1.16! root 537: ctr->returnCode = HD_STATUS_ERROR;
! 538: dev->nLastError = HD_REQSENS_NOSECTOR;
1.1.1.11 root 539: }
1.1.1.9 root 540:
541: /* Update DMA counter */
1.1.1.13 root 542: FDC_WriteDMAAddress(nDmaAddr + 512*n);
1.1.1.7 root 543: }
1.1 root 544:
1.1.1.16! root 545: dev->bSetLastBlockAddr = true;
1.1 root 546: }
547:
1.1.1.7 root 548:
1.1.1.8 root 549: /**
1.1.1.12 root 550: * Test unit ready
551: */
1.1.1.16! root 552: static void HDC_Cmd_TestUnitReady(SCSI_CTRLR *ctr)
1.1.1.12 root 553: {
1.1.1.16! root 554: LOG_TRACE(TRACE_SCSI_CMD, "HDC: TEST UNIT READY (%s).\n", HDC_CmdInfoStr(ctr));
! 555: ctr->returnCode = HD_STATUS_OK;
1.1.1.12 root 556: }
557:
558:
559: /**
1.1.1.8 root 560: * Emulation routine for HDC command packets.
561: */
1.1.1.16! root 562: static void HDC_EmulateCommandPacket(SCSI_CTRLR *ctr)
1.1 root 563: {
1.1.1.16! root 564: SCSI_DEV *dev = &ctr->devs[ctr->target];
1.1 root 565:
1.1.1.16! root 566: switch (ctr->opcode)
1.1.1.7 root 567: {
1.1.1.12 root 568: case HD_TEST_UNIT_RDY:
1.1.1.16! root 569: HDC_Cmd_TestUnitReady(ctr);
1.1.1.12 root 570: break;
571:
1.1.1.16! root 572: case HD_READ_CAPACITY1:
! 573: HDC_Cmd_ReadCapacity(ctr);
1.1.1.14 root 574: break;
575:
1.1.1.7 root 576: case HD_READ_SECTOR:
1.1.1.14 root 577: case HD_READ_SECTOR1:
1.1.1.16! root 578: HDC_Cmd_ReadSector(ctr);
1.1.1.7 root 579: break;
1.1.1.14 root 580:
1.1.1.7 root 581: case HD_WRITE_SECTOR:
1.1.1.14 root 582: case HD_WRITE_SECTOR1:
1.1.1.16! root 583: HDC_Cmd_WriteSector(ctr);
1.1.1.7 root 584: break;
585:
586: case HD_INQUIRY:
1.1.1.16! root 587: HDC_Cmd_Inquiry(ctr);
1.1.1.7 root 588: break;
589:
590: case HD_SEEK:
1.1.1.16! root 591: HDC_Cmd_Seek(ctr);
1.1.1.7 root 592: break;
593:
594: case HD_SHIP:
1.1.1.16! root 595: LOG_TRACE(TRACE_SCSI_CMD, "HDC: SHIP (%s).\n", HDC_CmdInfoStr(ctr));
! 596: ctr->returnCode = 0xFF;
1.1.1.7 root 597: break;
598:
599: case HD_REQ_SENSE:
1.1.1.16! root 600: HDC_Cmd_RequestSense(ctr);
1.1.1.7 root 601: break;
602:
603: case HD_MODESELECT:
1.1.1.16! root 604: LOG_TRACE(TRACE_SCSI_CMD, "HDC: MODE SELECT (%s) TODO!\n", HDC_CmdInfoStr(ctr));
! 605: ctr->returnCode = HD_STATUS_OK;
! 606: dev->nLastError = HD_REQSENS_OK;
! 607: dev->bSetLastBlockAddr = false;
1.1.1.7 root 608: break;
609:
610: case HD_MODESENSE:
1.1.1.16! root 611: HDC_Cmd_ModeSense(ctr);
1.1.1.7 root 612: break;
613:
614: case HD_FORMAT_DRIVE:
1.1.1.16! root 615: HDC_Cmd_FormatDrive(ctr);
1.1.1.7 root 616: break;
617:
618: /* as of yet unsupported commands */
619: case HD_VERIFY_TRACK:
620: case HD_FORMAT_TRACK:
621: case HD_CORRECTION:
622:
623: default:
1.1.1.16! root 624: LOG_TRACE(TRACE_SCSI_CMD, "HDC: Unsupported command (%s)!\n", HDC_CmdInfoStr(ctr));
! 625: ctr->returnCode = HD_STATUS_ERROR;
! 626: dev->nLastError = HD_REQSENS_OPCODE;
! 627: dev->bSetLastBlockAddr = false;
1.1.1.7 root 628: break;
629: }
1.1.1.9 root 630:
631: /* Update the led each time a command is processed */
1.1.1.15 root 632: Statusbar_EnableHDLed( LED_STATE_ON );
1.1 root 633: }
634:
1.1.1.7 root 635:
1.1 root 636: /*---------------------------------------------------------------------*/
1.1.1.8 root 637: /**
638: * Print data about the hard drive image
639: */
1.1.1.16! root 640: static void HDC_GetInfo(SCSI_DEV *dev)
1.1 root 641: {
1.1.1.7 root 642: /* Partition table contains hd size + 4 partition entries
643: * (composed of flag byte, 3 char ID, start offset and size),
644: * this is followed by bad sector list + count and the root sector checksum.
645: * Before this there's the boot code and with ICD hd driver additional 8
646: * partition entries (at offset 0x156).
647: */
648: #define HD_PARTITIONTABLE_SIZE (4+4*12)
649: #define HD_PARTITIONTABLE_OFFSET 0x1C2
650: long offset;
651: unsigned char hdinfo[HD_PARTITIONTABLE_SIZE];
652: int i;
1.1 root 653:
1.1.1.16! root 654: if (dev->image_file == NULL)
1.1.1.7 root 655: return;
1.1.1.16! root 656: offset = ftell(dev->image_file);
1.1.1.7 root 657:
1.1.1.16! root 658: fseek(dev->image_file, HD_PARTITIONTABLE_OFFSET, 0);
! 659: if (fread(hdinfo, HD_PARTITIONTABLE_SIZE, 1, dev->image_file) != 1)
1.1.1.11 root 660: {
661: perror("HDC_GetInfo");
662: return;
663: }
1.1 root 664:
1.1.1.14 root 665: #ifdef HDC_VERBOSE
1.1.1.16! root 666: fprintf(stderr, "Total disk size according to MBR: %i Mb\n",
! 667: HDC_ReadInt32(hdinfo, 0) >> 11);
1.1.1.7 root 668: /* flags for each partition entry are zero if they are not valid */
669: fprintf(stderr, "Partition 0 exists?: %s\n", (hdinfo[4] != 0)?"Yes":"No");
670: fprintf(stderr, "Partition 1 exists?: %s\n", (hdinfo[4+12] != 0)?"Yes":"No");
671: fprintf(stderr, "Partition 2 exists?: %s\n", (hdinfo[4+24] != 0)?"Yes":"No");
672: fprintf(stderr, "Partition 3 exists?: %s\n", (hdinfo[4+36] != 0)?"Yes":"No");
1.1 root 673: #endif
674:
1.1.1.7 root 675: for(i=0;i<4;i++)
676: if(hdinfo[4 + 12*i])
677: nPartitions++;
1.1 root 678:
1.1.1.16! root 679: fseek(dev->image_file, offset, 0);
1.1 root 680: }
681:
1.1.1.7 root 682:
1.1.1.8 root 683: /**
684: * Open the disk image file, set partitions.
1.1 root 685: */
1.1.1.12 root 686: bool HDC_Init(void)
1.1 root 687: {
1.1.1.16! root 688: off_t filesize;
! 689: int i;
1.1 root 690:
1.1.1.16! root 691: memset(&AcsiBus, 0, sizeof(AcsiBus));
! 692: nPartitions = 0;
! 693: bAcsiEmuOn = false;
1.1.1.12 root 694:
1.1.1.16! root 695: for (i = 0; i < MAX_ACSI_DEVS; i++)
1.1.1.7 root 696: {
1.1.1.16! root 697: char *filename;
1.1.1.3 root 698:
1.1.1.16! root 699: if (!ConfigureParams.Acsi[i].bUseDevice)
! 700: continue;
! 701: filename = ConfigureParams.Acsi[i].sDeviceFile;
! 702:
! 703: /* Check size for sanity - is the length a multiple of 512? */
! 704: filesize = File_Length(filename);
! 705: if (filesize <= 0 || (filesize & 0x1ff) != 0)
! 706: {
! 707: Log_Printf(LOG_ERROR, "HD file '%s' has strange size!\n",
! 708: filename);
! 709: continue;
! 710: }
! 711: AcsiBus.devs[i].hdSize = filesize / 512;
! 712:
! 713: AcsiBus.devs[i].image_file = fopen(filename, "rb+");
! 714: if (AcsiBus.devs[i].image_file == NULL)
! 715: {
! 716: Log_Printf(LOG_ERROR, "Can not open HD file '%s'!\n",
! 717: filename);
! 718: continue;
! 719: }
1.1.1.7 root 720:
1.1.1.16! root 721: HDC_GetInfo(&AcsiBus.devs[i]);
! 722: Log_Printf(LOG_INFO, "Hard drive image %s mounted.\n", filename);
! 723: AcsiBus.devs[i].enabled = true;
! 724: bAcsiEmuOn = true;
! 725: }
1.1.1.7 root 726:
727: /* set number of partitions */
728: nNumDrives += nPartitions;
729:
1.1.1.16! root 730: return bAcsiEmuOn;
1.1 root 731: }
1.1.1.3 root 732:
1.1.1.7 root 733:
1.1 root 734: /*---------------------------------------------------------------------*/
1.1.1.8 root 735: /**
736: * HDC_UnInit - close image file
737: *
1.1 root 738: */
1.1.1.4 root 739: void HDC_UnInit(void)
1.1 root 740: {
1.1.1.16! root 741: int i;
! 742:
1.1.1.7 root 743: if (!bAcsiEmuOn)
744: return;
745:
1.1.1.16! root 746: for (i = 0; i < MAX_ACSI_DEVS; i++)
! 747: {
! 748: if (!AcsiBus.devs[i].enabled)
! 749: continue;
! 750: fclose(AcsiBus.devs[i].image_file);
! 751: AcsiBus.devs[i].image_file = NULL;
! 752: AcsiBus.devs[i].enabled = false;
! 753: }
1.1.1.7 root 754:
755: nNumDrives -= nPartitions;
756: nPartitions = 0;
1.1.1.11 root 757: bAcsiEmuOn = false;
1.1 root 758: }
759:
1.1.1.7 root 760:
1.1 root 761: /*---------------------------------------------------------------------*/
1.1.1.8 root 762: /**
1.1.1.14 root 763: * Reset command status.
764: */
765: void HDC_ResetCommandStatus(void)
766: {
1.1.1.16! root 767: if (ConfigureParams.System.nMachineType != MACHINE_FALCON)
! 768: AcsiBus.returnCode = 0;
1.1.1.14 root 769: }
770:
771:
772: /**
1.1.1.16! root 773: * Process HDC command packets (SCSI/ACSI) bytes.
1.1.1.14 root 774: */
1.1.1.16! root 775: static void HDC_WriteCommandPacket(SCSI_CTRLR *ctr, Uint8 b)
1.1.1.14 root 776: {
1.1.1.16! root 777: SCSI_DEV *dev = &ctr->devs[ctr->target];
! 778:
! 779: /* Abort if the target device is not enabled */
! 780: if (!dev->enabled)
! 781: {
! 782: ctr->returnCode = HD_STATUS_ERROR;
! 783: return;
! 784: }
! 785:
! 786: /* Extract ACSI/SCSI opcode */
! 787: if (ctr->byteCount == 0)
! 788: {
! 789: ctr->opcode = b;
! 790: ctr->bDmaError = false;
! 791: }
1.1.1.14 root 792:
1.1.1.16! root 793: /* Successfully received one byte, and increase the byte-count */
! 794: if (ctr->byteCount < (int)sizeof(ctr->command))
! 795: ctr->command[ctr->byteCount] = b;
! 796: ++ctr->byteCount;
! 797:
! 798: /* have we received a complete 6-byte class 0 or 10-byte class 1 packet yet? */
! 799: if ((ctr->opcode < 0x20 && ctr->byteCount >= 6) ||
! 800: (ctr->opcode < 0x60 && ctr->byteCount >= 10))
! 801: {
! 802: /* We currently only support LUN 0, however INQUIRY must
! 803: * always be handled, see SCSI standard */
! 804: if (HDC_GetLUN(ctr) == 0 || ctr->opcode == HD_INQUIRY)
! 805: {
! 806: HDC_EmulateCommandPacket(ctr);
! 807: }
! 808: else
! 809: {
! 810: Log_Printf(LOG_WARN, "HDC: Access to non-existing LUN."
! 811: " Command = 0x%02x\n", ctr->opcode);
! 812: dev->nLastError = HD_REQSENS_INVLUN;
! 813: /* REQUEST SENSE is still handled for invalid LUNs */
! 814: if (ctr->opcode == HD_REQ_SENSE)
! 815: HDC_Cmd_RequestSense(ctr);
! 816: else
! 817: ctr->returnCode = HD_STATUS_ERROR;
! 818: }
! 819:
! 820: ctr->byteCount = 0;
! 821: }
! 822: else if (ctr->opcode >= 0x60)
! 823: {
! 824: /* Commands >= 0x60 are not supported right now */
! 825: ctr->returnCode = HD_STATUS_ERROR;
! 826: dev->nLastError = HD_REQSENS_OPCODE;
! 827: dev->bSetLastBlockAddr = false;
! 828: if (ctr->byteCount == 10)
! 829: {
! 830: LOG_TRACE(TRACE_SCSI_CMD, "HDC: Unsupported command (%s).\n",
! 831: HDC_CmdInfoStr(ctr));
! 832: }
! 833: }
! 834: else
! 835: {
! 836: ctr->returnCode = HD_STATUS_OK;
! 837: }
! 838: }
1.1.1.14 root 839:
840: /*---------------------------------------------------------------------*/
1.1.1.16! root 841:
! 842: static struct ncr5380_regs
! 843: {
! 844: Uint8 initiator_cmd;
! 845: Uint8 current_bus_status;
! 846: Uint8 bus_and_status;
! 847: } ncr_regs;
! 848:
1.1.1.14 root 849: /**
1.1.1.16! root 850: * Emulate external reset "pin": Clear registers etc.
1.1.1.14 root 851: */
1.1.1.16! root 852: void Ncr5380_Reset(void)
1.1.1.14 root 853: {
1.1.1.16! root 854: ncr_regs.initiator_cmd &= 0x7f;
1.1.1.14 root 855: }
856:
857: /**
1.1.1.16! root 858: * Write a command byte to the NCR 5380 SCSI controller
1.1.1.8 root 859: */
1.1.1.16! root 860: static void Ncr5380_WriteByte(int addr, Uint8 byte)
1.1 root 861: {
1.1.1.16! root 862: switch (addr)
1.1.1.14 root 863: {
1.1.1.16! root 864: case 0: /* Output Data register */
! 865: ncr_regs.current_bus_status |= 0x40;
! 866: break;
! 867: case 1: /* Initiator Command register */
! 868: ncr_regs.initiator_cmd = byte;
! 869: break;
! 870: case 2: /* Mode register */
! 871: break;
! 872: case 3: /* Target Command register */
! 873: break;
! 874: case 4: /* Select Enable register */
! 875: break;
! 876: case 5: /* Start DMA Send register */
! 877: break;
! 878: case 6: /* Start DMA Target Receive register */
! 879: break;
! 880: case 7: /* Start DMA Initiator Receive register */
! 881: break;
! 882: default:
! 883: fprintf(stderr, "Unexpected NCR5380 address\n");
1.1.1.14 root 884: }
1.1.1.16! root 885: }
1.1.1.12 root 886:
1.1.1.16! root 887: /**
! 888: * Read a command byte from the NCR 5380 SCSI controller
! 889: */
! 890: static Uint8 Ncr5380_ReadByte(int addr)
! 891: {
! 892: switch (addr)
1.1.1.12 root 893: {
1.1.1.16! root 894: case 0: /* Current SCSI Data register */
! 895: break;
! 896: case 1: /* Initiator Command register */
! 897: return ncr_regs.initiator_cmd & 0x9f;
! 898: case 2: /* Mode register */
! 899: break;
! 900: case 3: /* Target Command register */
! 901: break;
! 902: case 4: /* Current SCSI Bus Status register */
! 903: if (ncr_regs.current_bus_status & 0x40) /* BUSY? */
! 904: ncr_regs.current_bus_status |= 0x20;
! 905: else
! 906: ncr_regs.current_bus_status &= ~0x20;
! 907: if (ncr_regs.initiator_cmd & 0x80) /* ASSERT RST? */
! 908: ncr_regs.current_bus_status |= 0x80;
! 909: else
! 910: ncr_regs.current_bus_status &= ~0x80;
! 911: if (ncr_regs.initiator_cmd & 0x04) /* ASSERT BUSY? */
! 912: ncr_regs.current_bus_status |= 0x40;
! 913: else
! 914: ncr_regs.current_bus_status &= ~0x40;
! 915: return ncr_regs.current_bus_status;
! 916: case 5: /* Bus and Status register */
! 917: return ncr_regs.bus_and_status;
! 918: case 6: /* Input Data register */
! 919: break;
! 920: case 7: /* Reset Parity/Interrupts register */
! 921: /* Reset PARITY ERROR, IRQ REQUEST and BUSY ERROR bits */
! 922: ncr_regs.bus_and_status &= 0xcb;
! 923: return 0; /* TODO: Is this return value ok? */
! 924: default:
! 925: fprintf(stderr, "Unexpected NCR5380 address\n");
1.1.1.12 root 926: }
927:
1.1.1.16! root 928: return 0;
! 929: }
1.1.1.7 root 930:
1.1.1.16! root 931:
! 932: static void Acsi_WriteCommandByte(int addr, Uint8 byte)
! 933: {
! 934: /* When the A1 pin is pushed to 0, we want to start a new command */
! 935: if ((addr & 2) == 0)
1.1.1.7 root 936: {
1.1.1.16! root 937: AcsiBus.byteCount = 0;
! 938: AcsiBus.target = ((byte & 0xE0) >> 5);
! 939: /* Only process the first byte if it is not
! 940: * an extended ICD command marker byte */
! 941: if ((byte & 0x1F) != 0x1F)
1.1.1.7 root 942: {
1.1.1.16! root 943: HDC_WriteCommandPacket(&AcsiBus, byte & 0x1F);
1.1.1.7 root 944: }
945: else
946: {
1.1.1.16! root 947: AcsiBus.returnCode = HD_STATUS_OK;
! 948: AcsiBus.bDmaError = false;
1.1.1.7 root 949: }
1.1.1.16! root 950: }
! 951: else
! 952: {
! 953: /* Process normal command byte */
! 954: HDC_WriteCommandPacket(&AcsiBus, byte);
! 955: }
1.1.1.7 root 956:
1.1.1.16! root 957: if (AcsiBus.devs[AcsiBus.target].enabled)
! 958: {
! 959: FDC_SetDMAStatus(AcsiBus.bDmaError); /* Mark DMA error */
! 960: FDC_SetIRQ(FDC_IRQ_SOURCE_HDC);
1.1.1.7 root 961: }
962: else
963: {
1.1.1.16! root 964: /* If there's no target, the interrupt line stays high */
! 965: MFP_GPIP |= 0x20;
1.1.1.7 root 966: }
1.1 root 967: }
1.1.1.16! root 968:
! 969: /**
! 970: * Called when command bytes have been written to $FFFF8606 and
! 971: * the HDC (not the FDC) is selected.
! 972: */
! 973: void HDC_WriteCommandByte(int addr, Uint8 byte)
! 974: {
! 975: // fprintf(stderr, "HDC: Write cmd byte addr=%i, byte=%02x\n", addr, byte);
! 976:
! 977: if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
! 978: Ncr5380_WriteByte(addr, byte);
! 979: else if (bAcsiEmuOn)
! 980: Acsi_WriteCommandByte(addr, byte);
! 981: }
! 982:
! 983: /**
! 984: * Get command byte.
! 985: */
! 986: short int HDC_ReadCommandByte(int addr)
! 987: {
! 988: Uint16 ret;
! 989: if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
! 990: ret = Ncr5380_ReadByte(addr);
! 991: else
! 992: ret = AcsiBus.returnCode; /* ACSI status */
! 993: return ret;
! 994: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.