diff --git a/3B2/3b2_cpu.c b/3B2/3b2_cpu.c index 42f73baa..be044dfc 100644 --- a/3B2/3b2_cpu.c +++ b/3B2/3b2_cpu.c @@ -28,8 +28,6 @@ from the author. */ -#include - #include "3b2_cpu.h" #include "rom_400_bin.h" @@ -68,6 +66,8 @@ extern uint16 csr_data; uint32 R[16]; /* 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 */ 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" }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "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", 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 }, @@ -566,6 +568,10 @@ t_stat cpu_boot(int32 unit_num, DEVICE *dptr) * 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(); 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] |= 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; } @@ -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) { 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; - uint16 id = ipl; /* TODO: Does this need to be uint16? */ /* * "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." */ if (cpu_nmi) { - id = 0; + vec = 0; } cpu_km = TRUE; @@ -1548,10 +1572,11 @@ t_bool cpu_on_interrupt(uint8 ipl) if (R[NUM_PSW] & PSW_QIE_MASK) { /* TODO: Maybe implement quick interrupts at some point, but 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 */ irq_push_word(R[NUM_PCBP]); @@ -1572,8 +1597,6 @@ t_bool cpu_on_interrupt(uint8 ipl) cpu_context_switch_3(new_pcbp); cpu_km = FALSE; - - return TRUE; } t_stat sim_instr(void) @@ -1590,6 +1613,9 @@ t_stat sim_instr(void) uint32 width, offset; uint32 mask; + /* Generic index */ + uint32 i; + operand *src1, *src2, *src3, *dst; stop_reason = 0; @@ -1698,9 +1724,20 @@ t_stat sim_instr(void) increment_modep_b(); } - /* Process pending IRQ, if applicable */ - if (PSW_CUR_IPL < cpu_ipl()) { - cpu_on_interrupt(cpu_ipl()); + /* Set the correct IRQ state */ + cpu_calc_ints(); + + 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_in_wait = FALSE; } @@ -3171,7 +3208,9 @@ static uint32 cpu_read_op(operand * op) data = sign_extend_b(R[op->reg] & BYTE_MASK); break; default: - assert(0); + stop_reason = STOP_ERR; + data = 0; + break; } op->data = data; @@ -3233,7 +3272,7 @@ static uint32 cpu_read_op(operand * op) op->data = data; return data; default: - assert(0); + stop_reason = STOP_ERR; return 0; } } @@ -3288,53 +3327,42 @@ static void cpu_write_op(operand * op, t_uint64 val) write_b(eff, val & BYTE_MASK); break; default: - assert(0); + stop_reason = STOP_ERR; + break; } } /* - * This returns the current state of the IPL (Interrupt - * Priority Level) bus. This is affected by: - * - * - 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 + * Calculate the current state of interrupts. + * TODO: This could use a refactor. It's getting code-smelly. */ -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) { - 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; } /* diff --git a/3B2/3b2_cpu.h b/3B2/3b2_cpu.h index feaa20ab..1de5728b 100644 --- a/3B2/3b2_cpu.h +++ b/3B2/3b2_cpu.h @@ -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_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_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_set_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); 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_read_op(operand * op); static void cpu_write_op(operand * op, t_uint64 val); 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_stack_exception(uint8 isc); static SIM_INLINE void cpu_on_process_exception(uint8 isc); diff --git a/3B2/3b2_defs.h b/3B2/3b2_defs.h index fdc9ed01..408d9491 100644 --- a/3B2/3b2_defs.h +++ b/3B2/3b2_defs.h @@ -50,6 +50,8 @@ noret __libc_longjmp (jmp_buf buf, int val); #define longjmp __libc_longjmp #endif +/* -t flag: Translate a virtual address */ +#define EX_T_FLAG 1 << 19 /* -v flag for examine routine */ #define EX_V_FLAG 1 << 21 @@ -95,6 +97,13 @@ noret __libc_longjmp (jmp_buf buf, int val); #define NUM_ISP 14 #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)) /* 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_MMU 7 /* Unimplemented MMU Feature */ #define STOP_POWER 8 /* System power-off */ +#define STOP_ERR 9 /* Other error */ /* Exceptional conditions handled within the instruction loop */ #define ABORT_EXC 1 /* CPU exception */ @@ -292,6 +302,18 @@ noret __libc_longjmp (jmp_buf buf, int val); #define CLK_MD4 0x08 #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 */ #define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */ @@ -325,9 +347,6 @@ extern uint8 fault; extern DEBTAB sys_deb_tab[]; extern t_bool cpu_km; -/* Generic callback function */ -typedef void (*callback)(void); - /* global symbols from the DMAC */ typedef struct { uint8 page; @@ -378,6 +397,7 @@ extern void increment_modep_a(); extern void increment_modep_b(); /* 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_disable(); 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); /* Globally scoped CPU functions */ -void cpu_abort(uint8 et, uint8 isc); -void cpu_set_irq(uint8 ipl, uint8 id, uint16 csr_flags); -void cpu_clear_irq(uint8 ipl, uint16 csr_flags); +extern void cpu_abort(uint8 et, uint8 isc); +extern void cpu_set_irq(uint8 ipl, uint8 id, uint16 csr_flags); +extern void cpu_clear_irq(uint8 ipl, uint16 csr_flags); -/* Globally scoped IO functions */ -uint32 io_read(uint32 pa, size_t size); -void io_write(uint32 pa, uint32 val, size_t size); +/* global symbols from the IO system */ +extern uint32 io_read(uint32 pa, 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 diff --git a/3B2/3b2_id.c b/3B2/3b2_id.c index 522abfa8..1dd2b59a 100644 --- a/3B2/3b2_id.c +++ b/3B2/3b2_id.c @@ -42,7 +42,6 @@ * HD135 11 1224 15 18 512 Maxtor XT1190 */ -#include #include "3b2_id.h" /* Wait times, in CPU steps, for various actions */ @@ -525,7 +524,9 @@ uint32 id_read(uint32 pa, size_t size) } } } 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; diff --git a/3B2/3b2_if.c b/3B2/3b2_if.c index 3d6e291e..e38531a7 100644 --- a/3B2/3b2_if.c +++ b/3B2/3b2_if.c @@ -29,7 +29,6 @@ */ #include "3b2_if.h" -#include /* * TODO: Macros used for debugging timers. Remove when debugging is complete. @@ -397,7 +396,12 @@ void if_handle_command() } break; 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: 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); @@ -411,7 +415,11 @@ void if_handle_command() } break; 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; case IF_READ_ADDR: sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Address\n", if_state.cmd); @@ -421,7 +429,8 @@ void if_handle_command() break; case IF_READ_TRACK: 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; case IF_WRITE_TRACK: sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tWrite Track\n", if_state.cmd); diff --git a/3B2/3b2_io.c b/3B2/3b2_io.c index 91cea14a..8a12e60f 100644 --- a/3B2/3b2_io.c +++ b/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 @@ -30,6 +30,8 @@ #include "3b2_io.h" +CIO_STATE cio[CIO_SLOTS] = { 0 }; + struct iolink iotable[] = { { MMUBASE, MMUBASE+MMUSIZE, &mmu_read, &mmu_write }, { IFBASE, IFBASE+IFSIZE, &if_read, &if_write }, @@ -47,13 +49,197 @@ struct iolink iotable[] = { { 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) { struct iolink *p; + uint8 cid, reg, data; /* Special devices */ - if (pa == 0x4c003) { - /* MEMSIZE register */ + if (pa == MEMSIZE_REG) { /* It appears that the following values map to memory sizes: 0x00: 512KB ( 524,288 B) @@ -76,20 +262,112 @@ uint32 io_read(uint32 pa, size_t size) } /* IO Board Area - Unimplemented */ - if (pa >= 0x200000 && pa < 0x2000000) { - sim_debug(IO_D_MSG, &cpu_dev, "[%08x] [IO BOARD READ] ADDR=%08x\n", R[NUM_PC], pa); - /* When we implement boards, register them here - N.B.: High byte of board ID is read at 0xnnnnn0, - low byte at 0xnnnnn1 */ + if (pa >= CIO_BOTTOM && pa < CIO_TOP) { + cid = CID(pa); + reg = pa - CADDR(cid); - /* Since we have no cards in our system, there's nothing - to read. We indicate that our bus read timed out with - CSRTIMO, then abort.*/ - csr_data |= CSRTIMO; - cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); - return 0; + if (cio[cid].id == 0) { + /* Nothing lives here */ + csr_data |= CSRTIMO; + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + 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++) { if ((pa >= p->low) && (pa < p->high) && p->read) { 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) { struct iolink *p; + uint8 cid, reg; - /* IO Board Area - Unimplemented */ - if (pa >= 0x200000 && pa < 0x2000000) { - sim_debug(IO_D_MSG, &cpu_dev, - "[%08x] ADDR=%08x, DATA=%08x\n", - R[NUM_PC], pa, val); - csr_data |= CSRTIMO; - cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); - return; + /* Feature Card Area */ + if (pa >= CIO_BOTTOM && pa < CIO_TOP) { + cid = CID(pa); + reg = pa - CADDR(cid); + + if (cio[cid].id == 0) { + /* Nothing lives here */ + 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++) { if ((pa >= p->low) && (pa < p->high) && p->write) { p->write(pa, val, size); @@ -133,3 +503,14 @@ void io_write(uint32 pa, uint32 val, size_t size) csr_data |= CSRTIMO; 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); +} diff --git a/3B2/3b2_io.h b/3B2/3b2_io.h index c32289f5..660ef850 100644 --- a/3B2/3b2_io.h +++ b/3B2/3b2_io.h @@ -28,6 +28,72 @@ 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_ #define _3B2_IO_H_ @@ -38,6 +104,69 @@ #include "3b2_dmac.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 { uint32 low; uint32 high; @@ -45,4 +174,49 @@ struct iolink { 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 diff --git a/3B2/3b2_sys.c b/3B2/3b2_sys.c index 3c57d867..60b1eaf4 100644 --- a/3B2/3b2_sys.c +++ b/3B2/3b2_sys.c @@ -73,7 +73,8 @@ const char *sim_stop_messages[] = { "Exception/Trap", "Exception Stack Too Deep", "Unimplemented MMU Feature", - "System Powered Off" + "System Powered Off", + "Simulator Error" }; void full_reset()