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.
999 lines
32 KiB
C
999 lines
32 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/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
|
|
};
|