WinUAE/a2091.c
2010-02-22 21:46:46 +02:00

1332 lines
29 KiB
C
Executable File

/*
* UAE - The Un*x Amiga Emulator
*
* A590/A2091/A3000/CDTV SCSI expansion (DMAC/SuperDMAC + WD33C93) emulation
*
* Copyright 2007 Toni Wilen
*
*/
#define A2091_DEBUG 0
#define A3000_DEBUG 0
#define WD33C93_DEBUG 0
#include "sysconfig.h"
#include "sysdeps.h"
#include "options.h"
#include "uae.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "debug.h"
#include "scsi.h"
#include "a2091.h"
#include "blkdev.h"
#include "gui.h"
#include "zfile.h"
#include "filesys.h"
#include "autoconf.h"
#include "cdtv.h"
#define ROM_VECTOR 0x2000
#define ROM_OFFSET 0x2000
/* SuperDMAC CNTR bits. */
#define SCNTR_TCEN (1<<5)
#define SCNTR_PREST (1<<4)
#define SCNTR_PDMD (1<<3)
#define SCNTR_INTEN (1<<2)
#define SCNTR_DDIR (1<<1)
#define SCNTR_IO_DX (1<<0)
/* DMAC CNTR bits. */
#define CNTR_TCEN (1<<7)
#define CNTR_PREST (1<<6)
#define CNTR_PDMD (1<<5)
#define CNTR_INTEN (1<<4)
#define CNTR_DDIR (1<<3)
/* ISTR bits. */
#define ISTR_INTX (1<<8) /* XT/AT Interrupt pending */
#define ISTR_INT_F (1<<7) /* Interrupt Follow */
#define ISTR_INTS (1<<6) /* SCSI Peripheral Interrupt */
#define ISTR_E_INT (1<<5) /* End-Of-Process Interrupt */
#define ISTR_INT_P (1<<4) /* Interrupt Pending */
#define ISTR_UE_INT (1<<3) /* Under-Run FIFO Error Interrupt */
#define ISTR_OE_INT (1<<2) /* Over-Run FIFO Error Interrupt */
#define ISTR_FF_FLG (1<<1) /* FIFO-Full Flag */
#define ISTR_FE_FLG (1<<0) /* FIFO-Empty Flag */
/* wd register names */
#define WD_OWN_ID 0x00
#define WD_CONTROL 0x01
#define WD_TIMEOUT_PERIOD 0x02
#define WD_CDB_1 0x03
#define WD_CDB_2 0x04
#define WD_CDB_3 0x05
#define WD_CDB_4 0x06
#define WD_CDB_5 0x07
#define WD_CDB_6 0x08
#define WD_CDB_7 0x09
#define WD_CDB_8 0x0a
#define WD_CDB_9 0x0b
#define WD_CDB_10 0x0c
#define WD_CDB_11 0x0d
#define WD_CDB_12 0x0e
#define WD_TARGET_LUN 0x0f
#define WD_COMMAND_PHASE 0x10
#define WD_SYNCHRONOUS_TRANSFER 0x11
#define WD_TRANSFER_COUNT_MSB 0x12
#define WD_TRANSFER_COUNT 0x13
#define WD_TRANSFER_COUNT_LSB 0x14
#define WD_DESTINATION_ID 0x15
#define WD_SOURCE_ID 0x16
#define WD_SCSI_STATUS 0x17
#define WD_COMMAND 0x18
#define WD_DATA 0x19
#define WD_QUEUE_TAG 0x1a
#define WD_AUXILIARY_STATUS 0x1f
/* WD commands */
#define WD_CMD_RESET 0x00
#define WD_CMD_ABORT 0x01
#define WD_CMD_ASSERT_ATN 0x02
#define WD_CMD_NEGATE_ACK 0x03
#define WD_CMD_DISCONNECT 0x04
#define WD_CMD_RESELECT 0x05
#define WD_CMD_SEL_ATN 0x06
#define WD_CMD_SEL 0x07
#define WD_CMD_SEL_ATN_XFER 0x08
#define WD_CMD_SEL_XFER 0x09
#define WD_CMD_RESEL_RECEIVE 0x0a
#define WD_CMD_RESEL_SEND 0x0b
#define WD_CMD_WAIT_SEL_RECEIVE 0x0c
#define WD_CMD_TRANS_ADDR 0x18
#define WD_CMD_TRANS_INFO 0x20
#define WD_CMD_TRANSFER_PAD 0x21
#define WD_CMD_SBT_MODE 0x80
#define CSR_MSGIN 0x20
#define CSR_SDP 0x21
#define CSR_SEL_ABORT 0x22
#define CSR_RESEL_ABORT 0x25
#define CSR_RESEL_ABORT_AM 0x27
#define CSR_ABORT 0x28
/* successful completion interrupts */
#define CSR_RESELECT 0x10
#define CSR_SELECT 0x11
#define CSR_SEL_XFER_DONE 0x16
#define CSR_XFER_DONE 0x18
/* terminated interrupts */
#define CSR_INVALID 0x40
#define CSR_UNEXP_DISC 0x41
#define CSR_TIMEOUT 0x42
#define CSR_PARITY 0x43
#define CSR_PARITY_ATN 0x44
#define CSR_BAD_STATUS 0x45
#define CSR_UNEXP 0x48
/* service required interrupts */
#define CSR_RESEL 0x80
#define CSR_RESEL_AM 0x81
#define CSR_DISC 0x85
#define CSR_SRV_REQ 0x88
/* SCSI Bus Phases */
#define PHS_DATA_OUT 0x00
#define PHS_DATA_IN 0x01
#define PHS_COMMAND 0x02
#define PHS_STATUS 0x03
#define PHS_MESS_OUT 0x06
#define PHS_MESS_IN 0x07
static int configured;
static uae_u8 dmacmemory[100];
static uae_u8 *rom;
static int rombankswitcher, rombank;
static int rom_size, rom_mask;
static uae_u32 dmac_istr, dmac_cntr;
static uae_u32 dmac_dawr;
static uae_u32 dmac_acr;
static uae_u32 dmac_wtc;
static int dmac_dma;
static uae_u8 sasr, scmd, auxstatus;
static int wd_used;
static int wd_phase, wd_next_phase, wd_busy;
static int wd_dataoffset, wd_tc;
static uae_u8 wd_data[32];
static int superdmac;
static int scsidelay_irq;
static uae_u8 scsidelay_status;
static int wd33c93a = 1;
struct scsi_data *scsis[8];
uae_u8 wdregs[32];
static int isirq (void)
{
if (superdmac) {
if ((dmac_cntr & SCNTR_INTEN) && (dmac_istr & (ISTR_INTS | ISTR_E_INT)))
return 1;
} else {
if ((dmac_cntr & CNTR_INTEN) && (dmac_istr & (ISTR_INTS | ISTR_E_INT)))
return 1;
}
return 0;
}
void rethink_a2091 (void)
{
if (currprefs.cs_cdtvscsi)
return;
if (isirq()) {
uae_int_requested |= 2;
#if A2091_DEBUG > 2 || A3000_DEBUG > 2
write_log (L"Interrupt_RETHINK\n");
#endif
} else {
uae_int_requested &= ~2;
}
}
static void INT2 (void)
{
int irq = 0;
if (currprefs.cs_cdtvscsi)
return;
if (!(auxstatus & 0x80))
return;
dmac_istr |= ISTR_INTS;
if (isirq ())
uae_int_requested |= 2;
}
static void dmac_start_dma (void)
{
#if A3000_DEBUG > 0 || A2091_DEBUG > 0
write_log (L"DMAC DMA started, ADDR=%08X, LEN=%08X words\n", dmac_acr, dmac_wtc);
#endif
dmac_dma = 1;
}
static void dmac_stop_dma (void)
{
dmac_dma = 0;
dmac_istr &= ~ISTR_E_INT;
}
static void dmac_reset (void)
{
#if WD33C93_DEBUG > 0
if (superdmac)
write_log (L"A3000 %s SCSI reset\n", WD33C93);
else
write_log (L"A2091 %s SCSI reset\n", WD33C93);
#endif
}
static void incsasr (int w)
{
if (sasr == WD_AUXILIARY_STATUS || sasr == WD_DATA || sasr == WD_COMMAND)
return;
if (w && sasr == WD_SCSI_STATUS)
return;
sasr++;
sasr &= 0x1f;
}
static void dmac_cint (void)
{
dmac_istr = 0;
rethink_a2091();
}
static void doscsistatus (void)
{
wdregs[WD_SCSI_STATUS] = scsidelay_status;
auxstatus |= 0x80;
#if WD33C93_DEBUG > 0
write_log (L"%s STATUS=%02X\n", WD33C93, scsidelay_status);
#endif
if (currprefs.cs_cdtvscsi) {
cdtv_scsi_int ();
return;
}
if (!currprefs.cs_a2091 && currprefs.cs_mbdmac != 1)
return;
INT2();
#if A2091_DEBUG > 2 || A3000_DEBUG > 2
write_log (L"Interrupt\n");
#endif
}
void scsi_hsync (void)
{
if (scsidelay_irq == 1) {
scsidelay_irq = 0;
doscsistatus();
return;
}
if (scsidelay_irq > 1)
scsidelay_irq--;
}
static void set_status (uae_u8 status, int quick)
{
scsidelay_irq = quick <= 2 ? 2 : quick;
scsidelay_status = status;
}
static TCHAR *scsitostring (void)
{
static TCHAR buf[200];
TCHAR *p;
int i;
p = buf;
p[0] = 0;
for (i = 0; i < wd_tc && i < sizeof wd_data; i++) {
if (i > 0) {
_tcscat (p, L".");
p++;
}
_stprintf (p, L"%02X", wd_data[i]);
p += _tcslen (p);
}
return buf;
}
static void wd_cmd_sel_xfer (void)
{
int phase = wdregs[WD_COMMAND_PHASE];
#if WD33C93_DEBUG > 0
write_log (L"* %s select and transfer, ID=%d phase=%02X\n", WD33C93, wdregs[WD_DESTINATION_ID] & 0x7, phase);
#endif
if (!SCSIID) {
set_status (CSR_TIMEOUT, 0);
return;
}
SCSIID->buffer[0] = 0;
if (phase >= 0x46) {
phase = 0x50;
wdregs[WD_TARGET_LUN] = SCSIID->status;
SCSIID->buffer[0] = SCSIID->status;
}
wdregs[WD_COMMAND_PHASE] = phase;
wd_phase = CSR_XFER_DONE | PHS_MESS_IN;
set_status (wd_phase, 1);
}
static void do_dma (void)
{
if (currprefs.cs_cdtvscsi)
cdtv_getdmadata (&dmac_acr);
if (SCSIID->direction == 0) {
write_log (L"%s DMA but no data!?\n", WD33C93);
} else if (SCSIID->direction < 0) {
for (;;) {
uae_u8 v;
int status = scsi_receive_data (SCSIID, &v);
put_byte (dmac_acr, v);
if (wd_dataoffset < sizeof wd_data)
wd_data[wd_dataoffset++] = v;
dmac_acr++;
if (status)
break;
}
} else if (SCSIID->direction > 0) {
for (;;) {
int status;
uae_u8 v = get_byte (dmac_acr);
if (wd_dataoffset < sizeof wd_data)
wd_data[wd_dataoffset++] = v;
status = scsi_send_data (SCSIID, v);
dmac_acr++;
if (status)
break;
}
}
}
static void wd_do_transfer_out (void)
{
#if WD33C93_DEBUG > 0
write_log (L"%s SCSI O [%02X] %d/%d %s\n", WD33C93, wdregs[WD_COMMAND_PHASE], wd_dataoffset, wd_tc, scsitostring());
#endif
if (wdregs[WD_COMMAND_PHASE] == 0x11) {
wdregs[WD_COMMAND_PHASE] = 0x20;
wd_phase = CSR_XFER_DONE | PHS_COMMAND;
} else if (wdregs[WD_COMMAND_PHASE] == 0x30) {
/* command was sent */
SCSIID->direction = scsi_data_dir (SCSIID);
if (SCSIID->direction > 0) {
/* if write command, need to wait for data */
wd_phase = CSR_XFER_DONE | PHS_DATA_OUT;
wdregs[WD_COMMAND_PHASE] = 0x46;
} else {
scsi_emulate_cmd (SCSIID);
if (SCSIID->data_len <= 0 || SCSIID->status != 0 || SCSIID->direction == 0) {
wd_phase = CSR_XFER_DONE | PHS_STATUS;
wdregs[WD_COMMAND_PHASE] = 0x47;
} else {
wd_phase = CSR_XFER_DONE | PHS_DATA_IN;
wdregs[WD_COMMAND_PHASE] = 0x3f;
}
}
} else if (wdregs[WD_COMMAND_PHASE] == 0x46) {
if (SCSIID->direction > 0) {
/* data was sent */
scsi_emulate_cmd (SCSIID);
wd_phase = CSR_XFER_DONE | PHS_STATUS;
}
wdregs[WD_COMMAND_PHASE] = 0x47;
}
wd_dataoffset = 0;
set_status (wd_phase, SCSIID->direction ? 0 : 1);
wd_busy = 0;
}
static void wd_do_transfer_in (void)
{
#if WD33C93_DEBUG > 0
write_log (L"%s SCSI I [%02X] %d/%d %s\n", WD33C93, wdregs[WD_COMMAND_PHASE], wd_dataoffset, wd_tc, scsitostring());
#endif
wd_dataoffset = 0;
if (wdregs[WD_COMMAND_PHASE] >= 0x36 && wdregs[WD_COMMAND_PHASE] < 0x47) {
wdregs[WD_COMMAND_PHASE] = 0x47;
wd_phase = CSR_XFER_DONE | PHS_STATUS;
} else if (wdregs[WD_COMMAND_PHASE] == 0x47) {
wdregs[WD_COMMAND_PHASE] = 0x50;
wd_phase = CSR_XFER_DONE | PHS_MESS_IN;
} else if (wdregs[WD_COMMAND_PHASE] == 0x50) {
wdregs[WD_COMMAND_PHASE] = 0x60;
wd_phase = CSR_DISC;
}
set_status (wd_phase, 1);
wd_busy = 0;
SCSIID->direction = 0;
}
static void wd_cmd_sel_xfer_atn (void)
{
int i, tmp_tc;
tmp_tc = wdregs[WD_TRANSFER_COUNT_LSB] | (wdregs[WD_TRANSFER_COUNT] << 8) | (wdregs[WD_TRANSFER_COUNT_MSB] << 16);
#if WD33C93_DEBUG > 0
write_log (L"* %s select and transfer with atn, ID=%d PHASE=%02X TC=%d\n",
WD33C93, wdregs[WD_DESTINATION_ID] & 0x7, wdregs[WD_COMMAND_PHASE], tmp_tc);
#endif
if (wdregs[WD_COMMAND] & 0x80)
wd_tc = 1;
if (!SCSIID) {
set_status (CSR_TIMEOUT, 0);
return;
}
SCSIID->buffer[0] = 0;
SCSIID->direction = 0;
if (wdregs[WD_COMMAND_PHASE] <= 0x30) {
wd_tc = 6;
wd_dataoffset = 0;
scsi_start_transfer (SCSIID, 6);
for (i = 0; i < wd_tc; i++) {
uae_u8 b = wdregs[3 + i];
wd_data[i] = b;
scsi_send_data (SCSIID, b);
wd_dataoffset++;
}
// command
SCSIID->direction = (wdregs[WD_SOURCE_ID] & 0x20) ? 1 : -1;
wdregs[WD_COMMAND_PHASE] = 0x30;
wd_do_transfer_out ();
wdregs[WD_COMMAND_PHASE] = 0x36;
}
if (wdregs[WD_COMMAND_PHASE] <= 0x41) {
wd_tc = tmp_tc;
wd_dataoffset = 0;
wdregs[WD_COMMAND_PHASE] = 0x45;
if (wd_tc == 0) {
if (SCSIID->direction != 0 && SCSIID->status == 0) {
wd_phase = CSR_UNEXP;
if (SCSIID->direction < 0)
wd_phase |= PHS_DATA_IN;
else
wd_phase |= PHS_DATA_OUT;
set_status (wd_phase, 1);
return;
}
}
if ((wdregs[WD_CONTROL] >> 5) == 4) {
if (wd_phase == (CSR_XFER_DONE | PHS_DATA_IN))
do_dma ();
else if(wd_phase == (CSR_XFER_DONE | PHS_DATA_OUT))
do_dma ();
}
}
wdregs[WD_COMMAND_PHASE] = 0x60;
wdregs[WD_TARGET_LUN] = SCSIID->status;
SCSIID->buffer[0] = SCSIID->status;
wd_phase = CSR_SEL_XFER_DONE;
set_status (wd_phase, 0);
}
static void wd_cmd_trans_info (void)
{
if (wdregs[WD_COMMAND_PHASE] == 0x47)
SCSIID->buffer[0] = SCSIID->status;
if (wdregs[WD_COMMAND_PHASE] == 0x20)
wdregs[WD_COMMAND_PHASE] = 0x30;
wd_busy = 1;
wd_tc = wdregs[WD_TRANSFER_COUNT_LSB] | (wdregs[WD_TRANSFER_COUNT] << 8) | (wdregs[WD_TRANSFER_COUNT_MSB] << 16);
if (wdregs[WD_COMMAND] & 0x80)
wd_tc = 1;
wd_dataoffset = 0;
#if WD33C93_DEBUG > 0
write_log (L"* %s transfer info phase=%02x len=%d dma=%d\n", WD33C93, wdregs[WD_COMMAND_PHASE], wd_tc, wdregs[WD_CONTROL] >> 5);
#endif
scsi_start_transfer (SCSIID, wd_tc);
if (wdregs[WD_COMMAND_PHASE] >= 0x36 && wdregs[WD_COMMAND_PHASE] <= 0x3f)
wdregs[WD_COMMAND_PHASE] = 0x45;
if ((wdregs[WD_CONTROL] >> 5) == 4) {
do_dma ();
if (SCSIID->direction < 0)
wd_do_transfer_in ();
else if (SCSIID->direction > 0)
wd_do_transfer_out ();
SCSIID->direction = 0;
dmac_dma = 0;
}
}
static void wd_cmd_sel_atn (void)
{
#if WD33C93_DEBUG > 0
write_log (L"* %s select with atn, ID=%d\n", WD33C93, wdregs[WD_DESTINATION_ID] & 0x7);
#endif
wd_phase = 0;
wdregs[WD_COMMAND_PHASE] = 0;
if (SCSIID) {
wd_phase = CSR_SELECT;
set_status (wd_phase, 1);
wdregs[WD_COMMAND_PHASE] = 0x10;
return;
}
set_status (CSR_TIMEOUT, 1000);
}
static void wd_cmd_reset (void)
{
int i;
#if WD33C93_DEBUG > 0
write_log (L"%s reset\n", WD33C93);
#endif
for (i = 1; i < 0x16; i++)
wdregs[i] = 0;
wdregs[0x18] = 0;
if (!wd33c93a)
wdregs[0] &= ~(0x08 | 0x10);
set_status ((wdregs[0] & 0x10) ? 1 : 0, 1);
}
static void wd_cmd_abort (void)
{
#if WD33C93_DEBUG > 0
write_log (L"%s abort\n", WD33C93);
#endif
set_status (CSR_SEL_ABORT, 0);
}
static int writeonlyreg (int reg)
{
if (reg == WD_SCSI_STATUS)
return 1;
return 0;
}
void wdscsi_put (uae_u8 d)
{
#if WD33C93_DEBUG > 1
if (WD33C93_DEBUG > 3 || sasr != WD_DATA)
write_log (L"W %s REG %02X (%d) = %02X (%d) PC=%08X\n", WD33C93, sasr, sasr, d, d, M68K_GETPC);
#endif
if (!writeonlyreg (sasr))
wdregs[sasr] = d;
if (!wd_used) {
wd_used = 1;
write_log (L"%s in use\n", WD33C93);
}
if (sasr == WD_COMMAND_PHASE) {
#if WD33C93_DEBUG > 0
write_log (L"%s PHASE=%02X\n", WD33C93, d);
#endif
} else if (sasr == WD_DATA) {
if (wd_dataoffset < sizeof wd_data)
wd_data[wd_dataoffset] = wdregs[sasr];
wd_dataoffset++;
if (scsi_send_data (SCSIID, wdregs[sasr]))
wd_do_transfer_out ();
} else if (sasr == WD_COMMAND) {
switch (d & 0x7f)
{
case WD_CMD_RESET:
wd_cmd_reset ();
break;
break;
case WD_CMD_SEL_ATN:
wd_cmd_sel_atn ();
break;
case WD_CMD_SEL_ATN_XFER:
wd_cmd_sel_xfer_atn ();
break;
case WD_CMD_SEL_XFER:
wd_cmd_sel_xfer ();
break;
case WD_CMD_TRANS_INFO:
wd_cmd_trans_info ();
break;
default:
write_log (L"%s unimplemented/unknown command %02X\n", WD33C93, d);
break;
}
}
incsasr(1);
}
void wdscsi_sasr (uae_u8 b)
{
sasr = b;
}
uae_u8 wdscsi_getauxstatus (void)
{
return (auxstatus & 0x80) | (wd_busy ? 0x20 : 0) | (wd_busy ? 0x01 : 0);
}
uae_u8 wdscsi_get (void)
{
uae_u8 v, osasr = sasr;
v = wdregs[sasr];
if (sasr == WD_DATA) {
int status = scsi_receive_data (SCSIID, &v);
if (wd_dataoffset < sizeof wd_data)
wd_data[wd_dataoffset] = v;
wd_dataoffset++;
wdregs[sasr] = v;
if (status)
wd_do_transfer_in ();
} else if (sasr == WD_SCSI_STATUS) {
uae_int_requested &= ~2;
auxstatus &= ~0x80;
cdtv_scsi_clear_int ();
dmac_istr &= ~ISTR_INTS;
if (wdregs[WD_COMMAND_PHASE] == 0x10) {
wdregs[WD_COMMAND_PHASE] = 0x11;
wd_phase = CSR_SRV_REQ | PHS_MESS_OUT;
set_status (wd_phase, 1);
}
}
incsasr (0);
#if WD33C93_DEBUG > 1
if (WD33C93_DEBUG > 3 || osasr != WD_DATA)
write_log (L"R %s REG %02X (%d) = %02X (%d) PC=%08X\n", WD33C93, osasr, osasr, v, v, M68K_GETPC);
#endif
return v;
}
static uae_u32 dmac_bget2 (uaecptr addr)
{
uae_u32 v = 0;
if (addr < 0x40)
return dmacmemory[addr];
if (addr >= ROM_OFFSET) {
if (rom) {
int off = addr & rom_mask;
if (rombankswitcher && (addr & 0xffe0) == ROM_OFFSET)
rombank = (addr & 0x02) >> 1;
off += rombank * rom_size;
return rom[off];
}
return 0;
}
switch (addr)
{
case 0x41:
v = dmac_istr;
if (v)
v |= ISTR_INT_P;
dmac_istr &= ~0xf;
break;
case 0x43:
v = dmac_cntr;
break;
case 0x91:
v = wdscsi_getauxstatus ();
break;
case 0x93:
v = wdscsi_get ();
break;
case 0xc1:
v = 0xf8 | (1 << 0) | (1 << 1) | (1 << 2); // bits 0-2 = dip-switches
break;
/* XT IO */
case 0xa1:
case 0xa3:
case 0xa5:
case 0xa7:
case 0xc3:
case 0xc5:
case 0xc7:
v = 0xff;
break;
case 0xe0:
case 0xe1:
if (!dmac_dma)
dmac_start_dma ();
break;
case 0xe2:
case 0xe3:
dmac_stop_dma ();
break;
case 0xe4:
case 0xe5:
dmac_cint ();
break;
case 0xe8:
case 0xe9:
/* FLUSH */
dmac_istr |= ISTR_FE_FLG;
break;
}
#if A2091_DEBUG > 0
write_log (L"dmac_bget %04X=%02X PC=%08X\n", addr, v, M68K_GETPC);
#endif
return v;
}
static void dmac_bput2 (uaecptr addr, uae_u32 b)
{
if (addr < 0x40)
return;
if (addr >= ROM_OFFSET)
return;
switch (addr)
{
case 0x43:
dmac_cntr = b;
if (dmac_cntr & CNTR_PREST)
dmac_reset ();
break;
case 0x80:
dmac_wtc &= 0x00ffffff;
dmac_wtc |= b << 24;
break;
case 0x81:
dmac_wtc &= 0xff00ffff;
dmac_wtc |= b << 16;
break;
case 0x82:
dmac_wtc &= 0xffff00ff;
dmac_wtc |= b << 8;
break;
case 0x83:
dmac_wtc &= 0xffffff00;
dmac_wtc |= b << 0;
break;
case 0x84:
dmac_acr &= 0x00ffffff;
dmac_acr |= b << 24;
break;
case 0x85:
dmac_acr &= 0xff00ffff;
dmac_acr |= b << 16;
break;
case 0x86:
dmac_acr &= 0xffff00ff;
dmac_acr |= b << 8;
break;
case 0x87:
dmac_acr &= 0xffffff01;
dmac_acr |= (b & ~ 1) << 0;
break;
case 0x8e:
dmac_dawr &= 0x00ff;
dmac_dawr |= b << 8;
break;
case 0x8f:
dmac_dawr &= 0xff00;
dmac_dawr |= b << 0;
break;
case 0x91:
wdscsi_sasr (b);
break;
case 0x93:
wdscsi_put (b);
break;
case 0xe0:
case 0xe1:
if (!dmac_dma)
dmac_start_dma ();
break;
case 0xe2:
case 0xe3:
dmac_stop_dma ();
break;
case 0xe4:
case 0xe5:
dmac_cint ();
break;
case 0xe8:
case 0xe9:
/* FLUSH */
dmac_istr |= ISTR_FE_FLG;
break;
}
#if A2091_DEBUG > 0
write_log (L"dmac_bput %04X=%02X PC=%08X\n", addr, b & 255, M68K_GETPC);
#endif
}
static uae_u32 REGPARAM2 dmac_lget (uaecptr addr)
{
uae_u32 v;
#ifdef JIT
special_mem |= S_READ;
#endif
addr &= 65535;
v = dmac_bget2 (addr) << 24;
v |= dmac_bget2 (addr + 1) << 16;
v |= dmac_bget2 (addr + 2) << 8;
v |= dmac_bget2 (addr + 3);
#ifdef A2091_DEBUG
if (addr >= 0x40 && addr < ROM_OFFSET)
write_log (L"dmac_lget %08X=%08X PC=%08X\n", addr, v, M68K_GETPC);
#endif
return v;
}
static uae_u32 REGPARAM2 dmac_wget (uaecptr addr)
{
uae_u32 v;
#ifdef JIT
special_mem |= S_READ;
#endif
addr &= 65535;
v = dmac_bget2 (addr) << 8;
v |= dmac_bget2 (addr + 1);
#if A2091_DEBUG > 0
if (addr >= 0x40 && addr < ROM_OFFSET)
write_log (L"dmac_wget %08X=%04X PC=%08X\n", addr, v, M68K_GETPC);
#endif
return v;
}
static uae_u32 REGPARAM2 dmac_bget (uaecptr addr)
{
uae_u32 v;
#ifdef JIT
special_mem |= S_READ;
#endif
addr &= 65535;
v = dmac_bget2 (addr);
if (!configured)
return v;
return v;
}
static void REGPARAM2 dmac_lput (uaecptr addr, uae_u32 l)
{
#ifdef JIT
special_mem |= S_WRITE;
#endif
addr &= 65535;
#if A2091_DEBUG > 0
if (addr >= 0x40 && addr < ROM_OFFSET)
write_log (L"dmac_lput %08X=%08X PC=%08X\n", addr, l, M68K_GETPC);
#endif
dmac_bput2 (addr, l >> 24);
dmac_bput2 (addr + 1, l >> 16);
dmac_bput2 (addr + 2, l >> 8);
dmac_bput2 (addr + 3, l);
}
static void REGPARAM2 dmac_wput (uaecptr addr, uae_u32 w)
{
#ifdef JIT
special_mem |= S_WRITE;
#endif
addr &= 65535;
#if A2091_DEBUG > 0
if (addr >= 0x40 && addr < ROM_OFFSET)
write_log (L"dmac_wput %04X=%04X PC=%08X\n", addr, w & 65535, M68K_GETPC);
#endif
dmac_bput2 (addr, w >> 8);
dmac_bput2 (addr + 1, w);
}
static void REGPARAM2 dmac_bput (uaecptr addr, uae_u32 b)
{
#ifdef JIT
special_mem |= S_WRITE;
#endif
b &= 0xff;
addr &= 65535;
if (addr == 0x48 && !configured) {
map_banks (&dmaca2091_bank, b, 0x10000 >> 16, 0x10000);
write_log (L"A590/A2091 Z2 autoconfigured at %02X0000\n", b);
configured = 1;
expamem_next ();
return;
}
if (addr == 0x4c && !configured) {
write_log (L"A590/A2091 DMAC AUTOCONFIG SHUT-UP!\n");
configured = 1;
expamem_next ();
return;
}
if (!configured)
return;
dmac_bput2 (addr, b);
}
static uae_u32 REGPARAM2 dmac_wgeti (uaecptr addr)
{
uae_u32 v = 0xffff;
#ifdef JIT
special_mem |= S_READ;
#endif
addr &= 65535;
if (addr >= ROM_OFFSET)
v = (rom[addr & rom_mask] << 8) | rom[(addr + 1) & rom_mask];
return v;
}
static uae_u32 REGPARAM2 dmac_lgeti (uaecptr addr)
{
uae_u32 v = 0xffff;
#ifdef JIT
special_mem |= S_READ;
#endif
addr &= 65535;
v = (dmac_wgeti(addr) << 16) | dmac_wgeti(addr + 2);
return v;
}
addrbank dmaca2091_bank = {
dmac_lget, dmac_wget, dmac_bget,
dmac_lput, dmac_wput, dmac_bput,
default_xlate, default_check, NULL, L"A2091/A590",
dmac_lgeti, dmac_wgeti, ABFLAG_IO
};
static void dmacreg_write (uae_u32 *reg, int addr, uae_u32 val, int size)
{
addr = (size - 1) - addr;
(*reg) &= ~(0xff << (addr * 8));
(*reg) |= (val & 0xff) << (addr * 8);
}
static uae_u32 dmacreg_read (uae_u32 val, int addr, int size)
{
addr = (size - 1) - addr;
return (val >> (addr * 8)) & 0xff;
}
static void mbdmac_write (uae_u32 addr, uae_u32 val, int mode)
{
if (currprefs.cs_mbdmac > 1)
return;
#if A3000_DEBUG > 1
write_log (L"DMAC_WRITE %08X=%02X PC=%08X\n", addr, val & 0xff, M68K_GETPC);
#endif
addr &= 0xffff;
switch (addr)
{
case 0x02:
case 0x03:
dmacreg_write (&dmac_dawr, addr - 0x02, val, 2);
break;
case 0x04:
case 0x05:
case 0x06:
case 0x07:
dmacreg_write (&dmac_wtc, addr - 0x04, val, 4);
break;
case 0x0a:
case 0x0b:
dmacreg_write (&dmac_cntr, addr - 0x0a, val, 2);
if (dmac_cntr & SCNTR_PREST)
dmac_reset ();
break;
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
dmacreg_write (&dmac_acr, addr - 0x0c, val, 4);
break;
case 0x12:
case 0x13:
if (!dmac_dma)
dmac_start_dma ();
break;
case 0x16:
case 0x17:
/* FLUSH */
dmac_istr |= ISTR_FE_FLG;
break;
case 0x1a:
case 0x1b:
dmac_cint();
break;
case 0x1e:
case 0x1f:
/* ISTR */
break;
case 0x3e:
case 0x3f:
dmac_stop_dma ();
break;
case 0x41:
if (mode & 0x10)
sasr = val;
break;
case 0x49:
sasr = val;
break;
case 0x43:
if (mode & 0x10)
wdscsi_put (val);
else
sasr = val;
break;
}
}
static uae_u32 mbdmac_read (uae_u32 addr, int mode)
{
uae_u32 vaddr = addr;
uae_u32 v = 0xffffffff;
if (currprefs.cs_mbdmac > 1)
return 0;
addr &= 0xffff;
switch (addr)
{
case 0x02:
case 0x03:
v = dmacreg_read (dmac_dawr, addr - 0x02, 2);
break;
case 0x04:
case 0x05:
case 0x06:
case 0x07:
v = 0xff;
break;
case 0x0a:
case 0x0b:
v = dmacreg_read (dmac_cntr, addr - 0x0a, 2);
break;
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
v = dmacreg_read (dmac_acr, addr - 0x0c, 4);
break;
case 0x12:
case 0x13:
if (!dmac_dma)
dmac_start_dma ();
v = 0;
break;
case 0x1a:
case 0x1b:
dmac_cint ();
v = 0;
break;;
case 0x1e:
case 0x1f:
v = dmacreg_read (dmac_istr, addr - 0x1e, 2);
if (v & ISTR_INTS)
v |= ISTR_INT_P;
dmac_istr &= ~15;
break;
case 0x3e:
case 0x3f:
dmac_stop_dma ();
v = 0;
break;
case 0x41:
case 0x49:
v = wdscsi_getauxstatus ();
break;
case 0x43:
v = wdscsi_get ();
break;
}
#if A3000_DEBUG > 1
write_log (L"DMAC_READ %08X=%02X PC=%X\n", vaddr, v & 0xff, M68K_GETPC);
#endif
return v;
}
static uae_u32 REGPARAM3 mbdmac_lget (uaecptr) REGPARAM;
static uae_u32 REGPARAM3 mbdmac_wget (uaecptr) REGPARAM;
static uae_u32 REGPARAM3 mbdmac_bget (uaecptr) REGPARAM;
static void REGPARAM3 mbdmac_lput (uaecptr, uae_u32) REGPARAM;
static void REGPARAM3 mbdmac_wput (uaecptr, uae_u32) REGPARAM;
static void REGPARAM3 mbdmac_bput (uaecptr, uae_u32) REGPARAM;
static uae_u32 REGPARAM2 mbdmac_lget (uaecptr addr)
{
uae_u32 v;
#ifdef JIT
special_mem |= S_READ;
#endif
v = mbdmac_read (addr, 0x40 | 0) << 24;
v |= mbdmac_read (addr + 1, 0x40 | 1) << 16;
v |= mbdmac_read (addr + 2, 0x40 | 2) << 8;
v |= mbdmac_read (addr + 3, 0x40 | 3);
return v;
}
static uae_u32 REGPARAM2 mbdmac_wget (uaecptr addr)
{
uae_u32 v;
#ifdef JIT
special_mem |= S_READ;
#endif
v = mbdmac_read (addr, 0x40 | 0) << 8;
v |= mbdmac_read (addr + 1, 0x40 | 1) << 0;
return v;
}
static uae_u32 REGPARAM2 mbdmac_bget (uaecptr addr)
{
#ifdef JIT
special_mem |= S_READ;
#endif
return mbdmac_read (addr, 0x10);
}
static void REGPARAM2 mbdmac_lput (uaecptr addr, uae_u32 l)
{
#ifdef JIT
special_mem |= S_WRITE;
#endif
mbdmac_write (addr + 0, l >> 24, 0x40 | 0);
mbdmac_write (addr + 1, l >> 16, 0x40 | 1);
mbdmac_write (addr + 2, l >> 8, 0x40 | 2);
mbdmac_write (addr + 3, l, 0x40 | 3);
}
static void REGPARAM2 mbdmac_wput (uaecptr addr, uae_u32 w)
{
#ifdef JIT
special_mem |= S_WRITE;
#endif
mbdmac_write (addr + 0, w >> 8, 0x20 | 0);
mbdmac_write (addr + 1, w >> 0, 0x20 | 1);
}
static void REGPARAM2 mbdmac_bput (uaecptr addr, uae_u32 b)
{
#ifdef JIT
special_mem |= S_WRITE;
#endif
mbdmac_write (addr, b, 0x10 | 0);
}
addrbank mbdmac_a3000_bank = {
mbdmac_lget, mbdmac_wget, mbdmac_bget,
mbdmac_lput, mbdmac_wput, mbdmac_bput,
default_xlate, default_check, NULL, L"A3000 DMAC",
dummy_lgeti, dummy_wgeti, ABFLAG_IO
};
static void ew (int addr, uae_u32 value)
{
addr &= 0xffff;
if (addr == 00 || addr == 02 || addr == 0x40 || addr == 0x42) {
dmacmemory[addr] = (value & 0xf0);
dmacmemory[addr + 2] = (value & 0x0f) << 4;
} else {
dmacmemory[addr] = ~(value & 0xf0);
dmacmemory[addr + 2] = ~((value & 0x0f) << 4);
}
}
static void freescsi (struct scsi_data *sd)
{
if (!sd)
return;
hdf_hd_close (sd->hfd);
scsi_free (sd);
}
int addscsi (int ch, TCHAR *path, int blocksize, int readonly,
TCHAR *devname, int sectors, int surfaces, int reserved,
int bootpri, TCHAR *filesys, int scsi_level)
{
struct hd_hardfiledata *hfd;
freescsi (scsis[ch]);
scsis[ch] = NULL;
hfd = xcalloc (sizeof (struct hd_hardfiledata), 1);
if (!hdf_hd_open (hfd, path, blocksize, readonly, devname, sectors, surfaces, reserved, bootpri, filesys))
return 0;
hfd->ansi_version = scsi_level;
scsis[ch] = scsi_alloc (ch, hfd);
return scsis[ch] ? 1 : 0;
}
static void freenativescsi (void)
{
int i;
for (i = 0; i < 7; i++) {
freescsi (scsis[i]);
scsis[i] = NULL;
}
}
static void addnativescsi (void)
{
int i, j;
int devices[MAX_TOTAL_DEVICES];
int types[MAX_TOTAL_DEVICES];
struct device_info dis[MAX_TOTAL_DEVICES];
freenativescsi ();
i = 0;
while (i < MAX_TOTAL_DEVICES) {
types[i] = -1;
devices[i] = -1;
if (sys_command_open (DF_SCSI, i)) {
if (sys_command_info (DF_SCSI, i, &dis[i])) {
devices[i] = i;
types[i] = 100 - i;
if (dis[i].type == INQ_ROMD)
types[i] = 1000 - i;
}
sys_command_close (DF_SCSI, i);
}
i++;
}
i = 0;
while (devices[i] >= 0) {
j = i + 1;
while (devices[j] >= 0) {
if (types[i] > types[j]) {
int tmp = types[i];
types[i] = types[j];
types[j] = tmp;
}
j++;
}
i++;
}
i = 0; j = 0;
while (devices[i] >= 0 && j < 7) {
if (scsis[j] == NULL) {
scsis[j] = scsi_alloc_native(j, devices[i]);
write_log (L"SCSI: %d:'%s'\n", j, dis[i].label);
i++;
}
j++;
}
}
int a3000_add_scsi_unit (int ch, TCHAR *path, int blocksize, int readonly,
TCHAR *devname, int sectors, int surfaces, int reserved,
int bootpri, TCHAR *filesys)
{
return addscsi (ch, path, blocksize, readonly, devname, sectors, surfaces, reserved, bootpri, filesys, 2);
}
void a3000scsi_reset (void)
{
map_banks (&mbdmac_a3000_bank, 0xDD, 1, 0);
}
void a3000scsi_free (void)
{
freenativescsi ();
}
int a2091_add_scsi_unit (int ch, TCHAR *path, int blocksize, int readonly,
TCHAR *devname, int sectors, int surfaces, int reserved,
int bootpri, TCHAR *filesys)
{
return addscsi (ch, path, blocksize, readonly, devname, sectors, surfaces, reserved, bootpri, filesys, 1);
}
void a2091_free (void)
{
freenativescsi ();
xfree (rom);
rom = NULL;
}
void a2091_reset (void)
{
configured = 0;
wd_used = 0;
superdmac = 0;
superdmac = currprefs.cs_mbdmac ? 1 : 0;
if (currprefs.scsi == 2)
addnativescsi ();
}
void a2091_init (void)
{
int roms[5];
struct romlist *rl;
struct romdata *rd;
configured = 0;
memset (dmacmemory, 0xff, 100);
ew (0x00, 0xc0 | 0x01 | 0x10);
/* A590/A2091 hardware id */
ew (0x04, 0x03);
/* commodore's manufacturer id */
ew (0x10, 0x02);
ew (0x14, 0x02);
/* rom vector */
ew (0x28, ROM_VECTOR >> 8);
ew (0x2c, ROM_VECTOR);
ew (0x18, 0x00); /* ser.no. Byte 0 */
ew (0x1c, 0x00); /* ser.no. Byte 1 */
ew (0x20, 0x00); /* ser.no. Byte 2 */
ew (0x24, 0x00); /* ser.no. Byte 3 */
roms[0] = 55;
roms[1] = 54;
roms[2] = 53;
roms[3] = 56;
roms[4] = -1;
roms[0] = 53;
rombankswitcher = 0;
rombank = 0;
rl = getromlistbyids (roms);
if (rl) {
struct zfile *z;
rd = rl->rd;
z = read_rom (&rd);
if (z) {
write_log (L"A590/A2091 BOOT ROM %d.%d ", rd->ver, rd->rev);
rom_size = rd->size;
rom = xmalloc (rom_size);
if (rl->rd->id == 56)
rombankswitcher = 1;
zfile_fread (rom, rom_size, 1, z);
zfile_fclose (z);
rom_mask = rom_size - 1;
}
} else {
romwarning (roms);
}
map_banks (&dmaca2091_bank, 0xe80000 >> 16, 0x10000 >> 16, 0x10000);
}