Upon an initial POST, the initital status/control 53c400 port should be 0, fixes various versions of the T130B driver on Windows 95 builds.
1079 lines
36 KiB
C
1079 lines
36 KiB
C
/*
|
|
* 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/dma.h>
|
|
#include <86box/pic.h>
|
|
#include <86box/mca.h>
|
|
#include <86box/mem.h>
|
|
#include <86box/timer.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>
|
|
#include "cpu.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;
|
|
int reset;
|
|
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
|
|
|
|
static void
|
|
ncr53c400_timer_on_auto(void *ext_priv, double period)
|
|
{
|
|
ncr53c400_t *ncr400 = (ncr53c400_t *) ext_priv;
|
|
|
|
ncr53c400_log("53c400: PERIOD=%lf, timer=%x.\n", period, timer_is_enabled(&ncr400->timer));
|
|
if (period <= 0.0)
|
|
timer_stop(&ncr400->timer);
|
|
else if ((period > 0.0) && !timer_is_enabled(&ncr400->timer))
|
|
timer_on_auto(&ncr400->timer, period);
|
|
}
|
|
|
|
/* 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_bus_t *scsi_bus = &ncr->scsibus;
|
|
scsi_device_t *dev = &scsi_devices[ncr->bus][scsi_bus->target_id];
|
|
|
|
addr &= 0x3fff;
|
|
|
|
if (addr >= 0x3880)
|
|
ncr53c400_log("%04X:%08X: memio_write(%04x)=%02x\n", CS, cpu_state.pc, addr, val);
|
|
|
|
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;
|
|
if (ncr400->type != ROM_T130B)
|
|
timer_on_auto(&ncr400->timer, 1.0);
|
|
}
|
|
} else
|
|
ncr53c400_log("No Write.\n");
|
|
break;
|
|
|
|
case 0x3980:
|
|
switch (addr) {
|
|
case 0x3980: /* Control */
|
|
/*Parity bits*/
|
|
/*This is to avoid RTBios 8.10R BIOS problems with the hard disk and detection.*/
|
|
/*If the parity bits are set, bit 0 of the 53c400 status port should be set as well.*/
|
|
/*Required by RTASPI10.SYS otherwise it won't initialize.*/
|
|
if (val & 0x80) {
|
|
if (ncr->mode & 0x30) {
|
|
if (!(ncr->mode & MODE_DMA)) {
|
|
ncr->mode = 0x00;
|
|
ncr400->reset = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
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, scsi_bus->tx_mode, scsi_bus->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) && (dev->buffer_length > 0)) {
|
|
memset(ncr400->buffer, 0, MIN(128, dev->buffer_length));
|
|
timer_on_auto(&ncr400->timer, 10.0);
|
|
ncr53c400_log("DMA timer on=%02x, callback=%lf, scsi buflen=%d, waitdata=%d, waitcomplete=%d, clearreq=%d, p=%lf enabled=%d.\n",
|
|
ncr->mode & MODE_MONITOR_BUSY, scsi_device_get_callback(dev), dev->buffer_length, scsi_bus->wait_data, scsi_bus->wait_complete, scsi_bus->clear_req, scsi_bus->period, timer_is_enabled(&ncr400->timer));
|
|
} else
|
|
ncr53c400_log("No Timer.\n");
|
|
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_bus_t *scsi_bus = &ncr->scsibus;
|
|
scsi_device_t *dev = &scsi_devices[ncr->bus][scsi_bus->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;
|
|
if (ncr400->type != ROM_T130B) {
|
|
if (!ncr400->block_count_loaded) {
|
|
scsi_bus->tx_mode = PIO_TX_BUS;
|
|
ncr53c400_log("IO End of read transfer\n");
|
|
ncr->isr |= STATUS_END_OF_DMA;
|
|
if (ncr->mode & MODE_ENA_EOP_INT) {
|
|
ncr53c400_log("NCR read irq\n");
|
|
ncr5380_irq(ncr, 1);
|
|
}
|
|
} else if (!timer_is_enabled(&ncr400->timer)) {
|
|
ncr53c400_log("Timer re-enabled.\n");
|
|
timer_on_auto(&ncr400->timer, 1.0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 (ncr400->reset) {
|
|
ncr400->reset = 0;
|
|
ret |= 0x01;
|
|
}
|
|
ncr53c400_log("NCR 53c400 status=%02x.\n", ret);
|
|
break;
|
|
|
|
case 0x3981: /* block counter register*/
|
|
ret = ncr400->block_count;
|
|
ncr53c400_log("NCR 53c400 block count read=%02x.\n", ret);
|
|
break;
|
|
|
|
case 0x3982: /* switch register read */
|
|
if (ncr->irq != -1) {
|
|
ret = 0xf8;
|
|
ret += ncr->irq;
|
|
}
|
|
ncr53c400_log("Switches read=%02x.\n", ret);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (addr >= 0x3880)
|
|
ncr53c400_log("%04X:%08X: memio_read(%04x)=%02x\n", CS, cpu_state.pc, 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, uint8_t val)
|
|
{
|
|
ncr53c400_t *ncr400 = (ncr53c400_t *) ext_priv;
|
|
ncr_t *ncr = (ncr_t *) priv;
|
|
scsi_bus_t *scsi_bus = &ncr->scsibus;
|
|
|
|
/*When a pseudo-DMA transfer has completed (Send or Initiator Receive), mark it as complete and idle the status*/
|
|
ncr53c400_log("NCR 53c400: Loaded?=%d, DMA mode enabled=%02x, valDMA=%02x.\n", ncr400->block_count_loaded, ncr->mode & MODE_DMA, val & MODE_DMA);
|
|
if (!ncr400->block_count_loaded) {
|
|
if (!(val & MODE_DMA)) {
|
|
ncr->tcr &= ~TCR_LAST_BYTE_SENT;
|
|
ncr->isr &= ~STATUS_END_OF_DMA;
|
|
scsi_bus->tx_mode = PIO_TX_BUS;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
ncr53c400_callback(void *priv)
|
|
{
|
|
ncr53c400_t *ncr400 = (ncr53c400_t *) priv;
|
|
ncr_t *ncr = &ncr400->ncr;
|
|
scsi_bus_t *scsi_bus = &ncr->scsibus;
|
|
scsi_device_t *dev = &scsi_devices[ncr->bus][scsi_bus->target_id];
|
|
int bus;
|
|
uint8_t c;
|
|
uint8_t temp;
|
|
uint8_t status;
|
|
|
|
if (scsi_bus->tx_mode != PIO_TX_BUS) {
|
|
ncr53c400_log("PERIOD T130B DMA=%lf.\n", scsi_bus->period / 225.0);
|
|
timer_on_auto(&ncr400->timer, scsi_bus->period / 225.0);
|
|
}
|
|
|
|
if (scsi_bus->data_wait & 1) {
|
|
scsi_bus->clear_req = 3;
|
|
scsi_bus->data_wait &= ~1;
|
|
}
|
|
|
|
if (scsi_bus->tx_mode == PIO_TX_BUS) {
|
|
ncr53c400_log("Timer CMD=%02x.\n", scsi_bus->command[0]);
|
|
return;
|
|
}
|
|
|
|
switch (scsi_bus->tx_mode) {
|
|
case DMA_OUT_TX_BUS:
|
|
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++) {
|
|
status = scsi_bus_read(scsi_bus);
|
|
if (status & BUS_REQ)
|
|
break;
|
|
}
|
|
/* Data ready. */
|
|
temp = ncr400->buffer[ncr400->buffer_pos];
|
|
|
|
bus = ncr5380_get_bus_host(ncr) & ~BUS_DATAMASK;
|
|
bus |= BUS_SETDATA(temp);
|
|
|
|
scsi_bus_update(scsi_bus, bus | BUS_ACK);
|
|
scsi_bus_update(scsi_bus, 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) {
|
|
scsi_bus->tx_mode = PIO_TX_BUS;
|
|
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;
|
|
if (ncr->mode & MODE_ENA_EOP_INT) {
|
|
ncr53c400_log("NCR 53c400 write irq\n");
|
|
ncr5380_irq(ncr, 1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DMA_IN_TX_BUS:
|
|
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++) {
|
|
status = scsi_bus_read(scsi_bus);
|
|
if (status & BUS_REQ)
|
|
break;
|
|
}
|
|
/* Data ready. */
|
|
bus = scsi_bus_read(scsi_bus);
|
|
temp = BUS_GETDATA(bus);
|
|
|
|
bus = ncr5380_get_bus_host(ncr);
|
|
|
|
scsi_bus_update(scsi_bus, bus | BUS_ACK);
|
|
scsi_bus_update(scsi_bus, 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;
|
|
if (ncr400->type == ROM_T130B) {
|
|
scsi_bus->tx_mode = PIO_TX_BUS;
|
|
ncr53c400_log("IO End of read transfer\n");
|
|
ncr->isr |= STATUS_END_OF_DMA;
|
|
if (ncr->mode & MODE_ENA_EOP_INT) {
|
|
ncr53c400_log("NCR read irq\n");
|
|
ncr5380_irq(ncr, 1);
|
|
}
|
|
} else
|
|
timer_on_auto(&ncr400->timer, 1.0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
status = scsi_bus_read(scsi_bus);
|
|
|
|
if (!(status & BUS_BSY) && (ncr->mode & MODE_MONITOR_BUSY)) {
|
|
ncr53c400_log("Updating DMA\n");
|
|
ncr->mode &= ~MODE_DMA;
|
|
scsi_bus->tx_mode = PIO_TX_BUS;
|
|
ncr400->block_count_loaded = 0;
|
|
}
|
|
}
|
|
|
|
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 = calloc(1, sizeof(ncr53c400_t));
|
|
ncr_t *ncr = &ncr400->ncr;
|
|
scsi_bus_t *scsi_bus;
|
|
|
|
ncr400->type = info->local;
|
|
|
|
ncr->bus = scsi_get_bus();
|
|
scsi_bus = &ncr->scsibus;
|
|
|
|
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;
|
|
scsi_bus->bus_device = ncr->bus;
|
|
scsi_bus->timer = ncr->timer;
|
|
scsi_bus->priv = ncr->priv;
|
|
ncr400->status_ctrl = 0x00;
|
|
ncr400->buffer_host_pos = 128;
|
|
timer_add(&ncr400->timer, ncr53c400_callback, ncr400, 0);
|
|
|
|
scsi_bus_set_speed(ncr->bus, 5000000.0);
|
|
scsi_bus->speed = 0.2;
|
|
if (ncr400->type == ROM_T130B) {
|
|
scsi_bus->divider = 2.0;
|
|
scsi_bus->multi = 1.750;
|
|
} else {
|
|
scsi_bus->divider = 1.0;
|
|
scsi_bus->multi = 1.0;
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
scsi_device_reset(&scsi_devices[ncr->bus][i]);
|
|
|
|
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 = NULL,
|
|
.default_int = 0xD8000,
|
|
.file_filter = NULL,
|
|
.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 = "" }
|
|
},
|
|
.bios = { { 0 } }
|
|
},
|
|
{
|
|
.name = "irq",
|
|
.description = "IRQ",
|
|
.type = CONFIG_SELECTION,
|
|
.default_string = NULL,
|
|
.default_int = 5,
|
|
.file_filter = NULL,
|
|
.spinner = { 0 },
|
|
.selection = {
|
|
{ .description = "None", .value = -1 },
|
|
{ .description = "IRQ 3", .value = 3 },
|
|
{ .description = "IRQ 5", .value = 5 },
|
|
{ .description = "IRQ 7", .value = 7 },
|
|
{ .description = "" }
|
|
},
|
|
.bios = { { 0 } }
|
|
},
|
|
{ .name = "", .description = "", .type = CONFIG_END }
|
|
};
|
|
|
|
static const device_config_t rt1000b_config[] = {
|
|
{
|
|
.name = "bios_ver",
|
|
.description = "BIOS Revision",
|
|
.type = CONFIG_BIOS,
|
|
.default_string = "v8_10r",
|
|
.default_int = 0,
|
|
.file_filter = NULL,
|
|
.spinner = { 0 },
|
|
.selection = { { 0 } },
|
|
.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 = "bios_addr",
|
|
.description = "BIOS Address",
|
|
.type = CONFIG_HEX20,
|
|
.default_string = NULL,
|
|
.default_int = 0xD8000,
|
|
.file_filter = NULL,
|
|
.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 = "" }
|
|
},
|
|
.bios = { { 0 } }
|
|
},
|
|
{
|
|
.name = "irq",
|
|
.description = "IRQ",
|
|
.type = CONFIG_SELECTION,
|
|
.default_string = NULL,
|
|
.default_int = 5,
|
|
.file_filter = NULL,
|
|
.spinner = { 0 },
|
|
.selection = {
|
|
{ .description = "None", .value = -1 },
|
|
{ .description = "IRQ 3", .value = 3 },
|
|
{ .description = "IRQ 5", .value = 5 },
|
|
{ .description = "IRQ 7", .value = 7 },
|
|
{ .description = "" }
|
|
},
|
|
.bios = { { 0 } }
|
|
},
|
|
{ .name = "", .description = "", .type = CONFIG_END }
|
|
};
|
|
|
|
static const device_config_t rt1000b_mc_config[] = {
|
|
{
|
|
.name = "irq",
|
|
.description = "IRQ",
|
|
.type = CONFIG_SELECTION,
|
|
.default_string = NULL,
|
|
.default_int = 5,
|
|
.file_filter = NULL,
|
|
.spinner = { 0 },
|
|
.selection = {
|
|
{ .description = "None", .value = -1 },
|
|
{ .description = "IRQ 3", .value = 3 },
|
|
{ .description = "IRQ 5", .value = 5 },
|
|
{ .description = "IRQ 7", .value = 7 },
|
|
{ .description = "" }
|
|
},
|
|
.bios = { { 0 } }
|
|
},
|
|
{ .name = "", .description = "", .type = CONFIG_END }
|
|
};
|
|
|
|
static const device_config_t t130b_config[] = {
|
|
{
|
|
.name = "bios_addr",
|
|
.description = "BIOS Address",
|
|
.type = CONFIG_HEX20,
|
|
.default_string = NULL,
|
|
.default_int = 0xD8000,
|
|
.file_filter = NULL,
|
|
.spinner = { 0 },
|
|
.selection = {
|
|
{ .description = "Disabled", .value = 0 },
|
|
{ .description = "C800H", .value = 0xc8000 },
|
|
{ .description = "CC00H", .value = 0xcc000 },
|
|
{ .description = "D800H", .value = 0xd8000 },
|
|
{ .description = "DC00H", .value = 0xdc000 },
|
|
{ .description = "" }
|
|
},
|
|
.bios = { { 0 } }
|
|
},
|
|
{
|
|
.name = "base",
|
|
.description = "Address",
|
|
.type = CONFIG_HEX16,
|
|
.default_string = NULL,
|
|
.default_int = 0x0350,
|
|
.file_filter = NULL,
|
|
.spinner = { 0 },
|
|
.selection = {
|
|
{ .description = "240H", .value = 0x0240 },
|
|
{ .description = "250H", .value = 0x0250 },
|
|
{ .description = "340H", .value = 0x0340 },
|
|
{ .description = "350H", .value = 0x0350 },
|
|
{ .description = "" }
|
|
},
|
|
.bios = { { 0 } }
|
|
},
|
|
{
|
|
.name = "irq",
|
|
.description = "IRQ",
|
|
.type = CONFIG_SELECTION,
|
|
.default_string = NULL,
|
|
.default_int = 5,
|
|
.file_filter = NULL,
|
|
.spinner = { 0 },
|
|
.selection = {
|
|
{ .description = "None", .value = -1 },
|
|
{ .description = "IRQ 3", .value = 3 },
|
|
{ .description = "IRQ 5", .value = 5 },
|
|
{ .description = "IRQ 7", .value = 7 },
|
|
{ .description = "" }
|
|
},
|
|
.bios = { { 0 } }
|
|
},
|
|
{ .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 | DEVICE_SIDECAR,
|
|
.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
|
|
};
|