From da1ededb9383ae14ee72eed3ed5b809242ec31a6 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Wed, 10 Apr 2024 17:27:30 +0200 Subject: [PATCH] SCSI CD-ROM and 5380 fixes. CD-ROM side: fixed a mode sense page (0x08 Sony, used by both Sony and Texel drives) as well as corrected the Toshiba specific drive speeds (bytes_per_second). NCR 5380 side: split the work into the generic 5380 core and the ASICs into separate sources (53c400 and T128) and added the T228 MCA adapter based on the 128. --- src/include/86box/scsi_device.h | 4 +- src/include/86box/scsi_ncr5380.h | 114 ++- src/scsi/CMakeLists.txt | 4 +- src/scsi/scsi.c | 1 + src/scsi/scsi_cdrom.c | 2 +- src/scsi/scsi_ncr5380.c | 1595 +++--------------------------- src/scsi/scsi_ncr53c400.c | 998 +++++++++++++++++++ src/scsi/scsi_t128.c | 631 ++++++++++++ 8 files changed, 1865 insertions(+), 1484 deletions(-) create mode 100644 src/scsi/scsi_ncr53c400.c create mode 100644 src/scsi/scsi_t128.c diff --git a/src/include/86box/scsi_device.h b/src/include/86box/scsi_device.h index 09f9ee2d9..216fdb2ad 100644 --- a/src/include/86box/scsi_device.h +++ b/src/include/86box/scsi_device.h @@ -173,8 +173,8 @@ #define GPMODEP_RIGID_DISK_PAGE 0x0000000000000010LL #define GPMODEP_FLEXIBLE_DISK_PAGE 0x0000000000000020LL #define GPMODEP_CACHING_PAGE 0x0000000000000100LL -#define GPMODEP_CDROM_PAGE_SONY 0x0000000000000200LL -#define GPMODEP_CDROM_AUDIO_PAGE_SONY 0x0000000000000400LL +#define GPMODEP_CDROM_PAGE_SONY 0x0000000000000100LL +#define GPMODEP_CDROM_AUDIO_PAGE_SONY 0x0000000000000200LL #define GPMODEP_CDROM_PAGE 0x0000000000002000LL #define GPMODEP_CDROM_AUDIO_PAGE 0x0000000000004000LL #define GPMODEP_CAPABILITIES_PAGE 0x0000040000000000LL diff --git a/src/include/86box/scsi_ncr5380.h b/src/include/86box/scsi_ncr5380.h index ecf5660ef..a213e299d 100644 --- a/src/include/86box/scsi_ncr5380.h +++ b/src/include/86box/scsi_ncr5380.h @@ -6,9 +6,8 @@ * * This file is part of the 86Box distribution. * - * Implementation of the NCR 5380 series of SCSI Host Adapters - * made by NCR. These controllers were designed for - * the ISA bus. + * Implementation of the NCR 5380 chip made by NCR + * and used in various controllers. * * * @@ -17,21 +16,128 @@ * Fred N. van Kempen, * * Copyright 2017-2018 Sarah Walker. - * Copyright 2017-2018 TheCollector1995. * Copyright 2017-2018 Fred N. van Kempen. + * Copyright 2017-2024 TheCollector1995. */ #ifndef SCSI_NCR5380_H #define SCSI_NCR5380_H +#define NCR_CURDATA 0 /* current SCSI data (read only) */ +#define NCR_OUTDATA 0 /* output data (write only) */ +#define NCR_INITCOMMAND 1 /* initiator command (read/write) */ +#define NCR_MODE 2 /* mode (read/write) */ +#define NCR_TARGETCMD 3 /* target command (read/write) */ +#define NCR_SELENABLE 4 /* select enable (write only) */ +#define NCR_BUSSTATUS 4 /* bus status (read only) */ +#define NCR_STARTDMA 5 /* start DMA send (write only) */ +#define NCR_BUSANDSTAT 5 /* bus and status (read only) */ +#define NCR_DMATARGET 6 /* DMA target (write only) */ +#define NCR_INPUTDATA 6 /* input data (read only) */ +#define NCR_DMAINIRECV 7 /* DMA initiator receive (write only) */ +#define NCR_RESETPARITY 7 /* reset parity/interrupt (read only) */ + +#define ICR_DBP 0x01 +#define ICR_ATN 0x02 +#define ICR_SEL 0x04 +#define ICR_BSY 0x08 +#define ICR_ACK 0x10 +#define ICR_ARB_LOST 0x20 +#define ICR_ARB_IN_PROGRESS 0x40 + +#define MODE_ARBITRATE 0x01 +#define MODE_DMA 0x02 +#define MODE_MONITOR_BUSY 0x04 +#define MODE_ENA_EOP_INT 0x08 + +#define STATUS_ACK 0x01 +#define STATUS_BUSY_ERROR 0x04 +#define STATUS_PHASE_MATCH 0x08 +#define STATUS_INT 0x10 +#define STATUS_DRQ 0x40 +#define STATUS_END_OF_DMA 0x80 + +#define TCR_IO 0x01 +#define TCR_CD 0x02 +#define TCR_MSG 0x04 +#define TCR_REQ 0x08 +#define TCR_LAST_BYTE_SENT 0x80 + + +#define STATE_IDLE 0 +#define STATE_COMMAND 1 +#define STATE_DATAIN 2 +#define STATE_DATAOUT 3 +#define STATE_STATUS 4 +#define STATE_MESSAGEIN 5 +#define STATE_SELECT 6 +#define STATE_MESSAGEOUT 7 +#define STATE_MESSAGE_ID 8 + +#define DMA_IDLE 0 +#define DMA_SEND 1 +#define DMA_INITIATOR_RECEIVE 2 + +typedef struct ncr_t { + uint8_t icr; + uint8_t mode; + uint8_t tcr; + uint8_t data_wait; + uint8_t isr; + uint8_t output_data; + uint8_t target_id; + uint8_t tx_data; + uint8_t msglun; + + uint8_t command[20]; + uint8_t msgout[4]; + uint8_t bus; + + int msgout_pos; + int is_msgout; + + int dma_mode; + int cur_bus; + int bus_in; + int new_phase; + int state; + int clear_req; + int wait_data; + int wait_complete; + int command_pos; + int data_pos; + + int irq; + + double period; + + void *priv; + void (*dma_mode_ext)(void *priv, void *ext_priv); + int (*dma_send_ext)(void *priv, void *ext_priv); + int (*dma_initiator_receive_ext)(void *priv, void *ext_priv); + void (*timer)(void *ext_priv, double period); +} ncr_t; + +extern int ncr5380_cmd_len[8]; + +extern void ncr5380_irq(ncr_t *ncr, int set_irq); +extern uint32_t ncr5380_get_bus_host(ncr_t *ncr); +extern void ncr5380_bus_read(ncr_t *ncr); +extern void ncr5380_bus_update(ncr_t *ncr, int bus); +extern void ncr5380_write(uint16_t port, uint8_t val, ncr_t *ncr); +extern uint8_t ncr5380_read(uint16_t port, ncr_t *ncr); + +#ifdef EMU_DEVICE_H extern const device_t scsi_lcs6821n_device; extern const device_t scsi_rt1000b_device; extern const device_t scsi_rt1000mc_device; extern const device_t scsi_t128_device; +extern const device_t scsi_t228_device; extern const device_t scsi_t130b_device; extern const device_t scsi_ls2000_device; #if defined(DEV_BRANCH) && defined(USE_SUMO) extern const device_t scsi_scsiat_device; #endif +#endif #endif /*SCSI_NCR5380_H*/ diff --git a/src/scsi/CMakeLists.txt b/src/scsi/CMakeLists.txt index addde844e..01e20b929 100644 --- a/src/scsi/CMakeLists.txt +++ b/src/scsi/CMakeLists.txt @@ -14,5 +14,5 @@ # add_library(scsi OBJECT scsi.c scsi_device.c scsi_cdrom.c scsi_disk.c - scsi_x54x.c scsi_aha154x.c scsi_buslogic.c scsi_ncr5380.c - scsi_ncr53c8xx.c scsi_pcscsi.c scsi_spock.c) + scsi_x54x.c scsi_aha154x.c scsi_buslogic.c scsi_ncr5380.c scsi_ncr53c400.c + scsi_t128.c scsi_ncr53c8xx.c scsi_pcscsi.c scsi_spock.c) diff --git a/src/scsi/scsi.c b/src/scsi/scsi.c index 94c9048ef..328810a2f 100644 --- a/src/scsi/scsi.c +++ b/src/scsi/scsi.c @@ -82,6 +82,7 @@ static SCSI_CARD scsi_cards[] = { { &scsi_rt1000b_device, }, { &scsi_rt1000mc_device, }, { &scsi_t128_device, }, + { &scsi_t228_device, }, { &scsi_t130b_device, }, { &aha1640_device, }, { &buslogic_640a_device, }, diff --git a/src/scsi/scsi_cdrom.c b/src/scsi/scsi_cdrom.c index a6420fb01..3a49085ec 100644 --- a/src/scsi/scsi_cdrom.c +++ b/src/scsi/scsi_cdrom.c @@ -1001,7 +1001,7 @@ scsi_cdrom_command_common(scsi_cdrom_t *dev) case CDROM_TYPE_TOSHIBA_XM3301TA_0272: case CDROM_TYPE_TOSHIBA_XM5701TA_3136: case CDROM_TYPE_TOSHIBA_SDM1401_1008: - bytes_per_second = 176.0 * 1024.0; + bytes_per_second = 176400.0; bytes_per_second *= (double) dev->drv->cur_speed; break; } diff --git a/src/scsi/scsi_ncr5380.c b/src/scsi/scsi_ncr5380.c index e9ebe8504..d113d2cb3 100644 --- a/src/scsi/scsi_ncr5380.c +++ b/src/scsi/scsi_ncr5380.c @@ -6,8 +6,8 @@ * * This file is part of the 86Box distribution. * - * Implementation of the NCR 5380 series of SCSI Host Adapters - * made by NCR. These controllers were designed for the ISA bus. + * Implementation of the NCR 5380 chip made by NCR + * and used in various controllers. * * * @@ -16,8 +16,8 @@ * Fred N. van Kempen, * * Copyright 2017-2019 Sarah Walker. - * Copyright 2017-2019 TheCollector1995. * Copyright 2017-2019 Fred N. van Kempen. + * Copyright 2017-2024 TheCollector1995. */ #include #include @@ -42,156 +42,13 @@ #include <86box/scsi_device.h> #include <86box/scsi_ncr5380.h> -#define LCS6821N_ROM "roms/scsi/ncr5380/Longshine LCS-6821N - BIOS version 1.04.bin" -#define RT1000B_810R_ROM "roms/scsi/ncr5380/Rancho_RT1000_RTBios_version_8.10R.bin" -#define RT1000B_820R_ROM "roms/scsi/ncr5380/RTBIOS82.ROM" -#define T130B_ROM "roms/scsi/ncr5380/trantor_t130b_bios_v2.14.bin" -#define T128_ROM "roms/scsi/ncr5380/trantor_t128_bios_v1.12.bin" -#define COREL_LS2000_ROM "roms/scsi/ncr5380/Corel LS2000 - BIOS ROM - Ver 1.65.bin" - -#define NCR_CURDATA 0 /* current SCSI data (read only) */ -#define NCR_OUTDATA 0 /* output data (write only) */ -#define NCR_INITCOMMAND 1 /* initiator command (read/write) */ -#define NCR_MODE 2 /* mode (read/write) */ -#define NCR_TARGETCMD 3 /* target command (read/write) */ -#define NCR_SELENABLE 4 /* select enable (write only) */ -#define NCR_BUSSTATUS 4 /* bus status (read only) */ -#define NCR_STARTDMA 5 /* start DMA send (write only) */ -#define NCR_BUSANDSTAT 5 /* bus and status (read only) */ -#define NCR_DMATARGET 6 /* DMA target (write only) */ -#define NCR_INPUTDATA 6 /* input data (read only) */ -#define NCR_DMAINIRECV 7 /* DMA initiator receive (write only) */ -#define NCR_RESETPARITY 7 /* reset parity/interrupt (read only) */ - -#define ICR_DBP 0x01 -#define ICR_ATN 0x02 -#define ICR_SEL 0x04 -#define ICR_BSY 0x08 -#define ICR_ACK 0x10 -#define ICR_ARB_LOST 0x20 -#define ICR_ARB_IN_PROGRESS 0x40 - -#define MODE_ARBITRATE 0x01 -#define MODE_DMA 0x02 -#define MODE_MONITOR_BUSY 0x04 -#define MODE_ENA_EOP_INT 0x08 - -#define STATUS_ACK 0x01 -#define STATUS_BUSY_ERROR 0x04 -#define STATUS_PHASE_MATCH 0x08 -#define STATUS_INT 0x10 -#define STATUS_DRQ 0x40 -#define STATUS_END_OF_DMA 0x80 - -#define TCR_IO 0x01 -#define TCR_CD 0x02 -#define TCR_MSG 0x04 -#define TCR_REQ 0x08 -#define TCR_LAST_BYTE_SENT 0x80 - -#define CTRL_DATA_DIR 0x40 -#define STATUS_BUFFER_NOT_READY 0x04 -#define STATUS_53C80_ACCESSIBLE 0x80 - -typedef struct ncr_t { - uint8_t icr; - uint8_t mode; - uint8_t tcr; - uint8_t data_wait; - uint8_t isr; - uint8_t output_data; - uint8_t target_id; - uint8_t tx_data; - uint8_t msglun; - - uint8_t command[20]; - uint8_t msgout[4]; - int msgout_pos; - int is_msgout; - - int dma_mode; - int cur_bus; - int bus_in; - int new_phase; - int state; - int clear_req; - int wait_data; - int wait_complete; - int command_pos; - int data_pos; -} ncr_t; - -typedef struct t128_t { - uint8_t ctrl; - uint8_t status; - uint8_t buffer[512]; - uint8_t ext_ram[0x80]; - uint8_t block_count; - - int block_loaded; - int pos, host_pos; - - int bios_enabled; -} t128_t; - -typedef struct ncr5380_t { - ncr_t ncr; - t128_t t128; - - const char *name; - - uint8_t buffer[128]; - uint8_t int_ram[0x40]; - uint8_t ext_ram[0x600]; - - uint32_t rom_addr; - uint16_t base; - - int8_t irq; - int8_t type; - int8_t bios_ver; - uint8_t block_count; - uint8_t status_ctrl; - uint8_t bus, pad; - - rom_t bios_rom; - mem_mapping_t mapping; - - int block_count_loaded; - - int buffer_pos; - int buffer_host_pos; - - int dma_enabled; - - pc_timer_t timer; - double period; - - int ncr_busy; - uint8_t pos_regs[8]; -} ncr5380_t; - -#define STATE_IDLE 0 -#define STATE_COMMAND 1 -#define STATE_DATAIN 2 -#define STATE_DATAOUT 3 -#define STATE_STATUS 4 -#define STATE_MESSAGEIN 5 -#define STATE_SELECT 6 -#define STATE_MESSAGEOUT 7 -#define STATE_MESSAGE_ID 8 - -#define DMA_IDLE 0 -#define DMA_SEND 1 -#define DMA_INITIATOR_RECEIVE 2 - -static int cmd_len[8] = { 6, 10, 10, 6, 16, 12, 10, 6 }; +int ncr5380_cmd_len[8] = { 6, 10, 10, 6, 16, 12, 10, 6 }; #ifdef ENABLE_NCR5380_LOG int ncr5380_do_log = ENABLE_NCR5380_LOG; static void -ncr_log(const char *fmt, ...) +ncr5380_log(const char *fmt, ...) { va_list ap; @@ -202,28 +59,27 @@ ncr_log(const char *fmt, ...) } } #else -# define ncr_log(fmt, ...) +# 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)) -static void -ncr_callback(void *priv); - -static void -ncr_irq(ncr5380_t *ncr_dev, ncr_t *ncr, int set_irq) +void +ncr5380_irq(ncr_t *ncr, int set_irq) { if (set_irq) { ncr->isr |= STATUS_INT; - picint(1 << ncr_dev->irq); + if (ncr->irq != -1) + picint(1 << ncr->irq); } else { ncr->isr &= ~STATUS_INT; - picintc(1 << ncr_dev->irq); + if (ncr->irq != 1) + picintc(1 << ncr->irq); } } static int -get_dev_id(uint8_t data) +ncr5380_get_dev_id(uint8_t data) { for (uint8_t c = 0; c < SCSI_ID_MAX; c++) { if (data & (1 << c)) @@ -234,7 +90,7 @@ get_dev_id(uint8_t data) } static int -getmsglen(uint8_t *msgp, int len) +ncr5380_getmsglen(uint8_t *msgp, int len) { uint8_t msg = msgp[0]; if (msg == 0 || (msg >= 0x02 && msg <= 0x1f) || msg >= 0x80) @@ -247,21 +103,32 @@ getmsglen(uint8_t *msgp, int len) } static void -ncr_reset(ncr5380_t *ncr_dev, ncr_t *ncr) +ncr5380_reset(ncr_t *ncr) { - memset(ncr, 0x00, sizeof(ncr_t)); - ncr_log("NCR Reset\n"); + 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"); - timer_stop(&ncr_dev->timer); + ncr->timer(ncr->priv, 0.0); for (int i = 0; i < 8; i++) - scsi_device_reset(&scsi_devices[ncr_dev->bus][i]); + scsi_device_reset(&scsi_devices[ncr->bus][i]); - ncr_irq(ncr_dev, ncr, 0); + ncr5380_irq(ncr, 0); } -static uint32_t -get_bus_host(ncr_t *ncr) +uint32_t +ncr5380_get_bus_host(ncr_t *ncr) { uint32_t bus_host = 0; @@ -298,10 +165,9 @@ get_bus_host(ncr_t *ncr) return (bus_host | BUS_SETDATA(ncr->output_data)); } -static void -ncr_bus_read(ncr5380_t *ncr_dev) +void +ncr5380_bus_read(ncr_t *ncr) { - ncr_t *ncr = &ncr_dev->ncr; const scsi_device_t *dev; int phase; @@ -309,7 +175,7 @@ ncr_bus_read(ncr5380_t *ncr_dev) if (ncr->clear_req) { ncr->clear_req--; if (!ncr->clear_req) { - ncr_log("Prelude to command data\n"); + ncr5380_log("Prelude to command data\n"); SET_BUS_STATE(ncr, ncr->new_phase); ncr->cur_bus |= BUS_REQ; } @@ -318,7 +184,7 @@ ncr_bus_read(ncr5380_t *ncr_dev) if (ncr->wait_data) { ncr->wait_data--; if (!ncr->wait_data) { - dev = &scsi_devices[ncr_dev->bus][ncr->target_id]; + dev = &scsi_devices[ncr->bus][ncr->target_id]; SET_BUS_STATE(ncr, ncr->new_phase); phase = (ncr->cur_bus & SCSI_PHASE_MESSAGE_IN); @@ -354,12 +220,10 @@ ncr_bus_read(ncr5380_t *ncr_dev) } } -static void -ncr_bus_update(void *priv, int bus) +void +ncr5380_bus_update(ncr_t *ncr, int bus) { - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - ncr_t *ncr = &ncr_dev->ncr; - scsi_device_t *dev = &scsi_devices[ncr_dev->bus][ncr->target_id]; + scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; double p; uint8_t sel_data; int msglen; @@ -368,25 +232,25 @@ ncr_bus_update(void *priv, int bus) if (bus & BUS_ARB) ncr->state = STATE_IDLE; - ncr_log("State = %i\n", ncr->state); + ncr5380_log("State = %i\n", ncr->state); switch (ncr->state) { case STATE_IDLE: ncr->clear_req = ncr->wait_data = ncr->wait_complete = 0; if ((bus & BUS_SEL) && !(bus & BUS_BSY)) { - ncr_log("Selection phase\n"); + ncr5380_log("Selection phase\n"); sel_data = BUS_GETDATA(bus); - ncr->target_id = get_dev_id(sel_data); + ncr->target_id = ncr5380_get_dev_id(sel_data); - ncr_log("Select - target ID = %i\n", ncr->target_id); + ncr5380_log("Select - target ID = %i\n", ncr->target_id); /*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_dev->bus][ncr->target_id])) { + if ((ncr->target_id != (uint8_t) -1) && scsi_device_present(&scsi_devices[ncr->bus][ncr->target_id])) { ncr->cur_bus |= BUS_BSY; ncr->state = STATE_SELECT; } else { - ncr_log("Device not found at ID %i, Current Bus BSY=%02x\n", ncr->target_id, ncr->cur_bus); + ncr5380_log("Device not found at ID %i, Current Bus BSY=%02x\n", ncr->target_id, ncr->cur_bus); ncr->cur_bus = 0; } } @@ -394,11 +258,11 @@ ncr_bus_update(void *priv, int bus) case STATE_SELECT: if (!(bus & BUS_SEL)) { if (!(bus & BUS_ATN)) { - if ((ncr->target_id != (uint8_t) -1) && scsi_device_present(&scsi_devices[ncr_dev->bus][ncr->target_id])) { - ncr_log("Device found at ID %i, Current Bus BSY=%02x\n", ncr->target_id, ncr->cur_bus); + 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); ncr->state = STATE_COMMAND; ncr->cur_bus = BUS_BSY | BUS_REQ; - ncr_log("CurBus BSY|REQ=%02x\n", ncr->cur_bus); + ncr5380_log("CurBus BSY|REQ=%02x\n", ncr->cur_bus); ncr->command_pos = 0; SET_BUS_STATE(ncr, SCSI_PHASE_COMMAND); } else { @@ -406,7 +270,7 @@ ncr_bus_update(void *priv, int bus) ncr->cur_bus = 0; } } else { - ncr_log("Set to SCSI Message Out\n"); + ncr5380_log("Set to SCSI Message Out\n"); ncr->new_phase = SCSI_PHASE_MESSAGE_OUT; ncr->wait_data = 4; ncr->msgout_pos = 0; @@ -422,9 +286,9 @@ ncr_bus_update(void *priv, int bus) ncr->new_phase = ncr->cur_bus & SCSI_PHASE_MESSAGE_IN; ncr->cur_bus &= ~BUS_REQ; - ncr_log("Command pos=%i, output data=%02x\n", ncr->command_pos, BUS_GETDATA(bus)); + ncr5380_log("Command pos=%i, output data=%02x\n", ncr->command_pos, BUS_GETDATA(bus)); - if (ncr->command_pos == cmd_len[(ncr->command[0] >> 5) & 7]) { + if (ncr->command_pos == ncr5380_cmd_len[(ncr->command[0] >> 5) & 7]) { if (ncr->is_msgout) { ncr->is_msgout = 0; #if 0 @@ -435,23 +299,23 @@ ncr_bus_update(void *priv, int bus) /*Reset data position to default*/ ncr->data_pos = 0; - dev = &scsi_devices[ncr_dev->bus][ncr->target_id]; + dev = &scsi_devices[ncr->bus][ncr->target_id]; - ncr_log("SCSI Command 0x%02X for ID %d, status code=%02x\n", ncr->command[0], ncr->target_id, dev->status); + ncr5380_log("SCSI Command 0x%02X for ID %d, status code=%02x\n", ncr->command[0], ncr->target_id, dev->status); dev->buffer_length = -1; scsi_device_command_phase0(dev, ncr->command); - ncr_log("SCSI ID %i: Command %02X: Buffer Length %i, SCSI Phase %02X\n", ncr->target_id, ncr->command[0], dev->buffer_length, dev->phase); + 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); - ncr_dev->period = 1.0; + ncr->period = 1.0; 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)) { + if (dev->buffer_length && ((dev->phase == SCSI_PHASE_DATA_IN) || (dev->phase == SCSI_PHASE_DATA_OUT))) { p = scsi_device_get_callback(dev); - ncr_dev->period = (p > 0.0) ? p : (((double) dev->buffer_length) * 0.2); - ncr_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_dev->period, dev->buffer_length, ncr->dma_mode); + 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); } } ncr->new_phase = dev->phase; @@ -459,7 +323,7 @@ ncr_bus_update(void *priv, int bus) } break; case STATE_DATAIN: - dev = &scsi_devices[ncr_dev->bus][ncr->target_id]; + dev = &scsi_devices[ncr->bus][ncr->target_id]; if ((bus & BUS_ACK) && !(ncr->bus_in & BUS_ACK)) { if (ncr->data_pos >= dev->buffer_length) { ncr->cur_bus &= ~BUS_REQ; @@ -472,10 +336,10 @@ ncr_bus_update(void *priv, int bus) 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; - ncr_log("DMA mode idle in\n"); - timer_on_auto(&ncr_dev->timer, ncr_dev->period); + ncr5380_log("DMA mode idle in\n"); + ncr->timer(ncr->priv, ncr->period); } else { - ncr_log("DMA mode IN.\n"); + ncr5380_log("DMA mode IN.\n"); ncr->clear_req = 3; } @@ -485,7 +349,7 @@ ncr_bus_update(void *priv, int bus) } break; case STATE_DATAOUT: - dev = &scsi_devices[ncr_dev->bus][ncr->target_id]; + dev = &scsi_devices[ncr->bus][ncr->target_id]; if ((bus & BUS_ACK) && !(ncr->bus_in & BUS_ACK)) { dev->sc->temp_buffer[ncr->data_pos++] = BUS_GETDATA(bus); @@ -499,20 +363,20 @@ ncr_bus_update(void *priv, int bus) /*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; - ncr_log("DMA mode idle out\n"); - timer_on_auto(&ncr_dev->timer, ncr_dev->period); + ncr5380_log("DMA mode idle out\n"); + ncr->timer(ncr->priv, ncr->period); } else ncr->clear_req = 3; ncr->cur_bus &= ~BUS_REQ; - ncr_log("CurBus ~REQ_DataOut=%02x\n", ncr->cur_bus); + ncr5380_log("CurBus ~REQ_DataOut=%02x\n", ncr->cur_bus); } } 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_dev->bus][ncr->target_id], SCSI_LUN_USE_CDB); + scsi_device_identify(&scsi_devices[ncr->bus][ncr->target_id], SCSI_LUN_USE_CDB); ncr->cur_bus &= ~BUS_REQ; ncr->new_phase = SCSI_PHASE_MESSAGE_IN; ncr->wait_data = 4; @@ -527,10 +391,10 @@ ncr_bus_update(void *priv, int bus) } break; case STATE_MESSAGEOUT: - ncr_log("Ack on MSGOUT = %02x\n", (bus & BUS_ACK)); + ncr5380_log("Ack on MSGOUT = %02x\n", (bus & BUS_ACK)); if ((bus & BUS_ACK) && !(ncr->bus_in & BUS_ACK)) { ncr->msgout[ncr->msgout_pos++] = BUS_GETDATA(bus); - msglen = getmsglen(ncr->msgout, ncr->msgout_pos); + msglen = ncr5380_getmsglen(ncr->msgout, ncr->msgout_pos); if (ncr->msgout_pos >= msglen) { if ((ncr->msgout[0] & (0x80 | 0x20)) == 0x80) ncr->msglun = ncr->msgout[0] & 7; @@ -540,12 +404,12 @@ ncr_bus_update(void *priv, int bus) } break; case STATE_MESSAGE_ID: - if ((ncr->target_id != (uint8_t) -1) && scsi_device_present(&scsi_devices[ncr_dev->bus][ncr->target_id])) { - ncr_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_dev->bus][ncr->target_id], ncr->msglun); + 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); ncr->state = STATE_COMMAND; ncr->cur_bus = BUS_BSY | BUS_REQ; - ncr_log("CurBus BSY|REQ=%02x\n", ncr->cur_bus); + ncr5380_log("CurBus BSY|REQ=%02x\n", ncr->cur_bus); ncr->command_pos = 0; SET_BUS_STATE(ncr, SCSI_PHASE_COMMAND); } @@ -558,33 +422,30 @@ ncr_bus_update(void *priv, int bus) ncr->bus_in = bus; } -static void -ncr_write(uint16_t port, uint8_t val, void *priv) +void +ncr5380_write(uint16_t port, uint8_t val, ncr_t *ncr) { - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - ncr_t *ncr = &ncr_dev->ncr; - scsi_device_t *dev = &scsi_devices[ncr_dev->bus][ncr->target_id]; int bus_host = 0; - ncr_log("NCR5380 write(%04x,%02x)\n", port & 7, val); + ncr5380_log("NCR5380 write(%04x,%02x)\n", port & 7, val); switch (port & 7) { case 0: /* Output data register */ - ncr_log("Write: Output data register, val = %02x\n", val); + ncr5380_log("Write: Output data register, val = %02x\n", val); ncr->output_data = val; break; case 1: /* Initiator Command Register */ - ncr_log("Write: Initiator command register\n"); + ncr5380_log("Write: Initiator command register\n"); if ((val & 0x80) && !(ncr->icr & 0x80)) { - ncr_log("Resetting the 5380\n"); - ncr_reset(ncr_dev, &ncr_dev->ncr); + ncr5380_log("Resetting the 5380\n"); + ncr5380_reset(ncr); } ncr->icr = val; break; case 2: /* Mode register */ - ncr_log("Write: Mode register, val=%02x.\n", val); + ncr5380_log("Write: Mode register, val=%02x.\n", val); if ((val & MODE_ARBITRATE) && !(ncr->mode & MODE_ARBITRATE)) { ncr->icr &= ~ICR_ARB_LOST; ncr->icr |= ICR_ARB_IN_PROGRESS; @@ -592,132 +453,85 @@ ncr_write(uint16_t port, uint8_t val, void *priv) ncr->mode = val; - if (ncr_dev->type == 3) { - /*Don't stop the timer until it finishes the transfer*/ - if (ncr_dev->t128.block_loaded && (ncr->mode & MODE_DMA)) { - ncr_log("Continuing DMA mode\n"); - timer_on_auto(&ncr_dev->timer, ncr_dev->period + 1.0); - } - - /*When a pseudo-DMA transfer has completed (Send or Initiator Receive), mark it as complete and idle the status*/ - if (!ncr_dev->t128.block_loaded && !(ncr->mode & MODE_DMA)) { - ncr_log("No DMA mode\n"); - ncr->tcr &= ~TCR_LAST_BYTE_SENT; - ncr->isr &= ~STATUS_END_OF_DMA; - ncr->dma_mode = DMA_IDLE; - } - } else { - /*When a pseudo-DMA transfer has completed (Send or Initiator Receive), mark it as complete and idle the status*/ - if (!ncr_dev->block_count_loaded && !(ncr->mode & MODE_DMA)) { - ncr_log("No DMA mode\n"); - ncr->tcr &= ~TCR_LAST_BYTE_SENT; - ncr->isr &= ~STATUS_END_OF_DMA; - ncr->dma_mode = DMA_IDLE; - } - } + ncr->dma_mode_ext(ncr, ncr->priv); break; case 3: /* Target Command Register */ - ncr_log("Write: Target Command register\n"); + ncr5380_log("Write: Target Command register\n"); ncr->tcr = val; break; case 4: /* Select Enable Register */ - ncr_log("Write: Select Enable register\n"); + ncr5380_log("Write: Select Enable register\n"); break; case 5: /* start DMA Send */ - ncr_log("Write: start DMA send register\n"); + ncr5380_log("Write: start DMA send register\n"); /*a Write 6/10 has occurred, start the timer when the block count is loaded*/ ncr->dma_mode = DMA_SEND; - if (ncr_dev->type == 3) { - if ((ncr->mode & MODE_DMA) && !timer_is_on(&ncr_dev->timer) && (dev->buffer_length > 0)) { - memset(ncr_dev->t128.buffer, 0, MIN(512, dev->buffer_length)); - ncr_dev->t128.status |= 0x04; - ncr_dev->t128.host_pos = 0; - ncr_dev->t128.block_count = dev->buffer_length >> 9; - - if (dev->buffer_length < 512) - ncr_dev->t128.block_count = 1; - - ncr_dev->t128.block_loaded = 1; - } - } + if (ncr->dma_send_ext) + ncr->dma_send_ext(ncr, ncr->priv); break; case 7: /* start DMA Initiator Receive */ - ncr_log("Write: start DMA initiator receive register, dma? = %02x\n", ncr->mode & MODE_DMA); + ncr5380_log("Write: start DMA initiator receive register, dma? = %02x\n", ncr->mode & MODE_DMA); /*a Read 6/10 has occurred, start the timer when the block count is loaded*/ ncr->dma_mode = DMA_INITIATOR_RECEIVE; - if (ncr_dev->type == 3) { - if ((ncr->mode & MODE_DMA) && !timer_is_on(&ncr_dev->timer) && (dev->buffer_length > 0)) { - memset(ncr_dev->t128.buffer, 0, MIN(512, dev->buffer_length)); - ncr_dev->t128.status |= 0x04; - ncr_dev->t128.host_pos = MIN(512, dev->buffer_length); - ncr_dev->t128.block_count = dev->buffer_length >> 9; - - if (dev->buffer_length < 512) - ncr_dev->t128.block_count = 1; - - ncr_dev->t128.block_loaded = 1; - timer_on_auto(&ncr_dev->timer, 0.02); - } - } + if (ncr->dma_initiator_receive_ext) + ncr->dma_initiator_receive_ext(ncr, ncr->priv); break; default: - ncr_log("NCR5380: bad write %04x %02x\n", port, val); + ncr5380_log("NCR5380: bad write %04x %02x\n", port, val); break; } - bus_host = get_bus_host(ncr); - ncr_bus_update(priv, bus_host); + bus_host = ncr5380_get_bus_host(ncr); + ncr5380_bus_update(ncr, bus_host); } -static uint8_t -ncr_read(uint16_t port, void *priv) +uint8_t +ncr5380_read(uint16_t port, ncr_t *ncr) { - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - ncr_t *ncr = &ncr_dev->ncr; - uint8_t ret = 0xff; + uint8_t ret = 0xff; int bus; int bus_state; switch (port & 7) { case 0: /* Current SCSI data */ - ncr_log("Read: Current SCSI data register\n"); + ncr5380_log("Read: Current SCSI data register\n"); if (ncr->icr & ICR_DBP) { /*Return the data from the output register if on data bus phase from ICR*/ - ncr_log("Data Bus Phase, ret = %02x\n", ncr->output_data); + ncr5380_log("Data Bus Phase, ret = %02x\n", ncr->output_data); ret = ncr->output_data; } else { /*Return the data from the SCSI bus*/ - ncr_bus_read(ncr_dev); - ncr_log("NCR GetData=%02x\n", BUS_GETDATA(ncr->cur_bus)); + ncr5380_bus_read(ncr); + ncr5380_log("NCR GetData=%02x\n", BUS_GETDATA(ncr->cur_bus)); ret = BUS_GETDATA(ncr->cur_bus); } break; case 1: /* Initiator Command Register */ - ncr_log("Read: Initiator Command register, NCR ICR Read=%02x\n", ncr->icr); + ncr5380_log("Read: Initiator Command register, NCR ICR Read=%02x\n", ncr->icr); ret = ncr->icr; break; case 2: /* Mode register */ - ncr_log("Read: Mode register = %02x.\n", ncr->mode); + ncr5380_log("Read: Mode register = %02x.\n", ncr->mode); ret = ncr->mode; break; case 3: /* Target Command Register */ - ncr_log("Read: Target Command register, NCR target stat=%02x\n", ncr->tcr); + ncr5380_log("Read: Target Command register, NCR target stat=%02x\n", ncr->tcr); ret = ncr->tcr; break; case 4: /* Current SCSI Bus status */ - ncr_log("Read: SCSI bus status register\n"); + ncr5380_log("Read: SCSI bus status register\n"); ret = 0; - ncr_bus_read(ncr_dev); - ncr_log("NCR cur bus stat=%02x\n", ncr->cur_bus & 0xff); + ncr5380_bus_read(ncr); + ncr5380_log("NCR cur bus stat=%02x\n", ncr->cur_bus & 0xff); ret |= (ncr->cur_bus & 0xff); if (ncr->icr & ICR_SEL) ret |= BUS_SEL; @@ -726,19 +540,19 @@ ncr_read(uint16_t port, void *priv) break; case 5: /* Bus and Status register */ - ncr_log("Read: Bus and Status register\n"); + ncr5380_log("Read: Bus and Status register\n"); ret = 0; - bus = get_bus_host(ncr); - ncr_log("Get host from Interrupt\n"); + bus = ncr5380_get_bus_host(ncr); + ncr5380_log("Get host from Interrupt\n"); /*Check if the phase in process matches with TCR's*/ if ((bus & SCSI_PHASE_MESSAGE_IN) == (ncr->cur_bus & SCSI_PHASE_MESSAGE_IN)) { - ncr_log("Phase match\n"); + ncr5380_log("Phase match\n"); ret |= STATUS_PHASE_MATCH; } - ncr_bus_read(ncr_dev); + ncr5380_bus_read(ncr); bus = ncr->cur_bus; if ((bus & BUS_ACK) || (ncr->icr & ICR_ACK)) @@ -747,7 +561,7 @@ ncr_read(uint16_t port, void *priv) ret |= 0x02; if ((bus & BUS_REQ) && (ncr->mode & MODE_DMA)) { - ncr_log("Entering DMA mode\n"); + ncr5380_log("Entering DMA mode\n"); ret |= STATUS_DRQ; bus_state = 0; @@ -759,12 +573,12 @@ ncr_read(uint16_t port, void *priv) if (bus & BUS_MSG) bus_state |= TCR_MSG; if ((ncr->tcr & 7) != bus_state) { - ncr_irq(ncr_dev, ncr, 1); - ncr_log("IRQ issued\n"); + ncr5380_irq(ncr, 1); + ncr5380_log("IRQ issued\n"); } } if (!(bus & BUS_BSY) && (ncr->mode & MODE_MONITOR_BUSY)) { - ncr_log("Busy error\n"); + ncr5380_log("Busy error\n"); ret |= STATUS_BUSY_ERROR; } ret |= (ncr->isr & (STATUS_INT | STATUS_END_OF_DMA)); @@ -776,1185 +590,16 @@ ncr_read(uint16_t port, void *priv) case 7: /* reset Parity/Interrupt */ ncr->isr &= ~(STATUS_BUSY_ERROR | 0x20); - ncr_irq(ncr_dev, ncr, 0); - ncr_log("Reset Interrupt\n"); + ncr5380_irq(ncr, 0); + ncr5380_log("Reset Interrupt\n"); break; default: - ncr_log("NCR5380: bad read %04x\n", port); + ncr5380_log("NCR5380: bad read %04x\n", port); break; } - ncr_log("NCR5380 read(%04x)=%02x\n", port & 7, ret); + ncr5380_log("NCR5380 read(%04x)=%02x\n", port & 7, ret); return ret; } - -/* Memory-mapped I/O READ handler. */ -static uint8_t -memio_read(uint32_t addr, void *priv) -{ - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - ncr_t *ncr = &ncr_dev->ncr; - scsi_device_t *dev = &scsi_devices[ncr_dev->bus][ncr->target_id]; - uint8_t ret = 0xff; - - addr &= 0x3fff; - - if (addr < 0x2000) - ret = ncr_dev->bios_rom.rom[addr & 0x1fff]; - else if (addr < 0x3800) - ret = 0xff; - else if (addr >= 0x3a00) - ret = ncr_dev->ext_ram[addr - 0x3a00]; - else - switch (addr & 0x3f80) { - case 0x3800: -#if ENABLE_NCR5380_LOG - ncr_log("Read intRAM %02x %02x\n", addr & 0x3f, ncr_dev->int_ram[addr & 0x3f]); -#endif - ret = ncr_dev->int_ram[addr & 0x3f]; - break; - - case 0x3880: -#if ENABLE_NCR5380_LOG - ncr_log("Read 53c80 %04x\n", addr); -#endif - ret = ncr_read(addr, ncr_dev); - break; - - case 0x3900: - if (ncr_dev->buffer_host_pos >= MIN(128, dev->buffer_length) || (!(ncr_dev->status_ctrl & CTRL_DATA_DIR))) { - ret = 0xff; - ncr_log("No Read.\n"); - } else { - ret = ncr_dev->buffer[ncr_dev->buffer_host_pos++]; - ncr_log("Read host pos = %i, ret = %02x\n", ncr_dev->buffer_host_pos, ret); - - if (ncr_dev->buffer_host_pos == MIN(128, dev->buffer_length)) { - ncr_dev->status_ctrl |= STATUS_BUFFER_NOT_READY; - ncr_log("Transfer busy read, status = %02x\n", ncr_dev->status_ctrl); - } - } - break; - - case 0x3980: - switch (addr) { - case 0x3980: /* status */ - ret = ncr_dev->status_ctrl; - ncr_log("NCR status ctrl read=%02x\n", ncr_dev->status_ctrl & STATUS_BUFFER_NOT_READY); - if (!ncr_dev->ncr_busy) - ret |= STATUS_53C80_ACCESSIBLE; - if (ncr->mode & 0x30) { /*Parity bits*/ - if (!(ncr->mode & MODE_DMA)) { /*This is to avoid RTBios 8.10R BIOS problems with the hard disk and detection.*/ - ret |= 0x01; /*If the parity bits are set, bit 0 of the 53c400 status port should be set as well.*/ - ncr->mode = 0; /*Required by RTASPI10.SYS otherwise it won't initialize.*/ - } - } - ncr_log("NCR 53c400 status = %02x.\n", ret); - break; - - case 0x3981: /* block counter register*/ - ret = ncr_dev->block_count; - break; - - case 0x3982: /* switch register read */ - ret = 0xf8; - ret |= (ncr_dev->irq & 0x07); - ncr_log("Switches read=%02x.\n", ret); - break; - - default: - break; - } - break; - - default: - break; - } - -#if ENABLE_NCR5380_LOG - if (addr >= 0x3880) - ncr_log("memio_read(%08x)=%02x\n", addr, ret); -#endif - - return ret; -} - -/* Memory-mapped I/O WRITE handler. */ -static void -memio_write(uint32_t addr, uint8_t val, void *priv) -{ - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - ncr_t *ncr = &ncr_dev->ncr; - scsi_device_t *dev = &scsi_devices[ncr_dev->bus][ncr->target_id]; - - addr &= 0x3fff; - - if (addr >= 0x3a00) - ncr_dev->ext_ram[addr - 0x3a00] = val; - else - switch (addr & 0x3f80) { - case 0x3800: - ncr_dev->int_ram[addr & 0x3f] = val; - break; - - case 0x3880: - ncr_write(addr, val, ncr_dev); - break; - - case 0x3900: - if (!(ncr_dev->status_ctrl & CTRL_DATA_DIR) && ncr_dev->buffer_host_pos < MIN(128, dev->buffer_length)) { - ncr_dev->buffer[ncr_dev->buffer_host_pos++] = val; - - ncr_log("Write host pos = %i, val = %02x\n", ncr_dev->buffer_host_pos, val); - - if (ncr_dev->buffer_host_pos == MIN(128, dev->buffer_length)) { - ncr_dev->status_ctrl |= STATUS_BUFFER_NOT_READY; - ncr_dev->ncr_busy = 1; - } - } - break; - - case 0x3980: - switch (addr) { - case 0x3980: /* Control */ - ncr_log("NCR 53c400 control = %02x, mode = %02x.\n", val, ncr->mode); - if ((val & CTRL_DATA_DIR) && !(ncr_dev->status_ctrl & CTRL_DATA_DIR)) { - ncr_dev->buffer_host_pos = MIN(128, dev->buffer_length); - ncr_dev->status_ctrl |= STATUS_BUFFER_NOT_READY; - } else if (!(val & CTRL_DATA_DIR) && (ncr_dev->status_ctrl & CTRL_DATA_DIR)) { - ncr_dev->buffer_host_pos = 0; - ncr_dev->status_ctrl &= ~STATUS_BUFFER_NOT_READY; - } - ncr_dev->status_ctrl = (ncr_dev->status_ctrl & 0x87) | (val & 0x78); - break; - - case 0x3981: /* block counter register */ - ncr_log("Write block counter register: val=%d, dma mode=%x, period=%lf\n", val, ncr->dma_mode, ncr_dev->period); - ncr_dev->block_count = val; - ncr_dev->block_count_loaded = 1; - - if (ncr_dev->status_ctrl & CTRL_DATA_DIR) { - ncr_dev->buffer_host_pos = MIN(128, dev->buffer_length); - ncr_dev->status_ctrl |= STATUS_BUFFER_NOT_READY; - } else { - ncr_dev->buffer_host_pos = 0; - ncr_dev->status_ctrl &= ~STATUS_BUFFER_NOT_READY; - } - if ((ncr->mode & MODE_DMA) && !timer_is_on(&ncr_dev->timer) && (dev->buffer_length > 0)) { - memset(ncr_dev->buffer, 0, MIN(128, dev->buffer_length)); - ncr_log("DMA timer on\n"); - timer_on_auto(&ncr_dev->timer, ncr_dev->period); - } - break; - - default: - break; - } - break; - - default: - break; - } -} - -/* Memory-mapped I/O READ handler for the Trantor T130B. */ -static uint8_t -t130b_read(uint32_t addr, void *priv) -{ - const ncr5380_t *ncr_dev = (ncr5380_t *) priv; - uint8_t ret = 0xff; - - addr &= 0x3fff; - if (addr < 0x1800) - ret = ncr_dev->bios_rom.rom[addr & 0x1fff]; - else if (addr >= 0x1800 && addr < 0x1880) - ret = ncr_dev->ext_ram[addr & 0x7f]; - - ncr_log("MEM: Reading %02X from %08X\n", ret, addr); - return ret; -} - -/* Memory-mapped I/O WRITE handler for the Trantor T130B. */ -static void -t130b_write(uint32_t addr, uint8_t val, void *priv) -{ - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - - addr &= 0x3fff; - ncr_log("MEM: Writing %02X to %08X\n", val, addr); - if (addr >= 0x1800 && addr < 0x1880) - ncr_dev->ext_ram[addr & 0x7f] = val; -} - -static uint8_t -t130b_in(uint16_t port, void *priv) -{ - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - uint8_t ret = 0xff; - - switch (port & 0x0f) { - case 0x00: - case 0x01: - case 0x02: - case 0x03: - ret = memio_read((port & 7) | 0x3980, ncr_dev); - break; - - case 0x04: - case 0x05: - ret = memio_read(0x3900, ncr_dev); - break; - - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - ret = ncr_read(port, ncr_dev); - break; - - default: - break; - } - - ncr_log("I/O: Reading %02X from %04X\n", ret, port); - return ret; -} - -static void -t130b_out(uint16_t port, uint8_t val, void *priv) -{ - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - - ncr_log("I/O: Writing %02X to %04X\n", val, port); - - switch (port & 0x0f) { - case 0x00: - case 0x01: - case 0x02: - case 0x03: - memio_write((port & 7) | 0x3980, val, ncr_dev); - break; - - case 0x04: - case 0x05: - memio_write(0x3900, val, ncr_dev); - break; - - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - ncr_write(port, val, ncr_dev); - break; - - default: - break; - } -} - -static void -ncr_callback(void *priv) -{ - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - ncr_t *ncr = &ncr_dev->ncr; - scsi_device_t *dev = &scsi_devices[ncr_dev->bus][ncr->target_id]; - int bus; - int bytes_tx = 0; - int limit = 100; - uint8_t temp; - - if (ncr_dev->type != 3) { - if (ncr->dma_mode != DMA_IDLE) - timer_on_auto(&ncr_dev->timer, 1.0); - } else { - if ((ncr->dma_mode != DMA_IDLE) && (ncr->mode & MODE_DMA) && ncr_dev->t128.block_loaded) { - if ((ncr_dev->t128.host_pos == MIN(512, dev->buffer_length)) && ncr_dev->t128.block_count) - ncr_dev->t128.status |= 0x04; - - timer_on_auto(&ncr_dev->timer, ncr_dev->period / 55.0); - } - } - - if (ncr->data_wait & 1) { - ncr->clear_req = 3; - ncr->data_wait &= ~1; - if (ncr_dev->type == 3) { - if (ncr->dma_mode == DMA_IDLE) - return; - } - } - - if (ncr_dev->type != 3) { - if (ncr->dma_mode == DMA_IDLE) - return; - } - - switch (ncr->dma_mode) { - case DMA_SEND: - if (ncr_dev->type != 3) { - if (ncr_dev->status_ctrl & CTRL_DATA_DIR) { - ncr_log("DMA_SEND with DMA direction set wrong\n"); - break; - } - - if (!(ncr_dev->status_ctrl & STATUS_BUFFER_NOT_READY)) { - ncr_log("Write buffer status ready\n"); - break; - } - - if (!ncr_dev->block_count_loaded) - break; - - while (bytes_tx < limit) { - for (uint8_t c = 0; c < 10; c++) { - ncr_bus_read(ncr_dev); - if (ncr->cur_bus & BUS_REQ) - break; - } - - /* Data ready. */ - temp = ncr_dev->buffer[ncr_dev->buffer_pos]; - - bus = get_bus_host(ncr) & ~BUS_DATAMASK; - bus |= BUS_SETDATA(temp); - - ncr_bus_update(ncr_dev, bus | BUS_ACK); - ncr_bus_update(ncr_dev, bus & ~BUS_ACK); - - ncr_dev->buffer_pos++; - bytes_tx++; - ncr_log("Buffer pos for writing = %d\n", ncr_dev->buffer_pos); - - if (ncr_dev->buffer_pos == MIN(128, dev->buffer_length)) { - bytes_tx = 0; - ncr_dev->status_ctrl &= ~STATUS_BUFFER_NOT_READY; - ncr_dev->buffer_pos = 0; - ncr_dev->buffer_host_pos = 0; - ncr_dev->ncr_busy = 0; - ncr_dev->block_count = (ncr_dev->block_count - 1) & 0xff; - ncr_log("Remaining blocks to be written=%d\n", ncr_dev->block_count); - if (!ncr_dev->block_count) { - ncr_dev->block_count_loaded = 0; - ncr_log("IO End of write transfer\n"); - ncr->tcr |= TCR_LAST_BYTE_SENT; - ncr->isr |= STATUS_END_OF_DMA; - timer_stop(&ncr_dev->timer); - if (ncr->mode & MODE_ENA_EOP_INT) { - ncr_log("NCR write irq\n"); - ncr_irq(ncr_dev, ncr, 1); - } - } - break; - } - } - } else { - if (!(ncr_dev->t128.status & 0x04)) { - ncr_log("Write status busy, block count = %i, host pos = %i\n", ncr_dev->t128.block_count, ncr_dev->t128.host_pos); - break; - } - - if (!ncr_dev->t128.block_loaded) { - ncr_log("Write block not loaded\n"); - break; - } - - if (ncr_dev->t128.host_pos < MIN(512, dev->buffer_length)) - break; - -write_again: - for (uint8_t c = 0; c < 10; c++) { - ncr_bus_read(ncr_dev); - if (ncr->cur_bus & BUS_REQ) - break; - } - - /* Data ready. */ - temp = ncr_dev->t128.buffer[ncr_dev->t128.pos]; - - bus = get_bus_host(ncr) & ~BUS_DATAMASK; - bus |= BUS_SETDATA(temp); - - ncr_bus_update(ncr_dev, bus | BUS_ACK); - ncr_bus_update(ncr_dev, bus & ~BUS_ACK); - - ncr_dev->t128.pos++; - ncr_log("Buffer pos for writing = %d\n", ncr_dev->t128.pos); - - if (ncr_dev->t128.pos == MIN(512, dev->buffer_length)) { - ncr_dev->t128.pos = 0; - ncr_dev->t128.host_pos = 0; - ncr_dev->t128.status &= ~0x02; - ncr_dev->ncr_busy = 0; - ncr_dev->t128.block_count = (ncr_dev->t128.block_count - 1) & 0xff; - ncr_log("Remaining blocks to be written=%d\n", ncr_dev->t128.block_count); - if (!ncr_dev->t128.block_count) { - ncr_dev->t128.block_loaded = 0; - ncr_log("IO End of write transfer\n"); - ncr->tcr |= TCR_LAST_BYTE_SENT; - ncr->isr |= STATUS_END_OF_DMA; - timer_stop(&ncr_dev->timer); - if (ncr->mode & MODE_ENA_EOP_INT) { - ncr_log("NCR write irq\n"); - ncr_irq(ncr_dev, ncr, 1); - } - } - break; - } else - goto write_again; - } - break; - - case DMA_INITIATOR_RECEIVE: - if (ncr_dev->type != 3) { - if (!(ncr_dev->status_ctrl & CTRL_DATA_DIR)) { - ncr_log("DMA_INITIATOR_RECEIVE with DMA direction set wrong\n"); - break; - } - - if (!(ncr_dev->status_ctrl & STATUS_BUFFER_NOT_READY)) { - ncr_log("Read buffer status ready\n"); - break; - } - - if (!ncr_dev->block_count_loaded) - break; - - while (bytes_tx < limit) { - for (uint8_t c = 0; c < 10; c++) { - ncr_bus_read(ncr_dev); - if (ncr->cur_bus & BUS_REQ) - break; - } - - /* Data ready. */ - ncr_bus_read(ncr_dev); - temp = BUS_GETDATA(ncr->cur_bus); - - bus = get_bus_host(ncr); - - ncr_bus_update(ncr_dev, bus | BUS_ACK); - ncr_bus_update(ncr_dev, bus & ~BUS_ACK); - - ncr_dev->buffer[ncr_dev->buffer_pos++] = temp; - ncr_log("Buffer pos for reading = %d\n", ncr_dev->buffer_pos); - bytes_tx++; - - if (ncr_dev->buffer_pos == MIN(128, dev->buffer_length)) { - bytes_tx = 0; - ncr_dev->status_ctrl &= ~STATUS_BUFFER_NOT_READY; - ncr_dev->buffer_pos = 0; - ncr_dev->buffer_host_pos = 0; - ncr_dev->block_count = (ncr_dev->block_count - 1) & 0xff; - ncr_log("Remaining blocks to be read=%d\n", ncr_dev->block_count); - if (!ncr_dev->block_count) { - ncr_dev->block_count_loaded = 0; - ncr_log("IO End of read transfer\n"); - ncr->isr |= STATUS_END_OF_DMA; - timer_stop(&ncr_dev->timer); - if (ncr->mode & MODE_ENA_EOP_INT) { - ncr_log("NCR read irq\n"); - ncr_irq(ncr_dev, ncr, 1); - } - } - break; - } - } - } else { - if (!(ncr_dev->t128.status & 0x04)) { - ncr_log("Read status busy, block count = %i, host pos = %i\n", ncr_dev->t128.block_count, ncr_dev->t128.host_pos); - break; - } - - if (!ncr_dev->t128.block_loaded) { - ncr_log("Read block not loaded\n"); - break; - } - - if (ncr_dev->t128.host_pos < MIN(512, dev->buffer_length)) - break; - -read_again: - for (uint8_t c = 0; c < 10; c++) { - ncr_bus_read(ncr_dev); - if (ncr->cur_bus & BUS_REQ) - break; - } - - /* Data ready. */ - ncr_bus_read(ncr_dev); - temp = BUS_GETDATA(ncr->cur_bus); - - bus = get_bus_host(ncr); - - ncr_bus_update(ncr_dev, bus | BUS_ACK); - ncr_bus_update(ncr_dev, bus & ~BUS_ACK); - - ncr_dev->t128.buffer[ncr_dev->t128.pos++] = temp; - ncr_log("Buffer pos for reading=%d, temp=%02x, len=%d.\n", ncr_dev->t128.pos, temp, dev->buffer_length); - - if (ncr_dev->t128.pos == MIN(512, dev->buffer_length)) { - ncr_dev->t128.pos = 0; - ncr_dev->t128.host_pos = 0; - ncr_dev->t128.status &= ~0x02; - ncr_dev->t128.block_count = (ncr_dev->t128.block_count - 1) & 0xff; - ncr_log("Remaining blocks to be read=%d, status=%02x, len=%i, cdb[0] = %02x\n", ncr_dev->t128.block_count, ncr_dev->t128.status, dev->buffer_length, ncr->command[0]); - if (!ncr_dev->t128.block_count) { - ncr_dev->t128.block_loaded = 0; - ncr_log("IO End of read transfer\n"); - ncr->isr |= STATUS_END_OF_DMA; - timer_stop(&ncr_dev->timer); - if (ncr->mode & MODE_ENA_EOP_INT) { - ncr_log("NCR read irq\n"); - ncr_irq(ncr_dev, ncr, 1); - } - } - break; - } else - goto read_again; - } - break; - - default: - break; - } - - ncr_bus_read(ncr_dev); - - if (!(ncr->cur_bus & BUS_BSY) && (ncr->mode & MODE_MONITOR_BUSY)) { - ncr_log("Updating DMA\n"); - ncr->mode &= ~MODE_DMA; - ncr->dma_mode = DMA_IDLE; - if (ncr_dev->type == 3) - timer_on_auto(&ncr_dev->timer, 10.0); - } -} - -static uint8_t -t128_read(uint32_t addr, void *priv) -{ - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - const ncr_t *ncr = &ncr_dev->ncr; - scsi_device_t *dev = &scsi_devices[ncr_dev->bus][ncr->target_id]; - uint8_t ret = 0xff; - - addr &= 0x3fff; - if (ncr_dev->t128.bios_enabled && (addr >= 0) && (addr < 0x1800)) - ret = ncr_dev->bios_rom.rom[addr & 0x1fff]; - else if ((addr >= 0x1800) && (addr < 0x1880)) - ret = ncr_dev->t128.ext_ram[addr & 0x7f]; - else if ((addr >= 0x1c00) && (addr < 0x1c20)) { - ret = ncr_dev->t128.ctrl; - ncr_log("T128 ctrl read=%02x, dma=%02x\n", ret, ncr->mode & MODE_DMA); - } else if ((addr >= 0x1c20) && (addr < 0x1c40)) { - ret = ncr_dev->t128.status; - ncr_log("T128 status read=%02x, dma=%02x\n", ret, ncr->mode & MODE_DMA); - } else if ((addr >= 0x1d00) && (addr < 0x1e00)) - ret = ncr_read((addr - 0x1d00) >> 5, ncr_dev); - else if (addr >= 0x1e00 && addr < 0x2000) { - if ((ncr_dev->t128.host_pos >= MIN(512, dev->buffer_length)) || - (ncr->dma_mode != DMA_INITIATOR_RECEIVE)) - ret = 0xff; - else { - ret = ncr_dev->t128.buffer[ncr_dev->t128.host_pos++]; - - ncr_log("Read transfer, addr = %i, pos = %i\n", addr & 0x1ff, - ncr_dev->t128.host_pos); - - if (ncr_dev->t128.host_pos == MIN(512, dev->buffer_length)) { - ncr_dev->t128.status &= ~0x04; - ncr_log("Transfer busy read, status = %02x, period = %lf\n", - ncr_dev->t128.status, ncr_dev->period); - if ((ncr_dev->period == 0.2) || (ncr_dev->period == 0.02)) - timer_on_auto(&ncr_dev->timer, 40.2); - } else if ((ncr_dev->t128.host_pos < MIN(512, dev->buffer_length)) && - (scsi_device_get_callback(dev) > 100.0)) - cycles += 100; /*Needed to avoid timer de-syncing with transfers.*/ - } - } - - return ret; -} - -static void -t128_write(uint32_t addr, uint8_t val, void *priv) -{ - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - const ncr_t *ncr = &ncr_dev->ncr; - const scsi_device_t *dev = &scsi_devices[ncr_dev->bus][ncr->target_id]; - - addr &= 0x3fff; - if ((addr >= 0x1800) && (addr < 0x1880)) - ncr_dev->t128.ext_ram[addr & 0x7f] = val; - else if ((addr >= 0x1c00) && (addr < 0x1c20)) { - if ((val & 0x02) && !(ncr_dev->t128.ctrl & 0x02)) - ncr_dev->t128.status |= 0x02; - - ncr_dev->t128.ctrl = val; - ncr_log("T128 ctrl write=%02x\n", val); - } else if ((addr >= 0x1d00) && (addr < 0x1e00)) - ncr_write((addr - 0x1d00) >> 5, val, ncr_dev); - else if ((addr >= 0x1e00) && (addr < 0x2000)) { - if ((ncr_dev->t128.host_pos < MIN(512, dev->buffer_length)) && - (ncr->dma_mode == DMA_SEND)) { - ncr_dev->t128.buffer[ncr_dev->t128.host_pos] = val; - ncr_dev->t128.host_pos++; - - ncr_log("Write transfer, addr = %i, pos = %i, val = %02x\n", - addr & 0x1ff, ncr_dev->t128.host_pos, val); - - if (ncr_dev->t128.host_pos == MIN(512, dev->buffer_length)) { - ncr_dev->t128.status &= ~0x04; - ncr_dev->ncr_busy = 1; - ncr_log("Transfer busy write, status = %02x\n", ncr_dev->t128.status); - timer_on_auto(&ncr_dev->timer, 0.02); - } - } - } -} - -static uint8_t -rt1000b_mc_read(int port, void *priv) -{ - const ncr5380_t *ncr_dev = (ncr5380_t *) priv; - - return (ncr_dev->pos_regs[port & 7]); -} - -static void -rt1000b_mc_write(int port, uint8_t val, void *priv) -{ - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - - /* MCA does not write registers below 0x0100. */ - if (port < 0x0102) - return; - - mem_mapping_disable(&ncr_dev->bios_rom.mapping); - mem_mapping_disable(&ncr_dev->mapping); - - /* Save the MCA register value. */ - ncr_dev->pos_regs[port & 7] = val; - - if (ncr_dev->pos_regs[2] & 1) { - switch (ncr_dev->pos_regs[2] & 0xe0) { - case 0: - ncr_dev->rom_addr = 0xd4000; - break; - case 0x20: - ncr_dev->rom_addr = 0xd0000; - break; - case 0x40: - ncr_dev->rom_addr = 0xcc000; - break; - case 0x60: - ncr_dev->rom_addr = 0xc8000; - break; - case 0xc0: - ncr_dev->rom_addr = 0xdc000; - break; - case 0xe0: - ncr_dev->rom_addr = 0xd8000; - break; - - default: - break; - } - - mem_mapping_set_addr(&ncr_dev->bios_rom.mapping, ncr_dev->rom_addr, 0x4000); - mem_mapping_set_addr(&ncr_dev->mapping, ncr_dev->rom_addr, 0x4000); - } -} - -static uint8_t -rt1000b_mc_feedb(void *priv) -{ - const ncr5380_t *ncr_dev = (ncr5380_t *) priv; - - return ncr_dev->pos_regs[2] & 1; -} - -static void * -ncr_init(const device_t *info) -{ - const char *fn = NULL; - char temp[128]; - ncr5380_t *ncr_dev; - - ncr_dev = malloc(sizeof(ncr5380_t)); - memset(ncr_dev, 0x00, sizeof(ncr5380_t)); - ncr_dev->name = info->name; - ncr_dev->type = info->local; - - ncr_dev->bus = scsi_get_bus(); - - switch (ncr_dev->type) { - case 0: /* Longshine LCS6821N */ - ncr_dev->rom_addr = device_get_config_hex20("bios_addr"); - ncr_dev->irq = device_get_config_int("irq"); - rom_init(&ncr_dev->bios_rom, LCS6821N_ROM, - ncr_dev->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); - - mem_mapping_add(&ncr_dev->mapping, ncr_dev->rom_addr, 0x4000, - memio_read, NULL, NULL, - memio_write, NULL, NULL, - ncr_dev->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr_dev); - break; - - case 1: /* Rancho RT1000B/MC */ - ncr_dev->rom_addr = device_get_config_hex20("bios_addr"); - ncr_dev->irq = device_get_config_int("irq"); - ncr_dev->bios_ver = device_get_config_int("bios_ver"); - if (info->flags & DEVICE_MCA) { - ncr_dev->rom_addr = 0xd8000; - ncr_dev->bios_ver = 1; - } - - switch (ncr_dev->bios_ver) { - case 0: - fn = RT1000B_810R_ROM; - break; - case 1: - fn = RT1000B_820R_ROM; - break; - } - - rom_init(&ncr_dev->bios_rom, fn, - ncr_dev->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); - - if (info->flags & DEVICE_MCA) { - mem_mapping_add(&ncr_dev->mapping, 0, 0, - memio_read, NULL, NULL, - memio_write, NULL, NULL, - ncr_dev->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr_dev); - ncr_dev->pos_regs[0] = 0x8d; - ncr_dev->pos_regs[1] = 0x70; - mca_add(rt1000b_mc_read, rt1000b_mc_write, rt1000b_mc_feedb, NULL, ncr_dev); - } else { - mem_mapping_add(&ncr_dev->mapping, ncr_dev->rom_addr, 0x4000, - memio_read, NULL, NULL, - memio_write, NULL, NULL, - ncr_dev->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr_dev); - } - break; - - case 2: /* Trantor T130B */ - ncr_dev->rom_addr = device_get_config_hex20("bios_addr"); - ncr_dev->base = device_get_config_hex16("base"); - ncr_dev->irq = device_get_config_int("irq"); - - if (ncr_dev->rom_addr > 0x00000) { - rom_init(&ncr_dev->bios_rom, T130B_ROM, - ncr_dev->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); - - mem_mapping_add(&ncr_dev->mapping, ncr_dev->rom_addr, 0x4000, - t130b_read, NULL, NULL, - t130b_write, NULL, NULL, - ncr_dev->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr_dev); - } - - io_sethandler(ncr_dev->base, 16, - t130b_in, NULL, NULL, t130b_out, NULL, NULL, ncr_dev); - break; - - case 3: /* Trantor T128 */ - ncr_dev->rom_addr = device_get_config_hex20("bios_addr"); - ncr_dev->irq = device_get_config_int("irq"); - ncr_dev->t128.bios_enabled = device_get_config_int("boot"); - - if (ncr_dev->t128.bios_enabled) - rom_init(&ncr_dev->bios_rom, T128_ROM, - ncr_dev->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); - - mem_mapping_add(&ncr_dev->mapping, ncr_dev->rom_addr, 0x4000, - t128_read, NULL, NULL, - t128_write, NULL, NULL, - ncr_dev->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr_dev); - break; - - case 4: /* Corel LS2000 */ - ncr_dev->rom_addr = device_get_config_hex20("bios_addr"); - ncr_dev->irq = device_get_config_int("irq"); - rom_init(&ncr_dev->bios_rom, COREL_LS2000_ROM, - ncr_dev->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); - - mem_mapping_add(&ncr_dev->mapping, ncr_dev->rom_addr, 0x4000, - memio_read, NULL, NULL, - memio_write, NULL, NULL, - ncr_dev->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr_dev); - break; - - default: - break; - } - - sprintf(temp, "%s: BIOS=%05X", ncr_dev->name, ncr_dev->rom_addr); - if (ncr_dev->base != 0) - sprintf(&temp[strlen(temp)], " I/O=%04x", ncr_dev->base); - if (ncr_dev->irq != 0) - sprintf(&temp[strlen(temp)], " IRQ=%d", ncr_dev->irq); - ncr_log("%s\n", temp); - - if ((ncr_dev->type < 3) || (ncr_dev->type == 4)) { - ncr_dev->status_ctrl = STATUS_BUFFER_NOT_READY; - ncr_dev->buffer_host_pos = 128; - } else { - ncr_dev->t128.status = 0x04; - ncr_dev->t128.host_pos = 512; - if (!ncr_dev->t128.bios_enabled) - ncr_dev->t128.status |= 0x80; - } - timer_add(&ncr_dev->timer, ncr_callback, ncr_dev, 0); - - scsi_bus_set_speed(ncr_dev->bus, 5000000.0); - - return ncr_dev; -} - -static void -ncr_close(void *priv) -{ - ncr5380_t *ncr_dev = (ncr5380_t *) priv; - - if (ncr_dev) { - /* Tell the timer to terminate. */ - timer_stop(&ncr_dev->timer); - - free(ncr_dev); - ncr_dev = NULL; - } -} - -static int -lcs6821n_available(void) -{ - return (rom_present(LCS6821N_ROM)); -} - -static int -rt1000b_available(void) -{ - return (rom_present(RT1000B_820R_ROM) && rom_present(RT1000B_810R_ROM)); -} - -static int -rt1000b_820_available(void) -{ - return (rom_present(RT1000B_820R_ROM)); -} - -static int -t130b_available(void) -{ - return (rom_present(T130B_ROM)); -} - -static int -t128_available(void) -{ - return (rom_present(T128_ROM)); -} - -static int -corel_ls2000_available(void) -{ - return (rom_present(COREL_LS2000_ROM)); -} - -// clang-format off -static const device_config_t ncr5380_mmio_config[] = { - { - .name = "bios_addr", - .description = "BIOS Address", - .type = CONFIG_HEX20, - .default_string = "", - .default_int = 0xD8000, - .file_filter = "", - .spinner = { 0 }, - .selection = { - { .description = "C800H", .value = 0xc8000 }, - { .description = "CC00H", .value = 0xcc000 }, - { .description = "D000H", .value = 0xd0000 }, - { .description = "D400H", .value = 0xd4000 }, - { .description = "D800H", .value = 0xd8000 }, - { .description = "DC00H", .value = 0xdc000 }, - { .description = "" } - }, - }, - { - .name = "irq", - .description = "IRQ", - .type = CONFIG_SELECTION, - .default_string = "", - .default_int = 5, - .file_filter = "", - .spinner = { 0 }, - .selection = { - { .description = "IRQ 3", .value = 3 }, - { .description = "IRQ 5", .value = 5 }, - { .description = "IRQ 7", .value = 7 }, - { .description = "" } - }, - }, - { .name = "", .description = "", .type = CONFIG_END } -}; - -static const device_config_t rancho_config[] = { - { - .name = "bios_addr", - .description = "BIOS Address", - .type = CONFIG_HEX20, - .default_string = "", - .default_int = 0xD8000, - .file_filter = "", - .spinner = { 0 }, - .selection = { - { .description = "C800H", .value = 0xc8000 }, - { .description = "CC00H", .value = 0xcc000 }, - { .description = "D000H", .value = 0xd0000 }, - { .description = "D400H", .value = 0xd4000 }, - { .description = "D800H", .value = 0xd8000 }, - { .description = "DC00H", .value = 0xdc000 }, - { .description = "" } - }, - }, - { - .name = "irq", - .description = "IRQ", - .type = CONFIG_SELECTION, - .default_string = "", - .default_int = 5, - .file_filter = "", - .spinner = { 0 }, - .selection = { - { .description = "IRQ 3", .value = 3 }, - { .description = "IRQ 5", .value = 5 }, - { .description = "IRQ 7", .value = 7 }, - { .description = "" } - }, - }, - { - .name = "bios_ver", - .description = "BIOS Version", - .type = CONFIG_SELECTION, - .default_string = "", - .default_int = 1, - .file_filter = "", - .spinner = { 0 }, - .selection = { - { .description = "8.20R", .value = 1 }, - { .description = "8.10R", .value = 0 }, - { .description = "" } - }, - }, - { .name = "", .description = "", .type = CONFIG_END } -}; - -static const device_config_t rancho_mc_config[] = { - { - .name = "irq", - .description = "IRQ", - .type = CONFIG_SELECTION, - .default_string = "", - .default_int = 5, - .file_filter = "", - .spinner = { 0 }, - .selection = { - { .description = "IRQ 3", .value = 3 }, - { .description = "IRQ 5", .value = 5 }, - { .description = "IRQ 7", .value = 7 }, - { .description = "" } - }, - }, - { .name = "", .description = "", .type = CONFIG_END } -}; - -static const device_config_t t130b_config[] = { - { - .name = "bios_addr", - .description = "BIOS Address", - .type = CONFIG_HEX20, - .default_string = "", - .default_int = 0xD8000, - .file_filter = "", - .spinner = { 0 }, - .selection = { - { .description = "Disabled", .value = 0 }, - { .description = "C800H", .value = 0xc8000 }, - { .description = "CC00H", .value = 0xcc000 }, - { .description = "D800H", .value = 0xd8000 }, - { .description = "DC00H", .value = 0xdc000 }, - { .description = "" } - }, - }, - { - .name = "base", - .description = "Address", - .type = CONFIG_HEX16, - .default_string = "", - .default_int = 0x0350, - .file_filter = "", - .spinner = { 0 }, - .selection = { - { .description = "240H", .value = 0x0240 }, - { .description = "250H", .value = 0x0250 }, - { .description = "340H", .value = 0x0340 }, - { .description = "350H", .value = 0x0350 }, - { .description = "" } - }, - }, - { - .name = "irq", - .description = "IRQ", - .type = CONFIG_SELECTION, - .default_string = "", - .default_int = 5, - .file_filter = "", - .spinner = { 0 }, - .selection = { - { .description = "IRQ 3", .value = 3 }, - { .description = "IRQ 5", .value = 5 }, - { .description = "IRQ 7", .value = 7 }, - { .description = "" } - }, - }, - { .name = "", .description = "", .type = CONFIG_END } -}; - -static const device_config_t t128_config[] = { - { - .name = "bios_addr", - .description = "BIOS Address", - .type = CONFIG_HEX20, - .default_string = "", - .default_int = 0xD8000, - .file_filter = "", - .spinner = { 0 }, - .selection = { - { .description = "C800H", .value = 0xc8000 }, - { .description = "CC00H", .value = 0xcc000 }, - { .description = "D000H", .value = 0xd0000 }, - { .description = "D400H", .value = 0xd4000 }, - { .description = "D800H", .value = 0xd8000 }, - { .description = "DC00H", .value = 0xdc000 }, - { .description = "" } - }, - }, - { - .name = "irq", - .description = "IRQ", - .type = CONFIG_SELECTION, - .default_string = "", - .default_int = 5, - .file_filter = "", - .spinner ={ 0 }, - .selection = { - { .description = "IRQ 3", .value = 3 }, - { .description = "IRQ 5", .value = 5 }, - { .description = "IRQ 7", .value = 7 }, - { .description = "" } - }, - }, - { - .name = "boot", - .description = "Enable Boot ROM", - .type = CONFIG_BINARY, - .default_string = "", - .default_int = 1 - }, - { .name = "", .description = "", .type = CONFIG_END } -}; -// clang-format on - -const device_t scsi_lcs6821n_device = { - .name = "Longshine LCS-6821N", - .internal_name = "lcs6821n", - .flags = DEVICE_ISA, - .local = 0, - .init = ncr_init, - .close = ncr_close, - .reset = NULL, - { .available = lcs6821n_available }, - .speed_changed = NULL, - .force_redraw = NULL, - .config = ncr5380_mmio_config -}; - -const device_t scsi_rt1000b_device = { - .name = "Rancho RT1000B", - .internal_name = "rt1000b", - .flags = DEVICE_ISA, - .local = 1, - .init = ncr_init, - .close = ncr_close, - .reset = NULL, - { .available = rt1000b_available }, - .speed_changed = NULL, - .force_redraw = NULL, - .config = rancho_config -}; - -const device_t scsi_rt1000mc_device = { - .name = "Rancho RT1000B-MC", - .internal_name = "rt1000mc", - .flags = DEVICE_MCA, - .local = 1, - .init = ncr_init, - .close = ncr_close, - .reset = NULL, - { .available = rt1000b_820_available }, - .speed_changed = NULL, - .force_redraw = NULL, - .config = rancho_mc_config -}; - -const device_t scsi_t130b_device = { - .name = "Trantor T130B", - .internal_name = "t130b", - .flags = DEVICE_ISA, - .local = 2, - .init = ncr_init, - .close = ncr_close, - .reset = NULL, - { .available = t130b_available }, - .speed_changed = NULL, - .force_redraw = NULL, - .config = t130b_config -}; - -const device_t scsi_t128_device = { - .name = "Trantor T128", - .internal_name = "t128", - .flags = DEVICE_ISA, - .local = 3, - .init = ncr_init, - .close = ncr_close, - .reset = NULL, - { .available = t128_available }, - .speed_changed = NULL, - .force_redraw = NULL, - .config = t128_config -}; - -const device_t scsi_ls2000_device = { - .name = "Corel LS2000", - .internal_name = "ls2000", - .flags = DEVICE_ISA, - .local = 4, - .init = ncr_init, - .close = ncr_close, - .reset = NULL, - { .available = corel_ls2000_available }, - .speed_changed = NULL, - .force_redraw = NULL, - .config = ncr5380_mmio_config -}; diff --git a/src/scsi/scsi_ncr53c400.c b/src/scsi/scsi_ncr53c400.c new file mode 100644 index 000000000..d605f1bdb --- /dev/null +++ b/src/scsi/scsi_ncr53c400.c @@ -0,0 +1,998 @@ +/* + * 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 53c400 series of SCSI Host Adapters + * made by NCR. These controllers were designed for the ISA and MCA bus. + * + * + * + * Authors: Sarah Walker, + * TheCollector1995, + * Fred N. van Kempen, + * + * Copyright 2017-2019 Sarah Walker. + * Copyright 2017-2019 Fred N. van Kempen. + * Copyright 2017-2024 TheCollector1995. + */ +#include +#include +#include +#include +#include +#include +#include +#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> + +#define LCS6821N_ROM "roms/scsi/ncr5380/Longshine LCS-6821N - BIOS version 1.04.bin" +#define COREL_LS2000_ROM "roms/scsi/ncr5380/Corel LS2000 - BIOS ROM - Ver 1.65.bin" +#define RT1000B_810R_ROM "roms/scsi/ncr5380/Rancho_RT1000_RTBios_version_8.10R.bin" +#define RT1000B_820R_ROM "roms/scsi/ncr5380/RTBIOS82.ROM" +#define T130B_ROM "roms/scsi/ncr5380/trantor_t130b_bios_v2.14.bin" + +#define CTRL_DATA_DIR 0x40 +#define STATUS_BUFFER_NOT_READY 0x04 +#define STATUS_5380_ACCESSIBLE 0x80 + +enum { + ROM_LCS6821N = 0, + ROM_LS2000, + ROM_RT1000B, + ROM_T130B +}; + +typedef struct ncr53c400_t { + rom_t bios_rom; + mem_mapping_t mapping; + ncr_t ncr; + uint8_t buffer[128]; + uint8_t int_ram[0x40]; + uint8_t ext_ram[0x600]; + + uint32_t rom_addr; + uint16_t base; + + int8_t type; + uint8_t block_count; + uint8_t status_ctrl; + + int block_count_loaded; + + int buffer_pos; + int buffer_host_pos; + + int busy; + uint8_t pos_regs[8]; + + pc_timer_t timer; +} ncr53c400_t; + +#ifdef ENABLE_NCR53C400_LOG +int ncr53c400_do_log = ENABLE_NCR53C400_LOG; + +static void +ncr53c400_log(const char *fmt, ...) +{ + va_list ap; + + if (ncr53c400_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define ncr53c400_log(fmt, ...) +#endif + +/* Memory-mapped I/O WRITE handler. */ +static void +ncr53c400_write(uint32_t addr, uint8_t val, void *priv) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + ncr_t *ncr = &ncr400->ncr; + scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; + + addr &= 0x3fff; + + if (addr >= 0x3a00) + ncr400->ext_ram[addr - 0x3a00] = val; + else { + switch (addr & 0x3f80) { + case 0x3800: + ncr400->int_ram[addr & 0x3f] = val; + break; + + case 0x3880: + ncr5380_write(addr, val, ncr); + break; + + case 0x3900: + if (!(ncr400->status_ctrl & CTRL_DATA_DIR) && (ncr400->buffer_host_pos < MIN(128, dev->buffer_length))) { + ncr400->buffer[ncr400->buffer_host_pos++] = val; + + ncr53c400_log("Write host pos = %i, val = %02x\n", ncr400->buffer_host_pos, val); + + if (ncr400->buffer_host_pos == MIN(128, dev->buffer_length)) { + ncr400->status_ctrl |= STATUS_BUFFER_NOT_READY; + ncr400->busy = 1; + } + } + break; + + case 0x3980: + switch (addr) { + case 0x3980: /* Control */ + ncr53c400_log("NCR 53c400 control = %02x, mode = %02x.\n", val, ncr->mode); + if ((val & CTRL_DATA_DIR) && !(ncr400->status_ctrl & CTRL_DATA_DIR)) { + ncr400->buffer_host_pos = MIN(128, dev->buffer_length); + ncr400->status_ctrl |= STATUS_BUFFER_NOT_READY; + } else if (!(val & CTRL_DATA_DIR) && (ncr400->status_ctrl & CTRL_DATA_DIR)) { + ncr400->buffer_host_pos = 0; + ncr400->status_ctrl &= ~STATUS_BUFFER_NOT_READY; + } + ncr400->status_ctrl = (ncr400->status_ctrl & 0x87) | (val & 0x78); + break; + + case 0x3981: /* block counter register */ + ncr53c400_log("Write block counter register: val=%d, dma mode=%x, period=%lf\n", val, ncr->dma_mode, ncr->period); + ncr400->block_count = val; + ncr400->block_count_loaded = 1; + + if (ncr400->status_ctrl & CTRL_DATA_DIR) { + ncr400->buffer_host_pos = MIN(128, dev->buffer_length); + ncr400->status_ctrl |= STATUS_BUFFER_NOT_READY; + } else { + ncr400->buffer_host_pos = 0; + ncr400->status_ctrl &= ~STATUS_BUFFER_NOT_READY; + } + if ((ncr->mode & MODE_DMA) && !timer_is_on(&ncr400->timer) && (dev->buffer_length > 0)) { + memset(ncr400->buffer, 0, MIN(128, dev->buffer_length)); + ncr53c400_log("DMA timer on\n"); + timer_on_auto(&ncr400->timer, ncr->period); + } + break; + + default: + break; + } + break; + + default: + break; + } + } +} + +/* Memory-mapped I/O READ handler. */ +static uint8_t +ncr53c400_read(uint32_t addr, void *priv) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + ncr_t *ncr = &ncr400->ncr; + scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; + uint8_t ret = 0xff; + + addr &= 0x3fff; + + if (addr < 0x2000) + ret = ncr400->bios_rom.rom[addr & 0x1fff]; + else if (addr < 0x3800) + ret = 0xff; + else if (addr >= 0x3a00) + ret = ncr400->ext_ram[addr - 0x3a00]; + else { + switch (addr & 0x3f80) { + case 0x3800: + ncr53c400_log("Read intRAM %02x %02x\n", addr & 0x3f, ncr400->int_ram[addr & 0x3f]); + ret = ncr400->int_ram[addr & 0x3f]; + break; + + case 0x3880: + ncr53c400_log("Read 5380 %04x\n", addr); + ret = ncr5380_read(addr, ncr); + break; + + case 0x3900: + if (ncr400->buffer_host_pos >= MIN(128, dev->buffer_length) || (!(ncr400->status_ctrl & CTRL_DATA_DIR))) { + ret = 0xff; + ncr53c400_log("No Read.\n"); + } else { + ret = ncr400->buffer[ncr400->buffer_host_pos++]; + ncr53c400_log("Read host pos = %i, ret = %02x\n", ncr400->buffer_host_pos, ret); + + if (ncr400->buffer_host_pos == MIN(128, dev->buffer_length)) { + ncr400->status_ctrl |= STATUS_BUFFER_NOT_READY; + ncr53c400_log("Transfer busy read, status = %02x\n", ncr400->status_ctrl); + } + } + break; + + case 0x3980: + switch (addr) { + case 0x3980: /* status */ + ret = ncr400->status_ctrl; + ncr53c400_log("NCR status ctrl read=%02x\n", ncr400->status_ctrl & STATUS_BUFFER_NOT_READY); + if (!ncr400->busy) + ret |= STATUS_5380_ACCESSIBLE; + if (ncr->mode & 0x30) { /*Parity bits*/ + if (!(ncr->mode & MODE_DMA)) { /*This is to avoid RTBios 8.10R BIOS problems with the hard disk and detection.*/ + ret |= 0x01; /*If the parity bits are set, bit 0 of the 53c400 status port should be set as well.*/ + ncr->mode = 0; /*Required by RTASPI10.SYS otherwise it won't initialize.*/ + } + } + ncr53c400_log("NCR 53c400 status = %02x.\n", ret); + break; + + case 0x3981: /* block counter register*/ + ret = ncr400->block_count; + break; + + case 0x3982: /* switch register read */ + ret = 0xf8; + ret |= (ncr->irq & 0x07); + ncr53c400_log("Switches read=%02x.\n", ret); + break; + + default: + break; + } + break; + + default: + break; + } + } + + if (addr >= 0x3880) + ncr53c400_log("memio_read(%08x)=%02x\n", addr, ret); + + return ret; +} + + +/* Memory-mapped I/O WRITE handler for the Trantor T130B. */ +static void +t130b_write(uint32_t addr, uint8_t val, void *priv) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + + addr &= 0x3fff; + ncr53c400_log("MEM: Writing %02X to %08X\n", val, addr); + if (addr >= 0x1800 && addr < 0x1880) + ncr400->ext_ram[addr & 0x7f] = val; +} + +/* Memory-mapped I/O READ handler for the Trantor T130B. */ +static uint8_t +t130b_read(uint32_t addr, void *priv) +{ + const ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + uint8_t ret = 0xff; + + addr &= 0x3fff; + if (addr < 0x1800) + ret = ncr400->bios_rom.rom[addr & 0x1fff]; + else if (addr >= 0x1800 && addr < 0x1880) + ret = ncr400->ext_ram[addr & 0x7f]; + + ncr53c400_log("MEM: Reading %02X from %08X\n", ret, addr); + return ret; +} + +static void +t130b_out(uint16_t port, uint8_t val, void *priv) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + ncr_t *ncr = &ncr400->ncr; + + ncr53c400_log("I/O: Writing %02X to %04X\n", val, port); + + switch (port & 0x0f) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + ncr53c400_write((port & 7) | 0x3980, val, ncr400); + break; + + case 0x04: + case 0x05: + ncr53c400_write(0x3900, val, ncr400); + break; + + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + ncr5380_write(port, val, ncr); + break; + + default: + break; + } +} + +static uint8_t +t130b_in(uint16_t port, void *priv) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + ncr_t *ncr = &ncr400->ncr; + uint8_t ret = 0xff; + + switch (port & 0x0f) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + ret = ncr53c400_read((port & 7) | 0x3980, ncr400); + break; + + case 0x04: + case 0x05: + ret = ncr53c400_read(0x3900, ncr400); + break; + + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + ret = ncr5380_read(port, ncr); + break; + + default: + break; + } + + ncr53c400_log("I/O: Reading %02X from %04X\n", ret, port); + return ret; +} + +static void +ncr53c400_dma_mode_ext(void *priv, void *ext_priv) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) ext_priv; + ncr_t *ncr = (ncr_t *) priv; + + /*When a pseudo-DMA transfer has completed (Send or Initiator Receive), mark it as complete and idle the status*/ + if (!ncr400->block_count_loaded && !(ncr->mode & MODE_DMA)) { + ncr53c400_log("No DMA mode\n"); + ncr->tcr &= ~TCR_LAST_BYTE_SENT; + ncr->isr &= ~STATUS_END_OF_DMA; + ncr->dma_mode = DMA_IDLE; + } +} + +static void +ncr53c400_timer_on_auto(void *ext_priv, double period) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) ext_priv; + + ncr53c400_log("53c400: PERIOD=%lf.\n", period); + if (period == 0.0) + timer_stop(&ncr400->timer); + else + timer_on_auto(&ncr400->timer, period); +} + +static void +ncr53c400_callback(void *priv) +{ + ncr53c400_t *ncr400 = (void *) priv; + ncr_t *ncr = &ncr400->ncr; + scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; + int bus; + uint8_t c; + uint8_t temp; + + if (ncr->dma_mode != DMA_IDLE) + timer_on_auto(&ncr400->timer, 1.0); + + if (ncr->data_wait & 1) { + ncr->clear_req = 3; + ncr->data_wait &= ~1; + if (ncr->dma_mode == DMA_IDLE) { + timer_stop(&ncr400->timer); + return; + } + } + + if (ncr->dma_mode == DMA_IDLE) { + timer_stop(&ncr400->timer); + return; + } + + switch (ncr->dma_mode) { + case DMA_SEND: + if (ncr400->status_ctrl & CTRL_DATA_DIR) { + ncr53c400_log("DMA_SEND with DMA direction set wrong\n"); + break; + } + + if (!(ncr400->status_ctrl & STATUS_BUFFER_NOT_READY)) { + ncr53c400_log("Write buffer status ready\n"); + break; + } + + if (!ncr400->block_count_loaded) { + ncr53c400_log("Write block count not loaded.\n"); + break; + } + + while (1) { + for (c = 0; c < 10; c++) { + ncr5380_bus_read(ncr); + if (ncr->cur_bus & BUS_REQ) + break; + } + + /* Data ready. */ + temp = ncr400->buffer[ncr400->buffer_pos]; + + bus = ncr5380_get_bus_host(ncr) & ~BUS_DATAMASK; + bus |= BUS_SETDATA(temp); + + ncr5380_bus_update(ncr, bus | BUS_ACK); + ncr5380_bus_update(ncr, bus & ~BUS_ACK); + + ncr400->buffer_pos++; + ncr53c400_log("NCR 53c400 Buffer pos for writing = %d\n", ncr400->buffer_pos); + + if (ncr400->buffer_pos == MIN(128, dev->buffer_length)) { + ncr400->status_ctrl &= ~STATUS_BUFFER_NOT_READY; + ncr400->buffer_pos = 0; + ncr400->buffer_host_pos = 0; + ncr400->busy = 0; + ncr400->block_count = (ncr400->block_count - 1) & 0xff; + ncr53c400_log("NCR 53c400 Remaining blocks to be written=%d\n", ncr400->block_count); + if (!ncr400->block_count) { + ncr400->block_count_loaded = 0; + ncr53c400_log("IO End of write transfer\n"); + ncr->tcr |= TCR_LAST_BYTE_SENT; + ncr->isr |= STATUS_END_OF_DMA; + timer_stop(&ncr400->timer); + if (ncr->mode & MODE_ENA_EOP_INT) { + ncr53c400_log("NCR 53c400 write irq\n"); + ncr5380_irq(ncr, 1); + } + } + break; + } + } + break; + + case DMA_INITIATOR_RECEIVE: + if (!(ncr400->status_ctrl & CTRL_DATA_DIR)) { + ncr53c400_log("DMA_INITIATOR_RECEIVE with DMA direction set wrong\n"); + break; + } + + if (!(ncr400->status_ctrl & STATUS_BUFFER_NOT_READY)) { + ncr53c400_log("Read buffer status ready\n"); + break; + } + + if (!ncr400->block_count_loaded) + break; + + while (1) { + for (c = 0; c < 10; c++) { + ncr5380_bus_read(ncr); + if (ncr->cur_bus & BUS_REQ) + break; + } + + /* Data ready. */ + ncr5380_bus_read(ncr); + temp = BUS_GETDATA(ncr->cur_bus); + + bus = ncr5380_get_bus_host(ncr); + + ncr5380_bus_update(ncr, bus | BUS_ACK); + ncr5380_bus_update(ncr, bus & ~BUS_ACK); + + ncr400->buffer[ncr400->buffer_pos++] = temp; + ncr53c400_log("NCR 53c400 Buffer pos for reading = %d\n", ncr400->buffer_pos); + + if (ncr400->buffer_pos == MIN(128, dev->buffer_length)) { + ncr400->status_ctrl &= ~STATUS_BUFFER_NOT_READY; + ncr400->buffer_pos = 0; + ncr400->buffer_host_pos = 0; + ncr400->block_count = (ncr400->block_count - 1) & 0xff; + ncr53c400_log("NCR 53c400 Remaining blocks to be read=%d\n", ncr400->block_count); + if (!ncr400->block_count) { + ncr400->block_count_loaded = 0; + ncr53c400_log("IO End of read transfer\n"); + ncr->isr |= STATUS_END_OF_DMA; + timer_stop(&ncr400->timer); + if (ncr->mode & MODE_ENA_EOP_INT) { + ncr53c400_log("NCR read irq\n"); + ncr5380_irq(ncr, 1); + } + } + break; + } + } + break; + + default: + break; + } + + ncr5380_bus_read(ncr); + + if (!(ncr->cur_bus & BUS_BSY) && (ncr->mode & MODE_MONITOR_BUSY)) { + ncr53c400_log("Updating DMA\n"); + ncr->mode &= ~MODE_DMA; + ncr->dma_mode = DMA_IDLE; + } +} + +static uint8_t +rt1000b_mc_read(int port, void *priv) +{ + const ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + + return (ncr400->pos_regs[port & 7]); +} + +static void +rt1000b_mc_write(int port, uint8_t val, void *priv) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + + /* MCA does not write registers below 0x0100. */ + if (port < 0x0102) + return; + + mem_mapping_disable(&ncr400->bios_rom.mapping); + mem_mapping_disable(&ncr400->mapping); + + /* Save the MCA register value. */ + ncr400->pos_regs[port & 7] = val; + + if (ncr400->pos_regs[2] & 1) { + switch (ncr400->pos_regs[2] & 0xe0) { + case 0: + ncr400->rom_addr = 0xd4000; + break; + case 0x20: + ncr400->rom_addr = 0xd0000; + break; + case 0x40: + ncr400->rom_addr = 0xcc000; + break; + case 0x60: + ncr400->rom_addr = 0xc8000; + break; + case 0xc0: + ncr400->rom_addr = 0xdc000; + break; + case 0xe0: + ncr400->rom_addr = 0xd8000; + break; + + default: + break; + } + + mem_mapping_set_addr(&ncr400->bios_rom.mapping, ncr400->rom_addr, 0x4000); + mem_mapping_set_addr(&ncr400->mapping, ncr400->rom_addr, 0x4000); + } +} + +static uint8_t +rt1000b_mc_feedb(void *priv) +{ + const ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + + return ncr400->pos_regs[2] & 1; +} + +static void * +ncr53c400_init(const device_t *info) +{ + const char *bios_ver = NULL; + const char *fn; + ncr53c400_t *ncr400; + ncr_t *ncr; + + ncr400 = malloc(sizeof(ncr53c400_t)); + memset(ncr400, 0x00, sizeof(ncr53c400_t)); + ncr = &ncr400->ncr; + + ncr400->type = info->local; + + ncr->bus = scsi_get_bus(); + + switch (ncr400->type) { + case ROM_LCS6821N: /* Longshine LCS6821N */ + ncr400->rom_addr = device_get_config_hex20("bios_addr"); + ncr->irq = device_get_config_int("irq"); + + rom_init(&ncr400->bios_rom, LCS6821N_ROM, + ncr400->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); + + mem_mapping_add(&ncr400->mapping, ncr400->rom_addr, 0x4000, + ncr53c400_read, NULL, NULL, + ncr53c400_write, NULL, NULL, + ncr400->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr400); + break; + + + case ROM_LS2000: /* Corel LS2000 */ + ncr400->rom_addr = device_get_config_hex20("bios_addr"); + ncr->irq = device_get_config_int("irq"); + + rom_init(&ncr400->bios_rom, COREL_LS2000_ROM, + ncr400->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); + + mem_mapping_add(&ncr400->mapping, ncr400->rom_addr, 0x4000, + ncr53c400_read, NULL, NULL, + ncr53c400_write, NULL, NULL, + ncr400->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr400); + break; + + case ROM_RT1000B: /* Rancho RT1000B/MC */ + ncr400->rom_addr = device_get_config_hex20("bios_addr"); + ncr->irq = device_get_config_int("irq"); + if (info->flags & DEVICE_MCA) { + rom_init(&ncr400->bios_rom, RT1000B_820R_ROM, + 0xd8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); + mem_mapping_add(&ncr400->mapping, 0xd8000, 0x4000, + ncr53c400_read, NULL, NULL, + ncr53c400_write, NULL, NULL, + ncr400->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr400); + mem_mapping_disable(&ncr400->bios_rom.mapping); + ncr400->pos_regs[0] = 0x8d; + ncr400->pos_regs[1] = 0x70; + mca_add(rt1000b_mc_read, rt1000b_mc_write, rt1000b_mc_feedb, NULL, ncr400); + } else { + bios_ver = (char *) device_get_config_bios("bios_ver"); + fn = (char *) device_get_bios_file(info, bios_ver, 0); + rom_init(&ncr400->bios_rom, fn, + ncr400->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); + mem_mapping_add(&ncr400->mapping, ncr400->rom_addr, 0x4000, + ncr53c400_read, NULL, NULL, + ncr53c400_write, NULL, NULL, + ncr400->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr400); + } + break; + + case ROM_T130B: /* Trantor T130B */ + ncr400->rom_addr = device_get_config_hex20("bios_addr"); + ncr400->base = device_get_config_hex16("base"); + ncr->irq = device_get_config_int("irq"); + + if (ncr400->rom_addr > 0x00000) { + rom_init(&ncr400->bios_rom, T130B_ROM, + ncr400->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); + + mem_mapping_add(&ncr400->mapping, ncr400->rom_addr, 0x4000, + t130b_read, NULL, NULL, + t130b_write, NULL, NULL, + ncr400->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr400); + } + + io_sethandler(ncr400->base, 16, + t130b_in, NULL, NULL, t130b_out, NULL, NULL, ncr400); + break; + + default: + break; + } + + ncr->priv = ncr400; + ncr->dma_mode_ext = ncr53c400_dma_mode_ext; + ncr->dma_send_ext = NULL; + ncr->dma_initiator_receive_ext = NULL; + ncr->timer = ncr53c400_timer_on_auto; + ncr400->status_ctrl = STATUS_BUFFER_NOT_READY; + ncr400->buffer_host_pos = 128; + timer_add(&ncr400->timer, ncr53c400_callback, ncr400, 0); + + scsi_bus_set_speed(ncr->bus, 5000000.0); + + return ncr400; +} + +static void +ncr53c400_close(void *priv) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + + if (ncr400) { + /* Tell the timer to terminate. */ + timer_stop(&ncr400->timer); + + free(ncr400); + ncr400 = NULL; + } +} + +static int +lcs6821n_available(void) +{ + return (rom_present(LCS6821N_ROM)); +} + +static int +rt1000b_mc_available(void) +{ + return (rom_present(RT1000B_820R_ROM)); +} + +static int +t130b_available(void) +{ + return (rom_present(T130B_ROM)); +} + +static int +corel_ls2000_available(void) +{ + return (rom_present(COREL_LS2000_ROM)); +} + +// clang-format off +static const device_config_t ncr53c400_mmio_config[] = { + { + .name = "bios_addr", + .description = "BIOS Address", + .type = CONFIG_HEX20, + .default_string = "", + .default_int = 0xD8000, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { .description = "C800H", .value = 0xc8000 }, + { .description = "CC00H", .value = 0xcc000 }, + { .description = "D000H", .value = 0xd0000 }, + { .description = "D400H", .value = 0xd4000 }, + { .description = "D800H", .value = 0xd8000 }, + { .description = "DC00H", .value = 0xdc000 }, + { .description = "" } + }, + }, + { + .name = "irq", + .description = "IRQ", + .type = CONFIG_SELECTION, + .default_string = "", + .default_int = 5, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { .description = "None", .value = -1 }, + { .description = "IRQ 3", .value = 3 }, + { .description = "IRQ 5", .value = 5 }, + { .description = "IRQ 7", .value = 7 }, + { .description = "" } + }, + }, + { .name = "", .description = "", .type = CONFIG_END } +}; + +static const device_config_t rt1000b_config[] = { + { + .name = "bios_addr", + .description = "BIOS Address", + .type = CONFIG_HEX20, + .default_string = "", + .default_int = 0xD8000, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { .description = "C800H", .value = 0xc8000 }, + { .description = "CC00H", .value = 0xcc000 }, + { .description = "D000H", .value = 0xd0000 }, + { .description = "D400H", .value = 0xd4000 }, + { .description = "D800H", .value = 0xd8000 }, + { .description = "DC00H", .value = 0xdc000 }, + { .description = "" } + }, + }, + { + .name = "irq", + .description = "IRQ", + .type = CONFIG_SELECTION, + .default_string = "", + .default_int = 5, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { .description = "None", .value = -1 }, + { .description = "IRQ 3", .value = 3 }, + { .description = "IRQ 5", .value = 5 }, + { .description = "IRQ 7", .value = 7 }, + { .description = "" } + }, + }, + { + .name = "bios_ver", + .description = "BIOS Version", + .type = CONFIG_BIOS, + .default_string = "v8_10r", + .default_int = 0, + .file_filter = "", + .spinner = { 0 }, /*W1*/ + .bios = { + { .name = "Version 8.10R", .internal_name = "v8_10r", .bios_type = BIOS_NORMAL, + .files_no = 1, .local = 0, .size = 8192, .files = { RT1000B_810R_ROM, "" } }, + { .name = "Version 8.20R", .internal_name = "v8_20r", .bios_type = BIOS_NORMAL, + .files_no = 1, .local = 0, .size = 8192, .files = { RT1000B_820R_ROM, "" } }, + { .files_no = 0 } + }, + }, + { .name = "", .description = "", .type = CONFIG_END } +}; + +static const device_config_t rt1000b_mc_config[] = { + { + .name = "irq", + .description = "IRQ", + .type = CONFIG_SELECTION, + .default_string = "", + .default_int = 5, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { .description = "None", .value = -1 }, + { .description = "IRQ 3", .value = 3 }, + { .description = "IRQ 5", .value = 5 }, + { .description = "IRQ 7", .value = 7 }, + { .description = "" } + }, + }, + { .name = "", .description = "", .type = CONFIG_END } +}; + +static const device_config_t t130b_config[] = { + { + .name = "bios_addr", + .description = "BIOS Address", + .type = CONFIG_HEX20, + .default_string = "", + .default_int = 0xD8000, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { .description = "Disabled", .value = 0 }, + { .description = "C800H", .value = 0xc8000 }, + { .description = "CC00H", .value = 0xcc000 }, + { .description = "D800H", .value = 0xd8000 }, + { .description = "DC00H", .value = 0xdc000 }, + { .description = "" } + }, + }, + { + .name = "base", + .description = "Address", + .type = CONFIG_HEX16, + .default_string = "", + .default_int = 0x0350, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { .description = "240H", .value = 0x0240 }, + { .description = "250H", .value = 0x0250 }, + { .description = "340H", .value = 0x0340 }, + { .description = "350H", .value = 0x0350 }, + { .description = "" } + }, + }, + { + .name = "irq", + .description = "IRQ", + .type = CONFIG_SELECTION, + .default_string = "", + .default_int = 5, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { .description = "None", .value = -1 }, + { .description = "IRQ 3", .value = 3 }, + { .description = "IRQ 5", .value = 5 }, + { .description = "IRQ 7", .value = 7 }, + { .description = "" } + }, + }, + { .name = "", .description = "", .type = CONFIG_END } +}; +// clang-format on + +const device_t scsi_lcs6821n_device = { + .name = "Longshine LCS-6821N", + .internal_name = "lcs6821n", + .flags = DEVICE_ISA, + .local = ROM_LCS6821N, + .init = ncr53c400_init, + .close = ncr53c400_close, + .reset = NULL, + { .available = lcs6821n_available }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = ncr53c400_mmio_config +}; + +const device_t scsi_rt1000b_device = { + .name = "Rancho RT1000B", + .internal_name = "rt1000b", + .flags = DEVICE_ISA, + .local = ROM_RT1000B, + .init = ncr53c400_init, + .close = ncr53c400_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = rt1000b_config +}; + +const device_t scsi_rt1000mc_device = { + .name = "Rancho RT1000B-MC", + .internal_name = "rt1000mc", + .flags = DEVICE_MCA, + .local = ROM_RT1000B, + .init = ncr53c400_init, + .close = ncr53c400_close, + .reset = NULL, + { .available = rt1000b_mc_available }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = rt1000b_mc_config +}; + +const device_t scsi_t130b_device = { + .name = "Trantor T130B", + .internal_name = "t130b", + .flags = DEVICE_ISA, + .local = ROM_T130B, + .init = ncr53c400_init, + .close = ncr53c400_close, + .reset = NULL, + { .available = t130b_available }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = t130b_config +}; + +const device_t scsi_ls2000_device = { + .name = "Corel LS2000", + .internal_name = "ls2000", + .flags = DEVICE_ISA, + .local = ROM_LS2000, + .init = ncr53c400_init, + .close = ncr53c400_close, + .reset = NULL, + { .available = corel_ls2000_available }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = ncr53c400_mmio_config +}; diff --git a/src/scsi/scsi_t128.c b/src/scsi/scsi_t128.c new file mode 100644 index 000000000..b13511eac --- /dev/null +++ b/src/scsi/scsi_t128.c @@ -0,0 +1,631 @@ +/* + * 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 Trantor 128/228 series of SCSI Host Adapters + * made by Trantor. These controllers were designed for the ISA and MCA bus. + * + * + * + * Authors: Sarah Walker, + * TheCollector1995, + * Fred N. van Kempen, + * + * Copyright 2017-2019 Sarah Walker. + * Copyright 2017-2019 Fred N. van Kempen. + * Copyright 2017-2024 TheCollector1995. + */ +#include +#include +#include +#include +#include +#include +#include +#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> + +#define T128_ROM "roms/scsi/ncr5380/trantor_t128_bios_v1.12.bin" + +typedef struct t128_t { + ncr_t ncr; + rom_t bios_rom; + mem_mapping_t mapping; + + uint8_t ctrl; + uint8_t status; + uint8_t buffer[512]; + uint8_t ext_ram[0x80]; + uint8_t block_count; + + int block_loaded; + int pos, host_pos; + + uint32_t rom_addr; + + int bios_enabled; + uint8_t pos_regs[8]; + + pc_timer_t timer; +} t128_t; + +#ifdef ENABLE_T128_LOG +int t128_do_log = ENABLE_T128_LOG; + +static void +t128_log(const char *fmt, ...) +{ + va_list ap; + + if (t128_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define t128_log(fmt, ...) +#endif + +/* Memory-mapped I/O WRITE handler. */ +static void +t128_write(uint32_t addr, uint8_t val, void *priv) +{ + t128_t *t128 = (t128_t *) priv; + ncr_t *ncr = &t128->ncr; + const scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; + + addr &= 0x3fff; + if ((addr >= 0x1800) && (addr < 0x1880)) + t128->ext_ram[addr & 0x7f] = val; + else if ((addr >= 0x1c00) && (addr < 0x1c20)) { + if ((val & 0x02) && !(t128->ctrl & 0x02)) + t128->status |= 0x02; + + t128->ctrl = val; + t128_log("T128 ctrl write=%02x\n", val); + } else if ((addr >= 0x1d00) && (addr < 0x1e00)) + ncr5380_write((addr - 0x1d00) >> 5, val, ncr); + else if ((addr >= 0x1e00) && (addr < 0x2000)) { + if ((t128->host_pos < MIN(512, dev->buffer_length)) && + (ncr->dma_mode == DMA_SEND)) { + t128->buffer[t128->host_pos] = val; + t128->host_pos++; + + t128_log("T128 Write transfer, addr = %i, pos = %i, val = %02x\n", + addr & 0x1ff, t128->host_pos, val); + + if (t128->host_pos == MIN(512, dev->buffer_length)) { + t128->status &= ~0x04; + t128_log("Transfer busy write, status = %02x\n", t128->status); + timer_on_auto(&t128->timer, 0.02); + } + } + } +} + +/* Memory-mapped I/O READ handler. */ +static uint8_t +t128_read(uint32_t addr, void *priv) +{ + t128_t *t128 = (t128_t *) priv; + ncr_t *ncr = &t128->ncr; + scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; + uint8_t ret = 0xff; + + addr &= 0x3fff; + if (t128->bios_enabled && (addr >= 0) && (addr < 0x1800)) + ret = t128->bios_rom.rom[addr & 0x1fff]; + else if ((addr >= 0x1800) && (addr < 0x1880)) + ret = t128->ext_ram[addr & 0x7f]; + else if ((addr >= 0x1c00) && (addr < 0x1c20)) { + ret = t128->ctrl; + t128_log("T128 ctrl read=%02x, dma=%02x\n", ret, ncr->mode & MODE_DMA); + } else if ((addr >= 0x1c20) && (addr < 0x1c40)) { + ret = t128->status; + t128_log("T128 status read=%02x, dma=%02x\n", ret, ncr->mode & MODE_DMA); + } else if ((addr >= 0x1d00) && (addr < 0x1e00)) + ret = ncr5380_read((addr - 0x1d00) >> 5, ncr); + else if (addr >= 0x1e00 && addr < 0x2000) { + if ((t128->host_pos >= MIN(512, dev->buffer_length)) || + (ncr->dma_mode != DMA_INITIATOR_RECEIVE)) + ret = 0xff; + else { + ret = t128->buffer[t128->host_pos++]; + + t128_log("T128 Read transfer, addr = %i, pos = %i\n", addr & 0x1ff, + t128->host_pos); + + if (t128->host_pos == MIN(512, dev->buffer_length)) { + t128->status &= ~0x04; + t128_log("T128 Transfer busy read, status = %02x, period = %lf\n", + t128->status, ncr->period); + if ((ncr->period == 0.2) || (ncr->period == 0.02)) + timer_on_auto(&t128->timer, 40.2); + } else if ((t128->host_pos < MIN(512, dev->buffer_length)) && + (scsi_device_get_callback(dev) > 100.0)) + cycles += 100; /*Needed to avoid timer de-syncing with transfers.*/ + } + } + + return ret; +} + +static void +t128_dma_mode_ext(void *priv, void *ext_priv) +{ + t128_t *t128 = (t128_t *) ext_priv; + ncr_t *ncr = (ncr_t *) priv; + + /*Don't stop the timer until it finishes the transfer*/ + if (t128->block_loaded && (ncr->mode & MODE_DMA)) { + t128_log("Continuing DMA mode\n"); + timer_on_auto(&t128->timer, ncr->period + 1.0); + } + + /*When a pseudo-DMA transfer has completed (Send or Initiator Receive), mark it as complete and idle the status*/ + if (!t128->block_loaded && !(ncr->mode & MODE_DMA)) { + t128_log("No DMA mode\n"); + ncr->tcr &= ~TCR_LAST_BYTE_SENT; + ncr->isr &= ~STATUS_END_OF_DMA; + ncr->dma_mode = DMA_IDLE; + } +} + +static int +t128_dma_send_ext(void *priv, void *ext_priv) +{ + t128_t *t128 = (t128_t *) ext_priv; + ncr_t *ncr = (ncr_t *) priv; + scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; + + if ((ncr->mode & MODE_DMA) && !timer_is_on(&t128->timer) && (dev->buffer_length > 0)) { + memset(t128->buffer, 0, MIN(512, dev->buffer_length)); + t128->status |= 0x04; + t128->host_pos = 0; + t128->block_count = dev->buffer_length >> 9; + + if (dev->buffer_length < 512) + t128->block_count = 1; + + t128->block_loaded = 1; + } + return 1; +} + +static int +t128_dma_initiator_receive_ext(void *priv, void *ext_priv) +{ + t128_t *t128 = (t128_t *) ext_priv; + ncr_t *ncr = (ncr_t *) priv; + scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; + + if ((ncr->mode & MODE_DMA) && !timer_is_on(&t128->timer) && (dev->buffer_length > 0)) { + memset(t128->buffer, 0, MIN(512, dev->buffer_length)); + t128->status |= 0x04; + t128->host_pos = MIN(512, dev->buffer_length); + t128->block_count = dev->buffer_length >> 9; + + if (dev->buffer_length < 512) + t128->block_count = 1; + + t128->block_loaded = 1; + timer_on_auto(&t128->timer, 0.02); + } + return 1; +} + +static void +t128_timer_on_auto(void *ext_priv, double period) +{ + t128_t *t128 = (t128_t *) ext_priv; + + if (period == 0.0) + timer_stop(&t128->timer); + else + timer_on_auto(&t128->timer, period); +} + +static void +t128_callback(void *priv) +{ + t128_t *t128 = (void *) priv; + ncr_t *ncr = &t128->ncr; + scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; + int bus; + uint8_t c; + uint8_t temp; + + if ((ncr->dma_mode != DMA_IDLE) && (ncr->mode & MODE_DMA) && t128->block_loaded) { + if ((t128->host_pos == MIN(512, dev->buffer_length)) && t128->block_count) + t128->status |= 0x04; + + timer_on_auto(&t128->timer, ncr->period / 55.0); + } + + if (ncr->data_wait & 1) { + ncr->clear_req = 3; + ncr->data_wait &= ~1; + if (ncr->dma_mode == DMA_IDLE) + return; + } + + switch (ncr->dma_mode) { + case DMA_SEND: + if (!(t128->status & 0x04)) { + t128_log("Write status busy, block count = %i, host pos = %i\n", t128->block_count, t128->host_pos); + break; + } + + if (!t128->block_loaded) { + t128_log("Write block not loaded\n"); + break; + } + + if (t128->host_pos < MIN(512, dev->buffer_length)) + break; + +write_again: + for (c = 0; c < 10; c++) { + ncr5380_bus_read(ncr); + if (ncr->cur_bus & BUS_REQ) + break; + } + + /* Data ready. */ + temp = t128->buffer[t128->pos]; + + bus = ncr5380_get_bus_host(ncr) & ~BUS_DATAMASK; + bus |= BUS_SETDATA(temp); + + ncr5380_bus_update(ncr, bus | BUS_ACK); + ncr5380_bus_update(ncr, bus & ~BUS_ACK); + + t128->pos++; + t128_log("T128 Buffer pos for writing = %d\n", t128->pos); + + if (t128->pos == MIN(512, dev->buffer_length)) { + t128->pos = 0; + t128->host_pos = 0; + t128->status &= ~0x02; + t128->block_count = (t128->block_count - 1) & 0xff; + t128_log("T128 Remaining blocks to be written=%d\n", t128->block_count); + if (!t128->block_count) { + t128->block_loaded = 0; + t128_log("IO End of write transfer\n"); + ncr->tcr |= TCR_LAST_BYTE_SENT; + ncr->isr |= STATUS_END_OF_DMA; + timer_stop(&t128->timer); + if (ncr->mode & MODE_ENA_EOP_INT) { + t128_log("T128 write irq\n"); + ncr5380_irq(ncr, 1); + } + } + break; + } else + goto write_again; + break; + + case DMA_INITIATOR_RECEIVE: + if (!(t128->status & 0x04)) { + t128_log("Read status busy, block count = %i, host pos = %i\n", t128->block_count, t128->host_pos); + break; + } + + if (!t128->block_loaded) { + t128_log("Read block not loaded\n"); + break; + } + + if (t128->host_pos < MIN(512, dev->buffer_length)) + break; + +read_again: + for (c = 0; c < 10; c++) { + ncr5380_bus_read(ncr); + if (ncr->cur_bus & BUS_REQ) + break; + } + + /* Data ready. */ + ncr5380_bus_read(ncr); + temp = BUS_GETDATA(ncr->cur_bus); + + bus = ncr5380_get_bus_host(ncr); + + ncr5380_bus_update(ncr, bus | BUS_ACK); + ncr5380_bus_update(ncr, bus & ~BUS_ACK); + + t128->buffer[t128->pos++] = temp; + t128_log("T128 Buffer pos for reading=%d, temp=%02x, len=%d.\n", t128->pos, temp, dev->buffer_length); + + if (t128->pos == MIN(512, dev->buffer_length)) { + t128->pos = 0; + t128->host_pos = 0; + t128->status &= ~0x02; + t128->block_count = (t128->block_count - 1) & 0xff; + t128_log("T128 Remaining blocks to be read=%d, status=%02x, len=%i, cdb[0] = %02x\n", t128->block_count, t128->status, dev->buffer_length, ncr->command[0]); + if (!t128->block_count) { + t128->block_loaded = 0; + t128_log("IO End of read transfer\n"); + ncr->isr |= STATUS_END_OF_DMA; + timer_stop(&t128->timer); + if (ncr->mode & MODE_ENA_EOP_INT) { + t128_log("NCR read irq\n"); + ncr5380_irq(ncr, 1); + } + } + break; + } else + goto read_again; + break; + + default: + break; + } + + ncr5380_bus_read(ncr); + + if (!(ncr->cur_bus & BUS_BSY) && (ncr->mode & MODE_MONITOR_BUSY)) { + t128_log("Updating DMA\n"); + ncr->mode &= ~MODE_DMA; + ncr->dma_mode = DMA_IDLE; + timer_on_auto(&t128->timer, 10.0); + } +} + +static uint8_t +t228_read(int port, void *priv) +{ + const t128_t *t128 = (t128_t *) priv; + + return (t128->pos_regs[port & 7]); +} + +static void +t228_write(int port, uint8_t val, void *priv) +{ + t128_t *t128 = (t128_t *) priv; + ncr_t *ncr = &t128->ncr; + + /* MCA does not write registers below 0x0100. */ + if (port < 0x0102) + return; + + mem_mapping_disable(&t128->bios_rom.mapping); + mem_mapping_disable(&t128->mapping); + + /* Save the MCA register value. */ + t128->pos_regs[port & 7] = val; + + if (t128->pos_regs[2] & 1) { + switch (t128->pos_regs[2] & 6) { + case 0: + t128->rom_addr = 0xcc000; + break; + case 2: + t128->rom_addr = 0xc8000; + break; + case 4: + t128->rom_addr = 0xdc000; + break; + case 6: + t128->rom_addr = 0xd8000; + break; + + default: + break; + } + + t128->bios_enabled = !(t128->pos_regs[2] & 8); + + switch (t128->pos_regs[2] & 0x70) { + case 0: + ncr->irq = -1; + break; + case 0x10: + ncr->irq = 3; + break; + case 0x20: + ncr->irq = 5; + break; + case 0x30: + ncr->irq = 7; + break; + case 0x40: + ncr->irq = 10; + break; + case 0x50: + ncr->irq = 12; + break; + case 0x60: + ncr->irq = 14; + break; + case 0x70: + ncr->irq = 15; + break; + + default: + break; + } + + if (t128->bios_enabled) { + t128->status &= ~0x80; + mem_mapping_set_addr(&t128->bios_rom.mapping, t128->rom_addr, 0x4000); + mem_mapping_set_addr(&t128->mapping, t128->rom_addr, 0x4000); + } else + t128->status |= 0x80; + } +} + +static uint8_t +t228_feedb(void *priv) +{ + const t128_t *t128 = (t128_t *) priv; + + return t128->pos_regs[2] & 1; +} + +static void * +t128_init(const device_t *info) +{ + t128_t *t128; + ncr_t *ncr; + + t128 = malloc(sizeof(t128_t)); + memset(t128, 0x00, sizeof(t128_t)); + ncr = &t128->ncr; + + ncr->bus = scsi_get_bus(); + + if (info->flags & DEVICE_MCA) { + rom_init(&t128->bios_rom, T128_ROM, + 0xd8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); + mem_mapping_add(&t128->mapping, 0xd8000, 0x4000, + t128_read, NULL, NULL, + t128_write, NULL, NULL, + t128->bios_rom.rom, MEM_MAPPING_EXTERNAL, t128); + mem_mapping_disable(&t128->bios_rom.mapping); + t128->pos_regs[0] = 0x8c; + t128->pos_regs[1] = 0x50; + mca_add(t228_read, t228_write, t228_feedb, NULL, t128); + } else { + ncr->irq = device_get_config_int("irq"); + t128->rom_addr = device_get_config_hex20("bios_addr"); + t128->bios_enabled = device_get_config_int("boot"); + if (t128->bios_enabled) + rom_init(&t128->bios_rom, T128_ROM, + t128->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); + + mem_mapping_add(&t128->mapping, t128->rom_addr, 0x4000, + t128_read, NULL, NULL, + t128_write, NULL, NULL, + t128->bios_rom.rom, MEM_MAPPING_EXTERNAL, t128); + } + + ncr->priv = t128; + ncr->dma_mode_ext = t128_dma_mode_ext; + ncr->dma_send_ext = t128_dma_send_ext; + ncr->dma_initiator_receive_ext = t128_dma_initiator_receive_ext; + ncr->timer = t128_timer_on_auto; + t128->status = 0x04; + t128->host_pos = 512; + if (!t128->bios_enabled && !(info->flags & DEVICE_MCA)) + t128->status |= 0x80; + + timer_add(&t128->timer, t128_callback, t128, 0); + + scsi_bus_set_speed(ncr->bus, 5000000.0); + + return t128; +} + +static void +t128_close(void *priv) +{ + t128_t *t128 = (t128_t *) priv; + + if (t128) { + /* Tell the timer to terminate. */ + timer_stop(&t128->timer); + + free(t128); + t128 = NULL; + } +} + +static int +t128_available(void) +{ + return (rom_present(T128_ROM)); +} + +// clang-format off +static const device_config_t t128_config[] = { + { + .name = "bios_addr", + .description = "BIOS Address", + .type = CONFIG_HEX20, + .default_string = "", + .default_int = 0xD8000, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { .description = "C800H", .value = 0xc8000 }, + { .description = "CC00H", .value = 0xcc000 }, + { .description = "D800H", .value = 0xd8000 }, + { .description = "DC00H", .value = 0xdc000 }, + { .description = "" } + }, + }, + { + .name = "irq", + .description = "IRQ", + .type = CONFIG_SELECTION, + .default_string = "", + .default_int = 5, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { .description = "None", .value = -1 }, + { .description = "IRQ 3", .value = 3 }, + { .description = "IRQ 5", .value = 5 }, + { .description = "IRQ 7", .value = 7 }, + { .description = "" } + }, + }, + { + .name = "boot", + .description = "Enable Boot ROM", + .type = CONFIG_BINARY, + .default_string = "", + .default_int = 1 + }, + { .name = "", .description = "", .type = CONFIG_END } +}; +// clang-format on + +const device_t scsi_t128_device = { + .name = "Trantor T128", + .internal_name = "t128", + .flags = DEVICE_ISA, + .local = 0, + .init = t128_init, + .close = t128_close, + .reset = NULL, + { .available = t128_available }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = t128_config +}; + +const device_t scsi_t228_device = { + .name = "Trantor T228", + .internal_name = "t228", + .flags = DEVICE_MCA, + .local = 0, + .init = t128_init, + .close = t128_close, + .reset = NULL, + { .available = t128_available }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +};