Files
86Box/src/scsi/scsi_ncr5380.c

631 lines
22 KiB
C
Raw Normal View History

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Implementation of the NCR 5380 chip made by NCR
* and used in various controllers.
*
2020-03-25 00:46:02 +02:00
*
*
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* TheCollector1995, <mariogplayer@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2017-2019 Sarah Walker.
* Copyright 2017-2019 Fred N. van Kempen.
* Copyright 2017-2024 TheCollector1995.
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <wchar.h>
2017-12-10 02:53:10 -05:00
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/io.h>
#include <86box/timer.h>
#include <86box/dma.h>
#include <86box/pic.h>
#include <86box/mca.h>
#include <86box/mem.h>
#include <86box/rom.h>
#include <86box/device.h>
#include <86box/nvr.h>
#include <86box/plat.h>
#include <86box/scsi.h>
#include <86box/scsi_device.h>
#include <86box/scsi_ncr5380.h>
int ncr5380_cmd_len[8] = { 6, 10, 10, 6, 16, 12, 10, 6 };
Added the IBM 5161 ISA expansion for PC and XT; Cleaned up the parallel port emulation, added IRQ support, and made enabling/disabling per port; Added the Award 430NX and the Intel Classic/PCI (Alfredo, 420TX); Finished the 586MC1; Added 8087 emulation; Moved Cyrix 6x86'es to the Dev branch; Sanitized/cleaned up memregs.c/h and intel.c/h; Split the chipsets from machines and sanitized Port 92 emulation; Added support for the 15bpp mode to the Compaq ATI 28800; Moved the MR 386DX and 486 machines to the Dev branch; Ported the new dynamic recompiler from PCem, but it remains in Dev branch until after v2.00; Ported the new timer code from PCem; Cleaned up the CPU table of unused stuff and better optimized its structure; Ported the Open-XT and Open-AT from VARCem, the Open-AT is in the Dev branch; Ported the XT MFM controller rewrite and adding of more controllers (incl. two RLL ones), from VARCem; Added the AHA-1540A and the BusTek BT-542B; Moved the Sumo SCSI-AT to the Dev branch; Minor IDE, FDC, and floppy drive code clean-ups; Made NCR 5380/53C400-based cards' BIOS address configurable; Got rid of the legacy romset variable; Unified (video) buffer and buffer32 into one and make the unified buffer 32-bit; Added the Amstead PPC512 per PCem patch by John Elliott; Switched memory mapping granularity from 16k to 4k (less than 1k not possible due to internal pages); Rewrote the CL-GD 54xx blitter, fixes Win-OS/2 on the 54x6 among other thing; Added the Image Manager 1024 and Professional Graphics Controller per PCem patch by John Elliott and work done on VARCem; Added Headland HT-216, GC-205 and Video 7 VGA 1024i emulation based on PCem commit; Implemented the fuction keys for the Toshiba T1000/T1200/T3100 enhancement; Amstrad MegaPC does now works correctly with non-internal graphics card; The SLiRP code no longer casts a packed struct type to a non-packed struct type; The Xi8088 and PB410a no longer hang on 86Box when PS/2 mouse is not present; The S3 Virge on BeOS is no longer broken (was broken by build #1591); OS/2 2.0 build 6.167 now sees key presses again; Xi8088 now work on CGA again; 86F images converted from either the old or new variants of the HxC MFM format now work correctly; Hardware interrupts with a vector of 0xFF are now handled correctly; OPTi 495SX boards no longer incorrectly have 64 MB maximum RAM when 32 MB is correct; Fixed VNC keyboard input bugs; Fixed AT RTC periodic interrupt - Chicago 58s / 73f / 73g / 81 MIDI play no longer hangs with the build's own VTD driver; Fixed mouse polling with internal mice - Amstrad and Olivetti mice now work correctly; Triones ATAPI DMA driver now correctly reads a file at the end of a CD image with a sectors number not divisible by 4; Compaq Portable now works with all graphics cards; Fixed various MDSI Genius bugs; Added segment limit checks and improved page fault checks for several CPU instructions - Memphis 15xx WINSETUP and Chicago 58s WINDISK.CPL no longer issue a GPF, and some S3 drivers that used to have glitches, now work correctly; Further improved the 808x emulation, also fixes the noticably choppy sound when using 808x CPU's, also fixes #355; OS/2 installer no logner locks up on splash screen on PS/2 Model 70 and 80, fixes #400. Fixed several Amstead bugs, GEM no longer crashes on the Amstrad 1640, fixes #391. Ported John Elliott's Amstrad fixes and improvement from PCem, and fixed the default language so it's correctly Engliish, fixes #278, fixes #389. Fixed a minor IDE timing bug, fixes #388. Fixed Toshiba T1000 RAM issues, fixes #379. Fixed EGA/(S)VGA overscan border handling, fixes #378; Got rid of the now long useless IDE channel 2 auto-removal, fixes #370; Fixed the BIOS files used by the AMSTRAD PC1512, fixes #366; Ported the Unicode CD image file name fix from VARCem, fixes #365; Fixed high density floppy disks on the Xi8088, fixes #359; Fixed some bugs in the Hercules emulation, fixes #346, fixes #358; Fixed the SCSI hard disk mode sense pages, fixes #356; Removed the AMI Unknown 386SX because of impossibility to identify the chipset, closes #349; Fixed bugs in the serial mouse emulation, fixes #344; Compiled 86Box binaries now include all the required .DLL's, fixes #341; Made some combo boxes in the Settings dialog slightly wider, fixes #276.
2019-09-20 14:02:30 +02:00
#ifdef ENABLE_NCR5380_LOG
int ncr5380_do_log = ENABLE_NCR5380_LOG;
static void
ncr5380_log(const char *fmt, ...)
{
va_list ap;
if (ncr5380_do_log) {
2022-09-18 17:17:15 -04:00
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define ncr5380_log(fmt, ...)
#endif
#define SET_BUS_STATE(ncr, state) ncr->cur_bus = (ncr->cur_bus & ~(SCSI_PHASE_MESSAGE_IN)) | (state & (SCSI_PHASE_MESSAGE_IN))
void
ncr5380_irq(ncr_t *ncr, int set_irq)
{
if (set_irq) {
ncr->irq_state = 1;
2022-09-18 17:17:15 -04:00
ncr->isr |= STATUS_INT;
if (ncr->irq != -1)
picint(1 << ncr->irq);
} else {
ncr->irq_state = 0;
2022-09-18 17:17:15 -04:00
ncr->isr &= ~STATUS_INT;
if (ncr->irq != 1)
picintc(1 << ncr->irq);
}
}
void
ncr5380_set_irq(ncr_t *ncr, int irq)
{
ncr->irq = irq;
}
static int
ncr5380_get_dev_id(uint8_t data)
{
2023-05-29 01:30:51 -04:00
for (uint8_t c = 0; c < SCSI_ID_MAX; c++) {
2022-09-18 17:17:15 -04:00
if (data & (1 << c))
2023-05-29 01:30:51 -04:00
return c;
}
2023-05-29 01:30:51 -04:00
return -1;
}
2022-02-20 02:26:27 -05:00
static int
ncr5380_getmsglen(uint8_t *msgp, int len)
{
2022-09-18 17:17:15 -04:00
uint8_t msg = msgp[0];
if (msg == 0 || (msg >= 0x02 && msg <= 0x1f) || msg >= 0x80)
return 1;
if (msg >= 0x20 && msg <= 0x2f)
return 2;
if (len < 2)
return 3;
return msgp[1];
}
static void
ncr5380_reset(ncr_t *ncr)
{
ncr->command_pos = 0;
ncr->data_pos = 0;
ncr->state = STATE_IDLE;
ncr->clear_req = 0;
ncr->cur_bus = 0;
ncr->tx_data = 0;
ncr->output_data = 0;
ncr->data_wait = 0;
ncr->mode = 0;
ncr->tcr = 0;
ncr->icr = 0;
ncr->dma_mode = DMA_IDLE;
ncr5380_log("NCR Reset\n");
ncr->timer(ncr->priv, 0.0);
2022-02-20 02:26:27 -05:00
for (int i = 0; i < 8; i++)
scsi_device_reset(&scsi_devices[ncr->bus][i]);
ncr5380_irq(ncr, 0);
}
uint32_t
ncr5380_get_bus_host(ncr_t *ncr)
{
uint32_t bus_host = 0;
if (ncr->icr & ICR_DBP)
2022-09-18 17:17:15 -04:00
bus_host |= BUS_DBP;
if (ncr->icr & ICR_SEL)
2022-09-18 17:17:15 -04:00
bus_host |= BUS_SEL;
if (ncr->tcr & TCR_IO)
2022-09-18 17:17:15 -04:00
bus_host |= BUS_IO;
if (ncr->tcr & TCR_CD)
2022-09-18 17:17:15 -04:00
bus_host |= BUS_CD;
if (ncr->tcr & TCR_MSG)
2022-09-18 17:17:15 -04:00
bus_host |= BUS_MSG;
if (ncr->tcr & TCR_REQ)
2022-09-18 17:17:15 -04:00
bus_host |= BUS_REQ;
if (ncr->icr & ICR_BSY)
2022-09-18 17:17:15 -04:00
bus_host |= BUS_BSY;
if (ncr->icr & ICR_ATN)
bus_host |= BUS_ATN;
if (ncr->icr & ICR_ACK)
2022-09-18 17:17:15 -04:00
bus_host |= BUS_ACK;
if (ncr->mode & MODE_ARBITRATE)
2022-09-18 17:17:15 -04:00
bus_host |= BUS_ARB;
2022-09-18 17:17:15 -04:00
return (bus_host | BUS_SETDATA(ncr->output_data));
}
void
ncr5380_bus_read(ncr_t *ncr)
{
2023-07-20 18:58:26 -04:00
const scsi_device_t *dev;
int phase;
/*Wait processes to handle bus requests*/
if (ncr->clear_req) {
2022-09-18 17:17:15 -04:00
ncr->clear_req--;
if (!ncr->clear_req) {
ncr5380_log("Prelude to command data\n");
2022-09-18 17:17:15 -04:00
SET_BUS_STATE(ncr, ncr->new_phase);
ncr->cur_bus |= BUS_REQ;
}
}
if (ncr->wait_data) {
2022-09-18 17:17:15 -04:00
ncr->wait_data--;
if (!ncr->wait_data) {
dev = &scsi_devices[ncr->bus][ncr->target_id];
2022-09-18 17:17:15 -04:00
SET_BUS_STATE(ncr, ncr->new_phase);
phase = (ncr->cur_bus & SCSI_PHASE_MESSAGE_IN);
if (phase == SCSI_PHASE_DATA_IN) {
if (ncr->dma_mode == DMA_IDLE) {
ncr5380_log("Phase Data In.\n");
if ((dev->sc != NULL) && (dev->sc->temp_buffer != NULL))
ncr->tx_data = dev->sc->temp_buffer[ncr->data_pos++];
ncr->state = STATE_DATAIN;
ncr->cur_bus = (ncr->cur_bus & ~BUS_DATAMASK) | BUS_SETDATA(ncr->tx_data) | BUS_DBP;
}
2022-09-18 17:17:15 -04:00
} else if (phase == SCSI_PHASE_DATA_OUT) {
if (ncr->new_phase & BUS_IDLE) {
ncr->state = STATE_IDLE;
ncr->cur_bus &= ~BUS_BSY;
} else {
if (ncr->dma_mode == DMA_IDLE)
ncr->state = STATE_DATAOUT;
}
2022-09-18 17:17:15 -04:00
} else if (phase == SCSI_PHASE_STATUS) {
ncr5380_log("Phase Status.\n");
2022-09-18 17:17:15 -04:00
ncr->cur_bus |= BUS_REQ;
ncr->state = STATE_STATUS;
ncr->cur_bus = (ncr->cur_bus & ~BUS_DATAMASK) | BUS_SETDATA(dev->status) | BUS_DBP;
} else if (phase == SCSI_PHASE_MESSAGE_IN) {
ncr5380_log("Phase Message In.\n");
2022-09-18 17:17:15 -04:00
ncr->state = STATE_MESSAGEIN;
ncr->cur_bus = (ncr->cur_bus & ~BUS_DATAMASK) | BUS_SETDATA(0) | BUS_DBP;
} else if (phase == SCSI_PHASE_MESSAGE_OUT) {
ncr->cur_bus |= BUS_REQ;
ncr->state = STATE_MESSAGEOUT;
ncr->cur_bus = (ncr->cur_bus & ~BUS_DATAMASK) | BUS_SETDATA(ncr->target_id >> 5) | BUS_DBP;
}
}
}
if (ncr->wait_complete) {
2022-09-18 17:17:15 -04:00
ncr->wait_complete--;
if (!ncr->wait_complete)
ncr->cur_bus |= BUS_REQ;
}
}
void
ncr5380_bus_update(ncr_t *ncr, int bus)
{
scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id];
2022-09-18 17:17:15 -04:00
double p;
uint8_t sel_data;
int msglen;
/*Start the SCSI command layer, which will also make the timings*/
if (bus & BUS_ARB)
2022-09-18 17:17:15 -04:00
ncr->state = STATE_IDLE;
ncr5380_log("State = %i\n", ncr->state);
2021-01-09 15:28:39 +01:00
switch (ncr->state) {
2022-09-18 17:17:15 -04:00
case STATE_IDLE:
ncr->clear_req = ncr->wait_data = ncr->wait_complete = 0;
if ((bus & BUS_SEL) && !(bus & BUS_BSY)) {
ncr5380_log("Selection phase\n");
2022-09-18 17:17:15 -04:00
sel_data = BUS_GETDATA(bus);
ncr->target_id = ncr5380_get_dev_id(sel_data);
2022-09-18 17:17:15 -04:00
ncr5380_log("Select - target ID = %i\n", ncr->target_id);
2022-09-18 17:17:15 -04:00
/*Once the device has been found and selected, mark it as busy*/
if ((ncr->target_id != (uint8_t) -1) && scsi_device_present(&scsi_devices[ncr->bus][ncr->target_id])) {
2022-09-18 17:17:15 -04:00
ncr->cur_bus |= BUS_BSY;
ncr->state = STATE_SELECT;
} else {
ncr5380_log("Device not found at ID %i, Current Bus BSY=%02x\n", ncr->target_id, ncr->cur_bus);
2022-09-18 17:17:15 -04:00
ncr->cur_bus = 0;
}
}
break;
case STATE_SELECT:
if (!(bus & BUS_SEL)) {
if (!(bus & BUS_ATN)) {
if ((ncr->target_id != (uint8_t) -1) && scsi_device_present(&scsi_devices[ncr->bus][ncr->target_id])) {
ncr5380_log("Device found at ID %i, Current Bus BSY=%02x\n", ncr->target_id, ncr->cur_bus);
2022-09-18 17:17:15 -04:00
ncr->state = STATE_COMMAND;
ncr->cur_bus = BUS_BSY | BUS_REQ;
ncr5380_log("CurBus BSY|REQ=%02x\n", ncr->cur_bus);
2022-09-18 17:17:15 -04:00
ncr->command_pos = 0;
SET_BUS_STATE(ncr, SCSI_PHASE_COMMAND);
} else {
ncr->state = STATE_IDLE;
ncr->cur_bus = 0;
}
} else {
ncr5380_log("Set to SCSI Message Out\n");
2022-09-18 17:17:15 -04:00
ncr->new_phase = SCSI_PHASE_MESSAGE_OUT;
ncr->wait_data = 4;
ncr->msgout_pos = 0;
ncr->is_msgout = 1;
}
}
break;
case STATE_COMMAND:
if ((bus & BUS_ACK) && !(ncr->bus_in & BUS_ACK)) {
/*Write command byte to the output data register*/
ncr->command[ncr->command_pos++] = BUS_GETDATA(bus);
ncr->clear_req = 3;
ncr->new_phase = ncr->cur_bus & SCSI_PHASE_MESSAGE_IN;
ncr->cur_bus &= ~BUS_REQ;
ncr5380_log("Command pos=%i, output data=%02x\n", ncr->command_pos, BUS_GETDATA(bus));
2022-09-18 17:17:15 -04:00
if (ncr->command_pos == ncr5380_cmd_len[(ncr->command[0] >> 5) & 7]) {
2022-09-18 17:17:15 -04:00
if (ncr->is_msgout) {
ncr->is_msgout = 0;
2023-06-09 23:46:54 -04:00
#if 0
ncr->command[1] = (ncr->command[1] & 0x1f) | (ncr->msglun << 5);
#endif
2022-09-18 17:17:15 -04:00
}
/*Reset data position to default*/
ncr->data_pos = 0;
dev = &scsi_devices[ncr->bus][ncr->target_id];
2022-09-18 17:17:15 -04:00
ncr5380_log("SCSI Command 0x%02X for ID %d, status code=%02x\n", ncr->command[0], ncr->target_id, dev->status);
2022-09-18 17:17:15 -04:00
dev->buffer_length = -1;
scsi_device_command_phase0(dev, ncr->command);
ncr5380_log("SCSI ID %i: Command %02X: Buffer Length %i, SCSI Phase %02X\n", ncr->target_id, ncr->command[0], dev->buffer_length, dev->phase);
2022-09-18 17:17:15 -04:00
ncr->period = 1.0;
2022-09-18 17:17:15 -04:00
ncr->wait_data = 4;
ncr->data_wait = 0;
if (dev->status == SCSI_STATUS_OK) {
/*If the SCSI phase is Data In or Data Out, allocate the SCSI buffer based on the transfer length of the command*/
if (dev->buffer_length && ((dev->phase == SCSI_PHASE_DATA_IN) || (dev->phase == SCSI_PHASE_DATA_OUT))) {
2022-09-18 17:17:15 -04:00
p = scsi_device_get_callback(dev);
ncr->period = (p > 0.0) ? p : (((double) dev->buffer_length) * 0.2);
ncr5380_log("SCSI ID %i: command 0x%02x for p = %lf, update = %lf, len = %i, dmamode = %x\n", ncr->target_id, ncr->command[0], scsi_device_get_callback(dev), ncr->period, dev->buffer_length, ncr->dma_mode);
2022-09-18 17:17:15 -04:00
}
}
ncr->new_phase = dev->phase;
}
}
break;
case STATE_DATAIN:
dev = &scsi_devices[ncr->bus][ncr->target_id];
2022-09-18 17:17:15 -04:00
if ((bus & BUS_ACK) && !(ncr->bus_in & BUS_ACK)) {
if (ncr->data_pos >= dev->buffer_length) {
ncr->cur_bus &= ~BUS_REQ;
ncr5380_log("CMD Phase1 DataIn.\n");
2022-09-18 17:17:15 -04:00
scsi_device_command_phase1(dev);
ncr->new_phase = SCSI_PHASE_STATUS;
ncr->wait_data = 4;
ncr->wait_complete = 8;
} else {
if ((dev->sc != NULL) && (dev->sc->temp_buffer != NULL))
ncr->tx_data = dev->sc->temp_buffer[ncr->data_pos++];
2022-09-18 17:17:15 -04:00
ncr->cur_bus = (ncr->cur_bus & ~BUS_DATAMASK) | BUS_SETDATA(ncr->tx_data) | BUS_DBP | BUS_REQ;
if (ncr->dma_mode == DMA_IDLE) { /*If a data in command that is not read 6/10 has been issued*/
ncr->data_wait |= 1;
ncr5380_log("DMA mode idle IN=%d.\n", ncr->data_pos);
ncr->timer(ncr->priv, ncr->period);
} else {
ncr5380_log("DMA mode IN=%d.\n", ncr->data_pos);
2022-09-18 17:17:15 -04:00
ncr->clear_req = 3;
}
2022-09-18 17:17:15 -04:00
ncr->cur_bus &= ~BUS_REQ;
ncr->new_phase = SCSI_PHASE_DATA_IN;
}
}
break;
case STATE_DATAOUT:
dev = &scsi_devices[ncr->bus][ncr->target_id];
2022-09-18 17:17:15 -04:00
if ((bus & BUS_ACK) && !(ncr->bus_in & BUS_ACK)) {
if ((dev->sc != NULL) && (dev->sc->temp_buffer != NULL))
dev->sc->temp_buffer[ncr->data_pos++] = BUS_GETDATA(bus);
2022-09-18 17:17:15 -04:00
if (ncr->data_pos >= dev->buffer_length) {
ncr->cur_bus &= ~BUS_REQ;
scsi_device_command_phase1(dev);
ncr->new_phase = SCSI_PHASE_STATUS;
ncr->wait_data = 4;
ncr->wait_complete = 8;
} else {
/*More data is to be transferred, place a request*/
if (ncr->dma_mode == DMA_IDLE) { /*If a data out command that is not write 6/10 has been issued*/
ncr->data_wait |= 1;
ncr5380_log("DMA mode idle out\n");
ncr->timer(ncr->priv, ncr->period);
} else
2022-09-18 17:17:15 -04:00
ncr->clear_req = 3;
2022-09-18 17:17:15 -04:00
ncr->cur_bus &= ~BUS_REQ;
ncr5380_log("CurBus ~REQ_DataOut=%02x\n", ncr->cur_bus);
2022-09-18 17:17:15 -04:00
}
}
break;
case STATE_STATUS:
if ((bus & BUS_ACK) && !(ncr->bus_in & BUS_ACK)) {
/*All transfers done, wait until next transfer*/
scsi_device_identify(&scsi_devices[ncr->bus][ncr->target_id], SCSI_LUN_USE_CDB);
2022-09-18 17:17:15 -04:00
ncr->cur_bus &= ~BUS_REQ;
ncr->new_phase = SCSI_PHASE_MESSAGE_IN;
ncr->wait_data = 4;
ncr->wait_complete = 8;
}
break;
case STATE_MESSAGEIN:
if ((bus & BUS_ACK) && !(ncr->bus_in & BUS_ACK)) {
ncr->cur_bus &= ~BUS_REQ;
ncr->new_phase = BUS_IDLE;
ncr->wait_data = 4;
}
break;
case STATE_MESSAGEOUT:
ncr5380_log("Ack on MSGOUT = %02x\n", (bus & BUS_ACK));
2022-09-18 17:17:15 -04:00
if ((bus & BUS_ACK) && !(ncr->bus_in & BUS_ACK)) {
ncr->msgout[ncr->msgout_pos++] = BUS_GETDATA(bus);
msglen = ncr5380_getmsglen(ncr->msgout, ncr->msgout_pos);
2022-09-18 17:17:15 -04:00
if (ncr->msgout_pos >= msglen) {
if ((ncr->msgout[0] & (0x80 | 0x20)) == 0x80)
ncr->msglun = ncr->msgout[0] & 7;
ncr->cur_bus &= ~BUS_REQ;
ncr->state = STATE_MESSAGE_ID;
}
}
break;
case STATE_MESSAGE_ID:
if ((ncr->target_id != (uint8_t) -1) && scsi_device_present(&scsi_devices[ncr->bus][ncr->target_id])) {
ncr5380_log("Device found at ID %i on MSGOUT, Current Bus BSY=%02x\n", ncr->target_id, ncr->cur_bus);
scsi_device_identify(&scsi_devices[ncr->bus][ncr->target_id], ncr->msglun);
2022-09-18 17:17:15 -04:00
ncr->state = STATE_COMMAND;
ncr->cur_bus = BUS_BSY | BUS_REQ;
ncr5380_log("CurBus BSY|REQ=%02x\n", ncr->cur_bus);
2022-09-18 17:17:15 -04:00
ncr->command_pos = 0;
SET_BUS_STATE(ncr, SCSI_PHASE_COMMAND);
}
break;
2023-06-09 23:46:54 -04:00
default:
break;
}
ncr->bus_in = bus;
}
void
ncr5380_write(uint16_t port, uint8_t val, ncr_t *ncr)
{
2023-07-20 18:58:26 -04:00
int bus_host = 0;
ncr5380_log("NCR5380 write(%04x,%02x)\n", port & 7, val);
switch (port & 7) {
2022-09-18 17:17:15 -04:00
case 0: /* Output data register */
ncr5380_log("[%04X:%08X]: Write: Output data register, val=%02x\n", CS, cpu_state.pc, val);
2022-09-18 17:17:15 -04:00
ncr->output_data = val;
break;
case 1: /* Initiator Command Register */
ncr5380_log("[%04X:%08X]: Write: Initiator command register, val=%02x.\n", CS, cpu_state.pc, val);
2022-09-18 17:17:15 -04:00
if ((val & 0x80) && !(ncr->icr & 0x80)) {
ncr5380_log("Resetting the 5380\n");
ncr5380_reset(ncr);
2022-09-18 17:17:15 -04:00
}
ncr->icr = val;
break;
case 2: /* Mode register */
ncr5380_log("Write: Mode register, val=%02x.\n", val);
2022-09-18 17:17:15 -04:00
if ((val & MODE_ARBITRATE) && !(ncr->mode & MODE_ARBITRATE)) {
ncr->icr &= ~ICR_ARB_LOST;
ncr->icr |= ICR_ARB_IN_PROGRESS;
}
ncr->mode = val;
ncr->dma_mode_ext(ncr, ncr->priv);
2022-09-18 17:17:15 -04:00
break;
case 3: /* Target Command Register */
ncr5380_log("Write: Target Command register, val=%02x.\n", val);
2022-09-18 17:17:15 -04:00
ncr->tcr = val;
break;
case 4: /* Select Enable Register */
ncr5380_log("Write: Select Enable register\n");
2022-09-18 17:17:15 -04:00
break;
case 5: /* start DMA Send */
ncr5380_log("Write: start DMA send register\n");
2022-09-18 17:17:15 -04:00
/*a Write 6/10 has occurred, start the timer when the block count is loaded*/
ncr->dma_mode = DMA_SEND;
if (ncr->dma_send_ext)
ncr->dma_send_ext(ncr, ncr->priv);
2022-09-18 17:17:15 -04:00
break;
case 7: /* start DMA Initiator Receive */
ncr5380_log("[%04X:%08X]: Write: start DMA initiator receive register, dma? = %02x\n", CS, cpu_state.pc, ncr->mode & MODE_DMA);
2022-09-18 17:17:15 -04:00
/*a Read 6/10 has occurred, start the timer when the block count is loaded*/
ncr->dma_mode = DMA_INITIATOR_RECEIVE;
if (ncr->dma_initiator_receive_ext)
ncr->dma_initiator_receive_ext(ncr, ncr->priv);
2022-09-18 17:17:15 -04:00
break;
default:
ncr5380_log("NCR5380: bad write %04x %02x\n", port, val);
2022-09-18 17:17:15 -04:00
break;
}
2022-02-20 02:26:27 -05:00
bus_host = ncr5380_get_bus_host(ncr);
ncr5380_bus_update(ncr, bus_host);
}
uint8_t
ncr5380_read(uint16_t port, ncr_t *ncr)
{
uint8_t ret = 0xff;
2023-05-29 01:30:51 -04:00
int bus;
int bus_state;
switch (port & 7) {
2022-09-18 17:17:15 -04:00
case 0: /* Current SCSI data */
ncr5380_log("Read: Current SCSI data register\n");
2022-09-18 17:17:15 -04:00
if (ncr->icr & ICR_DBP) {
/*Return the data from the output register if on data bus phase from ICR*/
ret = ncr->output_data;
ncr5380_log("[%04X:%08X]: Data Bus Phase, ret=%02x, clearreq=%d, waitdata=%x.\n", CS, cpu_state.pc, ret, ncr->clear_req, ncr->wait_data);
2022-09-18 17:17:15 -04:00
} else {
/*Return the data from the SCSI bus*/
ncr5380_bus_read(ncr);
2022-09-18 17:17:15 -04:00
ret = BUS_GETDATA(ncr->cur_bus);
ncr5380_log("[%04X:%08X]: NCR Get SCSI bus data=%02x.\n", CS, cpu_state.pc, ret);
2022-09-18 17:17:15 -04:00
}
break;
case 1: /* Initiator Command Register */
ncr5380_log("Read: Initiator Command register, NCR ICR Read=%02x\n", ncr->icr);
2022-09-18 17:17:15 -04:00
ret = ncr->icr;
break;
case 2: /* Mode register */
ncr5380_log("Read: Mode register = %02x.\n", ncr->mode);
2022-09-18 17:17:15 -04:00
ret = ncr->mode;
break;
case 3: /* Target Command Register */
ncr5380_log("Read: Target Command register, NCR target stat=%02x\n", ncr->tcr);
2022-09-18 17:17:15 -04:00
ret = ncr->tcr;
break;
case 4: /* Current SCSI Bus status */
ncr5380_log("Read: SCSI bus status register\n");
2022-09-18 17:17:15 -04:00
ret = 0;
ncr5380_bus_read(ncr);
ncr5380_log("NCR cur bus stat=%02x\n", ncr->cur_bus & 0xff);
2022-09-18 17:17:15 -04:00
ret |= (ncr->cur_bus & 0xff);
if (ncr->icr & ICR_SEL)
ret |= BUS_SEL;
if (ncr->icr & ICR_BSY)
ret |= BUS_BSY;
// if ((ret & SCSI_PHASE_MESSAGE_IN) == SCSI_PHASE_MESSAGE_IN)
// ret &= ~BUS_REQ;
2022-09-18 17:17:15 -04:00
break;
case 5: /* Bus and Status register */
ncr5380_log("Read: Bus and Status register\n");
2022-09-18 17:17:15 -04:00
ret = 0;
bus = ncr5380_get_bus_host(ncr);
ncr5380_log("Get host from Interrupt\n");
2022-09-18 17:17:15 -04:00
/*Check if the phase in process matches with TCR's*/
if ((bus & SCSI_PHASE_MESSAGE_IN) == (ncr->cur_bus & SCSI_PHASE_MESSAGE_IN)) {
ncr5380_log("Phase match\n");
2022-09-18 17:17:15 -04:00
ret |= STATUS_PHASE_MATCH;
}
ncr5380_bus_read(ncr);
2022-09-18 17:17:15 -04:00
bus = ncr->cur_bus;
if ((bus & BUS_ACK) || (ncr->icr & ICR_ACK))
2022-09-18 17:17:15 -04:00
ret |= STATUS_ACK;
if ((bus & BUS_ATN) || (ncr->icr & ICR_ATN))
2022-09-18 17:17:15 -04:00
ret |= 0x02;
if ((bus & BUS_REQ) && (ncr->mode & MODE_DMA)) {
ncr5380_log("Entering DMA mode\n");
2022-09-18 17:17:15 -04:00
ret |= STATUS_DRQ;
bus_state = 0;
if (bus & BUS_IO)
bus_state |= TCR_IO;
if (bus & BUS_CD)
bus_state |= TCR_CD;
if (bus & BUS_MSG)
bus_state |= TCR_MSG;
if ((ncr->tcr & 7) != bus_state) {
ncr5380_irq(ncr, 1);
ncr5380_log("IRQ issued\n");
2022-09-18 17:17:15 -04:00
}
}
if (!(bus & BUS_BSY) && (ncr->mode & MODE_MONITOR_BUSY)) {
ncr5380_log("Busy error\n");
2022-09-18 17:17:15 -04:00
ret |= STATUS_BUSY_ERROR;
}
ret |= (ncr->isr & (STATUS_INT | STATUS_END_OF_DMA));
break;
case 6:
ncr5380_log("Read: Input Data.\n");
ncr5380_bus_read(ncr);
ret = BUS_GETDATA(ncr->cur_bus);
2022-09-18 17:17:15 -04:00
break;
case 7: /* reset Parity/Interrupt */
ncr->isr &= ~(STATUS_BUSY_ERROR | 0x20);
ncr5380_irq(ncr, 0);
ncr5380_log("Reset Interrupt\n");
2022-09-18 17:17:15 -04:00
break;
2023-06-09 23:46:54 -04:00
default:
ncr5380_log("NCR5380: bad read %04x\n", port);
2023-06-09 23:46:54 -04:00
break;
}
ncr5380_log("NCR5380 read(%04x)=%02x\n", port & 7, ret);
2022-09-18 17:17:15 -04:00
2023-05-29 01:30:51 -04:00
return ret;
}