Merge pull request #4346 from 86Box/tc1995

SCSI CD-ROM and 5380 fixes.
This commit is contained in:
Miran Grča
2024-04-10 17:29:21 +02:00
committed by GitHub
8 changed files with 1865 additions and 1484 deletions

View File

@@ -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

View File

@@ -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, <decwiz@yahoo.com>
*
* 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*/

View File

@@ -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)

View File

@@ -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, },

View File

@@ -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;
}

File diff suppressed because it is too large Load Diff

998
src/scsi/scsi_ncr53c400.c Normal file
View File

@@ -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, <https://pcem-emulator.co.uk/>
* TheCollector1995, <mariogplayer@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2017-2019 Sarah Walker.
* Copyright 2017-2019 Fred N. van Kempen.
* Copyright 2017-2024 TheCollector1995.
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <wchar.h>
#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
};

631
src/scsi/scsi_t128.c Normal file
View File

@@ -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, <https://pcem-emulator.co.uk/>
* TheCollector1995, <mariogplayer@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2017-2019 Sarah Walker.
* Copyright 2017-2019 Fred N. van Kempen.
* Copyright 2017-2024 TheCollector1995.
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <wchar.h>
#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
};