|
|
1.1 ! root 1: /* ! 2: * Hatari - NCR 5380 SCSI controller emulation ! 3: * ! 4: * Based on scsi.ccp from WinUAE: ! 5: * ! 6: * Copyright 2007-2015 Toni Wilen ! 7: * ! 8: * Adaptions to Hatari: ! 9: * ! 10: * Copyright 2018 Thomas Huth ! 11: * ! 12: * This file is distributed under the GNU General Public License, version 2 ! 13: * or at your option any later version. Read the file gpl.txt for details. ! 14: */ ! 15: const char NCR5380_fileid[] = "Hatari ncr5380.c"; ! 16: ! 17: #include "main.h" ! 18: #include "configuration.h" ! 19: #include "cycInt.h" ! 20: #include "file.h" ! 21: #include "fdc.h" ! 22: #include "hdc.h" ! 23: #include "hatari-glue.h" ! 24: #include "ioMem.h" ! 25: #include "log.h" ! 26: #include "memorySnapShot.h" ! 27: #include "mfp.h" ! 28: #include "ncr5380.h" ! 29: #include "stMemory.h" ! 30: #include "newcpu.h" ! 31: ! 32: #define WITH_NCR5380 1 ! 33: ! 34: static SCSI_CTRLR ScsiBus; ! 35: ! 36: #define MAX_TOTAL_SCSI_DEVICES 8 ! 37: ! 38: #define RAW_SCSI_DEBUG 2 ! 39: #define NCR5380_DEBUG 1 ! 40: #define NCR5380_DEBUG_IRQ 0 ! 41: ! 42: #ifndef _T ! 43: #define _T(x) x ! 44: #endif ! 45: ! 46: // raw scsi ! 47: ! 48: #define SCSI_IO_BUSY 0x80 ! 49: #define SCSI_IO_ATN 0x40 ! 50: #define SCSI_IO_SEL 0x20 ! 51: #define SCSI_IO_REQ 0x10 ! 52: #define SCSI_IO_DIRECTION 0x01 ! 53: #define SCSI_IO_COMMAND 0x02 ! 54: #define SCSI_IO_MESSAGE 0x04 ! 55: ! 56: #define SCSI_SIGNAL_PHASE_FREE -1 ! 57: #define SCSI_SIGNAL_PHASE_ARBIT -2 ! 58: #define SCSI_SIGNAL_PHASE_SELECT_1 -3 ! 59: #define SCSI_SIGNAL_PHASE_SELECT_2 -4 ! 60: ! 61: #define SCSI_SIGNAL_PHASE_DATA_OUT 0 ! 62: #define SCSI_SIGNAL_PHASE_DATA_IN 1 ! 63: #define SCSI_SIGNAL_PHASE_COMMAND 2 ! 64: #define SCSI_SIGNAL_PHASE_STATUS 3 ! 65: #define SCSI_SIGNAL_PHASE_MESSAGE_OUT 6 ! 66: #define SCSI_SIGNAL_PHASE_MESSAGE_IN 7 ! 67: ! 68: #define SCSI_STATUS_GOOD 0x00 ! 69: #define SCSI_STATUS_CHECK_CONDITION 0x02 ! 70: #define SCSI_STATUS_CONDITION_MET 0x04 ! 71: #define SCSI_STATUS_BUSY 0x08 ! 72: #define SCSI_STATUS_INTERMEDIATE 0x10 ! 73: #define SCSI_STATUS_ICM 0x14 /* intermediate condition met */ ! 74: #define SCSI_STATUS_RESERVATION_CONFLICT 0x18 ! 75: #define SCSI_STATUS_COMMAND_TERMINATED 0x22 ! 76: #define SCSI_STATUS_QUEUE_FULL 0x28 ! 77: #define SCSI_STATUS_ACA_ACTIVE 0x30 ! 78: ! 79: struct raw_scsi ! 80: { ! 81: int io; ! 82: int bus_phase; ! 83: bool atn; ! 84: bool ack; ! 85: uae_u8 data_write; ! 86: uae_u8 status; ! 87: bool databusoutput; ! 88: int initiator_id, target_id; ! 89: struct scsi_data *device[MAX_TOTAL_SCSI_DEVICES]; ! 90: struct scsi_data *target; ! 91: int msglun; ! 92: }; ! 93: ! 94: struct soft_scsi ! 95: { ! 96: uae_u8 regs[9]; ! 97: struct raw_scsi rscsi; ! 98: bool irq; ! 99: ! 100: int dma_direction; ! 101: bool dma_active; ! 102: bool dma_started; ! 103: bool dma_controller; ! 104: bool dma_drq; ! 105: ! 106: int dmac_direction; ! 107: int dmac_active; ! 108: }; ! 109: ! 110: struct soft_scsi ncr_soft_scsi; ! 111: ! 112: static const uae_s16 outcmd[] = { 0x04, 0x0a, 0x0c, 0x11, 0x2a, 0xaa, 0x15, 0x55, 0x0f, -1 }; ! 113: static const uae_s16 incmd[] = { 0x01, 0x03, 0x08, 0x0e, 0x12, 0x1a, 0x5a, 0x25, 0x28, 0x34, 0x37, 0x42, 0x43, 0xa8, 0x51, 0x52, 0xb9, 0xbd, 0xd8, 0xd9, 0xbe, -1 }; ! 114: static const uae_s16 nonecmd[] = { 0x00, 0x05, 0x06, 0x07, 0x09, 0x0b, 0x10, 0x16, 0x17, 0x19, 0x1b, 0x1d, 0x1e, 0x2b, 0x35, 0x45, 0x47, 0x48, 0x49, 0x4b, 0x4e, 0xa5, 0xa9, 0xba, 0xbc, 0xe0, 0xe3, 0xe4, -1 }; ! 115: static const uae_s16 scsicmdsizes[] = { 6, 10, 10, 12, 16, 12, 10, 6 }; ! 116: ! 117: static uae_u8 ncr5380_bget(struct soft_scsi *scsi, int reg); ! 118: void ncr5380_bput(struct soft_scsi *scsi, int reg, uae_u8 v); ! 119: ! 120: static int scsi_data_dir(struct scsi_data *sd) ! 121: { ! 122: int i; ! 123: uae_u8 cmd; ! 124: ! 125: cmd = sd->cmd[0]; ! 126: for (i = 0; outcmd[i] >= 0; i++) { ! 127: if (cmd == outcmd[i]) { ! 128: return 1; ! 129: } ! 130: } ! 131: for (i = 0; incmd[i] >= 0; i++) { ! 132: if (cmd == incmd[i]) { ! 133: return -1; ! 134: } ! 135: } ! 136: for (i = 0; nonecmd[i] >= 0; i++) { ! 137: if (cmd == nonecmd[i]) { ! 138: return 0; ! 139: } ! 140: } ! 141: write_log (_T("SCSI command %02X, no direction specified!\n"), cmd); ! 142: return 0; ! 143: } ! 144: ! 145: static void scsi_start_transfer(struct scsi_data *sd) ! 146: { ! 147: // sd->offset = 0; ! 148: ScsiBus.offset = 0; ! 149: } ! 150: ! 151: static int scsi_send_data(struct scsi_data *sd, uae_u8 b) ! 152: { ! 153: if (ScsiBus.offset < 0) { ! 154: write_log(_T("SCSI data offset is negative!\n")); ! 155: return 0; ! 156: } ! 157: if (sd->direction == 1) { ! 158: if (ScsiBus.offset >= ScsiBus.buffer_size) { ! 159: write_log (_T("SCSI data buffer overflow!\n")); ! 160: return 0; ! 161: } ! 162: ScsiBus.buffer[ScsiBus.offset++] = b; ! 163: } else if (sd->direction == 2) { ! 164: if (ScsiBus.offset >= 16) { ! 165: write_log (_T("SCSI command buffer overflow!\n")); ! 166: return 0; ! 167: } ! 168: sd->cmd[ScsiBus.offset++] = b; ! 169: if (ScsiBus.offset == sd->cmd_len) ! 170: return 1; ! 171: } else { ! 172: write_log (_T("scsi_send_data() without direction! (%02X)\n"), b); ! 173: return 0; ! 174: } ! 175: if (ScsiBus.offset == ScsiBus.data_len) ! 176: return 1; ! 177: return 0; ! 178: } ! 179: ! 180: static int scsi_receive_data(struct scsi_data *sd, uae_u8 *b, bool next) ! 181: { ! 182: if (!ScsiBus.data_len) { ! 183: fprintf(stderr, "scsi_receive_data without length!\n"); ! 184: return -1; ! 185: } ! 186: *b = ScsiBus.buffer[ScsiBus.offset]; ! 187: // fprintf(stderr,"scsi_receive_data %i <-> %i (%i)\n", ! 188: // ScsiBus.offset, ScsiBus.data_len, next); ! 189: if (next) { ! 190: ScsiBus.offset++; ! 191: if (ScsiBus.offset == ScsiBus.data_len) ! 192: return 1; // requested length got ! 193: } ! 194: return 0; ! 195: } ! 196: ! 197: static void bus_free(struct raw_scsi *rs) ! 198: { ! 199: // fprintf(stderr, "BUS FREE!\n"); ! 200: rs->bus_phase = SCSI_SIGNAL_PHASE_FREE; ! 201: rs->io = 0; ! 202: } ! 203: ! 204: static int getbit(uae_u8 v) ! 205: { ! 206: int i; ! 207: ! 208: for (i = 7; i >= 0; i--) { ! 209: if ((1 << i) & v) ! 210: return i; ! 211: } ! 212: return -1; ! 213: } ! 214: ! 215: static int countbits(uae_u8 v) ! 216: { ! 217: int i, cnt = 0; ! 218: ! 219: for (i = 7; i >= 0; i--) { ! 220: if ((1 << i) & v) ! 221: cnt++; ! 222: } ! 223: return cnt; ! 224: } ! 225: ! 226: static void raw_scsi_reset_bus(struct soft_scsi *scsi) ! 227: { ! 228: //struct raw_scsi *r = &scsi->rscsi; ! 229: #if RAW_SCSI_DEBUG ! 230: write_log(_T("SCSI BUS reset\n")); ! 231: #endif ! 232: #if 0 /* FIXME */ ! 233: for (int i = 0; i < 8; i++) { ! 234: scsi_emulate_reset_device(r->device[i]); ! 235: } ! 236: #endif ! 237: } ! 238: ! 239: ! 240: static void raw_scsi_set_databus(struct raw_scsi *rs, bool databusoutput) ! 241: { ! 242: rs->databusoutput = databusoutput; ! 243: } ! 244: ! 245: static void raw_scsi_set_signal_phase(struct raw_scsi *rs, bool busy, bool select, bool atn) ! 246: { ! 247: // fprintf(stderr,"raw_scsi_set_signal_phase busy=%i sel=%i atn=%i phase=%i\n", ! 248: // busy, select, atn, rs->bus_phase); ! 249: switch (rs->bus_phase) ! 250: { ! 251: case SCSI_SIGNAL_PHASE_FREE: ! 252: if (busy && !select && !rs->databusoutput) { ! 253: if (countbits(rs->data_write) != 1) { ! 254: #if RAW_SCSI_DEBUG ! 255: write_log(_T("raw_scsi: invalid arbitration scsi id mask! (%02x)\n"), rs->data_write); ! 256: #endif ! 257: return; ! 258: } ! 259: rs->bus_phase = SCSI_SIGNAL_PHASE_ARBIT; ! 260: rs->initiator_id = getbit(rs->data_write); ! 261: #if RAW_SCSI_DEBUG ! 262: write_log(_T("raw_scsi: arbitration initiator id %d (%02x)\n"), rs->initiator_id, rs->data_write); ! 263: #endif ! 264: } else if (!busy && select) { ! 265: if (countbits(rs->data_write) > 2 || rs->data_write == 0) { ! 266: #if RAW_SCSI_DEBUG ! 267: write_log(_T("raw_scsi: invalid scsi id selected mask (%02x)\n"), rs->data_write); ! 268: #endif ! 269: return; ! 270: } ! 271: rs->initiator_id = -1; ! 272: rs->bus_phase = SCSI_SIGNAL_PHASE_SELECT_1; ! 273: #if RAW_SCSI_DEBUG ! 274: write_log(_T("raw_scsi: selected scsi id mask (%02x)\n"), rs->data_write); ! 275: #endif ! 276: raw_scsi_set_signal_phase(rs, busy, select, atn); ! 277: } ! 278: break; ! 279: case SCSI_SIGNAL_PHASE_ARBIT: ! 280: rs->target_id = -1; ! 281: rs->target = NULL; ! 282: ScsiBus.target = -1; ! 283: if (busy && select) { ! 284: rs->bus_phase = SCSI_SIGNAL_PHASE_SELECT_1; ! 285: } ! 286: break; ! 287: case SCSI_SIGNAL_PHASE_SELECT_1: ! 288: rs->atn = atn; ! 289: rs->msglun = -1; ! 290: rs->target_id = -1; ! 291: ScsiBus.target = -1; ! 292: if (!busy) { ! 293: int i; ! 294: for (i = 0; i < 8; i++) { ! 295: if (i == rs->initiator_id) ! 296: continue; ! 297: if ((rs->data_write & (1 << i)) && rs->device[i]) { ! 298: rs->target_id = i; ! 299: rs->target = rs->device[rs->target_id]; ! 300: ScsiBus.target = i; ! 301: #if RAW_SCSI_DEBUG ! 302: write_log(_T("raw_scsi: selected id %d\n"), rs->target_id); ! 303: #endif ! 304: rs->io |= SCSI_IO_BUSY; ! 305: } ! 306: } ! 307: #if RAW_SCSI_DEBUG ! 308: if (rs->target_id < 0) { ! 309: int i; ! 310: for (i = 0; i < 8; i++) { ! 311: if (i == rs->initiator_id) ! 312: continue; ! 313: if ((rs->data_write & (1 << i)) && !rs->device[i]) { ! 314: write_log(_T("raw_scsi: selected non-existing id %d\n"), i); ! 315: } ! 316: } ! 317: } ! 318: #endif ! 319: if (rs->target_id >= 0) { ! 320: rs->bus_phase = SCSI_SIGNAL_PHASE_SELECT_2; ! 321: } else { ! 322: if (!select) { ! 323: rs->bus_phase = SCSI_SIGNAL_PHASE_FREE; ! 324: } ! 325: } ! 326: } ! 327: break; ! 328: case SCSI_SIGNAL_PHASE_SELECT_2: ! 329: if (!select) { ! 330: scsi_start_transfer(rs->target); ! 331: rs->bus_phase = rs->atn ? SCSI_SIGNAL_PHASE_MESSAGE_OUT : SCSI_SIGNAL_PHASE_COMMAND; ! 332: rs->io = SCSI_IO_BUSY | SCSI_IO_REQ; ! 333: } ! 334: break; ! 335: } ! 336: } ! 337: ! 338: static uae_u8 raw_scsi_get_signal_phase(struct raw_scsi *rs) ! 339: { ! 340: uae_u8 v = rs->io; ! 341: if (rs->bus_phase >= 0) ! 342: v |= rs->bus_phase; ! 343: if (rs->ack) ! 344: v &= ~SCSI_IO_REQ; ! 345: return v; ! 346: } ! 347: ! 348: static uae_u8 raw_scsi_get_data_2(struct raw_scsi *rs, bool next, bool nodebug) ! 349: { ! 350: struct scsi_data *sd = rs->target; ! 351: uae_u8 v = 0; ! 352: ! 353: switch (rs->bus_phase) ! 354: { ! 355: case SCSI_SIGNAL_PHASE_FREE: ! 356: v = 0; ! 357: break; ! 358: case SCSI_SIGNAL_PHASE_ARBIT: ! 359: #if RAW_SCSI_DEBUG ! 360: write_log(_T("raw_scsi: arbitration\n")); ! 361: #endif ! 362: v = rs->data_write; ! 363: break; ! 364: case SCSI_SIGNAL_PHASE_DATA_IN: ! 365: #if RAW_SCSI_DEBUG > 2 ! 366: scsi_receive_data(sd, &v, false); ! 367: write_log(_T("raw_scsi: read data byte %02x (%d/%d)\n"), v, ScsiBus.offset, ScsiBus.data_len); ! 368: #endif ! 369: if (scsi_receive_data(sd, &v, next)) { ! 370: #if RAW_SCSI_DEBUG ! 371: write_log(_T("raw_scsi: data in finished, %d bytes: status phase\n"), ScsiBus.offset); ! 372: #endif ! 373: rs->bus_phase = SCSI_SIGNAL_PHASE_STATUS; ! 374: } ! 375: break; ! 376: case SCSI_SIGNAL_PHASE_STATUS: ! 377: #if RAW_SCSI_DEBUG ! 378: if (!nodebug || next) ! 379: write_log(_T("raw_scsi: status byte read %02x. Next=%d\n"), ScsiBus.status, next); ! 380: #endif ! 381: v = ScsiBus.status; // sd->status; ! 382: if (next) { ! 383: ScsiBus.status = 0; // sd->status = 0; ! 384: rs->bus_phase = SCSI_SIGNAL_PHASE_MESSAGE_IN; ! 385: } ! 386: break; ! 387: case SCSI_SIGNAL_PHASE_MESSAGE_IN: ! 388: #if RAW_SCSI_DEBUG ! 389: if (!nodebug || next) ! 390: write_log(_T("raw_scsi: message byte read %02x. Next=%d\n"), ScsiBus.status, next); ! 391: #endif ! 392: v = ScsiBus.status; // sd->status; ! 393: rs->status = v; ! 394: if (next) { ! 395: bus_free(rs); ! 396: } ! 397: break; ! 398: default: ! 399: #if RAW_SCSI_DEBUG ! 400: write_log(_T("raw_scsi_get_data but bus phase is %d!\n"), rs->bus_phase); ! 401: #endif ! 402: break; ! 403: } ! 404: ! 405: return v; ! 406: } ! 407: ! 408: static uae_u8 raw_scsi_get_data(struct raw_scsi *rs, bool next) ! 409: { ! 410: return raw_scsi_get_data_2(rs, next, true); ! 411: } ! 412: ! 413: static int getmsglen(uae_u8 *msgp, int len) ! 414: { ! 415: uae_u8 msg = msgp[0]; ! 416: if (msg == 0 || (msg >= 0x02 && msg <= 0x1f) || msg >= 0x80) ! 417: return 1; ! 418: if (msg >= 0x20 && msg <= 0x2f) ! 419: return 2; ! 420: // extended message, at least 3 bytes ! 421: if (len < 2) ! 422: return 3; ! 423: return msgp[1]; ! 424: } ! 425: ! 426: static bool scsi_emulate_analyze (struct scsi_data *sd) ! 427: { ! 428: int cmd_len, data_len; ! 429: ! 430: data_len = ScsiBus.data_len; ! 431: cmd_len = scsicmdsizes[sd->cmd[0] >> 5]; ! 432: sd->cmd_len = cmd_len; ! 433: switch (sd->cmd[0]) ! 434: { ! 435: case 0x04: // FORMAT UNIT ! 436: // FmtData set? ! 437: if (sd->cmd[1] & 0x10) { ! 438: // int cl = (sd->cmd[1] & 8) != 0; ! 439: // int dlf = sd->cmd[1] & 7; ! 440: //data_len2 = 4; ! 441: } else { ! 442: sd->direction = 0; ! 443: ScsiBus.data_len = 0; ! 444: return true; ! 445: } ! 446: break; ! 447: case 0x06: // FORMAT TRACK ! 448: case 0x07: // FORMAT BAD TRACK ! 449: sd->direction = 0; ! 450: ScsiBus.data_len = 0; ! 451: return true; ! 452: case 0x0c: // INITIALIZE DRIVE CHARACTERICS (SASI) ! 453: data_len = 8; ! 454: break; ! 455: case 0x08: // READ(6) ! 456: break; ! 457: case 0x11: // ASSIGN ALTERNATE TRACK (SASI) ! 458: data_len = 4; ! 459: break; ! 460: case 0x28: // READ(10) ! 461: break; ! 462: case 0xa8: // READ(12) ! 463: break; ! 464: case 0x0f: // WRITE SECTOR BUFFER ! 465: data_len = 512; //sd->blocksize; ! 466: break; ! 467: case 0x0a: // WRITE(6) ! 468: data_len = (sd->cmd[4] == 0 ? 256 : sd->cmd[4]) * 512; //sd->blocksize; ! 469: break; ! 470: case 0x2a: // WRITE(10) ! 471: data_len = ((sd->cmd[7] << 8) | (sd->cmd[8] << 0)) * 512; //sd->blocksize; ! 472: break; ! 473: /* ! 474: case 0xaa: // WRITE(12) ! 475: data_len = ((sd->cmd[6] << 24) | (sd->cmd[7] << 16) | (sd->cmd[8] << 8) | (sd->cmd[9] << 0)) * 512; //sd->blocksize; ! 476: break; ! 477: */ ! 478: case 0x2f: // VERIFY ! 479: if (sd->cmd[1] & 2) { ! 480: ScsiBus.data_len = ((sd->cmd[7] << 8) | (sd->cmd[8] << 0)) * 512; // sd->blocksize; ! 481: sd->direction = 1; ! 482: } else { ! 483: ScsiBus.data_len = 0; ! 484: sd->direction = 0; ! 485: } ! 486: return true; ! 487: } ! 488: if (data_len < 0) { ! 489: if (cmd_len == 6) { ! 490: ScsiBus.data_len = sd->cmd[4]; ! 491: } else { ! 492: ScsiBus.data_len = (sd->cmd[7] << 8) | sd->cmd[8]; ! 493: } ! 494: } else { ! 495: ScsiBus.data_len = data_len; ! 496: } ! 497: sd->direction = scsi_data_dir(sd); ! 498: if (sd->direction > 0 && ScsiBus.data_len == 0) { ! 499: sd->direction = 0; ! 500: } ! 501: return true; ! 502: } ! 503: ! 504: static void scsi_emulate_cmd(struct scsi_data *sd) ! 505: { ! 506: int i; ! 507: ! 508: // fprintf(stderr,"scsi_emulate_cmd cmdlen=%i offset=%i\n", sd->cmd_len, ScsiBus.offset); ! 509: ScsiBus.byteCount = 0; ! 510: for (i = 0; i < sd->cmd_len; i++) ! 511: { ! 512: HDC_WriteCommandPacket(&ScsiBus, sd->cmd[i]); ! 513: } ! 514: } ! 515: ! 516: static void raw_scsi_write_data(struct raw_scsi *rs, uae_u8 data) ! 517: { ! 518: struct scsi_data *sd = rs->target; ! 519: int len; ! 520: ! 521: switch (rs->bus_phase) ! 522: { ! 523: case SCSI_SIGNAL_PHASE_SELECT_1: ! 524: case SCSI_SIGNAL_PHASE_FREE: ! 525: break; ! 526: case SCSI_SIGNAL_PHASE_COMMAND: ! 527: sd->cmd[ScsiBus.offset++] = data; ! 528: len = scsicmdsizes[sd->cmd[0] >> 5]; ! 529: #if RAW_SCSI_DEBUG > 1 ! 530: write_log(_T("raw_scsi: got command byte %02x (%d/%d)\n"), data, ScsiBus.offset, len); ! 531: #endif ! 532: if (ScsiBus.offset >= len) { ! 533: if (rs->msglun >= 0) { ! 534: sd->cmd[1] &= ~(0x80 | 0x40 | 0x20); ! 535: sd->cmd[1] |= rs->msglun << 5; ! 536: } ! 537: scsi_emulate_analyze(rs->target); ! 538: if (sd->direction > 0) { ! 539: #if RAW_SCSI_DEBUG ! 540: write_log(_T("raw_scsi: data out %d bytes required\n"), ScsiBus.data_len); ! 541: #endif ! 542: scsi_emulate_cmd(sd); /* Hatari only */ ! 543: scsi_start_transfer(sd); ! 544: rs->bus_phase = SCSI_SIGNAL_PHASE_DATA_OUT; ! 545: } else if (sd->direction <= 0) { ! 546: scsi_emulate_cmd(sd); ! 547: scsi_start_transfer(sd); ! 548: if (!ScsiBus.status && ScsiBus.data_len > 0) { ! 549: #if RAW_SCSI_DEBUG ! 550: write_log(_T("raw_scsi: data in %d bytes waiting\n"), ScsiBus.data_len); ! 551: #endif ! 552: rs->bus_phase = SCSI_SIGNAL_PHASE_DATA_IN; ! 553: } else { ! 554: #if RAW_SCSI_DEBUG ! 555: write_log(_T("raw_scsi: no data, status = %d\n"), ScsiBus.status); ! 556: #endif ! 557: rs->bus_phase = SCSI_SIGNAL_PHASE_STATUS; ! 558: } ! 559: } ! 560: } ! 561: break; ! 562: case SCSI_SIGNAL_PHASE_DATA_OUT: ! 563: #if RAW_SCSI_DEBUG > 2 ! 564: write_log(_T("raw_scsi: write data byte %02x (%d/%d)\n"), data, ScsiBus.offset, ScsiBus.data_len); ! 565: #endif ! 566: if (scsi_send_data(sd, data)) { ! 567: #if RAW_SCSI_DEBUG ! 568: write_log(_T("raw_scsi: data out finished, %d bytes\n"), ScsiBus.data_len); ! 569: #endif ! 570: if (ScsiBus.dmawrite_to_fh) ! 571: { ! 572: int r; ! 573: r = fwrite(ScsiBus.buffer, 1, ScsiBus.data_len, ScsiBus.dmawrite_to_fh); ! 574: if (r != ScsiBus.data_len) ! 575: { ! 576: Log_Printf(LOG_ERROR, "Could not write bytes to HD image (%d/%d).\n", ! 577: r, ScsiBus.data_len); ! 578: ScsiBus.status = HD_STATUS_ERROR; ! 579: } ! 580: ScsiBus.dmawrite_to_fh = NULL; ! 581: } ! 582: ! 583: rs->bus_phase = SCSI_SIGNAL_PHASE_STATUS; ! 584: } ! 585: break; ! 586: case SCSI_SIGNAL_PHASE_MESSAGE_OUT: ! 587: sd->msgout[ScsiBus.offset++] = data; ! 588: len = getmsglen(sd->msgout, ScsiBus.offset); ! 589: #if RAW_SCSI_DEBUG ! 590: write_log(_T("raw_scsi_put_data got message %02x (%d/%d)\n"), data, ScsiBus.offset, len); ! 591: #endif ! 592: if (ScsiBus.offset >= len) { ! 593: #if RAW_SCSI_DEBUG ! 594: write_log(_T("raw_scsi_put_data got message %02x (%d bytes)\n"), sd->msgout[0], len); ! 595: #endif ! 596: if ((sd->msgout[0] & (0x80 | 0x20)) == 0x80) ! 597: rs->msglun = sd->msgout[0] & 7; ! 598: scsi_start_transfer(sd); ! 599: rs->bus_phase = SCSI_SIGNAL_PHASE_COMMAND; ! 600: } ! 601: break; ! 602: default: ! 603: #if RAW_SCSI_DEBUG ! 604: write_log(_T("raw_scsi_put_data but bus phase is %d!\n"), rs->bus_phase); ! 605: #endif ! 606: break; ! 607: } ! 608: } ! 609: ! 610: static void raw_scsi_put_data(struct raw_scsi *rs, uae_u8 data, bool databusoutput) ! 611: { ! 612: rs->data_write = data; ! 613: if (!databusoutput) ! 614: return; ! 615: raw_scsi_write_data(rs, data); ! 616: } ! 617: ! 618: static void raw_scsi_set_ack(struct raw_scsi *rs, bool ack) ! 619: { ! 620: if (rs->ack != ack) { ! 621: rs->ack = ack; ! 622: if (!ack) ! 623: return; ! 624: if (rs->bus_phase < 0) ! 625: return; ! 626: if (!(rs->bus_phase & SCSI_IO_DIRECTION)) { ! 627: if (rs->databusoutput) { ! 628: raw_scsi_write_data(rs, rs->data_write); ! 629: } ! 630: } else { ! 631: raw_scsi_get_data_2(rs, true, false); ! 632: } ! 633: } ! 634: } ! 635: ! 636: static void Ncr5380_UpdateDmaAddrAndLen(uint32_t nDmaAddr, uint32_t nDataLen) ! 637: { ! 638: uint32_t nNewAddr = nDmaAddr + nDataLen; ! 639: uint32_t nNewLen; ! 640: ! 641: if (Config_IsMachineFalcon()) ! 642: { ! 643: FDC_WriteDMAAddress(nNewAddr); ! 644: } ! 645: else ! 646: { ! 647: IoMem[0xff8701] = nNewAddr >> 24; ! 648: IoMem[0xff8703] = nNewAddr >> 16; ! 649: IoMem[0xff8705] = nNewAddr >> 8; ! 650: IoMem[0xff8707] = nNewAddr; ! 651: ! 652: nNewLen = (Uint32)IoMem[0xff8709] << 24 | IoMem[0xff870b] << 16 ! 653: | IoMem[0xff870d] << 8 | IoMem[0xff870f]; ! 654: assert(nDataLen <= nNewLen); ! 655: nNewLen -= nDataLen; ! 656: IoMem[0xff8709] = nNewLen >> 24; ! 657: IoMem[0xff870b] = nNewLen >> 16; ! 658: IoMem[0xff870d] = nNewLen >> 8; ! 659: IoMem[0xff870f] = nNewLen; ! 660: } ! 661: } ! 662: ! 663: static void dma_check(struct soft_scsi *ncr) ! 664: { ! 665: int i, nDataLen; ! 666: uint32_t nDmaAddr; ! 667: ! 668: // fprintf(stderr, "dma_check: dma_direction=%i data_len=%i/%i phase=%i %i active=%i \n", ! 669: // ncr->dma_direction, ScsiBus.offset, ScsiBus.data_len, ncr->rscsi.bus_phase, ncr->regs[3] & 7, ncr->dma_active); ! 670: ! 671: /* Don't do anything if nothing to transfer */ ! 672: if (ScsiBus.data_len - ScsiBus.offset == 0 || !ncr->dma_active || !ncr->dma_direction) ! 673: return; ! 674: ! 675: if (Config_IsMachineFalcon()) ! 676: { ! 677: /* Is DMA really active? */ ! 678: if ((FDC_DMA_GetMode() & 0xc0) != 0x00) ! 679: return; ! 680: nDmaAddr = FDC_GetDMAAddress(); ! 681: nDataLen = FDC_DMA_GetSectorCount() * 512; ! 682: } ! 683: else ! 684: { ! 685: if ((IoMem[0xff8715] & 2) == 0) ! 686: return; ! 687: nDmaAddr = (Uint32)IoMem[0xff8701] << 24 | IoMem[0xff8703] << 16 ! 688: | IoMem[0xff8705] << 8 | IoMem[0xff8707]; ! 689: nDataLen = (Uint32)IoMem[0xff8709] << 24 | IoMem[0xff870b] << 16 ! 690: | IoMem[0xff870d] << 8 | IoMem[0xff870f]; ! 691: } ! 692: ! 693: if (nDataLen > ScsiBus.data_len - ScsiBus.offset) ! 694: nDataLen = ScsiBus.data_len - ScsiBus.offset; ! 695: ! 696: if (ncr_soft_scsi.dma_direction < 0) ! 697: { ! 698: if (STMemory_CheckAreaType(nDmaAddr, nDataLen, ABFLAG_RAM | ABFLAG_ROM)) ! 699: { ! 700: for (i = 0; i < nDataLen; i++) ! 701: { ! 702: uint8_t val = ncr5380_bget(ncr, 8); ! 703: STMemory_WriteByte(nDmaAddr + i, val); ! 704: } ! 705: ScsiBus.bDmaError = false; ! 706: } ! 707: else ! 708: { ! 709: ScsiBus.bDmaError = true; ! 710: ScsiBus.status = HD_STATUS_ERROR; ! 711: } ! 712: ! 713: if (Config_IsMachineFalcon()) ! 714: { ! 715: /* Note that the Falcon's DMA chip seems to report an ! 716: * end address that is 16 bytes too high if the DATA IN ! 717: * phase was interrupted by a different phase, but the ! 718: * address is correct if there was no interruption. */ ! 719: if (ScsiBus.offset < ScsiBus.data_len) ! 720: Ncr5380_UpdateDmaAddrAndLen(nDmaAddr, nDataLen + 16); ! 721: else ! 722: Ncr5380_UpdateDmaAddrAndLen(nDmaAddr, nDataLen); ! 723: } ! 724: else ! 725: { ! 726: int nRemainingBytes = IoMem[0xff8707] & 3; ! 727: Ncr5380_UpdateDmaAddrAndLen(nDmaAddr, nDataLen); ! 728: for (i = 0; i < nRemainingBytes; i++) ! 729: { ! 730: /* For more precise emulation, we should not ! 731: * pre-write the bytes to the STRam ... */ ! 732: const Uint32 addr = nDmaAddr + nDataLen - nRemainingBytes; ! 733: IoMem[0xff8710 + i] = STMemory_ReadByte(addr + i); ! 734: } ! 735: } ! 736: } ! 737: else if (ncr_soft_scsi.dma_direction > 0 && ScsiBus.dmawrite_to_fh) ! 738: { ! 739: /* write - if allowed */ ! 740: if (STMemory_CheckAreaType(nDmaAddr, nDataLen, ABFLAG_RAM | ABFLAG_ROM)) ! 741: { ! 742: for (i = 0; i < nDataLen; i++) ! 743: { ! 744: uint8_t val = STMemory_ReadByte(nDmaAddr + i); ! 745: ncr5380_bput(ncr, 8, val); ! 746: } ! 747: } ! 748: else ! 749: { ! 750: Log_Printf(LOG_WARN, "SCSI DMA write uses invalid RAM range 0x%x+%i\n", ! 751: nDmaAddr, nDataLen); ! 752: ScsiBus.bDmaError = true; ! 753: ScsiBus.status = HD_STATUS_ERROR; ! 754: } ! 755: Ncr5380_UpdateDmaAddrAndLen(nDmaAddr, nDataLen); ! 756: } ! 757: ! 758: if (Config_IsMachineFalcon()) ! 759: { ! 760: FDC_SetDMAStatus(ScsiBus.bDmaError); /* Mark DMA error */ ! 761: FDC_SetIRQ(FDC_IRQ_SOURCE_HDC); ! 762: } ! 763: else ! 764: { ! 765: ncr->irq = true; ! 766: } ! 767: ! 768: if (ScsiBus.offset == ScsiBus.data_len) ! 769: { ! 770: ncr->dmac_active = 0; ! 771: ncr->dma_active = 0; ! 772: } ! 773: } ! 774: ! 775: static void ncr5380_set_irq(struct soft_scsi *scsi) ! 776: { ! 777: if (scsi->irq) ! 778: return; ! 779: scsi->irq = true; ! 780: #if 0 /* FIXME */ ! 781: devices_rethink_all(ncr80_rethink); ! 782: if (scsi->delayed_irq) ! 783: x_do_cycles(2 * CYCLE_UNIT); ! 784: #endif ! 785: #if NCR5380_DEBUG_IRQ ! 786: write_log(_T("IRQ\n")); ! 787: #endif ! 788: ! 789: if (Config_IsMachineFalcon()) ! 790: FDC_SetIRQ(FDC_IRQ_SOURCE_HDC); ! 791: } ! 792: ! 793: static void ncr5380_databusoutput(struct soft_scsi *scsi) ! 794: { ! 795: bool databusoutput = (scsi->regs[1] & 1) != 0; ! 796: struct raw_scsi *r = &scsi->rscsi; ! 797: ! 798: if (r->bus_phase >= 0 && (r->bus_phase & SCSI_IO_DIRECTION)) ! 799: databusoutput = false; ! 800: raw_scsi_set_databus(r, databusoutput); ! 801: } ! 802: ! 803: static void ncr5380_check(struct soft_scsi *scsi) ! 804: { ! 805: ncr5380_databusoutput(scsi); ! 806: } ! 807: ! 808: static void ncr5380_check_phase(struct soft_scsi *scsi) ! 809: { ! 810: if (!(scsi->regs[2] & 2)) ! 811: return; ! 812: if (scsi->regs[2] & 0x40) ! 813: return; ! 814: if (scsi->rscsi.bus_phase != (scsi->regs[3] & 7)) { ! 815: if (scsi->dma_controller) { ! 816: scsi->regs[5] |= 0x80; // end of dma ! 817: scsi->regs[3] |= 0x80; // last byte sent ! 818: } ! 819: ncr5380_set_irq(scsi); ! 820: } ! 821: } ! 822: ! 823: static void ncr5380_reset(struct soft_scsi *scsi) ! 824: { ! 825: memset(scsi->regs, 0, sizeof scsi->regs); ! 826: raw_scsi_reset_bus(scsi); ! 827: scsi->regs[1] = 0x80; ! 828: ncr5380_set_irq(scsi); ! 829: } ! 830: ! 831: static uae_u8 ncr5380_bget(struct soft_scsi *scsi, int reg) ! 832: { ! 833: if (reg > 8) ! 834: return 0; ! 835: uae_u8 v = scsi->regs[reg]; ! 836: struct raw_scsi *r = &scsi->rscsi; ! 837: switch(reg) ! 838: { ! 839: case 1: ! 840: break; ! 841: case 4: ! 842: { ! 843: uae_u8 t = raw_scsi_get_signal_phase(r); ! 844: v = 0; ! 845: if (t & SCSI_IO_BUSY) ! 846: v |= 1 << 6; ! 847: if (t & SCSI_IO_REQ) ! 848: v |= 1 << 5; ! 849: if (t & SCSI_IO_SEL) ! 850: v |= 1 << 1; ! 851: if (r->bus_phase >= 0) ! 852: v |= r->bus_phase << 2; ! 853: if (scsi->regs[1] & 0x80) ! 854: v |= 0x80; ! 855: } ! 856: break; ! 857: case 5: ! 858: { ! 859: uae_u8 t = raw_scsi_get_signal_phase(r); ! 860: v &= (0x80 | 0x40 | 0x20 | 0x04); ! 861: if (t & SCSI_IO_ATN) ! 862: v |= 1 << 1; ! 863: if (r->bus_phase == (scsi->regs[3] & 7)) { ! 864: v |= 1 << 3; ! 865: } ! 866: if (scsi->irq) { ! 867: v |= 1 << 4; ! 868: } ! 869: if (scsi->dma_drq || (scsi->dma_active && !scsi->dma_controller && r->bus_phase == (scsi->regs[3] & 7))) { ! 870: scsi->dma_drq = true; ! 871: v |= 1 << 6; ! 872: } ! 873: if (scsi->regs[2] & 4) { ! 874: // monitor busy ! 875: if (r->bus_phase == SCSI_SIGNAL_PHASE_FREE) { ! 876: // any loss of busy = Busy error ! 877: // not just "unexpected" loss of busy ! 878: v |= 1 << 2; ! 879: scsi->dmac_active = false; ! 880: } ! 881: } ! 882: } ! 883: break; ! 884: case 0: ! 885: v = raw_scsi_get_data(r, false); ! 886: break; ! 887: case 6: ! 888: v = raw_scsi_get_data(r, scsi->dma_active); ! 889: ncr5380_check_phase(scsi); ! 890: break; ! 891: case 7: ! 892: scsi->irq = false; ! 893: if (Config_IsMachineFalcon()) ! 894: FDC_ClearIRQ(); ! 895: break; ! 896: case 8: // fake dma port ! 897: v = raw_scsi_get_data(r, true); ! 898: ncr5380_check_phase(scsi); ! 899: break; ! 900: } ! 901: ncr5380_check(scsi); ! 902: return v; ! 903: } ! 904: ! 905: void ncr5380_bput(struct soft_scsi *scsi, int reg, uae_u8 v) ! 906: { ! 907: if (reg > 8) ! 908: return; ! 909: bool dataoutput = (scsi->regs[1] & 1) != 0; ! 910: struct raw_scsi *r = &scsi->rscsi; ! 911: uae_u8 old = scsi->regs[reg]; ! 912: scsi->regs[reg] = v; ! 913: switch(reg) ! 914: { ! 915: case 0: ! 916: { ! 917: r->data_write = v; ! 918: // assert data bus can be only active if direction is out ! 919: // and bus phase matches ! 920: if (r->databusoutput) { ! 921: if (((scsi->regs[2] & 2) && scsi->dma_active) || r->bus_phase < 0) { ! 922: raw_scsi_write_data(r, v); ! 923: ncr5380_check_phase(scsi); ! 924: } ! 925: } ! 926: } ! 927: break; ! 928: case 1: ! 929: { ! 930: scsi->regs[reg] &= ~((1 << 5) | (1 << 6)); ! 931: scsi->regs[reg] |= old & ((1 << 5) | (1 << 6)); // AIP, LA ! 932: if (!(v & 0x80)) { ! 933: bool init = r->bus_phase < 0; ! 934: ncr5380_databusoutput(scsi); ! 935: if (init && !dataoutput && (v & 1) && (scsi->regs[2] & 1)) { ! 936: r->bus_phase = SCSI_SIGNAL_PHASE_SELECT_1; ! 937: } ! 938: raw_scsi_set_signal_phase(r, ! 939: (v & (1 << 3)) != 0, ! 940: (v & (1 << 2)) != 0, ! 941: (v & (1 << 1)) != 0); ! 942: if (!(scsi->regs[2] & 2)) ! 943: raw_scsi_set_ack(r, (v & (1 << 4)) != 0); ! 944: } ! 945: if (v & 0x80) { // RST ! 946: ncr5380_reset(scsi); ! 947: } ! 948: } ! 949: break; ! 950: case 2: ! 951: if ((v & 1) && !(old & 1)) { // Arbitrate ! 952: r->databusoutput = false; ! 953: raw_scsi_set_signal_phase(r, true, false, false); ! 954: scsi->regs[1] |= 1 << 6; // AIP ! 955: scsi->regs[1] &= ~(1 << 5); // LA ! 956: } else if (!(v & 1) && (old & 1)) { ! 957: scsi->regs[1] &= ~(1 << 6); ! 958: } ! 959: if (!(v & 2)) { ! 960: // end of dma and dma request ! 961: scsi->regs[5] &= ~(0x80 | 0x40); ! 962: scsi->dma_direction = 0; ! 963: scsi->dma_active = false; ! 964: scsi->dma_drq = false; ! 965: } ! 966: break; ! 967: case 5: ! 968: scsi->regs[reg] = old; ! 969: if (scsi->regs[2] & 2) { ! 970: scsi->dma_direction = 1; ! 971: scsi->dma_active = true; ! 972: dma_check(scsi); ! 973: } ! 974: #if NCR5380_DEBUG ! 975: write_log(_T("DMA send PC=%08x\n"), M68K_GETPC); ! 976: #endif ! 977: break; ! 978: case 6: ! 979: if (scsi->regs[2] & 2) { ! 980: scsi->dma_direction = 1; ! 981: scsi->dma_active = true; ! 982: scsi->dma_started = true; ! 983: dma_check(scsi); ! 984: } ! 985: #if NCR5380_DEBUG ! 986: write_log(_T("DMA target recv PC=%08x\n"), M68K_GETPC); ! 987: #endif ! 988: break; ! 989: case 7: ! 990: if (scsi->regs[2] & 2) { ! 991: scsi->dma_direction = -1; ! 992: scsi->dma_active = true; ! 993: scsi->dma_started = true; ! 994: dma_check(scsi); ! 995: } ! 996: #if NCR5380_DEBUG ! 997: write_log(_T("DMA initiator recv PC=%08x\n"), M68K_GETPC); ! 998: #endif ! 999: break; ! 1000: case 8: // fake dma port ! 1001: if (r->bus_phase == (scsi->regs[3] & 7)) { ! 1002: raw_scsi_put_data(r, v, true); ! 1003: } ! 1004: ncr5380_check_phase(scsi); ! 1005: break; ! 1006: } ! 1007: ncr5380_check(scsi); ! 1008: } ! 1009: ! 1010: /* ***** Hatari glue code below ***** */ ! 1011: ! 1012: /** ! 1013: * return number of identified partitions on existing SCSI images ! 1014: */ ! 1015: int Ncr5380_Init(void) ! 1016: { ! 1017: #if WITH_NCR5380 ! 1018: int i, partitions = 0; ! 1019: ! 1020: memset(&ScsiBus, 0, sizeof(ScsiBus)); ! 1021: ScsiBus.typestr = "SCSI"; ! 1022: ScsiBus.buffer_size = 512; ! 1023: ScsiBus.buffer = malloc(ScsiBus.buffer_size); ! 1024: if (!ScsiBus.buffer) ! 1025: { ! 1026: perror("Ncr5380_Init"); ! 1027: return 0; ! 1028: } ! 1029: for (i = 0; i < MAX_SCSI_DEVS; i++) ! 1030: { ! 1031: if (!ConfigureParams.Scsi[i].bUseDevice) ! 1032: continue; ! 1033: if (HDC_InitDevice(&ScsiBus.devs[i], ConfigureParams.Scsi[i].sDeviceFile, ConfigureParams.Scsi[i].nBlockSize) == 0) ! 1034: partitions += HDC_PartitionCount(ScsiBus.devs[i].image_file, TRACE_SCSI_CMD, NULL); ! 1035: } ! 1036: return partitions; ! 1037: #else ! 1038: return 0; ! 1039: #endif ! 1040: } ! 1041: ! 1042: void Ncr5380_UnInit(void) ! 1043: { ! 1044: #if WITH_NCR5380 ! 1045: int i; ! 1046: ! 1047: for (i = 0; i < MAX_SCSI_DEVS; i++) ! 1048: { ! 1049: if (!ScsiBus.devs[i].enabled) ! 1050: continue; ! 1051: File_UnLock(ScsiBus.devs[i].image_file); ! 1052: fclose(ScsiBus.devs[i].image_file); ! 1053: ScsiBus.devs[i].image_file = NULL; ! 1054: ScsiBus.devs[i].enabled = false; ! 1055: } ! 1056: free(ScsiBus.buffer); ! 1057: ScsiBus.buffer = NULL; ! 1058: #endif ! 1059: } ! 1060: ! 1061: /** ! 1062: * Emulate external reset "pin": Clear registers etc. ! 1063: */ ! 1064: void Ncr5380_Reset(void) ! 1065: { ! 1066: #if WITH_NCR5380 ! 1067: int i; ! 1068: ! 1069: ncr5380_reset(&ncr_soft_scsi); ! 1070: bus_free(&ncr_soft_scsi.rscsi); ! 1071: ! 1072: for (i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) { ! 1073: if (ScsiBus.devs[i].enabled) { ! 1074: ncr_soft_scsi.rscsi.device[i] = &ScsiBus.devs[i]; ! 1075: // fprintf(stderr, "SCSI #%i enabled\n", i); ! 1076: } ! 1077: else ! 1078: { ! 1079: ncr_soft_scsi.rscsi.device[i] = NULL; ! 1080: } ! 1081: } ! 1082: #endif ! 1083: } ! 1084: ! 1085: /** ! 1086: * Write a command byte to the NCR 5380 SCSI controller ! 1087: */ ! 1088: void Ncr5380_WriteByte(int addr, Uint8 byte) ! 1089: { ! 1090: #if WITH_NCR5380 ! 1091: ncr5380_bput(&ncr_soft_scsi, addr, byte); ! 1092: #endif ! 1093: } ! 1094: ! 1095: /** ! 1096: * Read a command byte from the NCR 5380 SCSI controller ! 1097: */ ! 1098: Uint8 Ncr5380_ReadByte(int addr) ! 1099: { ! 1100: #if WITH_NCR5380 ! 1101: return ncr5380_bget(&ncr_soft_scsi, addr); ! 1102: #else ! 1103: return 0; ! 1104: #endif ! 1105: } ! 1106: ! 1107: ! 1108: void Ncr5380_DmaTransfer_Falcon(void) ! 1109: { ! 1110: dma_check(&ncr_soft_scsi); ! 1111: } ! 1112: ! 1113: ! 1114: void Ncr5380_IoMemTT_WriteByte(void) ! 1115: { ! 1116: while (nIoMemAccessSize > 0) ! 1117: { ! 1118: if (IoAccessBaseAddress & 1) ! 1119: { ! 1120: int addr = IoAccessBaseAddress / 2 & 0x7; ! 1121: Ncr5380_WriteByte(addr, IoMem[IoAccessBaseAddress]); ! 1122: } ! 1123: IoAccessBaseAddress++; ! 1124: nIoMemAccessSize--; ! 1125: } ! 1126: } ! 1127: ! 1128: ! 1129: void Ncr5380_IoMemTT_ReadByte(void) ! 1130: { ! 1131: while (nIoMemAccessSize > 0) ! 1132: { ! 1133: if (IoAccessBaseAddress & 1) ! 1134: { ! 1135: int addr = IoAccessBaseAddress / 2 & 0x7; ! 1136: IoMem[IoAccessBaseAddress] = Ncr5380_ReadByte(addr); ! 1137: } ! 1138: IoAccessBaseAddress++; ! 1139: nIoMemAccessSize--; ! 1140: } ! 1141: } ! 1142: ! 1143: ! 1144: void Ncr5380_TT_DMA_Ctrl_WriteWord(void) ! 1145: { ! 1146: if (IoMem[0xff8715] & 2) ! 1147: dma_check(&ncr_soft_scsi); ! 1148: } ! 1149: ! 1150: ! 1151: /** ! 1152: * This is a temporary hack until we've got proper emulation of the ! 1153: * 2nd MFP in the TT ! 1154: */ ! 1155: void Ncr5380_TT_GPIP_ReadByte(void) ! 1156: { ! 1157: Uint8 val = 0x7f; ! 1158: ! 1159: if (ncr_soft_scsi.irq) ! 1160: val |= 0x80; ! 1161: ! 1162: IoMem[0xfffa81] = val; ! 1163: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.