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:
parent
d862d024ea
commit
9b62da6567
67 changed files with 26013 additions and 40292 deletions
1433
3B2/3b2_cpu.c
1433
3B2/3b2_cpu.c
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
326
3B2/3b2_ctc.c
326
3B2/3b2_ctc.c
|
@ -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");
|
ctc_conf = FALSE;
|
||||||
|
|
||||||
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;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
cio[cid].id = 0;
|
if (!ctc_conf) {
|
||||||
cio[cid].ipl = 0;
|
r = cio_install(CTC_ID, "CTC", CTC_IPL,
|
||||||
cio[cid].ivec = 0;
|
&ctc_express, &ctc_full, &ctc_sysgen, NULL,
|
||||||
cio[cid].exp_handler = NULL;
|
&slot);
|
||||||
cio[cid].full_handler = NULL;
|
if (r != SCPE_OK) {
|
||||||
cio[cid].sysgen = NULL;
|
return r;
|
||||||
|
|
||||||
ctc_conf = FALSE;
|
|
||||||
} else if (!ctc_conf) {
|
|
||||||
sim_debug(TRACE_DBG, &ctc_dev,
|
|
||||||
"[ctc_reset] ATTACHING CARD\n");
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
141
3B2/3b2_dmac.c
141
3B2/3b2_dmac.c
|
@ -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;
|
||||||
|
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);
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
202
3B2/3b2_id.c
202
3B2/3b2_id.c
|
@ -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, §sread, 1) == SCPE_OK) {
|
if (sim_disk_rdsect(id_sel_unit, lba, id_buf, §sread, 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, §swritten, 1) == SCPE_OK) {
|
if (sim_disk_wrsect(id_sel_unit, lba, id_buf, §swritten, 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
25
3B2/3b2_if.c
25
3B2/3b2_if.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
526
3B2/3b2_io.c
526
3B2/3b2_io.c
|
@ -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);
|
||||||
|
|
||||||
/* TODO: I don't remember why we do this! */
|
|
||||||
if ((pa & 0xfff) == 3) {
|
|
||||||
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
}
|
return 0;
|
||||||
|
|
||||||
/* 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! */
|
|
||||||
if ((pa & 0xfff) == 3) {
|
|
||||||
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
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);
|
|
||||||
}
|
|
||||||
|
|
67
3B2/3b2_io.h
67
3B2/3b2_io.h
|
@ -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];
|
||||||
|
|
||||||
|
|
974
3B2/3b2_iu.c
974
3B2/3b2_iu.c
File diff suppressed because it is too large
Load diff
94
3B2/3b2_iu.h
94
3B2/3b2_iu.h
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
366
3B2/3b2_mau.h
366
3B2/3b2_mau.h
|
@ -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_ */
|
||||||
|
|
234
3B2/3b2_mem.c
234
3B2/3b2_mem.c
|
@ -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;
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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
|
||||||
|
|
392
3B2/3b2_ni.c
392
3B2/3b2_ni.c
|
@ -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, ¢ry, app_data);
|
cio_cexpress(slot, NIQESIZE, ¢ry, app_data);
|
||||||
} else {
|
} else {
|
||||||
cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cexpress(slot, NIQESIZE, ¢ry, app_data);
|
||||||
} else {
|
} else {
|
||||||
cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cexpress(slot, NIQESIZE, ¢ry, app_data);
|
||||||
} else {
|
} else {
|
||||||
cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, 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()
|
t_stat ni_reset(DEVICE *dptr)
|
||||||
{
|
{
|
||||||
uint8 cid;
|
t_stat r;
|
||||||
|
uint8 slot;
|
||||||
/* Clear the CIO table of NI cards */
|
char uname[16];
|
||||||
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;
|
|
||||||
|
|
||||||
|
if (dptr->flags & DEV_DIS) {
|
||||||
|
cio_remove_all(NI_ID);
|
||||||
|
ni_conf = FALSE;
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
t_stat ni_reset(DEVICE *dptr)
|
if (!ni_conf) {
|
||||||
{
|
r = cio_install(NI_ID, "NI", NI_IPL,
|
||||||
t_stat status;
|
&ni_express, &ni_full, &ni_sysgen, &ni_cio_reset,
|
||||||
char uname[16];
|
&slot);
|
||||||
|
if (r != SCPE_OK) {
|
||||||
sim_debug(DBG_TRACE, &ni_dev,
|
return r;
|
||||||
"[ni_reset] Resetting NI device\n");
|
}
|
||||||
|
/* Remember the card slot */
|
||||||
/* Initial setup that should only ever be done once. */
|
ni.slot = slot;
|
||||||
if (!(dptr->flags & DEV_DIS) && !ni.initialized) {
|
ni_conf = TRUE;
|
||||||
/* 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 */
|
/* 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_setmac(ni_dev.units, 0, "80:00:10:03:00:00/32", NULL);
|
||||||
|
|
||||||
ni.initialized = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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.cid);
|
"[ni_cio_svc] Handling a CIO service (Setting Interrupt) for board %d\n", ni.slot);
|
||||||
CIO_SET_INT(ni.cid);
|
CIO_SET_INT(ni.slot);
|
||||||
}
|
|
||||||
|
|
||||||
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, ¢ry, capp_data);
|
cio_cqueue(ni.slot, CIO_STAT, NIQESIZE, ¢ry, 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;
|
|
||||||
}
|
|
||||||
|
|
22
3B2/3b2_ni.h
22
3B2/3b2_ni.h
|
@ -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_ */
|
||||||
|
|
555
3B2/3b2_ports.c
555
3B2/3b2_ports.c
|
@ -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 LN(slot,port) (ports_slot_ln[(slot)] + (port))
|
||||||
|
#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)
|
#define LPORT(ln) ((ln) % PORTS_LINES)
|
||||||
|
|
||||||
int8 ports_base_cid; /* First cid in our contiguous block */
|
int8 ports_base_slot; /* First slot in our contiguous block */
|
||||||
uint8 ports_int_cid; /* Interrupting card ID */
|
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, ¢ry, app_data);
|
cio_cexpress(slot, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
||||||
cio_cexpress(cid, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cexpress(slot, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
||||||
cio_cexpress(cid, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cexpress(slot, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cexpress(slot, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cexpress(slot, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_CMD, PPQESIZE, ¢ry, 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,
|
|
||||||
"[ports_reset] Resetting PORTS device\n");
|
|
||||||
|
|
||||||
if ((dptr->flags & DEV_DIS)) {
|
|
||||||
for (cid = 0; cid < CIO_SLOTS; cid++) {
|
|
||||||
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;
|
|
||||||
} else if (!ports_conf) {
|
|
||||||
|
|
||||||
/* Clear out any old cards, we're starting fresh */
|
|
||||||
for (cid = 0; cid < CIO_SLOTS; cid++) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 || 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;
|
|
||||||
|
|
||||||
if (ports_ldsc == NULL) {
|
if (ports_ldsc == NULL) {
|
||||||
|
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 =
|
ports_desc.ldsc = ports_ldsc =
|
||||||
(TMLN *)calloc(ports_desc.lines, sizeof(*ports_ldsc));
|
(TMLN *)calloc(ports_desc.lines, sizeof(*ports_ldsc));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ports_state == NULL) {
|
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));
|
ports_state = (PORTS_LINE_STATE *)calloc(ports_desc.lines, sizeof(*ports_state));
|
||||||
}
|
|
||||||
|
|
||||||
memset(ports_state, 0, ports_desc.lines*sizeof(*ports_state));
|
memset(ports_state, 0, ports_desc.lines*sizeof(*ports_state));
|
||||||
|
}
|
||||||
|
|
||||||
tmxr_set_port_speed_control(&ports_desc);
|
tmxr_set_port_speed_control(&ports_desc);
|
||||||
|
|
||||||
for (i = 0; i < ports_desc.lines; i++) {
|
if ((dptr->flags & DEV_DIS)) {
|
||||||
sim_debug(TRACE_DBG, &ports_dev,
|
cio_remove_all(PORTS_ID);
|
||||||
"[ports_reset] Setting up line %d...\n", i);
|
ports_conf = FALSE;
|
||||||
tmxr_set_line_unit(&ports_desc, i, &ports_unit[0]);
|
return SCPE_OK;
|
||||||
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 (!ports_conf) {
|
||||||
|
/* Clear out any old cards, we're starting fresh */
|
||||||
|
cio_remove_all(PORTS_ID);
|
||||||
|
|
||||||
|
memset(ports_slot_ln, 0, sizeof(ports_slot_ln));
|
||||||
|
memset(ports_ln_slot, 0, sizeof(ports_ln_slot));
|
||||||
|
|
||||||
|
/* Insert necessary cards into the backplane */
|
||||||
|
j = 0;
|
||||||
|
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;
|
||||||
|
for (; j < (i * PORTS_LINES) + PORTS_LINES; j++) {
|
||||||
|
ports_ln_slot[j] = slot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sim_is_active(&ports_unit[0])) {
|
ports_conf = TRUE;
|
||||||
sim_debug(TRACE_DBG, &ports_dev,
|
}
|
||||||
"[ports_reset] starting receive polling...\n");
|
|
||||||
sim_activate(&ports_unit[0], ports_unit[0].wait);
|
/* If attached, start polling for connections */
|
||||||
|
if (ports_unit[0].flags & UNIT_ATT) {
|
||||||
|
sim_activate_after_abs(&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, ¢ry, capp_data);
|
cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, 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, ¢ry, app_data);
|
cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, 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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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_ */
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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_ */
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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. */
|
|
|
@ -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
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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_ */
|
|
1082
3B2/3b2_scsi.c
1082
3B2/3b2_scsi.c
File diff suppressed because it is too large
Load diff
190
3B2/3b2_scsi.h
190
3B2/3b2_scsi.h
|
@ -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
|
* ==============
|
||||||
|
*
|
||||||
|
* Disk Drives
|
||||||
|
* -----------
|
||||||
|
*
|
||||||
|
* There are two emulated SCSI disk drives available.
|
||||||
|
*
|
||||||
|
* 1. 155 MB CDC Wren III (CDC 94161-9)
|
||||||
|
* 2. 327 MB CDC Wren IV (CDC 94171-9)
|
||||||
|
*
|
||||||
|
* The CDC 94161-9 was also OEMed as the "AT&T KS23483,L3"
|
||||||
|
* The CDC 94171-9 was also OEMed as the "AT&T KS23483,L25"
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Tape Drive
|
||||||
|
* ----------
|
||||||
|
*
|
||||||
|
* Wangtek 5125EN (AT&T Part number KS23417,L2)
|
||||||
|
*
|
||||||
|
* DC600A cartridge tape at 120MB (QIC-120 format)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* 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_PQUAL 0x00
|
||||||
#define SD327_SCSI 1
|
#define SD327_SCSI 1
|
||||||
#define SD327_BLK 512
|
#define SD327_BLK 512
|
||||||
#define SD327_LBN 640396
|
#define SD327_LBN 640458
|
||||||
#define SD327_MANU "AT&T"
|
#define SD327_MANU "AT&T"
|
||||||
#define SD327_DESC "KS23483"
|
#define SD327_DESC "KS23483"
|
||||||
#define SD327_REV "0001" /* TODO: Find real rev */
|
#define SD327_REV "0000"
|
||||||
#define SD327_TPZ 9
|
|
||||||
#define SD327_ASEC 3
|
|
||||||
#define SD327_ATPZ 0
|
|
||||||
#define SD327_ATPU 0
|
|
||||||
#define SD327_SPT 46
|
|
||||||
#define SD327_CYL 1549
|
|
||||||
#define SD327_HEADS 9
|
|
||||||
#define SD327_PREC 1200
|
|
||||||
#define SD327_RWC 1200
|
|
||||||
#define SD327_STEP 15
|
|
||||||
#define SD327_LZ 1549
|
|
||||||
#define SD327_RPM 3600
|
|
||||||
|
|
||||||
/* Wangtek 120MB cartridge tape (AT&T KS-23465) */
|
/* AT&T 630 MB Hard Disk (56 sec/t, 16 hd, 1447 cyl) */
|
||||||
#define ST120_DTYPE 1
|
#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 */
|
|
||||||
uint32 hlen; /* Length for read or write */
|
|
||||||
t_bool initialized; /* Card has been initialized */
|
|
||||||
t_bool frq; /* Fast Request Queue enabled */
|
t_bool frq; /* Fast Request Queue enabled */
|
||||||
uint8 edt[HA_EDT_LEN]; /* Equipped Device Table */
|
uint8 edt[HA_EDT_LEN]; /* Equipped Device Table */
|
||||||
ha_req request; /* Current job request */
|
ha_ts ts[8]; /* Target state */
|
||||||
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_ */
|
||||||
|
|
650
3B2/3b2_stddev.c
650
3B2/3b2_stddev.c
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t = sim_rtcn_calb(CLK_TPS, TMR_CLK);
|
||||||
|
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->tsec = 0;
|
||||||
td->unit_sec = tm.tm_sec % 10;
|
td->flags |= FLAG_DATA_CHANGED;
|
||||||
td->ten_sec = tm.tm_sec / 10;
|
if (++td->sec > 59) {
|
||||||
td->unit_min = tm.tm_min % 10;
|
td->sec = 0;
|
||||||
td->ten_min = tm.tm_min / 10;
|
if (++td->min > 59) {
|
||||||
td->unit_hour = tm.tm_hour % 10;
|
td->min = 0;
|
||||||
td->ten_hour = tm.tm_hour / 10;
|
td->hour++;
|
||||||
/* tm struct stores as 0-11, tod struct as 1-12 */
|
|
||||||
td->unit_mon = (tm.tm_mon + 1) % 10;
|
/* 12-hour clock cycles from 1-12, 24-hour clock cycles from 00-23 */
|
||||||
td->ten_mon = (tm.tm_mon + 1) / 10;
|
if (TOD_12H(td)) {
|
||||||
td->unit_day = tm.tm_mday % 10;
|
if (td->hour == 12) {
|
||||||
td->ten_day = tm.tm_mday / 10;
|
td->clkset ^= CLKSET_PM;
|
||||||
td->year = 1 << ((tm.tm_year - 1) % 4);
|
}
|
||||||
|
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)) {
|
||||||
* Re-calculate the delta between real time and simulated time
|
/* Manage day-of-week */
|
||||||
*/
|
td->wday++;
|
||||||
void tod_update_delta()
|
if (td->wday > 7) {
|
||||||
{
|
td->wday = 1;
|
||||||
struct timespec now;
|
}
|
||||||
struct tm tm = {0};
|
td->day++;
|
||||||
time_t ssec;
|
switch(td->mon) {
|
||||||
TOD_DATA *td = (TOD_DATA *)tod_unit.filebuf;
|
case 2: /* FEB */
|
||||||
sim_rtcn_get_time(&now, TMR_CLK);
|
if (TOD_LYEAR(td)) {
|
||||||
|
if (td->day > 29) {
|
||||||
/* Let the host decide if it is DST or not */
|
td->day = 1;
|
||||||
tm.tm_isdst = -1;
|
}
|
||||||
|
} else {
|
||||||
/* Compute the simulated seconds value */
|
if (td->day > 28) {
|
||||||
tm.tm_sec = (td->ten_sec * 10) + td->unit_sec;
|
td->day = 1;
|
||||||
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;
|
break;
|
||||||
case 2: /* Leap Year - 2 */
|
case 4: /* APR */
|
||||||
tm.tm_year = 86;
|
case 6: /* JUN */
|
||||||
|
case 9: /* SEP */
|
||||||
|
case 11: /* NOV */
|
||||||
|
if (td->day > 30) {
|
||||||
|
td->day = 1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 4: /* Leap Year - 1 */
|
case 1: /* JAN */
|
||||||
tm.tm_year = 87;
|
case 3: /* MAR */
|
||||||
break;
|
case 5: /* MAY */
|
||||||
case 8: /* Leap Year */
|
case 7: /* JUL */
|
||||||
tm.tm_year = 88;
|
case 8: /* AUG */
|
||||||
break;
|
case 10: /* OCT */
|
||||||
default:
|
case 12: /* DEC */
|
||||||
|
if (td->day > 31) {
|
||||||
|
td->day = 1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (td->day == 1) {
|
||||||
ssec = mktime(&tm);
|
if (++td->mon > 12) {
|
||||||
td->delta = (int32)(now.tv_sec - ssec);
|
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);
|
||||||
|
|
||||||
|
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;
|
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
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 the timer isn't enabled, do nothing. */
|
||||||
|
if (!ctr->enabled) {
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctr->enabled) {
|
|
||||||
sim_debug(EXECUTE_MSG, &timer_dev,
|
sim_debug(EXECUTE_MSG, &timer_dev,
|
||||||
"[%08x] TIMER 0 COMPLETION.\n",
|
"[tmr_svc] Handling timeout for ctr number %d\n",
|
||||||
R[NUM_PC]);
|
ctr_num);
|
||||||
if (!(csr_data & CSRISTIM)) {
|
|
||||||
sim_debug(EXECUTE_MSG, &timer_dev,
|
switch (ctr_num) {
|
||||||
"[%08x] TIMER 0 NMI IRQ.\n",
|
case TMR_SANITY:
|
||||||
R[NUM_PC]);
|
#if defined (REV3)
|
||||||
ctr->val = 0xffff;
|
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);
|
||||||
|
ctr->val = 0xffff;
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
|
break;
|
||||||
return SCPE_OK;
|
case TMR_INT:
|
||||||
}
|
if (!CSR(CSRITIM)) {
|
||||||
|
|
||||||
/*
|
|
||||||
* 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);
|
CSRBIT(CSRCLK, TRUE);
|
||||||
CPU_SET_INT(INT_CLOCK);
|
CPU_SET_INT(INT_CLOCK);
|
||||||
}
|
if (ctr->enabled && ctr->gate) {
|
||||||
|
usec_delay = DELAY_US(ctr, TMR_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;
|
|
||||||
|
|
||||||
ctr = (struct timer_ctr *)uptr->tmr;
|
|
||||||
|
|
||||||
if (ctr->enabled && TIMER_RW(ctr) == CLK_LSB) {
|
|
||||||
sim_debug(EXECUTE_MSG, &timer_dev,
|
sim_debug(EXECUTE_MSG, &timer_dev,
|
||||||
"[%08x] TIMER 2 COMPLETION.\n",
|
"[tmr_svc] Re-triggering TMR_INT in %d usec\n", usec_delay);
|
||||||
R[NUM_PC]);
|
sim_activate_after(uptr, usec_delay);
|
||||||
if (!(csr_data & CSRITIMO)) {
|
}
|
||||||
sim_debug(EXECUTE_MSG, &timer_dev,
|
|
||||||
"[%08x] TIMER 2 IRQ.\n",
|
|
||||||
R[NUM_PC]);
|
|
||||||
ctr->val = 0xffff;
|
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;
|
||||||
|
}
|
|
@ -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_ */
|
||||||
|
|
|
@ -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
|
||||||
|
|
BIN
3B2/rom_rev2.bin
BIN
3B2/rom_rev2.bin
Binary file not shown.
2081
3B2/rom_rev2_bin.h
2081
3B2/rom_rev2_bin.h
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
BIN
3B2/rom_rev3.bin
BIN
3B2/rom_rev3.bin
Binary file not shown.
8225
3B2/rom_rev3_bin.h
8225
3B2/rom_rev3_bin.h
File diff suppressed because it is too large
Load diff
|
@ -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.
|
@ -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 & git commit id"
|
Description="Check for required build dependencies & git commit id"
|
||||||
CommandLine="Pre-Build-Event.cmd "$(TargetDir)$(TargetName).exe" LIBPCRE ROM"
|
CommandLine="Pre-Build-Event.cmd "$(TargetDir)$(TargetName).exe" 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 "$(TargetDir)$(TargetName).exe""
|
CommandLine="Post-Build-Event.cmd 3B2-400 "$(TargetDir)$(TargetName).exe""
|
||||||
/>
|
/>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
<Configuration
|
<Configuration
|
||||||
|
@ -107,7 +107,7 @@
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCPreBuildEventTool"
|
Name="VCPreBuildEventTool"
|
||||||
Description="Check for required build dependencies & git commit id"
|
Description="Check for required build dependencies & git commit id"
|
||||||
CommandLine="Pre-Build-Event.cmd "$(TargetDir)$(TargetName).exe" LIBPCRE ROM"
|
CommandLine="Pre-Build-Event.cmd "$(TargetDir)$(TargetName).exe" 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 "$(TargetDir)$(TargetName).exe""
|
CommandLine="Post-Build-Event.cmd 3B2-400 "$(TargetDir)$(TargetName).exe""
|
||||||
/>
|
/>
|
||||||
</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"
|
||||||
>
|
>
|
|
@ -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 & git commit id"
|
Description="Check for required build dependencies & git commit id"
|
||||||
CommandLine="Pre-Build-Event.cmd "$(TargetDir)$(TargetName).exe" LIBPCRE ROM"
|
CommandLine="Pre-Build-Event.cmd "$(TargetDir)$(TargetName).exe" 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 "$(TargetDir)$(TargetName).exe""
|
CommandLine="Post-Build-Event.cmd 3B2-700 "$(TargetDir)$(TargetName).exe""
|
||||||
/>
|
/>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
<Configuration
|
<Configuration
|
||||||
|
@ -107,7 +107,7 @@
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCPreBuildEventTool"
|
Name="VCPreBuildEventTool"
|
||||||
Description="Check for required build dependencies & git commit id"
|
Description="Check for required build dependencies & git commit id"
|
||||||
CommandLine="Pre-Build-Event.cmd "$(TargetDir)$(TargetName).exe" LIBPCRE ROM"
|
CommandLine="Pre-Build-Event.cmd "$(TargetDir)$(TargetName).exe" 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 "$(TargetDir)$(TargetName).exe""
|
CommandLine="Post-Build-Event.cmd 3B2-700 "$(TargetDir)$(TargetName).exe""
|
||||||
/>
|
/>
|
||||||
</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"
|
||||||
>
|
>
|
|
@ -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
|
||||||
|
|
35
makefile
35
makefile
|
@ -2067,8 +2067,8 @@ 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 \
|
||||||
|
@ -2076,15 +2076,15 @@ ATT3B2M400 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.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}
|
||||||
|
|
|
@ -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"},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
57
sim_scsi.c
57
sim_scsi.c
|
@ -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,10 +502,25 @@ 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 |
|
||||||
|
((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);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Command - Mode Select (10 byte command) */
|
/* Command - Mode Select (10 byte command) */
|
||||||
|
@ -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,6 +823,33 @@ 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 (uptr->flags & SCSI_QIC) {
|
||||||
|
if (data[1] & 0x1) {
|
||||||
|
/* 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], §sread, 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 {
|
||||||
|
/* Otherwise, this is a normal streaming tape read */
|
||||||
if (data[1] & 0x1) {
|
if (data[1] & 0x1) {
|
||||||
r = sim_tape_rdrecf (uptr, &bus->buf[0], §sread, (sects * dev->block_size));
|
r = sim_tape_rdrecf (uptr, &bus->buf[0], §sread, (sects * dev->block_size));
|
||||||
scsi_debug_cmd (bus, "Read tape blk %d, read %d, r = %d\n", sects, sectsread, r);
|
scsi_debug_cmd (bus, "Read tape blk %d, read %d, r = %d\n", sects, sectsread, r);
|
||||||
|
@ -833,6 +880,8 @@ if (uptr->flags & UNIT_ATT) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
new_buf_b = sectsread;
|
||||||
|
}
|
||||||
|
|
||||||
if (r != MTSE_OK) {
|
if (r != MTSE_OK) {
|
||||||
scsi_debug_cmd (bus, "Read error, r = %d\n", r);
|
scsi_debug_cmd (bus, "Read error, r = %d\n", r);
|
||||||
|
@ -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 {
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Add table
Reference in a new issue