3B2-700 Initial Public Release

This commit introduces dozens of changes to make the 3B2-700 simulator
fully functional and ready for wider use. In addition to 3B2-700
availability, this commit includes a tremendous amount of refactoring
of the 3B2-400 and common code to make the project structure easier to
maintain and reason about.

One final important change: ROM files are no longer included in the
source code. 3B2 ROM images must be obtained separately and loaded
into the simulator before boot.

Changes:

- The 3b2 target has been aliased to 3b2-400
- The formerly named 3b2-600 project has become 3b2-700
- SCSI QIC tape support has been added to sim_scsi.c
- Header files have been reworked to reduce complexity of includes
- Common code has been consolidated
- Timer code has been unified
This commit is contained in:
Seth Morabito 2022-09-15 07:05:18 -07:00
parent d862d024ea
commit 9b62da6567
67 changed files with 26013 additions and 40292 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/* 3b2_cpu.h: AT&T 3B2 CPU (WE32100 and WE32200) Header /* 3b2_cpu.h: WE32100 and WE32200 CPU
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -127,6 +127,29 @@
#define PSW_CFD_MASK (1u << PSW_CFD) #define PSW_CFD_MASK (1u << PSW_CFD)
#define PSW_CUR_IPL (((R[NUM_PSW] & PSW_IPL_MASK) >> PSW_IPL) & 0xf) #define PSW_CUR_IPL (((R[NUM_PSW] & PSW_IPL_MASK) >> PSW_IPL) & 0xf)
#if defined(REV3)
#define PSW_X 26
#define PSW_AR 27
#define PSW_EXUC 28
#define PSW_EA 29
#define PSW_X_MASK (1u << PSW_X)
#define PSW_AR_MASK (1u << PSW_AR)
#define PSW_EXUC_MASK (1u << PSW_EXUC)
#define PSW_EA_MASK (1u << PSW_EA)
#endif
#if defined(REV3)
#define QIE_PSW_MASK (PSW_EA_MASK|PSW_EXUC_MASK|PSW_X_MASK| \
PSW_CFD_MASK|PSW_QIE_MASK|PSW_CD_MASK|PSW_OE_MASK| \
PSW_N_MASK|PSW_Z_MASK|PSW_V_MASK|PSW_C_MASK| \
PSW_TE_MASK|PSW_IPL_MASK|PSW_PM_MASK|PSW_I_MASK)
#else
#define QIE_PSW_MASK (PSW_CFD_MASK|PSW_QIE_MASK|PSW_CD_MASK|PSW_OE_MASK| \
PSW_N_MASK|PSW_Z_MASK|PSW_V_MASK|PSW_C_MASK| \
PSW_TE_MASK|PSW_IPL_MASK|PSW_PM_MASK|PSW_I_MASK)
#endif
/* A helper to set the PSW, preserving read-only fields */ /* A helper to set the PSW, preserving read-only fields */
#define PSW_RO_MASK 0x17f /* ET, TM, ISC, and R are read-only! */ #define PSW_RO_MASK 0x17f /* ET, TM, ISC, and R are read-only! */
#define WRITE_PSW(V) (R[NUM_PSW] = ((R[NUM_PSW] & PSW_RO_MASK) | ((V) & ~PSW_RO_MASK))) #define WRITE_PSW(V) (R[NUM_PSW] = ((R[NUM_PSW] & PSW_RO_MASK) | ((V) & ~PSW_RO_MASK)))
@ -225,7 +248,6 @@
* Opcodes * Opcodes
*/ */
typedef enum { typedef enum {
HALT = 0x00, /* Undocumented instruction */
SPOPRD = 0x02, SPOPRD = 0x02,
SPOPD2 = 0x03, SPOPD2 = 0x03,
MOVAW = 0x04, MOVAW = 0x04,
@ -310,7 +332,7 @@ typedef enum {
RGTRU = 0x54, RGTRU = 0x54,
BGUH = 0x56, BGUH = 0x56,
BGUB = 0x57, BGUB = 0x57,
BLSSU = 0x58, RLSSU = 0x58,
BLUH = 0x5A, BLUH = 0x5A,
BLUB = 0x5B, BLUB = 0x5B,
RLEQU = 0x5C, RLEQU = 0x5C,
@ -455,6 +477,9 @@ typedef enum {
RETG = 0x3045, RETG = 0x3045,
GATE = 0x3061, GATE = 0x3061,
CALLPS = 0x30ac, CALLPS = 0x30ac,
#if defined(REV3)
UCALLPS = 0x30c0,
#endif
RETPS = 0x30c8 RETPS = 0x30c8
} opcode; } opcode;
@ -498,6 +523,8 @@ typedef enum {
typedef enum { typedef enum {
OP_NONE, /* NULL type */ OP_NONE, /* NULL type */
OP_DESC, /* Descriptor byte */ OP_DESC, /* Descriptor byte */
OP_DESB, /* Descriptor with byte displacement (WE32200 only) */
OP_DESH, /* Descriptor with halfword displacement (WE32200 only) */
OP_BYTE, /* 8-bit signed value */ OP_BYTE, /* 8-bit signed value */
OP_HALF, /* 16-bit signed value */ OP_HALF, /* 16-bit signed value */
OP_COPR /* Coprocessor instruction */ OP_COPR /* Coprocessor instruction */
@ -536,6 +563,9 @@ typedef struct {
typedef struct { typedef struct {
uint8 mode; /* Embedded data addressing mode */ uint8 mode; /* Embedded data addressing mode */
uint8 reg; /* Operand register (0-15) */ uint8 reg; /* Operand register (0-15) */
#if defined(REV3)
uint8 reg2; /* Operand register 2 (16-31) */
#endif
int8 dtype; /* Default type for the operand */ int8 dtype; /* Default type for the operand */
int8 etype; /* Expanded type (-1 if none) */ int8 etype; /* Expanded type (-1 if none) */
union { union {
@ -562,18 +592,6 @@ typedef struct {
operand operands[4]; operand operands[4];
} instr; } instr;
/*
* A mapping of CIO identifier word to CIO device name.
*
* Each CIO expansion card in a 3B2 system identifies itself with a
* well-knon 16-bit value, used by drivers to identify the type of
* card installed in each slot.
*/
typedef struct {
uint16 id;
const char name[8];
} cio_device;
/* Function prototypes */ /* Function prototypes */
t_stat sys_boot(int32 flag, CONST char *ptr); t_stat sys_boot(int32 flag, CONST char *ptr);
t_stat cpu_svc(UNIT *uptr); t_stat cpu_svc(UNIT *uptr);
@ -650,13 +668,13 @@ void cpu_abort(uint8 et, uint8 isc);
#define CPU_SET_INT(flags) (sbd_int_req |= flags) #define CPU_SET_INT(flags) (sbd_int_req |= flags)
#define CPU_CLR_INT(flags) (sbd_int_req &= ~(flags)) #define CPU_CLR_INT(flags) (sbd_int_req &= ~(flags))
extern t_bool rom_loaded;
extern volatile int32 stop_reason; extern volatile int32 stop_reason;
extern uint16 sbd_int_req; extern uint16 sbd_int_req;
extern uint32 rom_size;
extern instr *cpu_instr; extern instr *cpu_instr;
extern t_bool cpu_nmi; extern t_bool cpu_nmi;
extern uint32 *ROM; extern uint8 *ROM;
extern uint32 *RAM; extern uint8 *RAM;
extern uint32 R[NUM_REGISTERS]; extern uint32 R[NUM_REGISTERS];
extern REG cpu_reg[]; extern REG cpu_reg[];
extern UNIT cpu_unit; extern UNIT cpu_unit;

View file

@ -1,6 +1,6 @@
/* 3b2_csr.h: Common CSR header /* 3b2_csr.h: Common CSR/CSER header
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -41,4 +41,6 @@
#define CLR_CSR(FLAGS) (csr_data &= ~(FLAGS)) #define CLR_CSR(FLAGS) (csr_data &= ~(FLAGS))
#define CSR(FLAGS) ((csr_data & FLAGS) != 0) #define CSR(FLAGS) ((csr_data & FLAGS) != 0)
extern CSR_DATA csr_data;
#endif /* _3B2_CSR_H_ */ #endif /* _3B2_CSR_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_ctc.c: AT&T 3B2 Model 400 "CTC" feature card /* 3b2_ctc.c: CM195H 23MB Cartridge Tape Controller CIO Card
Copyright (c) 2018, Seth J. Morabito Copyright (c) 2018-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -61,12 +61,7 @@
#define VTOC_BLOCK 0 #define VTOC_BLOCK 0
/* Static function declarations */ static uint8 int_slot; /* Interrupting card ID */
static t_stat ctc_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat ctc_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat ctc_show_queue_common(FILE *st, UNIT *uptr, int32 val, CONST void *desc, t_bool rq);
static uint8 int_cid; /* Interrupting card ID */
static uint8 int_subdev; /* Interrupting subdevice */ static uint8 int_subdev; /* Interrupting subdevice */
static t_bool ctc_conf = FALSE; /* Has a CTC card been configured? */ static t_bool ctc_conf = FALSE; /* Has a CTC card been configured? */
static uint32 ctc_crc; /* CRC32 of downloaded memory */ static uint32 ctc_crc; /* CRC32 of downloaded memory */
@ -105,10 +100,6 @@ MTAB ctc_mod[] = {
&set_writelock, &show_writelock, NULL, "Write enable tape drive" }, &set_writelock, &show_writelock, NULL, "Write enable tape drive" },
{ MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED", { MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED",
&set_writelock, NULL, NULL, "Write lock tape drive" }, &set_writelock, NULL, NULL, "Write lock tape drive" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "RQUEUE=n", NULL,
NULL, &ctc_show_rqueue, NULL, "Display Request Queue for card n" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "CQUEUE=n", NULL,
NULL, &ctc_show_cqueue, NULL, "Display Completion Queue for card n" },
{ 0 } { 0 }
}; };
@ -147,9 +138,9 @@ DEVICE ctc_dev = {
NULL, /* device description */ NULL, /* device description */
}; };
static void cio_irq(uint8 cid, uint8 dev, int32 delay) static void cio_irq(uint8 slot, uint8 dev, int32 delay)
{ {
int_cid = cid; int_slot = slot;
int_subdev = dev & 0x3f; int_subdev = dev & 0x3f;
sim_activate_after(&ctc_unit, delay); sim_activate_after(&ctc_unit, delay);
} }
@ -204,49 +195,49 @@ static void ctc_update_vtoc(uint32 maxpass,
{ {
uint32 i; uint32 i;
pwrite_w(vtoc_addr + 12, VTOC_VALID); pwrite_w(vtoc_addr + 12, VTOC_VALID, BUS_PER);
pwrite_w(vtoc_addr + 16, vtoc->version); pwrite_w(vtoc_addr + 16, vtoc->version, BUS_PER);
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
pwrite_b(vtoc_addr + 20 + i, (uint8)(vtoc->volume[i])); pwrite_b(vtoc_addr + 20 + i, (uint8)(vtoc->volume[i]), BUS_PER);
} }
pwrite_h(vtoc_addr + 28, vtoc->sectorsz); pwrite_h(vtoc_addr + 28, vtoc->sectorsz, BUS_PER);
pwrite_h(vtoc_addr + 30, vtoc->nparts); pwrite_h(vtoc_addr + 30, vtoc->nparts, BUS_PER);
for (i = 0; i < VTOC_PART; i++) { for (i = 0; i < VTOC_PART; i++) {
pwrite_h(vtoc_addr + 72 + (i * 12) + 0, vtoc_table[i].id); pwrite_h(vtoc_addr + 72 + (i * 12) + 0, vtoc_table[i].id, BUS_PER);
pwrite_h(vtoc_addr + 72 + (i * 12) + 2, vtoc_table[i].flag); pwrite_h(vtoc_addr + 72 + (i * 12) + 2, vtoc_table[i].flag, BUS_PER);
pwrite_w(vtoc_addr + 72 + (i * 12) + 4, vtoc_table[i].sstart); pwrite_w(vtoc_addr + 72 + (i * 12) + 4, vtoc_table[i].sstart, BUS_PER);
pwrite_w(vtoc_addr + 72 + (i * 12) + 8, vtoc_table[i].ssize); pwrite_w(vtoc_addr + 72 + (i * 12) + 8, vtoc_table[i].ssize, BUS_PER);
} }
/* Write the pdinfo */ /* Write the pdinfo */
pwrite_w(pdinfo_addr, pdinfo->driveid); pwrite_w(pdinfo_addr, pdinfo->driveid, BUS_PER);
pwrite_w(pdinfo_addr + 4, pdinfo->sanity); pwrite_w(pdinfo_addr + 4, pdinfo->sanity, BUS_PER);
pwrite_w(pdinfo_addr + 8, pdinfo->version); pwrite_w(pdinfo_addr + 8, pdinfo->version, BUS_PER);
for (i = 0; i < 12; i++) { for (i = 0; i < 12; i++) {
pwrite_b(pdinfo_addr + 12 + i, pdinfo->serial[i]); pwrite_b(pdinfo_addr + 12 + i, pdinfo->serial[i], BUS_PER);
} }
pwrite_w(pdinfo_addr + 24, pdinfo->cyls); pwrite_w(pdinfo_addr + 24, pdinfo->cyls, BUS_PER);
pwrite_w(pdinfo_addr + 28, pdinfo->tracks); pwrite_w(pdinfo_addr + 28, pdinfo->tracks, BUS_PER);
pwrite_w(pdinfo_addr + 32, pdinfo->sectors); pwrite_w(pdinfo_addr + 32, pdinfo->sectors, BUS_PER);
pwrite_w(pdinfo_addr + 36, pdinfo->bytes); pwrite_w(pdinfo_addr + 36, pdinfo->bytes, BUS_PER);
pwrite_w(pdinfo_addr + 40, pdinfo->logicalst); pwrite_w(pdinfo_addr + 40, pdinfo->logicalst, BUS_PER);
pwrite_w(pdinfo_addr + 44, pdinfo->errlogst); pwrite_w(pdinfo_addr + 44, pdinfo->errlogst, BUS_PER);
pwrite_w(pdinfo_addr + 48, pdinfo->errlogsz); pwrite_w(pdinfo_addr + 48, pdinfo->errlogsz, BUS_PER);
pwrite_w(pdinfo_addr + 52, pdinfo->mfgst); pwrite_w(pdinfo_addr + 52, pdinfo->mfgst, BUS_PER);
pwrite_w(pdinfo_addr + 56, pdinfo->mfgsz); pwrite_w(pdinfo_addr + 56, pdinfo->mfgsz, BUS_PER);
pwrite_w(pdinfo_addr + 60, pdinfo->defectst); pwrite_w(pdinfo_addr + 60, pdinfo->defectst, BUS_PER);
pwrite_w(pdinfo_addr + 64, pdinfo->defectsz); pwrite_w(pdinfo_addr + 64, pdinfo->defectsz, BUS_PER);
pwrite_w(pdinfo_addr + 68, pdinfo->relno); pwrite_w(pdinfo_addr + 68, pdinfo->relno, BUS_PER);
pwrite_w(pdinfo_addr + 72, pdinfo->relst); pwrite_w(pdinfo_addr + 72, pdinfo->relst, BUS_PER);
pwrite_w(pdinfo_addr + 76, pdinfo->relsz); pwrite_w(pdinfo_addr + 76, pdinfo->relsz, BUS_PER);
pwrite_w(pdinfo_addr + 80, pdinfo->relnext); pwrite_w(pdinfo_addr + 80, pdinfo->relnext, BUS_PER);
/* Now something horrible happens. We sneak RIGHT off the end of /* Now something horrible happens. We sneak RIGHT off the end of
* the pdinfo struct and reach deep into the pdsector struct that * the pdinfo struct and reach deep into the pdsector struct that
* it is part of. */ * it is part of. */
pwrite_w(pdinfo_addr + 128, maxpass); pwrite_w(pdinfo_addr + 128, maxpass, BUS_PER);
} }
/* /*
@ -257,7 +248,7 @@ static void ctc_update_vtoc(uint32 maxpass,
* expects response parameters to be placed in specific fields of the * expects response parameters to be placed in specific fields of the
* Completion Queue entry. It can be confusing to follow. * Completion Queue entry. It can be confusing to follow.
*/ */
static void ctc_cmd(uint8 cid, static void ctc_cmd(uint8 slot,
cio_entry *rqe, uint8 *rapp_data, cio_entry *rqe, uint8 *rapp_data,
cio_entry *cqe, uint8 *capp_data) cio_entry *cqe, uint8 *capp_data)
{ {
@ -283,7 +274,7 @@ static void ctc_cmd(uint8 cid,
switch(rqe->opcode) { switch(rqe->opcode) {
case CIO_DLM: case CIO_DLM:
for (i = 0; i < rqe->byte_count; i++) { for (i = 0; i < rqe->byte_count; i++) {
ctc_crc = cio_crc32_shift(ctc_crc, pread_b(rqe->address + i)); ctc_crc = cio_crc32_shift(ctc_crc, pread_b(rqe->address + i, BUS_PER));
} }
sim_debug(TRACE_DBG, &ctc_dev, sim_debug(TRACE_DBG, &ctc_dev,
"[ctc_cmd] CIO Download Memory: bytecnt=%04x " "[ctc_cmd] CIO Download Memory: bytecnt=%04x "
@ -311,11 +302,11 @@ static void ctc_cmd(uint8 cid,
if (ctc_crc == CTC_DIAG_CRC1 || if (ctc_crc == CTC_DIAG_CRC1 ||
ctc_crc == CTC_DIAG_CRC2 || ctc_crc == CTC_DIAG_CRC2 ||
ctc_crc == CTC_DIAG_CRC3) { ctc_crc == CTC_DIAG_CRC3) {
pwrite_h(0x200f000, 0x1); /* Test success */ pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */
pwrite_h(0x200f002, 0x0); /* Test Number */ pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */
pwrite_h(0x200f004, 0x0); /* Actual */ pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */
pwrite_h(0x200f006, 0x0); /* Expected */ pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */
pwrite_b(0x200f008, 0x1); /* Success flag again */ pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */
} }
/* An interesting (?) side-effect of FORCE FUNCTION CALL is /* An interesting (?) side-effect of FORCE FUNCTION CALL is
@ -323,7 +314,7 @@ static void ctc_cmd(uint8 cid,
* required in order for new commands to work. In fact, an * required in order for new commands to work. In fact, an
* INT0/INT1 combo _without_ a RESET can sysgen the board. So, * INT0/INT1 combo _without_ a RESET can sysgen the board. So,
* we reset the command bits here. */ * we reset the command bits here. */
cio[cid].sysgen_s = 0; cio[slot].sysgen_s = 0;
cqe->opcode = CTC_SUCCESS; cqe->opcode = CTC_SUCCESS;
break; break;
case CIO_DOS: case CIO_DOS:
@ -339,9 +330,9 @@ static void ctc_cmd(uint8 cid,
rqe->opcode); rqe->opcode);
delay = DELAY_DSD; delay = DELAY_DSD;
/* Write subdevice information to the host. */ /* Write subdevice information to the host. */
pwrite_h(rqe->address, CTC_NUM_SD); pwrite_h(rqe->address, CTC_NUM_SD, BUS_PER);
pwrite_h(rqe->address + 2, CTC_SD_FT25); pwrite_h(rqe->address + 2, CTC_SD_FT25, BUS_PER);
pwrite_h(rqe->address + 4, CTC_SD_FD5); pwrite_h(rqe->address + 4, CTC_SD_FD5, BUS_PER);
cqe->opcode = CTC_SUCCESS; cqe->opcode = CTC_SUCCESS;
break; break;
case CTC_FORMAT: case CTC_FORMAT:
@ -503,7 +494,7 @@ static void ctc_cmd(uint8 cid,
ctc_state[dev].time += 10; ctc_state[dev].time += 10;
for (j = 0; j < VTOC_SECSZ; j++) { for (j = 0; j < VTOC_SECSZ; j++) {
/* Fill the buffer */ /* Fill the buffer */
sec_buf[j] = pread_b(rqe->address + (b * VTOC_SECSZ) + j); sec_buf[j] = pread_b(rqe->address + (b * VTOC_SECSZ) + j, BUS_PER);
} }
lba = blkno + b; lba = blkno + b;
result = sim_disk_wrsect(&ctc_unit, lba, sec_buf, &secrw, 1); result = sim_disk_wrsect(&ctc_unit, lba, sec_buf, &secrw, 1);
@ -601,7 +592,7 @@ static void ctc_cmd(uint8 cid,
offset = j; offset = j;
} }
c = sec_buf[offset]; c = sec_buf[offset];
pwrite_b(dest++, c); pwrite_b(dest++, c, BUS_PER);
ctc_state[dev].bytnum++; ctc_state[dev].bytnum++;
} }
} else { } else {
@ -634,10 +625,10 @@ static void ctc_cmd(uint8 cid,
break; break;
} }
cio_irq(cid, rqe->subdevice, delay); cio_irq(slot, rqe->subdevice, delay);
} }
void ctc_sysgen(uint8 cid) void ctc_sysgen(uint8 slot)
{ {
cio_entry cqe = {0}; cio_entry cqe = {0};
uint8 rapp_data[12] = {0}; uint8 rapp_data[12] = {0};
@ -645,23 +636,23 @@ void ctc_sysgen(uint8 cid)
ctc_crc = 0; ctc_crc = 0;
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] Handling Sysgen.\n"); sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] Handling Sysgen.\n");
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqp=%08x\n", cio[cid].rqp); sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqp=%08x\n", cio[slot].rqp);
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqp=%08x\n", cio[cid].cqp); sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqp=%08x\n", cio[slot].cqp);
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqs=%d\n", cio[cid].rqs); sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqs=%d\n", cio[slot].rqs);
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqs=%d\n", cio[cid].cqs); sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqs=%d\n", cio[slot].cqs);
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] ivec=%d\n", cio[cid].ivec); sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] ivec=%d\n", cio[slot].ivec);
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] no_rque=%d\n", cio[cid].no_rque); sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] no_rque=%d\n", cio[slot].no_rque);
cqe.opcode = 3; /* Sysgen success! */ cqe.opcode = 3; /* Sysgen success! */
cio_cexpress(cid, CTQCESIZE, &cqe, rapp_data); cio_cexpress(slot, CTQCESIZE, &cqe, rapp_data);
cio_cqueue(cid, CIO_STAT, CTQCESIZE, &cqe, rapp_data); cio_cqueue(slot, CIO_STAT, CTQCESIZE, &cqe, rapp_data);
int_cid = cid; int_slot = slot;
sim_activate_after(&ctc_unit, DELAY_SYSGEN); sim_activate_after(&ctc_unit, DELAY_SYSGEN);
} }
void ctc_express(uint8 cid) void ctc_express(uint8 slot)
{ {
cio_entry rqe, cqe; cio_entry rqe, cqe;
uint8 rapp_data[12] = {0}; uint8 rapp_data[12] = {0};
@ -669,16 +660,13 @@ void ctc_express(uint8 cid)
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_express] Handling Express Request\n"); sim_debug(TRACE_DBG, &ctc_dev, "[ctc_express] Handling Express Request\n");
cio_rexpress(cid, CTQRESIZE, &rqe, rapp_data); cio_rexpress(slot, CTQRESIZE, &rqe, rapp_data);
ctc_cmd(cid, &rqe, rapp_data, &cqe, capp_data); ctc_cmd(slot, &rqe, rapp_data, &cqe, capp_data);
dump_entry(TRACE_DBG, &ctc_dev, "COMPLETION", cio_cexpress(slot, CTQCESIZE, &cqe, capp_data);
CTQCESIZE, &cqe, capp_data);
cio_cexpress(cid, CTQCESIZE, &cqe, capp_data);
} }
void ctc_full(uint8 cid) void ctc_full(uint8 slot)
{ {
cio_entry rqe, cqe; cio_entry rqe, cqe;
uint8 rapp_data[12] = {0}; uint8 rapp_data[12] = {0};
@ -686,69 +674,35 @@ void ctc_full(uint8 cid)
sim_debug(TRACE_DBG, &ctc_dev, "[ctc_full] Handling Full Request\n"); sim_debug(TRACE_DBG, &ctc_dev, "[ctc_full] Handling Full Request\n");
while (cio_cqueue_avail(cid, CTQCESIZE) && while (cio_cqueue_avail(slot, CTQCESIZE) &&
cio_rqueue(cid, TAPE_DEV, CTQRESIZE, &rqe, rapp_data) == SCPE_OK) { cio_rqueue(slot, TAPE_DEV, CTQRESIZE, &rqe, rapp_data) == SCPE_OK) {
ctc_cmd(cid, &rqe, rapp_data, &cqe, capp_data); ctc_cmd(slot, &rqe, rapp_data, &cqe, capp_data);
} }
cio_cqueue(cid, CIO_STAT, CTQCESIZE, &cqe, capp_data); cio_cqueue(slot, CIO_STAT, CTQCESIZE, &cqe, capp_data);
} }
t_stat ctc_reset(DEVICE *dptr) t_stat ctc_reset(DEVICE *dptr)
{ {
uint8 cid; uint8 slot;
t_stat r;
ctc_crc = 0; ctc_crc = 0;
sim_debug(TRACE_DBG, &ctc_dev,
"[ctc_reset] Resetting CTC device\n");
memset(ctc_state, 0, 2 * sizeof(CTC_STATE)); memset(ctc_state, 0, 2 * sizeof(CTC_STATE));
if (dptr->flags & DEV_DIS) { if (dptr->flags & DEV_DIS) {
sim_debug(TRACE_DBG, &ctc_dev, cio_remove_all(CTC_ID);
"[ctc_reset] REMOVING CARD\n");
for (cid = 0; cid < CIO_SLOTS; cid++) {
if (cio[cid].id == CTC_ID) {
break;
}
}
if (cid == CIO_SLOTS) {
/* No card was ever attached */
return SCPE_OK;
}
cio[cid].id = 0;
cio[cid].ipl = 0;
cio[cid].ivec = 0;
cio[cid].exp_handler = NULL;
cio[cid].full_handler = NULL;
cio[cid].sysgen = NULL;
ctc_conf = FALSE; ctc_conf = FALSE;
} else if (!ctc_conf) { return SCPE_OK;
sim_debug(TRACE_DBG, &ctc_dev, }
"[ctc_reset] ATTACHING CARD\n");
/* Find the first avaialable slot */ if (!ctc_conf) {
for (cid = 0; cid < CIO_SLOTS; cid++) { r = cio_install(CTC_ID, "CTC", CTC_IPL,
if (cio[cid].id == 0) { &ctc_express, &ctc_full, &ctc_sysgen, NULL,
break; &slot);
} if (r != SCPE_OK) {
return r;
} }
/* Do we have room? */
if (cid == CIO_SLOTS) {
return SCPE_NXM;
}
cio[cid].id = CTC_ID;
cio[cid].ipl = CTC_IPL;
cio[cid].exp_handler = &ctc_express;
cio[cid].full_handler = &ctc_full;
cio[cid].sysgen = &ctc_sysgen;
ctc_conf = TRUE; ctc_conf = TRUE;
} }
@ -759,20 +713,20 @@ t_stat ctc_svc(UNIT *uptr)
{ {
uint16 lp, ulp; uint16 lp, ulp;
if (cio[int_cid].ivec > 0) { if (cio[int_slot].ivec > 0) {
sim_debug(TRACE_DBG, &ctc_dev, sim_debug(TRACE_DBG, &ctc_dev,
"[cio_svc] IRQ for board %d (VEC=%d)\n", "[cio_svc] IRQ for board %d (VEC=%d)\n",
int_cid, cio[int_cid].ivec); int_slot, cio[int_slot].ivec);
CIO_SET_INT(int_cid); CIO_SET_INT(int_slot);
} }
/* Check to see if the completion queue has more work in it. We /* Check to see if the completion queue has more work in it. We
* need to schedule an interrupt for each job if we've fallen * need to schedule an interrupt for each job if we've fallen
* behind (this should be rare) */ * behind (this should be rare) */
lp = cio_c_lp(int_cid, CTQCESIZE); lp = cio_c_lp(int_slot, CTQCESIZE);
ulp = cio_c_ulp(int_cid, CTQCESIZE); ulp = cio_c_ulp(int_slot, CTQCESIZE);
if ((ulp + CTQCESIZE) % (CTQCESIZE * cio[int_cid].cqs) != lp) { if ((ulp + CTQCESIZE) % (CTQCESIZE * cio[int_slot].cqs) != lp) {
sim_debug(TRACE_DBG, &ctc_dev, sim_debug(TRACE_DBG, &ctc_dev,
"[cio_svc] Completion queue has fallen behind (lp=%04x ulp=%04x)\n", "[cio_svc] Completion queue has fallen behind (lp=%04x ulp=%04x)\n",
lp, ulp); lp, ulp);
@ -792,117 +746,3 @@ t_stat ctc_detach(UNIT *uptr)
{ {
return sim_disk_detach(uptr); return sim_disk_detach(uptr);
} }
t_stat ctc_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
return ctc_show_queue_common(st, uptr, val, desc, TRUE);
}
t_stat ctc_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
return ctc_show_queue_common(st, uptr, val, desc, FALSE);
}
static t_stat ctc_show_queue_common(FILE *st, UNIT *uptr, int32 val,
CONST void *desc, t_bool rq)
{
uint8 cid;
char *cptr = (char *) desc;
t_stat result;
uint32 ptr, size, no_rque, i, j;
uint8 op, dev, seq, cmdstat;
if (cptr) {
cid = (uint8) get_uint(cptr, 10, 12, &result);
if (result != SCPE_OK) {
return SCPE_ARG;
}
} else {
return SCPE_ARG;
}
/* If the card is not sysgen'ed, give up */
if (cio[cid].sysgen_s != CIO_SYSGEN) {
fprintf(st, "No card in slot %d, or card has not completed sysgen\n", cid);
return SCPE_ARG;
}
if (rq) {
ptr = cio[cid].rqp;
size = cio[cid].rqs;
no_rque = cio[cid].no_rque;
fprintf(st, "Dumping %d Request Queues\n", no_rque);
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "EXPRESS ENTRY:\n");
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2));
fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3));
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
ptr += CTQRESIZE;
for (i = 0; i < no_rque; i++) {
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "REQUEST QUEUE %d\n", i);
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / CTQRESIZE);
fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / CTQRESIZE);
fprintf(st, "---------------------------------------------------------\n");
ptr += 4;
for (j = 0; j < size; j++) {
dev = pread_b(ptr + 2);
op = pread_b(ptr + 3);
seq = (dev & 0x40) >> 6;
cmdstat = (dev & 0x80) >> 7;
fprintf(st, "REQUEST ENTRY %d\n", j);
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
fprintf(st, " Subdevice: %d\n", dev & 0x3f);
fprintf(st, " Cmd/Stat: %d\n", cmdstat);
fprintf(st, " Seqbit: %d\n", seq);
fprintf(st, " Opcode: 0x%02x (%d)\n", op, op);
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
fprintf(st, " App Data: 0x%08x 0x%08x 0x%08x\n",
pread_w(ptr + 8), pread_w(ptr + 12), pread_w(ptr + 16));
ptr += CTQRESIZE;
}
}
} else {
ptr = cio[cid].cqp;
size = cio[cid].cqs;
no_rque = 0; /* Not used */
fprintf(st, "Dumping Completion Queue\n");
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "EXPRESS ENTRY:\n");
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2));
fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3));
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
ptr += CTQCESIZE;
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / CTQCESIZE);
fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / CTQCESIZE);
fprintf(st, "---------------------------------------------------------\n");
ptr += 4;
for (i = 0; i < size; i++) {
dev = pread_b(ptr + 2);
op = pread_b(ptr + 3);
seq = (dev & 0x40) >> 6;
cmdstat = (dev & 0x80) >> 7;
fprintf(st, "COMPLETION ENTRY %d\n", i);
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
fprintf(st, " Subdevice: %d\n", dev & 0x3f);
fprintf(st, " Cmd/Stat: %d\n", cmdstat);
fprintf(st, " Seqbit: %d\n", seq);
fprintf(st, " Opcode: 0x%02x (%d)\n", op, op);
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
fprintf(st, " App Data: 0x%08x 0x%08x\n",
pread_w(ptr + 8), pread_w(ptr + 12));
ptr += CTQCESIZE;
}
}
return SCPE_OK;
}

View file

@ -1,6 +1,6 @@
/* 3b2_ctc.h: AT&T 3B2 Model 400 "CTC" feature card /* 3b2_ctc.h: CM195H 23MB Cartridge Tape Controller CIO Card
Copyright (c) 2018, Seth J. Morabito Copyright (c) 2018-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -146,8 +146,8 @@ t_stat ctc_reset(DEVICE *dptr);
t_stat ctc_svc(UNIT *uptr); t_stat ctc_svc(UNIT *uptr);
t_stat ctc_attach(UNIT *uptr, CONST char *cptr); t_stat ctc_attach(UNIT *uptr, CONST char *cptr);
t_stat ctc_detach(UNIT *uptr); t_stat ctc_detach(UNIT *uptr);
void ctc_sysgen(uint8 cid); void ctc_sysgen(uint8 slot);
void ctc_express(uint8 cid); void ctc_express(uint8 slot);
void ctc_full(uint8 cid); void ctc_full(uint8 slot);
#endif /* _3B2_CTC_H_ */ #endif /* _3B2_CTC_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_defs.h: AT&T 3B2 Shared Simulator Definitions /* 3b2_defs.h: AT&T 3B2 Shared Simulator Definitions
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -81,6 +81,9 @@
#define PCHAR(c) (((char) (c) >= 0x20 && (char) (c) < 0x7f) ? (char) (c) : '.') #define PCHAR(c) (((char) (c) >= 0x20 && (char) (c) < 0x7f) ? (char) (c) : '.')
#define ROM_SIZE (128 * 1024)
#define POLL_WAIT 70000
#define UNIT_V_EXBRK (UNIT_V_UF + 0) #define UNIT_V_EXBRK (UNIT_V_UF + 0)
#define UNIT_V_OPBRK (UNIT_V_UF + 1) #define UNIT_V_OPBRK (UNIT_V_UF + 1)
#define UNIT_EXBRK (1u << UNIT_V_EXBRK) #define UNIT_EXBRK (1u << UNIT_V_EXBRK)
@ -88,6 +91,7 @@
#define EX_V_FLAG 1 << 21 #define EX_V_FLAG 1 << 21
#define ROM_BASE 0
#define PHYS_MEM_BASE 0x2000000 #define PHYS_MEM_BASE 0x2000000
#define MSIZ_512K 0x80000 #define MSIZ_512K 0x80000
@ -131,10 +135,8 @@
#define TIMER_INTERVAL 1 #define TIMER_INTERVAL 1
#define TIMER_BUS 2 #define TIMER_BUS 2
/* Timer */ /* Timers */
#define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */ #define TMR_CLK 0 /* Calibrated 100Hz timer */
#define TPS_CLK 100 /* 100 ticks per second */
/* Global symbols */ /* Global symbols */
@ -159,6 +161,6 @@ extern DEVICE tto_dev;
#if defined(REV3) #if defined(REV3)
extern DEVICE flt_dev; extern DEVICE flt_dev;
extern DEVICE ha_dev; extern DEVICE ha_dev;
#endif #endif /* defined(REV3) */
#endif #endif /* _3B2_DEFS_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_dmac.c: AT&T 3B2 DMA Controller Implementation /* 3b2_dmac.c: AM9517 DMA Controller
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -38,6 +38,8 @@
#include "3b2_if.h" #include "3b2_if.h"
#include "3b2_iu.h" #include "3b2_iu.h"
#include "3b2_mem.h" #include "3b2_mem.h"
#include "3b2_stddev.h"
#include "3b2_csr.h"
DMA_STATE dma_state; DMA_STATE dma_state;
@ -71,9 +73,13 @@ dmac_dma_handler device_dma_handlers[] = {
{0, 0, NULL, NULL, NULL } {0, 0, NULL, NULL, NULL }
}; };
uint32 dma_address(uint8 channel, uint32 offset, t_bool r) { uint32 dma_address(uint8 channel, uint32 offset) {
uint32 addr, page; uint32 addr, page;
addr = (PHYS_MEM_BASE + (uint32)(dma_state.channels[channel].addr) + offset); if (DMA_DECR(channel)) {
addr = (PHYS_MEM_BASE + (uint32)(dma_state.channels[channel].addr) - offset);
} else {
addr = (PHYS_MEM_BASE + (uint32)(dma_state.channels[channel].addr) + offset);
}
#if defined (REV3) #if defined (REV3)
page = (uint32)dma_state.channels[channel].page; page = (uint32)dma_state.channels[channel].page;
#else #else
@ -94,6 +100,7 @@ t_stat dmac_reset(DEVICE *dptr)
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
dma_state.channels[i].page = 0; dma_state.channels[i].page = 0;
dma_state.channels[i].addr = 0; dma_state.channels[i].addr = 0;
dma_state.channels[i].mode = 0;
dma_state.channels[i].wcount = 0; dma_state.channels[i].wcount = 0;
dma_state.channels[i].addr_c = 0; dma_state.channels[i].addr_c = 0;
dma_state.channels[i].wcount_c = -1; dma_state.channels[i].wcount_c = -1;
@ -111,83 +118,83 @@ uint32 dmac_read(uint32 pa, size_t size)
reg = pa & 0xff; reg = pa & 0xff;
switch (base) { switch (base) {
case DMA_C: /* 0x48xxx */ case DMA_C:
switch (reg) { switch (reg) {
case 0: /* channel 0 current address reg */ case 0: /* channel 0 current address reg */
data = ((dma_state.channels[0].addr_c) >> (dma_state.bff * 8)) & 0xff; data = ((dma_state.channels[0].addr_c) >> (dma_state.bff * 8)) & 0xff;
sim_debug(READ_MSG, &dmac_dev, sim_debug(READ_MSG, &dmac_dev,
"[%08x] Reading Channel 0 Addr Reg: %08x\n", "Reading Channel 0 Addr Reg: %08x\n",
R[NUM_PC], data); data);
dma_state.bff ^= 1; dma_state.bff ^= 1;
break; break;
case 1: /* channel 0 current address reg */ case 1: /* channel 0 current address reg */
data = ((dma_state.channels[0].wcount_c) >> (dma_state.bff * 8)) & 0xff; data = ((dma_state.channels[0].wcount_c) >> (dma_state.bff * 8)) & 0xff;
sim_debug(READ_MSG, &dmac_dev, sim_debug(READ_MSG, &dmac_dev,
"[%08x] Reading Channel 0 Addr Count Reg: %08x\n", "Reading Channel 0 Addr Count Reg: %08x\n",
R[NUM_PC], data); data);
dma_state.bff ^= 1; dma_state.bff ^= 1;
break; break;
case 2: /* channel 1 current address reg */ case 2: /* channel 1 current address reg */
data = ((dma_state.channels[1].addr_c) >> (dma_state.bff * 8)) & 0xff; data = ((dma_state.channels[1].addr_c) >> (dma_state.bff * 8)) & 0xff;
sim_debug(READ_MSG, &dmac_dev, sim_debug(READ_MSG, &dmac_dev,
"[%08x] Reading Channel 1 Addr Reg: %08x\n", "Reading Channel 1 Addr Reg: %08x\n",
R[NUM_PC], data); data);
dma_state.bff ^= 1; dma_state.bff ^= 1;
break; break;
case 3: /* channel 1 current address reg */ case 3: /* channel 1 current address reg */
data = ((dma_state.channels[1].wcount_c) >> (dma_state.bff * 8)) & 0xff; data = ((dma_state.channels[1].wcount_c) >> (dma_state.bff * 8)) & 0xff;
sim_debug(READ_MSG, &dmac_dev, sim_debug(READ_MSG, &dmac_dev,
"[%08x] Reading Channel 1 Addr Count Reg: %08x\n", "Reading Channel 1 Addr Count Reg: %08x\n",
R[NUM_PC], data); data);
dma_state.bff ^= 1; dma_state.bff ^= 1;
break; break;
case 4: /* channel 2 current address reg */ case 4: /* channel 2 current address reg */
data = ((dma_state.channels[2].addr_c) >> (dma_state.bff * 8)) & 0xff; data = ((dma_state.channels[2].addr_c) >> (dma_state.bff * 8)) & 0xff;
sim_debug(READ_MSG, &dmac_dev, sim_debug(READ_MSG, &dmac_dev,
"[%08x] Reading Channel 2 Addr Reg: %08x\n", "Reading Channel 2 Addr Reg: %08x\n",
R[NUM_PC], data); data);
dma_state.bff ^= 1; dma_state.bff ^= 1;
break; break;
case 5: /* channel 2 current address reg */ case 5: /* channel 2 current address reg */
data = ((dma_state.channels[2].wcount_c) >> (dma_state.bff * 8)) & 0xff; data = ((dma_state.channels[2].wcount_c) >> (dma_state.bff * 8)) & 0xff;
sim_debug(READ_MSG, &dmac_dev, sim_debug(READ_MSG, &dmac_dev,
"[%08x] Reading Channel 2 Addr Count Reg: %08x\n", "Reading Channel 2 Addr Count Reg: %08x\n",
R[NUM_PC], data); data);
dma_state.bff ^= 1; dma_state.bff ^= 1;
break; break;
case 6: /* channel 3 current address reg */ case 6: /* channel 3 current address reg */
data = ((dma_state.channels[3].addr_c) >> (dma_state.bff * 8)) & 0xff; data = ((dma_state.channels[3].addr_c) >> (dma_state.bff * 8)) & 0xff;
sim_debug(READ_MSG, &dmac_dev, sim_debug(READ_MSG, &dmac_dev,
"[%08x] Reading Channel 3 Addr Reg: %08x\n", "Reading Channel 3 Addr Reg: %08x\n",
R[NUM_PC], data); data);
dma_state.bff ^= 1; dma_state.bff ^= 1;
break; break;
case 7: /* channel 3 current address reg */ case 7: /* channel 3 current address reg */
data = ((dma_state.channels[3].wcount_c) >> (dma_state.bff * 8)) & 0xff; data = ((dma_state.channels[3].wcount_c) >> (dma_state.bff * 8)) & 0xff;
sim_debug(READ_MSG, &dmac_dev, sim_debug(READ_MSG, &dmac_dev,
"[%08x] Reading Channel 3 Addr Count Reg: %08x\n", "Reading Channel 3 Addr Count Reg: %08x\n",
R[NUM_PC], data); data);
dma_state.bff ^= 1; dma_state.bff ^= 1;
break; break;
case 8: case 8:
data = dma_state.status; data = dma_state.status;
sim_debug(READ_MSG, &dmac_dev, sim_debug(READ_MSG, &dmac_dev,
"[%08x] Reading DMAC Status %08x\n", "Reading DMAC Status %08x\n",
R[NUM_PC], data); data);
dma_state.status = 0; dma_state.status = 0;
break; break;
default: default:
sim_debug(READ_MSG, &dmac_dev, sim_debug(READ_MSG, &dmac_dev,
"[%08x] DMAC READ %lu B @ %08x\n", "DMAC READ %lu B @ %08x\n",
R[NUM_PC], size, pa); size, pa);
data = 0; data = 0;
} }
return data; return data;
default: default:
sim_debug(READ_MSG, &dmac_dev, sim_debug(READ_MSG, &dmac_dev,
"[%08x] [BASE: %08x] DMAC READ %lu B @ %08x\n", "[BASE: %08x] DMAC READ %lu B @ %08x\n",
R[NUM_PC], base, size, pa); base, size, pa);
return 0; return 0;
} }
} }
@ -200,6 +207,12 @@ void dmac_program(uint8 reg, uint8 val)
uint8 channel_id, i, chan_num; uint8 channel_id, i, chan_num;
dma_channel *channel; dma_channel *channel;
#if defined(REV3)
/* TODO: More general DMA interrupt clearing */
CPU_CLR_INT(INT_UART_DMA);
CLR_CSR(CSRDMA);
#endif
if (reg < 8) { if (reg < 8) {
switch (reg) { switch (reg) {
case 0: case 0:
@ -259,13 +272,13 @@ void dmac_program(uint8 reg, uint8 val)
case 8: /* Command */ case 8: /* Command */
dma_state.command = val; dma_state.command = val;
sim_debug(WRITE_MSG, &dmac_dev, sim_debug(WRITE_MSG, &dmac_dev,
"[%08x] Command: val=%02x\n", "Command: val=%02x\n",
R[NUM_PC], val); val);
break; break;
case 9: /* Request */ case 9: /* Request */
sim_debug(WRITE_MSG, &dmac_dev, sim_debug(WRITE_MSG, &dmac_dev,
"[%08x] Request set: val=%02x\n", "Request set: val=%02x\n",
R[NUM_PC], val); val);
dma_state.request = val; dma_state.request = val;
break; break;
case 10: /* Write Single Mask Register Bit */ case 10: /* Write Single Mask Register Bit */
@ -281,14 +294,15 @@ void dmac_program(uint8 reg, uint8 val)
} }
sim_debug(WRITE_MSG, &dmac_dev, sim_debug(WRITE_MSG, &dmac_dev,
"[%08x] Write Single Mask Register Bit. channel=%d set/clear=%02x\n", "Write Single Mask Register Bit. channel=%d set/clear=%02x\n",
R[NUM_PC], channel_id, (val >> 2) & 1); channel_id, (val >> 2) & 1);
break; break;
case 11: /* Mode */ case 11: /* Mode */
channel_id = val & 3;
sim_debug(WRITE_MSG, &dmac_dev, sim_debug(WRITE_MSG, &dmac_dev,
"[%08x] Mode Set. val=%02x\n", "Mode Set. channel=%d val=%02x\n",
R[NUM_PC], val); channel_id, val);
dma_state.mode = val; dma_state.channels[channel_id].mode = val;
break; break;
case 12: /* Clear Byte Pointer Flip/Flop */ case 12: /* Clear Byte Pointer Flip/Flop */
dma_state.bff = 0; dma_state.bff = 0;
@ -308,19 +322,19 @@ void dmac_program(uint8 reg, uint8 val)
break; break;
case 15: /* Write All Mask Register Bits */ case 15: /* Write All Mask Register Bits */
sim_debug(WRITE_MSG, &dmac_dev, sim_debug(WRITE_MSG, &dmac_dev,
"[%08x] Write DMAC mask (all bits). Val=%02x\n", "Write DMAC mask (all bits). Val=%02x\n",
R[NUM_PC], val); val);
dma_state.mask = val & 0xf; dma_state.mask = val & 0xf;
break; break;
case 16: /* Clear DMAC Interrupt */ case 16: /* Clear DMAC Interrupt */
sim_debug(WRITE_MSG, &dmac_dev, sim_debug(WRITE_MSG, &dmac_dev,
"[%08x] Clear DMAC Interrupt in DMAC. val=%02x\n", "Clear DMA Interrupt in DMAC. val=%02x\n",
R[NUM_PC], val); val);
break; break;
default: default:
sim_debug(WRITE_MSG, &dmac_dev, sim_debug(WRITE_MSG, &dmac_dev,
"[%08x] Unhandled DMAC write. reg=%x val=%02x\n", "Unhandled DMAC write. reg=%x val=%02x\n",
R[NUM_PC], reg, val); reg, val);
break; break;
} }
} }
@ -398,46 +412,45 @@ void dmac_generic_dma(uint8 channel, uint32 service_address)
i = chan->wcount_c; i = chan->wcount_c;
/* TODO: This does not handle decrement-mode transfers, /* TODO: This assumes every transfer is a block mode, which is not
which don't seem to be used in SVR3 */ guaranteed to be valid, but is likely safe? */
switch ((dma_state.mode >> 2) & 0xf) { switch (DMA_XFER(channel)) {
case DMA_MODE_VERIFY: case DMA_XFER_VERIFY:
sim_debug(EXECUTE_MSG, &dmac_dev, sim_debug(EXECUTE_MSG, &dmac_dev,
"[%08x] [dmac_generic_dma channel=%d] unhandled VERIFY request.\n", "[dmac_generic_dma channel=%d] unhandled VERIFY request.\n",
R[NUM_PC], channel); channel);
break; break;
case DMA_MODE_WRITE: case DMA_XFER_WRITE:
sim_debug(EXECUTE_MSG, &dmac_dev, sim_debug(EXECUTE_MSG, &dmac_dev,
"[%08x] [dmac_generic_dma channel=%d] write: %d bytes to %08x from %08x (page=%04x addr=%08x)\n", "[dmac_generic_dma channel=%d] write: %d bytes to %08x from %08x (page=%04x addr=%08x)\n",
R[NUM_PC], channel, channel,
chan->wcount + 1, chan->wcount + 1,
dma_address(channel, 0, TRUE), dma_address(channel, 0),
service_address, service_address,
dma_state.channels[channel].page, dma_state.channels[channel].page,
dma_state.channels[channel].addr); dma_state.channels[channel].addr);
for (; i >= 0; i--) { for (; i >= 0; i--) {
chan->wcount_c--; chan->wcount_c--;
addr = dma_address(channel, chan->ptr, TRUE); addr = dma_address(channel, chan->ptr++);
chan->addr_c = dma_state.channels[channel].addr + chan->ptr; chan->addr_c = addr;
chan->ptr++; data = pread_b(service_address, BUS_PER);
data = pread_b(service_address); write_b(addr, data, BUS_PER);
write_b(addr, data);
} }
break; break;
case DMA_MODE_READ: case DMA_XFER_READ:
sim_debug(EXECUTE_MSG, &dmac_dev, sim_debug(EXECUTE_MSG, &dmac_dev,
"[%08x] [dmac_generic_dma channel=%d] read: %d bytes from %08x to %08x\n", "[dmac_generic_dma channel=%d] read: %d bytes from %08x to %08x\n",
R[NUM_PC], channel, channel,
chan->wcount + 1, chan->wcount + 1,
dma_address(channel, 0, TRUE), dma_address(channel, 0),
service_address); service_address);
for (; i >= 0; i--) { for (; i >= 0; i--) {
chan->wcount_c = i; chan->wcount_c = i;
addr = dma_address(channel, chan->ptr++, TRUE); addr = dma_address(channel, chan->ptr++);
chan->addr_c = dma_state.channels[channel].addr + chan->ptr; chan->addr_c = addr;
data = pread_b(addr); data = pread_b(addr, BUS_PER);
write_b(service_address, data); write_b(service_address, data, BUS_PER);
} }
break; break;
} }

View file

@ -1,6 +1,6 @@
/* 3b2_dmac.h: AT&T 3B2 DMA Controller Header /* 3b2_dmac.h: AM9517 DMA Controller
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -33,14 +33,20 @@
#include "3b2_defs.h" #include "3b2_defs.h"
#define DMA_MODE_VERIFY 0 #define DMA_XFER_VERIFY 0
#define DMA_MODE_WRITE 1 /* Write to memory from device */ #define DMA_XFER_WRITE 1 /* Write to memory from device */
#define DMA_MODE_READ 2 /* Read from memory to device */ #define DMA_XFER_READ 2 /* Read from memory to device */
#define DMA_IF_READ (IFBASE + IF_DATA_REG) #define DMA_IF_READ (IFBASE + IF_DATA_REG)
#define DMA_MODE(C) ((dma_state.channels[(C)].mode >> 6) & 3)
#define DMA_DECR(C) ((dma_state.channels[(C)].mode >> 5) & 1)
#define DMA_AUTOINIT(C) ((dma_state.channels[(C)].mode >> 4) & 1)
#define DMA_XFER(C) ((dma_state.channels[(C)].mode >> 2) & 3)
typedef struct { typedef struct {
uint16 page; uint8 mode; /* Channel mode */
uint16 page; /* Memory page */
uint16 addr; /* Original addr */ uint16 addr; /* Original addr */
uint16 wcount; /* Original wcount */ uint16 wcount; /* Original wcount */
uint16 addr_c; /* Current addr */ uint16 addr_c; /* Current addr */
@ -57,7 +63,6 @@ typedef struct {
/* DMAC programmable registers */ /* DMAC programmable registers */
uint8 command; uint8 command;
uint8 mode;
uint8 request; uint8 request;
uint8 mask; uint8 mask;
uint8 status; uint8 status;
@ -77,7 +82,7 @@ uint32 dmac_read(uint32 pa, size_t size);
void dmac_write(uint32 pa, uint32 val, size_t size); void dmac_write(uint32 pa, uint32 val, size_t size);
void dmac_service_drqs(); void dmac_service_drqs();
void dmac_generic_dma(uint8 channel, uint32 service_address); void dmac_generic_dma(uint8 channel, uint32 service_address);
uint32 dma_address(uint8 channel, uint32 offset, t_bool r); uint32 dma_address(uint8 channel, uint32 offset);
extern DMA_STATE dma_state; extern DMA_STATE dma_state;

View file

@ -1,6 +1,6 @@
/* 3b2_d.h: AT&T 3B2 Model 400 Hard Disk (uPD7261) Implementation /* 3b2_d.c: uPD7261 Integrated Disk Controller
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -245,15 +245,14 @@ t_stat id_ctlr_svc(UNIT *uptr)
switch (cmd) { switch (cmd) {
case ID_CMD_SIS: case ID_CMD_SIS:
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tINTR\t\tCOMPLETING Sense Interrupt Status.\n", "INTR\t\tCOMPLETING Sense Interrupt Status.\n");
R[NUM_PC]);
id_data[0] = id_int_status; id_data[0] = id_int_status;
id_int_status = 0; id_int_status = 0;
break; break;
default: default:
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tINTR\t\tCOMPLETING OTHER COMMAND 0x%x (CONTROLLER)\n", "INTR\t\tCOMPLETING OTHER COMMAND 0x%x (CONTROLLER)\n",
R[NUM_PC], cmd); cmd);
break; break;
} }
@ -299,15 +298,15 @@ t_stat id_unit_svc(UNIT *uptr)
case ID_SEEK_0: case ID_SEEK_0:
id_set_status(ID_STAT_CEH); id_set_status(ID_STAT_CEH);
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tINTR\t\tCOMPLETING Recal/Seek SEEK_0 UNIT %d\n", "INTR\t\tCOMPLETING Recal/Seek SEEK_0 UNIT %d\n",
R[NUM_PC], unit); unit);
id_seek_state[unit] = ID_SEEK_1; id_seek_state[unit] = ID_SEEK_1;
id_activate(uptr, 8000); /* TODO: Correct Delay based on steps */ id_activate(uptr, 8000); /* TODO: Correct Delay based on steps */
break; break;
case ID_SEEK_1: case ID_SEEK_1:
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tINTR\t\tCOMPLETING Recal/Seek SEEK_1 UNIT %d\n", "INTR\t\tCOMPLETING Recal/Seek SEEK_1 UNIT %d\n",
R[NUM_PC], unit); unit);
id_seek_state[unit] = ID_SEEK_NONE; id_seek_state[unit] = ID_SEEK_NONE;
id_set_status(ID_STAT_SRQ); id_set_status(ID_STAT_SRQ);
uptr->u4 = 0; /* Only clear out the command on a SEEK_1, never a SEEK_0 */ uptr->u4 = 0; /* Only clear out the command on a SEEK_1, never a SEEK_0 */
@ -319,14 +318,14 @@ t_stat id_unit_svc(UNIT *uptr)
break; break;
default: default:
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tINTR\t\tERROR, NOT SEEK_0 OR SEEK_1, UNIT %d\n", "INTR\t\tERROR, NOT SEEK_0 OR SEEK_1, UNIT %d\n",
R[NUM_PC], unit); unit);
break; break;
} }
} else { } else {
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tINTR\t\tCOMPLETING NON-POLLING Recal/Seek UNIT %d\n", "INTR\t\tCOMPLETING NON-POLLING Recal/Seek UNIT %d\n",
R[NUM_PC], unit); unit);
id_set_status(ID_STAT_CEH); id_set_status(ID_STAT_CEH);
uptr->u4 = 0; uptr->u4 = 0;
if (uptr->flags & UNIT_ATT) { if (uptr->flags & UNIT_ATT) {
@ -339,8 +338,8 @@ t_stat id_unit_svc(UNIT *uptr)
break; break;
case ID_CMD_SUS: case ID_CMD_SUS:
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tINTR\t\tCOMPLETING Sense Unit Status UNIT %d\n", "INTR\t\tCOMPLETING Sense Unit Status UNIT %d\n",
R[NUM_PC], unit); unit);
id_set_status(ID_STAT_CEH); id_set_status(ID_STAT_CEH);
uptr->u4 = 0; uptr->u4 = 0;
if ((uptr->flags & UNIT_ATT) == 0) { if ((uptr->flags & UNIT_ATT) == 0) {
@ -357,8 +356,8 @@ t_stat id_unit_svc(UNIT *uptr)
break; break;
default: default:
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tINTR\t\tCOMPLETING OTHER COMMAND 0x%x UNIT %d\n", "INTR\t\tCOMPLETING OTHER COMMAND 0x%x UNIT %d\n",
R[NUM_PC], cmd, unit); cmd, unit);
id_set_status(ID_STAT_CEH); id_set_status(ID_STAT_CEH);
uptr->u4 = 0; uptr->u4 = 0;
break; break;
@ -485,8 +484,7 @@ uint32 id_read(uint32 pa, size_t size)
* that's an error state. */ * that's an error state. */
if (id_scnt == 0) { if (id_scnt == 0) {
sim_debug(READ_MSG, &id_dev, sim_debug(READ_MSG, &id_dev,
"[%08x] ERROR\tid_scnt = 0 but still in dma\n", "ERROR\tid_scnt = 0 but still in dma\n");
R[NUM_PC]);
id_end_rw(ID_EST_OVR); id_end_rw(ID_EST_OVR);
return 0; return 0;
} }
@ -501,24 +499,21 @@ uint32 id_read(uint32 pa, size_t size)
if (sim_disk_rdsect(id_sel_unit, lba, id_buf, &sectsread, 1) == SCPE_OK) { if (sim_disk_rdsect(id_sel_unit, lba, id_buf, &sectsread, 1) == SCPE_OK) {
if (sectsread !=1) { if (sectsread !=1) {
sim_debug(READ_MSG, &id_dev, sim_debug(READ_MSG, &id_dev,
"[%08x]\tERROR: ASKED TO READ ONE SECTOR, READ: %d\n", "ERROR: ASKED TO READ ONE SECTOR, READ: %d\n",
R[NUM_PC], sectsread); sectsread);
} }
id_update_chs(); id_update_chs();
} else { } else {
/* Uh-oh! */ /* Uh-oh! */
sim_debug(READ_MSG, &id_dev, sim_debug(READ_MSG, &id_dev,
"[%08x]\tRDATA READ ERROR. Failure from sim_disk_rdsect!\n", "RDATA READ ERROR. Failure from sim_disk_rdsect!\n");
R[NUM_PC]);
id_end_rw(ID_EST_DER); id_end_rw(ID_EST_DER);
return 0; return 0;
} }
} }
data = id_buf[id_buf_ptr++]; data = id_buf[id_buf_ptr++];
sim_debug(READ_MSG, &id_dev, sim_debug(READ_MSG, &id_dev, "DATA\t%02x\n", data);
"[%08x]\tDATA\t%02x\n",
R[NUM_PC], data);
/* Done with this current sector, update id_scnt */ /* Done with this current sector, update id_scnt */
if (id_buf_ptr >= ID_SEC_SIZE) { if (id_buf_ptr >= ID_SEC_SIZE) {
@ -538,8 +533,8 @@ uint32 id_read(uint32 pa, size_t size)
data = id_idfield[id_idfield_ptr++]; data = id_idfield[id_idfield_ptr++];
sim_debug(READ_MSG, &id_dev, sim_debug(READ_MSG, &id_dev,
"[%08x]\tID DATA\t%02x\n", "ID DATA\t%02x\n",
R[NUM_PC], data); data);
if (id_idfield_ptr >= ID_IDFIELD_LEN) { if (id_idfield_ptr >= ID_IDFIELD_LEN) {
if (id_scnt-- > 0) { if (id_scnt-- > 0) {
@ -562,13 +557,12 @@ uint32 id_read(uint32 pa, size_t size)
} else { } else {
if (id_dpr < ID_FIFO_LEN) { if (id_dpr < ID_FIFO_LEN) {
sim_debug(READ_MSG, &id_dev, sim_debug(READ_MSG, &id_dev,
"[%08x]\tDATA\t%02x\n", "DATA\t%02x\n",
R[NUM_PC], id_data[id_dpr]); id_data[id_dpr]);
return id_data[id_dpr++]; return id_data[id_dpr++];
} else { } else {
sim_debug(READ_MSG, &id_dev, sim_debug(READ_MSG, &id_dev,
"[%08x] ERROR\tFIFO OVERRUN\n", "ERROR\tFIFO OVERRUN\n");
R[NUM_PC]);
return 0; return 0;
} }
} }
@ -576,14 +570,14 @@ uint32 id_read(uint32 pa, size_t size)
break; break;
case ID_CMD_STAT_REG: /* Status Register */ case ID_CMD_STAT_REG: /* Status Register */
sim_debug(READ_MSG, &id_dev, sim_debug(READ_MSG, &id_dev,
"[%08x]\tSTATUS\t%02x\n", "STATUS\t%02x\n",
R[NUM_PC], id_status|id_drq); id_status|id_drq);
return id_status|(id_drq ? 1u : 0); return id_status|(id_drq ? 1u : 0);
} }
sim_debug(READ_MSG, &id_dev, sim_debug(READ_MSG, &id_dev,
"[%08x] Read of unsuported register %x\n", "Read of unsuported register %x\n",
R[NUM_PC], id_status); id_status);
return 0; return 0;
} }
@ -607,8 +601,7 @@ void id_write(uint32 pa, uint32 val, size_t size)
* that's an error state. */ * that's an error state. */
if (id_scnt == 0) { if (id_scnt == 0) {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x] ERROR\tid_scnt = 0 but still in dma\n", "ERROR\tid_scnt = 0 but still in dma\n");
R[NUM_PC]);
id_end_rw(ID_EST_OVR); id_end_rw(ID_EST_OVR);
return; return;
} }
@ -617,12 +610,11 @@ void id_write(uint32 pa, uint32 val, size_t size)
if (id_buf_ptr < ID_SEC_SIZE) { if (id_buf_ptr < ID_SEC_SIZE) {
id_buf[id_buf_ptr++] = (uint8)(val & 0xff); id_buf[id_buf_ptr++] = (uint8)(val & 0xff);
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tDATA\t%02x\n", "DATA\t%02x\n",
R[NUM_PC], (uint8)(val & 0xff)); (uint8)(val & 0xff));
} else { } else {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tERROR\tWDATA OVERRUN\n", "ERROR\tWDATA OVERRUN\n");
R[NUM_PC]);
id_end_rw(ID_EST_OVR); id_end_rw(ID_EST_OVR);
return; return;
} }
@ -637,8 +629,8 @@ void id_write(uint32 pa, uint32 val, size_t size)
if (sim_disk_wrsect(id_sel_unit, lba, id_buf, &sectswritten, 1) == SCPE_OK) { if (sim_disk_wrsect(id_sel_unit, lba, id_buf, &sectswritten, 1) == SCPE_OK) {
if (sectswritten !=1) { if (sectswritten !=1) {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tERROR: ASKED TO WRITE ONE SECTOR, WROTE: %d\n", "ERROR: ASKED TO WRITE ONE SECTOR, WROTE: %d\n",
R[NUM_PC], sectswritten); sectswritten);
} }
id_update_chs(); id_update_chs();
if (--id_scnt == 0) { if (--id_scnt == 0) {
@ -647,8 +639,8 @@ void id_write(uint32 pa, uint32 val, size_t size)
} else { } else {
/* Uh-oh! */ /* Uh-oh! */
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x] ERROR\tWDATA WRITE ERROR. lba=%04x\n", "ERROR\tWDATA WRITE ERROR. lba=%04x\n",
R[NUM_PC], lba); lba);
id_end_rw(ID_EST_DER); id_end_rw(ID_EST_DER);
return; return;
} }
@ -656,14 +648,13 @@ void id_write(uint32 pa, uint32 val, size_t size)
return; return;
} else { } else {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tDATA\t%02x\n", "DATA\t%02x\n",
R[NUM_PC], val); val);
if (id_dpw < ID_FIFO_LEN) { if (id_dpw < ID_FIFO_LEN) {
id_data[id_dpw++] = (uint8) val; id_data[id_dpw++] = (uint8) val;
} else { } else {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x] ERROR\tFIFO OVERRUN\n", "ERROR\tFIFO OVERRUN\n");
R[NUM_PC]);
} }
} }
return; return;
@ -691,29 +682,29 @@ void id_handle_command(uint8 val)
if (aux_cmd & ID_AUX_CLCE) { if (aux_cmd & ID_AUX_CLCE) {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x] \tCOMMAND\t%02x\tAUX:CLCE\n", "COMMAND\t%02x\tAUX:CLCE\n",
R[NUM_PC], val); val);
id_clr_status(ID_STAT_CEH|ID_STAT_CEL); id_clr_status(ID_STAT_CEH|ID_STAT_CEL);
} }
if (aux_cmd & ID_AUX_HSRQ) { if (aux_cmd & ID_AUX_HSRQ) {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x] \tCOMMAND\t%02x\tAUX:HSRQ\n", "COMMAND\t%02x\tAUX:HSRQ\n",
R[NUM_PC], val); val);
id_set_srqm(TRUE); id_set_srqm(TRUE);
} }
if (aux_cmd & ID_AUX_CLB) { if (aux_cmd & ID_AUX_CLB) {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tAUX:CLBUF\n", "COMMAND\t%02x\tAUX:CLBUF\n",
R[NUM_PC], val); val);
id_clear_fifo(); id_clear_fifo();
} }
if (aux_cmd & ID_AUX_RST) { if (aux_cmd & ID_AUX_RST) {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tAUX:RESET\n", "COMMAND\t%02x\tAUX:RESET\n",
R[NUM_PC], val); val);
id_clear_fifo(); id_clear_fifo();
sim_cancel(id_sel_unit); sim_cancel(id_sel_unit);
sim_cancel(id_ctlr_unit); sim_cancel(id_ctlr_unit);
@ -765,15 +756,15 @@ void id_handle_command(uint8 val)
switch(cmd) { switch(cmd) {
case ID_CMD_SIS: case ID_CMD_SIS:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tSense Int. Status\n", "COMMAND\t%02x\tSense Int. Status\n",
R[NUM_PC], val); val);
id_clr_status(ID_STAT_SRQ); /* SIS immediately de-asserts SRQ */ id_clr_status(ID_STAT_SRQ); /* SIS immediately de-asserts SRQ */
id_activate(id_ctlr_unit, ID_SIS_WAIT); id_activate(id_ctlr_unit, ID_SIS_WAIT);
break; break;
case ID_CMD_SPEC: case ID_CMD_SPEC:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tSpecify - ETN=%02x ESN=%02x\n", "COMMAND\t%02x\tSpecify - ETN=%02x ESN=%02x\n",
R[NUM_PC], val, id_data[3], id_data[4]); val, id_data[3], id_data[4]);
id_dtlh = id_data[1]; id_dtlh = id_data[1];
id_etn = id_data[3]; id_etn = id_data[3];
id_esn = id_data[4]; id_esn = id_data[4];
@ -782,14 +773,14 @@ void id_handle_command(uint8 val)
break; break;
case ID_CMD_SUS: case ID_CMD_SUS:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tSense Unit Status - %d\n", "COMMAND\t%02x\tSense Unit Status - %d\n",
R[NUM_PC], val, id_ua); val, id_ua);
id_activate(id_sel_unit, ID_SUS_WAIT); id_activate(id_sel_unit, ID_SUS_WAIT);
break; break;
case ID_CMD_DERR: case ID_CMD_DERR:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tDetect Error\n", "COMMAND\t%02x\tDetect Error\n",
R[NUM_PC], val); val);
id_activate(id_ctlr_unit, ID_CMD_WAIT); id_activate(id_ctlr_unit, ID_CMD_WAIT);
break; break;
case ID_CMD_RECAL: case ID_CMD_RECAL:
@ -798,13 +789,13 @@ void id_handle_command(uint8 val)
id_seek_state[id_unit_num] = ID_SEEK_0; id_seek_state[id_unit_num] = ID_SEEK_0;
if (id_polling) { if (id_polling) {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tRecalibrate - %d - POLLING\n", "COMMAND\t%02x\tRecalibrate - %d - POLLING\n",
R[NUM_PC], val, id_ua); val, id_ua);
id_activate(id_sel_unit, 1000); id_activate(id_sel_unit, 1000);
} else { } else {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tRecalibrate - %d - NORMAL\n", "COMMAND\t%02x\tRecalibrate - %d - NORMAL\n",
R[NUM_PC], val, id_ua); val, id_ua);
id_activate(id_sel_unit, (ID_RECAL_WAIT + (time * ID_SEEK_WAIT))); id_activate(id_sel_unit, (ID_RECAL_WAIT + (time * ID_SEEK_WAIT)));
} }
break; break;
@ -818,20 +809,20 @@ void id_handle_command(uint8 val)
if (id_polling) { if (id_polling) {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tSeek - %d - POLLING\n", "COMMAND\t%02x\tSeek - %d - POLLING\n",
R[NUM_PC], val, id_ua); val, id_ua);
id_activate(id_sel_unit, 4000); id_activate(id_sel_unit, 4000);
} else { } else {
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tSeek - %d - NORMAL\n", "COMMAND\t%02x\tSeek - %d - NORMAL\n",
R[NUM_PC], val, id_ua); val, id_ua);
id_activate(id_sel_unit, ID_SEEK_BASE + (time * ID_SEEK_WAIT)); id_activate(id_sel_unit, ID_SEEK_BASE + (time * ID_SEEK_WAIT));
} }
break; break;
case ID_CMD_FMT: case ID_CMD_FMT:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tFormat - %d\n", "COMMAND\t%02x\tFormat - %d\n",
R[NUM_PC], val, id_ua); val, id_ua);
id_phn = id_data[0]; id_phn = id_data[0];
id_scnt = id_data[1]; id_scnt = id_data[1];
@ -850,12 +841,12 @@ void id_handle_command(uint8 val)
lba = id_lba(id_cyl[id_unit_num], id_phn, sec++); lba = id_lba(id_cyl[id_unit_num], id_phn, sec++);
if (sim_disk_wrsect(id_sel_unit, lba, id_buf, NULL, 1) == SCPE_OK) { if (sim_disk_wrsect(id_sel_unit, lba, id_buf, NULL, 1) == SCPE_OK) {
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tFORMAT: PHN=%d SCNT=%d PAT=%02x LBA=%04x\n", "FORMAT: PHN=%d SCNT=%d PAT=%02x LBA=%04x\n",
R[NUM_PC], id_phn, id_scnt, pattern, lba); id_phn, id_scnt, pattern, lba);
} else { } else {
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tFORMAT FAILED! PHN=%d SCNT=%d PAT=%02x LBA=%04x\n", "FORMAT FAILED! PHN=%d SCNT=%d PAT=%02x LBA=%04x\n",
R[NUM_PC], id_phn, id_scnt, pattern, lba); id_phn, id_scnt, pattern, lba);
break; break;
} }
} }
@ -872,16 +863,16 @@ void id_handle_command(uint8 val)
break; break;
case ID_CMD_VID: case ID_CMD_VID:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tVerify ID - %d\n", "COMMAND\t%02x\tVerify ID - %d\n",
R[NUM_PC], val, id_ua); val, id_ua);
id_data[0] = 0; id_data[0] = 0;
id_data[1] = 0x05; /* What do we put here? */ id_data[1] = 0x05; /* What do we put here? */
id_activate(id_sel_unit, ID_CMD_WAIT); id_activate(id_sel_unit, ID_CMD_WAIT);
break; break;
case ID_CMD_RID: case ID_CMD_RID:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tRead ID - %d\n", "COMMAND\t%02x\tRead ID - %d\n",
R[NUM_PC], val, id_ua); val, id_ua);
if (id_sel_unit->flags & UNIT_ATT) { if (id_sel_unit->flags & UNIT_ATT) {
id_drq = TRUE; id_drq = TRUE;
@ -894,21 +885,21 @@ void id_handle_command(uint8 val)
id_lsn = 0; id_lsn = 0;
} else { } else {
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tUNIT %d NOT ATTACHED, CANNOT READ ID.\n", "UNIT %d NOT ATTACHED, CANNOT READ ID.\n",
R[NUM_PC], id_ua); id_ua);
} }
id_activate(id_sel_unit, ID_CMD_WAIT); id_activate(id_sel_unit, ID_CMD_WAIT);
break; break;
case ID_CMD_RDIAG: case ID_CMD_RDIAG:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tRead Diag - %d\n", "COMMAND\t%02x\tRead Diag - %d\n",
R[NUM_PC], val, id_ua); val, id_ua);
id_activate(id_sel_unit, ID_CMD_WAIT); id_activate(id_sel_unit, ID_CMD_WAIT);
break; break;
case ID_CMD_RDATA: case ID_CMD_RDATA:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tRead Data - %d\n", "COMMAND\t%02x\tRead Data - %d\n",
R[NUM_PC], val, id_ua); val, id_ua);
if (id_sel_unit->flags & UNIT_ATT) { if (id_sel_unit->flags & UNIT_ATT) {
id_drq = TRUE; id_drq = TRUE;
id_buf_ptr = 0; id_buf_ptr = 0;
@ -922,33 +913,33 @@ void id_handle_command(uint8 val)
id_scnt = id_data[5]; id_scnt = id_data[5];
} else { } else {
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tUNIT %d NOT ATTACHED, CANNOT READ DATA.\n", "UNIT %d NOT ATTACHED, CANNOT READ DATA.\n",
R[NUM_PC], id_ua); id_ua);
} }
id_activate(id_sel_unit, ID_RW_WAIT); id_activate(id_sel_unit, ID_RW_WAIT);
break; break;
case ID_CMD_CHECK: case ID_CMD_CHECK:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tCheck - %d\n", "COMMAND\t%02x\tCheck - %d\n",
R[NUM_PC], val, id_ua); val, id_ua);
id_activate(id_sel_unit, ID_CMD_WAIT); id_activate(id_sel_unit, ID_CMD_WAIT);
break; break;
case ID_CMD_SCAN: case ID_CMD_SCAN:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tScan - %d\n", "COMMAND\t%02x\tScan - %d\n",
R[NUM_PC], val, id_ua); val, id_ua);
id_activate(id_sel_unit, ID_CMD_WAIT); id_activate(id_sel_unit, ID_CMD_WAIT);
break; break;
case ID_CMD_VDATA: case ID_CMD_VDATA:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tVerify Data - %d\n", "COMMAND\t%02x\tVerify Data - %d\n",
R[NUM_PC], val, id_ua); val, id_ua);
id_activate(id_sel_unit, ID_CMD_WAIT); id_activate(id_sel_unit, ID_CMD_WAIT);
break; break;
case ID_CMD_WDATA: case ID_CMD_WDATA:
sim_debug(WRITE_MSG, &id_dev, sim_debug(WRITE_MSG, &id_dev,
"[%08x]\tCOMMAND\t%02x\tWrite Data - %d\n", "COMMAND\t%02x\tWrite Data - %d\n",
R[NUM_PC], val, id_ua); val, id_ua);
if (id_sel_unit->flags & UNIT_ATT) { if (id_sel_unit->flags & UNIT_ATT) {
id_drq = TRUE; id_drq = TRUE;
id_buf_ptr = 0; id_buf_ptr = 0;
@ -962,8 +953,8 @@ void id_handle_command(uint8 val)
id_scnt = id_data[5]; id_scnt = id_data[5];
} else { } else {
sim_debug(EXECUTE_MSG, &id_dev, sim_debug(EXECUTE_MSG, &id_dev,
"[%08x]\tUNIT %d NOT ATTACHED, CANNOT WRITE.\n", "UNIT %d NOT ATTACHED, CANNOT WRITE.\n",
R[NUM_PC], id_ua); id_ua);
} }
id_activate(id_sel_unit, ID_RW_WAIT); id_activate(id_sel_unit, ID_RW_WAIT);
break; break;
@ -996,5 +987,10 @@ t_stat id_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
fprintf(st, " HD161 161.4 MB 11 1224 15 18 512 Maxtor XT1190 (SVR3+)\n\n"); fprintf(st, " HD161 161.4 MB 11 1224 15 18 512 Maxtor XT1190 (SVR3+)\n\n");
fprintf(st, "The drive ID and geometry values are used when low-level formatting a\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"); fprintf(st, "drive using the AT&T 'idtools' utility.\n");
fprint_set_help(st, dptr);
fprint_show_help(st, dptr);
fprint_reg_help(st, dptr);
return SCPE_OK; return SCPE_OK;
} }

View file

@ -1,6 +1,6 @@
/* 3b2_id.h: AT&T 3B2 Model 400 Hard Disk (uPD7261) Header /* 3b2_id.h: uPD7261 Integrated Disk Controller
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation

View file

@ -1,6 +1,6 @@
/* 3b2_if.c: AT&T 3B2 Floppy Controller (TMS2797NL) Implementation /* 3b2_if.c: TMS2797 Integrated Floppy Controller
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -51,12 +51,12 @@ static SIM_INLINE uint32 if_lba();
* *
*/ */
#define IF_STEP_DELAY 3000 /* us */ #define IF_STEP_DELAY 300 /* us */
#define IF_R_DELAY 65000 /* us */ #define IF_R_DELAY 6500 /* us */
#define IF_W_DELAY 70000 /* us */ #define IF_W_DELAY 7000 /* us */
#define IF_VERIFY_DELAY 20000 /* us */ #define IF_VERIFY_DELAY 2000 /* us */
#define IF_HLD_DELAY 60000 /* us */ #define IF_HLD_DELAY 6000 /* us */
#define IF_HSW_DELAY 40000 /* us */ #define IF_HSW_DELAY 4000 /* us */
#if defined(REV3) #if defined(REV3)
#define SET_INT CPU_SET_INT(INT_FLOPPY) #define SET_INT CPU_SET_INT(INT_FLOPPY)
@ -97,9 +97,9 @@ uint32 if_sec_ptr = 0;
/* Function implementation */ /* Function implementation */
static SIM_INLINE void if_activate(uint32 delay) static SIM_INLINE void if_activate(uint32 delay_us)
{ {
sim_activate_abs(&if_unit, delay); sim_activate_after(&if_unit, delay_us);
} }
t_stat if_svc(UNIT *uptr) t_stat if_svc(UNIT *uptr)
@ -618,6 +618,11 @@ t_stat if_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
fprintf(st, " ------ ----- ----------- ------------- -----------\n"); fprintf(st, " ------ ----- ----------- ------------- -----------\n");
fprintf(st, " 720 KB 2 80 9 512\n\n"); fprintf(st, " 720 KB 2 80 9 512\n\n");
fprintf(st, "Physical media is Double Sided/Quad Density, 96 tpi, 250kbps MFM encoding.\n"); fprintf(st, "Physical media is Double Sided/Quad Density, 96 tpi, 250kbps MFM encoding.\n");
fprint_set_help(st, dptr);
fprint_show_help(st, dptr);
fprint_reg_help(st, dptr);
return SCPE_OK; return SCPE_OK;
} }

View file

@ -1,6 +1,6 @@
/* 3b2_if.h: AT&T 3B2 Model Floppy Controller (TMS2797NL) Header /* 3b2_if.h: TMS2797 Integrated Floppy Controller
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation

View file

@ -1,6 +1,6 @@
/* 3b2_io.c: AT&T 3B2 Model 400 IO and CIO feature cards /* 3b2_io.c: Common I/O (CIO) Feature Card Support
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -45,7 +45,6 @@
#endif #endif
CIO_STATE cio[CIO_SLOTS] = {{0}}; CIO_STATE cio[CIO_SLOTS] = {{0}};
uint16 cio_int_req = 0; /* Bitset of card slots requesting interrupts */ uint16 cio_int_req = 0; /* Bitset of card slots requesting interrupts */
#if defined(REV3) #if defined(REV3)
@ -53,8 +52,8 @@ iolink iotable[] = {
{ MMUBASE, MMUBASE+MMUSIZE, &mmu_read, &mmu_write }, { MMUBASE, MMUBASE+MMUSIZE, &mmu_read, &mmu_write },
{ IFBASE, IFBASE+IFSIZE, &if_read, &if_write }, { IFBASE, IFBASE+IFSIZE, &if_read, &if_write },
{ IFCSRBASE, IFCSRBASE+IFCSRSIZE, &if_csr_read, &if_csr_write }, { IFCSRBASE, IFCSRBASE+IFCSRSIZE, &if_csr_read, &if_csr_write },
{ FLTLBASE, FLTLSIZE, &flt_read, &flt_write }, { FLTLBASE, FLTLBASE+FLTLSIZE, &flt_read, &flt_write },
{ FLTHBASE, FLTHSIZE, &flt_read, &flt_write }, { FLTHBASE, FLTHBASE+FLTHSIZE, &flt_read, &flt_write },
{ NVRBASE, NVRBASE+NVRSIZE, &nvram_read, &nvram_write }, { NVRBASE, NVRBASE+NVRSIZE, &nvram_read, &nvram_write },
{ TIMERBASE, TIMERBASE+TIMERSIZE, &timer_read, &timer_write }, { TIMERBASE, TIMERBASE+TIMERSIZE, &timer_read, &timer_write },
{ CSRBASE, CSRBASE+CSRSIZE, &csr_read, &csr_write }, { CSRBASE, CSRBASE+CSRSIZE, &csr_read, &csr_write },
@ -85,24 +84,73 @@ iolink iotable[] = {
}; };
#endif #endif
void cio_clear(uint8 cid) /*
* Insert a CIO card into the backplane.
*
* If a space could be found, SPCE_OK is returned, and the slot the
* card was installed in is placed in `slot`.
*
* If no room is availalbe, return SCPE_NXM.
*/
t_stat cio_install(uint16 id,
CONST char *name,
uint8 ipl,
void (*exp_handler)(uint8 slot),
void (*full_handler)(uint8 slot),
void (*sysgen)(uint8 slot),
void (*reset_handler)(uint8 slot),
uint8 *slot)
{ {
cio[cid].id = 0; uint8 s;
cio[cid].exp_handler = NULL;
cio[cid].full_handler = NULL; for (s = 0; s < CIO_SLOTS; s++) {
cio[cid].reset_handler = NULL; sim_debug(EXECUTE_MSG, &cpu_dev,
cio[cid].sysgen = NULL; "[cio_install] cio[%d]: populated=%d, id=%d\n",
cio[cid].rqp = 0; s, cio[s].populated, cio[s].id);
cio[cid].cqp = 0; if (!cio[s].populated) {
cio[cid].rqs = 0; sim_debug(EXECUTE_MSG, &cpu_dev,
cio[cid].cqs = 0; "[cio_install] >>> I found a free slot! Slot #%d has nothing\n", s);
cio[cid].ivec = 0; *slot = s;
cio[cid].no_rque = 0; /* Ensure the slot is in a clean state */
cio[cid].ipl = 0; cio_remove(s);
cio[cid].sysgen_s = 0; /* Populate the slot */
cio[cid].seqbit = 0; cio[s].populated = TRUE;
cio[cid].op = 0; cio[s].id = id;
CIO_CLR_INT(cid); cio[s].ipl = ipl;
strncpy(cio[s].name, name, CIO_NAME_LEN);
cio[s].exp_handler = exp_handler;
cio[s].full_handler = full_handler;
cio[s].sysgen = sysgen;
cio[s].reset_handler = reset_handler;
return SCPE_OK;
}
}
return SCPE_NXM;
}
/*
* Remove a CIO card from the specified backplane slot.
*/
void cio_remove(uint8 slot)
{
memset(&cio[slot], 0, sizeof(CIO_STATE));
/* cio[slot].populated = FALSE; */
CIO_CLR_INT(slot);
}
/*
* Remove all CIO cards of the matching type.
*/
void cio_remove_all(uint16 id)
{
int i;
for (i = 0; i < CIO_SLOTS; i++) {
if (cio[i].populated && cio[i].id == id) {
cio_remove(i);
}
}
} }
/* /*
@ -130,81 +178,80 @@ uint32 cio_crc32_shift(uint32 crc, uint8 data)
return ~crc; return ~crc;
} }
void cio_sysgen(uint8 cid) void cio_sysgen(uint8 slot)
{ {
uint32 sysgen_p; uint32 sysgen_p;
sysgen_p = pread_w(SYSGEN_PTR); sysgen_p = pread_w(SYSGEN_PTR, BUS_PER);
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[%08x] [SYSGEN] Starting sysgen for card %d. sysgen_p=%08x\n", "[SYSGEN] Starting sysgen for card %d (%s). sysgen_p=%08x\n",
R[NUM_PC], cid, sysgen_p); slot, cio[slot].name, sysgen_p);
/* seqbit is always reset to 0 on completion */ /* seqbit is always reset to 0 on completion */
cio[cid].seqbit = 0; cio[slot].seqbit = 0;
cio[cid].rqp = pread_w(sysgen_p); cio[slot].rqp = pread_w(sysgen_p, BUS_PER);
cio[cid].cqp = pread_w(sysgen_p + 4); cio[slot].cqp = pread_w(sysgen_p + 4, BUS_PER);
cio[cid].rqs = pread_b(sysgen_p + 8); cio[slot].rqs = pread_b(sysgen_p + 8, BUS_PER);
cio[cid].cqs = pread_b(sysgen_p + 9); cio[slot].cqs = pread_b(sysgen_p + 9, BUS_PER);
cio[cid].ivec = pread_b(sysgen_p + 10); cio[slot].ivec = pread_b(sysgen_p + 10, BUS_PER);
cio[cid].no_rque = pread_b(sysgen_p + 11); cio[slot].no_rque = pread_b(sysgen_p + 11, BUS_PER);
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[SYSGEN] sysgen rqp = %08x\n", "[SYSGEN] sysgen rqp = %08x\n",
cio[cid].rqp); cio[slot].rqp);
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[SYSGEN] sysgen cqp = %08x\n", "[SYSGEN] sysgen cqp = %08x\n",
cio[cid].cqp); cio[slot].cqp);
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[SYSGEN] sysgen rqs = %02x\n", "[SYSGEN] sysgen rqs = %02x\n",
cio[cid].rqs); cio[slot].rqs);
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[SYSGEN] sysgen cqs = %02x\n", "[SYSGEN] sysgen cqs = %02x\n",
cio[cid].cqs); cio[slot].cqs);
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[SYSGEN] sysgen ivec = %02x\n", "[SYSGEN] sysgen ivec = %02x\n",
cio[cid].ivec); cio[slot].ivec);
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[SYSGEN] sysgen no_rque = %02x\n", "[SYSGEN] sysgen no_rque = %02x\n",
cio[cid].no_rque); cio[slot].no_rque);
/* If the card has a custom sysgen handler, run it */ /* If the card has a custom sysgen handler, run it */
if (cio[cid].sysgen != NULL) { if (cio[slot].sysgen != NULL) {
cio[cid].sysgen(cid); cio[slot].sysgen(slot);
} else { } else {
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[%08x] [cio_sysgen] Not running custom sysgen.\n", "[cio_sysgen] Not running custom sysgen.\n");
R[NUM_PC]);
} }
} }
void cio_cexpress(uint8 cid, uint32 esize, cio_entry *cqe, uint8 *app_data) void cio_cexpress(uint8 slot, uint32 esize, cio_entry *cqe, uint8 *app_data)
{ {
uint32 i, cqp; uint32 i, cqp;
cqp = cio[cid].cqp; cqp = cio[slot].cqp;
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[%08x] [cio_cexpress] cqp = %08x seqbit = %d\n", "[cio_cexpress] [%s] cqp = %08x seqbit = %d\n",
R[NUM_PC], cqp, cio[cid].seqbit); cio[slot].name, cqp, cio[slot].seqbit);
cio[cid].seqbit ^= 1; cio[slot].seqbit ^= 1;
cqe->subdevice |= (cio[cid].seqbit << 6); cqe->subdevice |= (cio[slot].seqbit << 6);
pwrite_h(cqp, cqe->byte_count); pwrite_h(cqp, cqe->byte_count, BUS_PER);
pwrite_b(cqp + 2, cqe->subdevice); pwrite_b(cqp + 2, cqe->subdevice, BUS_PER);
pwrite_b(cqp + 3, cqe->opcode); pwrite_b(cqp + 3, cqe->opcode, BUS_PER);
pwrite_w(cqp + 4, cqe->address); pwrite_w(cqp + 4, cqe->address, BUS_PER);
/* Write application-specific data. */ /* Write application-specific data. */
for (i = 0; i < (esize - QESIZE); i++) { for (i = 0; i < (esize - QESIZE); i++) {
pwrite_b(cqp + 8 + i, app_data[i]); pwrite_b(cqp + 8 + i, app_data[i], BUS_PER);
} }
} }
void cio_cqueue(uint8 cid, uint8 cmd_stat, uint32 esize, void cio_cqueue(uint8 slot, uint8 cmd_stat, uint32 esize,
cio_entry *cqe, uint8 *app_data) cio_entry *cqe, uint8 *app_data)
{ {
uint32 i, cqp, top; uint32 i, cqp, top;
@ -215,7 +262,7 @@ void cio_cqueue(uint8 cid, uint8 cmd_stat, uint32 esize,
/* Get the physical address of the completion queue /* Get the physical address of the completion queue
* in main memory */ * in main memory */
cqp = cio[cid].cqp; cqp = cio[slot].cqp;
/* Get the physical address of the first entry in /* Get the physical address of the first entry in
* the completion queue */ * the completion queue */
@ -223,47 +270,47 @@ void cio_cqueue(uint8 cid, uint8 cmd_stat, uint32 esize,
/* Get the load pointer. This is a 16-bit absolute offset /* Get the load pointer. This is a 16-bit absolute offset
* from the top of the queue to the start of the entry. */ * from the top of the queue to the start of the entry. */
lp = pread_h(cqp + esize); lp = pread_h(cqp + esize, BUS_PER);
/* Load the entry at the supplied address */ /* Load the entry at the supplied address */
pwrite_h(top + lp, cqe->byte_count); pwrite_h(top + lp, cqe->byte_count, BUS_PER);
pwrite_b(top + lp + 2, cqe->subdevice); pwrite_b(top + lp + 2, cqe->subdevice, BUS_PER);
pwrite_b(top + lp + 3, cqe->opcode); pwrite_b(top + lp + 3, cqe->opcode, BUS_PER);
pwrite_w(top + lp + 4, cqe->address); pwrite_w(top + lp + 4, cqe->address, BUS_PER);
/* Write application-specific data. */ /* Write application-specific data. */
for (i = 0; i < (esize - QESIZE); i++) { for (i = 0; i < (esize - QESIZE); i++) {
pwrite_b(top + lp + 8 + i, app_data[i]); pwrite_b(top + lp + 8 + i, app_data[i], BUS_PER);
} }
/* Increment the load pointer to the next queue location. /* Increment the load pointer to the next queue location.
* If we go past the end of the queue, wrap around to the * If we go past the end of the queue, wrap around to the
* start of the queue */ * start of the queue */
if (cio[cid].cqs > 0) { if (cio[slot].cqs > 0) {
lp = (lp + esize) % (esize * cio[cid].cqs); lp = (lp + esize) % (esize * cio[slot].cqs);
/* Store it back to the correct location */ /* Store it back to the correct location */
pwrite_h(cqp + esize, lp); pwrite_h(cqp + esize, lp, BUS_PER);
} }
} }
/* /*
* Retrieve the Express Entry from the Request Queue * Retrieve the Express Entry from the Request Queue
*/ */
void cio_rexpress(uint8 cid, uint32 esize, cio_entry *rqe, uint8 *app_data) void cio_rexpress(uint8 slot, uint32 esize, cio_entry *rqe, uint8 *app_data)
{ {
uint32 i; uint32 i;
uint32 rqp; uint32 rqp;
rqp = cio[cid].rqp; rqp = cio[slot].rqp;
/* Unload the express entry from the request queue */ /* Unload the express entry from the request queue */
rqe->byte_count = pread_h(rqp); rqe->byte_count = pread_h(rqp, BUS_PER);
rqe->subdevice = pread_b(rqp + 2); rqe->subdevice = pread_b(rqp + 2, BUS_PER);
rqe->opcode = pread_b(rqp + 3); rqe->opcode = pread_b(rqp + 3, BUS_PER);
rqe->address = pread_w(rqp + 4); rqe->address = pread_w(rqp + 4, BUS_PER);
for (i = 0; i < (esize - QESIZE); i++) { for (i = 0; i < (esize - QESIZE); i++) {
app_data[i] = pread_b(rqp + 8 + i); app_data[i] = pread_b(rqp + 8 + i, BUS_PER);
} }
} }
@ -276,19 +323,19 @@ void cio_rexpress(uint8 cid, uint32 esize, cio_entry *rqe, uint8 *app_data)
* Returns SCPE_OK on success, or SCPE_NXM if no entry was found. * Returns SCPE_OK on success, or SCPE_NXM if no entry was found.
* *
*/ */
t_stat cio_rqueue(uint8 cid, uint32 qnum, uint32 esize, t_stat cio_rqueue(uint8 slot, uint32 qnum, uint32 esize,
cio_entry *rqe, uint8 *app_data) cio_entry *rqe, uint8 *app_data)
{ {
uint32 i, rqp, top; uint32 i, rqp, top;
uint16 lp, ulp; uint16 lp, ulp;
/* Get the physical address of the request queue in main memory */ /* Get the physical address of the request queue in main memory */
rqp = cio[cid].rqp + rqp = cio[slot].rqp +
esize + esize +
(qnum * (LUSIZE + (esize * cio[cid].rqs))); (qnum * (LUSIZE + (esize * cio[slot].rqs)));
lp = pread_h(rqp); lp = pread_h(rqp, BUS_PER);
ulp = pread_h(rqp + 2); ulp = pread_h(rqp + 2, BUS_PER);
/* Check to see if the request queue is empty. If it is, there's /* Check to see if the request queue is empty. If it is, there's
* nothing to take. */ * nothing to take. */
@ -299,22 +346,22 @@ t_stat cio_rqueue(uint8 cid, uint32 qnum, uint32 esize,
top = rqp + LUSIZE; top = rqp + LUSIZE;
/* Retrieve the entry at the supplied address */ /* Retrieve the entry at the supplied address */
rqe->byte_count = pread_h(top + ulp); rqe->byte_count = pread_h(top + ulp, BUS_PER);
rqe->subdevice = pread_b(top + ulp + 2); rqe->subdevice = pread_b(top + ulp + 2, BUS_PER);
rqe->opcode = pread_b(top + ulp + 3); rqe->opcode = pread_b(top + ulp + 3, BUS_PER);
rqe->address = pread_w(top + ulp + 4); rqe->address = pread_w(top + ulp + 4, BUS_PER);
/* Read application-specific data. */ /* Read application-specific data. */
for (i = 0; i < (esize - QESIZE); i++) { for (i = 0; i < (esize - QESIZE); i++) {
app_data[i] = pread_b(top + ulp + 8 + i); app_data[i] = pread_b(top + ulp + 8 + i, BUS_PER);
} }
/* Increment the unload pointer to the next queue location. If we /* Increment the unload pointer to the next queue location. If we
* go past the end of the queue, wrap around to the start of the * go past the end of the queue, wrap around to the start of the
* queue */ * queue */
if (cio[cid].rqs > 0) { if (cio[slot].rqs > 0) {
ulp = (ulp + esize) % (esize * cio[cid].rqs); ulp = (ulp + esize) % (esize * cio[slot].rqs);
pwrite_h(rqp + 2, ulp); pwrite_h(rqp + 2, ulp, BUS_PER);
} }
return SCPE_OK; return SCPE_OK;
@ -323,70 +370,70 @@ t_stat cio_rqueue(uint8 cid, uint32 qnum, uint32 esize,
/* /*
* Return the Load Pointer for the given request queue * Return the Load Pointer for the given request queue
*/ */
uint16 cio_r_lp(uint8 cid, uint32 qnum, uint32 esize) uint16 cio_r_lp(uint8 slot, uint32 qnum, uint32 esize)
{ {
uint32 rqp; uint32 rqp;
rqp = cio[cid].rqp + rqp = cio[slot].rqp +
esize + esize +
(qnum * (LUSIZE + (esize * cio[cid].rqs))); (qnum * (LUSIZE + (esize * cio[slot].rqs)));
return pread_h(rqp); return pread_h(rqp, BUS_PER);
} }
/* /*
* Return the Unload Pointer for the given request queue * Return the Unload Pointer for the given request queue
*/ */
uint16 cio_r_ulp(uint8 cid, uint32 qnum, uint32 esize) uint16 cio_r_ulp(uint8 slot, uint32 qnum, uint32 esize)
{ {
uint32 rqp; uint32 rqp;
rqp = cio[cid].rqp + rqp = cio[slot].rqp +
esize + esize +
(qnum * (LUSIZE + (esize * cio[cid].rqs))); (qnum * (LUSIZE + (esize * cio[slot].rqs)));
return pread_h(rqp + 2); return pread_h(rqp + 2, BUS_PER);
} }
uint16 cio_c_lp(uint8 cid, uint32 esize) uint16 cio_c_lp(uint8 slot, uint32 esize)
{ {
uint32 cqp; uint32 cqp;
cqp = cio[cid].cqp + esize; cqp = cio[slot].cqp + esize;
return pread_h(cqp); return pread_h(cqp, BUS_PER);
} }
uint16 cio_c_ulp(uint8 cid, uint32 esize) uint16 cio_c_ulp(uint8 slot, uint32 esize)
{ {
uint32 cqp; uint32 cqp;
cqp = cio[cid].cqp + esize; cqp = cio[slot].cqp + esize;
return pread_h(cqp + 2); return pread_h(cqp + 2, BUS_PER);
} }
/* /*
* Returns true if there is room in the completion queue * Returns true if there is room in the completion queue
* for a new entry. * for a new entry.
*/ */
t_bool cio_cqueue_avail(uint8 cid, uint32 esize) t_bool cio_cqueue_avail(uint8 slot, uint32 esize)
{ {
uint32 lp, ulp; uint32 lp, ulp;
lp = pread_h(cio[cid].cqp + esize); lp = pread_h(cio[slot].cqp + esize, BUS_PER);
ulp = pread_h(cio[cid].cqp + esize + 2); ulp = pread_h(cio[slot].cqp + esize + 2, BUS_PER);
return(((lp + esize) % (cio[cid].cqs * esize)) != ulp); return(((lp + esize) % (cio[slot].cqs * esize)) != ulp);
} }
t_bool cio_rqueue_avail(uint8 cid, uint32 qnum, uint32 esize) t_bool cio_rqueue_avail(uint8 slot, uint32 qnum, uint32 esize)
{ {
uint32 rqp, lp, ulp; uint32 rqp, lp, ulp;
/* Get the physical address of the request queue in main memory */ /* Get the physical address of the request queue in main memory */
rqp = cio[cid].rqp + rqp = cio[slot].rqp +
esize + esize +
(qnum * (LUSIZE + (esize * cio[cid].rqs))); (qnum * (LUSIZE + (esize * cio[slot].rqs)));
lp = pread_h(rqp); lp = pread_h(rqp, BUS_PER);
ulp = pread_h(rqp + 2); ulp = pread_h(rqp + 2, BUS_PER);
return(lp != ulp); return(lp != ulp);
} }
@ -394,67 +441,23 @@ t_bool cio_rqueue_avail(uint8 cid, uint32 qnum, uint32 esize)
uint32 io_read(uint32 pa, size_t size) uint32 io_read(uint32 pa, size_t size)
{ {
iolink *p; iolink *p;
uint8 cid, reg, data; uint8 slot, reg, data;
#if defined (REV3) #if defined (REV3)
/*
* NOTE: Not Yet Implemented, but: If 0x4BF00 is accessed and does
* not result in an error, the system assumes there are two MMUs
* installed. I think 0x4b000 is where a second MMU would live in
* IO space if there were multiple MMUs.
*/
if ((pa == MADDR_SLOT_0) ||
(pa == MADDR_SLOT_1) ||
(pa == MADDR_SLOT_2) ||
(pa == MADDR_SLOT_3)) {
switch(MEM_SIZE) {
case MSIZ_4M:
/* Configure with one 4MB boards */
if (pa < MADDR_SLOT_1) {
return MEMID_4M;
}
break;
case MSIZ_8M:
/* Configure with two 4MB boards */
if (pa < MADDR_SLOT_2) {
return MEMID_4M;
}
break;
case MSIZ_16M:
/* Configure with four 4MB boards */
return MEMID_4M;
case MSIZ_32M:
/* Configure with two 16MB boards */
if (pa < MADDR_SLOT_2) {
return MEMID_16M;
}
break;
case MSIZ_64M:
/* Configure with four 16MB boards */
return MEMID_16M;
default:
return 0;
}
return 0;
}
if (pa >= VCACHE_BOTTOM && pa < VCACHE_TOP) { if (pa >= VCACHE_BOTTOM && pa < VCACHE_TOP) {
sim_debug(EXECUTE_MSG, &cpu_dev,
"[UBUB] (VCACHE) Read addr %08x\n", pa);
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
return 0; return 0;
} }
if (pa >= BUB_BOTTOM && pa < BUB_TOP) { if (pa >= BUB_BOTTOM && pa < BUB_TOP) {
sim_debug(EXECUTE_MSG, &cpu_dev,
"[BUB] Read addr %08x\n", pa);
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
/* TODO: I don't remember why we do this! */ return 0;
if ((pa & 0xfff) == 3) {
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
}
/* TODO: Implement BUB */
return 1;
} }
#else #else
if (pa == MEMSIZE_REG) { if (pa == MEMSIZE_REG) {
@ -482,14 +485,14 @@ uint32 io_read(uint32 pa, size_t size)
/* CIO board area */ /* CIO board area */
if (pa >= CIO_BOTTOM && pa < CIO_TOP) { if (pa >= CIO_BOTTOM && pa < CIO_TOP) {
cid = CID(pa); slot = SLOT(pa);
reg = pa - CADDR(cid); reg = pa - CADDR(slot);
if (cio[cid].id == 0) { if (!cio[slot].populated) {
/* Nothing lives here */ /* Nothing lives here */
sim_debug(IO_DBG, &cpu_dev, sim_debug(IO_DBG, &cpu_dev,
"[READ] [%08x] No card at cid=%d reg=%d\n", "[READ] No card at slot=%d reg=%d\n",
R[NUM_PC], cid, reg); slot, reg);
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
return 0; return 0;
@ -504,96 +507,96 @@ uint32 io_read(uint32 pa, size_t size)
switch (reg) { switch (reg) {
case IOF_ID: case IOF_ID:
case IOF_VEC: case IOF_VEC:
switch(cio[cid].sysgen_s) { switch(cio[slot].sysgen_s) {
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
case CIO_INT0: /* We've seen an INT0 but not an INT1. */ case CIO_INT0: /* We've seen an INT0 but not an INT1. */
cio[cid].sysgen_s |= CIO_INT0; cio[slot].sysgen_s |= CIO_INT0;
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[READ] [%08x] (%d INT0) ID\n", "[READ] [%s] (%d INT0) ID\n",
R[NUM_PC], cid); cio[slot].name, slot);
/* Return the correct byte of our board ID */ /* Return the correct byte of our board ID */
if (reg == IOF_ID) { if (reg == IOF_ID) {
data = (cio[cid].id >> 8) & 0xff; data = (cio[slot].id >> 8) & 0xff;
} else { } else {
data = (cio[cid].id & 0xff); data = (cio[slot].id & 0xff);
} }
break; break;
case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */ case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */
cio[cid].sysgen_s |= CIO_INT0; cio[slot].sysgen_s |= CIO_INT0;
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[READ] [%08x] (%d INT0) SYSGEN\n", "[READ] [%s] (%d INT0) SYSGEN\n",
R[NUM_PC], cid); cio[slot].name, slot);
cio_sysgen(cid); cio_sysgen(slot);
data = cio[cid].ivec; data = cio[slot].ivec;
break; break;
case CIO_SYSGEN: /* We've already sysgen'ed */ case CIO_SYSGEN: /* We've already sysgen'ed */
cio[cid].sysgen_s |= CIO_INT0; /* This must come BEFORE the exp_handler */ cio[slot].sysgen_s |= CIO_INT0; /* This must come BEFORE the exp_handler */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[READ] [%08x] (%d INT0) EXPRESS JOB\n", "[READ] [%s] (%d INT0) EXPRESS JOB\n",
R[NUM_PC], cid); cio[slot].name, slot);
cio[cid].exp_handler(cid); cio[slot].exp_handler(slot);
data = cio[cid].ivec; data = cio[slot].ivec;
break; break;
default: default:
/* This should never happen */ /* This should never happen */
stop_reason = STOP_ERR; stop_reason = STOP_ERR;
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[READ] [%08x] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n", "[READ] [%s] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n",
R[NUM_PC], cid, cio[cid].sysgen_s); cio[slot].name, slot, cio[slot].sysgen_s);
data = 0; data = 0;
break; break;
} }
return data; return data;
case IOF_CTRL: case IOF_CTRL:
switch(cio[cid].sysgen_s) { switch(cio[slot].sysgen_s) {
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
case CIO_INT1: /* We've seen an INT1 but not an INT0 */ case CIO_INT1: /* We've seen an INT1 but not an INT0 */
/* There's nothing to do in this instance */ /* There's nothing to do in this instance */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[READ] [%08x] (%d INT1) IGNORED\n", "[READ] [%s] (%d INT1) IGNORED\n",
R[NUM_PC], cid); cio[slot].name, slot);
cio[cid].sysgen_s |= CIO_INT1; cio[slot].sysgen_s |= CIO_INT1;
break; break;
case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */ case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[READ] [%08x] (%d INT1) SYSGEN\n", "[READ] [%s] (%d INT1) SYSGEN\n",
R[NUM_PC], cid); cio[slot].name, slot);
cio[cid].sysgen_s |= CIO_INT1; cio[slot].sysgen_s |= CIO_INT1;
cio_sysgen(cid); cio_sysgen(slot);
break; break;
case CIO_SYSGEN: /* We've already sysgen'ed */ case CIO_SYSGEN: /* We've already sysgen'ed */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[READ] [%08x] (%d INT1) FULL\n", "[READ] [%s] (%d INT1) FULL\n",
R[NUM_PC], cid); cio[slot].name, slot);
cio[cid].sysgen_s |= CIO_INT1; /* This must come BEFORE the full handler */ cio[slot].sysgen_s |= CIO_INT1; /* This must come BEFORE the full handler */
cio[cid].full_handler(cid); cio[slot].full_handler(slot);
break; break;
default: default:
/* This should never happen */ /* This should never happen */
stop_reason = STOP_ERR; stop_reason = STOP_ERR;
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[READ] [%08x] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n", "[READ] [%s] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n",
R[NUM_PC], cid, cio[cid].sysgen_s); cio[slot].name, slot, cio[slot].sysgen_s);
break; break;
} }
return 0; /* Data returned is arbitrary */ return 0; /* Data returned is arbitrary */
case IOF_STAT: case IOF_STAT:
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[READ] [%08x] (%d RESET)\n", "[READ] [%s] (%d RESET)\n",
R[NUM_PC], cid); cio[slot].name, slot);
if (cio[cid].reset_handler) { if (cio[slot].reset_handler) {
cio[cid].reset_handler(cid); cio[slot].reset_handler(slot);
} }
cio[cid].sysgen_s = 0; cio[slot].sysgen_s = 0;
return 0; /* Data returned is arbitrary */ return 0; /* Data returned is arbitrary */
default: default:
/* We should never reach here, but if we do, there's /* We should never reach here, but if we do, there's
* nothing listening. */ * nothing listening. */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[READ] [%08x] No card at cid=%d reg=%d\n", "[READ] No card at slot=%d reg=%d\n",
R[NUM_PC], cid, reg); slot, reg);
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
return 0; return 0;
@ -609,8 +612,8 @@ uint32 io_read(uint32 pa, size_t size)
/* Not found. */ /* Not found. */
sim_debug(IO_DBG, &cpu_dev, sim_debug(IO_DBG, &cpu_dev,
"[%08x] [io_read] ADDR=%08x: No device found.\n", "[io_read] ADDR=%08x: No device found.\n",
R[NUM_PC], pa); pa);
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
return 0; return 0;
@ -619,36 +622,38 @@ uint32 io_read(uint32 pa, size_t size)
void io_write(uint32 pa, uint32 val, size_t size) void io_write(uint32 pa, uint32 val, size_t size)
{ {
iolink *p; iolink *p;
uint8 cid, reg; uint8 slot, reg;
#if defined(REV3) #if defined(REV3)
if (pa >= VCACHE_BOTTOM && pa < VCACHE_TOP) { if (pa >= VCACHE_BOTTOM && pa < VCACHE_TOP) {
sim_debug(EXECUTE_MSG, &cpu_dev,
"[UBUB] (VCACHE) Write addr %08x val 0x%x\n",
pa, val);
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
return; return;
} }
if (pa >= BUB_BOTTOM && pa < BUB_TOP) { if (pa >= BUB_BOTTOM && pa < BUB_TOP) {
sim_debug(EXECUTE_MSG, &cpu_dev,
"[BUB] Write addr %08x val 0x%x\n",
pa, val);
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
/* TODO: I don't remember why we do this! */ cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
if ((pa & 0xfff) == 3) {
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
}
/* TODO: Implement BUB */
return; return;
} }
#endif #endif
/* Feature Card Area */ /* Feature Card Area */
if (pa >= CIO_BOTTOM && pa < CIO_TOP) { if (pa >= CIO_BOTTOM && pa < CIO_TOP) {
cid = CID(pa); slot = SLOT(pa);
reg = pa - CADDR(cid); reg = pa - CADDR(slot);
if (cio[cid].id == 0) { if (!cio[slot].populated) {
/* Nothing lives here */ /* Nothing lives here */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[WRITE] [%08x] No card at cid=%d reg=%d\n", "[WRITE] No card at slot=%d reg=%d\n",
R[NUM_PC], cid, reg); slot, reg);
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
return; return;
@ -663,87 +668,87 @@ void io_write(uint32 pa, uint32 val, size_t size)
switch (reg) { switch (reg) {
case IOF_ID: case IOF_ID:
case IOF_VEC: case IOF_VEC:
switch(cio[cid].sysgen_s) { switch(cio[slot].sysgen_s) {
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
case CIO_INT0: /* We've seen an INT0 but not an INT1. */ case CIO_INT0: /* We've seen an INT0 but not an INT1. */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[WRITE] [%08x] (%d INT0) ID\n", "[WRITE] [%s] (%d INT0) ID\n",
R[NUM_PC], cid); cio[slot].name, slot);
cio[cid].sysgen_s |= CIO_INT0; cio[slot].sysgen_s |= CIO_INT0;
break; break;
case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */ case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[WRITE] [%08x] (%d INT0) SYSGEN\n", "[WRITE] [%s] (%d INT0) SYSGEN\n",
R[NUM_PC], cid); cio[slot].name, slot);
cio[cid].sysgen_s |= CIO_INT0; cio[slot].sysgen_s |= CIO_INT0;
cio_sysgen(cid); cio_sysgen(slot);
break; break;
case CIO_SYSGEN: /* We've already sysgen'ed */ case CIO_SYSGEN: /* We've already sysgen'ed */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[WRITE] [%08x] (%d INT0) EXPRESS JOB\n", "[WRITE] [%s] (%d INT0) EXPRESS JOB\n",
R[NUM_PC], cid); cio[slot].name, slot);
cio[cid].sysgen_s |= CIO_INT0; cio[slot].sysgen_s |= CIO_INT0;
cio[cid].exp_handler(cid); cio[slot].exp_handler(slot);
break; break;
default: default:
/* This should never happen */ /* This should never happen */
stop_reason = STOP_ERR; stop_reason = STOP_ERR;
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[WRITE] [%08x] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n", "[WRITE] [%s] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n",
R[NUM_PC], cid, cio[cid].sysgen_s); cio[slot].name, slot, cio[slot].sysgen_s);
break; break;
} }
return; return;
case IOF_CTRL: case IOF_CTRL:
switch(cio[cid].sysgen_s) { switch(cio[slot].sysgen_s) {
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
case CIO_INT1: /* We've seen an INT1 but not an INT0 */ case CIO_INT1: /* We've seen an INT1 but not an INT0 */
/* There's nothing to do in this instance */ /* There's nothing to do in this instance */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[WRITE] [%08x] (%d INT1) IGNORED\n", "[WRITE] [%s] (%d INT1) IGNORED\n",
R[NUM_PC], cid); cio[slot].name, slot);
cio[cid].sysgen_s |= CIO_INT1; cio[slot].sysgen_s |= CIO_INT1;
break; break;
case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */ case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[WRITE] [%08x] (%d INT1) SYSGEN\n", "[WRITE] [%s] (%d INT1) SYSGEN\n",
R[NUM_PC], cid); cio[slot].name, slot);
cio[cid].sysgen_s |= CIO_INT1; cio[slot].sysgen_s |= CIO_INT1;
cio_sysgen(cid); cio_sysgen(slot);
break; break;
case CIO_SYSGEN: /* We've already sysgen'ed */ case CIO_SYSGEN: /* We've already sysgen'ed */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[WRITE] [%08x] (%d INT1) FULL\n", "[WRITE] [%s] (%d INT1) FULL\n",
R[NUM_PC], cid); cio[slot].name, slot);
cio[cid].sysgen_s |= CIO_INT1; cio[slot].sysgen_s |= CIO_INT1;
cio[cid].full_handler(cid); cio[slot].full_handler(slot);
break; break;
default: default:
/* This should never happen */ /* This should never happen */
stop_reason = STOP_ERR; stop_reason = STOP_ERR;
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[WRITE] [%08x] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n", "[WRITE] [%s] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n",
R[NUM_PC], cid, cio[cid].sysgen_s); cio[slot].name, slot, cio[slot].sysgen_s);
break; break;
} }
return; return;
case IOF_STAT: case IOF_STAT:
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[WRITE] [%08x] (%d RESET)\n", "[WRITE] [%s] (%d RESET)\n",
R[NUM_PC], cid); cio[slot].name, slot);
if (cio[cid].reset_handler) { if (cio[slot].reset_handler) {
cio[cid].reset_handler(cid); cio[slot].reset_handler(slot);
} }
cio[cid].sysgen_s = 0; cio[slot].sysgen_s = 0;
return; return;
default: default:
/* We should never reach here, but if we do, there's /* We should never reach here, but if we do, there's
* nothing listening. */ * nothing listening. */
sim_debug(CIO_DBG, &cpu_dev, sim_debug(CIO_DBG, &cpu_dev,
"[WRITE] [%08x] No card at cid=%d reg=%d\n", "[WRITE] No card at slot=%d reg=%d\n",
R[NUM_PC], cid, reg); slot, reg);
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
return; return;
@ -760,27 +765,8 @@ void io_write(uint32 pa, uint32 val, size_t size)
/* Not found. */ /* Not found. */
sim_debug(IO_DBG, &cpu_dev, sim_debug(IO_DBG, &cpu_dev,
"[%08x] [io_write] ADDR=%08x: No device found.\n", "[io_write] ADDR=%08x: No device found.\n",
R[NUM_PC], pa); pa);
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
} }
/* For debugging only */
void dump_entry(uint32 dbits, DEVICE *dev, CONST char *type,
uint32 esize, cio_entry *entry, uint8 *app_data)
{
char appl[64];
uint32 i, c_offset;
for (i = 0, c_offset=0; i < (esize - QESIZE); i++) {
snprintf(appl + c_offset, 3, "%02x", app_data[i]);
c_offset += 2;
}
sim_debug(dbits, dev,
"*** %s ENTRY: byte_count=%04x, subdevice=%02x, opcode=%d, address=%08x, app_data=%s\n",
type, entry->byte_count, entry->subdevice,
entry->opcode, entry->address, appl);
}

View file

@ -1,6 +1,6 @@
/* 3b2_io.h: AT&T 3B2 Model 400 IO dispatch (Header) /* 3b2_io.h: Common I/O (CIO) Feature Card Support
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -119,9 +119,18 @@
#define IO_BOTTOM 0x40000 #define IO_BOTTOM 0x40000
#define IO_TOP 0x50000 #define IO_TOP 0x50000
#if defined(REV3)
#define UBUS_BOTTOM 0x1c00000
#define UBUS_TOP 0x2000000
#endif
/* CIO area */ /* CIO area */
#define CIO_BOTTOM 0x200000 #define CIO_BOTTOM 0x200000
#if defined(REV3)
#define CIO_TOP 0x1a00000
#else
#define CIO_TOP 0x2000000 #define CIO_TOP 0x2000000
#endif
#define IOF_ID 0 #define IOF_ID 0
#define IOF_VEC 1 #define IOF_VEC 1
@ -143,7 +152,7 @@
#define CIO_SYSGEN_OK 3 #define CIO_SYSGEN_OK 3
/* Map a physical address to a card ID */ /* Map a physical address to a card ID */
#define CID(pa) (((((pa) >> 0x14) & 0x1f) / 2) - 1) #define SLOT(pa) (((((pa) >> 0x14) & 0x1f) / 2) - 1)
/* Map a card ID to a base address */ /* Map a card ID to a base address */
#define CADDR(bid) (((((bid) + 1) * 2) << 0x14)) #define CADDR(bid) (((((bid) + 1) * 2) << 0x14))
@ -160,15 +169,19 @@
#define CIO_INT1 2 #define CIO_INT1 2
#define CIO_SYSGEN 3 #define CIO_SYSGEN 3
#define CIO_SET_INT(slot) (cio_int_req |= (1 << slot)) #define CIO_SET_INT(slot) if (cio[slot].populated && cio[slot].ivec >= 2) (cio_int_req |= (1 << slot))
#define CIO_CLR_INT(slot) (cio_int_req &= ~(1 << slot)) #define CIO_CLR_INT(slot) (cio_int_req &= ~(1 << slot))
#define CIO_NAME_LEN 8
typedef struct { typedef struct {
uint16 id; /* Card ID */ t_bool populated; /* Populated? */
void (*exp_handler)(uint8 cid); /* Handler for express jobs */ uint16 id; /* CIO identifier */
void (*full_handler)(uint8 cid); /* Handler for full jobs */ char name[CIO_NAME_LEN]; /* Device name */
void (*sysgen)(uint8 cid); /* Sysgen routine (optional) */ void (*exp_handler)(uint8 slot); /* Handler for express jobs */
void (*reset_handler)(uint8 cid); /* RESET request handler (optional) */ void (*full_handler)(uint8 slot); /* Handler for full jobs */
void (*sysgen)(uint8 slot); /* Sysgen routine (optional) */
void (*reset_handler)(uint8 slot); /* RESET request handler (optional) */
uint32 rqp; /* Request Queue Pointer */ uint32 rqp; /* Request Queue Pointer */
uint32 cqp; /* Completion Queue Pointer */ uint32 cqp; /* Completion Queue Pointer */
uint8 rqs; /* Request queue size */ uint8 rqs; /* Request queue size */
@ -226,26 +239,32 @@ typedef struct {
t_stat cio_reset(DEVICE *dptr); t_stat cio_reset(DEVICE *dptr);
t_stat cio_svc(UNIT *uptr); t_stat cio_svc(UNIT *uptr);
void cio_clear(uint8 cid); t_stat cio_install(uint16 id,
CONST char *name,
uint8 ipl,
void (*exp_handler)(uint8 slot),
void (*full_handler)(uint8 slot),
void (*sysgen)(uint8 slot),
void (*reset_handler)(uint8 slot),
uint8 *slot);
void cio_remove(uint8 slot);
void cio_remove_all(uint16 id);
uint32 cio_crc32_shift(uint32 crc, uint8 data); uint32 cio_crc32_shift(uint32 crc, uint8 data);
void cio_cexpress(uint8 cid, uint32 esize, cio_entry *cqe, uint8 *app_data); void cio_cexpress(uint8 slot, uint32 esize, cio_entry *cqe, uint8 *app_data);
void cio_cqueue(uint8 cid, uint8 cmd_stat, uint32 esize, cio_entry *cqe, uint8 *app_data); void cio_cqueue(uint8 slot, uint8 cmd_stat, uint32 esize, cio_entry *cqe, uint8 *app_data);
t_bool cio_cqueue_avail(uint8 cid, uint32 esize); t_bool cio_cqueue_avail(uint8 slot, uint32 esize);
void cio_rexpress(uint8 cid, uint32 esize, cio_entry *rqe, uint8 *app_data); void cio_rexpress(uint8 slot, uint32 esize, cio_entry *rqe, uint8 *app_data);
t_stat cio_rqueue(uint8 cid, uint32 qnum, uint32 esize, cio_entry *rqe, uint8 *app_data); t_stat cio_rqueue(uint8 slot, uint32 qnum, uint32 esize, cio_entry *rqe, uint8 *app_data);
t_bool cio_rqueue_avail(uint8 cid, uint32 qnum, uint32 esize); t_bool cio_rqueue_avail(uint8 slot, uint32 qnum, uint32 esize);
uint16 cio_r_lp(uint8 cid, uint32 qnum, uint32 esize); uint16 cio_r_lp(uint8 slot, uint32 qnum, uint32 esize);
uint16 cio_r_ulp(uint8 cid, uint32 qnum, uint32 esize); uint16 cio_r_ulp(uint8 slot, uint32 qnum, uint32 esize);
uint16 cio_c_lp(uint8 cid, uint32 esize); uint16 cio_c_lp(uint8 slot, uint32 esize);
uint16 cio_c_ulp(uint8 cid, uint32 esize); uint16 cio_c_ulp(uint8 slot, uint32 esize);
void cio_sysgen(uint8 cid); void cio_sysgen(uint8 slot);
uint32 io_read(uint32 pa, size_t size); uint32 io_read(uint32 pa, size_t size);
void io_write(uint32 pa, uint32 val, size_t size); void io_write(uint32 pa, uint32 val, size_t size);
void dump_entry(uint32 dbits, DEVICE *dev, CONST char *type,
uint32 esize, cio_entry *entry, uint8 *app_data);
extern uint16 cio_int_req; extern uint16 cio_int_req;
extern CIO_STATE cio[CIO_SLOTS]; extern CIO_STATE cio[CIO_SLOTS];

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/* 3b2_iu.h: SCN2681A Dual UART Header /* 3b2_iu.h: SCN2681A Dual UART
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -56,18 +56,22 @@
#define STS_FER 0x40 /* Framing error */ #define STS_FER 0x40 /* Framing error */
#define STS_RXB 0x80 /* Received break */ #define STS_RXB 0x80 /* Received break */
#define ISTS_TAI 0x01 /* Transmitter ready A */ #define ISTS_TXRA 0x01 /* Transmitter ready A */
#define ISTS_RAI 0x02 /* Receiver ready A */ #define ISTS_RXRA 0x02 /* Receiver ready A */
#define ISTS_CBA 0x04 /* Change in break A */ #define ISTS_DBA 0x04 /* Delta Break A */
#define ISTS_CRI 0x08 /* Counter ready */ #define ISTS_CRI 0x08 /* Counter ready */
#define ISTS_TBI 0x10 /* Transmitter ready B */ #define ISTS_TXRB 0x10 /* Transmitter ready B */
#define ISTS_RBI 0x20 /* Receiver ready B */ #define ISTS_RXRB 0x20 /* Receiver ready B */
#define ISTS_CBB 0x40 /* Change in break B */ #define ISTS_DBB 0x40 /* Delta Break B */
#define ISTS_IPC 0x80 /* Interrupt port change */ #define ISTS_IPC 0x80 /* Interrupt port change */
#define MODE_V_CHM 6 /* Channel mode */ #define MODE_V_CHM 6 /* Channel mode */
#define MODE_M_CHM 0x3 #define MODE_M_CHM 0x3
/* Transmitter State bits */
#define T_HOLD 1
#define T_XMIT 2
/* Used by the DMAC */ /* Used by the DMAC */
#define IUA_DATA_REG 3 #define IUA_DATA_REG 3
#define IUB_DATA_REG 11 #define IUB_DATA_REG 11
@ -81,7 +85,7 @@
#define CTL 7 #define CTL 7
#define SRB 9 #define SRB 9
#define RHRB 11 #define RHRB 11
#define INPRT 13 /* Input port data */ #define INPRT 13
#define START_CTR 14 #define START_CTR 14
#define STOP_CTR 15 #define STOP_CTR 15
@ -108,16 +112,14 @@
#define TX_EN 1 #define TX_EN 1
#define RX_EN 2 #define RX_EN 2
#define UM_CTR_EXT 0 /* Control Register commands */
#define UM_CTR_TXCA 1 #define CR_RST_MR 1
#define UM_CTR_TXCB 2 #define CR_RST_RX 2
#define UM_CTR_DIV16 3 #define CR_RST_TX 3
#define UM_TMR_EXT 4 #define CR_RST_ERR 4
#define UM_TMR_EXT16 5 #define CR_RST_BRK 5
#define UM_TMR_XTL 6 #define CR_START_BRK 6
#define UM_TMR_XTL16 7 #define CR_STOP_BRK 7
#define UM_MASK 0x70
#define UM_SHIFT 4
/* IMR bits */ /* IMR bits */
#define IMR_TXRA 0x01 #define IMR_TXRA 0x01
@ -139,40 +141,57 @@
#define IU_BUF_SIZE 3 #define IU_BUF_SIZE 3
#define IU_DCDA 0x01 /* Data Carrier Detect inputs and input change bits */
#if defined(REV3)
#define IU_DCDB_CH 0x80
#define IU_DCDA_CH 0x40
#define IU_DCDB 0x08
#define IU_DCDA 0x04
#else
#define IU_DCDB_CH 0x20
#define IU_DCDA_CH 0x10
#define IU_DCDB 0x02 #define IU_DCDB 0x02
#define IU_DTRA 0x01 #define IU_DCDA 0x01
#define IU_DTRB 0x02 #endif
#define DMA_NONE 0
#define DMA_VERIFY 1
#define DMA_WRITE 2
#define DMA_READ 4
/* Default baud rate generator (9600 baud) */ /* Default baud rate generator (9600 baud) */
#define BRG_DEFAULT 11 #define BRG_DEFAULT 11
#define IU_TIMER_RATE 2.114 /* microseconds per step */ /* The 2681 DUART includes a 16-bit timer/counter that can be used to
* trigger an interrupt after a certain amount of time has passed.
*
* The 2681 uses a crystal with a frequency of 3.686400 MHz, and the
* timer/counter uses this frequency divided by 16, giving a final
* timer/counter frequency of 230,400 Hz. There are therefore 4.34
* microseconds of wall time per tick of the timer.
*
* The multiplier defined below is a default that can be adjusted to
* make IU timing faster, but less accurate, if desired */
#define IU_TIMER_MULTIPLIER 4
typedef struct iu_port { typedef struct iu_port {
uint8 stat; /* Port Status */
uint8 cmd; /* Command */ uint8 cmd; /* Command */
uint8 mode[2]; /* Two mode buffers */ uint8 mode[2]; /* Two mode buffers */
uint8 modep; /* Point to mode[0] or mode[1] */ uint8 modep; /* Point to mode[0] or mode[1] */
uint8 conf; /* Configuration bits */ uint8 conf; /* Configuration bits */
uint8 txbuf; /* Transmit Holding Register */ uint8 sr; /* Status Register */
uint8 thr; /* Transmit Holding Register */
uint8 txr; /* Transmit Shift Register */
uint8 rxr; /* Receive Shift Register */
uint8 rxbuf[IU_BUF_SIZE]; /* Receive Holding Register (3 bytes) */ uint8 rxbuf[IU_BUF_SIZE]; /* Receive Holding Register (3 bytes) */
uint8 w_p; /* Buffer Write Pointer */ uint8 w_p; /* Receive Buffer Write Pointer */
uint8 r_p; /* Buffer Read Pointer */ uint8 r_p; /* Receive Buffer Read Pointer */
uint8 dma; /* Currently active DMA mode */ uint8 tx_state; /* Transmitting state flags (HOLD, XMIT) */
t_bool dma; /* DMA currently active */
t_bool drq; /* DMA request enabled */ t_bool drq; /* DMA request enabled */
t_bool rxr_full; /* Receive Shift Register is full */
} IU_PORT; } IU_PORT;
typedef struct iu_state { typedef struct iu_state {
uint8 istat; /* Interrupt Status */ uint8 isr; /* Interrupt Status Register */
uint8 imr; /* Interrupt Mask Register */ uint8 imr; /* Interrupt Mask Register */
uint8 acr; uint8 acr; /* Aux. Control Register */
uint8 opcr; /* Output Port Configuration */ uint8 opcr; /* Output Port Configuration */
uint8 inprt; /* Input Port Data */ uint8 inprt; /* Input Port Data */
uint8 ipcr; /* Input Port Change Register */ uint8 ipcr; /* Input Port Change Register */
@ -189,12 +208,13 @@ t_stat contty_detach(UNIT *uptr);
t_stat tti_reset(DEVICE *dptr); t_stat tti_reset(DEVICE *dptr);
t_stat contty_reset(DEVICE *dptr); t_stat contty_reset(DEVICE *dptr);
t_stat iu_timer_reset(DEVICE *dptr); t_stat iu_timer_reset(DEVICE *dptr);
t_stat iu_timer_set_mult(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat iu_timer_show_mult(FILE *st, UNIT *uptr, int val, CONST void *desc);
t_stat iu_svc_tti(UNIT *uptr); t_stat iu_svc_tti(UNIT *uptr);
t_stat iu_svc_tto(UNIT *uptr); t_stat iu_svc_tto(UNIT *uptr);
t_stat iu_svc_contty_rcv(UNIT *uptr); t_stat iu_svc_contty(UNIT *uptr);
t_stat iu_svc_contty_xmt(UNIT *uptr); t_stat iu_svc_contty_xmt(UNIT *uptr);
t_stat iu_svc_timer(UNIT *uptr); t_stat iu_svc_timer(UNIT *uptr);
t_stat iu_tx(uint8 portno, uint8 val);
uint32 iu_read(uint32 pa, size_t size); uint32 iu_read(uint32 pa, size_t size);
void iu_write(uint32 pa, uint32 val, size_t size); void iu_write(uint32 pa, uint32 val, size_t size);
void iua_drq_handled(); void iua_drq_handled();

View file

@ -1,7 +1,6 @@
/* 3b2_rev2_mau.c: AT&T 3B2 Rev 2 (Model 400) Math Acceleration /* 3b2_mau.c: WE32106 and WE32206 Math Accelerator Unit
Unit (WE32106 MAU) Implementation
Copyright (c) 2019, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -30,11 +29,33 @@
--------------------------------------------------------------------- ---------------------------------------------------------------------
This file is part of a simulation of the WE32106 Math Acceleration This file is part of a simulation of the WE 32106 and WE 32206 Math
Unit. The WE32106 MAU is an IEEE-754 compabitle floating point Acceleration Units. The WE 32106 and WE 32206 are IEEE-754
hardware math accelerator that was available as an optional compabitle floating point hardware math accelerators that were
component on the AT&T 3B2/310 and 3B2/400, and a standard component available as an optional component on the AT&T 3B2/310 and 3B2/400,
on the 3B2/500, 3B2/600, and 3B2/1000. and as a standard component on the 3B2/500, 3B2/600, 3B2/700, and
3B2/1000.
Unimplemented Features
======================
All features of the WE 32106 MAU have been implemented, but there
remain some features of the WE 32206 that are not yet implemented.
Neither System V UNIX nor the Version 3 MAU diagnostics appear to
use these features in any way, but there is no guarantee that other
software does not use them. They are:
- The FE, UW, and WF bits of the Auxiliary Status Register
- The new operand registers f4 through f7
- The register bank select feature of the Command Register
- The RC and RCS bits of the Command Register
- The new WE 32206 instructions:
1. ATAN
2. COS
3. PI
4. SIN
---------------------------------------------------------------------
Portions of this code are derived from the SoftFloat 2c library by Portions of this code are derived from the SoftFloat 2c library by
John R. Hauser. Functions derived from SoftFloat 2c are clearly John R. Hauser. Functions derived from SoftFloat 2c are clearly
@ -81,7 +102,7 @@
--------------------------------------------------------------------- ---------------------------------------------------------------------
*/ */
#include "3b2_rev2_mau.h" #include "3b2_mau.h"
#include <math.h> #include <math.h>
@ -123,8 +144,6 @@ static void sub_128(t_uint64 a0, t_uint64 a1,
static void mul_64_to_128(t_uint64 a, t_uint64 b, t_uint64 *r_low, t_uint64 *r_high); static void mul_64_to_128(t_uint64 a, t_uint64 b, t_uint64 *r_low, t_uint64 *r_high);
static void mul_64_by_shifted_32_to_128(t_uint64 a, uint32 b, t_mau_128 *result); static void mul_64_by_shifted_32_to_128(t_uint64 a, uint32 b, t_mau_128 *result);
static t_uint64 estimate_div_128_to_64(t_uint64 a0, t_uint64 a1, t_uint64 b); static t_uint64 estimate_div_128_to_64(t_uint64 a0, t_uint64 a1, t_uint64 b);
static uint32 estimate_sqrt_32(int16 a_exp, uint32 a);
static uint32 round_pack_int(t_bool sign, t_uint64 frac, RM rounding_mode); static uint32 round_pack_int(t_bool sign, t_uint64 frac, RM rounding_mode);
static t_int64 round_pack_int64(t_bool sign, static t_int64 round_pack_int64(t_bool sign,
t_uint64 abs_0, t_uint64 abs_1, t_uint64 abs_0, t_uint64 abs_1,
@ -208,7 +227,13 @@ UNIT mau_unit = { UDATA(NULL, 0, 0) };
MAU_STATE mau_state; MAU_STATE mau_state;
BITFIELD asr_bits[] = { BITFIELD asr_bits[] = {
#if defined(REV3)
BIT(FE),
BITFFMT(VER,3,%d),
BIT(UW),
#else
BITNCF(5), BITNCF(5),
#endif
BIT(PR), BIT(PR),
BIT(QS), BIT(QS),
BIT(US), BIT(US),
@ -219,7 +244,11 @@ BITFIELD asr_bits[] = {
BIT(UM), BIT(UM),
BIT(OM), BIT(OM),
BIT(IM), BIT(IM),
#if defined(REV3)
BIT(WF),
#else
BITNCF(1), BITNCF(1),
#endif
BIT(UO), BIT(UO),
BIT(CSC), BIT(CSC),
BIT(PS), BIT(PS),
@ -279,7 +308,7 @@ DEVICE mau_dev = {
#ifdef REV3 #ifdef REV3
DEV_DEBUG, /* Rev 3 flags: Always required */ DEV_DEBUG, /* Rev 3 flags: Always required */
#else #else
DEV_DISABLE|DEV_DIS|DEV_DEBUG, /* Rev 2 flags */ DEV_DISABLE|DEV_DEBUG, /* Rev 2 flags: Can be disabled */
#endif #endif
0, /* debug control flags */ 0, /* debug control flags */
mau_debug, /* debug flag names */ mau_debug, /* debug flag names */
@ -314,10 +343,18 @@ XFP GEN_NONTRAPPING_NAN = {
}; };
CONST char *mau_op_names[32] = { CONST char *mau_op_names[32] = {
"0x00", "0x01", "ADD", "SUB", "DIV", "REM", "MUL", "MOVE", /* 00-07 */ "0x00", "0x01", "ADD", "SUB",
"RDASR", "WRASR", "CMP", "CMPE", "ABS", "SQRT", "RTOI", "FTOI", /* 08-0F */ "DIV", "REM", "MUL", "MOVE",
"ITOF", "DTOF", "FTOD", "NOP", "EROF", "0x15", "0x16", "NEG", /* 10-17 */ "RDASR", "WRASR", "CMP", "CMPE",
"LDR", "0x19", "CMPS", "CMPES", "0x1C", "0x1D", "0x1E", "0x1F" /* 18-1F */ "ABS", "SQRT", "RTOI", "FTOI",
"ITOF", "DTOF", "FTOD", "NOP",
"EROF", "0x15", "0x16", "NEG",
"LDR", "0x19", "CMPS", "CMPES",
#if defined(REV3)
"SIN", "COS", "ATAN", "PI"
#else
"0x1C", "0x1D", "0x1E", "0x1F"
#endif
}; };
CONST char *src_op_names[8] = { CONST char *src_op_names[8] = {
@ -366,8 +403,8 @@ static SIM_INLINE void mau_case_div_zero(XFP *op1, XFP *op2, XFP *result)
static SIM_INLINE void mau_exc(uint32 flag, uint32 mask) static SIM_INLINE void mau_exc(uint32 flag, uint32 mask)
{ {
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [mau_exc] asr=%08x flag=%08x mask=%08x\n", "[mau_exc] asr=%08x flag=%08x mask=%08x\n",
R[NUM_PC], mau_state.asr, flag, mask); mau_state.asr, flag, mask);
mau_state.asr |= flag; mau_state.asr |= flag;
@ -427,8 +464,8 @@ static SIM_INLINE void abort_on_fault()
stop_reason = STOP_EX; stop_reason = STOP_EX;
} }
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [abort_on_fault] Aborting on un-maskable overflow fault. ASR=%08x\n", "[abort_on_fault] Aborting on un-maskable overflow fault. ASR=%08x\n",
R[NUM_PC], mau_state.asr); mau_state.asr);
cpu_abort(NORMAL_EXCEPTION, INTEGER_OVERFLOW); cpu_abort(NORMAL_EXCEPTION, INTEGER_OVERFLOW);
} }
@ -438,8 +475,8 @@ static SIM_INLINE void abort_on_fault()
stop_reason = STOP_EX; stop_reason = STOP_EX;
} }
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [abort_on_fault] Aborting on ECP fault. ASR=%08x\n", "[abort_on_fault] Aborting on ECP fault. ASR=%08x\n",
R[NUM_PC], mau_state.asr); mau_state.asr);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
} }
@ -475,7 +512,6 @@ static void clear_asr()
*/ */
static t_bool set_nz() static t_bool set_nz()
{ {
switch(mau_state.opcode) { switch(mau_state.opcode) {
case M_NOP: case M_NOP:
case M_RDASR: case M_RDASR:
@ -490,6 +526,9 @@ static t_bool set_nz()
t_stat mau_reset(DEVICE *dptr) t_stat mau_reset(DEVICE *dptr)
{ {
memset(&mau_state, 0, sizeof(MAU_STATE)); memset(&mau_state, 0, sizeof(MAU_STATE));
#if defined(REV3)
mau_state.asr |= MAU_ASR_VER; /* Version 1 MAU */
#endif
return SCPE_OK; return SCPE_OK;
} }
@ -846,44 +885,6 @@ static t_uint64 estimate_div_128_to_64(t_uint64 a0, t_uint64 a1, t_uint64 b)
return z; return z;
} }
/*
* Returns an approximation of the square root of the 32-bit
* value 'a'.
*
* Derived from the SoftFloat 2c package (see copyright notice above)
*/
static uint32 estimate_sqrt_32(int16 a_exp, uint32 a)
{
static const uint16 sqrt_odd_adjust[] = {
0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0,
0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67
};
static const uint16 sqrt_even_adjust[] = {
0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E,
0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002
};
int8 index;
uint32 z;
index = (a >> 27) & 0xf;
if (a_exp & 1) {
z = 0x4000 + (a >> 17) - sqrt_odd_adjust[index];
z = ((a / z) << 14) + (z << 15);
a >>= 1;
}
else {
z = 0x8000 + (a >> 17) - sqrt_even_adjust[index];
z = a / z + z;
z = (0x20000 <= z) ? 0xFFFF8000 : ( z<<15 );
if ( z <= a ) return (uint32) (((int32) a) >> 1);
}
return ((uint32) ((((t_uint64) a )<<31 ) / z)) + (z >> 1);
}
static uint32 approx_recip_sqrt_32(uint32 oddExpA, uint32 a) static uint32 approx_recip_sqrt_32(uint32 oddExpA, uint32 a)
{ {
int index; int index;
@ -1833,9 +1834,8 @@ void xfp_to_decimal(XFP *a, DEC *d, RM rounding_mode)
d->h |= (uint32)digits[15] << 8; d->h |= (uint32)digits[15] << 8;
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [xfp_to_decimal] " "[xfp_to_decimal] "
"Digits: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d 0x%x\n", "Digits: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d 0x%x\n",
R[NUM_PC],
digits[17], digits[16], digits[15], digits[14], digits[13], digits[12], digits[17], digits[16], digits[15], digits[14], digits[13], digits[12],
digits[11], digits[10], digits[9], digits[8], digits[7], digits[6], digits[11], digits[10], digits[9], digits[8], digits[7], digits[6],
digits[5], digits[4], digits[3], digits[2], digits[1], digits[0], digits[5], digits[4], digits[3], digits[2], digits[1], digits[0],
@ -1855,8 +1855,8 @@ void mau_decimal_to_xfp(DEC *d, XFP *a)
t_int64 signed_tmp; t_int64 signed_tmp;
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [mau_decimal_to_xfp] DEC input: %08x %08x %08x\n", "[mau_decimal_to_xfp] DEC input: %08x %08x %08x\n",
R[NUM_PC], d->h, (uint32)(d->l >> 32), (uint32)(d->l)); d->h, (uint32)(d->l >> 32), (uint32)(d->l));
sign = (d->l) & 15; sign = (d->l) & 15;
digits[0] = (d->l >> 4) & 15; digits[0] = (d->l >> 4) & 15;
@ -1879,9 +1879,8 @@ void mau_decimal_to_xfp(DEC *d, XFP *a)
digits[17] = (d->h >> 8) & 15; digits[17] = (d->h >> 8) & 15;
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [mau_decimal_to_xfp] " "[mau_decimal_to_xfp] "
"Digits: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d 0x%x\n", "Digits: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d 0x%x\n",
R[NUM_PC],
digits[17], digits[16], digits[15], digits[14], digits[13], digits[12], digits[17], digits[16], digits[15], digits[14], digits[13], digits[12],
digits[11], digits[10], digits[9], digits[8], digits[7], digits[6], digits[11], digits[10], digits[9], digits[8], digits[7], digits[6],
digits[5], digits[4], digits[3], digits[2], digits[1], digits[0], digits[5], digits[4], digits[3], digits[2], digits[1], digits[0],
@ -1906,14 +1905,14 @@ void mau_decimal_to_xfp(DEC *d, XFP *a)
} }
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [mau_decimal_to_xfp] tmp val = %lld\n", "[mau_decimal_to_xfp] tmp val = %lld\n",
R[NUM_PC], signed_tmp); signed_tmp);
mau_int64_to_xfp((t_uint64) signed_tmp, a); mau_int64_to_xfp((t_uint64) signed_tmp, a);
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [mau_decimal_to_xfp] XFP = %04x%016llx\n", "[mau_decimal_to_xfp] XFP = %04x%016llx\n",
R[NUM_PC], a->sign_exp, a->frac); a->sign_exp, a->frac);
} }
@ -2050,8 +2049,7 @@ static void xfp_add_fracs(XFP *a, XFP *b, t_bool sign, XFP *result, RM rounding_
int32 exp_diff; int32 exp_diff;
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [ADD_FRACS] a=%04x%016llx b=%04x%016llx\n", "[ADD_FRACS] a=%04x%016llx b=%04x%016llx\n",
R[NUM_PC],
a->sign_exp, a->frac, a->sign_exp, a->frac,
b->sign_exp, b->frac); b->sign_exp, b->frac);
@ -2384,8 +2382,7 @@ static void xfp_mul(XFP *a, XFP *b, XFP *result, RM rounding_mode)
t_uint64 a_frac, b_frac, r_frac_0, r_frac_1; t_uint64 a_frac, b_frac, r_frac_0, r_frac_1;
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [MUL] op1=%04x%016llx op2=%04x%016llx\n", "[MUL] op1=%04x%016llx op2=%04x%016llx\n",
R[NUM_PC],
a->sign_exp, a->frac, a->sign_exp, a->frac,
b->sign_exp, b->frac); b->sign_exp, b->frac);
@ -2469,8 +2466,8 @@ static void xfp_div(XFP *a, XFP *b, XFP *result, RM rounding_mode)
t_uint64 rem0, rem1, rem2, term0, term1, term2; t_uint64 rem0, rem1, rem2, term0, term1, term2;
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [DIV] op1=%04x%016llx op2=%04x%016llx\n", "[DIV] op1=%04x%016llx op2=%04x%016llx\n",
R[NUM_PC], b->sign_exp, b->frac, a->sign_exp, a->frac); b->sign_exp, b->frac, a->sign_exp, a->frac);
a_sign = XFP_SIGN(a); a_sign = XFP_SIGN(a);
a_exp = XFP_EXP(a); a_exp = XFP_EXP(a);
@ -2525,7 +2522,7 @@ static void xfp_div(XFP *a, XFP *b, XFP *result, RM rounding_mode)
} }
/* Divide by zero - SPECIAL CASE 4 */ /* Divide by zero - SPECIAL CASE 4 */
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [DIV] Divide by zero detected.\n", R[NUM_PC]); "[DIV] Divide by zero detected.\n");
mau_case_div_zero(a, b, result); mau_case_div_zero(a, b, result);
return; return;
} }
@ -2583,8 +2580,8 @@ static void xfp_sqrt(XFP *a, XFP *result, RM rounding_mode)
t_mau_128 nan_128, rem, y, term; t_mau_128 nan_128, rem, y, term;
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [SQRT] op1=%04x%016llx\n", "[SQRT] op1=%04x%016llx\n",
R[NUM_PC], a->sign_exp, a->frac); a->sign_exp, a->frac);
a_sign = XFP_SIGN(a); a_sign = XFP_SIGN(a);
a_exp = XFP_EXP(a); a_exp = XFP_EXP(a);
@ -2844,12 +2841,12 @@ static void load_src_op(uint8 op, XFP *xfp)
xfp->frac = mau_state.f3.frac; xfp->frac = mau_state.f3.frac;
break; break;
case M_OP_MEM_SINGLE: case M_OP_MEM_SINGLE:
sfp = read_w(mau_state.src, ACC_AF); sfp = read_w(mau_state.src, ACC_AF, BUS_PER);
sfp_to_xfp(sfp, xfp); sfp_to_xfp(sfp, xfp);
break; break;
case M_OP_MEM_DOUBLE: case M_OP_MEM_DOUBLE:
dfp = (t_uint64) read_w(mau_state.src + 4, ACC_AF); dfp = (t_uint64) read_w(mau_state.src + 4, ACC_AF, BUS_PER);
dfp |= ((t_uint64) read_w(mau_state.src, ACC_AF)) << 32; dfp |= ((t_uint64) read_w(mau_state.src, ACC_AF, BUS_PER)) << 32;
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[load_src_op][DOUBLE] Loaded %016llx\n", "[load_src_op][DOUBLE] Loaded %016llx\n",
dfp); dfp);
@ -2859,9 +2856,9 @@ static void load_src_op(uint8 op, XFP *xfp)
xfp->sign_exp, xfp->frac); xfp->sign_exp, xfp->frac);
break; break;
case M_OP_MEM_TRIPLE: case M_OP_MEM_TRIPLE:
xfp->frac = (t_uint64) read_w(mau_state.src + 8, ACC_AF); xfp->frac = (t_uint64) read_w(mau_state.src + 8, ACC_AF, BUS_PER);
xfp->frac |= ((t_uint64) read_w(mau_state.src + 4, ACC_AF)) << 32; xfp->frac |= ((t_uint64) read_w(mau_state.src + 4, ACC_AF, BUS_PER)) << 32;
xfp->sign_exp = (uint32) read_w(mau_state.src, ACC_AF); xfp->sign_exp = (uint32) read_w(mau_state.src, ACC_AF, BUS_PER);
break; break;
default: default:
break; break;
@ -2877,9 +2874,9 @@ static void load_op1_decimal(DEC *d)
switch (mau_state.op1) { switch (mau_state.op1) {
case M_OP_MEM_TRIPLE: case M_OP_MEM_TRIPLE:
low = read_w(mau_state.src + 8, ACC_AF); low = read_w(mau_state.src + 8, ACC_AF, BUS_PER);
mid = read_w(mau_state.src + 4, ACC_AF); mid = read_w(mau_state.src + 4, ACC_AF, BUS_PER);
high = read_w(mau_state.src, ACC_AF); high = read_w(mau_state.src, ACC_AF, BUS_PER);
d->l = low; d->l = low;
d->l |= ((t_uint64) mid << 32); d->l |= ((t_uint64) mid << 32);
d->h = high; d->h = high;
@ -2911,7 +2908,7 @@ static void store_op3_int(uint32 val)
mau_state.f3.frac = (t_uint64)val; mau_state.f3.frac = (t_uint64)val;
break; break;
case M_OP3_MEM_SINGLE: case M_OP3_MEM_SINGLE:
write_w(mau_state.dst, val); write_w(mau_state.dst, val, BUS_PER);
break; break;
default: default:
/* Indeterminate output, unsupported */ /* Indeterminate output, unsupported */
@ -2927,9 +2924,9 @@ static void store_op3_decimal(DEC *d)
switch(mau_state.op3) { switch(mau_state.op3) {
case M_OP3_MEM_TRIPLE: case M_OP3_MEM_TRIPLE:
write_w(mau_state.dst, d->h); write_w(mau_state.dst, d->h, BUS_PER);
write_w(mau_state.dst + 4, (uint32)((t_uint64)d->l >> 32)); write_w(mau_state.dst + 4, (uint32)((t_uint64)d->l >> 32), BUS_PER);
write_w(mau_state.dst + 8, (uint32)d->l); write_w(mau_state.dst + 8, (uint32)d->l, BUS_PER);
break; break;
default: default:
/* Unsupported */ /* Unsupported */
@ -2998,8 +2995,7 @@ static void store_op3(XFP *xfp)
t_bool store_dr = FALSE; t_bool store_dr = FALSE;
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [store_op3] op3=%04x%016llx\n", "[store_op3] op3=%04x%016llx\n",
R[NUM_PC],
xfp->sign_exp, xfp->sign_exp,
xfp->frac); xfp->frac);
@ -3049,7 +3045,7 @@ static void store_op3(XFP *xfp)
mau_state.asr |= MAU_ASR_Z; mau_state.asr |= MAU_ASR_Z;
} }
} }
write_w(mau_state.dst, (uint32)sfp); write_w(mau_state.dst, (uint32)sfp, BUS_PER);
break; break;
case M_OP3_MEM_DOUBLE: case M_OP3_MEM_DOUBLE:
if (mau_state.ntnan) { if (mau_state.ntnan) {
@ -3074,18 +3070,18 @@ static void store_op3(XFP *xfp)
mau_state.asr |= MAU_ASR_Z; mau_state.asr |= MAU_ASR_Z;
} }
} }
write_w(mau_state.dst, (uint32)(dfp >> 32)); write_w(mau_state.dst, (uint32)(dfp >> 32), BUS_PER);
write_w(mau_state.dst + 4, (uint32)(dfp)); write_w(mau_state.dst + 4, (uint32)(dfp), BUS_PER);
break; break;
case M_OP3_MEM_TRIPLE: case M_OP3_MEM_TRIPLE:
if (mau_state.ntnan) { if (mau_state.ntnan) {
write_w(mau_state.dst, (uint32)(GEN_NONTRAPPING_NAN.sign_exp)); write_w(mau_state.dst, (uint32)(GEN_NONTRAPPING_NAN.sign_exp), BUS_PER);
write_w(mau_state.dst + 4, (uint32)(GEN_NONTRAPPING_NAN.frac >> 32)); write_w(mau_state.dst + 4, (uint32)(GEN_NONTRAPPING_NAN.frac >> 32), BUS_PER);
write_w(mau_state.dst + 8, (uint32)(GEN_NONTRAPPING_NAN.frac)); write_w(mau_state.dst + 8, (uint32)(GEN_NONTRAPPING_NAN.frac), BUS_PER);
} else { } else {
write_w(mau_state.dst, (uint32)(xfp->sign_exp)); write_w(mau_state.dst, (uint32)(xfp->sign_exp), BUS_PER);
write_w(mau_state.dst + 4, (uint32)(xfp->frac >> 32)); write_w(mau_state.dst + 4, (uint32)(xfp->frac >> 32), BUS_PER);
write_w(mau_state.dst + 8, (uint32)(xfp->frac)); write_w(mau_state.dst + 8, (uint32)(xfp->frac), BUS_PER);
} }
if (set_nz()) { if (set_nz()) {
if (XFP_SIGN(xfp)) { if (XFP_SIGN(xfp)) {
@ -3114,22 +3110,22 @@ static void mau_rdasr()
switch (mau_state.op3) { switch (mau_state.op3) {
/* Handled */ /* Handled */
case M_OP3_MEM_SINGLE: case M_OP3_MEM_SINGLE:
write_w(mau_state.dst, mau_state.asr); write_w(mau_state.dst, mau_state.asr, BUS_PER);
break; break;
case M_OP3_MEM_DOUBLE: case M_OP3_MEM_DOUBLE:
write_w(mau_state.dst, mau_state.asr); write_w(mau_state.dst, mau_state.asr, BUS_PER);
write_w(mau_state.dst + 4, mau_state.asr); write_w(mau_state.dst + 4, mau_state.asr, BUS_PER);
break; break;
case M_OP3_MEM_TRIPLE: case M_OP3_MEM_TRIPLE:
write_w(mau_state.dst, mau_state.asr); write_w(mau_state.dst, mau_state.asr, BUS_PER);
write_w(mau_state.dst + 4, mau_state.asr); write_w(mau_state.dst + 4, mau_state.asr, BUS_PER);
write_w(mau_state.dst + 8, mau_state.asr); write_w(mau_state.dst + 8, mau_state.asr, BUS_PER);
break; break;
/* Unhandled */ /* Unhandled */
default: default:
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [mau_rdasr] WARNING: Unhandled source: %02x\n", "[mau_rdasr] WARNING: Unhandled source: %02x\n",
R[NUM_PC], mau_state.op3); mau_state.op3);
break; break;
} }
} }
@ -3139,15 +3135,14 @@ static void mau_wrasr()
switch (mau_state.op1) { switch (mau_state.op1) {
/* Handled */ /* Handled */
case M_OP_MEM_SINGLE: case M_OP_MEM_SINGLE:
mau_state.asr = read_w(mau_state.src, ACC_AF); mau_state.asr = read_w(mau_state.src, ACC_AF, BUS_PER);
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [WRASR] Writing ASR with: %08x\n", "[WRASR] Writing ASR with: %08x\n",
R[NUM_PC], mau_state.asr); mau_state.asr);
break; break;
default: default:
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [mau_wrasr] WARNING: Unhandled source: %02x\n", "[mau_wrasr] WARNING: Unhandled source: %02x\n",
R[NUM_PC],
mau_state.op3); mau_state.op3);
break; break;
} }
@ -3206,8 +3201,8 @@ static void mau_ldr()
load_src_op(mau_state.op1, &xfp); load_src_op(mau_state.op1, &xfp);
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [LDR] Loading DR with %04x%016llx\n", "[LDR] Loading DR with %04x%016llx\n",
R[NUM_PC], xfp.sign_exp, xfp.frac); xfp.sign_exp, xfp.frac);
mau_state.dr.sign_exp = xfp.sign_exp; mau_state.dr.sign_exp = xfp.sign_exp;
mau_state.dr.frac = xfp.frac; mau_state.dr.frac = xfp.frac;
} }
@ -3244,17 +3239,17 @@ static void mau_erof()
return; return;
case M_OP3_MEM_SINGLE: case M_OP3_MEM_SINGLE:
sfp = xfp_to_sfp(&(mau_state.dr), MAU_RM); sfp = xfp_to_sfp(&(mau_state.dr), MAU_RM);
write_w(mau_state.dst, (uint32)sfp); write_w(mau_state.dst, (uint32)sfp, BUS_PER);
return; return;
case M_OP3_MEM_DOUBLE: case M_OP3_MEM_DOUBLE:
dfp = xfp_to_dfp(&(mau_state.dr), MAU_RM); dfp = xfp_to_dfp(&(mau_state.dr), MAU_RM);
write_w(mau_state.dst + 4, (uint32)(dfp >> 32)); write_w(mau_state.dst + 4, (uint32)(dfp >> 32), BUS_PER);
write_w(mau_state.dst, (uint32)(dfp)); write_w(mau_state.dst, (uint32)(dfp), BUS_PER);
return; return;
case M_OP3_MEM_TRIPLE: case M_OP3_MEM_TRIPLE:
write_w(mau_state.dst, (uint32)(mau_state.dr.sign_exp)); write_w(mau_state.dst, (uint32)(mau_state.dr.sign_exp), BUS_PER);
write_w(mau_state.dst + 4, (uint32)(mau_state.dr.frac >> 32)); write_w(mau_state.dst + 4, (uint32)(mau_state.dr.frac >> 32), BUS_PER);
write_w(mau_state.dst + 8, (uint32)(mau_state.dr.frac)); write_w(mau_state.dst + 8, (uint32)(mau_state.dr.frac), BUS_PER);
return; return;
default: default:
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
@ -3349,8 +3344,7 @@ static void mau_div()
load_src_op(mau_state.op1, &a); load_src_op(mau_state.op1, &a);
load_src_op(mau_state.op2, &b); load_src_op(mau_state.op2, &b);
sim_debug(TRACE_DBG, &mau_dev, sim_debug(TRACE_DBG, &mau_dev,
"[%08x] [DIV OP2/OP1] OP2=0x%04x%016llx OP1=0x%04x%016llx\n", "[DIV OP2/OP1] OP2=0x%04x%016llx OP1=0x%04x%016llx\n",
R[NUM_PC],
b.sign_exp, b.frac, b.sign_exp, b.frac,
a.sign_exp, a.frac); a.sign_exp, a.frac);
xfp_div(&b, &a, &result, MAU_RM); xfp_div(&b, &a, &result, MAU_RM);
@ -3414,13 +3408,13 @@ static void mau_itof()
mau_exc(MAU_ASR_IS, MAU_ASR_IM); mau_exc(MAU_ASR_IS, MAU_ASR_IM);
return; return;
case M_OP_MEM_SINGLE: case M_OP_MEM_SINGLE:
val = read_w(mau_state.src, ACC_AF); val = read_w(mau_state.src, ACC_AF, BUS_PER);
break; break;
case M_OP_MEM_DOUBLE: case M_OP_MEM_DOUBLE:
val = read_w(mau_state.src + 4, ACC_AF); val = read_w(mau_state.src + 4, ACC_AF, BUS_PER);
break; break;
case M_OP_MEM_TRIPLE: case M_OP_MEM_TRIPLE:
val = read_w(mau_state.src + 8, ACC_AF); val = read_w(mau_state.src + 8, ACC_AF, BUS_PER);
break; break;
default: default:
break; break;
@ -3593,5 +3587,9 @@ t_stat mau_broadcast(uint32 cmd, uint32 src, uint32 dst)
CONST char *mau_description(DEVICE *dptr) CONST char *mau_description(DEVICE *dptr)
{ {
return "WE32106"; #if defined(REV3)
return "WE 32106 MAU";
#else
return "WE 32206 MAU";
#endif
} }

View file

@ -1,6 +1,6 @@
/* 3b2_mau.h: Common CSR header /* 3b2_mau.h: WE32106 and WE32206 Math Accelerator Unit
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -26,12 +26,364 @@
not be used in advertising or otherwise to promote the sale, use or not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization other dealings in this Software without prior written authorization
from the author. from the author.
---------------------------------------------------------------------
This file is part of a simulation of the WE 32106 and WE 32206 Math
Acceleration Units. The WE 32106 and WE 32206 are IEEE-754
compabitle floating point hardware math accelerators that were
available as an optional component on the AT&T 3B2/310 and 3B2/400,
and as a standard component on the 3B2/500, 3B2/600, 3B2/700, and
3B2/1000.
Portions of this code are derived from the SoftFloat 2c library by
John R. Hauser. Functions derived from SoftFloat 2c are clearly
marked in the comments.
Legal Notice
============
SoftFloat was written by John R. Hauser. Release 2c of SoftFloat
was made possible in part by the International Computer Science
Institute, located at Suite 600, 1947 Center Street, Berkeley,
California 94704. Funding was partially provided by the National
Science Foundation under grant MIP-9311980. The original version
of this code was written as part of a project to build a
fixed-point vector processor in collaboration with the University
of California at Berkeley, overseen by Profs. Nelson Morgan and
John Wawrzynek.
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable
effort has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS
THAT WILL AT TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS
SOFTWARE IS RESTRICTED TO PERSONS AND ORGANIZATIONS WHO CAN AND
WILL TOLERATE ALL LOSSES, COSTS, OR OTHER PROBLEMS THEY INCUR DUE
TO THE SOFTWARE WITHOUT RECOMPENSE FROM JOHN HAUSER OR THE
INTERNATIONAL COMPUTER SCIENCE INSTITUTE, AND WHO FURTHERMORE
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER
SCIENCE INSTITUTE (possibly via similar legal notice) AGAINST ALL
LOSSES, COSTS, OR OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND
CLIENTS DUE TO THE SOFTWARE, OR INCURRED BY ANYONE DUE TO A
DERIVATIVE WORK THEY CREATE USING ANY PART OF THE SOFTWARE.
The following are expressly permitted, even for commercial
purposes:
(1) distribution of SoftFloat in whole or in part, as long as this
and other legal notices remain and are prominent, and provided also
that, for a partial distribution, prominent notice is given that it
is a subset of the original; and
(2) inclusion or use of SoftFloat in whole or in part in a
derivative work, provided that the use restrictions above are met
and the minimal documentation requirements stated in the source
code are satisfied.
---------------------------------------------------------------------
Data Types
==========
The WE32106 MAU stores values using IEEE-754 1985 types, plus a
non-standard Decimal type.
- Decimal Type - 18 BCD digits long. Each digit is 4 bits wide.
Sign is encoded in byte 0.
3322 2222 2222 1111 1111 1100 0000 0000
1098 7654 3210 9876 5432 1098 7654 3210
+-------------------+----+----+----+----+
| unused | D18| D17| D16| D15| High Word
+----+----+----+----+----+----+----+----+
| D14| D13| D12| D11| D10| D09| D08| D07| Middle Word
+----+----+----+----+----+----+----+----+
| D06| D05| D04| D03| D02| D01| D00|sign| Low Word
+----+----+----+----+----+----+----+----+
Sign: 0: Positive Infinity 10: Positive Number
1: Negative Infinity 11: Negative Number
2: Positive NaN 12: Positive Number
3: Negative NaN 13: Negative Number
4-9: Trapping NaN 14-15: Positive Number
- Extended Precision (80-bit) - exponent biased by 16383
3 322222222221111 1 111110000000000
1 098765432109876 5 432109876543210
+-----------------+-+---------------+
| unused |S| Exponent | High Word
+-+---------------+-+---------------+
|J| Fraction (high word) | Middle Word
+-+---------------------------------+
| Fraction (low word) | Low Word
+-----------------------------------+
- Double Precision (64-bit) - exponent biased by 1023
3 3222222222 211111111110000000000
1 0987654321 098765432109876543210
+-+----------+---------------------+
|S| Exponent | Fraction (high) | High Word
+-+----------+---------------------+
| Fraction (low) | Low Word
+----------------------------------+
- Single Precision (32-bit) - exponent biased by 127
3 32222222 22211111111110000000000
1 09876543 21098765432109876543210
+-+--------+-----------------------+
|S| Exp | Fraction |
+-+--------+-----------------------+
*/ */
#ifndef _3B2_MAU_H_ #ifndef _3B2_REV2_MAU_H_
#define _3B2_MAU_H_ #define _3B2_REV2_MAU_H_
/* TODO: Update when rev3 mau is implemented */ #include "sim_defs.h"
#include "3b2_rev2_mau.h"
#endif /* _3B2_MAU_H */ #define SRC_LEN_INVALID 0
#define SRC_LEN_SINGLE 1
#define SRC_LEN_DOUBLE 2
#define SRC_LEN_TRIPLE 3
#define MAU_ASR_RC_SHIFT 22
#define MAU_ASR_PR 0x20u /* Partial Remainder */
#define MAU_ASR_QS 0x40u /* Divide By Zero Sticky */
#define MAU_ASR_US 0x80u /* Underflow Sticky */
#define MAU_ASR_OS 0x100u /* Overflow Sticky */
#define MAU_ASR_IS 0x200u /* Invalid Operation Sticky */
#define MAU_ASR_PM 0x400u /* Inexact Mask */
#define MAU_ASR_QM 0x800u /* Divide by Zero Mask */
#define MAU_ASR_UM 0x1000u /* Underflow Mask */
#define MAU_ASR_OM 0x2000u /* Overflow Mask */
#define MAU_ASR_IM 0x4000u /* Invalid Operation Mask */
#define MAU_ASR_UO 0x10000u /* Unordered */
#define MAU_ASR_CSC 0x20000u /* Context Switch Control */
#define MAU_ASR_PS 0x40000u /* Inexact Sticky */
#define MAU_ASR_IO 0x80000u /* Integer Overflow */
#define MAU_ASR_Z 0x100000u /* Zero Flag */
#define MAU_ASR_N 0x200000u /* Negative Flag */
#define MAU_ASR_RC 0x400000u /* Round Control */
#define MAU_ASR_NTNC 0x1000000u /* Nontrapping NaN Control */
#define MAU_ASR_ECP 0x2000000u /* Exception Condition */
#define MAU_ASR_RA 0x80000000u /* Result Available */
#if defined(REV3)
#define MAU_ASR_FE 0x1u /* Feature Enable */
#define MAU_ASR_VER 0x2u /* Version */
#define MAU_ASR_UW 0x10u /* Unaligned Word */
#define MAU_ASR_WF 0x8000u /* Write Fault Indicator */
#define MAU_RC_RN 0 /* Round toward Nearest */
#define MAU_RC_RP 1 /* Round toward Plus Infin. */
#define MAU_RC_RM 2 /* Round toward Neg. Infin. */
#define MAU_RC_RZ 3 /* Round toward Zero */
#endif
#define SFP_SIGN(V) (((V) >> 31) & 1)
#define SFP_EXP(V) (((V) >> 23) & 0xff)
#define SFP_FRAC(V) ((V) & 0x7fffff)
#define DFP_SIGN(V) (((V) >> 63) & 1)
#define DFP_EXP(V) (((V) >> 52) & 0x7ff)
#define DFP_FRAC(V) ((V) & 0xfffffffffffffull)
#define XFP_SIGN(V) (((V)->sign_exp >> 15) & 1)
#define XFP_EXP(V) ((V)->sign_exp & 0x7fff)
#define XFP_FRAC(V) ((V)->frac)
#define XFP_IS_NORMAL(V) ((V)->frac & 0x8000000000000000ull)
#define DEFAULT_XFP_NAN_SIGN_EXP 0xffff
#define DEFAULT_XFP_NAN_FRAC 0xc000000000000000ull
#define SFP_IS_TRAPPING_NAN(V) (((((V) >> 22) & 0x1ff) == 0x1fe) && \
((V) & 0x3fffff))
#define DFP_IS_TRAPPING_NAN(V) (((((V) >> 51) & 0xfff) == 0xffe) && \
((V) & 0x7ffffffffffffull))
#define XFP_IS_NAN(V) ((((V)->sign_exp & 0x7fff) == 0x7fff) && \
(t_uint64)((V)->frac << 1))
#define XFP_IS_TRAPPING_NAN(V) ((((V)->sign_exp) & 0x7fff) && \
((((V)->frac) & ~(0x4000000000000000ull)) << 1) && \
(((V)->frac) == ((V)->frac & ~(0x4000000000000000ull))))
#define PACK_DFP(SIGN,EXP,FRAC) ((((t_uint64)(SIGN))<<63) + \
(((t_uint64)(EXP))<<52) + \
((t_uint64)(FRAC)))
#define PACK_SFP(SIGN,EXP,FRAC) (((uint32)(SIGN)<<31) + \
((uint32)(EXP)<<23) + \
((uint32)(FRAC)))
#define PACK_XFP(SIGN,EXP,FRAC,V) do { \
(V)->frac = (FRAC); \
(V)->sign_exp = ((uint16)(SIGN) << 15) + (EXP); \
(V)->s = FALSE; \
} while (0)
#define PACK_XFP_S(SIGN,EXP,FRAC,S,V) do { \
(V)->frac = (FRAC); \
(V)->sign_exp = ((uint16)(SIGN) << 15) + (EXP); \
(V)->s = (S) != 0; \
} while (0)
#define MAU_RM ((RM)((mau_state.asr >> 22) & 3))
typedef enum {
M_ADD = 0x02,
M_SUB = 0x03,
M_DIV = 0x04,
M_REM = 0x05,
M_MUL = 0x06,
M_MOVE = 0x07,
M_RDASR = 0x08,
M_WRASR = 0x09,
M_CMP = 0x0a,
M_CMPE = 0x0b,
M_ABS = 0x0c,
M_SQRT = 0x0d,
M_RTOI = 0x0e,
M_FTOI = 0x0f,
M_ITOF = 0x10,
M_DTOF = 0x11,
M_FTOD = 0x12,
M_NOP = 0x13,
M_EROF = 0x14,
M_NEG = 0x17,
M_LDR = 0x18,
M_CMPS = 0x1a,
M_CMPES = 0x1b
} mau_opcodes;
typedef enum {
M_OP3_F0_SINGLE,
M_OP3_F1_SINGLE,
M_OP3_F2_SINGLE,
M_OP3_F3_SINGLE,
M_OP3_F0_DOUBLE,
M_OP3_F1_DOUBLE,
M_OP3_F2_DOUBLE,
M_OP3_F3_DOUBLE,
M_OP3_F0_TRIPLE,
M_OP3_F1_TRIPLE,
M_OP3_F2_TRIPLE,
M_OP3_F3_TRIPLE,
M_OP3_MEM_SINGLE,
M_OP3_MEM_DOUBLE,
M_OP3_MEM_TRIPLE,
M_OP3_NONE
} op3_spec;
/* Specifier bytes for Operands 1 and 2 */
typedef enum {
M_OP_F0,
M_OP_F1,
M_OP_F2,
M_OP_F3,
M_OP_MEM_SINGLE,
M_OP_MEM_DOUBLE,
M_OP_MEM_TRIPLE,
M_OP_NONE
} op_spec;
/*
* 128-bit value
*/
typedef struct {
t_uint64 low;
t_uint64 high;
} t_mau_128;
/*
* Not-a-Number Type
*/
typedef struct {
t_bool sign;
t_uint64 high;
t_uint64 low;
} T_NAN;
/*
* Extended Precision (80 bits).
*
* Note that an undocumented feature of the WE32106 requires the use
* of uint32 rather than uint16 for the sign and exponent components
* of the struct. Although bits 80-95 are "unused", several
* diagnostics actually expect these bits to be moved and preserved on
* word transfers. They are ignored and discarded by math routines,
* however.
*
* The 's' field holds the Sticky bit used by rounding.
*/
typedef struct {
uint32 sign_exp; /* Sign and Exponent */
t_uint64 frac; /* Fraction/Significand/Mantissa */
t_bool s; /* Sticky bit */
} XFP;
typedef struct {
uint32 h;
t_uint64 l;
} DEC;
/*
* Supported rounding modes.
*/
typedef enum {
ROUND_NEAREST,
ROUND_PLUS_INF,
ROUND_MINUS_INF,
ROUND_ZERO
} RM;
/*
* Double Precision (64 bits)
*/
typedef t_uint64 DFP;
/*
* Single Precision (32 bits)
*/
typedef uint32 SFP;
/*
* MAU state
*/
typedef struct {
uint32 cmd;
/* Exception */
uint32 exception;
/* Status register */
uint32 asr;
t_bool trapping_nan;
/* Generate a Non-Trapping NaN */
t_bool ntnan;
/* Source (from broadcast) */
uint32 src;
/* Destination (from broadcast) */
uint32 dst;
uint8 opcode;
uint8 op1;
uint8 op2;
uint8 op3;
/* Data Register */
XFP dr;
/* Operand Registers */
XFP f0;
XFP f1;
XFP f2;
XFP f3;
} MAU_STATE;
t_stat mau_reset(DEVICE *dptr);
t_stat mau_attach(UNIT *uptr, CONST char *cptr);
t_stat mau_detach(UNIT *uptr);
t_stat mau_broadcast(uint32 cmd, uint32 src, uint32 dst);
CONST char *mau_description(DEVICE *dptr);
t_stat mau_broadcast(uint32 cmd, uint32 src, uint32 dst);
#endif /* _3B2_REV2_MAU_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_mem.c: AT&T 3B2 memory access routines /* 3b2_mem.c: Memory Map Access Routines
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -34,82 +34,111 @@
#include "3b2_csr.h" #include "3b2_csr.h"
#include "3b2_io.h" #include "3b2_io.h"
#include "3b2_mmu.h" #include "3b2_mmu.h"
#include "3b2_stddev.h"
#include "3b2_dmac.h"
t_bool addr_is_rom(uint32 pa) #if defined(REV3)
{ static uint32 ecc_addr; /* ECC address */
return (pa < rom_size); static t_bool ecc_err; /* ECC multi-bit error */
} #endif
t_bool addr_is_mem(uint32 pa) /*
{ * ECC is simulated just enough to pass diagnostics, and no more.
return (pa >= PHYS_MEM_BASE && *
pa < (PHYS_MEM_BASE + MEM_SIZE)); * Checking and setting of ECC syndrome bits is a no-op for Rev 2.
} */
static SIM_INLINE void check_ecc(uint32 pa, t_bool write, uint8 src)
t_bool addr_is_io(uint32 pa)
{ {
#if defined(REV3) #if defined(REV3)
return ((pa >= IO_BOTTOM && pa < IO_TOP) || /* Force ECC Syndrome mode enables a diagnostic mode on the AM2960
(pa >= CIO_BOTTOM && pa < CIO_TOP) || data correction ICs */
(pa >= VCACHE_BOTTOM && pa < VCACHE_TOP) || if (write && !CSR(CSRFECC)) {
(pa >= BUB_BOTTOM && pa < BUB_TOP)); sim_debug(EXECUTE_MSG, &mmu_dev,
#else "ECC Error on Write. pa=%08x\n",
return ((pa >= IO_BOTTOM && pa < IO_TOP) || pa);
(pa >= CIO_BOTTOM && pa < CIO_TOP)); ecc_addr = pa;
ecc_err = TRUE;
} else if (ecc_err && !write && pa == ecc_addr) {
sim_debug(EXECUTE_MSG, &mmu_dev,
"ECC Error detected on Read. pa=%08x psw=%08x cur_ipl=%d csr=%08x\n",
pa, R[NUM_PSW], PSW_CUR_IPL, csr_data);
flt[0] = ecc_addr & 0x3ffff;
flt[1] = MA_CPU_IO|MA_CPU_BU;
ecc_err = FALSE;
CSRBIT(CSRFRF, TRUE); /* Fault registers frozen */
CSRBIT(CSRMBERR, TRUE); /* Multi-bit error */
CPU_SET_INT(INT_MBERR);
/* Only abort if CPU is doing the read */
if (src == BUS_CPU) {
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
}
}
#endif #endif
} }
/* Read Word (Physical Address) */ /* Read Word (Physical Address) */
uint32 pread_w(uint32 pa) uint32 pread_w(uint32 pa, uint8 src)
{ {
uint32 *m; uint8 *m;
uint32 index; uint32 index = 0;
#if defined(REV3)
if ((pa & 3) && (R[NUM_PSW] & PSW_EA_MASK) == 0) {
#else
if (pa & 3) { if (pa & 3) {
#endif
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] Cannot read physical address. ALIGNMENT ISSUE: %08x\n", "Cannot read physical address. ALIGNMENT ISSUE: %08x\n",
R[NUM_PC], pa); pa);
CSRBIT(CSRALGN, TRUE); CSRBIT(CSRALGN, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
} }
if (addr_is_io(pa)) { if (IS_IO(pa)) {
return io_read(pa, 32); return io_read(pa, 32);
} }
if (addr_is_rom(pa)) { if (IS_ROM(pa)) {
m = ROM; m = ROM;
index = pa >> 2; index = pa;
} else if (addr_is_mem(pa)) { } else if (IS_RAM(pa)) {
check_ecc(pa, FALSE, src);
m = RAM; m = RAM;
index = (pa - PHYS_MEM_BASE) >> 2; index = (pa - PHYS_MEM_BASE);
} else { } else {
return 0; return 0;
} }
return m[index]; return ATOW(m, index);
} }
/* /*
* Write Word (Physical Address) * Write Word (Physical Address)
*/ */
void pwrite_w(uint32 pa, uint32 val) void pwrite_w(uint32 pa, uint32 val, uint8 src)
{ {
uint32 index;
if (pa & 3) { if (pa & 3) {
sim_debug(WRITE_MSG, &mmu_dev, sim_debug(WRITE_MSG, &mmu_dev,
"[%08x] Cannot write physical address. ALIGNMENT ISSUE: %08x\n", "Cannot write physical address. ALIGNMENT ISSUE: %08x\n",
R[NUM_PC], pa); pa);
CSRBIT(CSRALGN, TRUE); CSRBIT(CSRALGN, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
} }
if (addr_is_io(pa)) { if (IS_IO(pa)) {
io_write(pa, val, 32); io_write(pa, val, 32);
return; return;
} }
if (addr_is_mem(pa)) { if (IS_RAM(pa)) {
RAM[(pa - PHYS_MEM_BASE) >> 2] = val; check_ecc(pa, TRUE, src);
index = pa - PHYS_MEM_BASE;
RAM[index] = (val >> 24) & 0xff;
RAM[index + 1] = (val >> 16) & 0xff;
RAM[index + 2] = (val >> 8) & 0xff;
RAM[index + 3] = val & 0xff;
return; return;
} }
} }
@ -117,154 +146,147 @@ void pwrite_w(uint32 pa, uint32 val)
/* /*
* Read Halfword (Physical Address) * Read Halfword (Physical Address)
*/ */
uint16 pread_h(uint32 pa) uint16 pread_h(uint32 pa, uint8 src)
{ {
uint32 *m; uint8 *m;
uint32 index; uint32 index;
if (pa & 1) { if (pa & 1) {
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] Cannot read physical address. ALIGNMENT ISSUE %08x\n", "Cannot read physical address. ALIGNMENT ISSUE %08x\n",
R[NUM_PC], pa); pa);
CSRBIT(CSRALGN, TRUE); CSRBIT(CSRALGN, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
} }
if (addr_is_io(pa)) { if (IS_IO(pa)) {
return (uint16) io_read(pa, 16); return (uint16) io_read(pa, 16);
} }
if (addr_is_rom(pa)) { if (IS_ROM(pa)) {
m = ROM; m = ROM;
index = pa >> 2; index = pa;
} else if (addr_is_mem(pa)) { } else if (IS_RAM(pa)) {
check_ecc(pa, FALSE, src);
m = RAM; m = RAM;
index = (pa - PHYS_MEM_BASE) >> 2; index = pa - PHYS_MEM_BASE;
} else { } else {
return 0; return 0;
} }
if (pa & 2) { return ATOH(m, index);
return m[index] & HALF_MASK;
} else {
return (m[index] >> 16) & HALF_MASK;
}
} }
/* /*
* Write Halfword (Physical Address) * Write Halfword (Physical Address)
*/ */
void pwrite_h(uint32 pa, uint16 val) void pwrite_h(uint32 pa, uint16 val, uint8 src)
{ {
uint32 *m;
uint32 index; uint32 index;
uint32 wval = (uint32)val;
#if defined(REV3)
if ((pa & 1) && (R[NUM_PSW] & PSW_EA_MASK) == 0) {
#else
if (pa & 1) { if (pa & 1) {
#endif
sim_debug(WRITE_MSG, &mmu_dev, sim_debug(WRITE_MSG, &mmu_dev,
"[%08x] Cannot write physical address %08x, ALIGNMENT ISSUE\n", "Cannot write physical address %08x, ALIGNMENT ISSUE\n",
R[NUM_PC], pa); pa);
CSRBIT(CSRALGN, TRUE); CSRBIT(CSRALGN, TRUE);
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
} }
if (addr_is_io(pa)) { if (IS_IO(pa)) {
io_write(pa, val, 16); io_write(pa, val, 16);
return; return;
} }
if (addr_is_mem(pa)) { if (IS_RAM(pa)) {
m = RAM; check_ecc(pa, TRUE, src);
index = (pa - PHYS_MEM_BASE) >> 2; index = pa - PHYS_MEM_BASE;
} else { RAM[index] = (val >> 8) & 0xff;
RAM[index + 1] = val & 0xff;
return; return;
} }
if (pa & 2) {
m[index] = (m[index] & ~HALF_MASK) | wval;
} else {
m[index] = (m[index] & HALF_MASK) | (wval << 16);
}
} }
/* /*
* Read Byte (Physical Address) * Read Byte (Physical Address)
*/ */
uint8 pread_b(uint32 pa) uint8 pread_b(uint32 pa, uint8 src)
{ {
uint32 data; if (IS_IO(pa)) {
int32 sc = (~(pa & 3) << 3) & 0x1f;
if (addr_is_io(pa)) {
return (uint8)(io_read(pa, 8)); return (uint8)(io_read(pa, 8));
} }
if (addr_is_rom(pa)) { if (IS_ROM(pa)) {
data = ROM[pa >> 2]; return ROM[pa];
} else if (addr_is_mem(pa)) { } else if (IS_RAM(pa)) {
data = RAM[(pa - PHYS_MEM_BASE) >> 2]; check_ecc(pa, FALSE, src);
return RAM[pa - PHYS_MEM_BASE];
} else { } else {
return 0; return 0;
} }
return (data >> sc) & BYTE_MASK;
} }
/* Write Byte (Physical Address) */ /* Write Byte (Physical Address) */
void pwrite_b(uint32 pa, uint8 val) void pwrite_b(uint32 pa, uint8 val, uint8 src)
{ {
uint32 *m; uint32 index;
int32 index;
int32 sc = (~(pa & 3) << 3) & 0x1f;
uint32 mask = 0xffu << sc;
if (addr_is_io(pa)) { if (IS_IO(pa)) {
io_write(pa, val, 8); io_write(pa, val, 8);
return; return;
} }
if (addr_is_mem(pa)) { if (IS_RAM(pa)) {
m = RAM; check_ecc(pa, TRUE, src);
index = (pa - PHYS_MEM_BASE) >> 2; index = pa - PHYS_MEM_BASE;
m[index] = (m[index] & ~mask) | (uint32) (val << sc); RAM[index] = val;
return; return;
} }
} }
/* Write to ROM (used by ROM load) */
void pwrite_b_rom(uint32 pa, uint8 val) {
if (IS_ROM(pa)) {
ROM[pa] = val;
}
}
/* Read Byte (Virtual Address) */ /* Read Byte (Virtual Address) */
uint8 read_b(uint32 va, uint8 r_acc) uint8 read_b(uint32 va, uint8 r_acc, uint8 src)
{ {
return pread_b(mmu_xlate_addr(va, r_acc)); return pread_b(mmu_xlate_addr(va, r_acc), src);
} }
/* Write Byte (Virtual Address) */ /* Write Byte (Virtual Address) */
void write_b(uint32 va, uint8 val) void write_b(uint32 va, uint8 val, uint8 src)
{ {
pwrite_b(mmu_xlate_addr(va, ACC_W), val); pwrite_b(mmu_xlate_addr(va, ACC_W), val, src);
} }
/* Read Halfword (Virtual Address) */ /* Read Halfword (Virtual Address) */
uint16 read_h(uint32 va, uint8 r_acc) uint16 read_h(uint32 va, uint8 r_acc, uint8 src)
{ {
return pread_h(mmu_xlate_addr(va, r_acc)); return pread_h(mmu_xlate_addr(va, r_acc), src);
} }
/* Write Halfword (Virtual Address) */ /* Write Halfword (Virtual Address) */
void write_h(uint32 va, uint16 val) void write_h(uint32 va, uint16 val, uint8 src)
{ {
pwrite_h(mmu_xlate_addr(va, ACC_W), val); pwrite_h(mmu_xlate_addr(va, ACC_W), val, src);
} }
/* Read Word (Virtual Address) */ /* Read Word (Virtual Address) */
uint32 read_w(uint32 va, uint8 r_acc) uint32 read_w(uint32 va, uint8 r_acc, uint8 src)
{ {
return pread_w(mmu_xlate_addr(va, r_acc)); return pread_w(mmu_xlate_addr(va, r_acc), src);
} }
/* Write Word (Virtual Address) */ /* Write Word (Virtual Address) */
void write_w(uint32 va, uint32 val) void write_w(uint32 va, uint32 val, uint8 src)
{ {
pwrite_w(mmu_xlate_addr(va, ACC_W), val); pwrite_w(mmu_xlate_addr(va, ACC_W), val, src);
} }
t_stat read_operand(uint32 va, uint8 *val) t_stat read_operand(uint32 va, uint8 *val)
@ -275,7 +297,7 @@ t_stat read_operand(uint32 va, uint8 *val)
succ = mmu_decode_va(va, ACC_IF, TRUE, &pa); succ = mmu_decode_va(va, ACC_IF, TRUE, &pa);
if (succ == SCPE_OK) { if (succ == SCPE_OK) {
*val = pread_b(pa); *val = pread_b(pa, BUS_CPU);
} else { } else {
*val = 0; *val = 0;
} }
@ -291,8 +313,8 @@ t_stat examine(uint32 va, uint8 *val)
succ = mmu_decode_va(va, 0, FALSE, &pa); succ = mmu_decode_va(va, 0, FALSE, &pa);
if (succ == SCPE_OK) { if (succ == SCPE_OK) {
if (addr_is_rom(pa) || addr_is_mem(pa)) { if (IS_ROM(pa) || IS_RAM(pa)) {
*val = pread_b(pa); *val = pread_b(pa, BUS_CPU);
return SCPE_OK; return SCPE_OK;
} else { } else {
*val = 0; *val = 0;
@ -312,8 +334,8 @@ t_stat deposit(uint32 va, uint8 val)
succ = mmu_decode_va(va, 0, FALSE, &pa); succ = mmu_decode_va(va, 0, FALSE, &pa);
if (succ == SCPE_OK) { if (succ == SCPE_OK) {
if (addr_is_mem(pa)) { if (IS_RAM(pa)) {
pwrite_b(pa, val); pwrite_b(pa, val, BUS_CPU);
return SCPE_OK; return SCPE_OK;
} else { } else {
return SCPE_NXM; return SCPE_NXM;

View file

@ -1,6 +1,6 @@
/* 3b2_mem.h: AT&T 3B2 3B2 memory access routines /* 3b2_mem.h: Memory Map Access Routines
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -33,30 +33,47 @@
#include "3b2_defs.h" #include "3b2_defs.h"
uint32 pread_w(uint32 pa); #define IS_ROM(PA) ((PA) < ROM_SIZE)
void pwrite_w(uint32 pa, uint32 val); #define IS_RAM(PA) (((PA) >= PHYS_MEM_BASE) && ((PA) < (PHYS_MEM_BASE + MEM_SIZE)))
uint8 pread_b(uint32 pa); #if defined(REV3)
void pwrite_b(uint32 pa, uint8 val); #define IS_IO(PA) (((PA >= IO_BOTTOM) && (PA < IO_TOP)) || \
uint16 pread_h(uint32 pa); ((PA >= CIO_BOTTOM) && (PA < CIO_TOP)) || \
void pwrite_h(uint32 pa, uint16 val); ((PA >= VCACHE_BOTTOM) && (PA < VCACHE_TOP)) || \
((PA >= BUB_BOTTOM) && (PA < BUB_TOP)))
#else
#define IS_IO(PA) (((PA >= IO_BOTTOM) && (PA < IO_TOP)) || \
((PA >= CIO_BOTTOM) && (PA < CIO_TOP)))
#endif
uint8 read_b(uint32 va, uint8 r_acc); #define MA_BUB3 0x100 /* BUBUS slot 3 master on fault */
uint16 read_h(uint32 va, uint8 r_acc); #define MA_BUB2 0x200 /* BUBUS slot 2 master on fault */
uint32 read_w(uint32 va, uint8 r_acc); #define MA_BUB1 0x400 /* BUBUS slot 1 master on fault */
void write_b(uint32 va, uint8 val); #define MA_CPU_BU 0x2000 /* CPU access BUBUS peripheral */
void write_h(uint32 va, uint16 val); #define MA_BUB0 0x4000 /* BUBUS slot 0 master on fault */
void write_w(uint32 va, uint32 val); #define MA_CPU_IO 0x8000 /* CPU accessing I/O peripheral */
#define MA_IO_NLY 0x10000 /* IO Bus Master on fault */
#define MA_IO_BM 0x80000 /* IO Bus Master or BUBUS was master on fault */
#define BUS_PER 0 /* Read or Write is from peripheral */
#define BUS_CPU 1 /* Read or Write is from CPU */
uint32 pread_w(uint32 pa, uint8 src);
void pwrite_w(uint32 pa, uint32 val, uint8 src);
uint8 pread_b(uint32 pa, uint8 src);
void pwrite_b(uint32 pa, uint8 val, uint8 src);
void pwrite_b_rom(uint32 pa, uint8 val);
uint16 pread_h(uint32 pa, uint8 src);
void pwrite_h(uint32 pa, uint16 val, uint8 src);
uint8 read_b(uint32 va, uint8 r_acc, uint8 src);
uint16 read_h(uint32 va, uint8 r_acc, uint8 src);
uint32 read_w(uint32 va, uint8 r_acc, uint8 src);
void write_b(uint32 va, uint8 val, uint8 src);
void write_h(uint32 va, uint16 val, uint8 src);
void write_w(uint32 va, uint32 val, uint8 src);
t_stat read_operand(uint32 va, uint8 *val); t_stat read_operand(uint32 va, uint8 *val);
t_stat examine(uint32 va, uint8 *val); t_stat examine(uint32 va, uint8 *val);
t_stat deposit(uint32 va, uint8 val); t_stat deposit(uint32 va, uint8 val);
t_bool addr_is_rom(uint32 pa);
t_bool addr_is_mem(uint32 pa);
t_bool addr_is_io(uint32 pa);
t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa);
void mmu_enable();
void mmu_disable();
#endif /* _3B2_MEM_H_ */ #endif /* _3B2_MEM_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_mmu.h: Common MMU header /* 3b2_mmu.h: Common MMU header
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation

View file

@ -1,6 +1,6 @@
/* 3b2_ni.c: AT&T 3B2 Model 400 "NI" feature card /* 3b2_ni.c: CM195A Network Interface CIO Card
Copyright (c) 2018, Seth J. Morabito Copyright (c) 2018-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -97,26 +97,23 @@
#include "3b2_io.h" #include "3b2_io.h"
#include "3b2_mem.h" #include "3b2_mem.h"
#include "3b2_timer.h" #include "3b2_stddev.h"
/* State container for the card */ /* State container for the card */
NI_STATE ni; NI_STATE ni;
t_bool ni_conf = FALSE;
/* Static Function Declarations */ /* Static Function Declarations */
static void dump_packet(const char *direction, ETH_PACK *pkt); static void dump_packet(const char *direction, ETH_PACK *pkt);
static void ni_enable(); static void ni_enable();
static void ni_disable(); static void ni_disable();
static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp); static void ni_cmd(uint8 slot, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp);
static t_stat ni_show_queue_common(FILE *st, UNIT *uptr, int32 val,
CONST void *desc, t_bool rq);
static t_stat ni_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat ni_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
/* /*
* When the NI card is pumped, its CRC depends on what slot it is * A list of pumped code CRCs that will cause Force Function Call to
* installed in and what version of driver has been installed. * respond with "Test Passed". Must be null-terminated.
*/ */
#define NI_DIAG_CRCS_LEN 7
static const uint32 NI_DIAG_CRCS[] = { static const uint32 NI_DIAG_CRCS[] = {
0x795268a4, 0x795268a4,
0xfab1057c, 0xfab1057c,
@ -125,6 +122,23 @@ static const uint32 NI_DIAG_CRCS[] = {
0x267b19a0, 0x267b19a0,
0x123f36c0, 0x123f36c0,
0xc04ca0ab, 0xc04ca0ab,
0x96d0506e, /* Rev 3 NI, SVR 3.2.3 */
0x9d3fafde, /* Rev 3 NI, SVR 3.2.3 */
0,
};
/*
* A list of pumped code CRCs that will cause the sysgen routine to
* respond with a full completion request instead of an express
* completion request. Must be null-terminated.
*/
static const uint32 NI_PUMP_CRCS[] = {
0xfab1057c, /* Rev 2 NI, SVR 3.x */
0xf6744bed, /* Rev 2 NI, SVR 3.x */
0x96d0506e, /* Rev 3 NI, SVR 3.2.3 */
0x9d3fafde, /* Rev 3 NI, SVR 3.2.3 */
0x3553230a, /* Rev 3 NI, SVR 3.2.3 */
0,
}; };
/* /*
@ -151,10 +165,6 @@ MTAB ni_mod[] = {
&ni_set_stats, &ni_show_stats, NULL, "Display or reset statistics" }, &ni_set_stats, &ni_show_stats, NULL, "Display or reset statistics" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "POLL", NULL, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "POLL", NULL,
NULL, &ni_show_poll, NULL, "Display the current polling mode" }, NULL, &ni_show_poll, NULL, "Display the current polling mode" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "RQUEUE=n", NULL,
NULL, &ni_show_rqueue, NULL, "Display Request Queue for card n" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "CQUEUE=n", NULL,
NULL, &ni_show_cqueue, NULL, "Display Completion Queue for card n" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx",
&ni_setmac, &ni_showmac, NULL, "MAC address" }, &ni_setmac, &ni_showmac, NULL, "MAC address" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "FILTERS", NULL, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "FILTERS", NULL,
@ -291,10 +301,10 @@ static void ni_disable()
sim_cancel(rq_unit); sim_cancel(rq_unit);
sim_cancel(cio_unit); sim_cancel(cio_unit);
sim_cancel(sanity_unit); sim_cancel(sanity_unit);
CIO_CLR_INT(ni.cid); CIO_CLR_INT(ni.slot);
} }
static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp) static void ni_cmd(uint8 slot, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp)
{ {
int i, j; int i, j;
int32 delay; int32 delay;
@ -310,14 +320,14 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
centry.subdevice = rentry->subdevice; centry.subdevice = rentry->subdevice;
centry.address = rentry->address; centry.address = rentry->address;
cio[cid].op = rentry->opcode; cio[slot].op = rentry->opcode;
delay = NI_INT_DELAY; delay = NI_INT_DELAY;
switch(rentry->opcode) { switch(rentry->opcode) {
case CIO_DLM: case CIO_DLM:
for (i = 0; i < rentry->byte_count; i++) { for (i = 0; i < rentry->byte_count; i++) {
ni.crc = cio_crc32_shift(ni.crc, pread_b(rentry->address + i)); ni.crc = cio_crc32_shift(ni.crc, pread_b(rentry->address + i, BUS_PER));
} }
centry.address = rentry->address + rentry->byte_count; centry.address = rentry->address + rentry->byte_count;
@ -328,9 +338,9 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
centry.address, centry.subdevice, ni.crc); centry.address, centry.subdevice, ni.crc);
if (is_exp) { if (is_exp) {
cio_cexpress(cid, NIQESIZE, &centry, app_data); cio_cexpress(slot, NIQESIZE, &centry, app_data);
} else { } else {
cio_cqueue(cid, CIO_STAT, NIQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, NIQESIZE, &centry, app_data);
} }
break; break;
@ -342,13 +352,13 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
/* If the currently running program is a diagnostics program, /* If the currently running program is a diagnostics program,
* we are expected to write results into memory at address * we are expected to write results into memory at address
* 0x200f000 */ * 0x200f000 */
for (i = 0; i < NI_DIAG_CRCS_LEN; i++) { for (i = 0; NI_DIAG_CRCS[i] != 0; i++) {
if (ni.crc == NI_DIAG_CRCS[i]) { if (ni.crc == NI_DIAG_CRCS[i]) {
pwrite_h(0x200f000, 0x1); /* Test success */ pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */
pwrite_h(0x200f002, 0x0); /* Test Number */ pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */
pwrite_h(0x200f004, 0x0); /* Actual */ pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */
pwrite_h(0x200f006, 0x0); /* Expected */ pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */
pwrite_b(0x200f008, 0x1); /* Success flag again */ pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */
break; break;
} }
} }
@ -362,12 +372,12 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
* in the right state. */ * in the right state. */
ni_disable(); ni_disable();
cio[cid].sysgen_s = 0; cio[slot].sysgen_s = 0;
if (cio[cid].ivec == 0 || cio[cid].ivec == 3) { if (cio[slot].ivec == 0 || cio[slot].ivec == 3) {
cio_cexpress(cid, NIQESIZE, &centry, app_data); cio_cexpress(slot, NIQESIZE, &centry, app_data);
} else { } else {
cio_cqueue(cid, CIO_STAT, NIQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, NIQESIZE, &centry, app_data);
} }
break; break;
@ -378,12 +388,12 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
/* The system wants us to write sub-device structures at the /* The system wants us to write sub-device structures at the
* supplied address */ * supplied address */
pwrite_h(rentry->address, 0x0); pwrite_h(rentry->address, 0x0, BUS_PER);
if (is_exp) { if (is_exp) {
cio_cexpress(cid, NIQESIZE, &centry, app_data); cio_cexpress(slot, NIQESIZE, &centry, app_data);
} else { } else {
cio_cqueue(cid, CIO_STAT, NIQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, NIQESIZE, &centry, app_data);
} }
break; break;
@ -393,7 +403,7 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
/* Try to read the mac from memory */ /* Try to read the mac from memory */
for (i = 0; i < MAC_SIZE_BYTES; i++) { for (i = 0; i < MAC_SIZE_BYTES; i++) {
ni.mac_bytes[i] = pread_b(rentry->address + i); ni.mac_bytes[i] = pread_b(rentry->address + i, BUS_PER);
} }
snprintf(ni.mac_str, MAC_SIZE_CHARS, "%02x:%02x:%02x:%02x:%02x:%02x", snprintf(ni.mac_str, MAC_SIZE_CHARS, "%02x:%02x:%02x:%02x:%02x:%02x",
@ -406,7 +416,7 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
status = ni_setmac(ni_dev.units, 0, ni.mac_str, NULL); status = ni_setmac(ni_dev.units, 0, ni.mac_str, NULL);
cio_cqueue(cid, CIO_STAT, NIQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, NIQESIZE, &centry, app_data);
break; break;
case NI_TURNOFF: case NI_TURNOFF:
@ -415,7 +425,7 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
ni_disable(); ni_disable();
cio_cqueue(cid, CIO_STAT, NIQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, NIQESIZE, &centry, app_data);
break; break;
case NI_TURNON: case NI_TURNON:
@ -424,14 +434,14 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
ni_enable(); ni_enable();
cio_cqueue(cid, CIO_STAT, NIQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, NIQESIZE, &centry, app_data);
break; break;
case NI_STATS: case NI_STATS:
sim_debug(DBG_TRACE, &ni_dev, sim_debug(DBG_TRACE, &ni_dev,
"[ni_cmd] NI STATS Operation\n"); "[ni_cmd] NI STATS Operation\n");
cio_cqueue(cid, CIO_STAT, NIQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, NIQESIZE, &centry, app_data);
break; break;
case NI_SEND: case NI_SEND:
@ -471,25 +481,25 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
ni.wr_buf.oversize = NULL; ni.wr_buf.oversize = NULL;
/* Read the size of the header */ /* Read the size of the header */
hdrsize = pread_h(rentry->address + EIG_TABLE_SIZE); hdrsize = pread_h(rentry->address + EIG_TABLE_SIZE, BUS_PER);
/* Read out the packet frame */ /* Read out the packet frame */
for (i = 0; i < rentry->byte_count; i++) { for (i = 0; i < rentry->byte_count; i++) {
ni.wr_buf.msg[i] = pread_b(rentry->address + PKT_START_OFFSET + i); ni.wr_buf.msg[i] = pread_b(rentry->address + PKT_START_OFFSET + i, BUS_PER);
} }
/* Get a pointer to the buffer containing the protocol data */ /* Get a pointer to the buffer containing the protocol data */
prot_info_offset = 0; prot_info_offset = 0;
i = 0; i = 0;
do { do {
ni.prot.addr = pread_w(rentry->address + prot_info_offset); ni.prot.addr = pread_w(rentry->address + prot_info_offset, BUS_PER);
ni.prot.size = pread_h(rentry->address + prot_info_offset + 4); ni.prot.size = pread_h(rentry->address + prot_info_offset + 4, BUS_PER);
ni.prot.last = pread_h(rentry->address + prot_info_offset + 6); ni.prot.last = pread_h(rentry->address + prot_info_offset + 6, BUS_PER);
prot_info_offset += 8; prot_info_offset += 8;
/* Fill in the frame from this buffer */ /* Fill in the frame from this buffer */
for (j=0; j < ni.prot.size; i++, j++) { for (j=0; j < ni.prot.size; i++, j++) {
ni.wr_buf.msg[hdrsize + i] = pread_b(ni.prot.addr + j); ni.wr_buf.msg[hdrsize + i] = pread_b(ni.prot.addr + j, BUS_PER);
} }
} while (!ni.prot.last); } while (!ni.prot.last);
@ -521,7 +531,7 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
centry.byte_count <<= 8; centry.byte_count <<= 8;
} }
cio_cqueue(cid, CIO_STAT, NIQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, NIQESIZE, &centry, app_data);
delay = 0; delay = 0;
@ -531,7 +541,7 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
"[ni_cmd] Opcode %d Not Handled Yet\n", "[ni_cmd] Opcode %d Not Handled Yet\n",
rentry->opcode); rentry->opcode);
cio_cqueue(cid, CIO_STAT, NIQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, NIQESIZE, &centry, app_data);
break; break;
} }
@ -595,8 +605,10 @@ t_stat ni_show_filters(FILE* st, UNIT* uptr, int32 val, CONST void* desc)
return SCPE_OK; return SCPE_OK;
} }
void ni_sysgen(uint8 cid) void ni_sysgen(uint8 slot)
{ {
int i;
t_bool pumped = FALSE;
cio_entry cqe = {0}; cio_entry cqe = {0};
uint8 app_data[4] = {0}; uint8 app_data[4] = {0};
@ -607,16 +619,21 @@ void ni_sysgen(uint8 cid)
sim_debug(DBG_TRACE, &ni_dev, sim_debug(DBG_TRACE, &ni_dev,
"[ni_sysgen] CIO SYSGEN. rqp=%08x, cqp=%08x, nrq=%d, rqs=%d cqs=%d\n", "[ni_sysgen] CIO SYSGEN. rqp=%08x, cqp=%08x, nrq=%d, rqs=%d cqs=%d\n",
cio[cid].rqp, cio[cid].cqp, cio[cid].no_rque, cio[cid].rqs, cio[cid].cqs); cio[slot].rqp, cio[slot].cqp, cio[slot].no_rque, cio[slot].rqs, cio[slot].cqs);
/* If the card has been successfully pumped, then we respond with /* If the card has been successfully pumped, then we respond with
* a full completion queue entry. Otherwise, an express entry is * a full completion queue entry. Otherwise, an express entry is
* used. */ * used. */
if (ni.crc == NI_PUMP_CRC1 || for (i = 0; NI_PUMP_CRCS[i] != 0; i++) {
ni.crc == NI_PUMP_CRC2) { if (NI_PUMP_CRCS[i] == ni.crc) {
cio_cqueue(cid, CIO_STAT, NIQESIZE, &cqe, app_data); cio_cqueue(slot, CIO_STAT, NIQESIZE, &cqe, app_data);
} else { pumped = TRUE;
cio_cexpress(cid, NIQESIZE, &cqe, app_data); break;
}
}
if (!pumped) {
cio_cexpress(slot, NIQESIZE, &cqe, app_data);
} }
/* Now clear out the old CRC value, in case the card needs to be /* Now clear out the old CRC value, in case the card needs to be
@ -629,7 +646,7 @@ void ni_sysgen(uint8 cid)
/* /*
* Handler for CIO INT0 (express job) requests. * Handler for CIO INT0 (express job) requests.
*/ */
void ni_express(uint8 cid) void ni_express(uint8 slot)
{ {
cio_entry rqe = {0}; cio_entry rqe = {0};
uint8 app_data[4] = {0}; uint8 app_data[4] = {0};
@ -637,14 +654,14 @@ void ni_express(uint8 cid)
sim_debug(DBG_TRACE, &ni_dev, sim_debug(DBG_TRACE, &ni_dev,
"[ni_express] Handling express CIO request.\n"); "[ni_express] Handling express CIO request.\n");
cio_rexpress(cid, NIQESIZE, &rqe, app_data); cio_rexpress(slot, NIQESIZE, &rqe, app_data);
ni_cmd(cid, &rqe, app_data, TRUE); ni_cmd(slot, &rqe, app_data, TRUE);
} }
/* /*
* Handler for CIO INT1 (full job) requests. * Handler for CIO INT1 (full job) requests.
*/ */
void ni_full(uint8 cid) void ni_full(uint8 slot)
{ {
cio_entry rqe = {0}; cio_entry rqe = {0};
uint8 app_data[4] = {0}; uint8 app_data[4] = {0};
@ -652,89 +669,49 @@ void ni_full(uint8 cid)
sim_debug(DBG_TRACE, &ni_dev, sim_debug(DBG_TRACE, &ni_dev,
"[ni_full] INT1 received. Handling full CIO request.\n"); "[ni_full] INT1 received. Handling full CIO request.\n");
while (cio_cqueue_avail(cid, NIQESIZE) && while (cio_cqueue_avail(slot, NIQESIZE) &&
cio_rqueue(cid, GE_QUEUE, NIQESIZE, &rqe, app_data) == SCPE_OK) { cio_rqueue(slot, GE_QUEUE, NIQESIZE, &rqe, app_data) == SCPE_OK) {
ni_cmd(cid, &rqe, app_data, FALSE); ni_cmd(slot, &rqe, app_data, FALSE);
} }
} }
/* /*
* Handler for CIO RESET requests. * Handler for CIO RESET requests.
*/ */
void ni_cio_reset(uint8 cid) void ni_cio_reset(uint8 slot)
{ {
UNUSED(cid); UNUSED(slot);
ni_disable(); ni_disable();
} }
t_stat ni_autoconfig()
{
uint8 cid;
/* Clear the CIO table of NI cards */
for (cid = 0; cid < CIO_SLOTS; cid++) {
if (cio[cid].id == NI_ID) {
cio[cid].id = 0;
cio[cid].ipl = 0;
cio[cid].ivec = 0;
cio[cid].exp_handler = NULL;
cio[cid].full_handler = NULL;
cio[cid].reset_handler = NULL;
cio[cid].sysgen = NULL;
}
}
/* Find the first avaialable slot */
for (cid = 0; cid < CIO_SLOTS; cid++) {
if (cio[cid].id == 0) {
break;
}
}
/* Do we have room? */
if (cid >= CIO_SLOTS) {
return SCPE_NXM;
}
/* Remember the card slot */
ni.cid = cid;
/* Set up the ni structure */
cio[cid].id = NI_ID;
cio[cid].ipl = NI_IPL;
cio[cid].exp_handler = &ni_express;
cio[cid].full_handler = &ni_full;
cio[cid].reset_handler = &ni_cio_reset;
cio[cid].sysgen = &ni_sysgen;
return SCPE_OK;
}
t_stat ni_reset(DEVICE *dptr) t_stat ni_reset(DEVICE *dptr)
{ {
t_stat status; t_stat r;
uint8 slot;
char uname[16]; char uname[16];
sim_debug(DBG_TRACE, &ni_dev, if (dptr->flags & DEV_DIS) {
"[ni_reset] Resetting NI device\n"); cio_remove_all(NI_ID);
ni_conf = FALSE;
/* Initial setup that should only ever be done once. */ return SCPE_OK;
if (!(dptr->flags & DEV_DIS) && !ni.initialized) {
/* Autoconfiguration will select the correct backplane slot
* for the device, and enable CIO routines. This should only
* be done once. */
status = ni_autoconfig();
if (status != SCPE_OK) {
return status;
}
/* Set an initial MAC address in the AT&T NI range */
ni_setmac(ni_dev.units, 0, "80:00:10:03:00:00/32", NULL);
ni.initialized = TRUE;
} }
if (!ni_conf) {
r = cio_install(NI_ID, "NI", NI_IPL,
&ni_express, &ni_full, &ni_sysgen, &ni_cio_reset,
&slot);
if (r != SCPE_OK) {
return r;
}
/* Remember the card slot */
ni.slot = slot;
ni_conf = TRUE;
}
/* Set an initial MAC address in the AT&T NI range */
ni_setmac(ni_dev.units, 0, "80:00:10:03:00:00/32", NULL);
/* Set up unit names */ /* Set up unit names */
snprintf(uname, 16, "%s-RCV", dptr->name); snprintf(uname, 16, "%s-RCV", dptr->name);
sim_set_uname(rcv_unit, uname); sim_set_uname(rcv_unit, uname);
@ -756,9 +733,6 @@ t_stat ni_reset(DEVICE *dptr)
* polling activity and interrupts are disabled. */ * polling activity and interrupts are disabled. */
ni_disable(); ni_disable();
/* We make no attempt to autoconfig until the device
* is attached. */
return SCPE_OK; return SCPE_OK;
} }
@ -798,16 +772,16 @@ t_stat ni_rq_svc(UNIT *uptr)
UNUSED(uptr); UNUSED(uptr);
rq_taken = FALSE; rq_taken = FALSE;
no_rque = cio[ni.cid].no_rque - 1; no_rque = cio[ni.slot].no_rque - 1;
for (i = 0; i < no_rque; i++) { for (i = 0; i < no_rque; i++) {
while (NI_CACHE_HAS_SPACE(i) && cio_rqueue(ni.cid, i+1, NIQESIZE, &rqe, slot) == SCPE_OK) { while (NI_CACHE_HAS_SPACE(i) && cio_rqueue(ni.slot, i+1, NIQESIZE, &rqe, slot) == SCPE_OK) {
sim_debug(DBG_CACHE, &ni_dev, sim_debug(DBG_CACHE, &ni_dev,
"[cache - FILL] %s packet entry. lp=%02x ulp=%02x " "[cache - FILL] %s packet entry. lp=%02x ulp=%02x "
"slot=%d addr=0x%08x\n", "slot=%d addr=0x%08x\n",
i == 0 ? "Small" : "Large", i == 0 ? "Small" : "Large",
cio_r_lp(ni.cid, i+1, NIQESIZE), cio_r_lp(ni.slot, i+1, NIQESIZE),
cio_r_ulp(ni.cid, i+1, NIQESIZE), cio_r_ulp(ni.slot, i+1, NIQESIZE),
slot[3], rqe.address); slot[3], rqe.address);
wp = ni.job_cache[i].wp; wp = ni.job_cache[i].wp;
ni.job_cache[i].req[wp].addr = rqe.address; ni.job_cache[i].req[wp].addr = rqe.address;
@ -835,11 +809,7 @@ t_stat ni_rq_svc(UNIT *uptr)
if (ni.poll_rate == NI_QPOLL_FAST) { if (ni.poll_rate == NI_QPOLL_FAST) {
sim_activate_abs(rq_unit, NI_QPOLL_FAST); sim_activate_abs(rq_unit, NI_QPOLL_FAST);
} else { } else {
if (sim_idle_enab) { sim_clock_coschedule(rq_unit, 1000);
sim_clock_coschedule(rq_unit, tmxr_poll);
} else {
sim_activate_abs(rq_unit, NI_QPOLL_SLOW);
}
} }
return SCPE_OK; return SCPE_OK;
@ -867,11 +837,9 @@ t_stat ni_sanity_svc(UNIT *uptr)
"[ni_sanity_svc] Firing sanity timer.\n"); "[ni_sanity_svc] Firing sanity timer.\n");
cqe.opcode = NI_SANITY; cqe.opcode = NI_SANITY;
cio_cqueue(ni.cid, CIO_STAT, NIQESIZE, &cqe, app_data); cio_cqueue(ni.slot, CIO_STAT, NIQESIZE, &cqe, app_data);
if (cio[ni.cid].ivec > 0) { CIO_SET_INT(ni.slot);
CIO_SET_INT(ni.cid);
}
sim_activate_after(sanity_unit, NI_SANITY_INTERVAL_US); sim_activate_after(sanity_unit, NI_SANITY_INTERVAL_US);
@ -882,11 +850,9 @@ t_stat ni_cio_svc(UNIT *uptr)
{ {
UNUSED(uptr); UNUSED(uptr);
if (cio[ni.cid].ivec > 0) { sim_debug(DBG_TRACE, &ni_dev,
sim_debug(DBG_TRACE, &ni_dev, "[ni_cio_svc] Handling a CIO service (Setting Interrupt) for board %d\n", ni.slot);
"[ni_cio_svc] Handling a CIO service (Setting Interrupt) for board %d\n", ni.cid); CIO_SET_INT(ni.slot);
CIO_SET_INT(ni.cid);
}
return SCPE_OK; return SCPE_OK;
} }
@ -924,13 +890,13 @@ void ni_process_packet()
"[cache - DRAIN] %s packet entry. lp=%02x ulp=%02x " "[cache - DRAIN] %s packet entry. lp=%02x ulp=%02x "
"slot=%d addr=0x%08x\n", "slot=%d addr=0x%08x\n",
que_num == 0 ? "Small" : "Large", que_num == 0 ? "Small" : "Large",
cio_r_lp(ni.cid, que_num+1, NIQESIZE), cio_r_lp(ni.slot, que_num+1, NIQESIZE),
cio_r_ulp(ni.cid, que_num+1, NIQESIZE), cio_r_ulp(ni.slot, que_num+1, NIQESIZE),
slot, addr); slot, addr);
/* Store the packet into main memory */ /* Store the packet into main memory */
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
pwrite_b(addr + i, rbuf[i]); pwrite_b(addr + i, rbuf[i], BUS_PER);
} }
if (ni_dev.dctrl & DBG_DAT) { if (ni_dev.dctrl & DBG_DAT) {
@ -948,12 +914,10 @@ void ni_process_packet()
capp_data[3] = slot; capp_data[3] = slot;
/* TODO: We should probably also check status here. */ /* TODO: We should probably also check status here. */
cio_cqueue(ni.cid, CIO_STAT, NIQESIZE, &centry, capp_data); cio_cqueue(ni.slot, CIO_STAT, NIQESIZE, &centry, capp_data);
/* Trigger an interrupt */ /* Trigger an interrupt */
if (cio[ni.cid].ivec > 0) { CIO_SET_INT(ni.slot);
CIO_SET_INT(ni.cid);
}
} }
t_stat ni_attach(UNIT *uptr, CONST char *cptr) t_stat ni_attach(UNIT *uptr, CONST char *cptr)
@ -1155,137 +1119,3 @@ const char *ni_description(DEVICE *dptr)
return "NI 10BASE5 Ethernet controller"; return "NI 10BASE5 Ethernet controller";
} }
/*
* Useful routines for debugging request and completion queues
*/
static t_stat ni_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
return ni_show_queue_common(st, uptr, val, desc, TRUE);
}
static t_stat ni_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
return ni_show_queue_common(st, uptr, val, desc, FALSE);
}
static t_stat ni_show_queue_common(FILE *st, UNIT *uptr, int32 val,
CONST void *desc, t_bool rq)
{
uint8 cid;
char *cptr = (char *) desc;
t_stat result;
uint32 ptr, size, no_rque, i, j;
uint16 lp, ulp;
uint8 op, dev, seq, cmdstat;
UNUSED(uptr);
UNUSED(val);
if (cptr) {
cid = (uint8) get_uint(cptr, 10, 12, &result);
if (result != SCPE_OK) {
return SCPE_ARG;
}
} else {
return SCPE_ARG;
}
/* If the card is not sysgen'ed, give up */
if (cio[cid].sysgen_s != CIO_SYSGEN) {
fprintf(st, "No card in slot %d, or card has not completed sysgen\n", cid);
return SCPE_ARG;
}
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "Sysgen Block:\n");
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, " Request Queue Pointer: 0x%08x\n", cio[cid].rqp);
fprintf(st, " Completion Queue Pointer: 0x%08x\n", cio[cid].cqp);
fprintf(st, " Request Queue Size: 0x%02x\n", cio[cid].rqs);
fprintf(st, " Completion Queue Size: 0x%02x\n", cio[cid].cqs);
fprintf(st, " Interrupt Vector: %d (0x%02x)\n", cio[cid].ivec, cio[cid].ivec);
fprintf(st, " Number of Request Queues: %d\n", cio[cid].no_rque);
fprintf(st, "---------------------------------------------------------\n");
/* Get the top of the queue */
if (rq) {
ptr = cio[cid].rqp;
size = cio[cid].rqs;
no_rque = cio[cid].no_rque;
} else {
ptr = cio[cid].cqp;
size = cio[cid].cqs;
no_rque = 0; /* Not used */
}
if (rq) {
fprintf(st, "Dumping %d Request Queues\n", no_rque);
} else {
fprintf(st, "Dumping Completion Queue\n");
}
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "EXPRESS ENTRY:\n");
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2));
fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3));
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
ptr += 12;
if (rq) {
for (i = 0; i < no_rque; i++) {
lp = pread_h(ptr);
ulp = pread_h(ptr + 2);
ptr += 4;
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "REQUEST QUEUE %d\n", i);
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "Load Pointer: 0x%04x (%d)\n", lp, (lp / NIQESIZE) + 1);
fprintf(st, "Unload Pointer: 0x%04x (%d)\n", ulp, (ulp / NIQESIZE) + 1);
fprintf(st, "---------------------------------------------------------\n");
for (j = 0; j < size; j++) {
dev = pread_b(ptr + 2);
op = pread_b(ptr + 3);
seq = (dev & 0x40) >> 6;
cmdstat = (dev & 0x80) >> 7;
fprintf(st, "REQUEST ENTRY %d (@ 0x%08x)\n", j + 1, ptr);
fprintf(st, " Byte Count: 0x%04x\n", pread_h(ptr));
fprintf(st, " Subdevice: %d\n", dev & 0x3f);
fprintf(st, " Cmd/Stat: %d\n", cmdstat);
fprintf(st, " Seqbit: %d\n", seq);
fprintf(st, " Opcode: 0x%02x (%d)\n", op, op);
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
ptr += 12;
}
}
} else {
lp = pread_h(ptr);
ulp = pread_h(ptr + 2);
ptr += 4;
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "Load Pointer: 0x%04x (%d)\n", lp, (lp / NIQESIZE) + 1);
fprintf(st, "Unload Pointer: 0x%04x (%d)\n", ulp, (ulp / NIQESIZE) + 1);
fprintf(st, "---------------------------------------------------------\n");
for (i = 0; i < size; i++) {
dev = pread_b(ptr + 2);
op = pread_b(ptr + 3);
seq = (dev & 0x40) >> 6;
cmdstat = (dev & 0x80) >> 7;
fprintf(st, "COMPLETION ENTRY %d (@ 0x%08x)\n", i + 1, ptr);
fprintf(st, " Byte Count: 0x%04x\n", pread_h(ptr));
fprintf(st, " Subdevice: %d\n", dev & 0x3f);
fprintf(st, " Cmd/Stat: %d\n", cmdstat);
fprintf(st, " Seqbit: %d\n", seq);
fprintf(st, " Opcode: 0x%02x (%d)\n", op, op);
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
ptr += 12;
}
}
return SCPE_OK;
}

View file

@ -1,6 +1,6 @@
/* 3b2_ni.h: AT&T 3B2 Model 400 "NI" feature card /* 3b2_ni.h: CM195A Network Interface CIO Card
Copyright (c) 2018, Seth J. Morabito Copyright (c) 2018-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -84,9 +84,6 @@
#define NI_QPOLL_FAST 100 #define NI_QPOLL_FAST 100
#define NI_QPOLL_SLOW 50000 #define NI_QPOLL_SLOW 50000
#define NI_PUMP_CRC1 0xfab1057c
#define NI_PUMP_CRC2 0xf6744bed
#define EIG_TABLE_SIZE 40 #define EIG_TABLE_SIZE 40
#define PKT_HEADER_LEN_OFFSET EIG_TABLE_SIZE #define PKT_HEADER_LEN_OFFSET EIG_TABLE_SIZE
#define PKT_START_OFFSET (PKT_HEADER_LEN_OFFSET + 4) #define PKT_START_OFFSET (PKT_HEADER_LEN_OFFSET + 4)
@ -168,8 +165,7 @@ typedef struct {
} ni_stat_info; } ni_stat_info;
typedef struct { typedef struct {
uint8 cid; uint8 slot;
t_bool initialized;
t_bool enabled; t_bool enabled;
uint32 crc; uint32 crc;
uint32 poll_rate; uint32 poll_rate;
@ -197,18 +193,18 @@ t_stat ni_attach(UNIT *uptr, CONST char *cptr);
t_stat ni_detach(UNIT *uptr); t_stat ni_detach(UNIT *uptr);
t_stat ni_setmac(UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat ni_setmac(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat ni_showmac(FILE* st, UNIT *uptr, int32 val, CONST void *desc); t_stat ni_showmac(FILE* st, UNIT *uptr, int32 val, CONST void *desc);
t_stat ni_try_job(uint8 cid); t_stat ni_try_job(uint8 slot);
t_stat ni_show_stats(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat ni_show_stats(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat ni_set_stats(UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat ni_set_stats(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat ni_show_poll(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat ni_show_poll(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat ni_show_filters(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat ni_show_filters(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
const char *ni_description(DEVICE *dptr); const char *ni_description(DEVICE *dptr);
t_stat ni_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); t_stat ni_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
void ni_cio_reset(uint8 cid); void ni_cio_reset(uint8 slot);
void ni_process_packet(); void ni_process_packet();
void ni_int_ack(uint8 cid); void ni_int_ack(uint8 slot);
void ni_sysgen(uint8 cid); void ni_sysgen(uint8 slot);
void ni_express(uint8 cid); void ni_express(uint8 slot);
void ni_full(uint8 cid); void ni_full(uint8 slot);
#endif /* _3B2_NI_H_ */ #endif /* _3B2_NI_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_ports.c: AT&T 3B2 Model 400 "PORTS" feature card /* 3b2_ports.c: CM195B 4-Port Serial CIO Card
Copyright (c) 2018, Seth J. Morabito Copyright (c) 2018-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -49,11 +49,9 @@
#include "3b2_cpu.h" #include "3b2_cpu.h"
#include "3b2_io.h" #include "3b2_io.h"
#include "3b2_mem.h" #include "3b2_mem.h"
#include "3b2_timer.h" #include "3b2_stddev.h"
/* Static function declarations */ #define IO_SCHED 1000
static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val,
CONST void *desc, t_bool rq);
/* Device and units for PORTS cards /* Device and units for PORTS cards
* -------------------------------- * --------------------------------
@ -78,6 +76,8 @@ static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val,
* *
*/ */
#define MAX_LINES 32
#define PPQESIZE 12 #define PPQESIZE 12
#define DELAY_ASYNC 25 #define DELAY_ASYNC 25
#define DELAY_DLM 100 #define DELAY_DLM 100
@ -100,16 +100,31 @@ static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val,
#define PORTS_DIAG_CRC5 0x4be7bccc /* 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 PORTS_DIAG_CRC6 0x3197f6dd /* Used by SVR 2.0.5 */
#define LN(cid,port) ((PORTS_LINES * ((cid) - ports_base_cid)) + (port)) #define PORTS_DFLT_LINES 4
#define LCID(ln) (((ln) / PORTS_LINES) + ports_base_cid) #define PORTS_DFLT_CARDS 1
#define LPORT(ln) ((ln) % PORTS_LINES)
int8 ports_base_cid; /* First cid in our contiguous block */ #define LN(slot,port) (ports_slot_ln[(slot)] + (port))
uint8 ports_int_cid; /* Interrupting card ID */ #define LSLOT(ln) (ports_ln_slot[ln])
/* #define LN(slot,port) ((PORTS_LINES * ((slot) - ports_base_slot)) + (port)) */
/* #define LSLOT(ln) (((ln) / PORTS_LINES) + ports_base_slot) */
#define LPORT(ln) ((ln) % PORTS_LINES)
int8 ports_base_slot; /* First slot in our contiguous block */
uint8 ports_int_slot; /* Interrupting card ID */
uint8 ports_int_subdev; /* Interrupting subdevice */ uint8 ports_int_subdev; /* Interrupting subdevice */
t_bool ports_conf = FALSE; /* Have PORTS cards been configured? */ t_bool ports_conf = FALSE; /* Have PORTS cards been configured? */
uint32 ports_crc; /* CRC32 of downloaded memory */ uint32 ports_crc; /* CRC32 of downloaded memory */
/* Mapping of line number to CIO card slot. Up to 32 lines spread over
8 slots are supported. */
uint8 ports_ln_slot[MAX_LINES];
/* Mapping of slot number to base line number belonging to the card in
that slot. I.e., if there are two PORTS cards, one in slot 3 and
one in slot 5, index 3 will have starting line 0, index 5 will have
starting line 4. */
uint32 ports_slot_ln[CIO_SLOTS];
/* PORTS-specific state for each slot */ /* PORTS-specific state for each slot */
PORTS_LINE_STATE *ports_state = NULL; PORTS_LINE_STATE *ports_state = NULL;
@ -136,12 +151,8 @@ MTAB ports_mod[] = {
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL, NULL, NULL, "7 bit mode" }, { TT_MODE, TT_MODE_7B, "7b", "7B", NULL, NULL, NULL, "7 bit mode" },
{ TT_MODE, TT_MODE_8B, "8b", "8B", NULL, NULL, NULL, "8 bit mode" }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL, NULL, NULL, "8 bit mode" },
{ TT_MODE, TT_MODE_7P, "7p", "7P", NULL, NULL, NULL, "7 bit mode - non printing suppressed" }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL, NULL, NULL, "7 bit mode - non printing suppressed" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "RQUEUE=n", NULL,
NULL, &ports_show_rqueue, NULL, "Display Request Queue for card n" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "CQUEUE=n", NULL,
NULL, &ports_show_cqueue, NULL, "Display Completion Queue for card n" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n", { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n",
&ports_setnl, &tmxr_show_lines, (void *) &ports_desc, "Display number of lines" }, &ports_setnl, &tmxr_show_lines, (void *) &ports_desc, "Show or set number of lines" },
{ 0 } { 0 }
}; };
@ -189,9 +200,9 @@ DEVICE ports_dev = {
}; };
static void cio_irq(uint8 cid, uint8 dev, int32 delay) static void cio_irq(uint8 slot, uint8 dev, int32 delay)
{ {
ports_int_cid = cid; ports_int_slot = slot;
ports_int_subdev = dev & 0xf; ports_int_subdev = dev & 0xf;
sim_activate(&ports_unit[2], delay); sim_activate(&ports_unit[2], delay);
} }
@ -209,16 +220,20 @@ t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
return SCPE_ARG; return SCPE_ARG;
} }
newln = (int32) get_uint(cptr, 10, (MAX_PORTS_CARDS * PORTS_LINES), &r); newln = (int32) get_uint(cptr, 10, (MAX_CARDS * PORTS_LINES), &r);
if ((r != SCPE_OK) || (newln == ports_desc.lines)) { if ((r != SCPE_OK) || (newln == ports_desc.lines)) {
return r; return r;
} }
if ((newln == 0) || LPORT(newln) != 0) { if ((newln == 0) || LPORT(newln) != 0 || newln > MAX_LINES) {
return SCPE_ARG; return SCPE_ARG;
} }
sim_debug(TRACE_DBG, &ports_dev,
"[ports_setnl] Setting line count to %d\n",
newln);
if (newln < ports_desc.lines) { if (newln < ports_desc.lines) {
for (i = newln, t = 0; i < ports_desc.lines; i++) { for (i = newln, t = 0; i < ports_desc.lines; i++) {
t = t | ports_ldsc[i].conn; t = t | ports_ldsc[i].conn;
@ -235,10 +250,6 @@ t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
} }
/* completely reset line */ /* completely reset line */
tmxr_detach_ln(&ports_ldsc[i]); tmxr_detach_ln(&ports_ldsc[i]);
if (LPORT(i) == (PORTS_LINES - 1)) {
/* Also drop the corresponding card from the CIO array */
cio_clear(LCID(i));
}
} }
} }
@ -258,7 +269,7 @@ t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
} }
static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data) static void ports_cmd(uint8 slot, cio_entry *rentry, uint8 *rapp_data)
{ {
cio_entry centry = {0}; cio_entry centry = {0};
uint32 ln, i; uint32 ln, i;
@ -267,37 +278,35 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data)
uint8 app_data[4] = {0}; uint8 app_data[4] = {0};
centry.address = rentry->address; centry.address = rentry->address;
cio[cid].op = rentry->opcode; cio[slot].op = rentry->opcode;
ln = LN(cid, rentry->subdevice & 0xf); ln = LN(slot, rentry->subdevice & 0xf);
switch(rentry->opcode) { switch(rentry->opcode) {
case CIO_DLM: case CIO_DLM:
for (i = 0; i < rentry->byte_count; i++) { for (i = 0; i < rentry->byte_count; i++) {
ports_crc = cio_crc32_shift(ports_crc, pread_b(rentry->address + i)); ports_crc = cio_crc32_shift(ports_crc, pread_b(rentry->address + i, BUS_PER));
} }
centry.address = rentry->address + rentry->byte_count; centry.address = rentry->address + rentry->byte_count;
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_cmd] CIO Download Memory: bytecnt=%04x " "[ports_cmd] CIO Download Memory: bytecnt=%04x "
"addr=%08x return_addr=%08x subdev=%02x (CRC=%08x)\n", "addr=%08x return_addr=%08x subdev=%02x (CRC=%08x)\n",
R[NUM_PC],
rentry->byte_count, rentry->address, rentry->byte_count, rentry->address,
centry.address, centry.subdevice, ports_crc); centry.address, centry.subdevice, ports_crc);
/* We intentionally do not set the subdevice in /* We intentionally do not set the subdevice in
* the completion entry */ * the completion entry */
cio_cexpress(cid, PPQESIZE, &centry, app_data); cio_cexpress(slot, PPQESIZE, &centry, app_data);
cio_irq(cid, rentry->subdevice, DELAY_DLM); cio_irq(slot, rentry->subdevice, DELAY_DLM);
break; break;
case CIO_ULM: case CIO_ULM:
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_cmd] CIO Upload Memory\n", "[ports_cmd] CIO Upload Memory\n");
R[NUM_PC]); cio_cexpress(slot, PPQESIZE, &centry, app_data);
cio_cexpress(cid, PPQESIZE, &centry, app_data); cio_irq(slot, rentry->subdevice, DELAY_ULM);
cio_irq(cid, rentry->subdevice, DELAY_ULM);
break; break;
case CIO_FCF: case CIO_FCF:
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_cmd] CIO Force Function Call (CRC=%08x)\n", "[ports_cmd] CIO Force Function Call (CRC=%08x)\n",
R[NUM_PC], ports_crc); ports_crc);
/* If the currently running program is a diagnostics program, /* If the currently running program is a diagnostics program,
* we are expected to write results into memory at address * we are expected to write results into memory at address
@ -308,11 +317,11 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data)
ports_crc == PORTS_DIAG_CRC4 || ports_crc == PORTS_DIAG_CRC4 ||
ports_crc == PORTS_DIAG_CRC5 || ports_crc == PORTS_DIAG_CRC5 ||
ports_crc == PORTS_DIAG_CRC6) { ports_crc == PORTS_DIAG_CRC6) {
pwrite_h(0x200f000, 0x1); /* Test success */ pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */
pwrite_h(0x200f002, 0x0); /* Test Number */ pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */
pwrite_h(0x200f004, 0x0); /* Actual */ pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */
pwrite_h(0x200f006, 0x0); /* Expected */ pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */
pwrite_b(0x200f008, 0x1); /* Success flag again */ pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */
} }
/* An interesting (?) side-effect of FORCE FUNCTION CALL is /* An interesting (?) side-effect of FORCE FUNCTION CALL is
@ -320,49 +329,46 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data)
* required in order for new commands to work. In fact, an * required in order for new commands to work. In fact, an
* INT0/INT1 combo _without_ a RESET can sysgen the board. So, * INT0/INT1 combo _without_ a RESET can sysgen the board. So,
* we reset the command bits here. */ * we reset the command bits here. */
cio[cid].sysgen_s = 0; cio[slot].sysgen_s = 0;
cio_cexpress(cid, PPQESIZE, &centry, app_data); cio_cexpress(slot, PPQESIZE, &centry, app_data);
cio_irq(cid, rentry->subdevice, DELAY_FCF); cio_irq(slot, rentry->subdevice, DELAY_FCF);
break; break;
case CIO_DOS: case CIO_DOS:
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_cmd] CIO Determine Op Status\n", "[ports_cmd] CIO Determine Op Status\n");
R[NUM_PC]); cio_cexpress(slot, PPQESIZE, &centry, app_data);
cio_cexpress(cid, PPQESIZE, &centry, app_data); cio_irq(slot, rentry->subdevice, DELAY_DOS);
cio_irq(cid, rentry->subdevice, DELAY_DOS);
break; break;
case CIO_DSD: case CIO_DSD:
/* Determine Sub-Devices. We have none. */ /* Determine Sub-Devices. We have none. */
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_cmd] Determine Sub-Devices.\n", "[ports_cmd] Determine Sub-Devices.\n");
R[NUM_PC]);
/* The system wants us to write sub-device structures /* The system wants us to write sub-device structures
* at the supplied address */ * at the supplied address */
pwrite_h(rentry->address, 0x0); pwrite_h(rentry->address, 0x0, BUS_PER);
cio_cexpress(cid, PPQESIZE, &centry, app_data); cio_cexpress(slot, PPQESIZE, &centry, app_data);
cio_irq(cid, rentry->subdevice, DELAY_DSD); cio_irq(slot, rentry->subdevice, DELAY_DSD);
break; break;
case PPC_OPTIONS: case PPC_OPTIONS:
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_cmd] PPC Options Operation\n", "[ports_cmd] PPC Options Operation\n");
R[NUM_PC]);
opts.line = pread_h(rentry->address); opts.line = pread_h(rentry->address, BUS_PER);
opts.iflag = pread_h(rentry->address + 4); opts.iflag = pread_h(rentry->address + 4, BUS_PER);
opts.oflag = pread_h(rentry->address + 6); opts.oflag = pread_h(rentry->address + 6, BUS_PER);
opts.cflag = pread_h(rentry->address + 8); opts.cflag = pread_h(rentry->address + 8, BUS_PER);
opts.lflag = pread_h(rentry->address + 10); opts.lflag = pread_h(rentry->address + 10, BUS_PER);
opts.cerase = pread_b(rentry->address + 11); opts.cerase = pread_b(rentry->address + 11, BUS_PER);
opts.ckill = pread_b(rentry->address + 12); opts.ckill = pread_b(rentry->address + 12, BUS_PER);
opts.cinter = pread_b(rentry->address + 13); opts.cinter = pread_b(rentry->address + 13, BUS_PER);
opts.cquit = pread_b(rentry->address + 14); opts.cquit = pread_b(rentry->address + 14, BUS_PER);
opts.ceof = pread_b(rentry->address + 15); opts.ceof = pread_b(rentry->address + 15, BUS_PER);
opts.ceol = pread_b(rentry->address + 16); opts.ceol = pread_b(rentry->address + 16, BUS_PER);
opts.itime = pread_b(rentry->address + 17); opts.itime = pread_b(rentry->address + 17, BUS_PER);
opts.vtime = pread_b(rentry->address + 18); opts.vtime = pread_b(rentry->address + 18, BUS_PER);
opts.vcount = pread_b(rentry->address + 19); opts.vcount = pread_b(rentry->address + 19, BUS_PER);
sim_debug(TRACE_DBG, &ports_dev, " PPC Options: iflag=%04x\n", opts.iflag); sim_debug(TRACE_DBG, &ports_dev, " PPC Options: iflag=%04x\n", opts.iflag);
sim_debug(TRACE_DBG, &ports_dev, " PPC Options: oflag=%04x\n", opts.oflag); sim_debug(TRACE_DBG, &ports_dev, " PPC Options: oflag=%04x\n", opts.oflag);
@ -391,46 +397,45 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data)
centry.opcode = PPC_OPTIONS; centry.opcode = PPC_OPTIONS;
centry.subdevice = rentry->subdevice; centry.subdevice = rentry->subdevice;
centry.address = rentry->address; centry.address = rentry->address;
cio_cqueue(cid, CIO_STAT, PPQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, PPQESIZE, &centry, app_data);
cio_irq(cid, rentry->subdevice, DELAY_OPTIONS); cio_irq(slot, rentry->subdevice, DELAY_OPTIONS);
break; break;
case PPC_VERS: case PPC_VERS:
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_cmd] PPC Version\n", "[ports_cmd] PPC Version\n");
R[NUM_PC]);
/* Write the version number at the supplied address */ /* Write the version number at the supplied address */
pwrite_b(rentry->address, PORTS_VERSION); pwrite_b(rentry->address, PORTS_VERSION, BUS_PER);
centry.opcode = CIO_ULM; centry.opcode = CIO_ULM;
/* TODO: It's unknown what the value 0x50 means, but this /* TODO: It's unknown what the value 0x50 means, but this
* is what a real board sends. */ * is what a real board sends. */
app_data[0] = 0x50; app_data[0] = 0x50;
cio_cqueue(cid, CIO_STAT, PPQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, PPQESIZE, &centry, app_data);
cio_irq(cid, rentry->subdevice, DELAY_VERS); cio_irq(slot, rentry->subdevice, DELAY_VERS);
break; break;
case PPC_CONN: case PPC_CONN:
/* CONNECT - Full request and completion queues */ /* CONNECT - Full request and completion queues */
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_cmd] PPC CONNECT - subdevice = %02x\n", "[ports_cmd] PPC CONNECT - subdevice = %02x\n",
R[NUM_PC], rentry->subdevice); rentry->subdevice);
ports_state[ln].conn = TRUE; ports_state[ln].conn = TRUE;
centry.opcode = PPC_CONN; centry.opcode = PPC_CONN;
centry.subdevice = rentry->subdevice; centry.subdevice = rentry->subdevice;
centry.address = rentry->address; centry.address = rentry->address;
cio_cqueue(cid, CIO_STAT, PPQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, PPQESIZE, &centry, app_data);
cio_irq(cid, rentry->subdevice, DELAY_CONN); cio_irq(slot, rentry->subdevice, DELAY_CONN);
break; break;
case PPC_XMIT: case PPC_XMIT:
/* XMIT - Full request and completion queues */ /* XMIT - Full request and completion queues */
/* The port being referred to is in the subdevice. */ /* The port being referred to is in the subdevice. */
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_cmd] PPC XMIT - subdevice = %02x, address=%08x, byte_count=%d\n", "[ports_cmd] PPC XMIT - subdevice = %02x, address=%08x, byte_count=%d\n",
R[NUM_PC], rentry->subdevice, rentry->address, rentry->byte_count); rentry->subdevice, rentry->address, rentry->byte_count);
/* Set state for xmit */ /* Set state for xmit */
ports_state[ln].tx_addr = rentry->address; ports_state[ln].tx_addr = rentry->address;
@ -445,20 +450,20 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data)
/* DEVICE Control - Express request and completion queues */ /* DEVICE Control - Express request and completion queues */
/* The port being referred to is in the subdevice. */ /* The port being referred to is in the subdevice. */
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_cmd] PPC DEVICE - subdevice = %02x\n", "[ports_cmd] PPC DEVICE - subdevice = %02x\n",
R[NUM_PC], rentry->subdevice); rentry->subdevice);
centry.subdevice = rentry->subdevice; centry.subdevice = rentry->subdevice;
centry.opcode = PPC_DEVICE; centry.opcode = PPC_DEVICE;
cio_cexpress(cid, PPQESIZE, &centry, app_data); cio_cexpress(slot, PPQESIZE, &centry, app_data);
cio_irq(cid, rentry->subdevice, DELAY_DEVICE); cio_irq(slot, rentry->subdevice, DELAY_DEVICE);
break; break;
case PPC_RECV: case PPC_RECV:
/* RECV - Full request and completion queues */ /* RECV - Full request and completion queues */
/* The port being referred to is in the subdevice. */ /* The port being referred to is in the subdevice. */
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_cmd] PPC RECV - subdevice = %02x addr=%08x\n", "[ports_cmd] PPC RECV - subdevice = %02x addr=%08x\n",
R[NUM_PC], rentry->subdevice, rentry->address); rentry->subdevice, rentry->address);
break; break;
case PPC_DISC: case PPC_DISC:
@ -466,8 +471,8 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data)
centry.subdevice = rentry->subdevice; centry.subdevice = rentry->subdevice;
centry.opcode = PPC_DISC; centry.opcode = PPC_DISC;
ports_ldsc[ln].rcve = 0; ports_ldsc[ln].rcve = 0;
cio_cqueue(cid, CIO_STAT, PPQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, PPQESIZE, &centry, app_data);
cio_irq(cid, rentry->subdevice, DELAY_STD); cio_irq(slot, rentry->subdevice, DELAY_STD);
break; break;
case PPC_BRK: case PPC_BRK:
case PPC_CLR: case PPC_CLR:
@ -476,8 +481,8 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data)
">>> Op %d Not Handled Yet\n", ">>> Op %d Not Handled Yet\n",
rentry->opcode); rentry->opcode);
cio_cexpress(cid, PPQESIZE, &centry, app_data); cio_cexpress(slot, PPQESIZE, &centry, app_data);
cio_irq(cid, rentry->subdevice, DELAY_STD); cio_irq(slot, rentry->subdevice, DELAY_STD);
break; break;
} }
} }
@ -488,14 +493,14 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data)
static void ports_update_conn(uint32 ln) static void ports_update_conn(uint32 ln)
{ {
cio_entry centry = {0}; cio_entry centry = {0};
uint8 cid; uint8 slot;
uint8 app_data[4] = {0}; uint8 app_data[4] = {0};
cid = LCID(ln); slot = LSLOT(ln);
/* If the card hasn't sysgened, there's no way to write a /* If the card hasn't sysgened, there's no way to write a
* completion queue entry */ * completion queue entry */
if (cio[cid].sysgen_s != CIO_SYSGEN) { if (cio[slot].sysgen_s != CIO_SYSGEN) {
return; return;
} }
@ -513,15 +518,13 @@ static void ports_update_conn(uint32 ln)
centry.opcode = PPC_ASYNC; centry.opcode = PPC_ASYNC;
centry.subdevice = LPORT(ln); centry.subdevice = LPORT(ln);
cio_cqueue(cid, CIO_CMD, PPQESIZE, &centry, app_data); cio_cqueue(slot, CIO_CMD, PPQESIZE, &centry, app_data);
/* Interrupt */ /* Interrupt */
if (cio[cid].ivec > 0) { CIO_SET_INT(slot);
CIO_SET_INT(cid);
}
} }
void ports_sysgen(uint8 cid) void ports_sysgen(uint8 slot)
{ {
cio_entry cqe = {0}; cio_entry cqe = {0};
uint8 app_data[4] = {0}; uint8 app_data[4] = {0};
@ -532,147 +535,98 @@ void ports_sysgen(uint8 cid)
/* It's not clear why we put a response in both the express /* It's not clear why we put a response in both the express
* and the full queue. */ * and the full queue. */
cio_cexpress(cid, PPQESIZE, &cqe, app_data); cio_cexpress(slot, PPQESIZE, &cqe, app_data);
cio_cqueue(cid, CIO_STAT, PPQESIZE, &cqe, app_data); cio_cqueue(slot, CIO_STAT, PPQESIZE, &cqe, app_data);
ports_int_cid = cid; ports_int_slot = slot;
sim_activate(&ports_unit[2], DELAY_STD); sim_activate(&ports_unit[2], DELAY_STD);
} }
void ports_express(uint8 cid) void ports_express(uint8 slot)
{ {
cio_entry rqe = {0}; cio_entry rqe = {0};
uint8 app_data[4] = {0}; uint8 app_data[4] = {0};
cio_rexpress(cid, PPQESIZE, &rqe, app_data); cio_rexpress(slot, PPQESIZE, &rqe, app_data);
ports_cmd(cid, &rqe, app_data); ports_cmd(slot, &rqe, app_data);
} }
void ports_full(uint8 cid) void ports_full(uint8 slot)
{ {
uint32 i; uint32 i;
cio_entry rqe = {0}; cio_entry rqe = {0};
uint8 app_data[4] = {0}; uint8 app_data[4] = {0};
for (i = 0; i < PORTS_LINES; i++) { for (i = 0; i < PORTS_LINES; i++) {
if (cio_rqueue(cid, i, PPQESIZE, &rqe, app_data) == SCPE_OK) { if (cio_rqueue(slot, i, PPQESIZE, &rqe, app_data) == SCPE_OK) {
ports_cmd(cid, &rqe, app_data); ports_cmd(slot, &rqe, app_data);
} }
} }
} }
t_stat ports_reset(DEVICE *dptr) t_stat ports_reset(DEVICE *dptr)
{ {
int32 i; int32 i, j;
uint8 cid, line, ln, end_slot; uint8 slot;
TMLN *lp; t_stat r;
ports_crc = 0; ports_crc = 0;
sim_debug(TRACE_DBG, &ports_dev, if (ports_ldsc == NULL) {
"[ports_reset] Resetting PORTS device\n"); sim_set_uname(&ports_unit[0], "PORTS-RCV");
sim_set_uname(&ports_unit[1], "PORTS-XMT");
sim_set_uname(&ports_unit[2], "PORTS-CIO");
ports_desc.lines = PORTS_DFLT_LINES;
ports_desc.ldsc = ports_ldsc =
(TMLN *)calloc(ports_desc.lines, sizeof(*ports_ldsc));
}
if (ports_state == NULL) {
ports_state = (PORTS_LINE_STATE *)calloc(ports_desc.lines, sizeof(*ports_state));
memset(ports_state, 0, ports_desc.lines*sizeof(*ports_state));
}
tmxr_set_port_speed_control(&ports_desc);
if ((dptr->flags & DEV_DIS)) { if ((dptr->flags & DEV_DIS)) {
for (cid = 0; cid < CIO_SLOTS; cid++) { cio_remove_all(PORTS_ID);
if (cio[cid].id == PORTS_ID) {
cio[cid].id = 0;
cio[cid].ipl = 0;
cio[cid].ivec = 0;
cio[cid].exp_handler = NULL;
cio[cid].full_handler = NULL;
cio[cid].sysgen = NULL;
}
}
ports_conf = FALSE; ports_conf = FALSE;
} else if (!ports_conf) { return SCPE_OK;
}
if (!ports_conf) {
/* Clear out any old cards, we're starting fresh */ /* Clear out any old cards, we're starting fresh */
for (cid = 0; cid < CIO_SLOTS; cid++) { cio_remove_all(PORTS_ID);
if (cio[cid].id == PORTS_ID) {
cio[cid].id = 0; memset(ports_slot_ln, 0, sizeof(ports_slot_ln));
cio[cid].ipl = 0; memset(ports_ln_slot, 0, sizeof(ports_ln_slot));
cio[cid].ivec = 0;
cio[cid].exp_handler = NULL; /* Insert necessary cards into the backplane */
cio[cid].full_handler = NULL; j = 0;
cio[cid].sysgen = NULL; for (i = 0; i < ports_desc.lines/PORTS_LINES; i++) {
r = cio_install(PORTS_ID, "PORTS", PORTS_IPL,
&ports_express, &ports_full, &ports_sysgen, NULL,
&slot);
if (r != SCPE_OK) {
return r;
} }
} /* Remember the port assignments */
ports_slot_ln[slot] = i * PORTS_LINES;
/* Find the first avaialable slot */ for (; j < (i * PORTS_LINES) + PORTS_LINES; j++) {
for (cid = 0; cid < CIO_SLOTS; cid++) { ports_ln_slot[j] = slot;
if (cio[cid].id == 0) {
break;
}
}
/* Do we have room? */
if (cid >= CIO_SLOTS || cid > (CIO_SLOTS - (ports_desc.lines/PORTS_LINES))) {
return SCPE_NXM;
}
/* Remember the base card slot */
ports_base_cid = cid;
end_slot = (cid + (ports_desc.lines/PORTS_LINES));
for (; cid < end_slot; cid++) {
/* Set up the ports structure */
cio[cid].id = PORTS_ID;
cio[cid].ipl = PORTS_IPL;
cio[cid].exp_handler = &ports_express;
cio[cid].full_handler = &ports_full;
cio[cid].sysgen = &ports_sysgen;
for (line = 0; line < PORTS_LINES; line++) {
ln = LN(cid, line);
sim_debug(TRACE_DBG, &ports_dev,
">>> Setting up lp %d (card %d, line %d)\n",
ln, cid, line);
lp = &ports_ldsc[ln];
tmxr_set_get_modem_bits(lp, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL);
} }
} }
ports_conf = TRUE; ports_conf = TRUE;
if (ports_ldsc == NULL) {
ports_desc.ldsc = ports_ldsc =
(TMLN *)calloc(ports_desc.lines, sizeof(*ports_ldsc));
}
if (ports_state == NULL) {
sim_debug(TRACE_DBG, &ports_dev,
"[ports_reset] calloc for ports_state...\n");
ports_state = (PORTS_LINE_STATE *)calloc(ports_desc.lines, sizeof(*ports_state));
}
memset(ports_state, 0, ports_desc.lines*sizeof(*ports_state));
tmxr_set_port_speed_control(&ports_desc);
for (i = 0; i < ports_desc.lines; i++) {
sim_debug(TRACE_DBG, &ports_dev,
"[ports_reset] Setting up line %d...\n", i);
tmxr_set_line_unit(&ports_desc, i, &ports_unit[0]);
tmxr_set_line_output_unit(&ports_desc, i, &ports_unit[1]);
if (!ports_ldsc[i].conn) {
ports_ldsc[i].xmte = 1;
}
ports_ldsc[i].rcve = 0;
tmxr_set_config_line(&ports_ldsc[i], "9600-8N1");
}
} }
if (!sim_is_active(&ports_unit[0])) { /* If attached, start polling for connections */
sim_debug(TRACE_DBG, &ports_dev, if (ports_unit[0].flags & UNIT_ATT) {
"[ports_reset] starting receive polling...\n"); sim_activate_after_abs(&ports_unit[0], ports_unit[0].wait);
sim_activate(&ports_unit[0], ports_unit[0].wait); } else {
sim_cancel(&ports_unit[0]);
} }
sim_debug(TRACE_DBG, &ports_dev,
"[ports_reset] returning scpe_ok\n");
return SCPE_OK; return SCPE_OK;
} }
@ -680,20 +634,18 @@ t_stat ports_cio_svc(UNIT *uptr)
{ {
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[ports_cio_svc] IRQ for board %d device %d\n", "[ports_cio_svc] IRQ for board %d device %d\n",
ports_int_cid, ports_int_subdev); ports_int_slot, ports_int_subdev);
if (cio[ports_int_cid].ivec > 0) { CIO_SET_INT(ports_int_slot);
CIO_SET_INT(ports_int_cid);
}
switch (cio[ports_int_cid].op) { switch (cio[ports_int_slot].op) {
case PPC_CONN: case PPC_CONN:
cio[ports_int_cid].op = PPC_ASYNC; cio[ports_int_slot].op = PPC_ASYNC;
ports_ldsc[LN(ports_int_cid, ports_int_subdev)].rcve = 1; ports_ldsc[LN(ports_int_slot, ports_int_subdev)].rcve = 1;
sim_activate(&ports_unit[2], DELAY_ASYNC); sim_activate(&ports_unit[2], DELAY_ASYNC);
break; break;
case PPC_ASYNC: case PPC_ASYNC:
ports_update_conn(LN(ports_int_cid, ports_int_subdev)); ports_update_conn(LN(ports_int_slot, ports_int_subdev));
break; break;
default: default:
break; break;
@ -704,7 +656,7 @@ t_stat ports_cio_svc(UNIT *uptr)
t_stat ports_rcv_svc(UNIT *uptr) t_stat ports_rcv_svc(UNIT *uptr)
{ {
uint8 cid; uint8 slot;
int32 temp, ln; int32 temp, ln;
char c; char c;
cio_entry rentry = {0}; cio_entry rentry = {0};
@ -721,10 +673,8 @@ t_stat ports_rcv_svc(UNIT *uptr)
ports_update_conn(ln); ports_update_conn(ln);
} }
tmxr_poll_rx(&ports_desc);
for (ln = 0; ln < ports_desc.lines; ln++) { for (ln = 0; ln < ports_desc.lines; ln++) {
cid = LCID(ln); slot = LSLOT(ln);
if (!ports_ldsc[ln].conn && ports_state[ln].conn) { if (!ports_ldsc[ln].conn && ports_state[ln].conn) {
ports_update_conn(ln); ports_update_conn(ln);
@ -743,24 +693,27 @@ t_stat ports_rcv_svc(UNIT *uptr)
c = 0xa; c = 0xa;
} }
if (cio[cid].ivec > 0 && if (cio[slot].ivec > 0 &&
cio_rqueue(cid, PORTS_RCV_QUEUE, cio_rqueue(slot, PORTS_RCV_QUEUE,
PPQESIZE, &rentry, rapp_data) == SCPE_OK) { PPQESIZE, &rentry, rapp_data) == SCPE_OK) {
CIO_SET_INT(cid); CIO_SET_INT(slot);
/* Write the character to the memory address */ /* Write the character to the memory address */
pwrite_b(rentry.address, c); pwrite_b(rentry.address, c, BUS_PER);
centry.subdevice = LPORT(ln); centry.subdevice = LPORT(ln);
centry.opcode = PPC_RECV; centry.opcode = PPC_RECV;
centry.address = rentry.address; centry.address = rentry.address;
capp_data[3] = RC_TMR; capp_data[3] = RC_TMR;
cio_cqueue(cid, CIO_STAT, PPQESIZE, &centry, capp_data); cio_cqueue(slot, CIO_STAT, PPQESIZE, &centry, capp_data);
} }
} }
} }
} }
tmxr_poll_rx(&ports_desc);
tmxr_poll_tx(&ports_desc);
tmxr_clock_coschedule(uptr, tmxr_poll); tmxr_clock_coschedule(uptr, tmxr_poll);
return SCPE_OK; return SCPE_OK;
@ -768,7 +721,7 @@ t_stat ports_rcv_svc(UNIT *uptr)
t_stat ports_xmt_svc(UNIT *uptr) t_stat ports_xmt_svc(UNIT *uptr)
{ {
uint8 cid, ln; uint8 slot, ln;
char c; char c;
t_bool tx = FALSE; /* Did a tx ever occur? */ t_bool tx = FALSE; /* Did a tx ever occur? */
cio_entry centry = {0}; cio_entry centry = {0};
@ -777,10 +730,10 @@ t_stat ports_xmt_svc(UNIT *uptr)
/* Scan all lines for output */ /* Scan all lines for output */
for (ln = 0; ln < ports_desc.lines; ln++) { for (ln = 0; ln < ports_desc.lines; ln++) {
cid = LCID(ln); slot = LSLOT(ln);
if (ports_ldsc[ln].conn && ports_state[ln].tx_chars > 0) { if (ports_ldsc[ln].conn && ports_state[ln].tx_chars > 0) {
tx = TRUE; /* Even an attempt at TX counts for rescheduling */ tx = TRUE; /* Even an attempt at TX counts for rescheduling */
c = sim_tt_outcvt(pread_b(ports_state[ln].tx_addr), c = sim_tt_outcvt(pread_b(ports_state[ln].tx_addr, BUS_PER),
TT_GET_MODE(ports_unit[0].flags)); TT_GET_MODE(ports_unit[0].flags));
/* The PORTS card optionally handles NL->CRLF */ /* The PORTS card optionally handles NL->CRLF */
@ -790,8 +743,8 @@ t_stat ports_xmt_svc(UNIT *uptr)
if (tmxr_putc_ln(&ports_ldsc[ln], 0xd) == SCPE_OK) { if (tmxr_putc_ln(&ports_ldsc[ln], 0xd) == SCPE_OK) {
wait = MIN(wait, ports_ldsc[ln].txdeltausecs); wait = MIN(wait, ports_ldsc[ln].txdeltausecs);
sim_debug(IO_DBG, &ports_dev, sim_debug(IO_DBG, &ports_dev,
"[%08x] [ports_xmt_svc] [LINE %d] XMIT (crlf): %02x (%c)\n", "[ports_xmt_svc] [LINE %d] XMIT (crlf): %02x (%c)\n",
R[NUM_PC], ln, 0xd, 0xd); ln, 0xd, 0xd);
/* Indicate that we're in a CRLF translation */ /* Indicate that we're in a CRLF translation */
ports_state[ln].crlf = TRUE; ports_state[ln].crlf = TRUE;
} }
@ -806,21 +759,21 @@ t_stat ports_xmt_svc(UNIT *uptr)
ports_state[ln].tx_chars--; ports_state[ln].tx_chars--;
ports_state[ln].tx_addr++; ports_state[ln].tx_addr++;
sim_debug(IO_DBG, &ports_dev, sim_debug(IO_DBG, &ports_dev,
"[%08x] [ports_xmt_svc] [LINE %d] XMIT: %02x (%c)\n", "[ports_xmt_svc] [LINE %d] XMIT: %02x (%c)\n",
R[NUM_PC], ln, c, c); ln, c, c);
} }
if (ports_state[ln].tx_chars == 0) { if (ports_state[ln].tx_chars == 0) {
sim_debug(TRACE_DBG, &ports_dev, sim_debug(TRACE_DBG, &ports_dev,
"[%08x] [ports_xmt_svc] Done with xmit, card=%d port=%d. Interrupting.\n", "[ports_xmt_svc] Done with xmit, card=%d port=%d. Interrupting.\n",
R[NUM_PC], cid, LPORT(ln)); slot, LPORT(ln));
centry.byte_count = ports_state[ln].tx_req_chars; centry.byte_count = ports_state[ln].tx_req_chars;
centry.subdevice = LPORT(ln); centry.subdevice = LPORT(ln);
centry.opcode = PPC_XMIT; centry.opcode = PPC_XMIT;
centry.address = ports_state[ln].tx_req_addr; centry.address = ports_state[ln].tx_req_addr;
app_data[0] = RC_FLU; app_data[0] = RC_FLU;
cio_cqueue(cid, CIO_STAT, PPQESIZE, &centry, app_data); cio_cqueue(slot, CIO_STAT, PPQESIZE, &centry, app_data);
CIO_SET_INT(cid); CIO_SET_INT(slot);
} }
} }
} }
@ -836,11 +789,24 @@ t_stat ports_xmt_svc(UNIT *uptr)
t_stat ports_attach(UNIT *uptr, CONST char *cptr) t_stat ports_attach(UNIT *uptr, CONST char *cptr)
{ {
TMLN *lp;
t_stat r; t_stat r;
int i;
sim_debug(TRACE_DBG, &ports_dev, "ports_attach()\n"); if ((sim_switches & SWMASK('M'))) {
tmxr_set_modem_control_passthru(&ports_desc);
}
tmxr_set_modem_control_passthru(&ports_desc); for (i = 0; i < ports_desc.lines; i++) {
sim_debug(TRACE_DBG, &ports_dev,
"[ports_reset] Setting up line %d...\n", i);
tmxr_set_line_output_unit(&ports_desc, i, &ports_unit[1]);
if (!ports_ldsc[i].conn) {
ports_ldsc[i].xmte = 1;
}
ports_ldsc[i].rcve = 0;
tmxr_set_config_line(&ports_ldsc[i], "9600-8N1");
}
r = tmxr_attach(&ports_desc, uptr, cptr); r = tmxr_attach(&ports_desc, uptr, cptr);
if (r != SCPE_OK) { if (r != SCPE_OK) {
@ -848,6 +814,11 @@ t_stat ports_attach(UNIT *uptr, CONST char *cptr)
return r; return r;
} }
for (i = 0; i < ports_desc.lines; i++) {
lp = &ports_ldsc[i];
tmxr_set_get_modem_bits(lp, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL);
}
return SCPE_OK; return SCPE_OK;
} }
@ -861,129 +832,7 @@ t_stat ports_detach(UNIT *uptr)
return r; return r;
} }
if (sim_is_active(&ports_unit[0])) {
sim_debug(TRACE_DBG, &ports_dev,
"[ports_detach] Stopping receive polling...\n");
sim_cancel(&ports_unit[0]);
}
tmxr_clear_modem_control_passthru(&ports_desc); tmxr_clear_modem_control_passthru(&ports_desc);
return SCPE_OK; return SCPE_OK;
} }
/*
* Useful routines for debugging request and completion queues
*/
t_stat ports_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
return ports_show_queue_common(st, uptr, val, desc, TRUE);
}
t_stat ports_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
return ports_show_queue_common(st, uptr, val, desc, FALSE);
}
static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val,
CONST void *desc, t_bool rq)
{
uint8 cid;
char *cptr = (char *) desc;
t_stat result;
uint32 ptr, size, no_rque, i, j;
uint8 op, dev, seq, cmdstat;
if (cptr) {
cid = (uint8) get_uint(cptr, 10, 12, &result);
if (result != SCPE_OK) {
return SCPE_ARG;
}
} else {
return SCPE_ARG;
}
/* If the card is not sysgen'ed, give up */
if (cio[cid].sysgen_s != CIO_SYSGEN) {
fprintf(st, "No card in slot %d, or card has not completed sysgen\n", cid);
return SCPE_ARG;
}
/* Get the top of the queue */
if (rq) {
ptr = cio[cid].rqp;
size = cio[cid].rqs;
no_rque = cio[cid].no_rque;
} else {
ptr = cio[cid].cqp;
size = cio[cid].cqs;
no_rque = 0; /* Not used */
}
if (rq) {
fprintf(st, "Dumping %d Request Queues\n", no_rque);
} else {
fprintf(st, "Dumping Completion Queue\n");
}
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "EXPRESS ENTRY:\n");
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2));
fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3));
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
ptr += 12;
if (rq) {
for (i = 0; i < no_rque; i++) {
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "REQUEST QUEUE %d\n", i);
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / 12);
fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / 12);
fprintf(st, "---------------------------------------------------------\n");
ptr += 4;
for (j = 0; j < size; j++) {
dev = pread_b(ptr + 2);
op = pread_b(ptr + 3);
seq = (dev & 0x40) >> 6;
cmdstat = (dev & 0x80) >> 7;
fprintf(st, "REQUEST ENTRY %d\n", j);
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
fprintf(st, " Subdevice: %d\n", dev & 0x3f);
fprintf(st, " Cmd/Stat: %d\n", cmdstat);
fprintf(st, " Seqbit: %d\n", seq);
fprintf(st, " Opcode: 0x%02x (%d)\n", op, op);
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
ptr += 12;
}
}
} else {
fprintf(st, "---------------------------------------------------------\n");
fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / 12);
fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / 12);
fprintf(st, "---------------------------------------------------------\n");
ptr += 4;
for (i = 0; i < size; i++) {
dev = pread_b(ptr + 2);
op = pread_b(ptr + 3);
seq = (dev & 0x40) >> 6;
cmdstat = (dev & 0x80) >> 7;
fprintf(st, "COMPLETION ENTRY %d\n", i);
fprintf(st, " Byte Count: %d\n", pread_h(ptr));
fprintf(st, " Subdevice: %d\n", dev & 0x3f);
fprintf(st, " Cmd/Stat: %d\n", cmdstat);
fprintf(st, " Seqbit: %d\n", seq);
fprintf(st, " Opcode: 0x%02x (%d)\n", op, op);
fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4));
fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8));
ptr += 12;
}
}
return SCPE_OK;
}

View file

@ -1,6 +1,6 @@
/* 3b2_ports.h: AT&T 3B2 Model 400 "PORTS" feature card /* 3b2_ports.h: CM195B 4-Port Serial CIO Card
Copyright (c) 2018, Seth J. Morabito Copyright (c) 2018-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -51,7 +51,8 @@
#define PORTS_IPL 10 #define PORTS_IPL 10
#define PORTS_VERSION 1 #define PORTS_VERSION 1
#define MAX_PORTS_CARDS 12 #define MAX_CARDS 8 /* Up to 8 PORTS cards with 32 lines total
supported */
#define PORTS_LINES 4 #define PORTS_LINES 4
#define PORTS_RCV_QUEUE 5 #define PORTS_RCV_QUEUE 5
@ -216,15 +217,13 @@ typedef struct {
t_stat ports_reset(DEVICE *dptr); t_stat ports_reset(DEVICE *dptr);
t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat ports_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat ports_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat ports_rcv_svc(UNIT *uptr); t_stat ports_rcv_svc(UNIT *uptr);
t_stat ports_xmt_svc(UNIT *uptr); t_stat ports_xmt_svc(UNIT *uptr);
t_stat ports_cio_svc(UNIT *uptr); t_stat ports_cio_svc(UNIT *uptr);
t_stat ports_attach(UNIT *uptr, CONST char *cptr); t_stat ports_attach(UNIT *uptr, CONST char *cptr);
t_stat ports_detach(UNIT *uptr); t_stat ports_detach(UNIT *uptr);
void ports_sysgen(uint8 cid); void ports_sysgen(uint8 slot);
void ports_express(uint8 cid); void ports_express(uint8 slot);
void ports_full(uint8 cid); void ports_full(uint8 slot);
#endif /* _3B2_PORTS_H_ */ #endif /* _3B2_PORTS_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_rev2_csr.c: AT&T 3B2 Rev 2 Control and Status Register /* 3b2_rev2_csr.c: ED System Board Control and Status Register
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -33,8 +33,9 @@
#include "3b2_cpu.h" #include "3b2_cpu.h"
#include "3b2_sys.h" #include "3b2_sys.h"
#include "3b2_timer.h" #include "3b2_timer.h"
#include "3b2_sys.h"
uint16 csr_data; CSR_DATA csr_data;
BITFIELD csr_bits[] = { BITFIELD csr_bits[] = {
BIT(IOF), BIT(IOF),
@ -94,8 +95,8 @@ uint32 csr_read(uint32 pa, size_t size)
uint32 reg = pa - CSRBASE; uint32 reg = pa - CSRBASE;
sim_debug(READ_MSG, &csr_dev, sim_debug(READ_MSG, &csr_dev,
"[%08x] CSR=%04x\n", "CSR=%04x\n",
R[NUM_PC], csr_data); csr_data);
switch (reg) { switch (reg) {
case 0x2: case 0x2:
@ -143,20 +144,15 @@ void csr_write(uint32 pa, uint32 val, size_t size)
break; break;
case 0x23: /* Set Inhibit Timers */ case 0x23: /* Set Inhibit Timers */
sim_debug(WRITE_MSG, &csr_dev, sim_debug(WRITE_MSG, &csr_dev,
"[%08x] SET INHIBIT TIMERS\n", R[NUM_PC]); "SET INHIBIT TIMERS\n");
csr_data |= CSRITIM; csr_data |= CSRITIM;
timer_gate(TIMER_INTERVAL, TRUE);
break; break;
case 0x27: /* Clear Inhibit Timers */ case 0x27: /* Clear Inhibit Timers */
sim_debug(WRITE_MSG, &csr_dev, sim_debug(WRITE_MSG, &csr_dev,
"[%08x] CLEAR INHIBIT TIMERS\n", R[NUM_PC]); "CLEAR INHIBIT TIMERS\n");
/* A side effect of clearing the timer inhibit bit is to cause
* a simulated "tick" of any active timers. This is a hack to
* make diagnostics pass. This is not 100% accurate, but it
* makes SVR3 and DGMON tests happy.
*/
timer_tick();
csr_data &= ~CSRITIM; csr_data &= ~CSRITIM;
timer_gate(TIMER_INTERVAL, FALSE);
break; break;
case 0x2b: /* Set Inhibit Faults */ case 0x2b: /* Set Inhibit Faults */
csr_data |= CSRIFLT; csr_data |= CSRIFLT;

View file

@ -1,6 +1,6 @@
/* 3b2_rev2_csr.h: AT&T 3B2 Rev 2 Control and Status Register /* 3b2_rev2_csr.h: ED System Board Control and Status Register
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -33,6 +33,8 @@
#include "3b2_defs.h" #include "3b2_defs.h"
typedef uint16 CSR_DATA;
/* CSR */ /* CSR */
t_stat csr_svc(UNIT *uptr); t_stat csr_svc(UNIT *uptr);
t_stat csr_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); t_stat csr_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw);
@ -41,6 +43,4 @@ t_stat csr_reset(DEVICE *dptr);
uint32 csr_read(uint32 pa, size_t size); uint32 csr_read(uint32 pa, size_t size);
void csr_write(uint32 pa, uint32 val, size_t size); void csr_write(uint32 pa, uint32 val, size_t size);
extern uint16 csr_data;
#endif /* 3B2_REV2_CSR_H_ */ #endif /* 3B2_REV2_CSR_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_rev2_defs.h: AT&T 3B2 Rev 2 (Model 400) Simulator Definitions /* 3b2_rev2_defs.h: Version 2 (3B2/400) Common Definitions
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -83,7 +83,7 @@
#define INT_SERR 0x01 /* IPL 15 */ #define INT_SERR 0x01 /* IPL 15 */
#define INT_CLOCK 0x02 /* IPL 15 */ #define INT_CLOCK 0x02 /* IPL 15 */
#define INT_DMA 0x04 /* IPL 13 */ #define INT_DMA 0x04 /* IPL 13 */
#define INT_UART 0x04 /* IPL 13 */ #define INT_UART 0x08 /* IPL 13 */
#define INT_DISK 0x10 /* IPL 11 */ #define INT_DISK 0x10 /* IPL 11 */
#define INT_FLOPPY 0x20 /* IPL 11 */ #define INT_FLOPPY 0x20 /* IPL 11 */
#define INT_PIR9 0x40 /* IPL 9 */ #define INT_PIR9 0x40 /* IPL 9 */

View file

@ -1,383 +0,0 @@
/* 3b2_rev2_mau.c: AT&T 3B2 Rev 2 (Model 400) Math Acceleration
Unit (WE32106 MAU) Header
Copyright (c) 2019, Seth J. Morabito
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Except as contained in this notice, the name of the author shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from the author.
---------------------------------------------------------------------
This file is part of a simulation of the WE32106 Math Acceleration
Unit. The WE32106 MAU is an IEEE-754 compabitle floating point
hardware math accelerator that was available as an optional
component on the AT&T 3B2/310 and 3B2/400, and a standard component
on the 3B2/500, 3B2/600, and 3B2/1000.
Portions of this code are derived from the SoftFloat 2c library by
John R. Hauser. Functions derived from SoftFloat 2c are clearly
marked in the comments.
Legal Notice
============
SoftFloat was written by John R. Hauser. Release 2c of SoftFloat
was made possible in part by the International Computer Science
Institute, located at Suite 600, 1947 Center Street, Berkeley,
California 94704. Funding was partially provided by the National
Science Foundation under grant MIP-9311980. The original version
of this code was written as part of a project to build a
fixed-point vector processor in collaboration with the University
of California at Berkeley, overseen by Profs. Nelson Morgan and
John Wawrzynek.
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable
effort has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS
THAT WILL AT TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS
SOFTWARE IS RESTRICTED TO PERSONS AND ORGANIZATIONS WHO CAN AND
WILL TOLERATE ALL LOSSES, COSTS, OR OTHER PROBLEMS THEY INCUR DUE
TO THE SOFTWARE WITHOUT RECOMPENSE FROM JOHN HAUSER OR THE
INTERNATIONAL COMPUTER SCIENCE INSTITUTE, AND WHO FURTHERMORE
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER
SCIENCE INSTITUTE (possibly via similar legal notice) AGAINST ALL
LOSSES, COSTS, OR OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND
CLIENTS DUE TO THE SOFTWARE, OR INCURRED BY ANYONE DUE TO A
DERIVATIVE WORK THEY CREATE USING ANY PART OF THE SOFTWARE.
The following are expressly permitted, even for commercial
purposes:
(1) distribution of SoftFloat in whole or in part, as long as this
and other legal notices remain and are prominent, and provided also
that, for a partial distribution, prominent notice is given that it
is a subset of the original; and
(2) inclusion or use of SoftFloat in whole or in part in a
derivative work, provided that the use restrictions above are met
and the minimal documentation requirements stated in the source
code are satisfied.
---------------------------------------------------------------------
Data Types
==========
The WE32106 MAU stores values using IEEE-754 1985 types, plus a
non-standard Decimal type.
- Decimal Type - 18 BCD digits long. Each digit is 4 bits wide.
Sign is encoded in byte 0.
3322 2222 2222 1111 1111 1100 0000 0000
1098 7654 3210 9876 5432 1098 7654 3210
+-------------------+----+----+----+----+
| unused | D18| D17| D16| D15| High Word
+----+----+----+----+----+----+----+----+
| D14| D13| D12| D11| D10| D09| D08| D07| Middle Word
+----+----+----+----+----+----+----+----+
| D06| D05| D04| D03| D02| D01| D00|sign| Low Word
+----+----+----+----+----+----+----+----+
Sign: 0: Positive Infinity 10: Positive Number
1: Negative Infinity 11: Negative Number
2: Positive NaN 12: Positive Number
3: Negative NaN 13: Negative Number
4-9: Trapping NaN 14-15: Positive Number
- Extended Precision (80-bit) - exponent biased by 16383
3 322222222221111 1 111110000000000
1 098765432109876 5 432109876543210
+-----------------+-+---------------+
| unused |S| Exponent | High Word
+-+---------------+-+---------------+
|J| Fraction (high word) | Middle Word
+-+---------------------------------+
| Fraction (low word) | Low Word
+-----------------------------------+
- Double Precision (64-bit) - exponent biased by 1023
3 3222222222 211111111110000000000
1 0987654321 098765432109876543210
+-+----------+---------------------+
|S| Exponent | Fraction (high) | High Word
+-+----------+---------------------+
| Fraction (low) | Low Word
+----------------------------------+
- Single Precision (32-bit) - exponent biased by 127
3 32222222 22211111111110000000000
1 09876543 21098765432109876543210
+-+--------+-----------------------+
|S| Exp | Fraction |
+-+--------+-----------------------+
*/
#ifndef _3B2_REV2_MAU_H_
#define _3B2_REV2_MAU_H_
#include "sim_defs.h"
#define SRC_LEN_INVALID 0
#define SRC_LEN_SINGLE 1
#define SRC_LEN_DOUBLE 2
#define SRC_LEN_TRIPLE 3
#define MAU_ASR_RC_SHIFT 22
#define MAU_ASR_PR 0x20u /* Partial Remainder */
#define MAU_ASR_QS 0x40u /* Divide By Zero Sticky */
#define MAU_ASR_US 0x80u /* Underflow Sticky */
#define MAU_ASR_OS 0x100u /* Overflow Sticky */
#define MAU_ASR_IS 0x200u /* Invalid Operation Sticky */
#define MAU_ASR_PM 0x400u /* Inexact Mask */
#define MAU_ASR_QM 0x800u /* Divide by Zero Mask */
#define MAU_ASR_UM 0x1000u /* Underflow Mask */
#define MAU_ASR_OM 0x2000u /* Overflow Mask */
#define MAU_ASR_IM 0x4000u /* Invalid Operation Mask */
#define MAU_ASR_UO 0x10000u /* Unordered */
#define MAU_ASR_CSC 0x20000u /* Context Switch Control */
#define MAU_ASR_PS 0x40000u /* Inexact Sticky */
#define MAU_ASR_IO 0x80000u /* Integer Overflow */
#define MAU_ASR_Z 0x100000u /* Zero Flag */
#define MAU_ASR_N 0x200000u /* Negative Flag */
#define MAU_ASR_RC 0x400000u /* Round Control */
#define MAU_ASR_NTNC 0x1000000u /* Nontrapping NaN Control */
#define MAU_ASR_ECP 0x2000000u /* Exception Condition */
#define MAU_ASR_RA 0x80000000u /* Result Available */
#define MAU_RC_RN 0 /* Round toward Nearest */
#define MAU_RC_RP 1 /* Round toward Plus Infin. */
#define MAU_RC_RM 2 /* Round toward Neg. Infin. */
#define MAU_RC_RZ 3 /* Round toward Zero */
#define SFP_SIGN(V) (((V) >> 31) & 1)
#define SFP_EXP(V) (((V) >> 23) & 0xff)
#define SFP_FRAC(V) ((V) & 0x7fffff)
#define DFP_SIGN(V) (((V) >> 63) & 1)
#define DFP_EXP(V) (((V) >> 52) & 0x7ff)
#define DFP_FRAC(V) ((V) & 0xfffffffffffffull)
#define XFP_SIGN(V) (((V)->sign_exp >> 15) & 1)
#define XFP_EXP(V) ((V)->sign_exp & 0x7fff)
#define XFP_FRAC(V) ((V)->frac)
#define XFP_IS_NORMAL(V) ((V)->frac & 0x8000000000000000ull)
#define DEFAULT_XFP_NAN_SIGN_EXP 0xffff
#define DEFAULT_XFP_NAN_FRAC 0xc000000000000000ull
#define SFP_IS_TRAPPING_NAN(V) (((((V) >> 22) & 0x1ff) == 0x1fe) && \
((V) & 0x3fffff))
#define DFP_IS_TRAPPING_NAN(V) (((((V) >> 51) & 0xfff) == 0xffe) && \
((V) & 0x7ffffffffffffull))
#define XFP_IS_NAN(V) ((((V)->sign_exp & 0x7fff) == 0x7fff) && \
(t_uint64)((V)->frac << 1))
#define XFP_IS_TRAPPING_NAN(V) ((((V)->sign_exp) & 0x7fff) && \
((((V)->frac) & ~(0x4000000000000000ull)) << 1) && \
(((V)->frac) == ((V)->frac & ~(0x4000000000000000ull))))
#define PACK_DFP(SIGN,EXP,FRAC) ((((t_uint64)(SIGN))<<63) + \
(((t_uint64)(EXP))<<52) + \
((t_uint64)(FRAC)))
#define PACK_SFP(SIGN,EXP,FRAC) (((uint32)(SIGN)<<31) + \
((uint32)(EXP)<<23) + \
((uint32)(FRAC)))
#define PACK_XFP(SIGN,EXP,FRAC,V) do { \
(V)->frac = (FRAC); \
(V)->sign_exp = ((uint16)(SIGN) << 15) + (EXP); \
(V)->s = FALSE; \
} while (0)
#define PACK_XFP_S(SIGN,EXP,FRAC,S,V) do { \
(V)->frac = (FRAC); \
(V)->sign_exp = ((uint16)(SIGN) << 15) + (EXP); \
(V)->s = (S) != 0; \
} while (0)
#define MAU_RM ((RM)((mau_state.asr >> 22) & 3))
typedef enum {
M_ADD = 0x02,
M_SUB = 0x03,
M_DIV = 0x04,
M_REM = 0x05,
M_MUL = 0x06,
M_MOVE = 0x07,
M_RDASR = 0x08,
M_WRASR = 0x09,
M_CMP = 0x0a,
M_CMPE = 0x0b,
M_ABS = 0x0c,
M_SQRT = 0x0d,
M_RTOI = 0x0e,
M_FTOI = 0x0f,
M_ITOF = 0x10,
M_DTOF = 0x11,
M_FTOD = 0x12,
M_NOP = 0x13,
M_EROF = 0x14,
M_NEG = 0x17,
M_LDR = 0x18,
M_CMPS = 0x1a,
M_CMPES = 0x1b
} mau_opcodes;
typedef enum {
M_OP3_F0_SINGLE,
M_OP3_F1_SINGLE,
M_OP3_F2_SINGLE,
M_OP3_F3_SINGLE,
M_OP3_F0_DOUBLE,
M_OP3_F1_DOUBLE,
M_OP3_F2_DOUBLE,
M_OP3_F3_DOUBLE,
M_OP3_F0_TRIPLE,
M_OP3_F1_TRIPLE,
M_OP3_F2_TRIPLE,
M_OP3_F3_TRIPLE,
M_OP3_MEM_SINGLE,
M_OP3_MEM_DOUBLE,
M_OP3_MEM_TRIPLE,
M_OP3_NONE
} op3_spec;
/* Specifier bytes for Operands 1 and 2 */
typedef enum {
M_OP_F0,
M_OP_F1,
M_OP_F2,
M_OP_F3,
M_OP_MEM_SINGLE,
M_OP_MEM_DOUBLE,
M_OP_MEM_TRIPLE,
M_OP_NONE
} op_spec;
/*
* 128-bit value
*/
typedef struct {
t_uint64 low;
t_uint64 high;
} t_mau_128;
/*
* Not-a-Number Type
*/
typedef struct {
t_bool sign;
t_uint64 high;
t_uint64 low;
} T_NAN;
/*
* Extended Precision (80 bits).
*
* Note that an undocumented feature of the WE32106 requires the use
* of uint32 rather than uint16 for the sign and exponent components
* of the struct. Although bits 80-95 are "unused", several
* diagnostics actually expect these bits to be moved and preserved on
* word transfers. They are ignored and discarded by math routines,
* however.
*
* The 's' field holds the Sticky bit used by rounding.
*/
typedef struct {
uint32 sign_exp; /* Sign and Exponent */
t_uint64 frac; /* Fraction/Significand/Mantissa */
t_bool s; /* Sticky bit */
} XFP;
typedef struct {
uint32 h;
t_uint64 l;
} DEC;
/*
* Supported rounding modes.
*/
typedef enum {
ROUND_NEAREST,
ROUND_PLUS_INF,
ROUND_MINUS_INF,
ROUND_ZERO
} RM;
/*
* Double Precision (64 bits)
*/
typedef t_uint64 DFP;
/*
* Single Precision (32 bits)
*/
typedef uint32 SFP;
/*
* MAU state
*/
typedef struct {
uint32 cmd;
/* Exception */
uint32 exception;
/* Status register */
uint32 asr;
t_bool trapping_nan;
/* Generate a Non-Trapping NaN */
t_bool ntnan;
/* Source (from broadcast) */
uint32 src;
/* Destination (from broadcast) */
uint32 dst;
uint8 opcode;
uint8 op1;
uint8 op2;
uint8 op3;
/* Data Register */
XFP dr;
/* Operand Registers */
XFP f0;
XFP f1;
XFP f2;
XFP f3;
} MAU_STATE;
t_stat mau_reset(DEVICE *dptr);
t_stat mau_attach(UNIT *uptr, CONST char *cptr);
t_stat mau_detach(UNIT *uptr);
t_stat mau_broadcast(uint32 cmd, uint32 src, uint32 dst);
CONST char *mau_description(DEVICE *dptr);
t_stat mau_broadcast(uint32 cmd, uint32 src, uint32 dst);
#endif /* _3B2_REV2_MAU_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_rev2_mmu.c: AT&T 3B2 Rev 2 (Model 400) MMU (WE32101) Implementation /* 3b2_rev2_mmu.c: WE32101 MMU
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -32,6 +32,7 @@
#include "3b2_cpu.h" #include "3b2_cpu.h"
#include "3b2_mmu.h" #include "3b2_mmu.h"
#include "3b2_sys.h" #include "3b2_sys.h"
#include "3b2_mem.h"
UNIT mmu_unit = { UDATA(NULL, 0, 0) }; UNIT mmu_unit = { UDATA(NULL, 0, 0) };
@ -260,13 +261,16 @@ static SIM_INLINE t_stat mmu_check_perm(uint8 flags, uint8 r_acc)
case 0: /* No Access */ case 0: /* No Access */
return SCPE_NXM; return SCPE_NXM;
case 1: /* Exec Only */ case 1: /* Exec Only */
if (r_acc != ACC_IF && r_acc != ACC_IFAD) { if (r_acc != ACC_IF &&
r_acc != ACC_IFAD) {
return SCPE_NXM; return SCPE_NXM;
} }
return SCPE_OK; return SCPE_OK;
case 2: /* Read / Execute */ case 2: /* Read / Execute */
if (r_acc != ACC_AF && r_acc != ACC_OF && if (r_acc != ACC_IF &&
r_acc != ACC_IF && r_acc != ACC_IFAD && r_acc != ACC_IFAD &&
r_acc != ACC_OF &&
r_acc != ACC_AF &&
r_acc != ACC_MT) { r_acc != ACC_MT) {
return SCPE_NXM; return SCPE_NXM;
} }
@ -288,8 +292,8 @@ static SIM_INLINE void mmu_update_sd(uint32 va, uint32 mask)
/* We go back to main memory to find the SD because the SD may /* We go back to main memory to find the SD because the SD may
have been loaded from cache, which is lossy. */ have been loaded from cache, which is lossy. */
sd0 = pread_w(SD_ADDR(va)); sd0 = pread_w(SD_ADDR(va), BUS_PER);
pwrite_w(SD_ADDR(va), sd0|mask); pwrite_w(SD_ADDR(va), sd0|mask, BUS_PER);
/* There is no 'R' bit in the SD cache, only an 'M' bit. */ /* There is no 'R' bit in the SD cache, only an 'M' bit. */
if (mask == SD_M_MASK) { if (mask == SD_M_MASK) {
@ -310,8 +314,8 @@ static SIM_INLINE void mmu_update_pd(uint32 va, uint32 pd_addr, uint32 mask)
/* We go back to main memory to find the PD because the PD may /* We go back to main memory to find the PD because the PD may
have been loaded from cache, which is lossy. */ have been loaded from cache, which is lossy. */
pd = pread_w(pd_addr); pd = pread_w(pd_addr, BUS_PER);
pwrite_w(pd_addr, pd|mask); pwrite_w(pd_addr, pd|mask, BUS_PER);
/* Update in the cache */ /* Update in the cache */
@ -349,50 +353,50 @@ uint32 mmu_read(uint32 pa, size_t size)
case MMU_SDCL: case MMU_SDCL:
data = mmu_state.sdcl[offset]; data = mmu_state.sdcl[offset];
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] [pa=%08x] MMU_SDCL[%d] = %08x\n", "[pa=%08x] MMU_SDCL[%d] = %08x\n",
R[NUM_PC], pa, offset, data); pa, offset, data);
break; break;
case MMU_SDCH: case MMU_SDCH:
data = mmu_state.sdch[offset]; data = mmu_state.sdch[offset];
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] MMU_SDCH[%d] = %08x\n", "MMU_SDCH[%d] = %08x\n",
R[NUM_PC], offset, data); offset, data);
break; break;
case MMU_PDCRL: case MMU_PDCRL:
data = mmu_state.pdcrl[offset]; data = mmu_state.pdcrl[offset];
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] MMU_PDCRL[%d] = %08x\n", "MMU_PDCRL[%d] = %08x\n",
R[NUM_PC], offset, data); offset, data);
break; break;
case MMU_PDCRH: case MMU_PDCRH:
data = mmu_state.pdcrh[offset]; data = mmu_state.pdcrh[offset];
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] MMU_PDCRH[%d] = %08x\n", "MMU_PDCRH[%d] = %08x\n",
R[NUM_PC], offset, data); offset, data);
break; break;
case MMU_PDCLL: case MMU_PDCLL:
data = mmu_state.pdcll[offset]; data = mmu_state.pdcll[offset];
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] MMU_PDCLL[%d] = %08x\n", "MMU_PDCLL[%d] = %08x\n",
R[NUM_PC], offset, data); offset, data);
break; break;
case MMU_PDCLH: case MMU_PDCLH:
data = mmu_state.pdclh[offset]; data = mmu_state.pdclh[offset];
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] MMU_PDCLH[%d] = %08x\n", "MMU_PDCLH[%d] = %08x\n",
R[NUM_PC], offset, data); offset, data);
break; break;
case MMU_SRAMA: case MMU_SRAMA:
data = mmu_state.sra[offset]; data = mmu_state.sra[offset];
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] MMU_SRAMA[%d] = %08x\n", "MMU_SRAMA[%d] = %08x\n",
R[NUM_PC], offset, data); offset, data);
break; break;
case MMU_SRAMB: case MMU_SRAMB:
data = mmu_state.srb[offset]; data = mmu_state.srb[offset];
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] MMU_SRAMB[%d] = %08x\n", "MMU_SRAMB[%d] = %08x\n",
R[NUM_PC], offset, data); offset, data);
break; break;
case MMU_FC: case MMU_FC:
data = mmu_state.fcode; data = mmu_state.fcode;
@ -403,14 +407,14 @@ uint32 mmu_read(uint32 pa, size_t size)
case MMU_CONF: case MMU_CONF:
data = mmu_state.conf & 0x7; data = mmu_state.conf & 0x7;
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] MMU_CONF = %08x\n", "MMU_CONF = %08x\n",
R[NUM_PC], data); data);
break; break;
case MMU_VAR: case MMU_VAR:
data = mmu_state.var; data = mmu_state.var;
sim_debug(READ_MSG, &mmu_dev, sim_debug(READ_MSG, &mmu_dev,
"[%08x] MMU_VAR = %08x\n", "MMU_VAR = %08x\n",
R[NUM_PC], data); data);
break; break;
} }
@ -466,8 +470,8 @@ void mmu_write(uint32 pa, uint32 val, size_t size)
mmu_state.sec[offset].addr = val & 0xffffffe0; mmu_state.sec[offset].addr = val & 0xffffffe0;
/* We flush the entire section on writing SRAMA */ /* We flush the entire section on writing SRAMA */
sim_debug(WRITE_MSG, &mmu_dev, sim_debug(WRITE_MSG, &mmu_dev,
"[%08x] MMU_SRAMA[%d] = %08x\n", "MMU_SRAMA[%d] = %08x\n",
R[NUM_PC], offset, val); offset, val);
flush_cache_sec((uint8) offset); flush_cache_sec((uint8) offset);
break; break;
case MMU_SRAMB: case MMU_SRAMB:
@ -476,8 +480,8 @@ void mmu_write(uint32 pa, uint32 val, size_t size)
mmu_state.sec[offset].len = (val >> 10) & 0x1fff; mmu_state.sec[offset].len = (val >> 10) & 0x1fff;
/* We do not flush the cache on writing SRAMB */ /* We do not flush the cache on writing SRAMB */
sim_debug(WRITE_MSG, &mmu_dev, sim_debug(WRITE_MSG, &mmu_dev,
"[%08x] MMU_SRAMB[%d] = %08x (len=%06x)\n", "MMU_SRAMB[%d] = %08x (len=%06x)\n",
R[NUM_PC], offset, val, mmu_state.sec[offset].len); offset, val, mmu_state.sec[offset].len);
break; break;
case MMU_FC: case MMU_FC:
mmu_state.fcode = val; mmu_state.fcode = val;
@ -518,20 +522,20 @@ t_stat mmu_get_sd(uint32 va, uint8 r_acc, t_bool fc,
if (SSL(va) > SRAMB_LEN(va)) { if (SSL(va) > SRAMB_LEN(va)) {
MMU_FAULT(MMU_F_SDTLEN); MMU_FAULT(MMU_F_SDTLEN);
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] SDT Length Fault. sramb_len=%x ssl=%x va=%08x\n", "SDT Length Fault. sramb_len=%x ssl=%x va=%08x\n",
R[NUM_PC], SRAMB_LEN(va), SSL(va), va); SRAMB_LEN(va), SSL(va), va);
return SCPE_NXM; return SCPE_NXM;
} }
/* sd0 contains the segment descriptor, sd1 contains a pointer to /* sd0 contains the segment descriptor, sd1 contains a pointer to
the PDT or Segment */ the PDT or Segment */
*sd0 = pread_w(SD_ADDR(va)); *sd0 = pread_w(SD_ADDR(va), BUS_PER);
*sd1 = pread_w(SD_ADDR(va) + 4); *sd1 = pread_w(SD_ADDR(va) + 4, BUS_PER);
if (!SD_VALID(*sd0)) { if (!SD_VALID(*sd0)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Invalid Segment Descriptor. va=%08x sd0=%08x\n", "Invalid Segment Descriptor. va=%08x sd0=%08x\n",
R[NUM_PC], va, *sd0); va, *sd0);
MMU_FAULT(MMU_F_INV_SD); MMU_FAULT(MMU_F_INV_SD);
return SCPE_NXM; return SCPE_NXM;
} }
@ -547,14 +551,14 @@ t_stat mmu_get_sd(uint32 va, uint8 r_acc, t_bool fc,
if (!SD_PRESENT(*sd0)) { if (!SD_PRESENT(*sd0)) {
if (SD_CONTIG(*sd0)) { if (SD_CONTIG(*sd0)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Segment Not Present. va=%08x", "Segment Not Present. va=%08x",
R[NUM_PC], va); va);
MMU_FAULT(MMU_F_SEG_NOT_PRES); MMU_FAULT(MMU_F_SEG_NOT_PRES);
return SCPE_NXM; return SCPE_NXM;
} else { } else {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] PDT Not Present. va=%08x", "PDT Not Present. va=%08x",
R[NUM_PC], va); va);
MMU_FAULT(MMU_F_PDT_NOT_PRES); MMU_FAULT(MMU_F_PDT_NOT_PRES);
return SCPE_NXM; return SCPE_NXM;
} }
@ -582,15 +586,15 @@ t_stat mmu_get_pd(uint32 va, uint8 r_acc, t_bool fc,
/* Bounds checking on length */ /* Bounds checking on length */
if ((PSL(va) * 4) >= MAX_OFFSET(sd0)) { if ((PSL(va) * 4) >= MAX_OFFSET(sd0)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] PDT Length Fault. " "PDT Length Fault. "
"PDT Offset=%08x Max Offset=%08x va=%08x\n", "PDT Offset=%08x Max Offset=%08x va=%08x\n",
R[NUM_PC], (PSL(va) * 4), (PSL(va) * 4),
MAX_OFFSET(sd0), va); MAX_OFFSET(sd0), va);
MMU_FAULT(MMU_F_PDTLEN); MMU_FAULT(MMU_F_PDTLEN);
return SCPE_NXM; return SCPE_NXM;
} }
*pd = pread_w(pd_addr); *pd = pread_w(pd_addr, BUS_PER);
/* Copy the access flags from the SD */ /* Copy the access flags from the SD */
*pd_acc = SD_ACC(sd0); *pd_acc = SD_ACC(sd0);
@ -614,23 +618,21 @@ t_stat mmu_decode_contig(uint32 va, uint8 r_acc,
/* Update R and M bits if configured */ /* Update R and M bits if configured */
if (SHOULD_UPDATE_SD_R_BIT(sd0)) { if (SHOULD_UPDATE_SD_R_BIT(sd0)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Updating R bit in SD\n", "Updating R bit in SD\n");
R[NUM_PC]);
mmu_update_sd(va, SD_R_MASK); mmu_update_sd(va, SD_R_MASK);
} }
if (SHOULD_UPDATE_SD_M_BIT(sd0)) { if (SHOULD_UPDATE_SD_M_BIT(sd0)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Updating M bit in SD\n", "Updating M bit in SD\n");
R[NUM_PC]);
mmu_update_sd(va, SD_M_MASK); mmu_update_sd(va, SD_M_MASK);
} }
/* Generate object trap if needed */ /* Generate object trap if needed */
if (SD_TRAP(sd0)) { if (SD_TRAP(sd0)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Object Trap. va=%08x", "Object Trap. va=%08x",
R[NUM_PC], va); va);
MMU_FAULT(MMU_F_OTRAP); MMU_FAULT(MMU_F_OTRAP);
return SCPE_NXM; return SCPE_NXM;
} }
@ -647,9 +649,9 @@ t_stat mmu_decode_paged(uint32 va, uint8 r_acc, t_bool fc,
/* If the PD is not marked present, fail */ /* If the PD is not marked present, fail */
if (!PD_PRESENT(pd)) { if (!PD_PRESENT(pd)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Page Not Present. " "Page Not Present. "
"pd=%08x r_acc=%x va=%08x\n", "pd=%08x r_acc=%x va=%08x\n",
R[NUM_PC], pd, r_acc, va); pd, r_acc, va);
MMU_FAULT(MMU_F_PAGE_NOT_PRES); MMU_FAULT(MMU_F_PAGE_NOT_PRES);
return SCPE_NXM; return SCPE_NXM;
} }
@ -659,8 +661,8 @@ t_stat mmu_decode_paged(uint32 va, uint8 r_acc, t_bool fc,
the 'W' bit is set, trigger a write fault */ the 'W' bit is set, trigger a write fault */
if ((r_acc == ACC_W || r_acc == ACC_IR) && PD_WFAULT(pd)) { if ((r_acc == ACC_W || r_acc == ACC_IR) && PD_WFAULT(pd)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Page Write Fault. va=%08x\n", "Page Write Fault. va=%08x\n",
R[NUM_PC], va); va);
MMU_FAULT(MMU_F_PW); MMU_FAULT(MMU_F_PW);
return SCPE_NXM; return SCPE_NXM;
} }
@ -668,16 +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 this is a write, modify the M bit */
if (SHOULD_UPDATE_PD_M_BIT(pd)) { if (SHOULD_UPDATE_PD_M_BIT(pd)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Updating M bit in PD\n", "Updating M bit in PD\n");
R[NUM_PC]);
mmu_update_pd(va, PD_LOC(sd1, va), PD_M_MASK); mmu_update_pd(va, PD_LOC(sd1, va), PD_M_MASK);
} }
/* Modify the R bit and write it back */ /* Modify the R bit and write it back */
if (SHOULD_UPDATE_PD_R_BIT(pd)) { if (SHOULD_UPDATE_PD_R_BIT(pd)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Updating R bit in PD\n", "Updating R bit in PD\n");
R[NUM_PC]);
mmu_update_pd(va, PD_LOC(sd1, va), PD_R_MASK); mmu_update_pd(va, PD_LOC(sd1, va), PD_R_MASK);
} }
} }
@ -717,29 +717,29 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa)
if (sd_cached == SCPE_OK && pd_cached != SCPE_OK) { if (sd_cached == SCPE_OK && pd_cached != SCPE_OK) {
if (SD_PAGED(sd0) && mmu_get_pd(va, r_acc, fc, sd0, sd1, &pd, &pd_acc) != SCPE_OK) { if (SD_PAGED(sd0) && mmu_get_pd(va, r_acc, fc, sd0, sd1, &pd, &pd_acc) != SCPE_OK) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Could not get PD (partial miss). r_acc=%d, fc=%d, va=%08x\n", "Could not get PD (partial miss). r_acc=%d, fc=%d, va=%08x\n",
R[NUM_PC], r_acc, fc, va); r_acc, fc, va);
return SCPE_NXM; return SCPE_NXM;
} }
} else if (sd_cached != SCPE_OK && pd_cached == SCPE_OK) { } else if (sd_cached != SCPE_OK && pd_cached == SCPE_OK) {
if (mmu_get_sd(va, r_acc, fc, &sd0, &sd1) != SCPE_OK) { if (mmu_get_sd(va, r_acc, fc, &sd0, &sd1) != SCPE_OK) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Could not get SD (partial miss). r_acc=%d, fc=%d, va=%08x\n", "Could not get SD (partial miss). r_acc=%d, fc=%d, va=%08x\n",
R[NUM_PC], r_acc, fc, va); r_acc, fc, va);
return SCPE_NXM; return SCPE_NXM;
} }
} else if (sd_cached != SCPE_OK && pd_cached != SCPE_OK) { } else if (sd_cached != SCPE_OK && pd_cached != SCPE_OK) {
if (mmu_get_sd(va, r_acc, fc, &sd0, &sd1) != SCPE_OK) { if (mmu_get_sd(va, r_acc, fc, &sd0, &sd1) != SCPE_OK) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Could not get SD (full miss). r_acc=%d, fc=%d, va=%08x\n", "Could not get SD (full miss). r_acc=%d, fc=%d, va=%08x\n",
R[NUM_PC], r_acc, fc, va); r_acc, fc, va);
return SCPE_NXM; return SCPE_NXM;
} }
if (SD_PAGED(sd0) && mmu_get_pd(va, r_acc, fc, sd0, sd1, &pd, &pd_acc) != SCPE_OK) { if (SD_PAGED(sd0) && mmu_get_pd(va, r_acc, fc, sd0, sd1, &pd, &pd_acc) != SCPE_OK) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Could not get PD (full miss). r_acc=%d, fc=%d, va=%08x\n", "Could not get PD (full miss). r_acc=%d, fc=%d, va=%08x\n",
R[NUM_PC], r_acc, fc, va); r_acc, fc, va);
return SCPE_NXM; return SCPE_NXM;
} }
} }
@ -747,18 +747,17 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa)
if (SD_PAGED(sd0)) { if (SD_PAGED(sd0)) {
if (fc && mmu_check_perm(pd_acc, r_acc) != SCPE_OK) { if (fc && mmu_check_perm(pd_acc, r_acc) != SCPE_OK) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] PAGED: NO ACCESS TO MEMORY AT %08x.\n" "PAGED: NO ACCESS TO MEMORY AT %08x.\n"
"\t\tcpu_cm=%d r_acc=%x pd_acc=%02x\n" "\t\tcpu_cm=%d r_acc=%x pd_acc=%02x\n"
"\t\tpd=%08x psw=%08x\n", "\t\tpd=%08x psw=%08x\n",
R[NUM_PC], va, CPU_CM, r_acc, pd_acc, va, CPU_CM, r_acc, pd_acc,
pd, R[NUM_PSW]); pd, R[NUM_PSW]);
MMU_FAULT(MMU_F_ACC); MMU_FAULT(MMU_F_ACC);
return SCPE_NXM; return SCPE_NXM;
} }
if (PD_LAST(pd) && (PSL_C(va) | POT(va)) >= MAX_OFFSET(sd0)) { if (PD_LAST(pd) && (PSL_C(va) | POT(va)) >= MAX_OFFSET(sd0)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] PAGED: Segment Offset Fault.\n", "PAGED: Segment Offset Fault.\n");
R[NUM_PC]);
MMU_FAULT(MMU_F_SEG_OFFSET); MMU_FAULT(MMU_F_SEG_OFFSET);
return SCPE_NXM; return SCPE_NXM;
} }
@ -766,20 +765,19 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa)
} else { } else {
if (fc && mmu_check_perm(SD_ACC(sd0), r_acc) != SCPE_OK) { if (fc && mmu_check_perm(SD_ACC(sd0), r_acc) != SCPE_OK) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] CONTIGUOUS: NO ACCESS TO MEMORY AT %08x.\n" "CONTIGUOUS: NO ACCESS TO MEMORY AT %08x.\n"
"\t\tsd0=%08x sd0_addr=%08x\n" "\t\tsd0=%08x sd0_addr=%08x\n"
"\t\tcpu_cm=%d acc_req=%x sd_acc=%02x\n", "\t\tcpu_cm=%d acc_req=%x sd_acc=%02x\n",
R[NUM_PC], va, va, sd0, SD_ADDR(va),
sd0, SD_ADDR(va),
CPU_CM, r_acc, SD_ACC(sd0)); CPU_CM, r_acc, SD_ACC(sd0));
MMU_FAULT(MMU_F_ACC); MMU_FAULT(MMU_F_ACC);
return SCPE_NXM; return SCPE_NXM;
} }
if (SOT(va) >= MAX_OFFSET(sd0)) { if (SOT(va) >= MAX_OFFSET(sd0)) {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] CONTIGUOUS: Segment Offset Fault. " "CONTIGUOUS: Segment Offset Fault. "
"sd0=%08x sd_addr=%08x SOT=%08x len=%08x va=%08x\n", "sd0=%08x sd_addr=%08x SOT=%08x len=%08x va=%08x\n",
R[NUM_PC], sd0, SD_ADDR(va), SOT(va), sd0, SD_ADDR(va), SOT(va),
MAX_OFFSET(sd0), va); MAX_OFFSET(sd0), va);
MMU_FAULT(MMU_F_SEG_OFFSET); MMU_FAULT(MMU_F_SEG_OFFSET);
return SCPE_NXM; return SCPE_NXM;
@ -807,16 +805,14 @@ uint32 mmu_xlate_addr(uint32 va, uint8 r_acc)
void mmu_enable() void mmu_enable()
{ {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Enabling MMU.\n", "Enabling MMU.\n");
R[NUM_PC]);
mmu_state.enabled = TRUE; mmu_state.enabled = TRUE;
} }
void mmu_disable() void mmu_disable()
{ {
sim_debug(EXECUTE_MSG, &mmu_dev, sim_debug(EXECUTE_MSG, &mmu_dev,
"[%08x] Disabling MMU.\n", "Disabling MMU.\n");
R[NUM_PC]);
mmu_state.enabled = FALSE; mmu_state.enabled = FALSE;
} }

View file

@ -1,6 +1,6 @@
/* 3b2_rev2_mmu.c: AT&T 3B2 Rev 2 (Model 400) MMU (WE32101) Header /* 3b2_rev2_mmu.c: WE32101 MMU
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -326,18 +326,6 @@ uint32 mmu_read(uint32 pa, size_t size);
void mmu_write(uint32 pa, uint32 val, size_t size); void mmu_write(uint32 pa, uint32 val, size_t size);
CONST char *mmu_description(DEVICE *dptr); CONST char *mmu_description(DEVICE *dptr);
/* Physical memory read/write */
uint8 pread_b(uint32 pa);
uint16 pread_h(uint32 pa);
uint32 pread_w(uint32 pa);
uint32 pread_w_u(uint32 pa);
void pwrite_b(uint32 pa, uint8 val);
void pwrite_h(uint32 pa, uint16 val);
void pwrite_w(uint32 pa, uint32 val);
/* TODO: REMOVE AFTER DEBUGGING */
uint32 safe_read_w(uint32 va);
/* Virtual memory translation */ /* Virtual memory translation */
uint32 mmu_xlate_addr(uint32 va, uint8 r_acc); uint32 mmu_xlate_addr(uint32 va, uint8 r_acc);
t_stat mmu_decode_vaddr(uint32 vaddr, uint8 r_acc, t_stat mmu_decode_vaddr(uint32 vaddr, uint8 r_acc,
@ -361,19 +349,6 @@ t_stat mmu_decode_vaddr(uint32 vaddr, uint8 r_acc,
#define SHOULD_UPDATE_PD_M_BIT(pd) \ #define SHOULD_UPDATE_PD_M_BIT(pd) \
(r_acc == ACC_W && !((pd) & PD_M_MASK)) (r_acc == ACC_W && !((pd) & PD_M_MASK))
/* Dispatch to the MMU when enabled, or to physical RW when
disabled */
uint8 read_b(uint32 va, uint8 r_acc);
uint16 read_h(uint32 va, uint8 r_acc);
uint32 read_w(uint32 va, uint8 r_acc);
void write_b(uint32 va, uint8 val);
void write_h(uint32 va, uint16 val);
void write_w(uint32 va, uint32 val);
t_bool addr_is_rom(uint32 pa);
t_bool addr_is_mem(uint32 pa);
t_bool addr_is_io(uint32 pa);
t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa); t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa);
void mmu_enable(); void mmu_enable();
void mmu_disable(); void mmu_disable();

View file

@ -1,6 +1,6 @@
/* 3b2_rev2_sys.c: AT&T 3B2 Rev 2 (Model 400) system definition /* 3b2_rev2_sys.c: Version 2 (3B2/400) System Definition
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation

View file

@ -1,401 +0,0 @@
/* 3b2_rev2_timer.c: 8253 Interval Timer
Copyright (c) 2017, Seth J. Morabito
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Except as contained in this notice, the name of the author shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from the author.
*/
/*
* 8253 Timer.
*
* The 8253 Timer IC has three interval timers, which we treat here as
* three units.
*
* Note that this simulation is very specific to the 3B2, and not
* usable as a general purpose 8253 simulator.
*
*/
#include "3b2_cpu.h"
#include "3b2_csr.h"
#include "3b2_defs.h"
#include "3b2_timer.h"
#define SET_INT do { \
CPU_SET_INT(INT_CLOCK); \
SET_CSR(CSRCLK); \
} while (0)
#define CLR_INT do { \
CPU_CLR_INT(INT_CLOCK); \
CLR_CSR(CSRCLK); \
} while (0)
struct timer_ctr TIMERS[3];
int32 tmxr_poll = 16667;
/*
* The three timers, (A, B, C) run at different
* programmatially controlled frequencies, so each must be
* handled through a different service routine.
*/
UNIT timer_unit[] = {
{ UDATA(&timer0_svc, 0, 0) },
{ UDATA(&timer1_svc, UNIT_IDLE, 0) },
{ UDATA(&timer2_svc, 0, 0) },
{ NULL }
};
UNIT *timer_clk_unit = &timer_unit[1];
REG timer_reg[] = {
{ HRDATAD(DIVA, TIMERS[0].divider, 16, "Divider A") },
{ HRDATAD(STA, TIMERS[0].mode, 8, "Mode A") },
{ HRDATAD(DIVB, TIMERS[1].divider, 16, "Divider B") },
{ HRDATAD(STB, TIMERS[1].mode, 8, "Mode B") },
{ HRDATAD(DIVC, TIMERS[2].divider, 16, "Divider C") },
{ HRDATAD(STC, TIMERS[2].mode, 8, "Mode C") },
{ NULL }
};
MTAB timer_mod[] = {
{ MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, NULL, "SHUTDOWN",
&timer_set_shutdown, NULL, NULL, "Soft Power Shutdown" },
{ 0 }
};
DEVICE timer_dev = {
"TIMER", timer_unit, timer_reg, timer_mod,
1, 16, 8, 4, 16, 32,
NULL, NULL, &timer_reset,
NULL, NULL, NULL, NULL,
DEV_DEBUG, 0, sys_deb_tab
};
t_stat timer_reset(DEVICE *dptr) {
int32 i, t;
memset(&TIMERS, 0, sizeof(struct timer_ctr) * 3);
for (i = 0; i < 3; i++) {
timer_unit[i].tmrnum = i;
timer_unit[i].tmr = (void *)&TIMERS[i];
}
/* Timer 1 gate is always active */
TIMERS[1].gate = 1;
if (!sim_is_running) {
t = sim_rtcn_init_unit(timer_clk_unit, TPS_CLK, TMR_CLK);
sim_activate_after(timer_clk_unit, 1000000 / t);
}
return SCPE_OK;
}
static void timer_activate(uint8 ctrnum)
{
struct timer_ctr *ctr;
ctr = &TIMERS[ctrnum];
switch (ctrnum) {
case TIMER_SANITY:
break;
case TIMER_INTERVAL:
if ((csr_data & CSRITIM) == 0) {
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] INTERVAL TIMER: Activating after %d ms\n",
R[NUM_PC], ctr->val);
sim_activate_after_abs(&timer_unit[ctrnum], ctr->val);
ctr->val--;
} else {
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] INTERVAL TIMER: Currently disabled, not starting\n",
R[NUM_PC]);
}
break;
case TIMER_BUS:
break;
default:
break;
}
}
t_stat timer_set_shutdown(UNIT *uptr, int32 val, CONST char* cptr, void* desc)
{
struct timer_ctr *sanity = (struct timer_ctr *)timer_unit[0].tmr;
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] Setting sanity timer to 0 for shutdown.\n", R[NUM_PC]);
sanity->val = 0;
CLR_INT;
CPU_SET_INT(INT_SERR);
CSRBIT(CSRTIMO, TRUE);
return SCPE_OK;
}
void timer_enable(uint8 ctrnum) {
timer_activate(ctrnum);
}
void timer_disable(uint8 ctrnum)
{
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] Disabling timer %d\n",
R[NUM_PC], ctrnum);
sim_cancel(&timer_unit[ctrnum]);
}
/*
* Sanity Timer
*/
t_stat timer0_svc(UNIT *uptr)
{
struct timer_ctr *ctr;
int32 time;
ctr = (struct timer_ctr *)uptr->tmr;
time = ctr->divider * TIMER_STP_US;
if (time == 0) {
time = TIMER_STP_US;
}
sim_activate_after_abs(uptr, time);
return SCPE_OK;
}
/*
* Interval Timer
*/
t_stat timer1_svc(UNIT *uptr)
{
struct timer_ctr *ctr;
int32 t;
ctr = (struct timer_ctr *)uptr->tmr;
if (ctr->enabled && !(csr_data & CSRITIM)) {
/* Fire the IPL 15 clock interrupt */
SET_INT;
}
t = sim_rtcn_calb(TPS_CLK, TMR_CLK);
sim_activate_after_abs(uptr, 1000000/TPS_CLK);
tmxr_poll = t;
return SCPE_OK;
}
/*
* Bus Timeout Timer
*/
t_stat timer2_svc(UNIT *uptr)
{
struct timer_ctr *ctr;
int32 time;
ctr = (struct timer_ctr *)uptr->tmr;
time = ctr->divider * TIMER_STP_US;
if (time == 0) {
time = TIMER_STP_US;
}
sim_activate_after_abs(uptr, time);
return SCPE_OK;
}
uint32 timer_read(uint32 pa, size_t size)
{
uint32 reg;
uint16 ctr_val;
uint8 ctrnum;
struct timer_ctr *ctr;
reg = pa - TIMERBASE;
ctrnum = (reg >> 2) & 0x3;
ctr = &TIMERS[ctrnum];
switch (reg) {
case TIMER_REG_DIVA:
case TIMER_REG_DIVB:
case TIMER_REG_DIVC:
ctr_val = ctr->val;
if (ctr_val != ctr->divider) {
sim_debug(READ_MSG, &timer_dev,
"[%08x] >>> ctr_val = %04x, ctr->divider = %04x\n",
R[NUM_PC], ctr_val, ctr->divider);
}
switch (ctr->mode & CLK_RW) {
case CLK_LSB:
return ctr_val & 0xff;
case CLK_MSB:
return (ctr_val & 0xff00) >> 8;
case CLK_LMB:
if (ctr->lmb) {
ctr->lmb = FALSE;
return (ctr_val & 0xff00) >> 8;
} else {
ctr->lmb = TRUE;
return ctr_val & 0xff;
}
default:
return 0;
}
break;
case TIMER_REG_CTRL:
return ctr->mode;
case TIMER_CLR_LATCH:
/* Clearing the timer latch has a side-effect
of also clearing pending interrupts */
CLR_INT;
return 0;
default:
/* Unhandled */
sim_debug(READ_MSG, &timer_dev,
"[%08x] UNHANDLED TIMER READ. ADDR=%08x\n",
R[NUM_PC], pa);
return 0;
}
}
void handle_timer_write(uint8 ctrnum, uint32 val)
{
struct timer_ctr *ctr;
ctr = &TIMERS[ctrnum];
switch(ctr->mode & 0x30) {
case 0x10:
ctr->divider &= 0xff00;
ctr->divider |= val & 0xff;
ctr->val = ctr->divider;
ctr->enabled = TRUE;
ctr->stime = sim_gtime();
sim_cancel(timer_clk_unit);
sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
break;
case 0x20:
ctr->divider &= 0x00ff;
ctr->divider |= (val & 0xff) << 8;
ctr->val = ctr->divider;
ctr->enabled = TRUE;
ctr->stime = sim_gtime();
/* Kick the timer to get the new divider value */
sim_cancel(timer_clk_unit);
sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
break;
case 0x30:
if (ctr->lmb) {
ctr->lmb = FALSE;
ctr->divider = (uint16) ((ctr->divider & 0x00ff) | ((val & 0xff) << 8));
ctr->val = ctr->divider;
ctr->enabled = TRUE;
ctr->stime = sim_gtime();
sim_debug(READ_MSG, &timer_dev,
"[%08x] Write timer %d val LMB (MSB): %02x\n",
R[NUM_PC], ctrnum, val & 0xff);
/* Kick the timer to get the new divider value */
sim_cancel(timer_clk_unit);
sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US);
} else {
ctr->lmb = TRUE;
ctr->divider = (ctr->divider & 0xff00) | (val & 0xff);
ctr->val = ctr->divider;
}
break;
default:
break;
}
}
void timer_write(uint32 pa, uint32 val, size_t size)
{
uint8 reg, ctrnum;
struct timer_ctr *ctr;
reg = (uint8) (pa - TIMERBASE);
switch(reg) {
case TIMER_REG_DIVA:
handle_timer_write(0, val);
break;
case TIMER_REG_DIVB:
handle_timer_write(1, val);
break;
case TIMER_REG_DIVC:
handle_timer_write(2, val);
break;
case TIMER_REG_CTRL:
/* The counter number is in bits 6 and 7 */
ctrnum = (val >> 6) & 3;
if (ctrnum > 2) {
sim_debug(WRITE_MSG, &timer_dev,
"[%08x] WARNING: Write to invalid counter: %d\n",
R[NUM_PC], ctrnum);
return;
}
ctr = &TIMERS[ctrnum];
ctr->mode = (uint8) val;
ctr->enabled = FALSE;
ctr->lmb = FALSE;
break;
case TIMER_CLR_LATCH:
sim_debug(WRITE_MSG, &timer_dev,
"[%08x] unexpected write to clear timer latch\n",
R[NUM_PC]);
break;
}
}
void timer_tick()
{
if (TIMERS[0].gate && TIMERS[0].enabled) {
TIMERS[0].val = TIMERS[0].divider - 1;
}
if (TIMERS[1].gate && TIMERS[1].enabled) {
TIMERS[1].val = TIMERS[1].divider - 1;
}
if (TIMERS[2].gate && TIMERS[2].enabled) {
TIMERS[2].val = TIMERS[2].divider - 1;
}
}

View file

@ -1,72 +0,0 @@
/* 3b2_rev2_timer.h: 8253 Interval Timer
Copyright (c) 2017, Seth J. Morabito
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Except as contained in this notice, the name of the author shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from the author.
*/
#ifndef _3B2_REV2_TIMER_H_
#define _3B2_REV2_TIMER_H_
#include "sim_defs.h"
#define TIMER_STP_US 1
#define tmrnum u3
#define tmr up7
#define TIMER_REG_DIVA 0x03
#define TIMER_REG_DIVB 0x07
#define TIMER_REG_DIVC 0x0b
#define TIMER_REG_CTRL 0x0f
#define TIMER_CLR_LATCH 0x13
#define CLK_RW 0x30
#define CLK_LSB 0x10
#define CLK_MSB 0x20
#define CLK_LMB 0x30
struct timer_ctr {
uint16 divider;
uint16 val;
uint8 mode;
t_bool lmb;
t_bool enabled;
t_bool gate;
double stime; /* Most recent start time of counter */
};
t_stat timer_reset(DEVICE *dptr);
uint32 timer_read(uint32 pa, size_t size);
void timer_write(uint32 pa, uint32 val, size_t size);
void timer_tick();
t_stat timer0_svc(UNIT *uptr);
t_stat timer1_svc(UNIT *uptr);
t_stat timer2_svc(UNIT *uptr);
t_stat timer_set_shutdown(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
void timer_disable(uint8 ctrnum);
void timer_enable(uint8 ctrnum);
#endif /* _3B2_REV2_TIMER_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_rev3_csr.c: AT&T 3B2/600G Control and Status Register /* 3b2_rev3_csr.c: CM518B System Board Control, Status & Error Register
Copyright (c) 2020, Seth J. Morabito Copyright (c) 2020-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -32,8 +32,9 @@
#include "3b2_csr.h" #include "3b2_csr.h"
#include "3b2_if.h" #include "3b2_if.h"
#include "3b2_timer.h" #include "3b2_timer.h"
#include "3b2_sys.h"
uint32 csr_data; CSR_DATA csr_data;
BITFIELD csr_bits[] = { BITFIELD csr_bits[] = {
BIT(UTIM), BIT(UTIM),
@ -104,8 +105,14 @@ t_stat csr_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw)
t_stat csr_reset(DEVICE *dptr) t_stat csr_reset(DEVICE *dptr)
{ {
/* Accordig to the technical reference manual, the CSR is NOT CSRBIT(CSRFECC, TRUE);
cleared on reset */ CSRBIT(CSRTHERM, FALSE);
CSRBIT(CSRITIM, TRUE);
CSRBIT(CSRISTIM, TRUE);
CSRBIT(CSRIBUB, TRUE);
CSRBIT(CSRPWRSPDN, FALSE);
CSRBIT(CSRFLPMO, TRUE);
return SCPE_OK; return SCPE_OK;
} }
@ -124,8 +131,8 @@ uint32 csr_read(uint32 pa, size_t size)
return (csr_data >> 24) & 0xff; return (csr_data >> 24) & 0xff;
default: default:
sim_debug(WRITE_MSG, &csr_dev, sim_debug(WRITE_MSG, &csr_dev,
"[%08x] CSR READ. Warning, unexpected register = %02x)\n", "CSR READ. Warning, unexpected register = %02x)\n",
R[NUM_PC], reg); reg);
return 0; return 0;
} }
} }
@ -174,36 +181,15 @@ void csr_write(uint32 pa, uint32 val, size_t size)
break; break;
case 0x1c: case 0x1c:
CSRBIT(CSRITIM, val); CSRBIT(CSRITIM, val);
sim_debug(WRITE_MSG, &csr_dev, timer_gate(TIMER_INTERVAL, CSR(CSRITIM));
"[%08x] CSR WRITE. Inhibit Interval Timer = %d\n",
R[NUM_PC], val);
if (csr_data & CSRITIM) {
timer_disable(TIMER_INTERVAL);
} else {
timer_enable(TIMER_INTERVAL);
}
break; break;
case 0x20: case 0x20:
CSRBIT(CSRISTIM, val); CSRBIT(CSRISTIM, val);
sim_debug(WRITE_MSG, &csr_dev, timer_gate(TIMER_SANITY, CSR(CSRISTIM));
"[%08x] CSR WRITE. Inhibit Sanity Timer = %d\n",
R[NUM_PC], val);
if (csr_data & CSRISTIM) {
timer_disable(TIMER_SANITY);
} else {
timer_enable(TIMER_SANITY);
}
break; break;
case 0x24: case 0x24:
CSRBIT(CSRITIMO, val); CSRBIT(CSRITIMO, val);
sim_debug(WRITE_MSG, &csr_dev, timer_gate(TIMER_BUS, CSR(CSRITIMO));
"[%08x] CSR WRITE. Inhibit Bus Timer = %d\n",
R[NUM_PC], val);
if (csr_data & CSRITIMO) {
timer_disable(TIMER_BUS);
} else {
timer_enable(TIMER_BUS);
}
break; break;
case 0x28: case 0x28:
CSRBIT(CSRICPUFLT, val); CSRBIT(CSRICPUFLT, val);
@ -219,6 +205,9 @@ void csr_write(uint32 pa, uint32 val, size_t size)
break; break;
case 0x38: case 0x38:
CSRBIT(CSRFECC, val); CSRBIT(CSRFECC, val);
sim_debug(WRITE_MSG, &csr_dev,
"CSR WRITE. Force ECC Syndrome = %d\n",
val);
break; break;
case 0x3c: case 0x3c:
CSRBIT(CSRTHERM, val); CSRBIT(CSRTHERM, val);
@ -229,6 +218,10 @@ void csr_write(uint32 pa, uint32 val, size_t size)
break; break;
case 0x44: case 0x44:
CSRBIT(CSRPWRSPDN, val); CSRBIT(CSRPWRSPDN, val);
if (!val) {
/* Stop the simulator - power down */
stop_reason = STOP_POWER;
}
break; break;
case 0x48: case 0x48:
CSRBIT(CSRFLPFST, val); CSRBIT(CSRFLPFST, val);
@ -290,6 +283,7 @@ void csr_write(uint32 pa, uint32 val, size_t size)
break; break;
case 0x7c: case 0x7c:
/* System reset request */ /* System reset request */
full_reset();
cpu_boot(0, &cpu_dev); cpu_boot(0, &cpu_dev);
break; break;
default: default:

View file

@ -1,6 +1,6 @@
/* 3b2_rev3_csr.h: AT&T 3B2/600G Control and Status Register /* 3b2_rev3_csr.h: CM518B System Board Control, Status & Error Register
Copyright (c) 2020, Seth J. Morabito Copyright (c) 2020-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -33,6 +33,8 @@
#include "3b2_defs.h" #include "3b2_defs.h"
typedef uint32 CSR_DATA;
t_stat csr_svc(UNIT *uptr); t_stat csr_svc(UNIT *uptr);
t_stat csr_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); t_stat csr_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw);
t_stat csr_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw); t_stat csr_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw);
@ -40,6 +42,4 @@ t_stat csr_reset(DEVICE *dptr);
uint32 csr_read(uint32 pa, size_t size); uint32 csr_read(uint32 pa, size_t size);
void csr_write(uint32 pa, uint32 val, size_t size); void csr_write(uint32 pa, uint32 val, size_t size);
extern uint32 csr_data;
#endif #endif

View file

@ -1,6 +1,6 @@
/* 3b2_rev3_defs.h: AT&T 3B2 Rev 3 (Model 600G) Simulator Definitions /* 3b2_rev3_defs.h: Veresion 3 (3B2/700) Common Definitions
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -90,14 +90,6 @@
#define INT_MAP_LEN 0x2000 #define INT_MAP_LEN 0x2000
/* Memory */
#define MEMID_4M 6
#define MEMID_16M 7
#define MADDR_SLOT_0 0x4d000
#define MADDR_SLOT_1 0x4d004
#define MADDR_SLOT_2 0x4d008
#define MADDR_SLOT_3 0x4d00c
#define IFCSRBASE 0x40000 #define IFCSRBASE 0x40000
#define IFCSRSIZE 0x100 #define IFCSRSIZE 0x100
#define TIMERBASE 0x41000 #define TIMERBASE 0x41000
@ -121,9 +113,9 @@
#define MMUBASE 0x4f000 #define MMUBASE 0x4f000
#define MMUSIZE 0x1000 #define MMUSIZE 0x1000
#define FLTLBASE 0x4c000 #define FLTLBASE 0x4c000
#define FLTLSIZE 0x10 #define FLTLSIZE 0x1000
#define FLTHBASE 0x4d000 #define FLTHBASE 0x4d000
#define FLTHSIZE 0x10 #define FLTHSIZE 0x1000
#define VCACHE_BOTTOM 0x1c00000 #define VCACHE_BOTTOM 0x1c00000
#define VCACHE_TOP 0x2000000 #define VCACHE_TOP 0x2000000

View file

@ -1,31 +0,0 @@
/* 3b2_rev3_mau.c: WE32206 Math Accelration Unit Implementation
Copyright (c) 2021, Seth J. Morabito
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Except as contained in this notice, the name of the author shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from the author.
*/
/* Stub file. Not Yet Implemented. */

View file

@ -1,36 +0,0 @@
/* 3b2_rev3_mau.h: WE32206 Math Accelration Unit Header
Copyright (c) 2021, Seth J. Morabito
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Except as contained in this notice, the name of the author shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from the author.
*/
#ifndef _3B2_REV3_MAU_H_
#define _3B2_REV3_MAU_H_
/* Stub file. Not Yet Implemented. */
#endif

View file

@ -1,6 +1,6 @@
/* 3b2_rev3_mmu.c: AT&T 3B2/600G MMU (WE32201) /* 3b2_rev3_mmu.c: WE32201 MMU
Copyright (c) 2020, Seth J. Morabito Copyright (c) 2020-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -188,8 +188,8 @@ static void put_sdce(uint32 va, uint32 sd_hi, uint32 sd_lo)
mmu_state.sdcl[ci] = SD_TO_SDCL(sd_lo, va); mmu_state.sdcl[ci] = SD_TO_SDCL(sd_lo, va);
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] CACHED SD AT IDX %d. va=%08x sd_hi=%08x sd_lo=%08x sdc_hi=%08x sdc_lo=%08x\n", "CACHED SD AT IDX %d. va=%08x sd_hi=%08x sd_lo=%08x sdc_hi=%08x sdc_lo=%08x\n",
R[NUM_PC], ci, va, sd_hi, sd_lo, mmu_state.sdch[ci], mmu_state.sdcl[ci]); ci, va, sd_hi, sd_lo, mmu_state.sdch[ci], mmu_state.sdcl[ci]);
} }
@ -236,8 +236,8 @@ static t_stat get_pdce(uint32 va, uint32 *pd, uint8 *pd_acc, uint32 *pdc_idx)
*pd_acc = (mmu_state.pdcl[i] >> 24) & 0xff; *pd_acc = (mmu_state.pdcl[i] >> 24) & 0xff;
*pdc_idx = i; *pdc_idx = i;
sim_debug(MMU_TRACE_DBG, &mmu_dev, sim_debug(MMU_TRACE_DBG, &mmu_dev,
"[%08x] PDC HIT. va=%08x idx=%d tag=%03x pd=%08x pdcl=%08x pdch=%08x\n", "PDC HIT. va=%08x idx=%d tag=%03x pd=%08x pdcl=%08x pdch=%08x\n",
R[NUM_PC], va, i, key_tag, *pd, va, i, key_tag, *pd,
mmu_state.pdcl[i], mmu_state.pdch[i]); mmu_state.pdcl[i], mmu_state.pdch[i]);
set_u_bit(i); set_u_bit(i);
return SCPE_OK; return SCPE_OK;
@ -245,8 +245,8 @@ static t_stat get_pdce(uint32 va, uint32 *pd, uint8 *pd_acc, uint32 *pdc_idx)
} }
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] PDC MISS. va=%08x tag=%03x\n", "PDC MISS. va=%08x tag=%03x\n",
R[NUM_PC], va, key_tag); va, key_tag);
return SCPE_NXM; return SCPE_NXM;
} }
@ -259,8 +259,8 @@ static void put_pdce_at(uint32 va, uint32 sd_lo, uint32 pd, uint32 slot)
mmu_state.pdcl[slot] = PD_TO_PDCL(pd, sd_lo); mmu_state.pdcl[slot] = PD_TO_PDCL(pd, sd_lo);
mmu_state.pdch[slot] = VA_TO_PDCH(va, sd_lo); mmu_state.pdch[slot] = VA_TO_PDCH(va, sd_lo);
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] Caching MMU PDC entry at index %d (pdc_hi=%08x pdc_lo=%08x va=%08x)\n", "Caching MMU PDC entry at index %d (pdc_hi=%08x pdc_lo=%08x va=%08x)\n",
R[NUM_PC], slot, mmu_state.pdch[slot], mmu_state.pdcl[slot], va); slot, mmu_state.pdch[slot], mmu_state.pdcl[slot], va);
set_u_bit(slot); set_u_bit(slot);
mmu_state.last_cached = slot; mmu_state.last_cached = slot;
} }
@ -279,8 +279,7 @@ static uint32 put_pdce(uint32 va, uint32 sd_lo, uint32 pd)
*/ */
if (mmu_state.flush_u) { if (mmu_state.flush_u) {
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] Flushing PDC U bits on all-set condition.\n", "Flushing PDC U bits on all-set condition.\n");
R[NUM_PC]);
mmu_state.flush_u = FALSE; mmu_state.flush_u = FALSE;
for (i = 0; i < MMU_PDCS; i++) { for (i = 0; i < MMU_PDCS; i++) {
if (i != mmu_state.last_cached) { if (i != mmu_state.last_cached) {
@ -332,16 +331,14 @@ static void flush_pdc(uint32 va)
target_tag = mmu_state.pdch[i] & PDC_TAG_MASK; target_tag = mmu_state.pdch[i] & PDC_TAG_MASK;
if (target_tag == key_tag) { if (target_tag == key_tag) {
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] Flushing MMU PDC entry pdc_lo=%08x pdc_hi=%08x index %d (va=%08x)\n", "Flushing MMU PDC entry pdc_lo=%08x pdc_hi=%08x index %d (va=%08x)\n",
R[NUM_PC],
mmu_state.pdcl[i], mmu_state.pdcl[i],
mmu_state.pdch[i], mmu_state.pdch[i],
i, i,
va); va);
if (mmu_state.pdch[i] & PDC_C_MASK) { if (mmu_state.pdch[i] & PDC_C_MASK) {
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] Flushing MMU PDC entry: CONTIGUOUS\n", "Flushing MMU PDC entry: CONTIGUOUS\n");
R[NUM_PC]);
/* If this PD came from a contiguous SD, we need to /* If this PD came from a contiguous SD, we need to
* flush ALL entries belonging to the same SD. All * flush ALL entries belonging to the same SD. All
* pages within the same segment have the same upper * pages within the same segment have the same upper
@ -361,8 +358,8 @@ static void flush_pdc(uint32 va)
} }
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] Flushing MMU PDC entry: NOT FOUND (va=%08x key_tag=%08x)\n", "Flushing MMU PDC entry: NOT FOUND (va=%08x key_tag=%08x)\n",
R[NUM_PC], va, key_tag); va, key_tag);
} }
@ -374,8 +371,7 @@ static void flush_caches()
uint32 i; uint32 i;
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] Flushing MMU PDC and SDC\n", "Flushing MMU PDC and SDC\n");
R[NUM_PC]);
for (i = 0; i < MMU_SDCS; i++) { for (i = 0; i < MMU_SDCS; i++) {
mmu_state.sdch[i] &= ~SDC_G_MASK; mmu_state.sdch[i] &= ~SDC_G_MASK;
@ -399,13 +395,16 @@ static t_stat mmu_check_perm(uint8 flags, uint8 r_acc)
case 0: /* No Access */ case 0: /* No Access */
return SCPE_NXM; return SCPE_NXM;
case 1: /* Exec Only */ case 1: /* Exec Only */
if (r_acc != ACC_IF && r_acc != ACC_IFAD) { if (r_acc != ACC_IF &&
r_acc != ACC_IFAD) {
return SCPE_NXM; return SCPE_NXM;
} }
return SCPE_OK; return SCPE_OK;
case 2: /* Read / Execute */ case 2: /* Read / Execute */
if (r_acc != ACC_IF && r_acc != ACC_IFAD && if (r_acc != ACC_IF &&
r_acc != ACC_AF && r_acc != ACC_OF && r_acc != ACC_IFAD &&
r_acc != ACC_OF &&
r_acc != ACC_AF &&
r_acc != ACC_MT) { r_acc != ACC_MT) {
return SCPE_NXM; return SCPE_NXM;
} }
@ -415,29 +414,6 @@ static t_stat mmu_check_perm(uint8 flags, uint8 r_acc)
} }
} }
/*
* Internally, the ID Number Cache is a fully associative cache with a
* tag consisting of the upper 29 bits of the segment address, plus a
* U bit indicating that the ID is usable. The value (the ID number)
* is the fixed index of the tag within the cache.
*/
static t_stat get_idnc(uint32 va, uint8 *id)
{
uint32 i, tag, entry;
tag = IDNC_TAG(va);
for (i = 0; i < MMU_IDNCS; i++) {
entry = mmu_state.idnc[i];
if ((IDNC_TAG(entry) == tag) && IDNC_U(entry)) {
*id = ((uint8)i & 0xff);
return SCPE_OK;
}
}
return SCPE_NXM;
}
/* /*
* Initialize the MMU device * Initialize the MMU device
*/ */
@ -465,56 +441,56 @@ uint32 mmu_read(uint32 pa, size_t size)
case MMU_SDCL: case MMU_SDCL:
data = mmu_state.sdcl[index]; data = mmu_state.sdcl[index];
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_SDCL[%d] = %08x\n", "MMU_SDCL[%d] = %08x\n",
R[NUM_PC], index, data); index, data);
break; break;
case MMU_SDCH: case MMU_SDCH:
data = mmu_state.sdch[index]; data = mmu_state.sdch[index];
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_SDCH[%d] = %08x\n", "MMU_SDCH[%d] = %08x\n",
R[NUM_PC], index, data); index, data);
break; break;
case MMU_PDCL: case MMU_PDCL:
data = mmu_state.pdcl[index]; data = mmu_state.pdcl[index];
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_PDCL[%d] = %08x\n", "MMU_PDCL[%d] = %08x\n",
R[NUM_PC], index, data); index, data);
break; break;
case MMU_PDCH: case MMU_PDCH:
data = mmu_state.pdch[index]; data = mmu_state.pdch[index];
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_PDCH[%d] = %08x\n", "MMU_PDCH[%d] = %08x\n",
R[NUM_PC], index, data); index, data);
break; break;
case MMU_SRAMA: case MMU_SRAMA:
data = mmu_state.sra[index]; data = mmu_state.sra[index];
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_SRAMA[%d] = %08x\n", "MMU_SRAMA[%d] = %08x\n",
R[NUM_PC], index, data); index, data);
break; break;
case MMU_SRAMB: case MMU_SRAMB:
data = mmu_state.srb[index]; data = mmu_state.srb[index];
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_SRAMB[%d] = %08x\n", "MMU_SRAMB[%d] = %08x\n",
R[NUM_PC], index, data); index, data);
break; break;
case MMU_FC: case MMU_FC:
data = mmu_state.fcode; data = mmu_state.fcode;
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_FC = %08x\n", "MMU_FC = %08x\n",
R[NUM_PC], data); data);
break; break;
case MMU_FA: case MMU_FA:
data = mmu_state.faddr; data = mmu_state.faddr;
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_FA = %08x\n", "MMU_FA = %08x\n",
R[NUM_PC], data); data);
break; break;
case MMU_CONF: case MMU_CONF:
data = mmu_state.conf; data = mmu_state.conf;
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_CONF = %02x (M=%d R=%d $=%d PS=%d MCE=%d DCE=%d)\n", "MMU_CONF = %02x (M=%d R=%d $=%d PS=%d MCE=%d DCE=%d)\n",
R[NUM_PC], data, data,
MMU_CONF_M, MMU_CONF_M,
MMU_CONF_R, MMU_CONF_R,
MMU_CONF_C, MMU_CONF_C,
@ -525,37 +501,36 @@ uint32 mmu_read(uint32 pa, size_t size)
case MMU_VAR: case MMU_VAR:
data = mmu_state.var; data = mmu_state.var;
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_VAR = %08x\n", "MMU_VAR = %08x\n",
R[NUM_PC], data); data);
break; break;
case MMU_IDC: case MMU_IDC:
/* TODO: Implement */ /* TODO: Implement */
data = 0; data = 0;
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_IDC\n", R[NUM_PC]); "MMU_IDC\n");
break; break;
case MMU_IDNR: case MMU_IDNR:
/* TODO: Implement */ /* TODO: Implement */
data = 0; data = 0;
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_IDNR\n", R[NUM_PC]); "MMU_IDNR\n");
break; break;
case MMU_FIDNR: case MMU_FIDNR:
/* TODO: Implement */ /* TODO: Implement */
data = 0; data = 0;
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_FIDNR\n", R[NUM_PC]); "MMU_FIDNR\n");
break; break;
case MMU_VR: case MMU_VR:
/* Simply not faulting here is good enough */ data = MMU_REV3_VER;
data = 0;
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] MMU_VR\n", R[NUM_PC]); "MMU_VR = 0x23\n");
break; break;
default: default:
sim_debug(MMU_READ_DBG, &mmu_dev, sim_debug(MMU_READ_DBG, &mmu_dev,
"[%08x] Invalid MMU register: pa=%08x\n", "Invalid MMU register: pa=%08x\n",
R[NUM_PC], pa); pa);
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
break; break;
} }
@ -600,15 +575,14 @@ void mmu_write(uint32 pa, uint32 val, size_t size)
break; break;
case MMU_FDCR: case MMU_FDCR:
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] MMU_FDCR\n", "MMU_FDCR\n");
R[NUM_PC]);
/* Data cache is not implemented */ /* Data cache is not implemented */
break; break;
case MMU_SRAMA: case MMU_SRAMA:
index = index & 3; index = index & 3;
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] MMU_SRAMA[%d] = %08x\n", "MMU_SRAMA[%d] = %08x\n",
R[NUM_PC], index, val); index, val);
mmu_state.sra[index] = val; mmu_state.sra[index] = val;
mmu_state.sec[index].addr = val & 0xfffffffc; mmu_state.sec[index].addr = val & 0xfffffffc;
@ -616,10 +590,9 @@ void mmu_write(uint32 pa, uint32 val, size_t size)
for (i = 0; i < MMU_SDCS; i++) { for (i = 0; i < MMU_SDCS; i++) {
if (((mmu_state.sdcl[i] >> 10) & 0x3) == index) { if (((mmu_state.sdcl[i] >> 10) & 0x3) == index) {
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] Flushing MMU SDC entry at index %d " "Flushing MMU SDC entry at index %d "
"(sdc_lo=%08x sdc_hi=%08x)\n", "(sdc_lo=%08x sdc_hi=%08x)\n",
R[NUM_PC], i, i, mmu_state.sdcl[i], mmu_state.sdch[i]);
mmu_state.sdcl[i], mmu_state.sdch[i]);
mmu_state.sdch[i] &= ~(SDC_G_MASK); mmu_state.sdch[i] &= ~(SDC_G_MASK);
} }
} }
@ -637,8 +610,7 @@ void mmu_write(uint32 pa, uint32 val, size_t size)
mmu_state.sec[index].len = (val >> 10) & 0x1fff; mmu_state.sec[index].len = (val >> 10) & 0x1fff;
/* We do not flush the cache on writing SRAMB */ /* We do not flush the cache on writing SRAMB */
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] MMU_SRAMB[%d] length=%04x (%d segments)\n", "MMU_SRAMB[%d] length=%04x (%d segments)\n",
R[NUM_PC],
index, index,
mmu_state.sec[index].len, mmu_state.sec[index].len,
mmu_state.sec[index].len + 1); mmu_state.sec[index].len + 1);
@ -647,20 +619,20 @@ void mmu_write(uint32 pa, uint32 val, size_t size)
/* Set a default value */ /* Set a default value */
mmu_state.fcode = (((CPU_CM) << 5) | (0xa << 7)); mmu_state.fcode = (((CPU_CM) << 5) | (0xa << 7));
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] MMU_FC = %08x\n", "MMU_FC = %08x\n",
R[NUM_PC], mmu_state.fcode); mmu_state.fcode);
break; break;
case MMU_FA: case MMU_FA:
mmu_state.faddr = val; mmu_state.faddr = val;
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] MMU_FADDR = %08x\n", "MMU_FADDR = %08x\n",
R[NUM_PC], val); val);
break; break;
case MMU_CONF: case MMU_CONF:
mmu_state.conf = val & 0x7f; mmu_state.conf = val & 0x7f;
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] MMU_CONF = %02x (M=%d R=%d $=%d PS=%d MCE=%d DCE=%d)\n", "MMU_CONF = %02x (M=%d R=%d $=%d PS=%d MCE=%d DCE=%d)\n",
R[NUM_PC], val, val,
MMU_CONF_M, MMU_CONF_M,
MMU_CONF_R, MMU_CONF_R,
MMU_CONF_C, MMU_CONF_C,
@ -671,14 +643,13 @@ void mmu_write(uint32 pa, uint32 val, size_t size)
case MMU_VAR: case MMU_VAR:
mmu_state.var = val; mmu_state.var = val;
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] MMU_VAR = %08x\n", "MMU_VAR = %08x\n", val);
R[NUM_PC], val);
if ((mmu_state.sdcl[SDC_IDX(val)] & SDC_VADDR_MASK) == if ((mmu_state.sdcl[SDC_IDX(val)] & SDC_VADDR_MASK) ==
((val >> 20) & SDC_VADDR_MASK)) { ((val >> 20) & SDC_VADDR_MASK)) {
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] Flushing MMU SDC entry at index %d " "Flushing MMU SDC entry at index %d "
"(sdc_lo=%08x sdc_hi=%08x)\n", "(sdc_lo=%08x sdc_hi=%08x)\n",
R[NUM_PC], SDC_IDX(val), SDC_IDX(val),
mmu_state.sdcl[SDC_IDX(val)], mmu_state.sdcl[SDC_IDX(val)],
mmu_state.sdch[SDC_IDX(val)]); mmu_state.sdch[SDC_IDX(val)]);
mmu_state.sdch[SDC_IDX(val)] &= ~SDC_G_MASK; mmu_state.sdch[SDC_IDX(val)] &= ~SDC_G_MASK;
@ -687,28 +658,28 @@ void mmu_write(uint32 pa, uint32 val, size_t size)
break; break;
case MMU_IDC: case MMU_IDC:
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] MMU_IDC = %08x\n", "MMU_IDC = %08x\n",
R[NUM_PC], val); val);
break; break;
case MMU_IDNR: case MMU_IDNR:
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] MMU_IDNR = %08x\n", "MMU_IDNR = %08x\n",
R[NUM_PC], val); val);
break; break;
case MMU_FIDNR: case MMU_FIDNR:
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] MMU_FIDNR = %08x\n", "MMU_FIDNR = %08x\n",
R[NUM_PC], val); val);
break; break;
case MMU_VR: case MMU_VR:
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] MMU_VR = %08x\n", "MMU_VR = %08x\n",
R[NUM_PC], val); val);
break; break;
default: default:
sim_debug(MMU_WRITE_DBG, &mmu_dev, sim_debug(MMU_WRITE_DBG, &mmu_dev,
"[%08x] UNHANDLED WRITE (entity=0x%x, index=0x%x, val=%08x)\n", "UNHANDLED WRITE (entity=0x%x, index=0x%x, val=%08x)\n",
R[NUM_PC], entity, index, val); entity, index, val);
break; break;
} }
} }
@ -726,8 +697,8 @@ static t_stat mmu_update_history(uint32 va, uint8 r_acc, uint32 pdc_idx, t_bool
update_sdc = FALSE; update_sdc = FALSE;
} }
sd_lo = pread_w(SD_ADDR(va)); sd_lo = pread_w(SD_ADDR(va), BUS_PER);
sd_hi = pread_w(SD_ADDR(va) + 4); sd_hi = pread_w(SD_ADDR(va) + 4, BUS_PER);
if (MMU_CONF_M && r_acc == ACC_W && (mmu_state.sdcl[SDC_IDX(va)] & SDC_M_MASK) == 0) { if (MMU_CONF_M && r_acc == ACC_W && (mmu_state.sdcl[SDC_IDX(va)] & SDC_M_MASK) == 0) {
if (update_sdc) { if (update_sdc) {
@ -736,13 +707,12 @@ static t_stat mmu_update_history(uint32 va, uint8 r_acc, uint32 pdc_idx, t_bool
if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) { if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] MMU R&M Update Fault (M)\n", "MMU R&M Update Fault (M)\n");
R[NUM_PC]);
MMU_FAULT(MMU_F_RM_UPD); MMU_FAULT(MMU_F_RM_UPD);
return SCPE_NXM; return SCPE_NXM;
} }
pwrite_w(SD_ADDR(va), sd_lo | SD_M_MASK); pwrite_w(SD_ADDR(va), sd_lo | SD_M_MASK, BUS_PER);
} }
if (MMU_CONF_R && (mmu_state.sdcl[SDC_IDX(va)] & SDC_R_MASK) == 0) { if (MMU_CONF_R && (mmu_state.sdcl[SDC_IDX(va)] & SDC_R_MASK) == 0) {
@ -752,13 +722,12 @@ static t_stat mmu_update_history(uint32 va, uint8 r_acc, uint32 pdc_idx, t_bool
if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) { if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] MMU R&M Update Fault (R)\n", "MMU R&M Update Fault (R)\n");
R[NUM_PC]);
MMU_FAULT(MMU_F_RM_UPD); MMU_FAULT(MMU_F_RM_UPD);
return SCPE_NXM; return SCPE_NXM;
} }
pwrite_w(SD_ADDR(va), sd_lo | SD_R_MASK); pwrite_w(SD_ADDR(va), sd_lo | SD_R_MASK, BUS_PER);
} }
if (!SD_CONTIG(sd_lo)) { if (!SD_CONTIG(sd_lo)) {
@ -766,14 +735,14 @@ static t_stat mmu_update_history(uint32 va, uint8 r_acc, uint32 pdc_idx, t_bool
if (r_acc == ACC_W && (mmu_state.pdcl[pdc_idx] & PDC_M_MASK) == 0) { if (r_acc == ACC_W && (mmu_state.pdcl[pdc_idx] & PDC_M_MASK) == 0) {
mmu_state.pdcl[pdc_idx] |= PDC_M_MASK; mmu_state.pdcl[pdc_idx] |= PDC_M_MASK;
pd = pread_w(pd_addr); pd = pread_w(pd_addr, BUS_PER);
pwrite_w(pd_addr, pd | PD_M_MASK); pwrite_w(pd_addr, pd | PD_M_MASK, BUS_PER);
} }
if ((mmu_state.pdcl[pdc_idx] & PDC_R_MASK) == 0) { if ((mmu_state.pdcl[pdc_idx] & PDC_R_MASK) == 0) {
mmu_state.pdcl[pdc_idx] |= PDC_R_MASK; mmu_state.pdcl[pdc_idx] |= PDC_R_MASK;
pd = pread_w(pd_addr); pd = pread_w(pd_addr, BUS_PER);
pwrite_w(pd_addr, pd | PD_R_MASK); pwrite_w(pd_addr, pd | PD_R_MASK, BUS_PER);
} }
} }
@ -821,8 +790,8 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc,
* checked because SSL out of bounds is a fatal error. */ * checked because SSL out of bounds is a fatal error. */
if (SSL(va) > SRAMB_LEN(va)) { if (SSL(va) > SRAMB_LEN(va)) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] SDT Length Fault. sramb_len=%x ssl=%x va=%08x\n", "SDT Length Fault. sramb_len=%x ssl=%x va=%08x\n",
R[NUM_PC], SRAMB_LEN(va), SSL(va), va); SRAMB_LEN(va), SSL(va), va);
MMU_FAULT(MMU_F_SDTLEN); MMU_FAULT(MMU_F_SDTLEN);
return SCPE_NXM; return SCPE_NXM;
} }
@ -836,17 +805,17 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc,
* memory. */ * memory. */
sdc_miss = TRUE; sdc_miss = TRUE;
sd_lo = pread_w(sd_ptr); /* Control Bits */ sd_lo = pread_w(sd_ptr, BUS_PER); /* Control Bits */
sd_hi = pread_w(sd_ptr + 4); /* Address Bits */ sd_hi = pread_w(sd_ptr + 4, BUS_PER); /* Address Bits */
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] SDC miss. Read sd_ptr=%08x sd_lo=%08x sd_hi=%08x va=%08x\n", "SDC miss. Read sd_ptr=%08x sd_lo=%08x sd_hi=%08x va=%08x\n",
R[NUM_PC], sd_ptr, sd_lo, sd_hi, va); sd_ptr, sd_lo, sd_hi, va);
} }
if (!SD_VALID(sd_lo)) { if (!SD_VALID(sd_lo)) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] Invalid Segment Descriptor. va=%08x sd_hi=%08x sd_lo=%08x\n", "Invalid Segment Descriptor. va=%08x sd_hi=%08x sd_lo=%08x\n",
R[NUM_PC], va, sd_hi, sd_lo); va, sd_hi, sd_lo);
MMU_FAULT(MMU_F_INV_SD); MMU_FAULT(MMU_F_INV_SD);
return SCPE_NXM; return SCPE_NXM;
} }
@ -854,8 +823,8 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc,
if (SD_INDIRECT(sd_lo)) { if (SD_INDIRECT(sd_lo)) {
if (++indirect_count > MAX_INDIRECTS) { if (++indirect_count > MAX_INDIRECTS) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] Max Indirects Fault. va=%08x sd_hi=%08x sd_lo=%08x\n", "Max Indirects Fault. va=%08x sd_hi=%08x sd_lo=%08x\n",
R[NUM_PC], va, sd_hi, sd_lo); va, sd_hi, sd_lo);
MMU_FAULT(MMU_F_INDIRECT); MMU_FAULT(MMU_F_INDIRECT);
return SCPE_NXM; return SCPE_NXM;
} }
@ -863,8 +832,8 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc,
/* Any permission failure at this point is actually an MMU_F_MISS_MEM */ /* Any permission failure at this point is actually an MMU_F_MISS_MEM */
if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) { if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] MMU Miss Processing Memory Fault (SD Access) (ckm=%d pd_acc=%02x r_acc=%02x)\n", "MMU Miss Processing Memory Fault (SD Access) (ckm=%d pd_acc=%02x r_acc=%02x)\n",
R[NUM_PC], CPU_CM, SD_ACC(sd_lo), r_acc); CPU_CM, SD_ACC(sd_lo), r_acc);
MMU_FAULT(MMU_F_MISS_MEM); MMU_FAULT(MMU_F_MISS_MEM);
return SCPE_NXM; return SCPE_NXM;
} }
@ -872,8 +841,8 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc,
/* sd_hi is a pointer to a new segment descriptor */ /* sd_hi is a pointer to a new segment descriptor */
sd_ptr = sd_hi; sd_ptr = sd_hi;
sd_lo = pread_w(sd_ptr); sd_lo = pread_w(sd_ptr, BUS_PER);
sd_hi = pread_w(sd_ptr + 4); sd_hi = pread_w(sd_ptr + 4, BUS_PER);
} else { } else {
/* If it's not an indirection, we're done. */ /* If it's not an indirection, we're done. */
break; break;
@ -886,14 +855,14 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc,
fault; otherwise, it's a PDT NOT PRESENT fault. */ fault; otherwise, it's a PDT NOT PRESENT fault. */
if (SD_CONTIG(sd_lo)) { if (SD_CONTIG(sd_lo)) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] Segment Not Present. va=%08x\n", "Segment Not Present. va=%08x\n",
R[NUM_PC], va); va);
MMU_FAULT(MMU_F_SEG_NOT_PRES); MMU_FAULT(MMU_F_SEG_NOT_PRES);
return SCPE_NXM; return SCPE_NXM;
} else { } else {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] PDT Not Present. va=%08x\n", "PDT Not Present. va=%08x\n",
R[NUM_PC], va); va);
MMU_FAULT(MMU_F_PDT_NOT_PRES); MMU_FAULT(MMU_F_PDT_NOT_PRES);
return SCPE_NXM; return SCPE_NXM;
} }
@ -903,16 +872,16 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc,
if (SD_CONTIG(sd_lo)) { if (SD_CONTIG(sd_lo)) {
if (PSL(va) > SD_MAX_OFF(sd_lo)) { if (PSL(va) > SD_MAX_OFF(sd_lo)) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] Segment Offset Fault. va=%08x\n", "Segment Offset Fault. va=%08x\n",
R[NUM_PC], va); va);
MMU_FAULT(MMU_F_SEG_OFFSET); MMU_FAULT(MMU_F_SEG_OFFSET);
return SCPE_NXM; return SCPE_NXM;
} }
} else { } else {
if ((va & 0x1ffff) > MAX_SEG_OFF(sd_lo)) { if ((va & 0x1ffff) > MAX_SEG_OFF(sd_lo)) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] PDT Length Fault. va=%08x max_seg_off=0x%x\n", "PDT Length Fault. va=%08x max_seg_off=0x%x\n",
R[NUM_PC], va, MAX_SEG_OFF(sd_lo)); va, MAX_SEG_OFF(sd_lo));
MMU_FAULT(MMU_F_PDTLEN); MMU_FAULT(MMU_F_PDTLEN);
return SCPE_NXM; return SCPE_NXM;
} }
@ -923,8 +892,8 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc,
/* TODO: VERIFY */ /* TODO: VERIFY */
if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) { if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] [AFTER DISCONTINUITY] Access to Memory Denied (va=%08x ckm=%d pd_acc=%02x r_acc=%02x)\n", "[AFTER DISCONTINUITY] Access to Memory Denied (va=%08x ckm=%d pd_acc=%02x r_acc=%02x)\n",
R[NUM_PC], va, CPU_CM, SD_ACC(sd_lo), r_acc); va, CPU_CM, SD_ACC(sd_lo), r_acc);
MMU_FAULT(MMU_F_ACC); MMU_FAULT(MMU_F_ACC);
return SCPE_NXM; return SCPE_NXM;
} }
@ -936,31 +905,31 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc,
1); /* P bit */ 1); /* P bit */
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] Contiguous Segment. Constructing PD. PSIZE=%d va=%08x sd_hi=%08x sd_lo=%08x pd=%08x\n", "Contiguous Segment. Constructing PD. PSIZE=%d va=%08x sd_hi=%08x sd_lo=%08x pd=%08x\n",
R[NUM_PC], MMU_CONF_PS, va, sd_hi, sd_lo, *pd); MMU_CONF_PS, va, sd_hi, sd_lo, *pd);
} else { } else {
/* We can find the PD in main memory */ /* We can find the PD in main memory */
pd_addr = SD_SEG_ADDR(sd_hi) + (PSL(va) * 4); pd_addr = SD_SEG_ADDR(sd_hi) + (PSL(va) * 4);
*pd = pread_w(pd_addr); *pd = pread_w(pd_addr, BUS_PER);
sim_debug(MMU_CACHE_DBG, &mmu_dev, sim_debug(MMU_CACHE_DBG, &mmu_dev,
"[%08x] Paged Segment. Loaded PD. va=%08x sd_hi=%08x sd_lo=%08x pd_addr=%08x pd=%08x\n", "Paged Segment. Loaded PD. va=%08x sd_hi=%08x sd_lo=%08x pd_addr=%08x pd=%08x\n",
R[NUM_PC], va, sd_hi, sd_lo, pd_addr, *pd); va, sd_hi, sd_lo, pd_addr, *pd);
} }
if (r_acc == ACC_W && (*pd & PD_W_MASK)) { if (r_acc == ACC_W && (*pd & PD_W_MASK)) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] Page Write Fault, pd=%08x va=%08x\n", "Page Write Fault, pd=%08x va=%08x\n",
R[NUM_PC], *pd, va); *pd, va);
MMU_FAULT(MMU_F_PW); MMU_FAULT(MMU_F_PW);
return SCPE_NXM; return SCPE_NXM;
} }
if ((*pd & PD_P_MASK) != PD_P_MASK) { if ((*pd & PD_P_MASK) != PD_P_MASK) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] Page Not Present Fault. pd=%08x va=%08x\n", "Page Not Present Fault. pd=%08x va=%08x\n",
R[NUM_PC], *pd, va); *pd, va);
MMU_FAULT(MMU_F_PAGE_NOT_PRES); MMU_FAULT(MMU_F_PAGE_NOT_PRES);
return SCPE_NXM; return SCPE_NXM;
} }
@ -1018,16 +987,16 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa)
if (get_pdce(va, &pd, &pd_acc, &pdc_idx) == SCPE_OK) { if (get_pdce(va, &pd, &pd_acc, &pdc_idx) == SCPE_OK) {
if (mmu_check_perm(pd_acc, r_acc) != SCPE_OK) { if (mmu_check_perm(pd_acc, r_acc) != SCPE_OK) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] Access to Memory Denied (va=%08x ckm=%d pd_acc=%02x r_acc=%02x)\n", "Access to Memory Denied (va=%08x ckm=%d pd_acc=%02x r_acc=%02x)\n",
R[NUM_PC], va, CPU_CM, pd_acc, r_acc); va, CPU_CM, pd_acc, r_acc);
MMU_FAULT(MMU_F_ACC); MMU_FAULT(MMU_F_ACC);
return SCPE_NXM; return SCPE_NXM;
} }
if (r_acc == ACC_W && (pd & PD_W_MASK)) { if (r_acc == ACC_W && (pd & PD_W_MASK)) {
sim_debug(MMU_FAULT_DBG, &mmu_dev, sim_debug(MMU_FAULT_DBG, &mmu_dev,
"[%08x] Page Write Fault, pd=%08x va=%08x\n", "Page Write Fault, pd=%08x va=%08x\n",
R[NUM_PC], pd, va); pd, va);
MMU_FAULT(MMU_F_PW); MMU_FAULT(MMU_F_PW);
return SCPE_NXM; return SCPE_NXM;
} }
@ -1053,8 +1022,8 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa)
*pa = PD_ADDR(pd) + POT(va); *pa = PD_ADDR(pd) + POT(va);
sim_debug(MMU_TRACE_DBG, &mmu_dev, sim_debug(MMU_TRACE_DBG, &mmu_dev,
"[%08x] XLATE DONE. r_acc=%d va=%08x pa=%08x\n", "XLATE DONE. r_acc=%d va=%08x pa=%08x\n",
R[NUM_PC], r_acc, va, *pa); r_acc, va, *pa);
return SCPE_OK; return SCPE_OK;
} }
@ -1189,8 +1158,8 @@ t_stat mmu_show_sdt(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
fprintf(st, "-------- -------- -------- -------- --- --------- ------\n"); fprintf(st, "-------- -------- -------- -------- --- --------- ------\n");
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
sd_lo = pread_w(addr + (i * 8)) & SD_RES_MASK; sd_lo = pread_w(addr + (i * 8), BUS_PER) & SD_RES_MASK;
sd_hi = pread_w(addr + (i * 8) + 4); sd_hi = pread_w(addr + (i * 8) + 4, BUS_PER);
base = (sec << 14 | i << 1) << 16; base = (sec << 14 | i << 1) << 16;
pages = ((sd_lo & SD_MAX_OFF_MASK) >> 18) + 1; pages = ((sd_lo & SD_MAX_OFF_MASK) >> 18) + 1;

View file

@ -1,6 +1,6 @@
/* 3b2_rev3_mmu.h: AT&T 3B2/600G MMU (WE32201) Header /* 3b2_rev3_mmu.h: WE32201 MMU
Copyright (c) 2020, Seth J. Morabito Copyright (c) 2020-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -28,8 +28,8 @@
from the author. from the author.
*/ */
#ifndef _3B2_1000_MMU_H_ #ifndef _3B2_REV3_MMU_H_
#define _3B2_1000_MMU_H_ #define _3B2_REV3_MMU_H_
#include "3b2_defs.h" #include "3b2_defs.h"
@ -55,6 +55,8 @@
#define MMU_FIDNR 14 /* Flush ID Number Register */ #define MMU_FIDNR 14 /* Flush ID Number Register */
#define MMU_VR 15 /* Version Register */ #define MMU_VR 15 /* Version Register */
#define MMU_REV3_VER 0x23 /* Version byte returned by WE32201 MMU */
#define MMU_CONF_M (mmu_state.conf & 0x1) #define MMU_CONF_M (mmu_state.conf & 0x1)
#define MMU_CONF_R ((mmu_state.conf & 0x2) >> 1) #define MMU_CONF_R ((mmu_state.conf & 0x2) >> 1)
#define MMU_CONF_C ((mmu_state.conf & 0x4) >> 1) #define MMU_CONF_C ((mmu_state.conf & 0x4) >> 1)
@ -353,4 +355,4 @@ t_stat mmu_show_sdt(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat mmu_show_sdc(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat mmu_show_sdc(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat mmu_show_pdc(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat mmu_show_pdc(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
#endif /* _3B2_1000_MMU_H_ */ #endif /* _3B2_REV3_MMU_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_rev3_sys.c: AT&T 3B2/600G system definition /* 3b2_rev3_sys.c: Version 3 (3B2/700) System Definition
Copyright (c) 2020, Seth J. Morabito Copyright (c) 2020-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -41,7 +41,7 @@
#include "3b2_stddev.h" #include "3b2_stddev.h"
#include "3b2_timer.h" #include "3b2_timer.h"
char sim_name[] = "AT&T 3B2/600G"; char sim_name[] = "AT&T 3B2/700";
DEVICE *sim_devices[] = { DEVICE *sim_devices[] = {
&cpu_dev, &cpu_dev,

View file

@ -1,79 +0,0 @@
/* 3b2_rev2_stddev.h: 82C54 Interval Timer.
Copyright (c) 2021, Seth J. Morabito
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Except as contained in this notice, the name of the author shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from the author.
*/
#ifndef _3B2_REV3_TIMER_H_
#define _3B2_REV3_TIMER_H_
#include "3b2_defs.h"
/* Timer definitions */
#define TIMER_STP_US 1
#define tmrnum u3
#define tmr up7
#define TIMER_REG_DIVA 0x03
#define TIMER_REG_DIVB 0x07
#define TIMER_REG_DIVC 0x0b
#define TIMER_REG_CTRL 0x0f
#define TIMER_CLR_LATCH 0x13
#define TIMER_MODE(ctr) (((ctr->ctrl) >> 1) & 7)
#define TIMER_RW(ctr) (((ctr->ctrl) >> 4) & 3)
#define CLK_LATCH 0
#define CLK_LSB 1
#define CLK_MSB 2
#define CLK_LMB 3
struct timer_ctr {
uint16 divider;
uint16 val;
uint8 ctrl_latch;
uint16 cnt_latch;
uint8 ctrl;
t_bool r_lmb;
t_bool w_lmb;
t_bool enabled;
t_bool r_ctrl_latch;
t_bool r_cnt_latch;
};
/* 82C54 Timer */
t_stat timer_reset(DEVICE *dptr);
uint32 timer_read(uint32 pa, size_t size);
void timer_write(uint32 pa, uint32 val, size_t size);
t_stat timer0_svc(UNIT *uptr);
t_stat timer1_svc(UNIT *uptr);
t_stat timer2_svc(UNIT *uptr);
/* void timer_tick(uint8 ctrnum); */
void timer_disable(uint8 ctrnum);
void timer_enable(uint8 ctrnum);
#endif /* _3B2_REV3_TIMER_H_ */

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/* 3b2_scsi.h: AT&T 3B2 SCSI (CM195W) Host Adapter /* 3b2_scsi.h: CM195W SCSI Controller CIO Card
Copyright (c) 2020, Seth J. Morabito Copyright (c) 2020-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -36,6 +36,7 @@
/* CIO Opcodes */ /* CIO Opcodes */
#define HA_BOOT 0x0a #define HA_BOOT 0x0a
#define HA_READ_BLK 0x0b #define HA_READ_BLK 0x0b
#define HA_WRITE_BLK 0x0c
#define HA_CNTRL 0x20 #define HA_CNTRL 0x20
#define HA_VERS 0x40 #define HA_VERS 0x40
#define HA_DL_EEDT 0x42 #define HA_DL_EEDT 0x42
@ -47,6 +48,7 @@
#define HA_FORMAT 0x04 #define HA_FORMAT 0x04
#define HA_WRITE 0x0a #define HA_WRITE 0x0a
#define HA_INQUIRY 0x12 #define HA_INQUIRY 0x12
#define HA_MODESEL 0x15
#define HA_MODESNS 0x1a #define HA_MODESNS 0x1a
#define HA_RDCPCTY 0x25 #define HA_RDCPCTY 0x25
#define HA_READ 0x08 #define HA_READ 0x08
@ -88,97 +90,119 @@
#define HA_MAX_CMD 12 #define HA_MAX_CMD 12
#define INQUIRY_MAX 36 #define INQUIRY_MAX 36
#define HA_STAT(ha_stat,cio_stat) { \ #define HA_STAT(tc,ha_stat,cio_stat) { \
ha_state.reply.ssb = (ha_stat); \ ha_state.ts[tc].rep.ssb = (ha_stat); \
ha_state.reply.status = (cio_stat); \ ha_state.ts[tc].rep.status = (cio_stat); \
} }
#define HA_MAX_DTYPE 1 #define HA_MAX_DTYPE 4
/* CDC Wren IV 327 MB Hard Disk (AT&T KS-23483,L3) */ /* Hardware Notes
#define SD327_DTYPE 0 * ==============
#define SD327_PQUAL 0x00 *
#define SD327_SCSI 1 * Disk Drives
#define SD327_BLK 512 * -----------
#define SD327_LBN 640396 *
#define SD327_MANU "AT&T" * There are two emulated SCSI disk drives available.
#define SD327_DESC "KS23483" *
#define SD327_REV "0001" /* TODO: Find real rev */ * 1. 155 MB CDC Wren III (CDC 94161-9)
#define SD327_TPZ 9 * 2. 327 MB CDC Wren IV (CDC 94171-9)
#define SD327_ASEC 3 *
#define SD327_ATPZ 0 * The CDC 94161-9 was also OEMed as the "AT&T KS23483,L3"
#define SD327_ATPU 0 * The CDC 94171-9 was also OEMed as the "AT&T KS23483,L25"
#define SD327_SPT 46 *
#define SD327_CYL 1549 *
#define SD327_HEADS 9 * Tape Drive
#define SD327_PREC 1200 * ----------
#define SD327_RWC 1200 *
#define SD327_STEP 15 * Wangtek 5125EN (AT&T Part number KS23417,L2)
#define SD327_LZ 1549 *
#define SD327_RPM 3600 * DC600A cartridge tape at 120MB (QIC-120 format)
*
*/
/* Wangtek 120MB cartridge tape (AT&T KS-23465) */
#define ST120_DTYPE 1 /* Other SCSI hard disk types
* ---------------------------
*
* These geometries are supported natively and automatically
* by System V Release 3.2.3 UNIX.
*
* 1. CDC 94161-9 155 MB/148 MiB 512 B/s, 35 s/t, 9 head, 965 cyl
* 2. AT&T KS23483 327 MB/312 MiB 512 B/s, 46 s/t, 9 head, 1547 cyl
* (a.k.a CDC 94171-9)
*
* Also supported was a SCSI-to-ESDI bridge controller that used the
* Emulex MD23/S2 SCSI-to-ESDI bridge. It allowed up to four ESDI
* drives to be mapped as LUNs 0-3.
*
*/
/* AT&T 155 MB Hard Disk (35 sec/t, 9 hd, 964 cyl) */
#define SD155_DTYPE 0
#define SD155_PQUAL 0x00
#define SD155_SCSI 1
#define SD155_BLK 512
#define SD155_LBN 303660
#define SD155_MANU "AT&T"
#define SD155_DESC "KS23483"
#define SD155_REV "0000"
/* AT&T 300 MB Hard Disk (43 sec/t, 9 hd, 1514 cyl) */
#define SD300_DTYPE 1
#define SD300_PQUAL 0x00
#define SD300_SCSI 1
#define SD300_BLK 512
#define SD300_LBN 585937
#define SD300_MANU "AT&T"
#define SD300_DESC "KS23483"
#define SD300_REV "0000"
/* AT&T 327 MB Hard Disk (46 sec/t, 9 hd, 1547 cyl) */
#define SD327_DTYPE 2
#define SD327_PQUAL 0x00
#define SD327_SCSI 1
#define SD327_BLK 512
#define SD327_LBN 640458
#define SD327_MANU "AT&T"
#define SD327_DESC "KS23483"
#define SD327_REV "0000"
/* AT&T 630 MB Hard Disk (56 sec/t, 16 hd, 1447 cyl) */
#define SD630_DTYPE 3
#define SD630_PQUAL 0x00
#define SD630_SCSI 1
#define SD630_BLK 512
#define SD630_LBN 1296512
#define SD630_MANU "AT&T"
#define SD630_DESC "KS23483"
#define SD630_REV "0000"
/* Wangtek 120MB cartridge tape */
#define ST120_DTYPE 4
#define ST120_PQUAL 0x00 #define ST120_PQUAL 0x00
#define ST120_SCSI 1 #define ST120_SCSI 1
#define ST120_BLK 512 #define ST120_BLK 512
#define ST120_LBN 266004 #define ST120_LBN 1
#define ST120_MANU "WANGTEK" #define ST120_MANU "WANGTEK"
#define ST120_DESC "KS23465" #define ST120_DESC "KS23465"
#define ST120_REV "CX17" #define ST120_REV "CX17"
#define ST120_DENS 5
#define UNIT_V_DTYPE (SCSI_V_UF + 0) #define UNIT_V_DTYPE (SCSI_V_UF + 0)
#define UNIT_M_DTYPE 0x1f #define UNIT_M_DTYPE 0x1f
#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) #define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE)
#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE)
#define HA_DISK(d) { \ #define HA_DISK(d) \
SCSI_DISK, d##_PQUAL, d##_SCSI, FALSE, d##_BLK, \ { SCSI_DISK, d##_PQUAL, d##_SCSI, FALSE, d##_BLK, \
d##_LBN, d##_MANU, d##_DESC, d##_REV, #d, 0 \ d##_LBN, d##_MANU, d##_DESC, d##_REV, #d }
}
#define HA_TAPE(d) { \ #define HA_TAPE(d) \
SCSI_TAPE, d##_PQUAL, d##_SCSI, TRUE, d##_BLK, \ { SCSI_TAPE, d##_PQUAL, d##_SCSI, TRUE, d##_BLK, \
d##_LBN, d##_MANU, d##_DESC, d##_REV, #d, 0 \ d##_LBN, d##_MANU, d##_DESC, d##_REV, #d }
}
#define HA_SIZE(d) d##_LBN #define HA_SIZE(d) d##_LBN
/* Hardware Notes
* ==============
*
* Disk Drive
* ----------
*
* We emulate a 300-Megabyte Hard Disk, AT&T part number KS23483,L3.
*
* This is the same as a CDC/Imprimis Wren IV 94171-327
*
* 512 bytes per block
* 1,520 cylinders
* 2 alternate cylinders (1518 available)
* 46 Sectors per Track
* 3 Alternate Sectors per Track (43 available)
* 9 tracks per cylinder (9 heads)
*
* Formatted Size: 587,466 blocks
*
*
* Tape Drive
* ----------
*
* Wangtek 5099EN (AT&T Part number KS23417,L2)
*
* DC600A cartridge tape
*
* 512 bytes per block
* 9 tracks
* 13,956 blocks per track
*
* Formatted Size: 125,604 blocks
*
*/
#define HA_JOB_QUICK 0 #define HA_JOB_QUICK 0
#define HA_JOB_EXPRESS 1 #define HA_JOB_EXPRESS 1
#define HA_JOB_FULL 2 #define HA_JOB_FULL 2
@ -212,7 +236,6 @@ typedef struct {
*/ */
typedef struct { typedef struct {
ha_jobtype type; /* Job type */ ha_jobtype type; /* Job type */
t_bool pending; /* Pending or completed? */
uint8 status; /* Result Status */ uint8 status; /* Result Status */
uint8 op; /* Command Opcode */ uint8 op; /* Command Opcode */
uint8 subdev; /* XXTTTLLL; T=Target, L=LUN */ uint8 subdev; /* XXTTTLLL; T=Target, L=LUN */
@ -225,19 +248,24 @@ typedef struct {
#define PUMP_SYSGEN 1 #define PUMP_SYSGEN 1
#define PUMP_COMPLETE 2 #define PUMP_COMPLETE 2
/*
* SCSI Target state
*/
typedef struct {
t_bool pending; /* Service pending */
ha_req req; /* SCSI job request */
ha_resp rep; /* SCSI job reply */
} ha_ts;
/* /*
* General SCSI HA internal state. * General SCSI HA internal state.
*/ */
typedef struct { typedef struct {
uint8 cid; /* Card Backsplane Slot # */ uint8 slot; /* Card Backsplane Slot # */
uint32 pump_state; uint32 pump_state;
uint32 haddr; /* Host address for read/write */ t_bool frq; /* Fast Request Queue enabled */
uint32 hlen; /* Length for read or write */ uint8 edt[HA_EDT_LEN]; /* Equipped Device Table */
t_bool initialized; /* Card has been initialized */ ha_ts ts[8]; /* Target state */
t_bool frq; /* Fast Request Queue enabled */
uint8 edt[HA_EDT_LEN]; /* Equipped Device Table */
ha_req request; /* Current job request */
ha_resp reply; /* Current job reply */
} HA_STATE; } HA_STATE;
t_stat ha_show_type(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat ha_show_type(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
@ -249,12 +277,12 @@ t_stat ha_attach(UNIT *uptr, CONST char *cptr);
t_stat ha_detach(UNIT *uptr); t_stat ha_detach(UNIT *uptr);
void ha_fast_queue_check(); void ha_fast_queue_check();
void ha_sysgen(uint8 cid); void ha_sysgen(uint8 slot);
void ha_express(uint8 cid); void ha_express(uint8 slot);
void ha_full(uint8 cid); void ha_full(uint8 slot);
/* Fast Completion */ /* Fast Completion */
void ha_fcm_express(); void ha_fcm_express(uint8 target);
#endif /* _3B2_SCSI_H_ */ #endif /* _3B2_SCSI_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_stddev.c: AT&T 3B2 miscellaneous system board devices. /* 3b2_stddev.c: Miscellaneous System Board Devices
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -33,14 +33,15 @@
following 3B2 devices: following 3B2 devices:
- nvram Non-Volatile RAM - nvram Non-Volatile RAM
- tod MM58174A Real-Time-Clock - tod MM58174A and MM58274C Real-Time-Clock
- flt Fault Register - flt Fault Register (Rev 3 only)
*/ */
#include "3b2_stddev.h" #include "3b2_stddev.h"
#include "3b2_cpu.h" #include "3b2_cpu.h"
#include "3b2_csr.h" #include "3b2_csr.h"
#include "3b2_timer.h"
DEBTAB sys_deb_tab[] = { DEBTAB sys_deb_tab[] = {
{ "INIT", INIT_MSG, "Init" }, { "INIT", INIT_MSG, "Init" },
@ -124,23 +125,19 @@ t_stat nvram_reset(DEVICE *dptr)
const char *nvram_description(DEVICE *dptr) const char *nvram_description(DEVICE *dptr)
{ {
return "Non-volatile memory, used to store system state between boots.\n"; return "Non-Volatile RAM.\n";
} }
t_stat nvram_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) t_stat nvram_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{ {
fprintf(st, fprintf(st, "Non-Volatile RAM\n\n");
"The NVRAM holds system state between boots. On initial startup,\n" fprintf(st, "The %s device is a small battery-backed, non-volatile RAM\n", dptr->name);
"if no valid NVRAM file is attached, you will see the message:\n" fprintf(st, "used by the 3B2 to hold system configuration and diagnostic data.\n\n");
"\n" fprintf(st, "In order for the simulator to keep track of this data while not\n");
" FW ERROR 1-01: NVRAM SANITY FAILURE\n" fprintf(st, "running, the %s device may be attached to a file, e.g.\n\n", dptr->name);
" DEFAULT VALUES ASSUMED\n" fprintf(st, " sim> ATTACH NVRAM <filename>\n");
" IF REPEATED, CHECK THE BATTERY\n" fprint_show_help(st, dptr);
"\n" fprint_reg_help(st, dptr);
"To avoid this message on subsequent boots, attach a new NVRAM file\n"
"with the SIMH command:\n"
"\n"
" sim> ATTACH NVRAM <filename>\n");
return SCPE_OK; return SCPE_OK;
} }
@ -177,7 +174,6 @@ t_stat nvram_detach(UNIT *uptr)
return r; return r;
} }
uint32 nvram_read(uint32 pa, size_t size) uint32 nvram_read(uint32 pa, size_t size)
{ {
uint32 offset = pa - NVRBASE; uint32 offset = pa - NVRBASE;
@ -229,25 +225,103 @@ void nvram_write(uint32 pa, uint32 val, size_t size)
} }
/* /*
* MM58174A Time Of Day Clock * MM58174A and MM58274C Time Of Day Clock.
*
* In addition to keeping track of time of day in tenths of seconds,
* this device is also used as the simulator's primary calibrated
* real-time clock. It operates at the speed of 100Hz, with every
* tenth step incrementing the time-of-day counter.
*/ */
#define TOD_12H(TD) (((TD)->clkset & 1) == 0)
#define TOD_BCDH(V) (((V) / 10) & 0xf)
#define TOD_BCDL(V) (((V) % 10) & 0xf)
#define TOD_LYEAR(TD) ((TD)->lyear == 0)
#define TOD_LYEAR_INC(TD) \
do { \
(TD)->lyear = (((TD)->lyear + 1) & 0x3); \
(TD)->clkset &= 3; \
(TD)->clkset |= (TD)->lyear << 2; \
} while(0)
#define CTRL_DISABLE 0x4
#define CLKSET_PM 0x2
#define FLAG_DATA_CHANGED 0x4
#define FLAG_INTERRUPT 0x1
#define MIN_DIFF 5l
#define MAX_DIFF 157680000l
#define CLK_DELAY 10000 /* 10 milliseconds per tick */
#define CLK_TPS 100l /* 100 ticks per second */
static void tod_resync(UNIT *uptr);
static void tod_tick(UNIT *uptr);
static t_stat tod_svc(UNIT *uptr);
static t_bool tod_enabled;
int32 tmr_poll = CLK_DELAY;
int32 tmxr_poll = CLK_DELAY;
UNIT tod_unit = { UNIT tod_unit = {
UDATA(NULL, UNIT_FIX+UNIT_BINK, sizeof(TOD_DATA)) UDATA(&tod_svc, UNIT_FIX|UNIT_BINK|UNIT_IDLE, sizeof(TOD_DATA)), CLK_DELAY
};
REG tod_reg[] = {
{ DRDATAD(POLL, tmr_poll, 24, "Calibrated poll interval") },
{ 0 }
}; };
DEVICE tod_dev = { DEVICE tod_dev = {
"TOD", &tod_unit, NULL, NULL, "TOD", &tod_unit, tod_reg, NULL,
1, 16, 8, 4, 16, 32, 1, 16, 8, 4, 16, 32,
NULL, NULL, &tod_reset, NULL, NULL, &tod_reset,
NULL, &tod_attach, &tod_detach, NULL, &tod_attach, &tod_detach,
NULL, 0, 0, sys_deb_tab, NULL, NULL, NULL, DEV_DEBUG, 0, sys_deb_tab, NULL, NULL,
&tod_help, NULL, NULL, &tod_help, NULL, NULL,
&tod_description &tod_description
}; };
/*
* Attempt to re-sync the TOD by catching up (if lagging) and updating
* the current time stored in the TOD state.
*
* Because this process may be expensive when catching up following a
* very long time without the simulator running, the process will
* short-circuit if the delta is longer than 5 years, or if no
* previous time was recorded.
*/
static void tod_resync(UNIT *uptr)
{
TOD_DATA *td;
time_t delta;
uint32_t catchup_ticks;
if (!(uptr->flags & UNIT_ATT) || uptr->filebuf == NULL) {
return;
}
td = (TOD_DATA *)uptr->filebuf;
if (td->time > 0) {
delta = time(NULL) - td->time;
if (delta > MIN_DIFF && delta < MAX_DIFF) {
catchup_ticks = (uint32_t) delta * CLK_TPS;
sim_debug(EXECUTE_MSG, &tod_dev,
"Catching up with a delta of %ld seconds (%d ticks).\n",
delta, catchup_ticks);
while (catchup_ticks-- > 0) {
tod_tick(&tod_unit);
}
}
}
td->time = time(NULL);
}
t_stat tod_reset(DEVICE *dptr) t_stat tod_reset(DEVICE *dptr)
{ {
int32 t;
if (tod_unit.filebuf == NULL) { if (tod_unit.filebuf == NULL) {
tod_unit.filebuf = calloc(sizeof(TOD_DATA), 1); tod_unit.filebuf = calloc(sizeof(TOD_DATA), 1);
if (tod_unit.filebuf == NULL) { if (tod_unit.filebuf == NULL) {
@ -255,6 +329,14 @@ t_stat tod_reset(DEVICE *dptr)
} }
} }
/* We start in a running state */
tod_enabled = TRUE;
t = sim_rtcn_init_unit(&tod_unit, tod_unit.wait, TMR_CLK);
sim_activate_after(&tod_unit, 1000000/CLK_TPS);
tmr_poll = t;
tmxr_poll = t;
return SCPE_OK; return SCPE_OK;
} }
@ -288,124 +370,191 @@ t_stat tod_detach(UNIT *uptr)
return r; return r;
} }
/* static t_stat tod_svc(UNIT *uptr)
* Re-set the tod_data registers based on the current simulated time.
*/
void tod_resync()
{ {
struct timespec now; TOD_DATA *td = (TOD_DATA *)uptr->filebuf;
struct tm tm; int32 t;
time_t sec;
TOD_DATA *td = (TOD_DATA *)tod_unit.filebuf;
sim_rtcn_get_time(&now, TMR_CLK); /* Re-sync the recorded system time once every second */
sec = now.tv_sec - td->delta; if (tod_enabled) {
tod_tick(uptr);
/* Populate the tm struct based on current sim_time */ if (td->tsec == 0) {
tm = *gmtime(&sec); tod_resync(uptr);
}
td->tsec = 0;
td->unit_sec = tm.tm_sec % 10;
td->ten_sec = tm.tm_sec / 10;
td->unit_min = tm.tm_min % 10;
td->ten_min = tm.tm_min / 10;
td->unit_hour = tm.tm_hour % 10;
td->ten_hour = tm.tm_hour / 10;
/* tm struct stores as 0-11, tod struct as 1-12 */
td->unit_mon = (tm.tm_mon + 1) % 10;
td->ten_mon = (tm.tm_mon + 1) / 10;
td->unit_day = tm.tm_mday % 10;
td->ten_day = tm.tm_mday / 10;
td->year = 1 << ((tm.tm_year - 1) % 4);
}
/*
* Re-calculate the delta between real time and simulated time
*/
void tod_update_delta()
{
struct timespec now;
struct tm tm = {0};
time_t ssec;
TOD_DATA *td = (TOD_DATA *)tod_unit.filebuf;
sim_rtcn_get_time(&now, TMR_CLK);
/* Let the host decide if it is DST or not */
tm.tm_isdst = -1;
/* Compute the simulated seconds value */
tm.tm_sec = (td->ten_sec * 10) + td->unit_sec;
tm.tm_min = (td->ten_min * 10) + td->unit_min;
tm.tm_hour = (td->ten_hour * 10) + td->unit_hour;
/* tm struct stores as 0-11, tod struct as 1-12 */
tm.tm_mon = ((td->ten_mon * 10) + td->unit_mon) - 1;
tm.tm_mday = (td->ten_day * 10) + td->unit_day;
/* We're forced to do this weird arithmetic because the TOD chip
* used by the 3B2 does not store the year. It only stores the
* offset from the nearest leap year. */
switch(td->year) {
case 1: /* Leap Year - 3 */
tm.tm_year = 85;
break;
case 2: /* Leap Year - 2 */
tm.tm_year = 86;
break;
case 4: /* Leap Year - 1 */
tm.tm_year = 87;
break;
case 8: /* Leap Year */
tm.tm_year = 88;
break;
default:
break;
} }
ssec = mktime(&tm); t = sim_rtcn_calb(CLK_TPS, TMR_CLK);
td->delta = (int32)(now.tv_sec - ssec); sim_activate_after(uptr, 1000000/CLK_TPS);
tmr_poll = t;
tmxr_poll = t;
AIO_SET_INTERRUPT_LATENCY(tmr_poll * CLK_TPS);
return SCPE_OK;
} }
/*
* The MM58174 and MM58274 consist of a set of failry "dumb" roll-over
* counters. In an ideal world, we'd just look at the real system time
* and translate that into whatever read the host needs.
* Unfortunately, since the Day-of-Week and Leap Year registers are
* totally independent of whatever the "real" date and time should be,
* this doesn't map very well, and DGMON hardware diagnostics fail.
*
* Instead, we model the behavior of the chip accurately here. Each
* rollover is cascaded to the next highest register, using the same
* logic the chip uses.
*/
static void tod_tick(UNIT *uptr)
{
TOD_DATA *td = (TOD_DATA *)uptr->filebuf;
if (++td->tsec > 99) {
td->tsec = 0;
td->flags |= FLAG_DATA_CHANGED;
if (++td->sec > 59) {
td->sec = 0;
if (++td->min > 59) {
td->min = 0;
td->hour++;
/* 12-hour clock cycles from 1-12, 24-hour clock cycles from 00-23 */
if (TOD_12H(td)) {
if (td->hour == 12) {
td->clkset ^= CLKSET_PM;
}
if (td->hour > 12) {
td->hour = 1;
}
} else if (td->hour > 23) {
td->hour = 0;
}
if ((TOD_12H(td) && td->hour == 12) || (!TOD_12H(td) && td->hour == 0)) {
/* Manage day-of-week */
td->wday++;
if (td->wday > 7) {
td->wday = 1;
}
td->day++;
switch(td->mon) {
case 2: /* FEB */
if (TOD_LYEAR(td)) {
if (td->day > 29) {
td->day = 1;
}
} else {
if (td->day > 28) {
td->day = 1;
}
}
break;
case 4: /* APR */
case 6: /* JUN */
case 9: /* SEP */
case 11: /* NOV */
if (td->day > 30) {
td->day = 1;
}
break;
case 1: /* JAN */
case 3: /* MAR */
case 5: /* MAY */
case 7: /* JUL */
case 8: /* AUG */
case 10: /* OCT */
case 12: /* DEC */
if (td->day > 31) {
td->day = 1;
}
break;
}
if (td->day == 1) {
if (++td->mon > 12) {
td->mon = 1;
TOD_LYEAR_INC(td);
if (++td->year > 99) {
td->year = 0;
}
}
}
}
}
}
}
}
uint32 tod_read(uint32 pa, size_t size) uint32 tod_read(uint32 pa, size_t size)
{ {
uint8 reg; uint8 reg, val;
TOD_DATA *td = (TOD_DATA *)(tod_unit.filebuf); TOD_DATA *td = (TOD_DATA *)(tod_unit.filebuf);
tod_resync(); reg = pa & 0xfc;
reg = pa - TODBASE;
switch(reg) { switch(reg) {
case 0x04: /* 1/10 Sec */ #if defined(REV3)
return td->tsec; case TOD_CTRL:
case 0x08: /* 1 Sec */ val = td->flags;
return td->unit_sec; td->flags &= ~(FLAG_DATA_CHANGED);
case 0x0c: /* 10 Sec */ break;
return td->ten_sec; #endif
case 0x10: /* 1 Min */ case TOD_TSEC:
return td->unit_min; val = TOD_BCDH(td->tsec);
case 0x14: /* 10 Min */ break;
return td->ten_min; case TOD_1SEC:
case 0x18: /* 1 Hour */ val = TOD_BCDL(td->sec);
return td->unit_hour; break;
case 0x1c: /* 10 Hour */ case TOD_10SEC:
return td->ten_hour; val = TOD_BCDH(td->sec);
case 0x20: /* 1 Day */ break;
return td->unit_day; case TOD_1MIN:
case 0x24: /* 10 Day */ val = TOD_BCDL(td->min);
return td->ten_day; break;
case 0x28: /* Day of Week */ case TOD_10MIN:
return td->wday; val = TOD_BCDH(td->min);
case 0x2c: /* 1 Month */ break;
return td->unit_mon; case TOD_1HOUR:
case 0x30: /* 10 Month */ val = TOD_BCDL(td->hour);
return td->ten_mon; break;
case 0x34: /* Year */ case TOD_10HOUR:
return td->year; val = TOD_BCDH(td->hour);
break;
case TOD_1DAY:
val = TOD_BCDL(td->day);
break;
case TOD_10DAY:
val = TOD_BCDH(td->day);
break;
case TOD_1MON:
val = TOD_BCDL(td->mon);
break;
case TOD_10MON:
val = TOD_BCDH(td->mon);
break;
case TOD_WDAY:
val = td->wday;
break;
case TOD_1YEAR:
#if defined(REV3)
val = TOD_BCDL(td->year);
#else
val = td->lyear;
#endif
break;
#if defined(REV3)
case TOD_10YEAR:
val = TOD_BCDH(td->year);
break;
case TOD_SET_INT:
val = td->clkset;
break;
#endif
default: default:
val = 0;
break; break;
} }
return 0; return val;
} }
void tod_write(uint32 pa, uint32 val, size_t size) void tod_write(uint32 pa, uint32 val, size_t size)
@ -413,52 +562,83 @@ void tod_write(uint32 pa, uint32 val, size_t size)
uint32 reg; uint32 reg;
TOD_DATA *td = (TOD_DATA *)(tod_unit.filebuf); TOD_DATA *td = (TOD_DATA *)(tod_unit.filebuf);
reg = pa - TODBASE; /* reg = pa - TODBASE; */
reg = pa & 0xfc;
switch(reg) { switch(reg) {
case 0x04: /* 1/10 Sec */ #if defined(REV3)
td->tsec = (uint8) val; case TOD_CTRL:
break; td->ctrl = (uint8) val;
case 0x08: /* 1 Sec */ if (val & CTRL_DISABLE) {
td->unit_sec = (uint8) val; tod_enabled = FALSE;
break; td->tsec = 0;
case 0x0c: /* 10 Sec */ } else {
td->ten_sec = (uint8) val; tod_enabled = TRUE;
break;
case 0x10: /* 1 Min */
td->unit_min = (uint8) val;
break;
case 0x14: /* 10 Min */
td->ten_min = (uint8) val;
break;
case 0x18: /* 1 Hour */
td->unit_hour = (uint8) val;
break;
case 0x1c: /* 10 Hour */
td->ten_hour = (uint8) val;
break;
case 0x20: /* 1 Day */
td->unit_day = (uint8) val;
break;
case 0x24: /* 10 Day */
td->ten_day = (uint8) val;
break;
case 0x28: /* Day of Week */
td->wday = (uint8) val;
break;
case 0x2c: /* 1 Month */
td->unit_mon = (uint8) val;
break;
case 0x30: /* 10 Month */
td->ten_mon = (uint8) val;
break;
case 0x34: /* Year */
td->year = (uint8) val;
break;
case 0x38:
if (val & 1) {
tod_update_delta();
} }
#else
case TOD_TEST:
/* test mode */
#endif
break;
case TOD_TSEC:
td->tsec = (uint8) val * 10;
break;
case TOD_1SEC:
td->sec = ((td->sec / 10) * 10) + (uint8) val;
break;
case TOD_10SEC:
td->sec = ((uint8) val * 10) + (td->sec % 10);
break;
case TOD_1MIN:
td->min = ((td->min / 10) * 10) + (uint8) val;
break;
case TOD_10MIN:
td->min = ((uint8) val * 10) + (td->min % 10);
break;
case TOD_1HOUR:
td->hour = ((td->hour / 10) * 10) + (uint8) val;
break;
case TOD_10HOUR:
td->hour = ((uint8) val * 10) + (td->hour % 10);
break;
case TOD_1DAY:
td->day = ((td->day / 10) * 10) + (uint8) val;
break;
case TOD_10DAY:
td->day = ((uint8) val * 10) + (td->day % 10);
break;
case TOD_1MON:
td->mon = ((td->mon / 10) * 10) + (uint8) val;
break;
case TOD_10MON:
td->mon = ((uint8) val * 10) + (td->mon % 10);
break;
case TOD_1YEAR:
#if defined(REV3)
td->year = ((td->year / 10) * 10) + (uint8) val;
#else
td->lyear = (uint8) val;
#endif
break;
#if defined(REV3)
case TOD_10YEAR:
td->year = ((uint8) val * 10) + (td->year % 10);
break;
case TOD_SET_INT:
td->clkset = (uint8) val;
if (!TOD_12H(td)) {
/* The AM/PM indicator is always 0 if not in 12H mode */
td->clkset &= ~(CLKSET_PM);
}
td->lyear = (val >> 2) & 3;
break;
#else
case TOD_STARTSTOP:
tod_enabled = val & 1;
break;
#endif
case TOD_WDAY:
td->wday = (uint8)val & 0x7;
break; break;
default: default:
break; break;
@ -467,27 +647,33 @@ void tod_write(uint32 pa, uint32 val, size_t size)
const char *tod_description(DEVICE *dptr) const char *tod_description(DEVICE *dptr)
{ {
return "Time-of-Day clock, used to store system time between boots.\n"; #if defined(REV3)
return("MM58274C real time clock");
#else
return("MM58174A real time clock");
#endif
} }
t_stat tod_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) t_stat tod_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{ {
fprintf(st, char dname[10];
"The TOD is a battery-backed time-of-day clock that holds system\n"
"time between boots. In order to store the time, a file must be\n"
"attached to the TOD device with the SIMH command:\n"
"\n"
" sim> ATTACH TOD <filename>\n"
"\n"
"On a newly installed System V Release 3 UNIX system, no system\n"
"time will be stored in the TOD clock. In order to set the system\n"
"time, run the following command from within UNIX (as root):\n"
"\n"
" # sysadm datetime\n"
"\n"
"On subsequent boots, the correct system time will restored from\n"
"from the TOD.\n");
#if defined(REV3)
snprintf(dname, 10, "MM58274C");
#else
snprintf(dname, 10, "MM58174A");
#endif
fprintf(st, "%s Time-Of-Day Clock (%s)\n\n", dname, dptr->name);
fprintf(st, "The %s controller simulates a National Semiconductor %s\n", dptr->name, dname);
fprintf(st, "real time clock. This clock keeps track of the current system time\n");
fprintf(st, "and date.\n\n");
fprintf(st, "In order to preserve simulated calendar time between simulator runs,\n");
fprintf(st, "the %s clock may be attached to a file which stores its state while\n", dptr->name);
fprintf(st, "the simulator is not running, e.g.:\n\n");
fprintf(st, " sim> ATTACH TOD <filename>\n");
fprint_show_help(st, dptr);
fprint_reg_help(st, dptr);
return SCPE_OK; return SCPE_OK;
} }
@ -500,44 +686,130 @@ t_stat tod_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr
* 0x4C000 and 0x4D000. These latch state of the last address to cause * 0x4C000 and 0x4D000. These latch state of the last address to cause
* a CPU fault. * a CPU fault.
* *
* Bits 00-25: Physical memory address bits 00-25 * Bits 00-25: Physical memory address bits
*
* Fault Register 2 does double duty. It actually consists of four
* words, each of which maps to a memory slot on the system board. If
* occupied, it records the size of memory equipped in the slot, as
* well as information about any memory faults.
*
*
* Bits Active Purpose
* ----------------------------------------------------
* 31-26 H Upper Halfword ECC Syndrome Bits
* 25-20 H Lower Halfword ECC Syndeome Bits
* 19 L I/O Bus or BUB master on Fault
* 18 H Invert low addr bit 2 (DWORD1)
* 17 H Decrement low addr by 4
* 16 L I/O Bus Master on Fault
* 15 L CPU accessing I/O Peripheral
* 14 L BUB Slot 0 master on fault
* 13 L CPU Accessing BUB Peripheral
* 12-11 H BUB peripheral accessed by CPU
* 10 L BUB Slot 1 master on fault
* 9 L BUB Slot 2 master on fault
* 8 L BUB Slot 3 master on fault
* 7-3 N/A Not Used
* 2 L Memory Equipped
* 1-0 H Equipped Memory Size
*
*/ */
uint32 flt_1 = 0; uint32 flt[2] = {0, 0};
uint32 flt_2 = 0;
UNIT flt_unit = { UNIT flt_unit = {
UDATA(NULL, UNIT_FIX+UNIT_BINK, 8) UDATA(NULL, UNIT_FIX+UNIT_BINK, 64)
}; };
REG flt_reg[] = { REG flt_reg[] = {
{ HRDATAD(FLT1, flt[0], 32, "Fault Register 1") },
{ HRDATAD(FLT2, flt[1], 32, "Fault Register 2") },
{ NULL } { NULL }
}; };
DEVICE flt_dev = { DEVICE flt_dev = {
"FLT", &flt_unit, flt_reg, NULL, "FLT", &flt_unit, flt_reg, NULL,
1, 16, 8, 4, 16, 32, 1, 16, 32, 1, 16, 32,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, DEV_DEBUG, 0, sys_deb_tab, NULL, NULL, NULL, DEV_DEBUG, 0, sys_deb_tab, NULL, NULL,
NULL, NULL, NULL, &flt_help, NULL, NULL,
NULL &flt_description
}; };
/*
* Return the configured memory size for a given backplane location.
*/
static uint32 mem_size(uint8 slot) {
switch(MEM_SIZE) {
case MSIZ_8M:
if (slot <= 1) {
return MEM_EQP|MEM_4M;
} else {
return 0;
}
case MSIZ_16M:
return MEM_EQP|MEM_4M;
case MSIZ_32M:
if (slot <= 1) {
return MEM_EQP|MEM_16M;
} else {
return 0;
}
case MSIZ_64M:
return MEM_EQP|MEM_16M;
default:
return 0;
}
}
uint32 flt_read(uint32 pa, size_t size) uint32 flt_read(uint32 pa, size_t size)
{ {
sim_debug(READ_MSG, &flt_dev, sim_debug(EXECUTE_MSG, &flt_dev,
"[%08x] Read from FLT Register at %x\n", "Read from FLT Register at %x\n",
R[NUM_PC], pa); pa);
return 0;
switch(pa) {
case FLTLBASE:
return flt[0];
case FLTHBASE:
return (flt[1] & FLT_MSK) | mem_size(0);
case FLTHBASE + 4:
return (flt[1] & FLT_MSK) | mem_size(1);
case FLTHBASE + 8:
return (flt[1] & FLT_MSK) | mem_size(2);
case FLTHBASE + 12:
return (flt[1] & FLT_MSK) | mem_size(3);
default:
sim_debug(EXECUTE_MSG, &flt_dev,
"Read from FLT Register at %x: FAILURE, NO DATA!!!!\n",
pa);
return 0;
}
} }
void flt_write(uint32 pa, uint32 val, size_t size) void flt_write(uint32 pa, uint32 val, size_t size)
{ {
sim_debug(WRITE_MSG, &flt_dev, sim_debug(EXECUTE_MSG, &flt_dev,
"[%08x] Write to FLT Register at %x (val=%x)\n", "Write to FLT Register at %x (val=%x)\n",
R[NUM_PC], pa, val); pa, val);
return; return;
} }
t_stat flt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf(st, "Fault Register\n\n");
fprintf(st, "The %s device is a pair of 32-bit registers that hold information about\n", dptr->name);
fprintf(st, "system memory faults.\n");
fprint_show_help(st, dptr);
fprint_reg_help(st, dptr);
return SCPE_OK;
}
const char *flt_description(DEVICE *dptr)
{
return "Fault Register";
}
#endif #endif

View file

@ -1,6 +1,6 @@
/* 3b2_stddev.h: AT&T 3B2 miscellaneous system board devices. /* 3b2_stddev.h: Miscellaneous System Board Devices
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -44,39 +44,95 @@ const char *nvram_description(DEVICE *dptr);
t_stat nvram_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); t_stat nvram_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
void nvram_write(uint32 pa, uint32 val, size_t size); void nvram_write(uint32 pa, uint32 val, size_t size);
/* TOD */
typedef struct tod_data { typedef struct tod_data {
int32 delta; /* Delta between simulated time and real time (sec.) */ time_t time; /* System time */
uint8 tsec; /* 1/10 seconds */
uint8 unit_sec; /* 1's column seconds */ uint8 ctrl; /* Control register (Rev 3 only) */
uint8 ten_sec; /* 10's column seconds */ uint8 flags; /* Data Changed & Interrutpt Flags (Rev 3 only) */
uint8 unit_min; /* 1's column minutes */ uint8 clkset; /* Clock / Setting register (Rev 3 only) */
uint8 ten_min; /* 10's column minutes */
uint8 unit_hour; /* 1's column hours */ uint8 tsec; /* 1/100th seconds, 00-99 */
uint8 ten_hour; /* 10's column hours */ uint8 sec; /* Seconds, 00-59 */
uint8 unit_day; /* 1's column day of month */ uint8 min; /* Minutes, 00-59 */
uint8 ten_day; /* 10's column day of month */ uint8 hour; /* Hours, 00-23 */
uint8 wday; /* Day of week (0-6) */ uint8 day; /* Days, 00-27, 28, 29, or 30 */
uint8 unit_mon; /* 1's column month */ uint8 mon; /* Months, 00-11 */
uint8 ten_mon; /* 10's column month */ uint8 year; /* Years, 00-99 (Rev 3 only) */
uint8 year; /* 1, 2, 4, 8 shift register */ uint8 wday; /* Day of Week, 0-6 */
uint8 pad[3]; /* Padding to 32 bytes */ uint8 lyear; /* Years since last leap year */
} TOD_DATA; } TOD_DATA;
void tod_resync(); #if defined(REV2)
#define TOD_TEST 0x00
#define TOD_TSEC 0x04
#define TOD_1SEC 0x08
#define TOD_10SEC 0x0c
#define TOD_1MIN 0x10
#define TOD_10MIN 0x14
#define TOD_1HOUR 0x18
#define TOD_10HOUR 0x1c
#define TOD_1DAY 0x20
#define TOD_10DAY 0x24
#define TOD_WDAY 0x28
#define TOD_1MON 0x2c
#define TOD_10MON 0x30
#define TOD_1YEAR 0x34
#define TOD_STARTSTOP 0x38
#define TOD_INT 0x3c
#else
#define TOD_FLAG_CHG 0x08
#define TOD_FLAG_IRQ 0x01
#define TOD_CTRL 0x00
#define TOD_TSEC 0x04
#define TOD_1SEC 0x08
#define TOD_10SEC 0x0c
#define TOD_1MIN 0x10
#define TOD_10MIN 0x14
#define TOD_1HOUR 0x18
#define TOD_10HOUR 0x1c
#define TOD_1DAY 0x20
#define TOD_10DAY 0x24
#define TOD_1MON 0x28
#define TOD_10MON 0x2c
#define TOD_1YEAR 0x30
#define TOD_10YEAR 0x34
#define TOD_WDAY 0x38
#define TOD_SET_INT 0x3c
#endif
void tod_update_delta(); void tod_update_delta();
t_stat tod_reset(DEVICE *dptr); t_stat tod_reset(DEVICE *dptr);
t_stat tod_attach(UNIT *uptr, CONST char *cptr); t_stat tod_attach(UNIT *uptr, CONST char *cptr);
t_stat tod_detach(UNIT *uptr); t_stat tod_detach(UNIT *uptr);
const char *tod_description(DEVICE *dptr);
t_stat tod_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); t_stat tod_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
const char *tod_description(DEVICE *dptr);
uint32 tod_read(uint32 pa, size_t size); uint32 tod_read(uint32 pa, size_t size);
void tod_write(uint32, uint32 val, size_t size); void tod_write(uint32, uint32 val, size_t size);
/* Global symbols */
extern int32 tmxr_poll;
#if defined(REV3) #if defined(REV3)
/* Fault Register */ /* Fault Register */
#define FLT_MSK 0xffffff00
#define MEM_EQP 0x4
#define MEM_4M 0x2
#define MEM_16M 0x3
uint32 flt_read(uint32 pa, size_t size); uint32 flt_read(uint32 pa, size_t size);
void flt_write(uint32 pa, uint32 val, size_t size); void flt_write(uint32 pa, uint32 val, size_t size);
#endif t_stat flt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
const char *flt_description(DEVICE *dptr);
extern uint32 flt[2];
#endif /* defined(REV3) */
#endif /* _3B2_STDDEV_H_ */ #endif /* _3B2_STDDEV_H_ */

View file

@ -1,6 +1,6 @@
/* 3b2_sys.c: AT&T 3B2 common system definitions /* 3b2_sys.c: Common System Definition
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -53,25 +53,57 @@ const char *sim_stop_messages[SCPE_BASE] = {
"Simulator Error" "Simulator Error"
}; };
/*
* ROM and Binary loader
*
* -r load ROM
* -o for memory, specify origin
*
*/
t_stat sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) t_stat sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
{ {
t_stat r;
int32 i; int32 i;
uint32 addr = 0; uint32 origin = 0, limit = 0;
int32 cnt = 0; int32 cnt = 0;
if ((*cptr != 0) || (flag != 0)) { if (flag) {
return SCPE_ARG; return sim_messagef(SCPE_NOFNC, "Command not implemented.");
} }
addr = R[NUM_PC]; if (sim_switches & SWMASK('R')) {
origin = ROM_BASE;
limit = ROM_BASE + ROM_SIZE;
} else {
origin = 0;
limit = (uint32) cpu_unit.capac;
if (sim_switches & SWMASK('O')) {
origin = (uint32) get_uint(cptr, 16, 0xffffffff, &r);
if (r != SCPE_OK) {
return SCPE_ARG;
}
}
}
while ((i = getc (fileref)) != EOF) { while ((i = Fgetc (fileref)) != EOF) {
pwrite_b(addr, (uint8)i); if (origin >= limit) {
addr++; return SCPE_NXM;
}
if (sim_switches & SWMASK('R')) {
pwrite_b_rom(origin, (uint8)i);
} else {
pwrite_b(origin, (uint8)i, BUS_CPU);
}
origin++;
cnt++; cnt++;
} }
printf ("%d Bytes loaded.\n", cnt); if (sim_switches & SWMASK('R')) {
rom_loaded = TRUE;
sim_messagef(SCPE_OK, "%d bytes loaded into ROM\n", cnt);
} else {
sim_messagef(SCPE_OK, "%d bytes loaded at address 0x%08x\n", cnt, origin - cnt);
}
return SCPE_OK; return SCPE_OK;
} }

View file

@ -1,6 +1,6 @@
/* 3b2_rev2_sys.h: AT&T 3B2 Rev 2 (Model 400) system header /* 3b2_sys.h: Common System Definition
Copyright (c) 2017, Seth J. Morabito Copyright (c) 2017-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation

View file

@ -1,6 +1,6 @@
/* 3b2_rev3_timer.c: 82C54 Interval Timer. /* 3b2_timer.c: 8253/82C54 Interval Timer
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -29,9 +29,7 @@
*/ */
/* /*
* 82C54 Timer. * The 8253/82C54 Timer IC has three interval timers, which we treat
*
* 82C54 (Rev3) Timer IC has three interval timers, which we treat
* here as three units. * here as three units.
* *
* In the 3B2, the three timers are assigned specific purposes: * In the 3B2, the three timers are assigned specific purposes:
@ -64,10 +62,10 @@
* Implementaiton Notes * Implementaiton Notes
* ==================== * ====================
* *
* In general, no attempt has been made to create an accurate * In general, no attempt has been made to create a truly accurate
* emulation of the 82C54 timer. This implementation is truly built * simulation of the 8253/82C54 timer. This implementation is built
* for the 3B2, and even more specifically for System V Unix, which is * for the 3B2, and even more specifically to pass System V timer
* the only operating system ever to have been ported to the 3B2. * "Sanity/Interval Timer" diagnostics.
* *
* - The Bus Timeout Timer is not implemented other than a stub that * - The Bus Timeout Timer is not implemented other than a stub that
* is designed to pass hardware diagnostics. The simulator IO * is designed to pass hardware diagnostics. The simulator IO
@ -77,9 +75,8 @@
* - The System Sanity Timer is also not implemented other than a * - The System Sanity Timer is also not implemented other than a
* stub to pass diagnostics. * stub to pass diagnostics.
* *
* - The main Unix Interval Timer is implemented as a true SIMH clock * - The main Unix Interval Timer is more fully implemented, because
* when set up for the correct mode. In other modes, it likewise * it drives system interrupts in System V UNIX.
* implements a stub designed to pass diagnostics.
*/ */
#include "3b2_cpu.h" #include "3b2_cpu.h"
@ -87,18 +84,50 @@
#include "3b2_defs.h" #include "3b2_defs.h"
#include "3b2_timer.h" #include "3b2_timer.h"
#define MIN_DIVIDER 50
#if defined (REV3)
#define QUICK_DELAY 10
#else
#define QUICK_DELAY 100
#endif
#define DELAY_US(C,N) ((TIMER_MODE(C) == 3) ? \
(TIME_BASE[(N)] * (C)->divider) / 2 : \
TIME_BASE[(N)] * (C)->divider)
#if defined(REV3)
/* Microseconds per step (Version 3 system board):
*
* Timer 0: 10KHz time base
* Timer 1: 100KHz time base
* Timer 2: 500KHz time base
*/
static uint32 TIME_BASE[3] = {
100, 10, 1
};
#else
/* Microseconds per step (Version 2 system board):
*
* Timer 0: 100Khz time base
* Timer 1: 100Khz time base
* Timer 2: 500Khz time base
*/
static uint32 TIME_BASE[3] = {
10, 10, 2
};
#endif
struct timer_ctr TIMERS[3]; struct timer_ctr TIMERS[3];
int32 tmxr_poll = 16667;
UNIT timer_unit[] = { UNIT timer_unit[] = {
{ UDATA(&timer0_svc, 0, 0) }, { UDATA(&tmr_svc, UNIT_IDLE, 0) },
{ UDATA(&timer1_svc, UNIT_IDLE, 0) }, { UDATA(&tmr_svc, UNIT_IDLE, 0) },
{ UDATA(&timer2_svc, 0, 0) }, { UDATA(&tmr_svc, UNIT_IDLE, 0) },
{ NULL } { NULL }
}; };
UNIT *timer_clk_unit = &timer_unit[1]; UNIT *tmr_int_unit = &timer_unit[3];
REG timer_reg[] = { REG timer_reg[] = {
{ HRDATAD(DIV0, TIMERS[0].divider, 16, "Divider (0)") }, { HRDATAD(DIV0, TIMERS[0].divider, 16, "Divider (0)") },
@ -114,11 +143,32 @@ REG timer_reg[] = {
}; };
DEVICE timer_dev = { DEVICE timer_dev = {
"TIMER", timer_unit, timer_reg, NULL, "TMR",
1, 16, 8, 4, 16, 32, timer_unit,
NULL, NULL, &timer_reset, timer_reg,
NULL, NULL, NULL, NULL, NULL,
DEV_DEBUG, 0, sys_deb_tab 3,
16,
8,
4,
16,
32,
NULL,
NULL,
&timer_reset,
NULL,
NULL,
NULL,
NULL,
DEV_DEBUG,
0,
sys_deb_tab,
NULL,
NULL,
&tmr_help,
NULL,
NULL,
&tmr_description
}; };
t_stat timer_reset(DEVICE *dptr) { t_stat timer_reset(DEVICE *dptr) {
@ -126,164 +176,111 @@ t_stat timer_reset(DEVICE *dptr) {
memset(&TIMERS, 0, sizeof(struct timer_ctr) * 3); memset(&TIMERS, 0, sizeof(struct timer_ctr) * 3);
/* Store the timer/counter number in the UNIT */
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
timer_unit[i].tmrnum = i; timer_unit[i].u3 = i;
timer_unit[i].tmr = (void *)&TIMERS[i];
} }
/* TODO: I don't think this is right. Verify. */
/*
if (!sim_is_running) {
t = sim_rtcn_init_unit(timer_clk_unit, TPS_CLK, TMR_CLK);
sim_activate_after_abs(timer_clk_unit, 1000000 / t);
}
*/
return SCPE_OK; return SCPE_OK;
} }
/*
* Inhibit or allow a timer externally.
*/
void timer_gate(uint8 ctrnum, t_bool inhibit)
{
struct timer_ctr *ctr = &TIMERS[ctrnum];
if (inhibit) {
ctr->gate = FALSE;
sim_cancel(&timer_unit[ctrnum]);
} else {
ctr->gate = TRUE;
if (ctr->enabled && !sim_is_active(&timer_unit[ctrnum])) {
sim_activate_after(&timer_unit[ctrnum], DELAY_US(ctr, ctrnum));
ctr->val--;
}
}
}
static void timer_activate(uint8 ctrnum) static void timer_activate(uint8 ctrnum)
{ {
struct timer_ctr *ctr; struct timer_ctr *ctr = &TIMERS[ctrnum];
ctr = &TIMERS[ctrnum]; if (ctr->enabled && ctr->gate) {
if (ctr->divider < MIN_DIVIDER) {
switch (ctrnum) { /* If the timer delay is too short, we need to force a
case TIMER_SANITY: very quick activation */
if ((csr_data & CSRISTIM) == 0) { sim_activate_abs(&timer_unit[ctrnum], QUICK_DELAY);
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] SANITY TIMER: Activating after %d steps\n",
R[NUM_PC], ctr->val);
sim_activate_abs(&timer_unit[ctrnum], ctr->val);
ctr->val--;
} else { } else {
sim_debug(EXECUTE_MSG, &timer_dev, /* Otherwise, use a computed time in microseconds */
"[%08x] SANITY TIMER: Currently disabled, not starting\n", sim_activate_after_abs(&timer_unit[ctrnum], DELAY_US(ctr, ctrnum));
R[NUM_PC]);
} }
break;
case TIMER_INTERVAL:
if ((csr_data & CSRITIM) == 0) {
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] INTERVAL TIMER: Activating after %d ms\n",
R[NUM_PC], ctr->val);
sim_activate_after_abs(&timer_unit[ctrnum], ctr->val);
ctr->val--;
} else {
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] INTERVAL TIMER: Currently disabled, not starting\n",
R[NUM_PC]);
}
break;
case TIMER_BUS:
if ((csr_data & CSRITIMO) == 0) {
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] BUS TIMER: Activating after %d steps\n",
R[NUM_PC], ctr->val);
sim_activate_abs(&timer_unit[ctrnum], (ctr->val - 2));
ctr->val -= 2;
} else {
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] BUS TIMER: Currently disabled, not starting\n",
R[NUM_PC]);
}
break;
default:
break;
} }
} }
void timer_enable(uint8 ctrnum)
{
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] Enabling timer %d\n",
R[NUM_PC], ctrnum);
timer_activate(ctrnum);
}
void timer_disable(uint8 ctrnum)
{
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] Disabling timer %d\n",
R[NUM_PC], ctrnum);
sim_cancel(&timer_unit[ctrnum]);
}
/* /*
* Sanity Timer * Sanity, Non-calibrated Interval, and Bus Timeout Timer service routine
*/ */
t_stat timer0_svc(UNIT *uptr) t_stat tmr_svc(UNIT *uptr)
{ {
struct timer_ctr *ctr; int32 ctr_num = uptr->u3;
uint32 usec_delay;
struct timer_ctr *ctr = &TIMERS[ctr_num];
ctr = (struct timer_ctr *)uptr->tmr; if (ctr == NULL) {
return SCPE_SUB;
}
if (ctr->enabled) { /* If the timer isn't enabled, do nothing. */
sim_debug(EXECUTE_MSG, &timer_dev, if (!ctr->enabled) {
"[%08x] TIMER 0 COMPLETION.\n", return SCPE_OK;
R[NUM_PC]); }
if (!(csr_data & CSRISTIM)) {
sim_debug(EXECUTE_MSG, &timer_dev, sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] TIMER 0 NMI IRQ.\n", "[tmr_svc] Handling timeout for ctr number %d\n",
R[NUM_PC]); ctr_num);
ctr->val = 0xffff;
switch (ctr_num) {
case TMR_SANITY:
#if defined (REV3)
if (!CSR(CSRISTIM) && TIMER_MODE(ctr) != 4) {
cpu_nmi = TRUE; cpu_nmi = TRUE;
CSRBIT(CSRSTIMO, TRUE); CSRBIT(CSRSTIMO, TRUE);
CPU_SET_INT(INT_BUS_TMO); CPU_SET_INT(INT_BUS_TMO);
}
}
return SCPE_OK;
}
/*
* Interval Timer
*/
t_stat timer1_svc(UNIT *uptr)
{
struct timer_ctr *ctr;
int32 t;
ctr = (struct timer_ctr *)uptr->tmr;
if (ctr->enabled && !(csr_data & CSRITIM)) {
/* Fire the IPL 15 clock interrupt */
CSRBIT(CSRCLK, TRUE);
CPU_SET_INT(INT_CLOCK);
}
t = sim_rtcn_calb(TPS_CLK, TMR_CLK);
sim_activate_after_abs(uptr, 1000000/TPS_CLK);
tmxr_poll = t;
return SCPE_OK;
}
/*
* Bus Timeout Timer
*/
t_stat timer2_svc(UNIT *uptr)
{
struct timer_ctr *ctr;
ctr = (struct timer_ctr *)uptr->tmr;
if (ctr->enabled && TIMER_RW(ctr) == CLK_LSB) {
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] TIMER 2 COMPLETION.\n",
R[NUM_PC]);
if (!(csr_data & CSRITIMO)) {
sim_debug(EXECUTE_MSG, &timer_dev,
"[%08x] TIMER 2 IRQ.\n",
R[NUM_PC]);
ctr->val = 0xffff; ctr->val = 0xffff;
}
#endif
break;
case TMR_INT:
if (!CSR(CSRITIM)) {
CSRBIT(CSRCLK, TRUE);
CPU_SET_INT(INT_CLOCK);
if (ctr->enabled && ctr->gate) {
usec_delay = DELAY_US(ctr, TMR_INT);
sim_debug(EXECUTE_MSG, &timer_dev,
"[tmr_svc] Re-triggering TMR_INT in %d usec\n", usec_delay);
sim_activate_after(uptr, usec_delay);
}
ctr->val = 0xffff;
}
break;
case TMR_BUS:
#if defined (REV3)
/* Only used during diagnostics */
if (TIMER_RW(ctr) == CLK_LSB) {
sim_debug(EXECUTE_MSG, &timer_dev,
"[tmr_svc] BUS TIMER FIRING. Setting memory fault and interrupt\n");
CSRBIT(CSRTIMO, TRUE); CSRBIT(CSRTIMO, TRUE);
CPU_SET_INT(INT_BUS_TMO); CPU_SET_INT(INT_BUS_TMO);
/* Also trigger a bus abort */
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
ctr->val = 0xffff;
} }
#endif
break;
} }
return SCPE_OK; return SCPE_OK;
} }
@ -298,6 +295,9 @@ uint32 timer_read(uint32 pa, size_t size)
ctrnum = (reg >> 2) & 0x3; ctrnum = (reg >> 2) & 0x3;
ctr = &TIMERS[ctrnum]; ctr = &TIMERS[ctrnum];
sim_debug(EXECUTE_MSG, &timer_dev,
"timer_read: reg=%x\n", reg);
switch (reg) { switch (reg) {
case TIMER_REG_DIVA: case TIMER_REG_DIVA:
case TIMER_REG_DIVB: case TIMER_REG_DIVB:
@ -307,78 +307,60 @@ uint32 timer_read(uint32 pa, size_t size)
switch (TIMER_RW(ctr)) { switch (TIMER_RW(ctr)) {
case CLK_LSB: case CLK_LSB:
retval = ctr_val & 0xff; retval = ctr_val & 0xff;
sim_debug(READ_MSG, &timer_dev,
"[%08x] [%d] [LSB] val=%d (0x%x)\n",
R[NUM_PC], ctrnum, retval, retval);
break; break;
case CLK_MSB: case CLK_MSB:
retval = (ctr_val & 0xff00) >> 8; retval = (ctr_val & 0xff00) >> 8;
sim_debug(READ_MSG, &timer_dev,
"[%08x] [%d] [MSB] val=%d (0x%x)\n",
R[NUM_PC], ctrnum, retval, retval);
break; break;
case CLK_LMB: case CLK_LMB:
if (ctr->r_ctrl_latch) { if (ctr->r_ctrl_latch) {
ctr->r_ctrl_latch = FALSE; ctr->r_ctrl_latch = FALSE;
retval = ctr->ctrl_latch; retval = ctr->ctrl_latch;
sim_debug(READ_MSG, &timer_dev,
"[%08x] [%d] [LATCH CTRL] val=%d (0x%x)\n",
R[NUM_PC], ctrnum, retval, retval);
} else if (ctr->r_cnt_latch) { } else if (ctr->r_cnt_latch) {
if (ctr->r_lmb) { if (ctr->r_lmb) {
ctr->r_lmb = FALSE; ctr->r_lmb = FALSE;
retval = (ctr->cnt_latch & 0xff00) >> 8; retval = (ctr->cnt_latch & 0xff00) >> 8;
ctr->r_cnt_latch = FALSE; ctr->r_cnt_latch = FALSE;
sim_debug(READ_MSG, &timer_dev,
"[%08x] [%d] [LATCH DATA MSB] val=%d (0x%x)\n",
R[NUM_PC], ctrnum, retval, retval);
} else { } else {
ctr->r_lmb = TRUE; ctr->r_lmb = TRUE;
retval = ctr->cnt_latch & 0xff; retval = ctr->cnt_latch & 0xff;
sim_debug(READ_MSG, &timer_dev,
"[%08x] [%d] [LATCH DATA LSB] val=%d (0x%x)\n",
R[NUM_PC], ctrnum, retval, retval);
} }
} else if (ctr->r_lmb) { } else if (ctr->r_lmb) {
ctr->r_lmb = FALSE; ctr->r_lmb = FALSE;
retval = (ctr_val & 0xff00) >> 8; retval = (ctr_val & 0xff00) >> 8;
sim_debug(READ_MSG, &timer_dev,
"[%08x] [%d] [LMB - MSB] val=%d (0x%x)\n",
R[NUM_PC], ctrnum, retval, retval);
} else { } else {
ctr->r_lmb = TRUE; ctr->r_lmb = TRUE;
retval = ctr_val & 0xff; retval = ctr_val & 0xff;
sim_debug(READ_MSG, &timer_dev,
"[%08x] [%d] [LMB - LSB] val=%d (0x%x)\n",
R[NUM_PC], ctrnum, retval, retval);
} }
break; break;
default: default:
retval = 0; retval = 0;
} }
return retval; break;
case TIMER_REG_CTRL: case TIMER_REG_CTRL:
return ctr->ctrl; retval = ctr->ctrl;
break;
case TIMER_CLR_LATCH: case TIMER_CLR_LATCH:
/* Clearing the timer latch has a side-effect /* Clearing the timer latch has a side-effect
of also clearing pending interrupts */ of also clearing pending interrupts */
CSRBIT(CSRCLK, FALSE); CSRBIT(CSRCLK, FALSE);
CPU_CLR_INT(INT_CLOCK); CPU_CLR_INT(INT_CLOCK);
return 0; /* Acknowledge a clock tick */
sim_rtcn_tick_ack(1, TMR_CLK);
retval = 0;
break;
default: default:
/* Unhandled */ /* Unhandled */
sim_debug(READ_MSG, &timer_dev, retval = 0;
"[%08x] UNHANDLED TIMER READ. ADDR=%08x\n", break;
R[NUM_PC], pa);
return 0;
} }
return retval;
} }
void handle_timer_write(uint8 ctrnum, uint32 val) void handle_timer_write(uint8 ctrnum, uint32 val)
{ {
struct timer_ctr *ctr; struct timer_ctr *ctr;
UNIT *unit = &timer_unit[ctrnum];
ctr = &TIMERS[ctrnum]; ctr = &TIMERS[ctrnum];
ctr->enabled = TRUE; ctr->enabled = TRUE;
@ -387,17 +369,13 @@ void handle_timer_write(uint8 ctrnum, uint32 val)
case CLK_LSB: case CLK_LSB:
ctr->divider = val & 0xff; ctr->divider = val & 0xff;
ctr->val = ctr->divider; ctr->val = ctr->divider;
sim_debug(WRITE_MSG, &timer_dev, sim_debug(EXECUTE_MSG, &timer_dev, "TIMER_WRITE: CTR=%d LSB=%02x\n", ctrnum, val & 0xff);
"[%08x] [%d] [LSB] val=%d (0x%x)\n",
R[NUM_PC], ctrnum, val & 0xff, val & 0xff);
timer_activate(ctrnum); timer_activate(ctrnum);
break; break;
case CLK_MSB: case CLK_MSB:
ctr->divider = (val & 0xff) << 8; ctr->divider = (val & 0xff) << 8;
ctr->val = ctr->divider; ctr->val = ctr->divider;
sim_debug(WRITE_MSG, &timer_dev, sim_debug(EXECUTE_MSG, &timer_dev, "TIMER_WRITE: CTR=%d MSB=%02x\n", ctrnum, val & 0xff);
"[%08x] [%d] [MSB] val=%d (0x%x)\n",
R[NUM_PC], ctrnum, val & 0xff, val & 0xff);
timer_activate(ctrnum); timer_activate(ctrnum);
break; break;
case CLK_LMB: case CLK_LMB:
@ -405,17 +383,13 @@ void handle_timer_write(uint8 ctrnum, uint32 val)
ctr->w_lmb = FALSE; ctr->w_lmb = FALSE;
ctr->divider = (uint16) ((ctr->divider & 0x00ff) | ((val & 0xff) << 8)); ctr->divider = (uint16) ((ctr->divider & 0x00ff) | ((val & 0xff) << 8));
ctr->val = ctr->divider; ctr->val = ctr->divider;
sim_debug(WRITE_MSG, &timer_dev, sim_debug(EXECUTE_MSG, &timer_dev, "TIMER_WRITE: CTR=%d (L/M) MSB=%02x\n", ctrnum, val & 0xff);
"[%08x] [%d] [LMB - MSB] val=%d (0x%x)\n",
R[NUM_PC], ctrnum, val & 0xff, val & 0xff);
timer_activate(ctrnum); timer_activate(ctrnum);
} else { } else {
ctr->w_lmb = TRUE; ctr->w_lmb = TRUE;
ctr->divider = (ctr->divider & 0xff00) | (val & 0xff); ctr->divider = (ctr->divider & 0xff00) | (val & 0xff);
ctr->val = ctr->divider; ctr->val = ctr->divider;
sim_debug(WRITE_MSG, &timer_dev, sim_debug(EXECUTE_MSG, &timer_dev, "TIMER_WRITE: CTR=%d (L/M) LSB=%02x\n", ctrnum, val & 0xff);
"[%08x] [%d] [LMB - LSB] val=%d (0x%x)\n",
R[NUM_PC], ctrnum, val & 0xff, val & 0xff);
} }
break; break;
default: default:
@ -431,6 +405,9 @@ void timer_write(uint32 pa, uint32 val, size_t size)
reg = (uint8) (pa - TIMERBASE); reg = (uint8) (pa - TIMERBASE);
sim_debug(EXECUTE_MSG, &timer_dev,
"timer_write: reg=%x val=%x\n", reg, val);
switch(reg) { switch(reg) {
case TIMER_REG_DIVA: case TIMER_REG_DIVA:
handle_timer_write(0, val); handle_timer_write(0, val);
@ -444,9 +421,6 @@ void timer_write(uint32 pa, uint32 val, size_t size)
case TIMER_REG_CTRL: case TIMER_REG_CTRL:
ctrnum = (val >> 6) & 3; ctrnum = (val >> 6) & 3;
if (ctrnum == 3) { if (ctrnum == 3) {
sim_debug(WRITE_MSG, &timer_dev,
"[%08x] READ BACK COMMAND. DATA=%02x\n",
R[NUM_PC], val);
if (val & 2) { if (val & 2) {
ctr = &TIMERS[0]; ctr = &TIMERS[0];
if ((val & 0x20) == 0) { if ((val & 0x20) == 0) {
@ -481,9 +455,6 @@ void timer_write(uint32 pa, uint32 val, size_t size)
} }
} }
} else { } else {
sim_debug(WRITE_MSG, &timer_dev,
"[%08x] Timer Control Write: timer %d => %02x\n",
R[NUM_PC], ctrnum, val & 0xff);
ctr = &TIMERS[ctrnum]; ctr = &TIMERS[ctrnum];
ctr->ctrl = (uint8) val; ctr->ctrl = (uint8) val;
ctr->enabled = FALSE; ctr->enabled = FALSE;
@ -495,12 +466,41 @@ void timer_write(uint32 pa, uint32 val, size_t size)
break; break;
case TIMER_CLR_LATCH: case TIMER_CLR_LATCH:
sim_debug(WRITE_MSG, &timer_dev, sim_debug(WRITE_MSG, &timer_dev,
"[%08x] unexpected write to clear timer latch\n", "unexpected write to clear timer latch\n");
R[NUM_PC]);
break; break;
default: default:
sim_debug(WRITE_MSG, &timer_dev, sim_debug(WRITE_MSG, &timer_dev,
"[%08x] unknown timer register: %d\n", "unknown timer register: %d\n",
R[NUM_PC], reg); reg);
} }
} }
CONST char *tmr_description(DEVICE *dptr)
{
#if defined (REV3)
return "82C54 Programmable Interval Timer";
#else
return "8253 Programmable Interval Timer";
#endif
}
t_stat tmr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
#if defined (REV3)
fprintf(st, "82C54 Programmable Interval Timer (TMR)\n\n");
fprintf(st, "The TMR device implements three programmable timers used by the 3B2/700\n");
#else
fprintf(st, "8253 Programmable Interval Timer (TMR)\n\n");
fprintf(st, "The TMR device implements three programmable timers used by the 3B2/400\n");
#endif
fprintf(st, "to perform periodic tasks and sanity checks.\n\n");
fprintf(st, "- TMR0: Used as a system sanity timer.\n");
fprintf(st, "- TMR1: Used as a periodic 10 millisecond interval timer.\n");
fprintf(st, "- TMR2: Used as a bus timeout timer.\n");
fprint_set_help(st, dptr);
fprint_show_help(st, dptr);
fprint_reg_help(st, dptr);
return SCPE_OK;
}

View file

@ -1,6 +1,6 @@
/* 3b2_timer.h: Common TIMER header /* 3b2_timer.h: 8253/82C54 Interval Timer
Copyright (c) 2021, Seth J. Morabito Copyright (c) 2021-2022, Seth J. Morabito
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
@ -31,12 +31,48 @@
#ifndef _3B2_TIMER_H_ #ifndef _3B2_TIMER_H_
#define _3B2_TIMER_H_ #define _3B2_TIMER_H_
#if defined(REV3) #include "3b2_defs.h"
#include "3b2_rev3_timer.h"
#else
#include "3b2_rev2_timer.h"
#endif
extern int32 tmxr_poll; #define TIMER_REG_DIVA 0x03
#define TIMER_REG_DIVB 0x07
#define TIMER_REG_DIVC 0x0b
#define TIMER_REG_CTRL 0x0f
#define TIMER_CLR_LATCH 0x13
#define TIMER_MODE(ctr) (((ctr->ctrl) >> 1) & 7)
#define TIMER_RW(ctr) (((ctr->ctrl) >> 4) & 3)
#define CLK_LATCH 0
#define CLK_LSB 1
#define CLK_MSB 2
#define CLK_LMB 3
#define TMR_SANITY 0
#define TMR_INT 1
#define TMR_BUS 2
struct timer_ctr {
uint16 divider;
uint16 val;
uint8 ctrl_latch;
uint16 cnt_latch;
uint8 ctrl;
t_bool r_lmb;
t_bool w_lmb;
t_bool enabled;
t_bool gate;
t_bool r_ctrl_latch;
t_bool r_cnt_latch;
};
t_stat timer_reset(DEVICE *dptr);
uint32 timer_read(uint32 pa, size_t size);
void timer_write(uint32 pa, uint32 val, size_t size);
void timer_gate(uint8 ctrnum, t_bool inhibit);
t_stat tmr_svc(UNIT *uptr);
t_stat tmr_int_svc(UNIT *uptr);
CONST char *tmr_description(DEVICE *dptr);
t_stat tmr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
#endif /* _3B2_TIMER_H_ */ #endif /* _3B2_TIMER_H_ */

View file

@ -3,18 +3,19 @@ AT&T 3B2 Simulator
This module contains the source for two simulators: This module contains the source for two simulators:
1. A simulator for the AT&T 3B2 Model 400 computer (Rev. 2) 1. A simulator for the AT&T 3B2/400 computer (3b2 or 3B2.EXE)
2. A simulator for the AT&T 3B2 Model 600 computer (Rev. 3) 2. A simulator for the AT&T 3B2/700 computer (3b2-700 or 3B2-700.EXE)
The 3B2/400 simulator is complete, usable, and robust. The 3B2/600 simulator The 3B2/400 simulator is complete, usable, and robust. The 3B2/700
is not yet usable, however. It is under active development. simulator is under active development and is not yet considered
stable.
Full documentation for the 3B2 simulator is available here: Full documentation for the 3B2 simulator is available here:
- https://loomcom.com/3b2/emulator.html - https://loomcom.com/3b2/emulator.html
Devices 3B2/400 Simulator Devices
------- -------------------------
The following devices are simulated. The SIMH names for the simulated The following devices are simulated. The SIMH names for the simulated
devices are given in parentheses: devices are given in parentheses:
@ -22,7 +23,7 @@ devices are given in parentheses:
- 3B2 Model 400 System Board with 1MB, 2MB, or 4MB RAM (CSR, NVRAM) - 3B2 Model 400 System Board with 1MB, 2MB, or 4MB RAM (CSR, NVRAM)
- WE32100 CPU (CPU) - WE32100 CPU (CPU)
- WE32101 MMU (MMU) - WE32101 MMU (MMU)
- PD8253 Interval Timer (TIMER) - PD8253 Interval Timer (TMR)
- AM9517 DMA controller (DMAC) - AM9517 DMA controller (DMAC)
- SCN2681A Integrated DUART (IU) - SCN2681A Integrated DUART (IU)
- TMS2793 Integrated Floppy Controller (IFLOPPY) - TMS2793 Integrated Floppy Controller (IFLOPPY)
@ -33,8 +34,8 @@ devices are given in parentheses:
- CM195B 4-port Serial MUX (PORTS) - CM195B 4-port Serial MUX (PORTS)
- CM195H Cartridge Tape Controller (CTC) - CM195H Cartridge Tape Controller (CTC)
Usage 3B2/400 Simulator Usage
----- -----------------------
To boot the 3B2 simulator into firmware mode, simply type: To boot the 3B2 simulator into firmware mode, simply type:
@ -80,12 +81,12 @@ You should then be prompted with:
Here, you may type a question mark (?) and press Enter to see a list Here, you may type a question mark (?) and press Enter to see a list
of available firmware programs. of available firmware programs.
Booting UNIX SVR3 Booting UNIX SVR3 on the 3B2/400
----------------- --------------------------------
UNIX SVR3 is the only operating system available for the 3B2. To boot UNIX System V UNIX is the only operating system available for the 3B2.
UNIX, attach the first disk image from the 3B2 "Essential Utilities" To boot UNIX, attach the first disk image from the 3B2 "Essential
distribution. Utilities" distribution.
sim> ATTACH IFLOPPY <floppy-image> sim> ATTACH IFLOPPY <floppy-image>
sim> BOOT sim> BOOT
@ -104,8 +105,8 @@ carriage return.
Enter Load Device Option Number [0 (FD5)]: Enter Load Device Option Number [0 (FD5)]:
Installing SVR3 Installing SVR3 on the 3B2/400
--------------- ------------------------------
To install SVR3 to the first hard disk, first, attach a new image To install SVR3 to the first hard disk, first, attach a new image
to the IDISK0 device: to the IDISK0 device:
@ -127,3 +128,8 @@ integrated disk 0. Parameters for the default 72MB hard disk are:
After low-level formatting integrated disk 0, boot the file `unix` After low-level formatting integrated disk 0, boot the file `unix`
from the first diskette of the 3B2 "Essential Utilities" distribution, from the first diskette of the 3B2 "Essential Utilities" distribution,
and follow the prompts. and follow the prompts.
More information about installing AT&T System V Release 3.2 UNIX is
available on the web:
- https://loomcom.com/3b2/installing_unix.html

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -1,34 +0,0 @@
:: 3b2-diag.ini
:: This script will run the available 3B2/400 core diagnostics.
::
cd %~p0
set runlimit 2 minutes
set on
on error ignore
on runtime echof "\r\n*** Test Runtime Limit %SIM_RUNLIMIT% %SIM_RUNLIMIT_UNITS% Exceeded ***\n"; exit 1
set env DIAG_QUIET_MODE=0
if ("%1" == "-v") set console notelnet
else set -qu console telnet=localhost:65432,telnet=buffered; set env -a DIAG_QUIET_MODE=1
:: Set maximum memory size
set cpu 4M
if not exist rev2_diags.dsk echof "\r\nMISSING - Diagnostic disk image '%~p0rev2_diags.dsk' is missing\n"; exit 1
attach -rq ifloppy rev2_diags.dsk
:: Initial setup
expect "UTILITIES GUIDE" send "mcp\r"; go -q
expect "Enter name of program to execute [ ]:" send "filledt\r"; go -q
expect "Enter Load Device Option Number [0 (FD5)]:" send "0\r"; go -q
expect "Enter name of program to execute [ ]:" send "dgmon\r"; go -q
expect "Enter Load Device Option Number [0 (FD5)]:" send "0\r"; go -q
expect "Did you boot filledt? [y or n] (n)" send "y\r"; go -q
expect "DGMON > " send "DGN SBD\r"; go -q
expect "SBD 0 (IN SLOT 0) DIAGNOSTICS PASSED" echof; echof "PASSED: 3B2 DGMON SBD Diagnostics."; exit 0
expect [4] "FAIL" echof; echof "FAILED: 3B2 DGMON SBD Diagnostics."; exit 1
:: Run tests
if (DIAG_QUIET_MODE) echof "\nStarting 3B2 DGMON SBD Diagnostics."
boot -q CPU
return

Binary file not shown.

View file

@ -2,9 +2,9 @@
<VisualStudioProject <VisualStudioProject
ProjectType="Visual C++" ProjectType="Visual C++"
Version="9.00" Version="9.00"
Name="3B2" Name="3B2-400"
ProjectGUID="{56178F08-8783-4ADA-820C-20C06412678E}" ProjectGUID="{56178F08-8783-4ADA-820C-20C06412678E}"
RootNamespace="3B2" RootNamespace="3B2-400"
Keyword="Win32Proj" Keyword="Win32Proj"
TargetFrameworkVersion="131072" TargetFrameworkVersion="131072"
> >
@ -26,7 +26,7 @@
<Tool <Tool
Name="VCPreBuildEventTool" Name="VCPreBuildEventTool"
Description="Check for required build dependencies &amp; git commit id" Description="Check for required build dependencies &amp; git commit id"
CommandLine="Pre-Build-Event.cmd &quot;$(TargetDir)$(TargetName).exe&quot; LIBPCRE ROM" CommandLine="Pre-Build-Event.cmd &quot;$(TargetDir)$(TargetName).exe&quot; LIBPCRE"
/> />
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
@ -94,7 +94,7 @@
<Tool <Tool
Name="VCPostBuildEventTool" Name="VCPostBuildEventTool"
Description="Running Available Tests" Description="Running Available Tests"
CommandLine="Post-Build-Event.cmd 3B2 &quot;$(TargetDir)$(TargetName).exe&quot;" CommandLine="Post-Build-Event.cmd 3B2-400 &quot;$(TargetDir)$(TargetName).exe&quot;"
/> />
</Configuration> </Configuration>
<Configuration <Configuration
@ -107,7 +107,7 @@
<Tool <Tool
Name="VCPreBuildEventTool" Name="VCPreBuildEventTool"
Description="Check for required build dependencies &amp; git commit id" Description="Check for required build dependencies &amp; git commit id"
CommandLine="Pre-Build-Event.cmd &quot;$(TargetDir)$(TargetName).exe&quot; LIBPCRE ROM" CommandLine="Pre-Build-Event.cmd &quot;$(TargetDir)$(TargetName).exe&quot; LIBPCRE"
/> />
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
@ -180,7 +180,7 @@
<Tool <Tool
Name="VCPostBuildEventTool" Name="VCPostBuildEventTool"
Description="Running Available Tests" Description="Running Available Tests"
CommandLine="Post-Build-Event.cmd 3B2 &quot;$(TargetDir)$(TargetName).exe&quot;" CommandLine="Post-Build-Event.cmd 3B2-400 &quot;$(TargetDir)$(TargetName).exe&quot;"
/> />
</Configuration> </Configuration>
</Configurations> </Configurations>
@ -220,15 +220,7 @@
> >
</File> </File>
<File <File
RelativePath="..\3B2\3b2_sys.c" RelativePath="..\3B2\3b2_mau.c"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_mau.c"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_mmu.c"
> >
</File> </File>
<File <File
@ -243,22 +235,30 @@
RelativePath="..\3B2\3b2_ports.c" RelativePath="..\3B2\3b2_ports.c"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_rev2_sys.c"
>
</File>
<File <File
RelativePath="..\3B2\3b2_rev2_csr.c" RelativePath="..\3B2\3b2_rev2_csr.c"
> >
</File> </File>
<File <File
RelativePath="..\3B2\3b2_rev2_timer.c" RelativePath="..\3B2\3b2_rev2_mmu.c"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_sys.c"
> >
</File> </File>
<File <File
RelativePath="..\3B2\3b2_stddev.c" RelativePath="..\3B2\3b2_stddev.c"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_sys.c"
>
</File>
<File
RelativePath="..\3B2\3b2_timer.c"
>
</File>
<File <File
RelativePath="..\..\windows-build\pthreads\pthread.c" RelativePath="..\..\windows-build\pthreads\pthread.c"
> >
@ -499,6 +499,10 @@
RelativePath="..\3B2\3b2_cpu.h" RelativePath="..\3B2\3b2_cpu.h"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_csr.h"
>
</File>
<File <File
RelativePath="..\3B2\3b2_ctc.h" RelativePath="..\3B2\3b2_ctc.h"
> >
@ -507,10 +511,6 @@
RelativePath="..\3B2\3b2_defs.h" RelativePath="..\3B2\3b2_defs.h"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_rev2_defs.h"
>
</File>
<File <File
RelativePath="..\3B2\3b2_dmac.h" RelativePath="..\3B2\3b2_dmac.h"
> >
@ -532,21 +532,17 @@
> >
</File> </File>
<File <File
RelativePath="..\3B2\3b2_sys.h" RelativePath="..\3B2\3b2_mau.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_mau.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_mmu.h"
> >
</File> </File>
<File <File
RelativePath="..\3B2\3b2_mem.h" RelativePath="..\3B2\3b2_mem.h"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_mmu.h"
>
</File>
<File <File
RelativePath="..\3B2\3b2_ni.h" RelativePath="..\3B2\3b2_ni.h"
> >
@ -555,22 +551,18 @@
RelativePath="..\3B2\3b2_ports.h" RelativePath="..\3B2\3b2_ports.h"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_rev2_sys.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_csr.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_timer.h"
>
</File>
<File <File
RelativePath="..\3B2\3b2_stddev.h" RelativePath="..\3B2\3b2_stddev.h"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_sys.h"
>
</File>
<File
RelativePath="..\3B2\3b2_timer.h"
>
</File>
<File <File
RelativePath="..\scp.h" RelativePath="..\scp.h"
> >

View file

@ -2,9 +2,9 @@
<VisualStudioProject <VisualStudioProject
ProjectType="Visual C++" ProjectType="Visual C++"
Version="9.00" Version="9.00"
Name="3B2-600" Name="3B2-700"
ProjectGUID="{A7AE7747-DFA0-49F5-9D6C-9094657A8EE3}" ProjectGUID="{A7AE7747-DFA0-49F5-9D6C-9094657A8EE3}"
RootNamespace="3B2-600" RootNamespace="3B2-700"
Keyword="Win32Proj" Keyword="Win32Proj"
TargetFrameworkVersion="131072" TargetFrameworkVersion="131072"
> >
@ -26,7 +26,7 @@
<Tool <Tool
Name="VCPreBuildEventTool" Name="VCPreBuildEventTool"
Description="Check for required build dependencies &amp; git commit id" Description="Check for required build dependencies &amp; git commit id"
CommandLine="Pre-Build-Event.cmd &quot;$(TargetDir)$(TargetName).exe&quot; LIBPCRE ROM" CommandLine="Pre-Build-Event.cmd &quot;$(TargetDir)$(TargetName).exe&quot; LIBPCRE"
/> />
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
@ -94,7 +94,7 @@
<Tool <Tool
Name="VCPostBuildEventTool" Name="VCPostBuildEventTool"
Description="Running Available Tests" Description="Running Available Tests"
CommandLine="Post-Build-Event.cmd 3B2-600 &quot;$(TargetDir)$(TargetName).exe&quot;" CommandLine="Post-Build-Event.cmd 3B2-700 &quot;$(TargetDir)$(TargetName).exe&quot;"
/> />
</Configuration> </Configuration>
<Configuration <Configuration
@ -107,7 +107,7 @@
<Tool <Tool
Name="VCPreBuildEventTool" Name="VCPreBuildEventTool"
Description="Check for required build dependencies &amp; git commit id" Description="Check for required build dependencies &amp; git commit id"
CommandLine="Pre-Build-Event.cmd &quot;$(TargetDir)$(TargetName).exe&quot; LIBPCRE ROM" CommandLine="Pre-Build-Event.cmd &quot;$(TargetDir)$(TargetName).exe&quot; LIBPCRE"
/> />
<Tool <Tool
Name="VCCustomBuildTool" Name="VCCustomBuildTool"
@ -180,7 +180,7 @@
<Tool <Tool
Name="VCPostBuildEventTool" Name="VCPostBuildEventTool"
Description="Running Available Tests" Description="Running Available Tests"
CommandLine="Post-Build-Event.cmd 3B2-600 &quot;$(TargetDir)$(TargetName).exe&quot;" CommandLine="Post-Build-Event.cmd 3B2-700 &quot;$(TargetDir)$(TargetName).exe&quot;"
/> />
</Configuration> </Configuration>
</Configurations> </Configurations>
@ -211,6 +211,10 @@
RelativePath="..\3B2\3b2_iu.c" RelativePath="..\3B2\3b2_iu.c"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_mau.c"
>
</File>
<File <File
RelativePath="..\3B2\3b2_mem.c" RelativePath="..\3B2\3b2_mem.c"
> >
@ -223,10 +227,6 @@
RelativePath="..\3B2\3b2_ports.c" RelativePath="..\3B2\3b2_ports.c"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_rev2_mau.c"
>
</File>
<File <File
RelativePath="..\3B2\3b2_rev3_csr.c" RelativePath="..\3B2\3b2_rev3_csr.c"
> >
@ -239,10 +239,6 @@
RelativePath="..\3B2\3b2_rev3_sys.c" RelativePath="..\3B2\3b2_rev3_sys.c"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_rev3_timer.c"
>
</File>
<File <File
RelativePath="..\3B2\3b2_scsi.c" RelativePath="..\3B2\3b2_scsi.c"
> >
@ -255,6 +251,10 @@
RelativePath="..\3B2\3b2_sys.c" RelativePath="..\3B2\3b2_sys.c"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_timer.c"
>
</File>
<File <File
RelativePath="..\..\windows-build\pthreads\pthread.c" RelativePath="..\..\windows-build\pthreads\pthread.c"
> >
@ -500,7 +500,7 @@
> >
</File> </File>
<File <File
RelativePath="..\3B2\3b2_ctc.h" RelativePath="..\3B2\3b2_csr.h"
> >
</File> </File>
<File <File
@ -527,10 +527,18 @@
RelativePath="..\3B2\3b2_iu.h" RelativePath="..\3B2\3b2_iu.h"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_mau.h"
>
</File>
<File <File
RelativePath="..\3B2\3b2_mem.h" RelativePath="..\3B2\3b2_mem.h"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_mmu.h"
>
</File>
<File <File
RelativePath="..\3B2\3b2_ni.h" RelativePath="..\3B2\3b2_ni.h"
> >
@ -539,30 +547,6 @@
RelativePath="..\3B2\3b2_ports.h" RelativePath="..\3B2\3b2_ports.h"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_rev3_csr.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev3_defs.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_mau.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev3_mmu.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev3_sys.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev3_timer.h"
>
</File>
<File <File
RelativePath="..\3B2\3b2_stddev.h" RelativePath="..\3B2\3b2_stddev.h"
> >
@ -571,6 +555,10 @@
RelativePath="..\3B2\3b2_sys.h" RelativePath="..\3B2\3b2_sys.h"
> >
</File> </File>
<File
RelativePath="..\3B2\3b2_timer.h"
>
</File>
<File <File
RelativePath="..\scp.h" RelativePath="..\scp.h"
> >

View file

@ -228,7 +228,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scelbi", "scelbi.vcproj", "
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "3B2", "3B2.vcproj", "{56178F08-8783-4ADA-820C-20C06412678E}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "3B2-400", "3B2-400.vcproj", "{56178F08-8783-4ADA-820C-20C06412678E}"
ProjectSection(ProjectDependencies) = postProject ProjectSection(ProjectDependencies) = postProject
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
EndProjectSection EndProjectSection
@ -373,7 +373,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tt2500", "tt2500.vcproj", "
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "3B2-600", "3B2-600.vcproj", "{A7AE7747-DFA0-49F5-9D6C-9094657A8EE3}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "3B2-700", "3B2-700.vcproj", "{A7AE7747-DFA0-49F5-9D6C-9094657A8EE3}"
ProjectSection(ProjectDependencies) = postProject ProjectSection(ProjectDependencies) = postProject
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
EndProjectSection EndProjectSection

View file

@ -2066,25 +2066,25 @@ KS10_OPT = -DKS=1 -DUSE_INT64 -I $(KS10D) -I $(PDP11D) ${NETWORK_OPT}
ATT3B2D = ${SIMHD}/3B2 ATT3B2D = ${SIMHD}/3B2
ATT3B2M400 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \ ATT3B2M400 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \
${ATT3B2D}/3b2_rev2_sys.c ${ATT3B2D}/3b2_rev2_mmu.c \ ${ATT3B2D}/3b2_rev2_sys.c ${ATT3B2D}/3b2_rev2_mmu.c \
${ATT3B2D}/3b2_rev2_mau.c ${ATT3B2D}/3b2_rev2_csr.c \ ${ATT3B2D}/3b2_mau.c ${ATT3B2D}/3b2_rev2_csr.c \
${ATT3B2D}/3b2_rev2_timer.c ${ATT3B2D}/3b2_stddev.c \ ${ATT3B2D}/3b2_timer.c ${ATT3B2D}/3b2_stddev.c \
${ATT3B2D}/3b2_mem.c ${ATT3B2D}/3b2_iu.c \ ${ATT3B2D}/3b2_mem.c ${ATT3B2D}/3b2_iu.c \
${ATT3B2D}/3b2_if.c ${ATT3B2D}/3b2_id.c \ ${ATT3B2D}/3b2_if.c ${ATT3B2D}/3b2_id.c \
${ATT3B2D}/3b2_dmac.c ${ATT3B2D}/3b2_io.c \ ${ATT3B2D}/3b2_dmac.c ${ATT3B2D}/3b2_io.c \
${ATT3B2D}/3b2_ports.c ${ATT3B2D}/3b2_ctc.c \ ${ATT3B2D}/3b2_ports.c ${ATT3B2D}/3b2_ctc.c \
${ATT3B2D}/3b2_ni.c ${ATT3B2D}/3b2_ni.c
ATT3B2M400_OPT = -DUSE_INT64 -DUSE_ADDR64 -DREV2 -I ${ATT3B2D} ${NETWORK_OPT} ATT3B2M400_OPT = -DUSE_INT64 -DUSE_ADDR64 -DREV2 -I ${ATT3B2D} ${NETWORK_OPT}
ATT3B2M600 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \ ATT3B2M700 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \
${ATT3B2D}/3b2_rev3_sys.c ${ATT3B2D}/3b2_rev3_mmu.c \ ${ATT3B2D}/3b2_rev3_sys.c ${ATT3B2D}/3b2_rev3_mmu.c \
${ATT3B2D}/3b2_rev2_mau.c ${ATT3B2D}/3b2_rev3_csr.c \ ${ATT3B2D}/3b2_mau.c ${ATT3B2D}/3b2_rev3_csr.c \
${ATT3B2D}/3b2_rev3_timer.c ${ATT3B2D}/3b2_stddev.c \ ${ATT3B2D}/3b2_timer.c ${ATT3B2D}/3b2_stddev.c \
${ATT3B2D}/3b2_mem.c ${ATT3B2D}/3b2_iu.c \ ${ATT3B2D}/3b2_mem.c ${ATT3B2D}/3b2_iu.c \
${ATT3B2D}/3b2_if.c ${ATT3B2D}/3b2_dmac.c \ ${ATT3B2D}/3b2_if.c ${ATT3B2D}/3b2_dmac.c \
${ATT3B2D}/3b2_io.c ${ATT3B2D}/3b2_ports.c \ ${ATT3B2D}/3b2_io.c ${ATT3B2D}/3b2_ports.c \
${ATT3B2D}/3b2_scsi.c ${ATT3B2D}/3b2_ni.c ${ATT3B2D}/3b2_scsi.c ${ATT3B2D}/3b2_ni.c
ATT3B2M600_OPT = -DUSE_INT64 -DUSE_ADDR64 -DREV3 -I ${ATT3B2D} ${NETWORK_OPT} ATT3B2M700_OPT = -DUSE_INT64 -DUSE_ADDR64 -DREV3 -I ${ATT3B2D} ${NETWORK_OPT}
SIGMAD = ${SIMHD}/sigma SIGMAD = ${SIMHD}/sigma
SIGMA = ${SIGMAD}/sigma_cpu.c ${SIGMAD}/sigma_sys.c ${SIGMAD}/sigma_cis.c \ SIGMA = ${SIGMAD}/sigma_cpu.c ${SIGMAD}/sigma_sys.c ${SIGMAD}/sigma_cis.c \
@ -2155,13 +2155,13 @@ ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \
nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri \ nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri \
i7094 ibm1130 id16 id32 sds lgp h316 cdc1700 \ i7094 ibm1130 id16 id32 sds lgp h316 cdc1700 \
swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 intel-mds \ swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 intel-mds \
scelbi 3b2 i701 i704 i7010 i7070 i7080 i7090 \ scelbi 3b2 3b2-700 i701 i704 i7010 i7070 i7080 i7090 \
sigma uc15 pdp10-ka pdp10-ki pdp10-kl pdp10-ks pdp6 i650 \ sigma uc15 pdp10-ka pdp10-ki pdp10-kl pdp10-ks pdp6 i650 \
imlac tt2500 sel32 imlac tt2500 sel32
all : ${ALL} all : ${ALL}
EXPERIMENTAL = cdc1700 3b2-600 EXPERIMENTAL = cdc1700
experimental : ${EXPERIMENTAL} experimental : ${EXPERIMENTAL}
@ -2806,22 +2806,29 @@ ifneq (,$(call find_test,${B5500D},b5500))
$@ $(call find_test,${B5500D},b5500) ${TEST_ARG} $@ $(call find_test,${B5500D},b5500) ${TEST_ARG}
endif endif
3b2-400 : 3b2
3b2 : ${BIN}3b2${EXE} 3b2 : ${BIN}3b2${EXE}
${BIN}3b2${EXE} : ${ATT3B2M400} ${SIM} ${BUILD_ROMS} ${BIN}3b2${EXE} : ${ATT3B2M400} ${SIM}
${MKDIRBIN} ${MKDIRBIN}
${CC} ${ATT3B2M400} ${SIM} ${ATT3B2M400_OPT} ${CC_OUTSPEC} ${LDFLAGS} ${CC} ${ATT3B2M400} ${SIM} ${ATT3B2M400_OPT} ${CC_OUTSPEC} ${LDFLAGS}
ifeq (${WIN32},)
cp ${BIN}3b2${EXE} ${BIN}3b2-400${EXE}
else
copy $(@D)\3b2${EXE} $(@D)\3b2-400${EXE}
endif
ifneq (,$(call find_test,${ATT3B2D},3b2)) ifneq (,$(call find_test,${ATT3B2D},3b2))
$@ $(call find_test,${ATT3B2D},3b2) ${TEST_ARG} $@ $(call find_test,${ATT3B2D},3b2) ${TEST_ARG}
endif endif
3b2-600 : ${BIN}3b2-600${EXE} 3b2-700 : ${BIN}3b2-700${EXE}
${BIN}3b2-600${EXE} : ${ATT3B2M600} ${SIM} ${BUILD_ROMS} ${BIN}3b2-700${EXE} : ${ATT3B2M700} ${SIM}
${MKDIRBIN} ${MKDIRBIN}
${CC} ${ATT3B2M600} ${SCSI} ${SIM} ${ATT3B2M600_OPT} ${CC_OUTSPEC} ${LDFLAGS} ${CC} ${ATT3B2M700} ${SCSI} ${SIM} ${ATT3B2M700_OPT} ${CC_OUTSPEC} ${LDFLAGS}
ifneq (,$(call find_test,${ATT3B2D},3b2-600)) ifneq (,$(call find_test,${ATT3B2D},3b2-700))
$@ $(call find_test,${ATT3B2D},3b2-600) ${TEST_ARG} $@ $(call find_test,${ATT3B2D},3b2-700) ${TEST_ARG}
endif endif
i7090 : ${BIN}i7090${EXE} i7090 : ${BIN}i7090${EXE}

View file

@ -70,9 +70,6 @@ struct ROM_File_Descriptor {
{"PDP11/dazzledart/dazzle.lda", "PDP11/pdp11_dazzle_dart_rom.h", 6096, 0xFFF83848, "dazzle_lda"}, {"PDP11/dazzledart/dazzle.lda", "PDP11/pdp11_dazzle_dart_rom.h", 6096, 0xFFF83848, "dazzle_lda"},
{"PDP11/11logo/11logo.lda", "PDP11/pdp11_11logo_rom.h", 26009, 0xFFDD77F7, "logo_lda"}, {"PDP11/11logo/11logo.lda", "PDP11/pdp11_11logo_rom.h", 26009, 0xFFDD77F7, "logo_lda"},
{"swtp6800/swtp6800/swtbugv10.bin", "swtp6800/swtp6800/swtp_swtbugv10_bin.h", 1024, 0xFFFE4FBC, "swtp_swtbugv10_bin"}, {"swtp6800/swtp6800/swtbugv10.bin", "swtp6800/swtp6800/swtp_swtbugv10_bin.h", 1024, 0xFFFE4FBC, "swtp_swtbugv10_bin"},
{"3B2/rom_rev2.bin", "3B2/rom_rev2_bin.h", 32768, 0xFFD55762, "rom_rev2_bin"},
{"3B2/rom_rev2_demon.bin", "3B2/rom_rev2_demon_bin.h", 65536, 0xFFB345BB, "rom_rev2_demon_bin"},
{"3B2/rom_rev3.bin", "3B2/rom_rev3_bin.h", 131072, 0xFFDC0EB8, "rom_rev3_bin"},
}; };

View file

@ -490,6 +490,9 @@ scsi_set_req (bus); /* request to send data
void scsi_mode_sel6 (SCSI_BUS *bus, uint8 *data, uint32 len) void scsi_mode_sel6 (SCSI_BUS *bus, uint8 *data, uint32 len)
{ {
UNIT *uptr = bus->dev[bus->target];
SCSI_DEV *dev = (SCSI_DEV *)uptr->up7;
uint32 blk_size;
if (bus->phase == SCSI_CMD) { if (bus->phase == SCSI_CMD) {
scsi_debug_cmd (bus, "Mode Select(6) - CMD\n"); scsi_debug_cmd (bus, "Mode Select(6) - CMD\n");
memcpy (&bus->cmd[0], &data[0], 6); memcpy (&bus->cmd[0], &data[0], 6);
@ -499,9 +502,24 @@ if (bus->phase == SCSI_CMD) {
} }
else if (bus->phase == SCSI_DATO) { else if (bus->phase == SCSI_DATO) {
scsi_debug_cmd (bus, "Mode Select(6) - DATO\n"); scsi_debug_cmd (bus, "Mode Select(6) - DATO\n");
/* Not currently implemented so just return if (dev->devtype == SCSI_TAPE && uptr->flags & SCSI_QIC) {
good status for now */ blk_size = ((uint32)bus->buf[9]) << 16 |
scsi_status (bus, STS_OK, KEY_OK, ASC_OK); ((uint32)bus->buf[10]) << 8 |
(uint32)bus->buf[11];
/* QIC tape ONLY supports requesting a fixed block size of
* 0x200 bytes. Any other block size will cause an illegal
* request. */
if (blk_size == SCSI_QIC_BLKSZ) {
scsi_status(bus, STS_OK, KEY_OK, ASC_OK);
}
else {
scsi_status(bus, STS_CHK, KEY_ILLREQ|KEY_M_ILI, ASC_INVCDB);
}
}
else {
/* Not implemented for disk and non-QIC tape */
scsi_status(bus, STS_OK, KEY_OK, ASC_OK);
}
} }
} }
@ -784,8 +802,9 @@ void scsi_read6_tape (SCSI_BUS *bus, uint8 *data, uint32 len)
{ {
UNIT *uptr = bus->dev[bus->target]; UNIT *uptr = bus->dev[bus->target];
SCSI_DEV *dev = (SCSI_DEV *)uptr->up7; SCSI_DEV *dev = (SCSI_DEV *)uptr->up7;
t_seccnt sects, sectsread; t_seccnt sects, sectsread, new_buf_b;
t_stat r; t_stat r;
uint32 i;
if ((data[1] & 0x3) == 0x3) { /* SILI and FIXED? */ if ((data[1] & 0x3) == 0x3) { /* SILI and FIXED? */
scsi_status (bus, STS_CHK, KEY_ILLREQ, ASC_INVCDB); scsi_status (bus, STS_CHK, KEY_ILLREQ, ASC_INVCDB);
@ -793,6 +812,7 @@ if ((data[1] & 0x3) == 0x3) { /* SILI and FIXED? */
} }
sects = GETW (data, 3) | (data[2] << 16); sects = GETW (data, 3) | (data[2] << 16);
new_buf_b = 0;
sectsread = 0; sectsread = 0;
if (sects == 0) { /* no data to read */ if (sects == 0) { /* no data to read */
@ -803,35 +823,64 @@ if (sects == 0) { /* no data to read */
scsi_debug_cmd (bus, "Read(6) blks %d fixed %d\n", sects, (data[1] & 0x1)); scsi_debug_cmd (bus, "Read(6) blks %d fixed %d\n", sects, (data[1] & 0x1));
if (uptr->flags & UNIT_ATT) { if (uptr->flags & UNIT_ATT) {
if (data[1] & 0x1) { if (uptr->flags & SCSI_QIC) {
r = sim_tape_rdrecf (uptr, &bus->buf[0], &sectsread, (sects * dev->block_size)); if (data[1] & 0x1) {
scsi_debug_cmd (bus, "Read tape blk %d, read %d, r = %d\n", sects, sectsread, r); /* If this is a QIC tape drive and bit 0 is set, this is a
request to read multiple fixed-length blocks. */
scsi_debug_cmd(bus, "QIC in fixed block mode\n");
for (i = 0; i < sects; i++) {
r = sim_tape_rdrecf(uptr, &bus->buf[new_buf_b], &sectsread, SCSI_QIC_BLKSZ);
scsi_debug_cmd(bus, "Read tape blk %d, read %d, r = %d\n",
sects, sectsread, r);
if (r == MTSE_OK) {
new_buf_b += SCSI_QIC_BLKSZ;
} else {
scsi_tape_status(bus, r);
scsi_status(bus, bus->status, bus->sense_key, bus->sense_code);
return;
}
}
} else {
/* QIC drives respond with an illegal request when the
request does not specify fixed-block mode */
scsi_debug_cmd(bus, "QIC not in fixed block mode, invalid command\n");
scsi_status(bus, STS_CHK, KEY_ILLREQ|KEY_M_ILI, ASC_INVCDB);
return;
}
} }
else { else {
r = sim_tape_rdrecf (uptr, &bus->buf[0], &sectsread, sects); /* Otherwise, this is a normal streaming tape read */
scsi_debug_cmd (bus, "Read tape max %d, read %d, r = %d\n", sects, sectsread, r); if (data[1] & 0x1) {
if (r == MTSE_INVRL) { /* overlength condition */ r = sim_tape_rdrecf (uptr, &bus->buf[0], &sectsread, (sects * dev->block_size));
scsi_debug_cmd (bus, "Overlength\n"); scsi_debug_cmd (bus, "Read tape blk %d, read %d, r = %d\n", sects, sectsread, r);
if ((data[1] & 0x2) && (dev->block_size == 0)) { /* SILI set */ }
scsi_debug_cmd (bus, "SILI set\n"); else {
r = sim_tape_rdrecf (uptr, &bus->buf[0], &sectsread, sects);
scsi_debug_cmd (bus, "Read tape max %d, read %d, r = %d\n", sects, sectsread, r);
if (r == MTSE_INVRL) { /* overlength condition */
scsi_debug_cmd (bus, "Overlength\n");
if ((data[1] & 0x2) && (dev->block_size == 0)) { /* SILI set */
scsi_debug_cmd (bus, "SILI set\n");
}
else {
scsi_debug_cmd (bus, "SILI not set - check condition\n");
scsi_status (bus, STS_CHK, (KEY_OK | KEY_M_ILI), ASC_OK);
return;
}
} }
else { else if ((r == MTSE_OK) && (sectsread < sects)) { /* underlength condition */
scsi_debug_cmd (bus, "SILI not set - check condition\n"); scsi_debug_cmd (bus, "Underlength\n");
scsi_status (bus, STS_CHK, (KEY_OK | KEY_M_ILI), ASC_OK); if (data[1] & 0x2) { /* SILI set */
return; scsi_debug_cmd (bus, "SILI set\n");
} }
} else {
else if ((r == MTSE_OK) && (sectsread < sects)) { /* underlength condition */ scsi_debug_cmd (bus, "SILI not set - check condition\n");
scsi_debug_cmd (bus, "Underlength\n"); scsi_status_deferred (bus, STS_CHK, (KEY_OK | KEY_M_ILI), ASC_OK);
if (data[1] & 0x2) { /* SILI set */ bus->sense_info = (sects - sectsread);
scsi_debug_cmd (bus, "SILI set\n"); }
}
else {
scsi_debug_cmd (bus, "SILI not set - check condition\n");
scsi_status_deferred (bus, STS_CHK, (KEY_OK | KEY_M_ILI), ASC_OK);
bus->sense_info = (sects - sectsread);
} }
} }
new_buf_b = sectsread;
} }
if (r != MTSE_OK) { if (r != MTSE_OK) {
@ -845,7 +894,7 @@ else {
} }
if (sectsread > 0) { if (sectsread > 0) {
bus->buf_b = sectsread; bus->buf_b = new_buf_b;
scsi_set_phase (bus, SCSI_DATI); /* data in phase next */ scsi_set_phase (bus, SCSI_DATI); /* data in phase next */
} }
else { else {

View file

@ -68,10 +68,13 @@
#define SCSI_DBG_TAP 0x10000000 /* tape activity */ #define SCSI_DBG_TAP 0x10000000 /* tape activity */
#define SCSI_V_NOAUTO ((DKUF_V_UF > MTUF_V_UF) ? DKUF_V_UF : MTUF_V_UF)/* noautosize */ #define SCSI_V_NOAUTO ((DKUF_V_UF > MTUF_V_UF) ? DKUF_V_UF : MTUF_V_UF)/* noautosize */
#define SCSI_V_UF (SCSI_V_NOAUTO + 1) #define SCSI_V_QIC (SCSI_V_NOAUTO + 1)
#define SCSI_V_UF (SCSI_V_QIC + 1)
#define SCSI_QIC (1 << SCSI_V_QIC)
#define SCSI_WLK (UNIT_WLK|UNIT_RO) /* hwre write lock */ #define SCSI_WLK (UNIT_WLK|UNIT_RO) /* hwre write lock */
#define SCSI_NOAUTO DKUF_NOAUTOSIZE #define SCSI_NOAUTO DKUF_NOAUTOSIZE
#define SCSI_QIC_BLKSZ 0x200
struct scsi_dev_t { struct scsi_dev_t {
uint8 devtype; /* device type */ uint8 devtype; /* device type */