Files
86Box/src/scsi/scsi_spock.c

1221 lines
45 KiB
C
Raw Normal View History

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Implementation of the IBM PS/2 SCSI controller with
* cache for MCA only.
*
2020-03-25 00:46:02 +02:00
*
*
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* TheCollector1995, <mariogplayer@gmail.com>
*
* Copyright 2020 Sarah Walker.
* Copyright 2020 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_spock.h>
2022-09-18 17:17:15 -04:00
#define SPOCK_U68_1990_ROM "roms/scsi/ibm/64f4376.bin"
#define SPOCK_U69_1990_ROM "roms/scsi/ibm/64f4377.bin"
2022-09-18 17:17:15 -04:00
#define SPOCK_U68_1991_ROM "roms/scsi/ibm/92F2244.U68"
#define SPOCK_U69_1991_ROM "roms/scsi/ibm/92F2245.U69"
2022-09-18 17:17:15 -04:00
#define SPOCK_TIME (20)
2022-09-18 17:17:15 -04:00
typedef enum {
SCSI_STATE_IDLE,
SCSI_STATE_SELECT,
SCSI_STATE_SEND_COMMAND,
SCSI_STATE_END_PHASE
} scsi_state_t;
2022-09-18 17:17:15 -04:00
#pragma pack(push, 1)
typedef struct {
2022-09-18 17:17:15 -04:00
uint16_t pos;
uint16_t pos1;
uint16_t pos2;
uint16_t pos3;
uint16_t pos4;
uint16_t pos5;
uint16_t pos6;
uint16_t pos7;
uint16_t pos8;
} get_pos_info_t;
typedef struct {
2022-09-18 17:17:15 -04:00
uint16_t scb_status;
uint16_t retry_count;
uint32_t residual_byte_count;
uint32_t sg_list_element_addr;
uint16_t device_dep_status_len;
uint16_t cmd_status;
uint16_t error;
uint16_t reserved;
uint16_t cache_info_status;
uint32_t scb_addr;
} get_complete_stat_t;
typedef struct {
2022-09-18 17:17:15 -04:00
uint32_t sys_buf_addr;
uint32_t sys_buf_byte_count;
} SGE;
typedef struct {
2022-09-18 17:17:15 -04:00
uint16_t command;
uint16_t enable;
uint32_t lba_addr;
SGE sge;
uint32_t term_status_block_addr;
uint32_t scb_chain_addr;
uint16_t block_count;
uint16_t block_length;
} scb_t;
#pragma pack(pop)
typedef struct {
2022-09-18 17:17:15 -04:00
rom_t bios_rom;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
int bios_ver;
int irq, irq_inactive;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
uint8_t pos_regs[8];
2022-09-18 17:17:15 -04:00
uint8_t basic_ctrl;
uint32_t command;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
uint8_t attention,
attention_pending;
int attention_wait;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
uint8_t cir[4],
cir_pending[4];
2022-09-18 17:17:15 -04:00
uint8_t irq_status;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
uint32_t scb_addr;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
uint8_t status;
2022-09-18 17:17:15 -04:00
get_complete_stat_t get_complete_stat;
get_pos_info_t get_pos_info;
2022-09-18 17:17:15 -04:00
scb_t scb;
int scb_id;
int adapter_id;
int assign;
int present[8];
2022-09-18 17:17:15 -04:00
int cmd_status;
int cir_status;
2022-09-18 17:17:15 -04:00
uint8_t pacing;
2022-09-18 17:17:15 -04:00
uint8_t buf[0x600];
2022-09-18 17:17:15 -04:00
struct {
int phys_id;
int lun_id;
} dev_id[SCSI_ID_MAX];
2022-09-18 17:17:15 -04:00
uint8_t last_status, bus;
uint8_t cdb[12];
int cdb_len;
int cdb_id;
uint32_t data_ptr, data_len;
uint8_t temp_cdb[12];
2022-09-18 17:17:15 -04:00
int irq_requests[SCSI_ID_MAX];
2022-09-18 17:17:15 -04:00
pc_timer_t callback_timer;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
int cmd_timer;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
int scb_state;
int in_reset;
int in_invalid;
2022-09-18 17:17:15 -04:00
uint64_t temp_period;
double media_period;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
scsi_state_t scsi_state;
} spock_t;
2022-09-18 17:17:15 -04:00
#define CTRL_RESET (1 << 7)
#define CTRL_DMA_ENA (1 << 1)
#define CTRL_IRQ_ENA (1 << 0)
#define STATUS_CMD_FULL (1 << 3)
#define STATUS_CMD_EMPTY (1 << 2)
#define STATUS_IRQ (1 << 1)
#define STATUS_BUSY (1 << 0)
#define ENABLE_PT (1 << 12)
#define CMD_MASK 0xff3f
#define CMD_ASSIGN 0x040e
#define CMD_DEVICE_INQUIRY 0x1c0b
#define CMD_DMA_PACING_CONTROL 0x040d
#define CMD_FEATURE_CONTROL 0x040c
#define CMD_GET_POS_INFO 0x1c0a
#define CMD_INVALID_412 0x0412
#define CMD_GET_COMPLETE_STATUS 0x1c07
#define CMD_FORMAT_UNIT 0x1c16
#define CMD_READ_DATA 0x1c01
#define CMD_READ_DEVICE_CAPACITY 0x1c09
#define CMD_REQUEST_SENSE 0x1c08
#define CMD_RESET 0x0400
#define CMD_SEND_OTHER_SCSI 0x241f
#define CMD_UNKNOWN_1C10 0x1c10
#define CMD_UNKNOWN_1C11 0x1c11
#define CMD_WRITE_DATA 0x1c02
#define CMD_VERIFY 0x1c03
#define IRQ_TYPE_NONE 0x0
#define IRQ_TYPE_SCB_COMPLETE 0x1
#define IRQ_TYPE_SCB_COMPLETE_RETRY 0x5
#define IRQ_TYPE_ADAPTER_HW_FAILURE 0x7
#define IRQ_TYPE_IMM_CMD_COMPLETE 0xa
#define IRQ_TYPE_COMMAND_FAIL 0xc
#define IRQ_TYPE_COMMAND_ERROR 0xe
#define IRQ_TYPE_SW_SEQ_ERROR 0xf
#define IRQ_TYPE_RESET_COMPLETE 0x10
#ifdef ENABLE_SPOCK_LOG
int spock_do_log = ENABLE_SPOCK_LOG;
static void
spock_log(const char *fmt, ...)
{
va_list ap;
if (spock_do_log) {
2022-09-18 17:17:15 -04:00
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
2022-09-18 17:17:15 -04:00
# define spock_log(fmt, ...)
#endif
static void
spock_rethink_irqs(spock_t *scsi)
{
2022-09-18 17:17:15 -04:00
int irq_pending = 0;
if (!scsi->irq_status) {
2023-05-29 01:30:51 -04:00
for (uint8_t c = 0; c < SCSI_ID_MAX; c++) {
2022-09-18 17:17:15 -04:00
if (scsi->irq_requests[c] != IRQ_TYPE_NONE) {
/* Found IRQ */
scsi->irq_status = c | (scsi->irq_requests[c] << 4);
spock_log("Found IRQ: status = %02x\n", scsi->irq_status);
scsi->status |= STATUS_IRQ;
irq_pending = 1;
2022-09-18 17:17:15 -04:00
break;
}
}
} else
irq_pending = 1;
if (scsi->basic_ctrl & CTRL_IRQ_ENA) {
if (irq_pending) {
spock_log("IRQ issued\n");
scsi->irq_inactive = 0;
picint(1 << scsi->irq);
} else {
2022-09-18 17:17:15 -04:00
/* No IRQs pending, clear IRQ state */
spock_log("IRQ cleared\n");
scsi->irq_status = 0;
scsi->irq_inactive = 1;
scsi->status &= ~STATUS_IRQ;
picintc(1 << scsi->irq);
}
2022-09-18 17:17:15 -04:00
} else {
spock_log("IRQ disabled\n");
picintc(1 << scsi->irq);
}
}
2022-02-20 02:26:27 -05:00
static __inline void
spock_set_irq(spock_t *scsi, int id, int type)
{
2022-09-18 17:17:15 -04:00
spock_log("spock_set_irq id=%i type=%x %02x\n", id, type, scsi->irq_status);
scsi->irq_requests[id] = type;
if (!scsi->irq_status) /* Don't change IRQ status if one is currently being processed */
spock_rethink_irqs(scsi);
}
2022-02-20 02:26:27 -05:00
static __inline void
spock_clear_irq(spock_t *scsi, int id)
{
2022-09-18 17:17:15 -04:00
spock_log("spock_clear_irq id=%i\n", id);
scsi->irq_requests[id] = IRQ_TYPE_NONE;
spock_rethink_irqs(scsi);
}
static void
spock_add_to_period(spock_t *scsi, int TransferLength)
{
2022-09-18 17:17:15 -04:00
scsi->temp_period += (uint64_t) TransferLength;
}
static void
2023-06-09 23:46:54 -04:00
spock_write(uint16_t port, uint8_t val, void *priv)
{
2023-06-09 23:46:54 -04:00
spock_t *scsi = (spock_t *) priv;
2022-09-18 17:17:15 -04:00
spock_log("spock_write: port=%04x val=%02x %04x:%04x\n", port, val, CS, cpu_state.pc);
switch (port & 7) {
case 0:
case 1:
case 2:
case 3: /*Command Interface Register*/
scsi->cir_pending[port & 3] = val;
if (port & 2)
scsi->cir_status |= 2;
else
scsi->cir_status |= 1;
break;
case 4: /*Attention Register*/
scsi->attention_pending = val;
scsi->attention_wait = 2;
scsi->status |= STATUS_BUSY;
break;
case 5: /*Basic Control Register*/
if ((scsi->basic_ctrl & CTRL_RESET) && !(val & CTRL_RESET)) {
spock_log("Spock: SCSI reset and busy\n");
scsi->in_reset = 1;
scsi->cmd_timer = SPOCK_TIME * 2;
scsi->status |= STATUS_BUSY;
}
scsi->basic_ctrl = val;
spock_rethink_irqs(scsi);
break;
2023-06-09 23:46:54 -04:00
default:
break;
2022-09-18 17:17:15 -04:00
}
}
static void
2023-06-09 23:46:54 -04:00
spock_writew(uint16_t port, uint16_t val, void *priv)
{
2023-06-09 23:46:54 -04:00
spock_t *scsi = (spock_t *) priv;
2022-09-18 17:17:15 -04:00
switch (port & 7) {
case 0: /*Command Interface Register*/
scsi->cir_pending[0] = val & 0xff;
scsi->cir_pending[1] = val >> 8;
scsi->cir_status |= 1;
break;
case 2: /*Command Interface Register*/
scsi->cir_pending[2] = val & 0xff;
scsi->cir_pending[3] = val >> 8;
scsi->cir_status |= 2;
break;
2023-06-09 23:46:54 -04:00
default:
break;
2022-09-18 17:17:15 -04:00
}
2022-09-18 17:17:15 -04:00
spock_log("spock_writew: port=%04x val=%04x\n", port, val);
}
static uint8_t
2023-06-09 23:46:54 -04:00
spock_read(uint16_t port, void *priv)
{
2023-06-09 23:46:54 -04:00
spock_t *scsi = (spock_t *) priv;
2022-09-18 17:17:15 -04:00
uint8_t temp = 0xff;
switch (port & 7) {
case 0:
case 1:
case 2:
case 3: /*Command Interface Register*/
temp = scsi->cir_pending[port & 3];
break;
case 4: /*Attention Register*/
temp = scsi->attention_pending;
break;
case 5: /*Basic Control Register*/
temp = scsi->basic_ctrl;
break;
case 6: /*IRQ status*/
temp = scsi->irq_status;
break;
case 7: /*Basic Status Register*/
temp = scsi->status;
spock_log("Cir Status=%d\n", scsi->cir_status);
if (scsi->cir_status == 0) {
spock_log("Status Cmd Empty\n");
temp |= STATUS_CMD_EMPTY;
} else if (scsi->cir_status == 3) {
spock_log("Status Cmd Full\n");
temp |= STATUS_CMD_FULL;
}
break;
2023-06-09 23:46:54 -04:00
default:
break;
2022-09-18 17:17:15 -04:00
}
2022-02-20 02:26:27 -05:00
spock_log("spock_read: port=%04x val=%02x %04x(%05x):%04x.\n", port, temp, CS, cs, cpu_state.pc);
2022-09-18 17:17:15 -04:00
return temp;
}
static uint16_t
2023-06-09 23:46:54 -04:00
spock_readw(uint16_t port, void *priv)
{
2023-06-09 23:46:54 -04:00
spock_t *scsi = (spock_t *) priv;
2022-09-18 17:17:15 -04:00
uint16_t temp = 0xffff;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
switch (port & 7) {
case 0: /*Command Interface Register*/
temp = scsi->cir_pending[0] | (scsi->cir_pending[1] << 8);
break;
case 2: /*Command Interface Register*/
temp = scsi->cir_pending[2] | (scsi->cir_pending[3] << 8);
break;
2023-06-09 23:46:54 -04:00
default:
break;
2022-09-18 17:17:15 -04:00
}
spock_log("spock_readw: port=%04x val=%04x\n", port, temp);
return temp;
}
static void
spock_rd_sge(spock_t *scsi, uint32_t Address, SGE *SG)
{
2022-09-18 17:17:15 -04:00
dma_bm_read(Address, (uint8_t *) SG, sizeof(SGE), 2);
spock_add_to_period(scsi, sizeof(SGE));
}
static int
spock_get_len(spock_t *scsi, scb_t *scb)
{
2023-05-29 01:30:51 -04:00
uint32_t DataToTransfer = 0;
2022-09-18 17:17:15 -04:00
spock_log("Data Buffer write: length %d, pointer 0x%04X\n",
scsi->data_len, scsi->data_ptr);
2022-09-18 17:17:15 -04:00
if (!scsi->data_len)
2023-05-29 01:30:51 -04:00
return 0;
2022-09-18 17:17:15 -04:00
if (scb->enable & ENABLE_PT) {
2023-05-29 01:30:51 -04:00
for (uint32_t i = 0; i < scsi->data_len; i += 8) {
2022-09-18 17:17:15 -04:00
spock_rd_sge(scsi, scsi->data_ptr + i, &scb->sge);
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
DataToTransfer += scb->sge.sys_buf_byte_count;
}
2023-05-29 01:30:51 -04:00
return DataToTransfer;
2022-09-18 17:17:15 -04:00
} else {
return (scsi->data_len);
}
}
static void
spock_process_imm_cmd(spock_t *scsi)
{
2023-05-29 01:30:51 -04:00
int i;
int j = 0;
int adapter_id;
int phys_id;
int lun_id;
2022-02-20 02:26:27 -05:00
scsi->assign = 0;
switch (scsi->command & CMD_MASK) {
case CMD_ASSIGN:
scsi->assign = 1;
2023-02-28 23:24:58 -05:00
adapter_id = (scsi->command >> 16) & 15;
phys_id = (scsi->command >> 20) & 7;
lun_id = (scsi->command >> 24) & 7;
2022-09-18 17:17:15 -04:00
if (adapter_id == 15) {
if (phys_id == 7) /*Device 15 always adapter*/
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
else /*Can not re-assign device 15 (always adapter)*/
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_FAIL);
} else {
if (scsi->command & (1 << 23)) {
spock_log("Assign: adapter id=%d\n", adapter_id);
scsi->dev_id[adapter_id].phys_id = -1;
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
} else {
if (phys_id != scsi->adapter_id) {
scsi->dev_id[adapter_id].phys_id = phys_id;
2022-09-18 17:17:15 -04:00
scsi->dev_id[adapter_id].lun_id = lun_id;
spock_log("Assign: adapter dev=%x scsi ID=%i LUN=%i.\n", adapter_id, scsi->dev_id[adapter_id].phys_id, scsi->dev_id[adapter_id].lun_id);
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
} else { /*Can not assign adapter*/
spock_log("Assign: PUN=%d, cannot assign adapter.\n", phys_id);
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_FAIL);
}
}
}
2022-09-18 17:17:15 -04:00
break;
case CMD_DMA_PACING_CONTROL:
scsi->pacing = scsi->cir[2];
spock_log("Pacing control: %i\n", scsi->pacing);
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
break;
case CMD_FEATURE_CONTROL:
spock_log("Feature control: timeout=%is d-rate=%i\n", (scsi->command >> 16) & 0x1fff, scsi->command >> 29);
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
break;
case CMD_INVALID_412:
spock_log("Invalid 412\n");
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
break;
case CMD_RESET:
spock_log("Reset Command, attention = %d.\n", scsi->attention & 0x0f);
if ((scsi->attention & 0x0f) == 0x0f) { /*Adapter reset*/
for (i = 0; i < 8; i++) {
scsi_device_reset(&scsi_devices[scsi->bus][i]);
}
for (i = 6; i > -1; i--) {
if (scsi_device_present(&scsi_devices[scsi->bus][i])) {
spock_log("Adapter Reset, SCSI reset present devices=%d, phys ID=%d, type=%04x.\n", j, scsi->dev_id[i].phys_id, scsi_devices[scsi->bus][i].type);
scsi->present[j] = i;
j++;
} else {
spock_log("Adapter Reset, SCSI reset not present devices=%d, phys ID=%d, type=%04x.\n", j, scsi->dev_id[i].phys_id, scsi_devices[scsi->bus][i].type);
}
}
scsi->scb_state = 0;
2022-09-18 17:17:15 -04:00
}
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
break;
default:
fatal("scsi_callback: Bad command %02x\n", scsi->command);
break;
}
}
static void
spock_execute_cmd(spock_t *scsi, scb_t *scb)
{
2023-05-29 01:30:51 -04:00
int c;
int j = 0;
2022-09-18 17:17:15 -04:00
int old_scb_state;
2022-09-18 17:17:15 -04:00
if (scsi->in_reset) {
spock_log("Reset type = %d\n", scsi->in_reset);
2022-09-18 17:17:15 -04:00
scsi->status &= ~STATUS_BUSY;
scsi->irq_status = 0;
2022-09-18 17:17:15 -04:00
for (c = 0; c < SCSI_ID_MAX; c++)
spock_clear_irq(scsi, c);
if (scsi->in_reset == 1) {
scsi->basic_ctrl |= CTRL_IRQ_ENA;
}
spock_set_irq(scsi, 0x0f, IRQ_TYPE_RESET_COMPLETE);
2022-09-18 17:17:15 -04:00
/*Reset device mappings*/
for (c = 0; c < 7; c++) {
scsi->dev_id[c].phys_id = c;
scsi->dev_id[c].lun_id = 0;
}
for (; c < (SCSI_ID_MAX - 1); c++)
scsi->dev_id[c].phys_id = -1;
2022-09-18 17:17:15 -04:00
scsi->in_reset = 0;
for (c = 6; c > -1; c--) {
if (scsi_device_present(&scsi_devices[scsi->bus][c])) {
spock_log("Reset, SCSI reset present devices=%d, phys ID=%d, type=%04x.\n", j, scsi->dev_id[c].phys_id, scsi_devices[scsi->bus][c].type);
scsi->present[j] = c;
j++;
} else {
spock_log("Reset, SCSI reset not present devices=%d, phys ID=%d, type=%04x.\n", j, scsi->dev_id[c].phys_id, scsi_devices[scsi->bus][c].type);
}
}
2022-09-18 17:17:15 -04:00
return;
}
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
if (scsi->in_invalid) {
spock_log("Invalid command\n");
spock_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_ERROR);
scsi->in_invalid = 0;
return;
}
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
spock_log("SCB State = %d\n", scsi->scb_state);
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
do {
old_scb_state = scsi->scb_state;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
switch (scsi->scb_state) {
case 0: /* Idle */
break;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
case 1: /* Select */
if (scsi->dev_id[scsi->scb_id].phys_id == -1) {
uint16_t term_stat_block_addr7 = (0xe << 8) | 0;
uint16_t term_stat_block_addr8 = (0xa << 8) | 0;
spock_log("Start failed, SCB ID = %d\n", scsi->scb_id);
spock_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL);
scsi->scb_state = 0;
2022-09-18 17:17:15 -04:00
dma_bm_write(scb->term_status_block_addr + 0x7 * 2, (uint8_t *) &term_stat_block_addr7, 2, 2);
dma_bm_write(scb->term_status_block_addr + 0x8 * 2, (uint8_t *) &term_stat_block_addr8, 2, 2);
break;
}
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
dma_bm_read(scsi->scb_addr, (uint8_t *) &scb->command, 2, 2);
dma_bm_read(scsi->scb_addr + 2, (uint8_t *) &scb->enable, 2, 2);
dma_bm_read(scsi->scb_addr + 4, (uint8_t *) &scb->lba_addr, 4, 2);
dma_bm_read(scsi->scb_addr + 8, (uint8_t *) &scb->sge.sys_buf_addr, 4, 2);
dma_bm_read(scsi->scb_addr + 12, (uint8_t *) &scb->sge.sys_buf_byte_count, 4, 2);
dma_bm_read(scsi->scb_addr + 16, (uint8_t *) &scb->term_status_block_addr, 4, 2);
dma_bm_read(scsi->scb_addr + 20, (uint8_t *) &scb->scb_chain_addr, 4, 2);
dma_bm_read(scsi->scb_addr + 24, (uint8_t *) &scb->block_count, 2, 2);
dma_bm_read(scsi->scb_addr + 26, (uint8_t *) &scb->block_length, 2, 2);
spock_log("SCB : \n"
" Command = %04x\n"
" Enable = %04x\n"
" LBA addr = %08x\n"
" System buffer addr = %08x\n"
" System buffer byte count = %08x\n"
" Terminate status block addr = %08x\n"
" SCB chain address = %08x\n"
" Block count = %04x\n"
" Block length = %04x\n"
" SCB id = %d, Phys id = %d, Spock CMD = %08x, CMD Mask = %02x.\n",
2022-09-18 17:17:15 -04:00
scb->command, scb->enable, scb->lba_addr,
scb->sge.sys_buf_addr, scb->sge.sys_buf_byte_count,
scb->term_status_block_addr, scb->scb_chain_addr,
scb->block_count, scb->block_length, scsi->scb_id, scsi->dev_id[scsi->scb_id].phys_id, scsi->command, scb->command & 0xc0);
2022-09-18 17:17:15 -04:00
switch (scb->command & CMD_MASK) {
case CMD_GET_COMPLETE_STATUS:
{
spock_log("Get Complete Status\n");
get_complete_stat_t *get_complete_stat = &scsi->get_complete_stat;
get_complete_stat->scb_status = 0x201;
get_complete_stat->retry_count = 0;
get_complete_stat->residual_byte_count = 0;
get_complete_stat->sg_list_element_addr = 0;
get_complete_stat->device_dep_status_len = 0x0c;
get_complete_stat->cmd_status = scsi->cmd_status << 8;
get_complete_stat->error = 0;
get_complete_stat->reserved = 0;
get_complete_stat->cache_info_status = 0;
get_complete_stat->scb_addr = scsi->scb_addr;
dma_bm_write(scb->sge.sys_buf_addr, (uint8_t *) &get_complete_stat->scb_status, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 2, (uint8_t *) &get_complete_stat->retry_count, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 4, (uint8_t *) &get_complete_stat->residual_byte_count, 4, 2);
dma_bm_write(scb->sge.sys_buf_addr + 8, (uint8_t *) &get_complete_stat->sg_list_element_addr, 4, 2);
dma_bm_write(scb->sge.sys_buf_addr + 12, (uint8_t *) &get_complete_stat->device_dep_status_len, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 14, (uint8_t *) &get_complete_stat->cmd_status, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 16, (uint8_t *) &get_complete_stat->error, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 18, (uint8_t *) &get_complete_stat->reserved, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 20, (uint8_t *) &get_complete_stat->cache_info_status, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 22, (uint8_t *) &get_complete_stat->scb_addr, 4, 2);
scsi->scb_state = 3;
}
break;
case CMD_UNKNOWN_1C10:
spock_log("Unknown 1C10\n");
dma_bm_read(scb->sge.sys_buf_addr, scsi->buf, scb->sge.sys_buf_byte_count, 2);
scsi->scb_state = 3;
break;
case CMD_UNKNOWN_1C11:
spock_log("Unknown 1C11\n");
dma_bm_write(scb->sge.sys_buf_addr, scsi->buf, scb->sge.sys_buf_byte_count, 2);
scsi->scb_state = 3;
break;
case CMD_GET_POS_INFO:
{
spock_log("Get POS Info\n");
get_pos_info_t *get_pos_info = &scsi->get_pos_info;
get_pos_info->pos = 0x8eff;
get_pos_info->pos1 = scsi->pos_regs[3] | (scsi->pos_regs[2] << 8);
get_pos_info->pos2 = 0x0e | (scsi->pos_regs[4] << 8);
get_pos_info->pos3 = 1 << 12;
get_pos_info->pos4 = (7 << 8) | 8;
get_pos_info->pos5 = (16 << 8) | scsi->pacing;
get_pos_info->pos6 = (30 << 8) | 1;
get_pos_info->pos7 = 0;
get_pos_info->pos8 = 0;
dma_bm_write(scb->sge.sys_buf_addr, (uint8_t *) &get_pos_info->pos, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 2, (uint8_t *) &get_pos_info->pos1, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 4, (uint8_t *) &get_pos_info->pos2, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 6, (uint8_t *) &get_pos_info->pos3, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 8, (uint8_t *) &get_pos_info->pos4, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 10, (uint8_t *) &get_pos_info->pos5, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 12, (uint8_t *) &get_pos_info->pos6, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 14, (uint8_t *) &get_pos_info->pos7, 2, 2);
dma_bm_write(scb->sge.sys_buf_addr + 16, (uint8_t *) &get_pos_info->pos8, 2, 2);
scsi->scb_state = 3;
}
break;
case CMD_DEVICE_INQUIRY:
scsi->cdb_id = scsi->assign ? scsi->dev_id[scsi->scb_id].phys_id : scsi->present[scsi->scb_id];
2022-09-18 17:17:15 -04:00
spock_log("Device Inquiry, ID=%d\n", scsi->cdb_id);
scsi->cdb[0] = GPCMD_INQUIRY;
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
scsi->cdb[2] = 0; /*Page code*/
scsi->cdb[3] = 0;
scsi->cdb[4] = scb->sge.sys_buf_byte_count; /*Allocation length*/
scsi->cdb[5] = 0; /*Control*/
scsi->cdb_len = 6;
scsi->data_ptr = scb->sge.sys_buf_addr;
scsi->data_len = scb->sge.sys_buf_byte_count;
scsi->scsi_state = SCSI_STATE_SELECT;
scsi->scb_state = 2;
return;
case CMD_SEND_OTHER_SCSI:
scsi->cdb_id = scsi->assign ? scsi->dev_id[scsi->scb_id].phys_id : scsi->present[scsi->scb_id];
2022-09-18 17:17:15 -04:00
dma_bm_read(scsi->scb_addr + 0x18, scsi->cdb, 12, 2);
spock_log("Send Other SCSI, SCB ID=%d, PHYS ID=%d\n", scsi->scb_id, scsi->dev_id[scsi->scb_id].phys_id);
2022-09-18 17:17:15 -04:00
scsi->cdb[1] = (scsi->cdb[1] & 0x1f) | (scsi->dev_id[scsi->scb_id].lun_id << 5); /*Patch correct LUN into command*/
scsi->cdb_len = (scb->lba_addr & 0xff) ? (scb->lba_addr & 0xff) : 6;
scsi->scsi_state = SCSI_STATE_SELECT;
scsi->scb_state = 2;
return;
case CMD_READ_DEVICE_CAPACITY:
scsi->cdb_id = scsi->assign ? scsi->dev_id[scsi->scb_id].phys_id : scsi->present[scsi->scb_id];
spock_log("Device Capacity, SCB ID=%d, PHYS ID=%d\n", scsi->scb_id, scsi->dev_id[scsi->scb_id].phys_id);
2022-09-18 17:17:15 -04:00
scsi->cdb[0] = GPCMD_READ_CDROM_CAPACITY;
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
scsi->cdb[2] = 0; /*LBA*/
scsi->cdb[3] = 0;
scsi->cdb[4] = 0;
scsi->cdb[5] = 0;
scsi->cdb[6] = 0; /*Reserved*/
scsi->cdb[7] = 0;
scsi->cdb[8] = 0;
scsi->cdb[9] = 0; /*Control*/
scsi->cdb_len = 10;
scsi->scsi_state = SCSI_STATE_SELECT;
scsi->scb_state = 2;
return;
case CMD_READ_DATA:
scsi->cdb_id = scsi->assign ? scsi->dev_id[scsi->scb_id].phys_id : scsi->present[scsi->scb_id];
spock_log("Device Read Data, SCB ID=%d, PHYS ID=%d\n", scsi->scb_id, scsi->dev_id[scsi->scb_id].phys_id);
2022-09-18 17:17:15 -04:00
scsi->cdb[0] = GPCMD_READ_10;
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
scsi->cdb[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/
scsi->cdb[3] = (scb->lba_addr >> 16) & 0xff;
scsi->cdb[4] = (scb->lba_addr >> 8) & 0xff;
scsi->cdb[5] = scb->lba_addr & 0xff;
scsi->cdb[6] = 0; /*Reserved*/
scsi->cdb[7] = (scb->block_count >> 8) & 0xff;
scsi->cdb[8] = scb->block_count & 0xff;
scsi->cdb[9] = 0; /*Control*/
scsi->cdb_len = 10;
scsi->scsi_state = SCSI_STATE_SELECT;
scsi->scb_state = 2;
return;
case CMD_WRITE_DATA:
scsi->cdb_id = scsi->assign ? scsi->dev_id[scsi->scb_id].phys_id : scsi->present[scsi->scb_id];
2022-09-18 17:17:15 -04:00
spock_log("Device Write Data\n");
scsi->cdb[0] = GPCMD_WRITE_10;
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
scsi->cdb[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/
scsi->cdb[3] = (scb->lba_addr >> 16) & 0xff;
scsi->cdb[4] = (scb->lba_addr >> 8) & 0xff;
scsi->cdb[5] = scb->lba_addr & 0xff;
scsi->cdb[6] = 0; /*Reserved*/
scsi->cdb[7] = (scb->block_count >> 8) & 0xff;
scsi->cdb[8] = scb->block_count & 0xff;
scsi->cdb[9] = 0; /*Control*/
scsi->cdb_len = 10;
scsi->scsi_state = SCSI_STATE_SELECT;
scsi->scb_state = 2;
return;
case CMD_VERIFY:
scsi->cdb_id = scsi->assign ? scsi->dev_id[scsi->scb_id].phys_id : scsi->present[scsi->scb_id];
2022-09-18 17:17:15 -04:00
spock_log("Device Verify\n");
scsi->cdb[0] = GPCMD_VERIFY_10;
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
scsi->cdb[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/
scsi->cdb[3] = (scb->lba_addr >> 16) & 0xff;
scsi->cdb[4] = (scb->lba_addr >> 8) & 0xff;
scsi->cdb[5] = scb->lba_addr & 0xff;
scsi->cdb[6] = 0; /*Reserved*/
scsi->cdb[7] = (scb->block_count >> 8) & 0xff;
scsi->cdb[8] = scb->block_count & 0xff;
scsi->cdb[9] = 0; /*Control*/
scsi->cdb_len = 10;
scsi->data_len = 0;
scsi->scsi_state = SCSI_STATE_SELECT;
scsi->scb_state = 2;
return;
case CMD_REQUEST_SENSE:
scsi->cdb_id = scsi->assign ? scsi->dev_id[scsi->scb_id].phys_id : scsi->present[scsi->scb_id];
2022-09-18 17:17:15 -04:00
spock_log("Device Request Sense, ID=%d\n", scsi->cdb_id);
scsi->cdb[0] = GPCMD_REQUEST_SENSE;
scsi->cdb[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
scsi->cdb[2] = 0;
scsi->cdb[3] = 0;
scsi->cdb[4] = scb->sge.sys_buf_byte_count; /*Allocation length*/
scsi->cdb[5] = 0;
scsi->cdb_len = 6;
scsi->scsi_state = SCSI_STATE_SELECT;
scsi->scb_state = 2;
return;
2023-06-09 23:46:54 -04:00
default:
break;
2022-09-18 17:17:15 -04:00
}
break;
case 2: /* Wait */
if (scsi->scsi_state == SCSI_STATE_IDLE) {
if (scsi_device_present(&scsi_devices[scsi->bus][scsi->cdb_id])) {
if (scsi->last_status == SCSI_STATUS_OK) {
scsi->scb_state = 3;
spock_log("Status is Good on device ID %d, cdb id = %d.\n", scsi->scb_id, scsi->cdb_id);
} else if (scsi->last_status == SCSI_STATUS_CHECK_CONDITION) {
uint16_t term_stat_block_addr7 = (0xc << 8) | 2;
uint16_t term_stat_block_addr8 = 0x20;
uint16_t term_stat_block_addrb = scsi->scb_addr & 0xffff;
uint16_t term_stat_block_addrc = scsi->scb_addr >> 16;
spock_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL);
scsi->scb_state = 0;
spock_log("Status Check Condition on device ID %d, cdb id = %d.\n", scsi->attention & 0x0f, scsi->cdb_id);
2022-09-18 17:17:15 -04:00
dma_bm_write(scb->term_status_block_addr + 0x7 * 2, (uint8_t *) &term_stat_block_addr7, 2, 2);
dma_bm_write(scb->term_status_block_addr + 0x8 * 2, (uint8_t *) &term_stat_block_addr8, 2, 2);
dma_bm_write(scb->term_status_block_addr + 0xb * 2, (uint8_t *) &term_stat_block_addrb, 2, 2);
dma_bm_write(scb->term_status_block_addr + 0xc * 2, (uint8_t *) &term_stat_block_addrc, 2, 2);
}
} else {
uint16_t term_stat_block_addr7 = (0xc << 8) | 2;
uint16_t term_stat_block_addr8 = 0x10;
spock_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL);
scsi->scb_state = 0;
spock_log("Status Check Condition on device ID %d on no device\n", scsi->scb_id);
2022-09-18 17:17:15 -04:00
dma_bm_write(scb->term_status_block_addr + 0x7 * 2, (uint8_t *) &term_stat_block_addr7, 2, 2);
dma_bm_write(scb->term_status_block_addr + 0x8 * 2, (uint8_t *) &term_stat_block_addr8, 2, 2);
}
2022-09-18 17:17:15 -04:00
}
break;
case 3: /* Complete */
if (scb->enable & 1) {
scsi->scb_state = 1;
scsi->scb_addr = scb->scb_chain_addr;
spock_log("Next SCB - %08x\n", scsi->scb_addr);
} else {
spock_set_irq(scsi, scsi->scb_id, IRQ_TYPE_SCB_COMPLETE);
scsi->scb_state = 0;
spock_log("Complete SCB ID = %d.\n", scsi->attention & 0x0f);
2022-09-18 17:17:15 -04:00
}
break;
2023-06-09 23:46:54 -04:00
default:
break;
2022-09-18 17:17:15 -04:00
}
} while (scsi->scb_state != old_scb_state);
}
static void
spock_process_scsi(spock_t *scsi, scb_t *scb)
{
2022-09-18 17:17:15 -04:00
double p;
scsi_device_t *sd;
switch (scsi->scsi_state) {
case SCSI_STATE_IDLE:
break;
case SCSI_STATE_SELECT:
spock_log("Selecting ID %d, SCB ID %d, LUN %d, adapter id = %d.\n", scsi->cdb_id, scsi->scb_id, scsi->dev_id[scsi->scb_id].lun_id, scsi->attention);
2022-09-18 17:17:15 -04:00
if ((scsi->cdb_id != (uint8_t) -1) && scsi_device_present(&scsi_devices[scsi->bus][scsi->cdb_id])) {
scsi->scsi_state = SCSI_STATE_SEND_COMMAND;
spock_log("Device selected at ID %i.\n", scsi->cdb_id);
2022-09-18 17:17:15 -04:00
} else {
spock_log("Device selection failed at ID %i.\n", scsi->cdb_id);
2022-09-18 17:17:15 -04:00
scsi->scsi_state = SCSI_STATE_IDLE;
if (!scsi->cmd_timer) {
spock_log("Callback to reset\n");
scsi->cmd_timer = 1;
}
spock_add_to_period(scsi, 1);
}
break;
case SCSI_STATE_SEND_COMMAND:
sd = &scsi_devices[scsi->bus][scsi->cdb_id];
memset(scsi->temp_cdb, 0x00, 12);
if (scsi->cdb_len < 12) {
memcpy(scsi->temp_cdb, scsi->cdb,
scsi->cdb_len);
spock_add_to_period(scsi, scsi->cdb_len);
} else {
memcpy(scsi->temp_cdb, scsi->cdb,
12);
spock_add_to_period(scsi, 12);
}
scsi->data_ptr = scb->sge.sys_buf_addr;
scsi->data_len = scb->sge.sys_buf_byte_count;
if (scb->enable & 0x400)
sd->buffer_length = -1;
else
sd->buffer_length = spock_get_len(scsi, scb);
scsi_device_command_phase0(sd, scsi->temp_cdb);
spock_log("SCSI ID %i: Current CDB[0] = %02x, LUN = %i, data len = %i, max len = %i, phase val = %02x\n", scsi->cdb_id, scsi->temp_cdb[0], scsi->temp_cdb[1] >> 5, sd->buffer_length, spock_get_len(scsi, scb), sd->phase);
if (sd->phase != SCSI_PHASE_STATUS && sd->buffer_length > 0) {
p = scsi_device_get_callback(sd);
if (p <= 0.0)
spock_add_to_period(scsi, sd->buffer_length);
else
scsi->media_period += p;
if (scb->enable & ENABLE_PT) {
int32_t buflen = sd->buffer_length;
int sg_pos = 0;
uint32_t DataTx = 0;
uint32_t Address;
if (scb->sge.sys_buf_byte_count > 0) {
2023-05-29 01:30:51 -04:00
for (uint32_t c = 0; c < scsi->data_len; c += 8) {
2022-09-18 17:17:15 -04:00
spock_rd_sge(scsi, scsi->data_ptr + c, &scb->sge);
Address = scb->sge.sys_buf_addr;
DataTx = MIN((int) scb->sge.sys_buf_byte_count, buflen);
if ((sd->phase == SCSI_PHASE_DATA_IN) && DataTx) {
spock_log("Writing S/G segment %i: length %i, pointer %08X\n", c, DataTx, Address);
dma_bm_write(Address, &sd->sc->temp_buffer[sg_pos], DataTx, 2);
} else if ((sd->phase == SCSI_PHASE_DATA_OUT) && DataTx) {
spock_log("Reading S/G segment %i: length %i, pointer %08X\n", c, DataTx, Address);
dma_bm_read(Address, &sd->sc->temp_buffer[sg_pos], DataTx, 2);
}
sg_pos += scb->sge.sys_buf_byte_count;
buflen -= scb->sge.sys_buf_byte_count;
if (buflen < 0)
buflen = 0;
}
}
} else {
spock_log("Normal Transfer\n");
if (sd->phase == SCSI_PHASE_DATA_IN) {
dma_bm_write(scsi->data_ptr, sd->sc->temp_buffer, MIN(sd->buffer_length, (int) scsi->data_len), 2);
} else if (sd->phase == SCSI_PHASE_DATA_OUT)
dma_bm_read(scsi->data_ptr, sd->sc->temp_buffer, MIN(sd->buffer_length, (int) scsi->data_len), 2);
}
scsi_device_command_phase1(sd);
}
scsi->last_status = sd->status;
scsi->scsi_state = SCSI_STATE_END_PHASE;
break;
case SCSI_STATE_END_PHASE:
scsi->scsi_state = SCSI_STATE_IDLE;
spock_log("State to idle, cmd timer %d\n", scsi->cmd_timer);
if (!scsi->cmd_timer) {
scsi->cmd_timer = 1;
}
spock_add_to_period(scsi, 1);
break;
}
}
static void
spock_callback(void *priv)
{
2022-09-18 17:17:15 -04:00
double period;
spock_t *scsi = (spock_t *) priv;
scb_t *scb = &scsi->scb;
scsi->temp_period = 0;
scsi->media_period = 0.0;
if (scsi->cmd_timer) {
scsi->cmd_timer--;
if (!scsi->cmd_timer) {
spock_execute_cmd(scsi, scb);
}
}
if (scsi->attention_wait && (scsi->scb_state == 0 || (scsi->attention_pending & 0xf0) == 0xe0)) {
scsi->attention_wait--;
if (!scsi->attention_wait) {
scsi->attention = scsi->attention_pending;
scsi->status &= ~STATUS_BUSY;
scsi->cir[0] = scsi->cir_pending[0];
scsi->cir[1] = scsi->cir_pending[1];
scsi->cir[2] = scsi->cir_pending[2];
scsi->cir[3] = scsi->cir_pending[3];
scsi->cir_status = 0;
spock_log("SCSI attention = %02x.\n", scsi->attention_pending);
2022-09-18 17:17:15 -04:00
switch (scsi->attention >> 4) {
case 1: /*Immediate command*/
scsi->cmd_status = 0x0a;
scsi->command = scsi->cir[0] | (scsi->cir[1] << 8) | (scsi->cir[2] << 16) | (scsi->cir[3] << 24);
switch (scsi->command & CMD_MASK) {
case CMD_ASSIGN:
case CMD_DMA_PACING_CONTROL:
case CMD_FEATURE_CONTROL:
case CMD_INVALID_412:
case CMD_RESET:
spock_process_imm_cmd(scsi);
break;
2023-06-09 23:46:54 -04:00
default:
break;
2022-09-18 17:17:15 -04:00
}
break;
case 3:
case 4:
case 0x0f: /*Start SCB*/
scsi->cmd_status = 1;
scsi->scb_addr = scsi->cir[0] | (scsi->cir[1] << 8) | (scsi->cir[2] << 16) | (scsi->cir[3] << 24);
scsi->scb_id = scsi->attention & 0x0f;
scsi->cmd_timer = SPOCK_TIME * 2;
spock_log("Start SCB at ID = %d, attention = %02x\n", scsi->scb_id, scsi->attention >> 4);
2022-09-18 17:17:15 -04:00
scsi->scb_state = 1;
break;
case 5: /*Invalid*/
case 0x0a: /*Invalid*/
scsi->in_invalid = 1;
scsi->cmd_timer = SPOCK_TIME * 2;
break;
case 0x0e: /*EOI*/
scsi->irq_status = 0;
spock_clear_irq(scsi, scsi->attention & 0x0f);
2022-09-18 17:17:15 -04:00
break;
2023-06-09 23:46:54 -04:00
default:
break;
2022-09-18 17:17:15 -04:00
}
}
}
spock_process_scsi(scsi, scb);
period = 0.2 * ((double) scsi->temp_period);
timer_on(&scsi->callback_timer, (scsi->media_period + period + 10.0), 0);
spock_log("Temporary period: %lf us (%" PRIi64 " periods)\n", scsi->callback_timer.period, scsi->temp_period);
}
2022-02-20 02:26:27 -05:00
static void
spock_mca_write(int port, uint8_t val, void *priv)
{
2022-09-18 17:17:15 -04:00
spock_t *scsi = (spock_t *) priv;
2022-09-18 17:17:15 -04:00
if (port < 0x102)
return;
2022-09-18 17:17:15 -04:00
io_removehandler((((scsi->pos_regs[2] >> 1) & 7) * 8) + 0x3540, 0x0008, spock_read, spock_readw, NULL, spock_write, spock_writew, NULL, scsi);
mem_mapping_disable(&scsi->bios_rom.mapping);
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
scsi->pos_regs[port & 7] = val;
2022-09-18 17:17:15 -04:00
scsi->adapter_id = (scsi->pos_regs[3] & 0xe0) >> 5;
2022-09-18 17:17:15 -04:00
if (scsi->pos_regs[2] & 1) {
io_sethandler((((scsi->pos_regs[2] >> 1) & 7) * 8) + 0x3540, 0x0008, spock_read, spock_readw, NULL, spock_write, spock_writew, NULL, scsi);
if ((scsi->pos_regs[2] & 0xf0) != 0xf0) {
2022-09-18 17:17:15 -04:00
mem_mapping_set_addr(&scsi->bios_rom.mapping, ((scsi->pos_regs[2] >> 4) * 0x2000) + 0xc0000, 0x8000);
mem_mapping_enable(&scsi->bios_rom.mapping);
}
2022-09-18 17:17:15 -04:00
}
}
2022-02-20 02:26:27 -05:00
static uint8_t
spock_mca_read(int port, void *priv)
{
2022-09-18 17:17:15 -04:00
spock_t *scsi = (spock_t *) priv;
2022-09-18 17:17:15 -04:00
return scsi->pos_regs[port & 7];
}
static uint8_t
spock_mca_feedb(void *priv)
{
2022-09-18 17:17:15 -04:00
spock_t *scsi = (spock_t *) priv;
2022-09-18 17:17:15 -04:00
return (scsi->pos_regs[2] & 0x01);
}
static void
spock_mca_reset(void *priv)
{
2022-09-18 17:17:15 -04:00
spock_t *scsi = (spock_t *) priv;
scsi->in_reset = 2;
scsi->cmd_timer = SPOCK_TIME * 50;
scsi->status = STATUS_BUSY;
scsi->scsi_state = SCSI_STATE_IDLE;
scsi->scb_state = 0;
scsi->in_invalid = 0;
scsi->attention_wait = 0;
scsi->basic_ctrl = 0;
/* Reset all devices on controller reset. */
2023-05-29 01:30:51 -04:00
for (uint8_t i = 0; i < 8; i++) {
2022-09-18 17:17:15 -04:00
scsi_device_reset(&scsi_devices[scsi->bus][i]);
scsi->present[i] = 0;
}
}
static void *
2023-06-09 23:46:54 -04:00
spock_init(UNUSED(const device_t *info))
{
2022-09-18 17:17:15 -04:00
spock_t *scsi = malloc(sizeof(spock_t));
memset(scsi, 0x00, sizeof(spock_t));
2022-09-18 17:17:15 -04:00
scsi->bus = scsi_get_bus();
2022-09-18 17:17:15 -04:00
scsi->irq = 14;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
scsi->bios_ver = device_get_config_int("bios_ver");
2022-02-20 02:26:27 -05:00
switch (scsi->bios_ver) {
2022-09-18 17:17:15 -04:00
case 1:
rom_init_interleaved(&scsi->bios_rom, SPOCK_U68_1991_ROM, SPOCK_U69_1991_ROM,
2022-09-18 17:17:15 -04:00
0xc8000, 0x8000, 0x7fff, 0x4000, MEM_MAPPING_EXTERNAL);
break;
2022-09-18 17:17:15 -04:00
case 0:
rom_init_interleaved(&scsi->bios_rom, SPOCK_U68_1990_ROM, SPOCK_U69_1990_ROM,
2022-09-18 17:17:15 -04:00
0xc8000, 0x8000, 0x7fff, 0x4000, MEM_MAPPING_EXTERNAL);
break;
2023-06-09 23:46:54 -04:00
default:
break;
}
mem_mapping_disable(&scsi->bios_rom.mapping);
scsi->pos_regs[0] = 0xff;
scsi->pos_regs[1] = 0x8e;
2022-09-18 17:17:15 -04:00
mca_add(spock_mca_read, spock_mca_write, spock_mca_feedb, spock_mca_reset, scsi);
2022-09-18 17:17:15 -04:00
scsi->in_reset = 2;
scsi->cmd_timer = SPOCK_TIME * 50;
scsi->status = STATUS_BUSY;
2023-05-29 01:30:51 -04:00
for (uint8_t c = 0; c < (SCSI_ID_MAX - 1); c++) {
scsi->dev_id[c].phys_id = -1;
}
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
scsi->dev_id[SCSI_ID_MAX - 1].phys_id = scsi->adapter_id;
2022-09-18 17:17:15 -04:00
timer_add(&scsi->callback_timer, spock_callback, scsi, 1);
scsi->callback_timer.period = 10.0;
timer_set_delay_u64(&scsi->callback_timer, (uint64_t) (scsi->callback_timer.period * ((double) TIMER_USEC)));
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
return scsi;
}
2022-02-20 02:26:27 -05:00
static void
2023-06-09 23:46:54 -04:00
spock_close(void *priv)
{
2023-06-09 23:46:54 -04:00
spock_t *scsi = (spock_t *) priv;
2022-02-20 02:26:27 -05:00
2022-09-18 17:17:15 -04:00
if (scsi) {
free(scsi);
scsi = NULL;
}
}
2022-02-20 02:26:27 -05:00
static int
spock_available(void)
{
2022-09-18 17:17:15 -04:00
return rom_present(SPOCK_U68_1991_ROM) && rom_present(SPOCK_U69_1991_ROM) && rom_present(SPOCK_U68_1990_ROM) && rom_present(SPOCK_U69_1990_ROM);
}
static const device_config_t spock_rom_config[] = {
2022-09-18 17:17:15 -04:00
// clang-format off
2022-02-26 23:31:28 -05:00
{
2022-04-03 02:08:21 -04:00
.name = "bios_ver",
2023-01-08 14:25:18 +01:00
.description = "BIOS Version",
.type = CONFIG_SELECTION,
.default_string = "",
.default_int = 1,
.file_filter = "",
.spinner = { 0 },
2022-04-03 02:08:21 -04:00
.selection = {
{ .description = "1991 BIOS (>1GB)", .value = 1 },
{ .description = "1990 BIOS", .value = 0 },
{ .description = "" }
},
2022-02-26 23:31:28 -05:00
},
2022-04-03 02:08:21 -04:00
{ .name = "", .description = "", .type = CONFIG_END }
2023-02-28 23:24:58 -05:00
// clang-format on
};
2022-03-13 09:54:10 -04:00
const device_t spock_device = {
2022-09-18 17:17:15 -04:00
.name = "IBM PS/2 SCSI Adapter (Spock)",
2022-03-13 09:54:10 -04:00
.internal_name = "spock",
2022-09-18 17:17:15 -04:00
.flags = DEVICE_MCA,
.local = 0,
.init = spock_init,
.close = spock_close,
.reset = NULL,
2022-03-13 09:54:10 -04:00
{ .available = spock_available },
.speed_changed = NULL,
2022-09-18 17:17:15 -04:00
.force_redraw = NULL,
.config = spock_rom_config
};