1
0
mirror of https://github.com/open-simh/simh.git synced 2026-01-28 04:58:26 +00:00

3B2: Multiple bugfixes and improvements

- Allow 3 MB RAM configuration (previously only 1MB, 2MB, and 4MB
  configurations were allowed)
- Allow SCSI CIO card to be used under 3B2/400 emulation (previously
  it could only be used under 3B2/700 emulation)
- Improved CTC, PORTS, and SCSI diagnostic checks
- Fixed a bug in IDISK device that allowed impossible disk
  configurations

The last update is a breaking change that disables the HD161 disk
type by default, since real 3B2 hardware does not support it. The
disk type will still allowed in backward compatibility mode through
use of the "SET IDISK LARGE" command.
This commit is contained in:
Seth Morabito
2024-10-24 13:36:53 -07:00
committed by Paul Koning
parent 5a184eb71d
commit 3e0214aed0
17 changed files with 479 additions and 352 deletions

View File

@@ -355,11 +355,15 @@ static const char *att3b2_clock_precalibrate_commands[] = {
MTAB cpu_mod[] = {
#if defined(REV2)
{ UNIT_MSIZE, (1u << 20), NULL, "1M",
{ UNIT_MSIZE, MSIZ_512K, NULL, "512K",
&cpu_set_size, NULL, NULL, "Set Memory to 512K bytes" },
{ UNIT_MSIZE, MSIZ_1M, NULL, "1M",
&cpu_set_size, NULL, NULL, "Set Memory to 1M bytes" },
{ UNIT_MSIZE, (1u << 21), NULL, "2M",
{ UNIT_MSIZE, MSIZ_2M, NULL, "2M",
&cpu_set_size, NULL, NULL, "Set Memory to 2M bytes" },
{ UNIT_MSIZE, (1u << 22), NULL, "4M",
{ UNIT_MSIZE, MSIZ_3M, NULL, "3M",
&cpu_set_size, NULL, NULL, "Set Memory to 3M bytes" },
{ UNIT_MSIZE, MSIZ_4M, NULL, "4M",
&cpu_set_size, NULL, NULL, "Set Memory to 4M bytes" },
#endif
#if defined(REV3)

View File

@@ -52,15 +52,18 @@
#define DELAY_UNK 1000
#define DELAY_CATCHUP 10000
#define CTC_DIAG_CRC1 0xa4a5752f
#define CTC_DIAG_CRC2 0xd3d20eb3
#define CTC_DIAG_CRC3 0x0f387ce3 /* Used by SVR 2.0.5 */
#define TAPE_DEV 0 /* CTAPE device */
#define XMF_DEV 1 /* XM Floppy device */
#define VTOC_BLOCK 0
static uint32 diag_crc[] = {
0xa4a5752f,
0xd3d20eb3,
0x668e4b3e, /* Used by SVR 3.1 */
0x0f387ce3 /* Used by SVR 2.0.5 */
};
static uint8 int_slot; /* Interrupting card ID */
static uint8 int_subdev; /* Interrupting subdevice */
static t_bool ctc_conf = FALSE; /* Has a CTC card been configured? */
@@ -299,14 +302,17 @@ static void ctc_cmd(uint8 slot,
/* If the currently running program is a diagnostic program,
* we are expected to write results into memory at address
* 0x200f000 */
if (ctc_crc == CTC_DIAG_CRC1 ||
ctc_crc == CTC_DIAG_CRC2 ||
ctc_crc == CTC_DIAG_CRC3) {
pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */
pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */
pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */
pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */
pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */
for (i = 0; i < (sizeof(diag_crc) / sizeof(diag_crc[0])); i++) {
if (ctc_crc == diag_crc[i]) {
sim_debug(TRACE_DBG, &ctc_dev,
"[ctc_cmd] CIO_FCF found CRC==%08x\n", ctc_crc);
pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */
pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */
pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */
pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */
pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */
break;
}
}
/* An interesting (?) side-effect of FORCE FUNCTION CALL is

View File

@@ -97,6 +97,7 @@
#define MSIZ_512K 0x80000
#define MSIZ_1M 0x100000
#define MSIZ_2M 0x200000
#define MSIZ_3M 0x300000
#define MSIZ_4M 0x400000
#define MSIZ_8M 0x800000
#define MSIZ_16M 0x1000000
@@ -146,6 +147,7 @@ extern DEVICE cpu_dev;
extern DEVICE csr_dev;
extern DEVICE ctc_dev;
extern DEVICE dmac_dev;
extern DEVICE ha_dev;
extern DEVICE id_dev;
extern DEVICE if_dev;
extern DEVICE iu_timer_dev;
@@ -161,7 +163,6 @@ extern DEVICE tto_dev;
extern DEVICE lpt_dev;
#if defined(REV3)
extern DEVICE flt_dev;
extern DEVICE ha_dev;
#endif /* defined(REV3) */
#endif /* _3B2_DEFS_H_ */

View File

@@ -48,71 +48,58 @@
#include "3b2_cpu.h"
#define ID_SEEK_WAIT 50
#define ID_SEEK_BASE 700
#define ID_RECAL_WAIT 6000
#define ID_RW_WAIT 1000
#define ID_SUS_WAIT 200
#define ID_SPEC_WAIT 1250
#define ID_SIS_WAIT 142
#define ID_CMD_WAIT 140
#define ID_STEP_WAIT 4000 /* Delay per track step */
#define ID_STEP_MIN 10000 /* Minimum delay for RECAL/SEEK command */
#define ID_RW_WAIT 5000
#define ID_SUS_WAIT 1000
#define ID_SPEC_WAIT 1000
#define ID_SIS_WAIT 1000
#define ID_CMD_WAIT 2000
#define POLLING(id) ((id_dtlh & ID_DTLH_POLL) == 0)
/* Static function declarations */
static SIM_INLINE t_lba id_lba(uint16 cyl, uint8 head, uint8 sec);
/* Data FIFO pointer - Read */
uint8 id_dpr = 0;
/* Data FIFO pointer - Write */
uint8 id_dpw = 0;
/* Controller Status Register */
uint8 id_status = 0;
/* Unit Interrupt Status */
uint8 id_int_status = 0;
/* Last command received */
uint8 id_cmd = 0;
/* DMAC request */
t_bool id_drq = FALSE;
t_bool id_drq = FALSE;
struct id_state id_state[ID_NUM_UNITS] = {0};
/* Enable support for disks with > 1024 cylinders */
static t_bool id_large = FALSE;
/* Data FIFO pointer - Read */
static uint8 id_dpr = 0;
/* Data FIFO pointer - Write */
static uint8 id_dpw = 0;
/* Controller Status Register */
static uint8 id_status = 0;
/* Unit Interrupt Status */
static uint8 id_int_status = 0;
/* Last command received */
static uint8 id_cmd = 0;
/* 8-byte FIFO */
uint8 id_data[ID_FIFO_LEN] = {0};
static uint8 id_data[ID_FIFO_LEN] = {0};
/* SRQM bit */
t_bool id_srqm = FALSE;
static t_bool id_srqm = FALSE;
/* The logical unit number (0-1) */
uint8 id_unit_num = 0;
static uint8 id_unit_num = 0;
/* The physical unit number (0-3) */
uint8 id_ua = 0;
/* Cylinder the drive is positioned on */
uint16 id_cyl[ID_NUM_UNITS] = {0};
/* Ending Track Number (from Specify) */
uint8 id_etn = 0;
/* Ending Sector Number (from Specify) */
uint8 id_esn = 0;
/* DTLH word (from Specify) */
uint8 id_dtlh = 0;
/* Physical sector number */
uint8 id_psn = 0;
/* Physical head number */
uint8 id_phn = 0;
/* Logical cylinder number, high byte */
uint8 id_lcnh = 0;
/* Logical cylinder number, low byte */
uint8 id_lcnl = 0;
/* Logical head number */
uint8 id_lhn = 0;
/* Logical sector number */
uint8 id_lsn = 0;
/* Number of sectors to transfer, decremented after each sector */
uint8 id_scnt = 0;
/* Whether we are using polling mode or not */
t_bool id_polling = FALSE;
static uint8 id_ua = 0;
/* Whether we are using buffered SEEK/RECAL or not */
static t_bool id_buffered = FALSE;
/* Sector buffer */
uint8 id_buf[ID_SEC_SIZE];
static uint8 id_buf[ID_SEC_SIZE];
/* Buffer pointer */
size_t id_buf_ptr = 0;
static size_t id_buf_ptr = 0;
uint8 id_idfield[ID_IDFIELD_LEN];
uint8 id_idfield_ptr = 0;
/* SPECIFY parameters */
static uint8 id_esn;
static uint8 id_etn;
static uint8 id_dtlh;
int8 id_seek_state[ID_NUM_UNITS] = {ID_SEEK_NONE};
static uint8 id_idfield[ID_IDFIELD_LEN];
static uint8 id_idfield_ptr = 0;
struct id_dtype {
uint8 hd; /* Number of heads */
@@ -125,14 +112,14 @@ static struct id_dtype id_dtab[] = {
ID_DRV(HD72),
ID_DRV(HD72C),
ID_DRV(HD135),
ID_DRV(HD161),
ID_DRV(HD161), /* Only enabled if 'id_large' is set */
{ 0 }
};
UNIT id_unit[] = {
{ UDATA (&id_unit_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BINK+ID_AUTOSIZE+
{ UDATA (&id_unit_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BINK+DKUF_NOAUTOSIZE+
(ID_HD72_DTYPE << ID_V_DTYPE), ID_DSK_SIZE(HD72)), 0, ID0, 0 },
{ UDATA (&id_unit_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BINK+ID_AUTOSIZE+
{ UDATA (&id_unit_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BINK+DKUF_NOAUTOSIZE+
(ID_HD72_DTYPE << ID_V_DTYPE), ID_DSK_SIZE(HD72)), 0, ID1, 0 },
{ UDATA (&id_ctlr_svc, 0, 0) },
{ NULL }
@@ -146,15 +133,14 @@ UNIT *id_sel_unit = &id_unit[ID0];
REG id_reg[] = {
{ HRDATAD(CMD, id_cmd, 8, "Command") },
{ HRDATAD(STAT, id_status, 8, "Status") },
{ BRDATAD(CYL, id_cyl, 8, 8, ID_NUM_UNITS, "Track") },
{ NULL }
};
/* HD161 and HD135 are identical; the difference is only in the
* software being run on the emulator. SVR 2.0 will support a maximum
* of 1024 cylinders, so can only format the first 1024 cylinders of
* the HD135. SVR 3.0+ can support all 1224 cylinders of the HD161. */
MTAB id_mod[] = {
{ MTAB_XTD|MTAB_VDV, 1, NULL, "LARGE",
&id_set_large, NULL, NULL, "Enable support for disks with > 1024 cylinders" },
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOLARGE",
&id_set_large, NULL, NULL, "Disable support disks with > 1024 cylinders" },
{ MTAB_XTD|MTAB_VUN, ID_HD30_DTYPE, NULL, "HD30",
&id_set_type, NULL, NULL, "Set HD30 Disk Type" },
{ MTAB_XTD|MTAB_VUN, ID_HD72_DTYPE, NULL, "HD72",
@@ -167,10 +153,6 @@ MTAB id_mod[] = {
&id_set_type, NULL, NULL, "Set HD161 Disk Type" },
{ MTAB_XTD|MTAB_VUN, 0, "TYPE", NULL,
NULL, &id_show_type, NULL, "Display device type" },
{ ID_AUTOSIZE, ID_AUTOSIZE, "autosize", "AUTOSIZE",
NULL, NULL, NULL, "Set type based on file size at attach" },
{ ID_AUTOSIZE, 0, "noautosize", "NOAUTOSIZE",
NULL, NULL, NULL, "Disable disk autosize on attach" },
{ 0 }
};
@@ -184,15 +166,12 @@ DEVICE id_dev = {
&id_description
};
/* Function implementation */
#define UPDATE_INT { \
if ((id_status & (ID_STAT_CEL|ID_STAT_CEH)) || \
((id_status & ID_STAT_SRQ) && !id_srqm)) { \
CPU_SET_INT(INT_DISK); \
} else { \
CPU_CLR_INT(INT_DISK); \
} \
#define UPDATE_INT { \
if ((id_status & ID_IRQ_MASK) && !id_srqm) { \
CPU_SET_INT(INT_DISK); \
} else { \
CPU_CLR_INT(INT_DISK); \
} \
}
static SIM_INLINE void id_set_status(uint8 flags)
@@ -264,12 +243,15 @@ t_stat id_ctlr_svc(UNIT *uptr)
*/
t_stat id_unit_svc(UNIT *uptr)
{
uint8 unit, other, cmd;
t_bool recal_error;
uint8 unit, other, cmd, end_flags;
unit = uptr->u3; /* The unit number that needs an interrupt */
cmd = uptr->u4; /* The command that caused the activity */
other = unit ^ 1; /* The number of the other unit */
recal_error = FALSE;
/* If the other unit is active, we cannot interrupt, so we delay
* here */
if (id_unit[other].u4 == ID_CMD_RDATA ||
@@ -279,62 +261,35 @@ t_stat id_unit_svc(UNIT *uptr)
}
id_set_srqm(FALSE);
id_clr_status(ID_STAT_CB);
/* Note that we don't set CEH, in case this is a SEEK/RECAL ID_SEEK_1 */
switch (cmd) {
case ID_CMD_SEEK: /* fall-through */
case ID_CMD_RECAL:
/* In POLLING mode, SEEK and RECAL actually interrupt twice.
*
* 1. Immediately after the correct number of stepping pulses
* have been issued (SRQ is not set)
*
* 2. After the drive has completed seeking and is ready
* for a new command (SRQ is set)
*/
if (id_polling) {
switch (id_seek_state[unit]) {
case ID_SEEK_0:
id_set_status(ID_STAT_CEH);
sim_debug(EXECUTE_MSG, &id_dev,
"INTR\t\tCOMPLETING Recal/Seek SEEK_0 UNIT %d\n",
unit);
id_seek_state[unit] = ID_SEEK_1;
id_activate(uptr, 8000); /* TODO: Correct Delay based on steps */
break;
case ID_SEEK_1:
sim_debug(EXECUTE_MSG, &id_dev,
"INTR\t\tCOMPLETING Recal/Seek SEEK_1 UNIT %d\n",
unit);
id_seek_state[unit] = ID_SEEK_NONE;
id_set_status(ID_STAT_SRQ);
uptr->u4 = 0; /* Only clear out the command on a SEEK_1, never a SEEK_0 */
if (uptr->flags & UNIT_ATT) {
id_int_status |= (ID_IST_SEN|unit);
} else {
id_int_status |= (ID_IST_NR|unit);
}
break;
default:
sim_debug(EXECUTE_MSG, &id_dev,
"INTR\t\tERROR, NOT SEEK_0 OR SEEK_1, UNIT %d\n",
unit);
break;
}
/* Recalibrate ends in an error condition if we were never able to
reach track 0 */
recal_error = (id_state[unit].cyl != 0);
/* intentional fall-through */
case ID_CMD_SEEK:
id_int_status = 0;
uptr->u4 = 0;
sim_debug(EXECUTE_MSG, &id_dev,
"INTR\t\tCOMPLETING Recal/Seek UNIT %d (polling=%d)\n",
unit, POLLING(unit));
if (recal_error) {
end_flags = 0;
} else {
sim_debug(EXECUTE_MSG, &id_dev,
"INTR\t\tCOMPLETING NON-POLLING Recal/Seek UNIT %d\n",
unit);
id_set_status(ID_STAT_CEH);
uptr->u4 = 0;
if (uptr->flags & UNIT_ATT) {
id_int_status |= (ID_IST_SEN|unit);
} else {
id_int_status |= (ID_IST_NR|unit);
}
end_flags = POLLING(unit) ? ID_STAT_SRQ : ID_STAT_CEH;
}
if (uptr->flags & UNIT_ATT) {
id_set_status(end_flags);
id_int_status |= (ID_IST_SEN|unit);
} else {
id_int_status |= (ID_IST_NR|unit);
}
id_data[0] = id_int_status;
break;
case ID_CMD_SUS:
sim_debug(EXECUTE_MSG, &id_dev,
@@ -349,7 +304,7 @@ t_stat id_unit_svc(UNIT *uptr)
} else {
/* Put Unit Status into byte 0 */
id_data[0] = (ID_UST_DSEL|ID_UST_SCL|ID_UST_RDY);
if (id_cyl[unit] == 0) {
if (id_state[unit].cyl == 0) {
id_data[0] |= ID_UST_TK0;
}
}
@@ -363,6 +318,23 @@ t_stat id_unit_svc(UNIT *uptr)
break;
}
/* In all conditions except on a RECALIBRATE failure, CB is cleared */
if (!recal_error) {
id_clr_status(ID_STAT_CB);
}
return SCPE_OK;
}
t_stat id_set_large(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
id_large = (t_bool)val;
if (!id_large && (ID_GET_DTYPE(id_unit[0].flags) > ID_HD135_DTYPE ||
ID_GET_DTYPE(id_unit[1].flags) > ID_HD135_DTYPE)) {
return sim_messagef(SCPE_OK, "Large disk support disabled, but at least one attached disk is large!\n");
}
return SCPE_OK;
}
@@ -379,12 +351,22 @@ t_stat id_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
uptr->flags = (uptr->flags & ~ID_DTYPE) | (val << ID_V_DTYPE);
uptr->capac = (t_addr)id_dtab[val].capac;
/*
* Warn the user after setting the type, so we can guard against accidentally
* attaching to a disk we can't use.
*/
if (val == ID_HD161_DTYPE && !id_large) {
return sim_messagef(SCPE_OK, "HD161 disks can only be used if large disk support is enabled (SET IDISK LARGE)\n");
}
return SCPE_OK;
}
t_stat id_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
fprintf (st, "%s", id_dtab[ID_GET_DTYPE(uptr->flags)].name);
fprintf(st, "%s%s",
id_dtab[ID_GET_DTYPE(uptr->flags)].name,
id_large ? " (Large Disk Support Enabled)" : "");
return SCPE_OK;
}
@@ -396,10 +378,12 @@ t_stat id_reset(DEVICE *dptr)
t_stat id_attach(UNIT *uptr, CONST char *cptr)
{
static const char *drives[] = {"HD30", "HD72", "HD72C", "HD135", "HD161", NULL};
return sim_disk_attach_ex(uptr, cptr, 512, 1, TRUE, 0, id_dtab[ID_GET_DTYPE(uptr->flags)].name,
0, 0, (uptr->flags & ID_AUTOSIZE) ? drives : NULL);
if (ID_GET_DTYPE(uptr->flags) == ID_HD161_DTYPE && !id_large) {
return sim_messagef(SCPE_ARG, "HD161 disks can only be used if large disk support is enabled (SET IDISK LARGE)\n");
}
return sim_disk_attach_ex(uptr, cptr, 512, 1, TRUE, 0, id_dtab[ID_GET_DTYPE(uptr->flags)].name, 0, 0, NULL);
}
t_stat id_detach(UNIT *uptr)
@@ -423,29 +407,33 @@ static t_lba id_lba(uint16 cyl, uint8 head, uint8 sec)
* with the correct return parameters. */
static void SIM_INLINE id_end_rw(uint8 est)
{
uint8 id = id_sel_unit->u3;
id_clear_fifo();
id_data[0] = est;
id_data[1] = id_phn;
id_data[2] = ~(id_lcnh);
id_data[3] = id_lcnl;
id_data[4] = id_lhn;
id_data[5] = id_lsn;
id_data[6] = id_scnt;
id_data[1] = id_state[id].phn;
id_data[2] = ~(id_state[id].lcnh);
id_data[3] = id_state[id].lcnl;
id_data[4] = id_state[id].lhn;
id_data[5] = id_state[id].lsn;
id_data[6] = id_state[id].scnt;
}
/* The controller wraps id_lsn, id_lhn, and id_lcnl on each sector
* read, so that they point to the next C/H/S */
static void SIM_INLINE id_update_chs()
{
if (id_lsn++ >= id_esn) {
id_lsn = 0;
if (id_lhn++ >= id_etn) {
id_lhn = 0;
if (id_lcnl == 0xff) {
id_lcnl = 0;
id_lcnh++;
uint8 id = id_sel_unit->u3;
if (id_state[id].lsn++ >= id_esn) {
id_state[id].lsn = 0;
if (id_state[id].lhn++ >= id_etn) {
id_state[id].lhn = 0;
if (id_state[id].lcnl == 0xff) {
id_state[id].lcnl = 0;
id_state[id].lcnh++;
} else {
id_lcnl++;
id_state[id].lcnl++;
}
}
}
@@ -453,12 +441,13 @@ static void SIM_INLINE id_update_chs()
uint32 id_read(uint32 pa, size_t size)
{
uint8 reg;
uint8 reg, id;
uint16 cyl;
t_lba lba;
uint32 data;
t_seccnt sectsread;
id = id_sel_unit->u3;
reg = (uint8) (pa - IDBASE);
switch(reg) {
@@ -482,7 +471,7 @@ uint32 id_read(uint32 pa, size_t size)
if (CMD_NUM == ID_CMD_RDATA) {
/* If we're still in DRQ but we've read all our sectors,
* that's an error state. */
if (id_scnt == 0) {
if (id_state[id].scnt == 0) {
sim_debug(READ_MSG, &id_dev,
"ERROR\tid_scnt = 0 but still in dma\n");
id_end_rw(ID_EST_OVR);
@@ -493,9 +482,9 @@ uint32 id_read(uint32 pa, size_t size)
if (id_buf_ptr == 0 || id_buf_ptr >= ID_SEC_SIZE) {
/* It's time to read a new sector into our sector buf */
id_buf_ptr = 0;
cyl = (uint16) (((uint16)id_lcnh << 8)|(uint16)id_lcnl);
id_cyl[id_unit_num] = cyl;
lba = id_lba(cyl, id_lhn, id_lsn);
cyl = (uint16) (((uint16)id_state[id].lcnh << 8)|(uint16)id_state[id].lcnl);
id_state[id_unit_num].cyl = cyl;
lba = id_lba(cyl, id_state[id].lhn, id_state[id].lsn);
if (sim_disk_rdsect(id_sel_unit, lba, id_buf, &sectsread, 1) == SCPE_OK) {
if (sectsread !=1) {
sim_debug(READ_MSG, &id_dev,
@@ -517,17 +506,17 @@ uint32 id_read(uint32 pa, size_t size)
/* Done with this current sector, update id_scnt */
if (id_buf_ptr >= ID_SEC_SIZE) {
if (--id_scnt == 0) {
if (--id_state[id].scnt == 0) {
id_end_rw(0);
}
}
} else if (CMD_NUM == ID_CMD_RID) {
/* We have to return the ID bytes for the current C/H/S */
if (id_idfield_ptr == 0 || id_idfield_ptr >= ID_IDFIELD_LEN) {
id_idfield[0] = ~(id_lcnh);
id_idfield[1] = id_lcnl;
id_idfield[2] = id_lhn;
id_idfield[3] = id_lsn;
id_idfield[0] = ~(id_state[id].lcnh);
id_idfield[1] = id_state[id].lcnl;
id_idfield[2] = id_state[id].lhn;
id_idfield[3] = id_state[id].lsn;
id_idfield_ptr = 0;
}
@@ -537,14 +526,14 @@ uint32 id_read(uint32 pa, size_t size)
data);
if (id_idfield_ptr >= ID_IDFIELD_LEN) {
if (id_scnt-- > 0) {
if (id_state[id].scnt-- > 0) {
/* Another sector to ID */
id_idfield_ptr = 0;
} else {
/* All done, set return codes */
id_clear_fifo();
id_data[0] = 0;
id_data[1] = id_scnt;
id_data[1] = id_state[id].scnt;
}
}
} else {
@@ -584,12 +573,13 @@ uint32 id_read(uint32 pa, size_t size)
void id_write(uint32 pa, uint32 val, size_t size)
{
uint8 reg;
uint8 reg, id;
uint16 cyl;
t_lba lba;
t_seccnt sectswritten;
reg = (uint8) (pa - IDBASE);
id = id_sel_unit->u3;
switch(reg) {
case ID_DATA_REG:
@@ -599,7 +589,7 @@ void id_write(uint32 pa, uint32 val, size_t size)
if (id_drq) {
/* If we're still in DRQ but we've written all our sectors,
* that's an error state. */
if (id_scnt == 0) {
if (id_state[id].scnt == 0) {
sim_debug(WRITE_MSG, &id_dev,
"ERROR\tid_scnt = 0 but still in dma\n");
id_end_rw(ID_EST_OVR);
@@ -623,9 +613,9 @@ void id_write(uint32 pa, uint32 val, size_t size)
if (id_buf_ptr >= ID_SEC_SIZE) {
/* It's time to start the next sector, and flush the old. */
id_buf_ptr = 0;
cyl = (uint16) (((uint16) id_lcnh << 8)|(uint16)id_lcnl);
id_cyl[id_unit_num] = cyl;
lba = id_lba(cyl, id_lhn, id_lsn);
cyl = (uint16) (((uint16) id_state[id].lcnh << 8)|(uint16)id_state[id].lcnl);
id_state[id].cyl = cyl;
lba = id_lba(cyl, id_state[id].lhn, id_state[id].lsn);
if (sim_disk_wrsect(id_sel_unit, lba, id_buf, &sectswritten, 1) == SCPE_OK) {
if (sectswritten !=1) {
sim_debug(WRITE_MSG, &id_dev,
@@ -633,7 +623,7 @@ void id_write(uint32 pa, uint32 val, size_t size)
sectswritten);
}
id_update_chs();
if (--id_scnt == 0) {
if (--id_state[id].scnt == 0) {
id_end_rw(0);
}
} else {
@@ -668,7 +658,7 @@ void id_write(uint32 pa, uint32 val, size_t size)
void id_handle_command(uint8 val)
{
uint8 cmd, aux_cmd, sec, pattern;
uint8 cmd, aux_cmd, sec, pattern, id;
uint16 cyl;
uint32 time;
t_lba lba;
@@ -709,8 +699,7 @@ void id_handle_command(uint8 val)
sim_cancel(id_sel_unit);
sim_cancel(id_ctlr_unit);
id_status = 0;
id_srqm = FALSE;
UPDATE_INT;
id_set_srqm(FALSE);
}
/* Just return early */
@@ -721,7 +710,7 @@ void id_handle_command(uint8 val)
* nothing */
if (id_status & ID_STAT_CB) {
sim_debug(EXECUTE_MSG, &id_dev,
"!!! Controller Busy. Skipping command byte %02x\n",
"Controller Busy. Skipping command byte %02x\n",
val);
return;
}
@@ -733,17 +722,14 @@ void id_handle_command(uint8 val)
id_cmd = val;
cmd = (id_cmd >> 4) & 0xf;
/* Now that we know it's not an aux command, we can get the unit
* number. Note that we don't update the unit in the case of three
* special commands. */
if (cmd != ID_CMD_SIS && cmd != ID_CMD_SPEC && cmd != ID_CMD_DERR) {
if ((id_cmd & 3) != id_ua) {
id_unit_num = id_cmd & 1;
id_ua = id_cmd & 3;
id_sel_unit = &id_unit[id_unit_num];
}
if ((id_cmd & 3) != id_ua) {
id_unit_num = id_cmd & 1;
id_ua = id_cmd & 3;
id_sel_unit = &id_unit[id_unit_num];
}
id = id_unit_num;
/* TODO: Fix this hack */
if (cmd == ID_CMD_SIS || cmd == ID_CMD_SPEC || cmd == ID_CMD_DERR) {
id_ctlr_unit->u4 = cmd;
@@ -768,7 +754,6 @@ void id_handle_command(uint8 val)
id_dtlh = id_data[1];
id_etn = id_data[3];
id_esn = id_data[4];
id_polling = (id_dtlh & ID_DTLH_POLL) == 0;
id_activate(id_ctlr_unit, ID_SPEC_WAIT);
break;
case ID_CMD_SUS:
@@ -784,48 +769,56 @@ void id_handle_command(uint8 val)
id_activate(id_ctlr_unit, ID_CMD_WAIT);
break;
case ID_CMD_RECAL:
time = id_cyl[id_unit_num];
id_cyl[id_unit_num] = 0;
id_seek_state[id_unit_num] = ID_SEEK_0;
if (id_polling) {
sim_debug(WRITE_MSG, &id_dev,
"COMMAND\t%02x\tRecalibrate - %d - POLLING\n",
val, id_ua);
id_activate(id_sel_unit, 1000);
} else {
sim_debug(WRITE_MSG, &id_dev,
"COMMAND\t%02x\tRecalibrate - %d - NORMAL\n",
val, id_ua);
id_activate(id_sel_unit, (ID_RECAL_WAIT + (time * ID_SEEK_WAIT)));
id_buffered = (val & 0x08) == 0x08;
time = MAX(ID_STEP_MIN, id_state[id].cyl * ID_STEP_WAIT);
sim_debug(WRITE_MSG, &id_dev,
"COMMAND\t%02x\tRecalibrate - %d - (buffered=%d, polling=%d, delay=%u)\n",
val, id_ua, id_buffered, POLLING(id), time);
if (POLLING(id)) {
id_set_status(ID_STAT_CEH);
}
/* If large disk support hasn't been enabled, and the starting cylinder we're
* recalibrating from is > 1023, we can't reach track 0! */
if (!id_large && id_state[id].cyl > 1023) {
sim_debug(WRITE_MSG, &id_dev,
"WARNING: RECALIBRATE - Starting cylinder %d is > 1023!\n",
id_state[id].cyl);
id_state[id].cyl -= 1023;
} else {
id_state[id].cyl = 0;
}
id_activate(id_sel_unit, time);
break;
case ID_CMD_SEEK:
id_lcnh = id_data[0];
id_lcnl = id_data[1];
cyl = id_lcnh << 8 | id_lcnl;
time = (uint32) abs(id_cyl[id_unit_num] - cyl);
id_cyl[id_unit_num] = cyl;
id_seek_state[id_unit_num] = ID_SEEK_0;
id_buffered = (val & 0x08) == 0x08;
id_state[id].lcnh = id_data[0];
id_state[id].lcnl = id_data[1];
cyl = ((uint16)id_state[id].lcnh) << 8 | (uint16)id_state[id].lcnl;
time = MAX(ID_STEP_MIN, ID_STEP_WAIT * (cyl > id_state[id].cyl ? cyl - id_state[id].cyl : id_state[id].cyl - cyl));
if (id_polling) {
sim_debug(WRITE_MSG, &id_dev,
"COMMAND\t%02x\tSeek - %d - POLLING\n",
val, id_ua);
id_activate(id_sel_unit, 4000);
} else {
sim_debug(WRITE_MSG, &id_dev,
"COMMAND\t%02x\tSeek - %d - NORMAL\n",
val, id_ua);
id_activate(id_sel_unit, ID_SEEK_BASE + (time * ID_SEEK_WAIT));
sim_debug(WRITE_MSG, &id_dev,
"COMMAND\t%02x\tSeek - %d - CYL=%u BUF=%d POL=%d DELAY=%u\n",
val, id_ua, cyl, id_buffered, POLLING(id), time);
if (POLLING(id)) {
id_set_status(ID_STAT_CEH);
}
id_state[id].cyl = cyl;
id_activate(id_sel_unit, time);
break;
case ID_CMD_FMT:
sim_debug(WRITE_MSG, &id_dev,
"COMMAND\t%02x\tFormat - %d\n",
val, id_ua);
id_phn = id_data[0];
id_scnt = id_data[1];
id_state[id].phn = id_data[0];
id_state[id].scnt = id_data[1];
pattern = id_data[2];
/* Format scnt sectors with the given pattern, if attached */
@@ -833,20 +826,20 @@ void id_handle_command(uint8 val)
/* Formatting soft-sectored disks always begins at sector 0 */
sec = 0;
while (id_scnt-- > 0) {
while (id_state[id].scnt-- > 0) {
/* Write one sector of pattern */
for (id_buf_ptr = 0; id_buf_ptr < ID_SEC_SIZE; id_buf_ptr++) {
id_buf[id_buf_ptr] = pattern;
}
lba = id_lba(id_cyl[id_unit_num], id_phn, sec++);
lba = id_lba(id_state[id].cyl, id_state[id].phn, sec++);
if (sim_disk_wrsect(id_sel_unit, lba, id_buf, NULL, 1) == SCPE_OK) {
sim_debug(EXECUTE_MSG, &id_dev,
"FORMAT: PHN=%d SCNT=%d PAT=%02x LBA=%04x\n",
id_phn, id_scnt, pattern, lba);
id_state[id].phn, id_state[id].scnt, pattern, lba);
} else {
sim_debug(EXECUTE_MSG, &id_dev,
"FORMAT FAILED! PHN=%d SCNT=%d PAT=%02x LBA=%04x\n",
id_phn, id_scnt, pattern, lba);
id_state[id].phn, id_state[id].scnt, pattern, lba);
break;
}
}
@@ -857,7 +850,7 @@ void id_handle_command(uint8 val)
id_data[0] = ID_EST_NR;
}
id_data[1] = id_scnt;
id_data[1] = id_state[id].scnt;
id_activate(id_sel_unit, ID_CMD_WAIT);
break;
@@ -877,12 +870,12 @@ void id_handle_command(uint8 val)
id_drq = TRUE;
/* Grab our arguments */
id_phn = id_data[0];
id_scnt = id_data[1];
id_state[id].phn = id_data[0];
id_state[id].scnt = id_data[1];
/* Compute logical values used by ID verification */
id_lhn = id_phn;
id_lsn = 0;
id_state[id]. lhn = id_state[id].phn;
id_state[id].lsn = 0;
} else {
sim_debug(EXECUTE_MSG, &id_dev,
"UNIT %d NOT ATTACHED, CANNOT READ ID.\n",
@@ -897,25 +890,29 @@ void id_handle_command(uint8 val)
id_activate(id_sel_unit, ID_CMD_WAIT);
break;
case ID_CMD_RDATA:
/* Grab our arguments */
id_state[id].phn = id_data[0];
id_state[id].lcnh = ~(id_data[1]);
id_state[id].lcnl = id_data[2];
id_state[id].lhn = id_data[3];
id_state[id].lsn = id_data[4];
id_state[id].scnt = id_data[5];
sim_debug(WRITE_MSG, &id_dev,
"COMMAND\t%02x\tRead Data - %d\n",
val, id_ua);
"COMMAND\t%02x\tRead Data - %d - CYL=%u PH=%u LH=%u SEC=%u SCNT=%u\n",
val, id_ua,
(uint16)id_state[id].lcnh << 8 | (uint16)id_state[id].lcnl,
id_data[0], id_data[3], id_data[4], id_data[5]);
if (id_sel_unit->flags & UNIT_ATT) {
id_drq = TRUE;
id_buf_ptr = 0;
/* Grab our arguments */
id_phn = id_data[0];
id_lcnh = ~(id_data[1]);
id_lcnl = id_data[2];
id_lhn = id_data[3];
id_lsn = id_data[4];
id_scnt = id_data[5];
} else {
sim_debug(EXECUTE_MSG, &id_dev,
"UNIT %d NOT ATTACHED, CANNOT READ DATA.\n",
id_ua);
}
id_activate(id_sel_unit, ID_RW_WAIT);
break;
case ID_CMD_CHECK:
@@ -937,20 +934,23 @@ void id_handle_command(uint8 val)
id_activate(id_sel_unit, ID_CMD_WAIT);
break;
case ID_CMD_WDATA:
/* Grab our arguments */
id_state[id].phn = id_data[0];
id_state[id].lcnh = ~(id_data[1]);
id_state[id].lcnl = id_data[2];
id_state[id].lhn = id_data[3];
id_state[id].lsn = id_data[4];
id_state[id].scnt = id_data[5];
sim_debug(WRITE_MSG, &id_dev,
"COMMAND\t%02x\tWrite Data - %d\n",
val, id_ua);
"COMMAND\t%02x\tWrite Data - %d - CYL=%u PH=%u LH=%u SEC=%u SCNT=%u\n",
val, id_ua,
(uint16)id_state[id].lcnh << 8 | (uint16)id_state[id].lcnl,
id_data[0], id_data[3], id_data[4], id_data[5]);
if (id_sel_unit->flags & UNIT_ATT) {
id_drq = TRUE;
id_buf_ptr = 0;
/* Grab our arguments */
id_phn = id_data[0];
id_lcnh = ~(id_data[1]);
id_lcnl = id_data[2];
id_lhn = id_data[3];
id_lsn = id_data[4];
id_scnt = id_data[5];
} else {
sim_debug(EXECUTE_MSG, &id_dev,
"UNIT %d NOT ATTACHED, CANNOT WRITE.\n",
@@ -983,8 +983,10 @@ t_stat id_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
fprintf(st, " HD30 30.6 MB 3 697 5 18 512 CDC Wren 94155-36\n");
fprintf(st, " HD72 73.2 MB 5 925 9 18 512 CDC Wren II 94156-86\n");
fprintf(st, " HD72C 72.9 MB 8 754 11 18 512 Fujitsu M2243AS\n");
fprintf(st, " HD135 135.0 MB 11 1024 15 18 512 Maxtor XT1190 (SVR2)\n");
fprintf(st, " HD161 161.4 MB 11 1224 15 18 512 Maxtor XT1190 (SVR3+)\n\n");
fprintf(st, " HD135 135.0 MB 11 1024 15 18 512 Maxtor XT-2190\n");
if (id_large) {
fprintf(st, " HD161 161.4 MB 11 1224 15 18 512 Maxtor XT-2190\n\n");
}
fprintf(st, "The drive ID and geometry values are used when low-level formatting a\n");
fprintf(st, "drive using the AT&T 'idtools' utility.\n");

View File

@@ -69,6 +69,7 @@
#define ID_STAT_CEL 0x20
#define ID_STAT_CEH 0x40
#define ID_STAT_CB 0x80
#define ID_IRQ_MASK (ID_STAT_CEL|ID_STAT_CEH|ID_STAT_SRQ)
#define ID_IST_SEN 0x80 /* Seek End */
#define ID_IST_RC 0x40 /* Ready Change */
@@ -105,7 +106,7 @@
#define ID_CYL_SIZE ID_SEC_SIZE * ID_SEC_CNT
/* Specific to each drive type */
#define ID_MAX_DTYPE 3
#define ID_MAX_DTYPE 4
#define ID_HD30_DTYPE 0
#define ID_HD30_CYL 697
@@ -122,24 +123,19 @@
#define ID_HD72C_HEADS 11
#define ID_HD72C_LBN 149292
/* The HD135 is actually just an HD161 with only 1024 cylinders
* formatted. This is a software limitation, not hardware. */
#define ID_HD135_DTYPE 3
#define ID_HD135_CYL 1224
#define ID_HD135_CYL 1024
#define ID_HD135_HEADS 15
#define ID_HD135_LBN 330480
#define ID_HD135_LBN 276480
#define ID_HD161_DTYPE 3
#define ID_HD161_DTYPE 4
#define ID_HD161_CYL 1224
#define ID_HD161_HEADS 15
#define ID_HD161_LBN 330480
#define ID_V_DTYPE (DKUF_V_UF + 0)
#define ID_M_DTYPE 3
#define ID_M_DTYPE 7
#define ID_DTYPE (ID_M_DTYPE << ID_V_DTYPE)
#define ID_V_AUTOSIZE (ID_V_DTYPE + 2)
#define ID_AUTOSIZE (1 << ID_V_AUTOSIZE)
#define ID_GET_DTYPE(x) (((x) >> ID_V_DTYPE) & ID_M_DTYPE)
#define ID_DRV(d) { ID_##d##_HEADS, ID_##d##_LBN, #d }
@@ -156,11 +152,22 @@
#define CMD_NUM ((id_cmd >> 4) & 0xf)
struct id_state {
uint16 cyl; /* Cylinder the drive is positioned on */
uint8 phn; /* Physical head number */
uint8 lhn; /* Logical head number */
uint8 lsn; /* Logical sector number */
uint8 scnt; /* Sector count */
uint8 lcnh; /* Logical Cylinder Number, high byte */
uint8 lcnl; /* Logical Cylinder Number, low byte */
};
/* Function prototypes */
t_stat id_ctlr_svc(UNIT *uptr);
t_stat id_unit_svc(UNIT *uptr);
t_stat id_reset(DEVICE *dptr);
t_stat id_set_large(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat id_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat id_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat id_attach(UNIT *uptr, CONST char *cptr);

View File

@@ -461,22 +461,45 @@ uint32 io_read(uint32 pa, size_t size)
}
#else
if (pa == MEMSIZE_REG) {
/* The memsize register at 0x4c003 maps its lower two bits to the
* SIZE64K and ONEBANK signals from the first DPDRAM slot. This
* determines what kind of memory module is installed in that
* slot.
*
* SIZE64K | ONEBANK | Module Size
* --------+---------+----------
* 0 | 0 | 256 KB
* 0 | 1 | 1 MB
* 1 | 1 | 2 MB
*
* After determining the size of the module in slot 1, the firmware
* then tries to write to higher memory addresses to see if more
* than one memory module is installed.
*
* In a real 3B2/300, /310, or /400, if two memory modules are
* installed, the module in slot 2 should be the same size or
* smaller than the module in slot 1, otherwise it won't all get
* used. For example, if a 1 MB module is in slot 1 and a 2 MB
* card is in slot 2, only 1 MB of the second module would be
* used!
*
* Since the 3B2 simulator doesn't let you do this, we just assume
* the configuration of the two modules based on the total memory
* configured.
*
* N.B.: A 512 KB configuration was only supported through SVR 2.
* A single 256 KB memory module was never supported.
*/
/* The following values map to memory sizes:
0x00: 512KB ( 524,288 B)
0x01: 2MB (2,097,152 B)
0x02: 1MB (1,048,576 B)
0x03: 4MB (4,194,304 B)
*/
switch(MEM_SIZE) {
case 0x80000: /* 512KB */
case MSIZ_512K: /* slot 1=256 KB, slot 2=256 KB */
return 0;
case 0x100000: /* 1MB */
return 2;
case 0x200000: /* 2MB */
return 1;
case 0x400000: /* 4MB */
return 3;
case MSIZ_1M: /* slot 1=1 MB, slot 2=empty */
return 0x1;
case MSIZ_2M: /* slot 1=2 MB, slot 2=empty */
case MSIZ_3M: /* slot 1=2 MB, slot 2=1 MB */
case MSIZ_4M: /* slot 1=2 MB, slot 2=2 MB */
return 0x3;
default:
return 0;
}

View File

@@ -305,7 +305,7 @@ DEVICE mau_dev = {
NULL, /* attach routine */
NULL, /* detach routine */
NULL, /* context */
#ifdef REV3
#if defined(REV3)
DEV_DEBUG, /* Rev 3 flags: Always required */
#else
DEV_DISABLE|DEV_DEBUG, /* Rev 2 flags: Can be disabled */

View File

@@ -91,17 +91,25 @@
#define DELAY_DEVICE 25
#define DELAY_STD 100
#define PORTS_DIAG_CRC1 0x7ceec900
#define PORTS_DIAG_CRC2 0x77a1ea56
#define PORTS_DIAG_CRC3 0x84cf938b
#define PORTS_DIAG_CRC4 0x31b32383 /* Used by SVR 2.0.5 */
#define PORTS_DIAG_CRC5 0x4be7bccc /* Used by SVR 2.0.5 */
#define PORTS_DIAG_CRC6 0x3197f6dd /* Used by SVR 2.0.5 */
#define LN(slot,port) (ports_slot_ln[(slot)] + (port))
#define LSLOT(ln) (ports_ln_slot[ln])
#define LPORT(ln) ((ln) % PORTS_LINES)
static uint32 diag_crc[] = {
0x7ceec900, /* Used by SVR 3.2 and up */
0x77a1ea56, /* Used by SVR 3.2 and up */
0x84cf938b, /* Used by SVR 3.2 and up */
0xd9d56d23, /* Used by SVR 3.1 */
0x17c6c722, /* Used by SVR 3.1 */
0xeaf367fa, /* Used by SVR 3.1 */
0x5805145e, /* Used by SVR 3.1 */
0x18d72e94, /* Used by SVR 3.1 */
0x3b8706b1, /* Used by SVR 2.0.5 */
0x31b32383, /* Used by SVR 2.0.5 */
0x4be7bccc, /* Used by SVR 2.0.5 */
0x3197f6dd /* Used by SVR 2.0.5 */
};
int8 ports_base_slot; /* First slot in our contiguous block */
uint8 ports_int_slot; /* Interrupting card ID */
uint8 ports_int_subdev; /* Interrupting subdevice */
@@ -367,17 +375,18 @@ static void ports_cmd(uint8 slot, cio_entry *rentry, uint8 *rapp_data)
/* If the currently running program is a diagnostics program,
* we are expected to write results into memory at address
* 0x200f000 */
if (ports_crc == PORTS_DIAG_CRC1 ||
ports_crc == PORTS_DIAG_CRC2 ||
ports_crc == PORTS_DIAG_CRC3 ||
ports_crc == PORTS_DIAG_CRC4 ||
ports_crc == PORTS_DIAG_CRC5 ||
ports_crc == PORTS_DIAG_CRC6) {
pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */
pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */
pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */
pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */
pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */
for (i = 0 ; i < (sizeof(diag_crc) / sizeof(diag_crc[0])); i++) {
if (ports_crc == diag_crc[i]) {
sim_debug(TRACE_DBG, &ports_dev,
"[ports_cmd] CIO_FCF found CRC==%08x\n",
ports_crc);
pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */
pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */
pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */
pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */
pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */
break;
}
}
/* An interesting (?) side-effect of FORCE FUNCTION CALL is

View File

@@ -275,7 +275,7 @@ static SIM_INLINE t_stat mmu_check_perm(uint8 flags, uint8 r_acc)
return SCPE_NXM;
}
return SCPE_OK;
default:
default: /* Read / Write / Execute */
return SCPE_OK;
}
}
@@ -670,14 +670,14 @@ t_stat mmu_decode_paged(uint32 va, uint8 r_acc, t_bool fc,
/* If this is a write, modify the M bit */
if (SHOULD_UPDATE_PD_M_BIT(pd)) {
sim_debug(EXECUTE_MSG, &mmu_dev,
"Updating M bit in PD\n");
"Updating M bit in PD [va=%08x, pd=%08x, r_acc=%d]\n", va, pd, r_acc);
mmu_update_pd(va, PD_LOC(sd1, va), PD_M_MASK);
}
/* Modify the R bit and write it back */
if (SHOULD_UPDATE_PD_R_BIT(pd)) {
sim_debug(EXECUTE_MSG, &mmu_dev,
"Updating R bit in PD\n");
"Updating R bit in PD [va=%08x, pd=%08x, r_acc=%d]\n", va, pd, r_acc);
mmu_update_pd(va, PD_LOC(sd1, va), PD_R_MASK);
}
}

View File

@@ -38,6 +38,7 @@
#include "3b2_iu.h"
#include "3b2_ni.h"
#include "3b2_ports.h"
#include "3b2_scsi.h"
#include "3b2_mau.h"
#include "3b2_stddev.h"
#include "3b2_timer.h"
@@ -60,6 +61,7 @@ DEVICE *sim_devices[] = {
&if_dev,
&id_dev,
&ports_dev,
&ha_dev,
&lpt_dev,
&ctc_dev,
&ni_dev,
@@ -78,6 +80,7 @@ void full_reset()
id_reset(&id_dev);
csr_reset(&csr_dev);
ports_reset(&ports_dev);
ha_reset(&ha_dev);
lpt_reset(&lpt_dev);
ctc_reset(&ctc_dev);
ni_reset(&ni_dev);

View File

@@ -37,9 +37,6 @@
#include "3b2_io.h"
#include "3b2_mem.h"
#define DIAG_CRC_1 0x271b114c
#define PUMP_CRC 0x201b3617
#define HA_SCSI_ID 0
#define HA_MAXFR (1u << 16)
@@ -48,6 +45,16 @@ static void ha_cmd(uint8 op, uint8 subdev, uint32 addr,
static void ha_build_req(uint8 tc, uint8 subdev, t_bool express);
static void ha_ctrl(uint8 tc);
static uint32 diag_crc[] = {
0x271b114c, /* BOOT DIAG CRC under SVR 3.2.3 */
0x4bf2592f, /* BOOT DIAG CRC under SVR 3.2 (SCSI HA Utilities 1.0) */
0x23fc023c /* dgmon phase 1 DIAG CRC under Maintenance Utilities 4.0 */
};
static uint32 pump_crc[] = {
0x6ec6932d, /* SCSI HA Utilities 1.0 pump CRC */
0x201b3617 /* SVR 3.2.3 pump CRC */
};
HA_STATE ha_state;
SCSI_BUS ha_bus;
@@ -136,7 +143,11 @@ DEVICE ha_dev = {
&ha_attach, /* attach routine */
&ha_detach, /* detach routine */
NULL, /* context */
#if defined(REV3)
DEV_DEBUG|DEV_DISK|DEV_SECTORS, /* flags */
#else
DEV_DISABLE|DEV_DIS|DEV_DEBUG|DEV_DISK|DEV_SECTORS, /* flags */
#endif
0, /* debug control flags */
ha_debug, /* debug flag names */
NULL, /* memory size change */
@@ -163,31 +174,7 @@ t_stat ha_reset(DEVICE *dptr)
t_stat r;
ha_state.pump_state = PUMP_NONE;
if (ha_buf == NULL) {
ha_buf = (uint8 *)calloc(HA_MAXFR, sizeof(uint8));
}
r = scsi_init(&ha_bus, HA_MAXFR);
if (r != SCPE_OK) {
return r;
}
ha_bus.dptr = dptr;
scsi_reset(&ha_bus);
for (t = 0; t < 8; t++) {
uptr = dptr->units + t;
if (t == HA_SCSI_ID) {
uptr->flags = UNIT_DIS;
}
scsi_add_unit(&ha_bus, t, uptr);
dtyp = GET_DTYPE(uptr->flags);
scsi_set_unit(&ha_bus, uptr, &ha_tab[dtyp]);
scsi_reset_unit(uptr);
}
ha_crc = 0;
if (dptr->flags & DEV_DIS) {
cio_remove_all(HA_ID);
@@ -195,13 +182,46 @@ t_stat ha_reset(DEVICE *dptr)
return SCPE_OK;
}
if (ha_buf == NULL) {
ha_buf = (uint8 *)calloc(HA_MAXFR, sizeof(uint8));
}
if (!ha_conf) {
r = cio_install(HA_ID, "SCSI", HA_IPL,
&ha_express, &ha_full, &ha_sysgen, &ha_cio_reset,
&slot);
r = scsi_init(&ha_bus, HA_MAXFR);
if (r != SCPE_OK) {
return r;
}
ha_bus.dptr = dptr;
scsi_reset(&ha_bus);
for (t = 0; t < 8; t++) {
uptr = dptr->units + t;
if (t == HA_SCSI_ID) {
uptr->flags = UNIT_DIS;
}
scsi_add_unit(&ha_bus, t, uptr);
dtyp = GET_DTYPE(uptr->flags);
scsi_set_unit(&ha_bus, uptr, &ha_tab[dtyp]);
scsi_reset_unit(uptr);
}
if (dptr->flags & DEV_DIS) {
cio_remove_all(HA_ID);
ha_conf = FALSE;
return SCPE_OK;
}
r = cio_install(HA_ID, "SCSI", HA_IPL,
&ha_express, &ha_full, &ha_sysgen, &ha_cio_reset,
&slot);
if (r != SCPE_OK) {
return r;
}
ha_state.slot = slot;
ha_conf = TRUE;
}
@@ -331,6 +351,7 @@ void ha_sysgen(uint8 slot)
{
uint32 sysgen_p;
uint32 alert_buf_p;
uint32 i;
cq_offset = 0;
@@ -355,14 +376,17 @@ void ha_sysgen(uint8 slot)
ha_state.ts[HA_SCSI_ID].rep.op = 0;
ha_state.ts[HA_SCSI_ID].pending = TRUE;
if (ha_crc == PUMP_CRC) {
sim_debug(HA_TRACE, &ha_dev,
"[ha_sysgen] PUMP: NEW STATE = PUMP_SYSGEN\n");
ha_state.pump_state = PUMP_SYSGEN;
} else {
sim_debug(HA_TRACE, &ha_dev,
"[ha_sysgen] PUMP: NEW STATE = PUMP_NONE\n");
ha_state.pump_state = PUMP_NONE;
ha_state.pump_state = PUMP_NONE;
for (i = 0; i < sizeof(pump_crc) / sizeof(pump_crc[0]); i++) {
if (ha_crc == pump_crc[i]) {
sim_debug(HA_TRACE, &ha_dev,
"[ha_sysgen] PUMP: NEW STATE = PUMP_SYSGEN\n");
ha_state.pump_state = PUMP_SYSGEN;
break;
}
}
sim_activate_abs(cio_unit, 1000);
@@ -396,15 +420,21 @@ void ha_express(uint8 slot)
cio_entry rqe;
uint8 rapp_data[RAPP_LEN] = {0};
sim_debug(HA_TRACE, &ha_dev,
"[ha_express] Handling Express Request. pump_state=%d, ha_state.frq=%d, subdev=%02x\n",
ha_state.pump_state, ha_state.frq, rqe.subdevice);
if (ha_state.pump_state == PUMP_SYSGEN) {
sim_debug(HA_TRACE, &ha_dev,
"[ha_full] PUMP: NEW STATE = PUMP_COMPLETE\n");
ha_state.pump_state = PUMP_COMPLETE;
}
if (ha_state.frq) {
ha_fast_queue_check();
} else {
cio_rexpress(slot, SCQRESIZE, &rqe, rapp_data);
sim_debug(HA_TRACE, &ha_dev,
"[ha_express] Handling Express Request. subdev=%02x\n",
rqe.subdevice);
ha_cmd(rqe.opcode, rqe.subdevice, rqe.address, rqe.byte_count, TRUE);
}
}
@@ -815,12 +845,18 @@ static void ha_cmd(uint8 op, uint8 subdev, uint32 addr, int32 len, t_bool expres
sim_debug(HA_TRACE, &ha_dev,
"[ha_cmd] SCSI Force Function Call. (CRC=%08x)\n",
ha_crc);
if (ha_crc == DIAG_CRC_1) {
pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */
pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */
pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */
pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */
pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */
for (i = 0; i < sizeof(diag_crc) / sizeof(diag_crc[0]); i++) {
if (ha_crc == diag_crc[i]) {
sim_debug(HA_TRACE, &ha_dev,
"[ha_cmd] Found matching diagnostics CRC at position %d (%08x==%08x).\n",
i, ha_crc, diag_crc[i]);
pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */
pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */
pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */
pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */
pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */
break;
}
}
cio[ha_state.slot].sysgen_s = 0;
@@ -1077,12 +1113,21 @@ static void ha_cmd(uint8 op, uint8 subdev, uint32 addr, int32 len, t_bool expres
tc = HA_SCSI_ID;
ha_cmd_prep(tc, op, subdev, express);
if (len > HA_EDT_LEN) {
sim_debug(HA_TRACE, &ha_dev,
"[ha_cmd] SCSI DOWNLOAD EDT: Requested to download %d bytes; downloading only %d byte\n",
len, HA_EDT_LEN);
len = HA_EDT_LEN;
}
sim_debug(HA_TRACE, &ha_dev,
"[ha_cmd] SCSI DOWNLOAD EDT (%d bytes to address %08x)\n",
len, addr);
for (i = 0; i < len; i++) {
pwrite_b(addr + i, ha_state.edt[i], BUS_PER);
sim_debug(HA_TRACE, &ha_dev,
"[ha_cmd] SCSI DOWNLOAD EDT: edt[%i] = %02x\n", i, ha_state.edt[i]);
}
ha_state.ts[tc].rep.status = CIO_SUCCESS;
@@ -1099,12 +1144,21 @@ static void ha_cmd(uint8 op, uint8 subdev, uint32 addr, int32 len, t_bool expres
tc = HA_SCSI_ID;
ha_cmd_prep(tc, op, subdev, express);
if (len > HA_EDT_LEN) {
sim_debug(HA_TRACE, &ha_dev,
"[ha_cmd] SCSI UPLOAD EDT: Requested to upload %d bytes; uploading only %d byte\n",
len, HA_EDT_LEN);
len = HA_EDT_LEN;
}
sim_debug(HA_TRACE, &ha_dev,
"[ha_cmd] SCSI UPLOAD EDT (%d bytes from address %08x)\n",
len, addr);
for (i = 0; i < len; i++) {
ha_state.edt[i] = pread_b(addr + i, BUS_PER);
sim_debug(HA_TRACE, &ha_dev,
"[ha_cmd] SCSI UPLOAD EDT: edt[%i] = %02x\n", i, ha_state.edt[i]);
}
ha_state.ts[tc].rep.status = CIO_SUCCESS;

View File

@@ -32,6 +32,7 @@ add_simulator(3b2
3b2_io.c
3b2_ports.c
3b2_ctc.c
3b2_scsi.c
3b2_ni.c
INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}

View File

@@ -8,7 +8,7 @@ This module contains the source for two simulators:
Full documentation for the 3B2 simulator is available here:
- https://loomcom.com/3b2/emulator.html
- https://loomcom.com/3b2/emulator/
3B2/400 Simulator Devices
-------------------------
@@ -31,6 +31,7 @@ devices are given in parentheses:
- CM195A Ethernet Network Interface (NI)
- CM195B 4-port Serial MUX (PORTS)
- CM195H Cartridge Tape Controller (CTC)
- CM195W SCSI Host Adapter (SCSI)
3B2/700 Simulator Devices
-------------------------
@@ -49,6 +50,6 @@ devices are given in parentheses:
- TMS2793 Integrated Floppy Controller (IFLOPPY)
- Non-Volatile Memory (NVRAM)
- MM58274C Time Of Day Clock (TOD)
- CM195W SCSI Host Adapter (SCSI)
- CM195A Ethernet Network Interface (NI)
- CM195B 4-port Serial MUX (PORTS)
- CM195W SCSI Host Adapter (SCSI)