3b2: CIO feature card framework
Adds a skeleton framework for CIO ("Common I/O") feature cards. The first feature card to be implemented will be the "PORTS" serial MUX. Part of this support involved reworking IRQ handling in the CPU. It now respects both IRQ Vector and IPL. This change also removes all 'assert(0)' calls from the simulator and replaces them with generic "Simulator Error" halts. These should only happen if there's a genuine logic error lurking somewhere.
This commit is contained in:
parent
05ba8c4d2c
commit
a7d0d4a6e7
8 changed files with 720 additions and 97 deletions
142
3B2/3b2_cpu.c
142
3B2/3b2_cpu.c
|
@ -28,8 +28,6 @@
|
||||||
from the author.
|
from the author.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "3b2_cpu.h"
|
#include "3b2_cpu.h"
|
||||||
#include "rom_400_bin.h"
|
#include "rom_400_bin.h"
|
||||||
|
|
||||||
|
@ -68,6 +66,8 @@ extern uint16 csr_data;
|
||||||
uint32 R[16];
|
uint32 R[16];
|
||||||
|
|
||||||
/* Other global CPU state */
|
/* Other global CPU state */
|
||||||
|
uint8 cpu_int_ipl = 0; /* Interrupt IPL level */
|
||||||
|
uint8 cpu_int_vec = 0; /* Interrupt vector */
|
||||||
t_bool cpu_nmi = FALSE; /* If set, there has been an NMI */
|
t_bool cpu_nmi = FALSE; /* If set, there has been an NMI */
|
||||||
|
|
||||||
int32 pc_incr = 0; /* Length (in bytes) of instruction
|
int32 pc_incr = 0; /* Length (in bytes) of instruction
|
||||||
|
@ -147,6 +147,8 @@ MTAB cpu_mod[] = {
|
||||||
&cpu_set_size, NULL, NULL, "Set Memory to 4M bytes" },
|
&cpu_set_size, NULL, NULL, "Set Memory to 4M bytes" },
|
||||||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
|
||||||
&cpu_set_hist, &cpu_show_hist, NULL, "Displays instruction history" },
|
&cpu_set_hist, &cpu_show_hist, NULL, "Displays instruction history" },
|
||||||
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL,
|
||||||
|
NULL, &cpu_show_virt, NULL, "Show translation for virtual address" },
|
||||||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "STACK", "STACK",
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "STACK", "STACK",
|
||||||
NULL, &cpu_show_stack, NULL, "Display the current stack with optional depth" },
|
NULL, &cpu_show_stack, NULL, "Display the current stack with optional depth" },
|
||||||
{ MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle },
|
{ MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle },
|
||||||
|
@ -566,6 +568,10 @@ t_stat cpu_boot(int32 unit_num, DEVICE *dptr)
|
||||||
* in the PCB, if bit I in PSW is set.
|
* in the PCB, if bit I in PSW is set.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
sim_debug(EXECUTE_MSG, &cpu_dev,
|
||||||
|
"CPU Boot/Reset Initiated. PC=%08x SP=%08x\n",
|
||||||
|
R[NUM_PC], R[NUM_SP]);
|
||||||
|
|
||||||
mmu_disable();
|
mmu_disable();
|
||||||
|
|
||||||
R[NUM_PCBP] = pread_w(0x80);
|
R[NUM_PCBP] = pread_w(0x80);
|
||||||
|
@ -582,10 +588,6 @@ t_stat cpu_boot(int32 unit_num, DEVICE *dptr)
|
||||||
R[NUM_PSW] &= ~PSW_ISC_MASK;
|
R[NUM_PSW] &= ~PSW_ISC_MASK;
|
||||||
R[NUM_PSW] |= 3 << PSW_ISC ;
|
R[NUM_PSW] |= 3 << PSW_ISC ;
|
||||||
|
|
||||||
sim_debug(EXECUTE_MSG, &cpu_dev,
|
|
||||||
">>> CPU BOOT/RESET COMPLETE. PC=%08x SP=%08x\n",
|
|
||||||
R[NUM_PC], R[NUM_SP]);
|
|
||||||
|
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -973,6 +975,29 @@ void fprint_sym_hist(FILE *st, instr *ip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t_stat cpu_show_virt(FILE *of, UNIT *uptr, int32 val, CONST void *desc)
|
||||||
|
{
|
||||||
|
uint32 va, pa;
|
||||||
|
t_stat r;
|
||||||
|
|
||||||
|
const char *cptr = (const char *)desc;
|
||||||
|
if (cptr) {
|
||||||
|
va = (uint32) get_uint(cptr, 16, 0xffffffff, &r);
|
||||||
|
if (r == SCPE_OK) {
|
||||||
|
r = mmu_decode_va(va, 0, FALSE, &pa);
|
||||||
|
if (r == SCPE_OK) {
|
||||||
|
fprintf(of, "Virtual %08x = Physical %08x\n", va, pa);
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(of, "Translation not possible.\n");
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
t_stat cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
t_stat cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
||||||
{
|
{
|
||||||
uint32 i;
|
uint32 i;
|
||||||
|
@ -1528,10 +1553,9 @@ static SIM_INLINE void cpu_context_switch_1(uint32 new_pcbp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t_bool cpu_on_interrupt(uint8 ipl)
|
void cpu_on_interrupt(uint16 vec)
|
||||||
{
|
{
|
||||||
uint32 new_pcbp;
|
uint32 new_pcbp;
|
||||||
uint16 id = ipl; /* TODO: Does this need to be uint16? */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "If a nonmaskable interrupt request is received, an auto-vector
|
* "If a nonmaskable interrupt request is received, an auto-vector
|
||||||
|
@ -1540,7 +1564,7 @@ t_bool cpu_on_interrupt(uint8 ipl)
|
||||||
* Interrupt-ID is fetched. The value 0 is used as the ID."
|
* Interrupt-ID is fetched. The value 0 is used as the ID."
|
||||||
*/
|
*/
|
||||||
if (cpu_nmi) {
|
if (cpu_nmi) {
|
||||||
id = 0;
|
vec = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu_km = TRUE;
|
cpu_km = TRUE;
|
||||||
|
@ -1548,10 +1572,11 @@ t_bool cpu_on_interrupt(uint8 ipl)
|
||||||
if (R[NUM_PSW] & PSW_QIE_MASK) {
|
if (R[NUM_PSW] & PSW_QIE_MASK) {
|
||||||
/* TODO: Maybe implement quick interrupts at some point, but
|
/* TODO: Maybe implement quick interrupts at some point, but
|
||||||
the 3B2 ROM and SVR3 don't appear to use them. */
|
the 3B2 ROM and SVR3 don't appear to use them. */
|
||||||
assert(0);
|
stop_reason = STOP_ERR;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_pcbp = read_w(0x8c + (4 * id), ACC_AF);
|
new_pcbp = read_w(0x8c + (4 * vec), ACC_AF);
|
||||||
|
|
||||||
/* Save the old PCBP */
|
/* Save the old PCBP */
|
||||||
irq_push_word(R[NUM_PCBP]);
|
irq_push_word(R[NUM_PCBP]);
|
||||||
|
@ -1572,8 +1597,6 @@ t_bool cpu_on_interrupt(uint8 ipl)
|
||||||
cpu_context_switch_3(new_pcbp);
|
cpu_context_switch_3(new_pcbp);
|
||||||
|
|
||||||
cpu_km = FALSE;
|
cpu_km = FALSE;
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t_stat sim_instr(void)
|
t_stat sim_instr(void)
|
||||||
|
@ -1590,6 +1613,9 @@ t_stat sim_instr(void)
|
||||||
uint32 width, offset;
|
uint32 width, offset;
|
||||||
uint32 mask;
|
uint32 mask;
|
||||||
|
|
||||||
|
/* Generic index */
|
||||||
|
uint32 i;
|
||||||
|
|
||||||
operand *src1, *src2, *src3, *dst;
|
operand *src1, *src2, *src3, *dst;
|
||||||
|
|
||||||
stop_reason = 0;
|
stop_reason = 0;
|
||||||
|
@ -1698,9 +1724,20 @@ t_stat sim_instr(void)
|
||||||
increment_modep_b();
|
increment_modep_b();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process pending IRQ, if applicable */
|
/* Set the correct IRQ state */
|
||||||
if (PSW_CUR_IPL < cpu_ipl()) {
|
cpu_calc_ints();
|
||||||
cpu_on_interrupt(cpu_ipl());
|
|
||||||
|
if (PSW_CUR_IPL < cpu_int_ipl) {
|
||||||
|
cpu_on_interrupt(cpu_int_vec);
|
||||||
|
for (i = 0; i < CIO_SLOTS; i++) {
|
||||||
|
if (cio[i].intr &&
|
||||||
|
cio[i].ipl == cpu_int_ipl &&
|
||||||
|
cio[i].ivec == cpu_int_vec) {
|
||||||
|
cio[i].intr = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cpu_int_ipl = 0;
|
||||||
|
cpu_int_vec = 0;
|
||||||
cpu_nmi = FALSE;
|
cpu_nmi = FALSE;
|
||||||
cpu_in_wait = FALSE;
|
cpu_in_wait = FALSE;
|
||||||
}
|
}
|
||||||
|
@ -3171,7 +3208,9 @@ static uint32 cpu_read_op(operand * op)
|
||||||
data = sign_extend_b(R[op->reg] & BYTE_MASK);
|
data = sign_extend_b(R[op->reg] & BYTE_MASK);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(0);
|
stop_reason = STOP_ERR;
|
||||||
|
data = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
op->data = data;
|
op->data = data;
|
||||||
|
@ -3233,7 +3272,7 @@ static uint32 cpu_read_op(operand * op)
|
||||||
op->data = data;
|
op->data = data;
|
||||||
return data;
|
return data;
|
||||||
default:
|
default:
|
||||||
assert(0);
|
stop_reason = STOP_ERR;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3288,53 +3327,42 @@ static void cpu_write_op(operand * op, t_uint64 val)
|
||||||
write_b(eff, val & BYTE_MASK);
|
write_b(eff, val & BYTE_MASK);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(0);
|
stop_reason = STOP_ERR;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This returns the current state of the IPL (Interrupt
|
* Calculate the current state of interrupts.
|
||||||
* Priority Level) bus. This is affected by:
|
* TODO: This could use a refactor. It's getting code-smelly.
|
||||||
*
|
|
||||||
* - Latched values in the CSR for:
|
|
||||||
* o CSRCLK 15
|
|
||||||
* o CSRDMA 13
|
|
||||||
* o CSRUART 13
|
|
||||||
* o CSRDISK 11
|
|
||||||
* o CSRPIR9 9
|
|
||||||
* o CSRPIR8 8
|
|
||||||
* - IRQ currently enabled for:
|
|
||||||
* o HD Ctlr. 11
|
|
||||||
*/
|
*/
|
||||||
static SIM_INLINE uint8 cpu_ipl()
|
static void cpu_calc_ints()
|
||||||
{
|
{
|
||||||
/* CSRPIR9 is cleared by writing to c_pir8 */
|
uint32 i;
|
||||||
|
|
||||||
|
/* First scan for a CIO interrupt */
|
||||||
|
for (i = 0; i < CIO_SLOTS; i++) {
|
||||||
|
if (cio[i].intr) {
|
||||||
|
cpu_int_ipl = cio[i].ipl;
|
||||||
|
cpu_int_vec = cio[i].ivec;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If none was found, look for system board interrupts */
|
||||||
if (csr_data & CSRPIR8) {
|
if (csr_data & CSRPIR8) {
|
||||||
return 8;
|
cpu_int_ipl = cpu_int_vec = CPU_PIR8_IPL;
|
||||||
|
} else if (csr_data & CSRPIR9) {
|
||||||
|
cpu_int_ipl = cpu_int_vec = CPU_PIR9_IPL;
|
||||||
|
} else if (id_int() || (csr_data & CSRDISK)) {
|
||||||
|
cpu_int_ipl = cpu_int_vec = CPU_ID_IF_IPL;
|
||||||
|
} else if ((csr_data & CSRUART) || (csr_data & CSRDMA)) {
|
||||||
|
cpu_int_ipl = cpu_int_vec = CPU_IU_DMA_IPL;
|
||||||
|
} else if (csr_data & CSRCLK) {
|
||||||
|
cpu_int_ipl = cpu_int_vec = CPU_TMR_IPL;
|
||||||
|
} else {
|
||||||
|
cpu_int_ipl = cpu_int_vec = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CSRPIR9 is cleared by writing to c_pir9 */
|
|
||||||
if (csr_data & CSRPIR9) {
|
|
||||||
return 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CSRDISK is cleared when the floppy "if_irq" goes low */
|
|
||||||
if (id_int() || (csr_data & CSRDISK)) {
|
|
||||||
return 11;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CSRDMA is cleared by write/read to 0x49011 */
|
|
||||||
/* CSRUART is cleared when the uart "iu_irq" goes low */
|
|
||||||
if ((csr_data & CSRUART) || (csr_data & CSRDMA)) {
|
|
||||||
return 13;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CSRCLK is cleared by $clrclkint */
|
|
||||||
if (csr_data & CSRCLK) {
|
|
||||||
return 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -395,6 +395,7 @@ t_stat cpu_reset(DEVICE *dptr);
|
||||||
t_stat cpu_set_size(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
t_stat cpu_set_size(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||||||
t_stat cpu_set_hist(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
t_stat cpu_set_hist(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||||||
t_stat cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
t_stat cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||||
|
t_stat cpu_show_virt(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||||
t_stat cpu_show_stack(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
t_stat cpu_show_stack(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||||||
t_stat cpu_set_halt(UNIT *uptr, int32 val, char *cptr, void *desc);
|
t_stat cpu_set_halt(UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||||
t_stat cpu_clear_halt(UNIT *uptr, int32 val, char *cptr, void *desc);
|
t_stat cpu_clear_halt(UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||||
|
@ -410,14 +411,14 @@ t_stat fprint_sym_m(FILE *of, t_addr addr, t_value *val);
|
||||||
instr *cpu_next_instruction(void);
|
instr *cpu_next_instruction(void);
|
||||||
|
|
||||||
uint8 decode_instruction(instr *instr);
|
uint8 decode_instruction(instr *instr);
|
||||||
t_bool cpu_on_interrupt(uint8 ipl);
|
void cpu_on_interrupt(uint16 vec);
|
||||||
|
|
||||||
static uint32 cpu_effective_address(operand * op);
|
static uint32 cpu_effective_address(operand * op);
|
||||||
static uint32 cpu_read_op(operand * op);
|
static uint32 cpu_read_op(operand * op);
|
||||||
static void cpu_write_op(operand * op, t_uint64 val);
|
static void cpu_write_op(operand * op, t_uint64 val);
|
||||||
static void cpu_set_nz_flags(t_uint64 data, operand * op);
|
static void cpu_set_nz_flags(t_uint64 data, operand * op);
|
||||||
|
static void cpu_calc_ints();
|
||||||
|
|
||||||
static SIM_INLINE uint8 cpu_ipl();
|
|
||||||
static SIM_INLINE void cpu_on_normal_exception(uint8 isc);
|
static SIM_INLINE void cpu_on_normal_exception(uint8 isc);
|
||||||
static SIM_INLINE void cpu_on_stack_exception(uint8 isc);
|
static SIM_INLINE void cpu_on_stack_exception(uint8 isc);
|
||||||
static SIM_INLINE void cpu_on_process_exception(uint8 isc);
|
static SIM_INLINE void cpu_on_process_exception(uint8 isc);
|
||||||
|
|
|
@ -50,6 +50,8 @@ noret __libc_longjmp (jmp_buf buf, int val);
|
||||||
#define longjmp __libc_longjmp
|
#define longjmp __libc_longjmp
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* -t flag: Translate a virtual address */
|
||||||
|
#define EX_T_FLAG 1 << 19
|
||||||
/* -v flag for examine routine */
|
/* -v flag for examine routine */
|
||||||
#define EX_V_FLAG 1 << 21
|
#define EX_V_FLAG 1 << 21
|
||||||
|
|
||||||
|
@ -95,6 +97,13 @@ noret __libc_longjmp (jmp_buf buf, int val);
|
||||||
#define NUM_ISP 14
|
#define NUM_ISP 14
|
||||||
#define NUM_PC 15
|
#define NUM_PC 15
|
||||||
|
|
||||||
|
/* System board interrupt priority levels */
|
||||||
|
#define CPU_PIR8_IPL 8
|
||||||
|
#define CPU_PIR9_IPL 9
|
||||||
|
#define CPU_ID_IF_IPL 11
|
||||||
|
#define CPU_IU_DMA_IPL 13
|
||||||
|
#define CPU_TMR_IPL 15
|
||||||
|
|
||||||
#define CPU_CM (cpu_km ? L_KERNEL : ((R[NUM_PSW] >> PSW_CM) & 3))
|
#define CPU_CM (cpu_km ? L_KERNEL : ((R[NUM_PSW] >> PSW_CM) & 3))
|
||||||
|
|
||||||
/* Simulator stop codes */
|
/* Simulator stop codes */
|
||||||
|
@ -106,6 +115,7 @@ noret __libc_longjmp (jmp_buf buf, int val);
|
||||||
#define STOP_ESTK 6 /* Exception stack too deep */
|
#define STOP_ESTK 6 /* Exception stack too deep */
|
||||||
#define STOP_MMU 7 /* Unimplemented MMU Feature */
|
#define STOP_MMU 7 /* Unimplemented MMU Feature */
|
||||||
#define STOP_POWER 8 /* System power-off */
|
#define STOP_POWER 8 /* System power-off */
|
||||||
|
#define STOP_ERR 9 /* Other error */
|
||||||
|
|
||||||
/* Exceptional conditions handled within the instruction loop */
|
/* Exceptional conditions handled within the instruction loop */
|
||||||
#define ABORT_EXC 1 /* CPU exception */
|
#define ABORT_EXC 1 /* CPU exception */
|
||||||
|
@ -292,6 +302,18 @@ noret __libc_longjmp (jmp_buf buf, int val);
|
||||||
#define CLK_MD4 0x08
|
#define CLK_MD4 0x08
|
||||||
#define CLK_MD5 0x0a
|
#define CLK_MD5 0x0a
|
||||||
|
|
||||||
|
/* IO area */
|
||||||
|
|
||||||
|
#define MEMSIZE_REG 0x4C003
|
||||||
|
#define CIO_BOTTOM 0x200000
|
||||||
|
#define CIO_TOP 0x2000000
|
||||||
|
|
||||||
|
#define CIO_CSBIT 0x80
|
||||||
|
#define CIO_SEQBIT 0x40
|
||||||
|
|
||||||
|
#define CIO_INT_DELAY 8000
|
||||||
|
|
||||||
|
|
||||||
/* Timer definitions */
|
/* Timer definitions */
|
||||||
|
|
||||||
#define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */
|
#define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */
|
||||||
|
@ -325,9 +347,6 @@ extern uint8 fault;
|
||||||
extern DEBTAB sys_deb_tab[];
|
extern DEBTAB sys_deb_tab[];
|
||||||
extern t_bool cpu_km;
|
extern t_bool cpu_km;
|
||||||
|
|
||||||
/* Generic callback function */
|
|
||||||
typedef void (*callback)(void);
|
|
||||||
|
|
||||||
/* global symbols from the DMAC */
|
/* global symbols from the DMAC */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8 page;
|
uint8 page;
|
||||||
|
@ -378,6 +397,7 @@ extern void increment_modep_a();
|
||||||
extern void increment_modep_b();
|
extern void increment_modep_b();
|
||||||
|
|
||||||
/* global symbols from the MMU */
|
/* global symbols from the MMU */
|
||||||
|
extern t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa);
|
||||||
extern void mmu_enable();
|
extern void mmu_enable();
|
||||||
extern void mmu_disable();
|
extern void mmu_disable();
|
||||||
extern uint8 read_b(uint32 va, uint8 acc);
|
extern uint8 read_b(uint32 va, uint8 acc);
|
||||||
|
@ -388,12 +408,20 @@ extern void write_h(uint32 va, uint16 val);
|
||||||
extern void write_w(uint32 va, uint32 val);
|
extern void write_w(uint32 va, uint32 val);
|
||||||
|
|
||||||
/* Globally scoped CPU functions */
|
/* Globally scoped CPU functions */
|
||||||
void cpu_abort(uint8 et, uint8 isc);
|
extern void cpu_abort(uint8 et, uint8 isc);
|
||||||
void cpu_set_irq(uint8 ipl, uint8 id, uint16 csr_flags);
|
extern void cpu_set_irq(uint8 ipl, uint8 id, uint16 csr_flags);
|
||||||
void cpu_clear_irq(uint8 ipl, uint16 csr_flags);
|
extern void cpu_clear_irq(uint8 ipl, uint16 csr_flags);
|
||||||
|
|
||||||
/* Globally scoped IO functions */
|
/* global symbols from the IO system */
|
||||||
uint32 io_read(uint32 pa, size_t size);
|
extern uint32 io_read(uint32 pa, size_t size);
|
||||||
void io_write(uint32 pa, uint32 val, size_t size);
|
extern void io_write(uint32 pa, uint32 val, size_t size);
|
||||||
|
extern void cio_xfer();
|
||||||
|
extern uint8 cio_int;
|
||||||
|
extern uint16 cio_ipl;
|
||||||
|
|
||||||
|
/* Future Use: Global symbols from the PORTS card */
|
||||||
|
/* extern void ports_express(uint8 cid); */
|
||||||
|
/* extern void ports_full(uint8 cid); */
|
||||||
|
/* extern void ports_xfer(uint8 cid); */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
* HD135 11 1224 15 18 512 Maxtor XT1190
|
* HD135 11 1224 15 18 512 Maxtor XT1190
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include "3b2_id.h"
|
#include "3b2_id.h"
|
||||||
|
|
||||||
/* Wait times, in CPU steps, for various actions */
|
/* Wait times, in CPU steps, for various actions */
|
||||||
|
@ -525,7 +524,9 @@ uint32 id_read(uint32 pa, size_t size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(0); // cmd not Read Data or Read ID
|
/* cmd not Read Data or Read ID */
|
||||||
|
stop_reason = STOP_ERR;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|
17
3B2/3b2_if.c
17
3B2/3b2_if.c
|
@ -29,7 +29,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "3b2_if.h"
|
#include "3b2_if.h"
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: Macros used for debugging timers. Remove when debugging is complete.
|
* TODO: Macros used for debugging timers. Remove when debugging is complete.
|
||||||
|
@ -397,7 +396,12 @@ void if_handle_command()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case IF_READ_SEC_M:
|
case IF_READ_SEC_M:
|
||||||
assert(0);
|
/* Not yet implemented. Halt the emulator. */
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev,
|
||||||
|
"\tCOMMAND\t%02x\tRead Sector (Multi) - NOT IMPLEMENTED\n",
|
||||||
|
if_state.cmd);
|
||||||
|
stop_reason = STOP_ERR;
|
||||||
|
break;
|
||||||
case IF_WRITE_SEC:
|
case IF_WRITE_SEC:
|
||||||
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tWrite Sector %d/%d/%d\n",
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tWrite Sector %d/%d/%d\n",
|
||||||
if_state.cmd, if_state.track, if_state.side, if_state.sector);
|
if_state.cmd, if_state.track, if_state.side, if_state.sector);
|
||||||
|
@ -411,7 +415,11 @@ void if_handle_command()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case IF_WRITE_SEC_M:
|
case IF_WRITE_SEC_M:
|
||||||
assert(0);
|
/* Not yet implemented. Halt the emulator. */
|
||||||
|
sim_debug(EXECUTE_MSG, &if_dev,
|
||||||
|
"\tCOMMAND\t%02x\tWrite Sector (Multi) - NOT IMPLEMENTED\n",
|
||||||
|
if_state.cmd);
|
||||||
|
stop_reason = STOP_ERR;
|
||||||
break;
|
break;
|
||||||
case IF_READ_ADDR:
|
case IF_READ_ADDR:
|
||||||
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Address\n", if_state.cmd);
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Address\n", if_state.cmd);
|
||||||
|
@ -421,7 +429,8 @@ void if_handle_command()
|
||||||
break;
|
break;
|
||||||
case IF_READ_TRACK:
|
case IF_READ_TRACK:
|
||||||
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Track\n", if_state.cmd);
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Track\n", if_state.cmd);
|
||||||
assert(0); /* NOT YET IMPLEMENTED */
|
/* Not yet implemented. Halt the emulator. */
|
||||||
|
stop_reason = STOP_ERR;
|
||||||
break;
|
break;
|
||||||
case IF_WRITE_TRACK:
|
case IF_WRITE_TRACK:
|
||||||
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tWrite Track\n", if_state.cmd);
|
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tWrite Track\n", if_state.cmd);
|
||||||
|
|
425
3B2/3b2_io.c
425
3B2/3b2_io.c
|
@ -1,4 +1,4 @@
|
||||||
/* 3b2_cpu.h: AT&T 3B2 Model 400 IO dispatch implemenation
|
/* 3b2_cpu.h: AT&T 3B2 Model 400 IO and CIO feature cards
|
||||||
|
|
||||||
Copyright (c) 2017, Seth J. Morabito
|
Copyright (c) 2017, Seth J. Morabito
|
||||||
|
|
||||||
|
@ -30,6 +30,8 @@
|
||||||
|
|
||||||
#include "3b2_io.h"
|
#include "3b2_io.h"
|
||||||
|
|
||||||
|
CIO_STATE cio[CIO_SLOTS] = { 0 };
|
||||||
|
|
||||||
struct iolink iotable[] = {
|
struct 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 },
|
||||||
|
@ -47,13 +49,197 @@ struct iolink iotable[] = {
|
||||||
{ 0, 0, NULL, NULL}
|
{ 0, 0, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void cio_sysgen(uint8 cid)
|
||||||
|
{
|
||||||
|
uint32 sysgen_p;
|
||||||
|
uint32 cq_exp;
|
||||||
|
cio_entry cqe;
|
||||||
|
|
||||||
|
sysgen_p = pread_w(SYSGEN_PTR);
|
||||||
|
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[%08x] [SYSGEN] Starting sysgen for card %d. sysgen_p=%08x\n",
|
||||||
|
R[NUM_PC], cid, sysgen_p);
|
||||||
|
|
||||||
|
/* seqbit is always reset to 0 on completion */
|
||||||
|
cio[cid].seqbit = 0;
|
||||||
|
|
||||||
|
cio[cid].rqp = pread_w(sysgen_p);
|
||||||
|
cio[cid].cqp = pread_w(sysgen_p + 4);
|
||||||
|
cio[cid].rqs = pread_b(sysgen_p + 8);
|
||||||
|
cio[cid].cqs = pread_b(sysgen_p + 9);
|
||||||
|
cio[cid].ivec = pread_b(sysgen_p + 10);
|
||||||
|
cio[cid].no_rque = pread_b(sysgen_p + 11);
|
||||||
|
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[SYSGEN] sysgen rqp = %08x\n",
|
||||||
|
cio[cid].rqp);
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[SYSGEN] sysgen cqp = %08x\n",
|
||||||
|
cio[cid].cqp);
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[SYSGEN] sysgen rqs = %02x\n",
|
||||||
|
cio[cid].rqs);
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[SYSGEN] sysgen cqs = %02x\n",
|
||||||
|
cio[cid].cqs);
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[SYSGEN] sysgen ivec = %02x\n",
|
||||||
|
cio[cid].ivec);
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[SYSGEN] sysgen no_rque = %02x\n",
|
||||||
|
cio[cid].no_rque);
|
||||||
|
|
||||||
|
cq_exp = cio[cid].cqp;
|
||||||
|
|
||||||
|
cqe.byte_count = 0;
|
||||||
|
cqe.subdevice = 0;
|
||||||
|
cqe.opcode = 3;
|
||||||
|
cqe.address = 0;
|
||||||
|
cqe.app_data = 0;
|
||||||
|
|
||||||
|
cio_cexpress(cid, &cqe);
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[SYSGEN] Sysgen complete. Completion Queue written.\n");
|
||||||
|
|
||||||
|
/* If the card has a custom sysgen handler, run it */
|
||||||
|
if (cio[cid].sysgen != NULL) {
|
||||||
|
cio[cid].sysgen(cid);
|
||||||
|
} else {
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[%08x] [cio_sysgen] Not running custom sysgen.\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cio_cexpress(uint8 cid, cio_entry *cqe)
|
||||||
|
{
|
||||||
|
uint32 cqp;
|
||||||
|
|
||||||
|
cqp = cio[cid].cqp;
|
||||||
|
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[%08x] [cio_cexpress] cqp = %08x seqbit = %d\n",
|
||||||
|
R[NUM_PC], cqp, cio[cid].seqbit);
|
||||||
|
|
||||||
|
cio[cid].seqbit ^= 1;
|
||||||
|
|
||||||
|
if (cio[cid].seqbit) {
|
||||||
|
cqe->subdevice |= CIO_SEQBIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
pwrite_h(cqp, cqe->byte_count);
|
||||||
|
pwrite_b(cqp + 2, cqe->subdevice);
|
||||||
|
pwrite_b(cqp + 3, cqe->opcode);
|
||||||
|
pwrite_w(cqp + 4, cqe->address);
|
||||||
|
pwrite_w(cqp + 8, cqe->app_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write an entry into the Completion Queue */
|
||||||
|
void cio_cqueue(uint8 cid, cio_entry *cqe)
|
||||||
|
{
|
||||||
|
uint32 cqp, top;
|
||||||
|
uint16 lp;
|
||||||
|
|
||||||
|
/* Get the physical address of the completion queue
|
||||||
|
* in main memory */
|
||||||
|
cqp = cio[cid].cqp;
|
||||||
|
|
||||||
|
/* Get the physical address of the first entry in
|
||||||
|
* the completion queue */
|
||||||
|
top = cqp + QUE_OFFSET;
|
||||||
|
|
||||||
|
/* Get the load pointer. This is a 16-bit absolute offset
|
||||||
|
* from the top of the queue to the start of the entry. */
|
||||||
|
lp = pread_h(cqp + LOAD_OFFSET);
|
||||||
|
|
||||||
|
/* Load the entry at the supplied address */
|
||||||
|
pwrite_h(top + lp, cqe->byte_count);
|
||||||
|
pwrite_b(top + lp + 2, cqe->subdevice);
|
||||||
|
pwrite_b(top + lp + 3, cqe->opcode);
|
||||||
|
pwrite_w(top + lp + 4, cqe->address);
|
||||||
|
pwrite_w(top + lp + 8, cqe->app_data);
|
||||||
|
|
||||||
|
/* Increment the load pointer to the next queue location.
|
||||||
|
* If we go past the end of the queue, wrap around to the
|
||||||
|
* start of the queue */
|
||||||
|
if (cio[cid].cqs > 0) {
|
||||||
|
lp = (lp + QUE_E_SIZE) % (QUE_E_SIZE * cio[cid].cqs);
|
||||||
|
|
||||||
|
/* Store it back to the correct location */
|
||||||
|
pwrite_h(cqp + LOAD_OFFSET, lp);
|
||||||
|
} else {
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[%08x] [cio_cqueue] ERROR! Completion Queue Size is 0!",
|
||||||
|
R[NUM_PC]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve an entry from the Request Queue */
|
||||||
|
void cio_rqueue(uint8 cid, cio_entry *cqe)
|
||||||
|
{
|
||||||
|
uint32 rqp, top, i;
|
||||||
|
uint16 ulp;
|
||||||
|
|
||||||
|
/* Get the physical address of the request queue in main memory */
|
||||||
|
rqp = cio[cid].rqp + 12; /* Skip past the Express Queue Entry */
|
||||||
|
|
||||||
|
/* Scan each queue until we find one with a command in it. */
|
||||||
|
for (i = 0; i < cio[cid].no_rque; i++) {
|
||||||
|
/* Get the physical address of the first entry in the request
|
||||||
|
* queue */
|
||||||
|
top = rqp + 4;
|
||||||
|
|
||||||
|
/* Check to see what we've got in the queue. */
|
||||||
|
ulp = pread_h(rqp + 2);
|
||||||
|
|
||||||
|
cqe->opcode = pread_b(top + ulp + 3);
|
||||||
|
|
||||||
|
if (cqe->opcode > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rqp += 4 + (12 * cio[cid].rqs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= cio[cid].no_rque) {
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[%08x] [cio_rque] FAILURE! NO MORE QUEUES TO EXAMINE.\n",
|
||||||
|
R[NUM_PC]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve the entry at the supplied address */
|
||||||
|
cqe->byte_count = pread_h(top + ulp);
|
||||||
|
cqe->subdevice = pread_b(top + ulp + 2);
|
||||||
|
cqe->address = pread_w(top + ulp + 4);
|
||||||
|
cqe->app_data = pread_w(top + ulp + 8);
|
||||||
|
|
||||||
|
dump_entry("REQUEST", cqe);
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
* queue */
|
||||||
|
if (cio[cid].rqs > 0) {
|
||||||
|
ulp = (ulp + QUE_E_SIZE) % (QUE_E_SIZE * cio[cid].rqs);
|
||||||
|
|
||||||
|
/* Store it back to the correct location */
|
||||||
|
pwrite_h(rqp + 2, ulp);
|
||||||
|
} else {
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[%08x] [cio_rqueue] ERROR! Request Queue Size is 0!",
|
||||||
|
R[NUM_PC]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32 io_read(uint32 pa, size_t size)
|
uint32 io_read(uint32 pa, size_t size)
|
||||||
{
|
{
|
||||||
struct iolink *p;
|
struct iolink *p;
|
||||||
|
uint8 cid, reg, data;
|
||||||
|
|
||||||
/* Special devices */
|
/* Special devices */
|
||||||
if (pa == 0x4c003) {
|
if (pa == MEMSIZE_REG) {
|
||||||
/* MEMSIZE register */
|
|
||||||
|
|
||||||
/* It appears that the following values map to memory sizes:
|
/* It appears that the following values map to memory sizes:
|
||||||
0x00: 512KB ( 524,288 B)
|
0x00: 512KB ( 524,288 B)
|
||||||
|
@ -76,20 +262,112 @@ uint32 io_read(uint32 pa, size_t size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* IO Board Area - Unimplemented */
|
/* IO Board Area - Unimplemented */
|
||||||
if (pa >= 0x200000 && pa < 0x2000000) {
|
if (pa >= CIO_BOTTOM && pa < CIO_TOP) {
|
||||||
sim_debug(IO_D_MSG, &cpu_dev, "[%08x] [IO BOARD READ] ADDR=%08x\n", R[NUM_PC], pa);
|
cid = CID(pa);
|
||||||
/* When we implement boards, register them here
|
reg = pa - CADDR(cid);
|
||||||
N.B.: High byte of board ID is read at 0xnnnnn0,
|
|
||||||
low byte at 0xnnnnn1 */
|
|
||||||
|
|
||||||
/* Since we have no cards in our system, there's nothing
|
if (cio[cid].id == 0) {
|
||||||
to read. We indicate that our bus read timed out with
|
/* Nothing lives here */
|
||||||
CSRTIMO, then abort.*/
|
csr_data |= CSRTIMO;
|
||||||
csr_data |= CSRTIMO;
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
return 0;
|
||||||
return 0;
|
}
|
||||||
|
|
||||||
|
/* A normal SYSGEN sequence is: RESET -> INT0 -> INT1.
|
||||||
|
* However, there's a bug in the 3B2/400 DGMON test suite that
|
||||||
|
* runs on every startup. This diagnostic code performs a
|
||||||
|
* SYSGEN by calling RESET -> INT1 -> INT0. So, we must handle
|
||||||
|
* both orders. */
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case IOF_ID:
|
||||||
|
case IOF_VEC:
|
||||||
|
switch(cio[cid].cmdbits) {
|
||||||
|
case 0x00: /* We've never seen an INT0 or INT1 */
|
||||||
|
case 0x01: /* We've seen an INT0 but not an INT1. */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[READ] [%08x] (%d INT0) ID\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
/* Return the correct byte of our board ID */
|
||||||
|
if (reg == IOF_ID) {
|
||||||
|
data = (cio[cid].id >> 8) & 0xff;
|
||||||
|
} else {
|
||||||
|
data = (cio[cid].id & 0xff);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x02: /* We've seen an INT1 but not an INT0. Time to sysgen */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[READ] [%08x] (%d INT0) SYSGEN\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
cio_sysgen(cid);
|
||||||
|
data = cio[cid].ivec;
|
||||||
|
break;
|
||||||
|
case 0x03: /* We've already sysgen'ed */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[READ] [%08x] (%d INT0) EXPRESS JOB\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
cio[cid].exp_handler(cid);
|
||||||
|
data = cio[cid].ivec;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* This should never happen */
|
||||||
|
stop_reason = STOP_ERR;
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[READ] [%08x] (%d INT0) ERROR IN STATE MACHINE cmdbits=%02x\n",
|
||||||
|
R[NUM_PC], cid, cio[cid].cmdbits);
|
||||||
|
data = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Record that we've seen an INT0 */
|
||||||
|
cio[cid].cmdbits |= CIO_INT0;
|
||||||
|
return data;
|
||||||
|
case IOF_CTRL:
|
||||||
|
switch(cio[cid].cmdbits) {
|
||||||
|
case 0x00: /* We've never seen an INT0 or INT1 */
|
||||||
|
case 0x02: /* We've seen an INT1 but not an INT0 */
|
||||||
|
/* There's nothing to do in this instance */
|
||||||
|
break;
|
||||||
|
case 0x01: /* We've seen an INT0 but not an INT1. Time to sysgen */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[READ] [%08x] (%d INT1) SYSGEN\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
cio_sysgen(cid);
|
||||||
|
break;
|
||||||
|
case 0x03: /* We've already sysgen'ed */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[READ] [%08x] (%d INT1) FULL\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
cio[cid].full_handler(cid);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* This should never happen */
|
||||||
|
stop_reason = STOP_ERR;
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[READ] [%08x] (%d INT1) ERROR IN STATE MACHINE cmdbits=%02x\n",
|
||||||
|
R[NUM_PC], cid, cio[cid].cmdbits);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Record that we've seen an INT1 */
|
||||||
|
cio[cid].cmdbits |= CIO_INT1;
|
||||||
|
return 0; /* Data returned is arbitrary */
|
||||||
|
case IOF_STAT:
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[READ] [%08x] (%d RESET)\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
cio[cid].cmdbits = 0;
|
||||||
|
return 0; /* Data returned is arbitrary */
|
||||||
|
default:
|
||||||
|
/* We should never reach here, but if we do, there's
|
||||||
|
* nothing listening. */
|
||||||
|
csr_data |= CSRTIMO;
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Memory-mapped IO devices */
|
||||||
for (p = &iotable[0]; p->low != 0; p++) {
|
for (p = &iotable[0]; p->low != 0; p++) {
|
||||||
if ((pa >= p->low) && (pa < p->high) && p->read) {
|
if ((pa >= p->low) && (pa < p->high) && p->read) {
|
||||||
return p->read(pa, size);
|
return p->read(pa, size);
|
||||||
|
@ -108,17 +386,109 @@ 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)
|
||||||
{
|
{
|
||||||
struct iolink *p;
|
struct iolink *p;
|
||||||
|
uint8 cid, reg;
|
||||||
|
|
||||||
/* IO Board Area - Unimplemented */
|
/* Feature Card Area */
|
||||||
if (pa >= 0x200000 && pa < 0x2000000) {
|
if (pa >= CIO_BOTTOM && pa < CIO_TOP) {
|
||||||
sim_debug(IO_D_MSG, &cpu_dev,
|
cid = CID(pa);
|
||||||
"[%08x] ADDR=%08x, DATA=%08x\n",
|
reg = pa - CADDR(cid);
|
||||||
R[NUM_PC], pa, val);
|
|
||||||
csr_data |= CSRTIMO;
|
if (cio[cid].id == 0) {
|
||||||
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
/* Nothing lives here */
|
||||||
return;
|
csr_data |= CSRTIMO;
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A normal SYSGEN sequence is: RESET -> INT0 -> INT1.
|
||||||
|
* However, there's a bug in the 3B2/400 DGMON test suite that
|
||||||
|
* runs on every startup. This diagnostic code performs a
|
||||||
|
* SYSGEN by calling RESET -> INT1 -> INT0. So, we must handle
|
||||||
|
* both orders. */
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case IOF_ID:
|
||||||
|
case IOF_VEC:
|
||||||
|
switch(cio[cid].cmdbits) {
|
||||||
|
case 0x00: /* We've never seen an INT0 or INT1 */
|
||||||
|
case 0x01: /* We've seen an INT0 but not an INT1. */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[WRITE] [%08x] (%d INT0) ID\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
break;
|
||||||
|
case 0x02: /* We've seen an INT1 but not an INT0. Time to sysgen */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[READ] [%08x] (%d INT0) SYSGEN\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
cio_sysgen(cid);
|
||||||
|
break;
|
||||||
|
case 0x03: /* We've already sysgen'ed */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[READ] [%08x] (%d INT0) EXPRESS JOB\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
cio[cid].exp_handler(cid);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* This should never happen */
|
||||||
|
stop_reason = STOP_ERR;
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[READ] [%08x] (%d INT0) ERROR IN STATE MACHINE cmdbits=%02x\n",
|
||||||
|
R[NUM_PC], cid, cio[cid].cmdbits);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Record that we've seen an INT0 */
|
||||||
|
cio[cid].cmdbits |= CIO_INT0;
|
||||||
|
return;
|
||||||
|
case IOF_CTRL:
|
||||||
|
switch(cio[cid].cmdbits) {
|
||||||
|
case 0x00: /* We've never seen an INT0 or INT1 */
|
||||||
|
case 0x02: /* We've seen an INT1 but not an INT0 */
|
||||||
|
/* There's nothing to do in this instance */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[WRITE] [%08x] (%d INT1)\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
break;
|
||||||
|
case 0x01: /* We've seen an INT0 but not an INT1. Time to sysgen */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[WRITE] [%08x] (%d INT1) SYSGEN\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
cio_sysgen(cid);
|
||||||
|
break;
|
||||||
|
case 0x03: /* We've already sysgen'ed */
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[WRITE] [%08x] (%d INT1) FULL\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
cio[cid].full_handler(cid);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* This should never happen */
|
||||||
|
stop_reason = STOP_ERR;
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[WRITE] [%08x] (%d INT1) ERROR IN STATE MACHINE cmdbits=%02x\n",
|
||||||
|
R[NUM_PC], cid, cio[cid].cmdbits);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Record that we've seen an INT1 */
|
||||||
|
cio[cid].cmdbits |= CIO_INT1;
|
||||||
|
return;
|
||||||
|
case IOF_STAT:
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"[WRITE] [%08x] (%d RESET)\n",
|
||||||
|
R[NUM_PC], cid);
|
||||||
|
cio[cid].cmdbits = 0;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
/* We should never reach here, but if we do, there's
|
||||||
|
* nothing listening. */
|
||||||
|
csr_data |= CSRTIMO;
|
||||||
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Memory-mapped IO devices */
|
||||||
for (p = &iotable[0]; p->low != 0; p++) {
|
for (p = &iotable[0]; p->low != 0; p++) {
|
||||||
if ((pa >= p->low) && (pa < p->high) && p->write) {
|
if ((pa >= p->low) && (pa < p->high) && p->write) {
|
||||||
p->write(pa, val, size);
|
p->write(pa, val, size);
|
||||||
|
@ -133,3 +503,14 @@ void io_write(uint32 pa, uint32 val, size_t size)
|
||||||
csr_data |= CSRTIMO;
|
csr_data |= CSRTIMO;
|
||||||
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* For debugging only */
|
||||||
|
void dump_entry(CONST char *type, cio_entry *entry)
|
||||||
|
{
|
||||||
|
sim_debug(IO_D_MSG, &cpu_dev,
|
||||||
|
"*** %s ENTRY: byte_count=%04x, subdevice=%02x,\n"
|
||||||
|
" opcode=%d, address=%08x, app_data=%08x\n",
|
||||||
|
type, entry->byte_count, entry->subdevice,
|
||||||
|
entry->opcode, entry->address, entry->app_data);
|
||||||
|
}
|
||||||
|
|
174
3B2/3b2_io.h
174
3B2/3b2_io.h
|
@ -28,6 +28,72 @@
|
||||||
from the author.
|
from the author.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Reference Documentation
|
||||||
|
* =======================
|
||||||
|
*
|
||||||
|
* All communication between the system board and feature cards is
|
||||||
|
* done through in-memory queues, and causing interrupts in the
|
||||||
|
* feature card by accessing the Control or ID/VEC memory-mapped IO
|
||||||
|
* addresses. The structure of these queues is defined below in
|
||||||
|
* tables.
|
||||||
|
*
|
||||||
|
* Sysgen Block
|
||||||
|
* ------------
|
||||||
|
*
|
||||||
|
* Pointed to by address at 0x2000000 after an INT0/INT1 combo
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* | Address | Size | Contents |
|
||||||
|
* +---------------+------+-----------------------------------------+
|
||||||
|
* | SYSGEN_P | 4 | Address of request queue |
|
||||||
|
* | SYSGEN_P + 4 | 4 | Address of completion queue |
|
||||||
|
* | SYSGEN_P + 8 | 1 | Number of entries in request queue |
|
||||||
|
* | SYSGEN_P + 9 | 1 | Number of entries in completion queue |
|
||||||
|
* | SYSGEN_P + 10 | 1 | Interrupt Vector number |
|
||||||
|
* | SYSGEN_P + 11 | 1 | Number of request queues |
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Queue Entry
|
||||||
|
* -----------
|
||||||
|
*
|
||||||
|
* Each queue has one Express Entry, and n regular entries.
|
||||||
|
*
|
||||||
|
* | Address | Size | Contents |
|
||||||
|
* +---------------+------+-----------------------------------------+
|
||||||
|
* | ENTRY_P | 2 | Byte Count |
|
||||||
|
* | ENTRY_P + 2 | 1 | Subdevice [1] |
|
||||||
|
* | ENTRY_P + 3 | 1 | Opcode |
|
||||||
|
* | ENTRY_P + 4 | 4 | Address / Data |
|
||||||
|
* | ENTRY_P + 8 | 4 | Application Specific Data |
|
||||||
|
*
|
||||||
|
* [1] The "Subdevice" entry is further divided into a bitset:
|
||||||
|
* Bit 7: Command (1) / Status (0)
|
||||||
|
* Bit 6: Sequence Bit
|
||||||
|
* Bit 5-1: Subdevice
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Queue
|
||||||
|
* -----
|
||||||
|
*
|
||||||
|
* The Queue structures (one for request, one for completion) hold:
|
||||||
|
* - An express entry
|
||||||
|
* - A set of pointers for load and unload from the queue
|
||||||
|
* - Zero or more Queue Entries
|
||||||
|
*
|
||||||
|
* | Address | Size | Contents |
|
||||||
|
* +---------------+------+-----------------------------------------+
|
||||||
|
* | QUEUE_P | 12 | Express Queue Entry [1] |
|
||||||
|
* | QUEUE_P + 12 | 2 | Load Pointer |
|
||||||
|
* | QUEUE_P + 14 | 2 | Unload Pointer |
|
||||||
|
* | QUEUE_P + 16 | 12 | Entry 0 [1] |
|
||||||
|
* | QUEUE_P + 28 | 12 | Entry 1 [1] |
|
||||||
|
* | ... | ... | ... |
|
||||||
|
*
|
||||||
|
* [1] See Queue Entry above
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef _3B2_IO_H_
|
#ifndef _3B2_IO_H_
|
||||||
#define _3B2_IO_H_
|
#define _3B2_IO_H_
|
||||||
|
|
||||||
|
@ -38,6 +104,69 @@
|
||||||
#include "3b2_dmac.h"
|
#include "3b2_dmac.h"
|
||||||
#include "3b2_mmu.h"
|
#include "3b2_mmu.h"
|
||||||
|
|
||||||
|
#include "sim_tmxr.h"
|
||||||
|
|
||||||
|
#define IOF_ID 0
|
||||||
|
#define IOF_VEC 1
|
||||||
|
#define IOF_CTRL 3
|
||||||
|
#define IOF_STAT 5
|
||||||
|
|
||||||
|
#define SYSGEN_PTR PHYS_MEM_BASE
|
||||||
|
#define CIO_LOAD_SIZE 0x4
|
||||||
|
#define CIO_ENTRY_SIZE 0x0c
|
||||||
|
#define CIO_QUE_OFFSET 0x10
|
||||||
|
#define CIO_SLOTS 12
|
||||||
|
|
||||||
|
/* CIO opcodes */
|
||||||
|
#define CIO_DLM 1
|
||||||
|
#define CIO_ULM 2
|
||||||
|
#define CIO_FCF 3
|
||||||
|
#define CIO_DOS 4
|
||||||
|
#define CIO_DSD 5
|
||||||
|
|
||||||
|
/* Map a physical address to a card ID */
|
||||||
|
#define CID(pa) (((((pa) >> 0x14) & 0x1f) / 2) - 1)
|
||||||
|
/* Map a card ID to a base address */
|
||||||
|
#define CADDR(bid) (((((bid) + 1) * 2) << 0x14))
|
||||||
|
|
||||||
|
#define CIO_INT0 0x1
|
||||||
|
#define CIO_INT1 0x2
|
||||||
|
|
||||||
|
/* Offsets into the request/completion queues of various values */
|
||||||
|
#define LOAD_OFFSET 12
|
||||||
|
#define ULOAD_OFFSET 14
|
||||||
|
#define QUE_OFFSET 16
|
||||||
|
#define QUE_E_SIZE 12
|
||||||
|
|
||||||
|
#define CIO_SYGEN_MASK 0x3
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16 id; /* Card ID */
|
||||||
|
void (*exp_handler)(uint8 cid); /* Handler for express jobs */
|
||||||
|
void (*full_handler)(uint8 cid); /* Handler for full jobs */
|
||||||
|
void (*sysgen)(uint8 cid); /* Sysgen routine (optional) */
|
||||||
|
uint32 rqp; /* Request Queue Pointer */
|
||||||
|
uint32 cqp; /* Completion Queue Pointer */
|
||||||
|
uint8 rqs; /* Request queue size */
|
||||||
|
uint8 cqs; /* Completion queue size */
|
||||||
|
uint8 ivec; /* Interrupt Vector */
|
||||||
|
uint8 no_rque; /* Number of request queues */
|
||||||
|
uint8 ipl; /* IPL that this card uses */
|
||||||
|
t_bool intr; /* Card needs to interrupt */
|
||||||
|
uint8 cmdbits; /* Commands received since RESET */
|
||||||
|
uint8 seqbit; /* Squence Bit */
|
||||||
|
uint8 op; /* Last received opcode */
|
||||||
|
TMLN *lines[4]; /* Terminal Multiplexer lines */
|
||||||
|
} CIO_STATE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16 byte_count;
|
||||||
|
uint8 subdevice;
|
||||||
|
uint8 opcode;
|
||||||
|
uint32 address;
|
||||||
|
uint32 app_data;
|
||||||
|
} cio_entry;
|
||||||
|
|
||||||
struct iolink {
|
struct iolink {
|
||||||
uint32 low;
|
uint32 low;
|
||||||
uint32 high;
|
uint32 high;
|
||||||
|
@ -45,4 +174,49 @@ struct iolink {
|
||||||
void (*write)(uint32 pa, uint32 val, size_t size);
|
void (*write)(uint32 pa, uint32 val, size_t size);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Example pump structure
|
||||||
|
* ----------------------
|
||||||
|
*
|
||||||
|
* Used during initial setup of PORTS card in slot 0:
|
||||||
|
*
|
||||||
|
* dev = 0100
|
||||||
|
* min = 0000
|
||||||
|
* cmdcode = 0003
|
||||||
|
* options = 0000
|
||||||
|
* bufaddr = 808821A0
|
||||||
|
* ioaddr = 00000500
|
||||||
|
* size = 00000650
|
||||||
|
* numbrd = 00000000
|
||||||
|
* retcode = 00000008 (PU_NULL)
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16 dev;
|
||||||
|
uint16 min;
|
||||||
|
uint16 cmdcode;
|
||||||
|
uint16 options;
|
||||||
|
uint32 bufaddr;
|
||||||
|
uint32 ioaddr;
|
||||||
|
uint32 size;
|
||||||
|
uint32 numbrd;
|
||||||
|
uint32 retcode;
|
||||||
|
} pump;
|
||||||
|
|
||||||
|
extern uint16 cio_ints;
|
||||||
|
extern CIO_STATE cio[CIO_SLOTS];
|
||||||
|
|
||||||
|
t_stat cio_reset(DEVICE *dptr);
|
||||||
|
t_stat cio_svc(UNIT *uptr);
|
||||||
|
|
||||||
|
/* Put an entry into the Completion Queue's Express entry */
|
||||||
|
void cio_cexpress(uint8 cid, cio_entry *cqe);
|
||||||
|
/* Put an entry into the Completion Queue */
|
||||||
|
void cio_cqueue(uint8 cid, cio_entry *cqe);
|
||||||
|
/* Get an entry from the Request Queue */
|
||||||
|
void cio_rqueue(uint8 cid, cio_entry *cqe);
|
||||||
|
/* Perform a Sysgen */
|
||||||
|
void cio_sysgen(uint8 cid);
|
||||||
|
/* Debugging only */
|
||||||
|
void dump_entry(CONST char *type, cio_entry *entry);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -73,7 +73,8 @@ const char *sim_stop_messages[] = {
|
||||||
"Exception/Trap",
|
"Exception/Trap",
|
||||||
"Exception Stack Too Deep",
|
"Exception Stack Too Deep",
|
||||||
"Unimplemented MMU Feature",
|
"Unimplemented MMU Feature",
|
||||||
"System Powered Off"
|
"System Powered Off",
|
||||||
|
"Simulator Error"
|
||||||
};
|
};
|
||||||
|
|
||||||
void full_reset()
|
void full_reset()
|
||||||
|
|
Loading…
Add table
Reference in a new issue