diff --git a/3B2/3b2_cpu.c b/3B2/3b2_cpu.c index 3ec97b24..9061394d 100644 --- a/3B2/3b2_cpu.c +++ b/3B2/3b2_cpu.c @@ -28,13 +28,34 @@ from the author. */ -#include "3b2_defs.h" -#include "3b2_cpu.h" -#include "rom_rev2_bin.h" +/* + * This is an implementation of the WE32100 and WE32200 CPUs, used in + * the Rev 2 (e.g. 3B2/400) and Rev 3 (e.g. 3B2/600G) AT&T 3B2 + * systems, respectively. + * + * The WE32000 series of microprocessors were a fully 32-bit general + * purpose CISC architecture purposely designed to run UNIX. + * + * The architecture is fully described in the following books: + * + * - "WE 32100 Microprocessor Information Manual" (AT&T, 1985) + * - "WE 32200 Microprocessor Information Manual" (AT&T, 1988) + * + */ #include +#include "3b2_defs.h" +#include "3b2_cpu.h" +#if defined(REV3) +#include "rom_rev3_bin.h" +#else +#include "rom_rev2_bin.h" +#endif + #define MAX_SUB_RETURN_SKIP 9 +uint32 rom_size = BOOT_CODE_SIZE; + /* Static function declarations */ static uint32 cpu_effective_address(operand * op); static uint32 cpu_read_op(operand * op); @@ -100,7 +121,7 @@ volatile int32 stop_reason; volatile uint32 abort_reason; /* Register data */ -uint32 R[16]; +uint32 R[NUM_REGISTERS]; /* Other global CPU state */ uint8 cpu_int_ipl = 0; /* Interrupt IPL level */ @@ -108,7 +129,7 @@ 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 - currently being executed */ + currently being executed */ t_bool cpu_ex_halt = FALSE; /* Flag to halt on exceptions / traps */ t_bool cpu_km = FALSE; /* If true, kernel mode has been forced @@ -137,13 +158,20 @@ BITFIELD psw_bits[] = { BIT(CD), /* Cache Disable */ BIT(QIE), /* Quick-Interrupt Enable */ BIT(CFD), /* Cache Flush Disable */ +#if defined(REV3) + BIT(X), /* Extend Carry / Borrow */ + BIT(AR), /* Additional Register Save */ + BIT(EXUC), /* Exception/User Call Option */ + BIT(EA), /* Enable Arbitrary Alignment */ + BITNCF(2), /* Unused */ +#else BITNCF(6), /* Unused */ +#endif ENDBITS }; /* Registers. */ REG cpu_reg[] = { - { HRDATAD (PC, R[NUM_PC], 32, "Program Counter") }, { HRDATAD (R0, R[0], 32, "General purpose register 0") }, { HRDATAD (R1, R[1], 32, "General purpose register 1") }, { HRDATAD (R2, R[2], 32, "General purpose register 2") }, @@ -159,6 +187,27 @@ REG cpu_reg[] = { { HRDATAD (SP, R[NUM_SP], 32, "Stack Pointer") }, { HRDATAD (PCBP, R[NUM_PCBP], 32, "Process Control Block Pointer") }, { HRDATAD (ISP, R[NUM_ISP], 32, "Interrupt Stack Pointer") }, + { HRDATAD (PC, R[NUM_PC], 32, "Program Counter") }, +#if defined(REV3) + { HRDATAD (R16, R[16], 32, "General purpose register 16")}, + { HRDATAD (R17, R[17], 32, "General purpose register 17")}, + { HRDATAD (R18, R[18], 32, "General purpose register 18")}, + { HRDATAD (R19, R[19], 32, "General purpose register 19")}, + { HRDATAD (R20, R[20], 32, "General purpose register 20")}, + { HRDATAD (R21, R[21], 32, "General purpose register 21")}, + { HRDATAD (R22, R[22], 32, "General purpose register 22")}, + { HRDATAD (R23, R[23], 32, "General purpose register 23")}, + { HRDATAD (R24, R[24], 32, "Privileged register 24")}, + { HRDATAD (R25, R[25], 32, "Privileged register 25")}, + { HRDATAD (R26, R[26], 32, "Privileged register 26")}, + { HRDATAD (R27, R[27], 32, "Privileged register 27")}, + { HRDATAD (R28, R[28], 32, "Privileged register 28")}, + { HRDATAD (R29, R[29], 32, "Privileged register 29")}, + { HRDATAD (R30, R[30], 32, "Privileged register 30")}, + { HRDATAD (R31, R[31], 32, "Privileged register 31")}, +#endif + { HRDATAD (IPL, cpu_int_ipl, 8, "Current CPU IPL bits")}, + { HRDATAD (VEC, cpu_int_vec, 8, "Current CPU interrupt vector")}, { NULL } }; @@ -176,7 +225,9 @@ static DEBTAB cpu_deb_tab[] = { { NULL, 0, NULL } }; -UNIT cpu_unit = { UDATA (NULL, UNIT_FIX|UNIT_BINK|UNIT_IDLE, MAXMEMSIZE) }; +UNIT cpu_unit = { + UDATA (NULL, UNIT_FIX|UNIT_BINK|UNIT_IDLE, DEFMEMSIZE) +}; /* * The following commands deposit a small calibration program into @@ -239,22 +290,34 @@ static const char *att3b2_clock_precalibrate_commands[] = { NULL }; -/* - * TODO: This works fine for now, but the moment we want to emulate - * SCSI (0x0100) or EPORTS (0x0102) we're in trouble! - */ -const char *cio_names[8] = { - "", "SBD", "NI", "PORTS", - "*VOID*", "CTC", "NAU", "*VOID*" +CONST cio_device cio_entries[] = { + {0x0001, "SBD"}, /* System Board */ + {0x0002, "NI"}, /* Network Interface Card (ethernet) */ + {0x0003, "PORTS"}, /* Serial I/O Card */ + {0x0005, "CTC"}, /* Cartridge Tape Controller */ + {0x0100, "SCSI"}, /* SCSI disk and tape controller */ + {0} /* END */ }; MTAB cpu_mod[] = { +#if defined(REV2) { UNIT_MSIZE, (1u << 20), NULL, "1M", &cpu_set_size, NULL, NULL, "Set Memory to 1M bytes" }, { UNIT_MSIZE, (1u << 21), NULL, "2M", &cpu_set_size, NULL, NULL, "Set Memory to 2M bytes" }, +#endif { UNIT_MSIZE, (1u << 22), NULL, "4M", &cpu_set_size, NULL, NULL, "Set Memory to 4M bytes" }, +#if defined(REV3) + { UNIT_MSIZE, (1u << 23), NULL, "8M", + &cpu_set_size, NULL, NULL, "Set Memory to 8M bytes" }, + { UNIT_MSIZE, (1u << 24), NULL, "16M", + &cpu_set_size, NULL, NULL, "Set Memory to 16M bytes" }, + { UNIT_MSIZE, (1u << 25), NULL, "32M", + &cpu_set_size, NULL, NULL, "Set Memory to 32M bytes" }, + { UNIT_MSIZE, (1u << 26), NULL, "64M", + &cpu_set_size, NULL, NULL, "Set Memory to 64M bytes" }, +#endif { 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, @@ -265,10 +328,14 @@ MTAB cpu_mod[] = { NULL, &cpu_show_cio, NULL, "Display CIO configuration" }, { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, - { UNIT_EXHALT, UNIT_EXHALT, "Halt on Exception", "EXHALT", - NULL, NULL, NULL, "Enables Halt on exceptions and traps" }, - { UNIT_EXHALT, 0, "No halt on exception", "NOEXHALT", - NULL, NULL, NULL, "Disables Halt on exceptions and traps" }, + { UNIT_EXBRK, UNIT_EXBRK, "Break on exceptions", "EXBRK", + NULL, NULL, NULL, "Enable break on exceptions and traps" }, + { UNIT_EXBRK, 0, "No break on exceptions", "NOEXBRK", + NULL, NULL, NULL, "Disable break on exceptions and traps" }, + { UNIT_OPBRK, UNIT_OPBRK, "Break on invalid opcodes", "OPBRK", + NULL, NULL, NULL, "Enable break on invalid opcodes" }, + { UNIT_OPBRK, 0, "No break on invalid opcodes", "NOOPBRK", + NULL, NULL, NULL, "Disable break on invalid opcodes" }, { 0 } }; @@ -301,8 +368,6 @@ DEVICE cpu_dev = { &cpu_description /* Device Description */ }; -#define HWORD_OP_COUNT 11 - mnemonic hword_ops[HWORD_OP_COUNT] = { {0x3009, 0, OP_NONE, NA, "MVERNO", -1, -1, -1, -1}, {0x300d, 0, OP_NONE, NA, "ENBVJMP", -1, -1, -1, -1}, @@ -314,6 +379,9 @@ mnemonic hword_ops[HWORD_OP_COUNT] = { {0x3045, 0, OP_NONE, NA, "RETG", -1, -1, -1, -1}, {0x3061, 0, OP_NONE, NA, "GATE", -1, -1, -1, -1}, {0x30ac, 0, OP_NONE, NA, "CALLPS", -1, -1, -1, -1}, +#if defined(REV3) + {0x30c0, 0, OP_NONE, NA, "UCALLPS", -1, -1, -1, -1}, +#endif {0x30c8, 0, OP_NONE, NA, "RETPS", -1, -1, -1, -1} }; @@ -328,7 +396,11 @@ mnemonic ops[256] = { {0x06, 2, OP_COPR, WD, "SPOPRT", 1, -1, -1, -1}, {0x07, 3, OP_COPR, WD, "SPOPT2", 1, -1, -1, 2}, {0x08, 0, OP_NONE, NA, "RET", -1, -1, -1, -1}, +#if defined(REV3) + {0x09, 3, OP_DESC, WD, "CASWI", 0, 1, -1, 2}, +#else {0x09, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0x0a, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x0b, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x0c, 2, OP_DESC, WD, "MOVTRW", 0, -1, -1, 1}, @@ -650,12 +722,23 @@ t_stat cpu_show_stack(FILE *st, UNIT *uptr, int32 val, CONST void *desc) t_stat cpu_show_cio(FILE *st, UNIT *uptr, int32 val, CONST void *desc) { - uint32 i; + uint32 i, j; fprintf(st, " SLOT DEVICE\n"); fprintf(st, "---------------------\n"); for (i = 0; i < CIO_SLOTS; i++) { - fprintf(st, " %d %s\n", i, cio_names[cio[i].id & 0x7]); + /* Find the matching entry for this slot */ + for (j = 0; cio_entries[j].id != 0; j++) { + if (cio_entries[j].id == cio[i].id) { + break; + } + } + + if (cio_entries[j].id == 0) { + fprintf(st, " %d\n", i); + } else { + fprintf(st, " %d %s\n", i, cio_entries[j].name); + } } return SCPE_OK; @@ -669,7 +752,7 @@ void cpu_load_rom() return; } - for (i = 0; i < BOOT_CODE_SIZE; i++) { + for (i = 0; i < rom_size; i++) { val = BOOT_CODE_ARRAY[i]; sc = (~(i & 3) << 3) & 0x1f; mask = 0xffu << sc; @@ -790,32 +873,31 @@ t_stat cpu_reset(DEVICE *dptr) if (!sim_is_running) { /* Clear registers */ - for (i = 0; i < 16; i++) { + for (i = 0; i < NUM_REGISTERS; i++) { R[i] = 0; } - /* Allocate memory */ - if (ROM == NULL) { - ROM = (uint32 *) calloc(BOOT_CODE_SIZE >> 2, sizeof(uint32)); - if (ROM == NULL) { - return SCPE_MEM; - } - - memset(ROM, 0, BOOT_CODE_SIZE >> 2); + /* Allocate ROM */ + if (ROM != NULL) { + free(ROM); } - - if (RAM == NULL) { - RAM = (uint32 *) calloc((size_t)(MEM_SIZE >> 2), sizeof(uint32)); - if (RAM == NULL) { - return SCPE_MEM; - } - - memset(RAM, 0, (size_t)(MEM_SIZE >> 2)); - - sim_vm_is_subroutine_call = cpu_is_pc_a_subroutine_call; + ROM = (uint32 *) calloc((size_t)(rom_size >> 2), sizeof(uint32)); + if (ROM == NULL) { + return SCPE_MEM; } cpu_load_rom(); + + /* Allocate RAM */ + if (RAM != NULL) { + free(RAM); + } + RAM = (uint32 *) calloc((size_t)(MEM_SIZE >> 2), sizeof(uint32)); + if (RAM == NULL) { + return SCPE_MEM; + } + + sim_vm_is_subroutine_call = cpu_is_pc_a_subroutine_call; } abort_context = C_NONE; @@ -894,7 +976,7 @@ t_stat fprint_sym_m(FILE *of, t_addr addr, t_value *val) if (inst == 0x30) { /* Scan to find opcode */ - inst = 0x3000 | (int8) val[vp++]; + inst = 0x3000 | (uint8) val[vp++]; for (i = 0; i < HWORD_OP_COUNT; i++) { if (hword_ops[i].opcode == inst) { mn = &hword_ops[i]; @@ -1415,7 +1497,7 @@ static uint8 decode_operand(uint32 pa, instr *instr, uint8 op_number, int8 *etyp operand *oper = &instr->operands[op_number]; /* Read in the descriptor byte */ - desc = read_b(pa + offset++, ACC_OF); + desc = read_b(pa + offset++, ACC_IF); oper->mode = (desc >> 4) & 0xf; oper->reg = desc & 0xf; @@ -1434,10 +1516,10 @@ static uint8 decode_operand(uint32 pa, instr *instr, uint8 op_number, int8 *etyp case 4: /* Word Immediate, Register Mode */ switch (oper->reg) { case 15: /* Word Immediate */ - oper->embedded.w = (uint32) read_b(pa + offset++, ACC_OF); - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 8u; - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 16u; - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 24u; + oper->embedded.w = (uint32) read_b(pa + offset++, ACC_IF); + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 8u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 16u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 24u; oper->data = oper->embedded.w; break; default: /* Register mode */ @@ -1448,8 +1530,8 @@ static uint8 decode_operand(uint32 pa, instr *instr, uint8 op_number, int8 *etyp case 5: /* Halfword Immediate, Register Deferred Mode */ switch (oper->reg) { case 15: /* Halfword Immediate */ - oper->embedded.h = (uint16) read_b(pa + offset++, ACC_OF); - oper->embedded.h |= ((uint16) read_b(pa + offset++, ACC_OF)) << 8u; + oper->embedded.h = (uint16) read_b(pa + offset++, ACC_IF); + oper->embedded.h |= ((uint16) read_b(pa + offset++, ACC_IF)) << 8u; oper->data = oper->embedded.h; break; case 11: /* INVALID */ @@ -1463,7 +1545,7 @@ static uint8 decode_operand(uint32 pa, instr *instr, uint8 op_number, int8 *etyp case 6: /* Byte Immediate, FP Short Offset */ switch (oper->reg) { case 15: /* Byte Immediate */ - oper->embedded.b = read_b(pa + offset++, ACC_OF); + oper->embedded.b = read_b(pa + offset++, ACC_IF); oper->data = oper->embedded.b; break; default: /* FP Short Offset */ @@ -1475,10 +1557,10 @@ static uint8 decode_operand(uint32 pa, instr *instr, uint8 op_number, int8 *etyp case 7: /* Absolute, AP Short Offset */ switch (oper->reg) { case 15: /* Absolute */ - oper->embedded.w = (uint32) read_b(pa + offset++, ACC_OF); - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 8u; - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 16u; - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 24u; + oper->embedded.w = (uint32) read_b(pa + offset++, ACC_IF); + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 8u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 16u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 24u; oper->data = oper->embedded.w; break; default: /* AP Short Offset */ @@ -1489,30 +1571,30 @@ static uint8 decode_operand(uint32 pa, instr *instr, uint8 op_number, int8 *etyp break; case 8: /* Word Displacement */ case 9: /* Word Displacement Deferred */ - oper->embedded.w = (uint32) read_b(pa + offset++, ACC_OF); - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 8u; - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 16u; - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 24u; + oper->embedded.w = (uint32) read_b(pa + offset++, ACC_IF); + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 8u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 16u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 24u; oper->data = oper->embedded.w; break; case 10: /* Halfword Displacement */ case 11: /* Halfword Displacement Deferred */ - oper->embedded.h = read_b(pa + offset++, ACC_OF); - oper->embedded.h |= ((uint16) read_b(pa + offset++, ACC_OF)) << 8u; + oper->embedded.h = read_b(pa + offset++, ACC_IF); + oper->embedded.h |= ((uint16) read_b(pa + offset++, ACC_IF)) << 8u; oper->data = oper->embedded.h; break; case 12: /* Byte Displacement */ case 13: /* Byte Displacement Deferred */ - oper->embedded.b = read_b(pa + offset++, ACC_OF); + oper->embedded.b = read_b(pa + offset++, ACC_IF); oper->data = oper->embedded.b; break; case 14: /* Absolute Deferred, Extended-Operand */ switch (oper->reg) { case 15: /* Absolute Deferred */ - oper->embedded.w = (uint32) read_b(pa + offset++, ACC_OF); - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 8u; - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 16u; - oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_OF)) << 24u; + oper->embedded.w = (uint32) read_b(pa + offset++, ACC_IF); + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 8u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 16u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF)) << 24u; break; case 0: case 2: @@ -1613,21 +1695,21 @@ uint8 decode_instruction(instr *instr) case OP_NONE: break; case OP_BYTE: - instr->operands[0].embedded.b = read_b(pa + offset++, ACC_OF); + instr->operands[0].embedded.b = read_b(pa + offset++, ACC_IF); instr->operands[0].mode = 6; instr->operands[0].reg = 15; break; case OP_HALF: - instr->operands[0].embedded.h = read_b(pa + offset++, ACC_OF); - instr->operands[0].embedded.h |= read_b(pa + offset++, ACC_OF) << 8; + instr->operands[0].embedded.h = read_b(pa + offset++, ACC_IF); + instr->operands[0].embedded.h |= read_b(pa + offset++, ACC_IF) << 8; instr->operands[0].mode = 5; instr->operands[0].reg = 15; break; case OP_COPR: - instr->operands[0].embedded.w = (uint32) read_b(pa + offset++, ACC_OF); - instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_OF) << 8; - instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_OF) << 16; - instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_OF) << 24; + instr->operands[0].embedded.w = (uint32) read_b(pa + offset++, ACC_IF); + instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_IF) << 8; + instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_IF) << 16; + instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_IF) << 24; instr->operands[0].mode = 4; instr->operands[0].reg = 15; @@ -1725,11 +1807,11 @@ static SIM_INLINE void cpu_context_switch_1(uint32 new_pcbp) void cpu_on_interrupt(uint16 vec) { - uint32 new_pcbp; + uint32 new_pcbp, new_pcbp_ptr; sim_debug(IRQ_MSG, &cpu_dev, - "[%08x] [cpu_on_interrupt] vec=%02x (%d)\n", - R[NUM_PC], vec, vec); + "[%08x] [cpu_on_interrupt] vec=%02x (%d) csr_data = %x\n", + R[NUM_PC], vec, vec, csr_data); /* * "If a nonmaskable interrupt request is received, an auto-vector @@ -1741,6 +1823,7 @@ void cpu_on_interrupt(uint16 vec) vec = 0; } + cpu_nmi = FALSE; cpu_km = TRUE; if (R[NUM_PSW] & PSW_QIE_MASK) { @@ -1750,7 +1833,8 @@ void cpu_on_interrupt(uint16 vec) return; } - new_pcbp = read_w(0x8c + (4 * vec), ACC_AF); + new_pcbp_ptr = (uint32)0x8c + (4 * (uint32)vec); + new_pcbp = read_w(new_pcbp_ptr, ACC_AF); /* Save the old PCBP */ irq_push_word(R[NUM_PCBP]); @@ -1810,7 +1894,7 @@ t_stat sim_instr(void) return STOP_ESTK; } - if (cpu_unit.flags & UNIT_EXHALT) { + if (cpu_unit.flags & UNIT_EXBRK) { return STOP_EX; } @@ -1906,7 +1990,7 @@ t_stat sim_instr(void) /* Set the correct IRQ state */ cpu_calc_ints(); - if (PSW_CUR_IPL < cpu_int_ipl) { + if (cpu_nmi || (PSW_CUR_IPL < cpu_int_ipl)) { cpu_on_interrupt(cpu_int_vec); for (i = 0; i < CIO_SLOTS; i++) { if (cio[i].intr && @@ -1933,7 +2017,8 @@ t_stat sim_instr(void) } /* Reset the TM bits */ - R[NUM_PSW] |= PSW_TM_MASK; + /* TODO: Figure out why we were doing this! */ + /* R[NUM_PSW] |= PSW_TM_MASK; */ /* Record the instruction for history */ if (cpu_hist_size > 0) { @@ -2139,6 +2224,7 @@ t_stat sim_instr(void) case BPT: case HALT: trap = BREAKPOINT_TRAP; + stop_reason = STOP_IBKPT; break; case BRH: pc_incr = sign_extend_h(dst->embedded.h); @@ -2189,6 +2275,25 @@ t_stat sim_instr(void) R[NUM_AP] = a; pc_incr = 0; break; +#if defined(REV3) + case CASWI: + a = cpu_read_op(src1); + b = cpu_read_op(src2); + c = cpu_read_op(dst); + result = c - b; + + if (result == 0) { + cpu_write_op(src2, c); + } else { + cpu_write_op(dst, a); + } + + cpu_set_n_flag((int32)result < 0); + cpu_set_z_flag(result == 0); + cpu_set_c_flag((uint32)b > (uint32)c); + cpu_set_v_flag_op(result, dst); + break; +#endif case CFLUSH: break; case CALLPS: @@ -2384,7 +2489,7 @@ t_stat sim_instr(void) cpu_set_c_flag(0); break; case MVERNO: - R[0] = WE32100_VER; + R[0] = CPU_VERSION; break; case ENBVJMP: if (cpu_execution_level() != EX_LVL_KERN) { @@ -2483,21 +2588,21 @@ t_stat sim_instr(void) result = a >> b; /* Ensure the MSB is copied appropriately */ switch (op_type(src2)) { - case WD: - if (a & 0x80000000) { - result |= shift_32_table[b + 1]; - } - break; - case HW: - if (a & 0x8000) { - result |= shift_16_table[b + 1]; - } - break; - case BT: - if (a & 0x80) { - result |= shift_8_table[b + 1]; - } - break; + case WD: + if (a & 0x80000000) { + result |= shift_32_table[b + 1]; + } + break; + case HW: + if (a & 0x8000) { + result |= shift_16_table[b + 1]; + } + break; + case BT: + if (a & 0x80) { + result |= shift_8_table[b + 1]; + } + break; } cpu_write_op(dst, result); cpu_set_nz_flags(result, dst); @@ -2549,6 +2654,20 @@ t_stat sim_instr(void) /* Finish push of PC and PSW */ R[NUM_SP] += 8; pc_incr = 0; + +#if defined(REV3) + /* + * NB: The WE32200 processor manual claims that this is + * not a privileged instruction, and that it can be run + * from any processor level. This is not true. Tests in + * the Rev 3 off-line processor diagnostics check to + * ensure that there is an exception raised when calling + * GATE in a non-privileged context. + */ + if (cpu_execution_level() != EX_LVL_KERN) { + cpu_abort(NORMAL_EXCEPTION, PRIVILEGED_OPCODE); + } +#endif break; case MCOMW: case MCOMH: @@ -3098,7 +3217,13 @@ t_stat sim_instr(void) cpu_set_v_flag_op(a, dst); break; default: - stop_reason = STOP_OPCODE; + if (cpu_unit.flags & UNIT_OPBRK) { + stop_reason = STOP_OPCODE; + } + sim_debug(EXECUTE_MSG, &cpu_dev, + "[%08x] Illegal Opcode 0x%x\n", + R[NUM_PC], inst.mn->opcode); + cpu_abort(NORMAL_EXCEPTION, ILLEGAL_OPCODE); break; }; @@ -3237,6 +3362,7 @@ static SIM_INLINE void cpu_perform_gate(uint32 index1, uint32 index2) { uint32 gate_l2, new_psw; + abort_context = C_NORMAL_GATE_VECTOR; cpu_km = TRUE; gate_l2 = read_w(index1, ACC_AF) + index2; @@ -3262,6 +3388,7 @@ static SIM_INLINE void cpu_perform_gate(uint32 index1, uint32 index2) R[NUM_PSW] = new_psw; cpu_km = FALSE; + abort_context = C_NONE; } /* @@ -3329,7 +3456,9 @@ static uint32 cpu_effective_address(operand *op) return read_w(R[op->reg] + sign_extend_b(op->embedded.b), ACC_AF); } - stop_reason = STOP_OPCODE; + if (cpu_unit.flags & UNIT_OPBRK) { + stop_reason = STOP_OPCODE; + } return 0; } @@ -3507,9 +3636,13 @@ static void cpu_write_op(operand * op, t_uint64 val) return; } - /* Registers always get the full 32-bits written */ - - R[op->reg] = (uint32) val; + /* Registers always get the full 32-bits written, EXCEPT for + * the PSW, which has some Read-Only fields. */ + if (op->reg == NUM_PSW) { + WRITE_PSW((uint32) val); + } else { + R[op->reg] = (uint32) val; + } return; } @@ -3566,19 +3699,25 @@ static void cpu_calc_ints() } /* If none was found, look for system board interrupts */ + +#if defined(REV3) + // TODO: Rev 3 interrupt support + cpu_int_ipl = cpu_int_vec = 0; +#else if (csr_data & CSRPIR8) { - cpu_int_ipl = cpu_int_vec = CPU_PIR8_IPL; + cpu_int_ipl = cpu_int_vec = CPU_IPL_8; } else if (csr_data & CSRPIR9) { - cpu_int_ipl = cpu_int_vec = CPU_PIR9_IPL; + cpu_int_ipl = cpu_int_vec = CPU_IPL_9; } else if (id_int() || (csr_data & CSRDISK)) { - cpu_int_ipl = cpu_int_vec = CPU_ID_IF_IPL; + cpu_int_ipl = cpu_int_vec = CPU_IPL_11; } else if ((csr_data & CSRUART) || (csr_data & CSRDMA)) { - cpu_int_ipl = cpu_int_vec = CPU_IU_DMA_IPL; + cpu_int_ipl = cpu_int_vec = CPU_IPL_13; } else if ((csr_data & CSRCLK) || (csr_data & CSRTIMO)) { - cpu_int_ipl = cpu_int_vec = CPU_TMR_IPL; + cpu_int_ipl = cpu_int_vec = CPU_IPL_15; } else { cpu_int_ipl = cpu_int_vec = 0; } +#endif } /* @@ -3642,27 +3781,27 @@ static SIM_INLINE t_bool cpu_v_flag() static SIM_INLINE void cpu_set_z_flag(t_bool val) { if (val) { - R[NUM_PSW] = R[NUM_PSW] | PSW_Z_MASK; + R[NUM_PSW] |= PSW_Z_MASK; } else { - R[NUM_PSW] = R[NUM_PSW] & ~PSW_Z_MASK; + R[NUM_PSW] &= ~PSW_Z_MASK; } } static SIM_INLINE void cpu_set_n_flag(t_bool val) { if (val) { - R[NUM_PSW] = R[NUM_PSW] | PSW_N_MASK; + R[NUM_PSW] |= PSW_N_MASK; } else { - R[NUM_PSW] = R[NUM_PSW] & ~PSW_N_MASK; + R[NUM_PSW] &= ~PSW_N_MASK; } } static SIM_INLINE void cpu_set_c_flag(t_bool val) { if (val) { - R[NUM_PSW] = R[NUM_PSW] | PSW_C_MASK; + R[NUM_PSW] |= PSW_C_MASK; } else { - R[NUM_PSW] = R[NUM_PSW] & ~PSW_C_MASK; + R[NUM_PSW] &= ~PSW_C_MASK; } } @@ -3688,12 +3827,12 @@ static SIM_INLINE void cpu_set_v_flag_op(t_uint64 val, operand *op) static SIM_INLINE void cpu_set_v_flag(t_bool val) { if (val) { - R[NUM_PSW] = R[NUM_PSW] | PSW_V_MASK; + R[NUM_PSW] |= PSW_V_MASK; if (R[NUM_PSW] & PSW_OE_MASK) { cpu_abort(NORMAL_EXCEPTION, INTEGER_OVERFLOW); } } else { - R[NUM_PSW] = R[NUM_PSW] & ~PSW_V_MASK; + R[NUM_PSW] &= ~PSW_V_MASK; } } @@ -3825,19 +3964,28 @@ void cpu_abort(uint8 et, uint8 isc) CONST char *cpu_description(DEVICE *dptr) { +#if defined(REV3) + return "3B2/600G CPU (WE 32200)"; +#else return "3B2/400 CPU (WE 32100)"; +#endif } t_stat cpu_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { +#if defined(REV3) + fprintf(st, "3B2/600G CPU Help\n\n"); + fprintf(st, "The 3B2/600G CPU simulates a WE 32200 at 24 MHz.\n\n"); +#else fprintf(st, "3B2/400 CPU Help\n\n"); fprintf(st, "The 3B2/400 CPU simulates a WE 32100 at 10 MHz.\n\n"); +#endif fprintf(st, "CPU options include the size of main memory.\n\n"); if (dptr->modifiers) { MTAB *mptr; for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) { if (mptr->valid == &cpu_set_size) { - fprintf(st, " sim> SET CPU %4s set memory size = %sB\n", + fprintf(st, " sim> SET CPU %3s set memory size = %sB\n", mptr->mstring, mptr->mstring); } } @@ -3860,8 +4008,11 @@ t_stat cpu_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr fprintf(st, " sim> SET CPU HISTORY=n enable history, length = n\n"); fprintf(st, " sim> SHOW CPU HISTORY print CPU history\n"); fprintf(st, " sim> SHOW CPU HISTORY=n print last n entries of CPU history\n\n"); - - fprintf(st, "Additional docuentation for the 3B2/400 Simulator is available on the web:\n\n"); +#if defined(REV3) + fprintf(st, "Additional documentation for the 3B2/600G Simulator is available on the web:\n\n"); +#else + fprintf(st, "Additional documentation for the 3B2/400 Simulator is available on the web:\n\n"); +#endif fprintf(st, " https://loomcom.com/3b2/emulator.html\n\n"); return SCPE_OK; diff --git a/3B2/3b2_cpu.h b/3B2/3b2_cpu.h index f83811ac..1f7bfec8 100644 --- a/3B2/3b2_cpu.h +++ b/3B2/3b2_cpu.h @@ -67,7 +67,7 @@ #define NEW_PCB_FAULT 4 #define GATE_VECTOR_FAULT 6 -/* Processor Exceptions */ +/* Process Exceptions */ #define GATE_PCB_FAULT 1 /* Stack Exceptions */ @@ -91,8 +91,8 @@ #define PRIVILEGED_REGISTER 15 #define PSW_ET 0 -#define PSW_TM 2u -#define PSW_ISC 3u +#define PSW_TM 2 +#define PSW_ISC 3 #define PSW_I 7 #define PSW_R 8 #define PSW_PM 9 @@ -127,6 +127,10 @@ #define PSW_CFD_MASK (1u << PSW_CFD) #define PSW_CUR_IPL (((R[NUM_PSW] & PSW_IPL_MASK) >> PSW_IPL) & 0xf) +/* A helper to set the PSW, preserving read-only fields */ +#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))) + /* Exceptional conditions handled within the instruction loop */ #define ABORT_EXC 1 /* CPU exception */ @@ -152,12 +156,19 @@ #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_IPL_8 8 +#define CPU_IPL_9 9 +#define CPU_IPL_11 11 +#define CPU_IPL_13 13 +#define CPU_IPL_15 15 +/* Processor permission levels */ +#define L_KERNEL 0 +#define L_EXEC 1 +#define L_SUPER 2 +#define L_USER 3 + +/* Currently selected processor permission level */ #define CPU_CM (cpu_km ? L_KERNEL : ((R[NUM_PSW] >> PSW_CM) & 3)) /* Data types operated on by instructions. NB: These integer values @@ -172,9 +183,6 @@ #define NA -1 -/* Processor Version Number */ -#define WE32100_VER 0x1A - /* * * Mode Syntax Mode Reg. Bytes Notes @@ -224,13 +232,29 @@ typedef enum { SPOPRT = 0x06, SPOPT2 = 0x07, RET = 0x08, +#if defined(REV3) + CASWI = 0x09, + SETX = 0x0A, + CLRX = 0x0B, +#endif MOVTRW = 0x0C, +#if defined(REV3) + TEDTH = 0x0D, + PACKB = 0x0E, + UNPACKB = 0x0F, +#endif SAVE = 0x10, SPOPWD = 0x13, EXTOP = 0x14, SPOPWT = 0x17, RESTORE = 0x18, +#if defined(REV3) + DTH = 0x19, +#endif SWAPWI = 0x1C, +#if defined(REV3) + TGEDTH = 0x1D, +#endif SWAPHI = 0x1E, SWAPBI = 0x1F, POPW = 0x20, @@ -239,9 +263,15 @@ typedef enum { JMP = 0x24, CFLUSH = 0x27, TSTW = 0x28, +#if defined(REV3) + DTB = 0x29, +#endif TSTH = 0x2A, TSTB = 0x2B, CALL = 0x2C, +#if defined(REV3) + TGDTH = 0x2D, +#endif BPT = 0x2E, WAIT = 0x2F, EMB = 0x30, /* Multi-byte */ @@ -254,6 +284,9 @@ typedef enum { BITH = 0x3A, BITB = 0x3B, CMPW = 0x3C, +#if defined(REV3) + TNEDTH = 0x3D, +#endif CMPH = 0x3E, CMPB = 0x3F, RGEQ = 0x40, @@ -266,6 +299,9 @@ typedef enum { BLH = 0x4A, BLB = 0x4B, RLEQ = 0x4C, +#if defined(REV3) + TEDTB = 0x4D, +#endif BLEH = 0x4E, BLEB = 0x4F, RGEQU = 0x50, @@ -278,6 +314,9 @@ typedef enum { BLUH = 0x5A, BLUB = 0x5B, RLEQU = 0x5C, +#if defined(REV3) + TGEDTB = 0x5D, +#endif BLEUH = 0x5E, BLEUB = 0x5F, RVC = 0x60, @@ -290,12 +329,14 @@ typedef enum { BVSH = 0x6A, BVSB = 0x6B, REQLU = 0x6C, +#if defined(REV3) + TGDTB = 0x6D, +#endif BEH_D = 0x6E, BEB_D = 0x6F, NOP = 0x70, NOP3 = 0x72, NOP2 = 0x73, - BNEQ = 0x74, RNEQ = 0x74, BNEH = 0x76, BNEB = 0x77, @@ -303,6 +344,9 @@ typedef enum { BRH = 0x7A, BRB = 0x7B, REQL = 0x7C, +#if defined(REV3) + TNEDTB = 0x7D, +#endif BEH = 0x7E, BEB = 0x7F, CLRW = 0x80, @@ -323,10 +367,17 @@ typedef enum { DECW = 0x94, DECH = 0x96, DECB = 0x97, +#if defined(REV3) + RETQINT = 0x98, + SUBPB2 = 0x9B, +#endif ADDW2 = 0x9C, ADDH2 = 0x9E, ADDB2 = 0x9F, PUSHW = 0xA0, +#if defined(REV3) + ADDPB2 = 0xA3, +#endif MODW2 = 0xA4, MODH2 = 0xA6, MODB2 = 0xA7, @@ -363,10 +414,16 @@ typedef enum { LLSB3 = 0xD3, LRSW3 = 0xD4, ROTW = 0xD8, +#if defined(REV3) + SUBPB3 = 0xDB, +#endif ADDW3 = 0xDC, ADDH3 = 0xDE, ADDB3 = 0xDF, PUSHAW = 0xE0, +#if defined(REV3) + ADDPB3 = 0xE3, +#endif MODW3 = 0xE4, MODH3 = 0xE6, MODB3 = 0xE7, @@ -446,8 +503,22 @@ typedef enum { OP_COPR /* Coprocessor instruction */ } op_mode; -/* Describes a mnemonic */ -typedef struct _mnemonic { +/* Describes a CPU opcode. + * + * e.g.: + * + * {0x09, 3, OP_DESC, WD, "CASWI", 0, 1, -1, 2} + * + * - Opcode 0x09. + * - Followed by three operands. + * - Operands use descriptor bytes. + * - Default data type is word (32 bit). + * - Operand 0 is source 1. + * - Operand 1 is source 2. + * - Operand 2 is destination. + * + */ +typedef struct { uint16 opcode; int8 op_count; /* Number of operands */ op_mode mode; /* Dispatch mode */ @@ -462,7 +533,7 @@ typedef struct _mnemonic { /* * Structure that describes each operand in a decoded instruction */ -typedef struct _operand { +typedef struct { uint8 mode; /* Embedded data addressing mode */ uint8 reg; /* Operand register (0-15) */ int8 dtype; /* Default type for the operand */ @@ -491,6 +562,18 @@ typedef struct { operand operands[4]; } 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 */ t_stat sys_boot(int32 flag, CONST char *ptr); t_stat cpu_svc(UNIT *uptr); diff --git a/3B2/3b2_ctc.c b/3B2/3b2_ctc.c index ce41d4dc..8a91e746 100644 --- a/3B2/3b2_ctc.c +++ b/3B2/3b2_ctc.c @@ -56,9 +56,6 @@ #define VTOC_BLOCK 0 -#define ATOW(arr,i) ((uint32)arr[i+3] + ((uint32)arr[i+2] << 8) + \ - ((uint32)arr[i+1] << 16) + ((uint32)arr[i] << 24)) - /* Static function declarations */ 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); diff --git a/3B2/3b2_defs.h b/3B2/3b2_defs.h index b5d77c10..62158609 100644 --- a/3B2/3b2_defs.h +++ b/3B2/3b2_defs.h @@ -33,23 +33,34 @@ #include "sim_defs.h" #include "sim_tmxr.h" +#include "sim_disk.h" #include -#include "3b2_cpu.h" + +#if defined(REV3) +#include "3b2_rev3_defs.h" +#include "3b2_rev3_csr.h" +#include "3b2_rev3_mmu.h" +#include "3b2_rev2_mau.h" /* Use Rev 2 MAU until Rev 3 is implemented */ +#include "3b2_scsi.h" +#else #include "3b2_rev2_defs.h" +#include "3b2_rev2_csr.h" #include "3b2_rev2_mau.h" #include "3b2_rev2_mmu.h" -#include "3b2_rev2_stddev.h" -#include "3b2_rev2_sys.h" #include "3b2_id.h" +#include "3b2_ctc.h" +#endif +#include "3b2_sys.h" +#include "3b2_cpu.h" #include "3b2_io.h" +#include "3b2_stddev.h" +#include "3b2_mem.h" #include "3b2_dmac.h" #include "3b2_if.h" #include "3b2_iu.h" - #include "3b2_ports.h" -#include "3b2_ctc.h" #include "3b2_ni.h" #ifndef FALSE @@ -81,12 +92,39 @@ noret __libc_longjmp(jmp_buf buf, int val); #define UNUSED(x) ((void)((x))) #endif -#define UNIT_V_EXHALT (UNIT_V_UF + 0) -#define UNIT_EXHALT (1u << UNIT_V_EXHALT) +#define ATOW(arr, i) \ + ((uint32)(arr)[i + 3] + ((uint32)(arr)[i + 2] << 8) + \ + ((uint32)(arr)[i + 1] << 16) + ((uint32)(arr)[i] << 24)) + +#define ATOH(arr, i) ((uint32)(arr)[i + 1] + ((uint32)(arr)[i] << 8)) + +#define CSRBIT(bit, sc) \ + { \ + if (sc) { \ + csr_data |= (bit); \ + } else { \ + csr_data &= ~(bit); \ + } \ + } + +#define UNIT_V_EXBRK (UNIT_V_UF + 0) +#define UNIT_V_OPBRK (UNIT_V_UF + 1) +#define UNIT_EXBRK (1u << UNIT_V_EXBRK) +#define UNIT_OPBRK (1u << UNIT_V_OPBRK) + #define EX_V_FLAG 1 << 21 #define PHYS_MEM_BASE 0x2000000 +#define MSIZ_512K 0x80000 +#define MSIZ_1M 0x100000 +#define MSIZ_2M 0x200000 +#define MSIZ_4M 0x400000 +#define MSIZ_8M 0x800000 +#define MSIZ_16M 0x1000000 +#define MSIZ_32M 0x2000000 +#define MSIZ_64M 0x4000000 + /* Simulator stop codes */ #define STOP_RSRV 1 #define STOP_IBKPT 2 /* Breakpoint encountered */ @@ -115,25 +153,29 @@ noret __libc_longjmp(jmp_buf buf, int val); #define CACHE_DBG 0x1000 #define DECODE_DBG 0x2000 +#define TIMER_SANITY 0 +#define TIMER_INTERVAL 1 +#define TIMER_BUS 2 + /* Global symbols */ extern volatile int32 stop_reason; extern CIO_STATE cio[CIO_SLOTS]; +extern uint32 rom_size; extern instr *cpu_instr; +extern t_bool cpu_nmi; extern uint32 *ROM; extern uint32 *RAM; -extern uint32 R[16]; +extern uint32 R[NUM_REGISTERS]; extern REG cpu_reg[]; extern UNIT cpu_unit; extern uint8 fault; extern t_bool cpu_km; -extern uint32 R[16]; extern char sim_name[]; extern REG *sim_PC; extern int32 sim_emax; -extern uint16 csr_data; extern int32 tmxr_poll; extern DEBTAB sys_deb_tab[]; diff --git a/3B2/3b2_dmac.c b/3B2/3b2_dmac.c index 5bfb7755..b989d744 100644 --- a/3B2/3b2_dmac.c +++ b/3B2/3b2_dmac.c @@ -1,6 +1,6 @@ -/* 3b2_dmac.c: AT&T 3B2 Model 400 AM9517A DMA Controller Implementation +/* 3b2_dmac.c: AT&T 3B2 DMA Controller Implementation - Copyright (c) 2017, Seth J. Morabito + 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 @@ -54,7 +54,9 @@ DEVICE dmac_dev = { }; dmac_dma_handler device_dma_handlers[] = { +#if defined(REV2) {DMA_ID_CHAN, IDBASE+ID_DATA_REG, &id_drq, dmac_generic_dma, id_after_dma}, +#endif {DMA_IF_CHAN, IFBASE+IF_DATA_REG, &if_state.drq, dmac_generic_dma, if_after_dma}, {DMA_IUA_CHAN, IUBASE+IUA_DATA_REG, &iu_console.drq, iu_dma_console, NULL}, {DMA_IUB_CHAN, IUBASE+IUB_DATA_REG, &iu_contty.drq, iu_dma_contty, NULL}, @@ -62,10 +64,16 @@ dmac_dma_handler device_dma_handlers[] = { }; uint32 dma_address(uint8 channel, uint32 offset, t_bool r) { - uint32 addr; - addr = (PHYS_MEM_BASE + dma_state.channels[channel].addr + offset); - /* The top bit of the page address is a R/W bit, so we mask it here */ - addr |= (uint32) (((uint32)dma_state.channels[channel].page & 0x7f) << 16); + uint32 addr, page; + addr = (PHYS_MEM_BASE + (uint32)(dma_state.channels[channel].addr) + offset); +#if defined (REV3) + page = (uint32)dma_state.channels[channel].page; +#else + /* In Rev 2, the top bit of the page address is a R/W bit, so + we mask it here */ + page = (uint32)dma_state.channels[channel].page & 0x7f; +#endif + addr |= page << 16; return addr; } @@ -91,7 +99,7 @@ uint32 dmac_read(uint32 pa, size_t size) { uint8 reg, base, data; - base =(uint8) (pa >> 12); + base = (uint8) (pa >> 12); reg = pa & 0xff; switch (base) { @@ -202,6 +210,9 @@ void dmac_program(uint8 reg, uint8 val) case 7: chan_num = 3; break; + default: + chan_num = 0; + break; } channel = &dma_state.channels[chan_num]; @@ -315,32 +326,35 @@ void dmac_page_update(uint8 base, uint8 reg, uint8 val) return; } - /* The actual register is a 32-bit, byte-addressed register, so - that address 4x000 is the highest byte, 4x003 is the lowest - byte. */ - +#if defined(REV2) + /* In Rev2 systems, the actual register is a 32-bit, + byte-addressed register, so that address 4x000 is the highest + byte, 4x003 is the lowest byte. */ shift = -(reg - 3) * 8; +#endif switch (base) { +#if defined (REV2) case DMA_ID: sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 0 = %x\n", val); dma_state.channels[DMA_ID_CHAN].page &= ~(0xff << shift); - dma_state.channels[DMA_ID_CHAN].page |= (val << shift); + dma_state.channels[DMA_ID_CHAN].page |= ((uint16)val << shift); break; +#endif case DMA_IF: sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 1 = %x\n", val); dma_state.channels[DMA_IF_CHAN].page &= ~(0xff << shift); - dma_state.channels[DMA_IF_CHAN].page |= (val << shift); + dma_state.channels[DMA_IF_CHAN].page |= ((uint16)val << shift); break; case DMA_IUA: sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 2 = %x\n", val); dma_state.channels[DMA_IUA_CHAN].page &= ~(0xff << shift); - dma_state.channels[DMA_IUA_CHAN].page |= (val << shift); + dma_state.channels[DMA_IUA_CHAN].page |= ((uint16)val << shift); break; case DMA_IUB: sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 3 = %x\n", val); dma_state.channels[DMA_IUB_CHAN].page &= ~(0xff << shift); - dma_state.channels[DMA_IUB_CHAN].page |= (val << shift); + dma_state.channels[DMA_IUB_CHAN].page |= ((uint16)val << shift); break; } } @@ -353,14 +367,15 @@ void dmac_write(uint32 pa, uint32 val, size_t size) reg = pa & 0xff; switch (base) { - case DMA_C: /* 0x48xxx */ + case DMA_C: dmac_program(reg, (uint8) val); break; - - case DMA_ID: /* 0x45xxx */ - case DMA_IUA: /* 0x46xxx */ - case DMA_IUB: /* 0x47xxx */ - case DMA_IF: /* 0x4Exxx */ +#if defined (REV2) + case DMA_ID: +#endif + case DMA_IUA: + case DMA_IUB: + case DMA_IF: dmac_page_update(base, reg, (uint8) val); break; } @@ -386,10 +401,13 @@ void dmac_generic_dma(uint8 channel, uint32 service_address) break; case DMA_MODE_WRITE: sim_debug(EXECUTE_MSG, &dmac_dev, - "[%08x] [dmac_generic_dma channel=%d] write: %d bytes from %08x\n", + "[%08x] [dmac_generic_dma channel=%d] write: %d bytes to %08x from %08x (page=%04x addr=%08x)\n", R[NUM_PC], channel, chan->wcount + 1, - dma_address(channel, 0, TRUE)); + dma_address(channel, 0, TRUE), + service_address, + dma_state.channels[channel].page, + dma_state.channels[channel].addr); for (; i >= 0; i--) { chan->wcount_c--; addr = dma_address(channel, chan->ptr, TRUE); @@ -401,10 +419,11 @@ void dmac_generic_dma(uint8 channel, uint32 service_address) break; case DMA_MODE_READ: sim_debug(EXECUTE_MSG, &dmac_dev, - "[%08x] [dmac_generic_dma channel=%d] read: %d bytes to %08x\n", + "[%08x] [dmac_generic_dma channel=%d] read: %d bytes from %08x to %08x\n", R[NUM_PC], channel, chan->wcount + 1, - dma_address(channel, 0, TRUE)); + dma_address(channel, 0, TRUE), + service_address); for (; i >= 0; i--) { chan->wcount_c = i; addr = dma_address(channel, chan->ptr++, TRUE); @@ -425,7 +444,7 @@ void dmac_generic_dma(uint8 channel, uint32 service_address) */ void dmac_service_drqs() { - dmac_dma_handler *h; + volatile dmac_dma_handler *h; for (h = &device_dma_handlers[0]; h->drq != NULL; h++) { /* Only trigger if the channel has a DRQ set and its channel's diff --git a/3B2/3b2_dmac.h b/3B2/3b2_dmac.h index 016a6c56..f9d4fc6e 100644 --- a/3B2/3b2_dmac.h +++ b/3B2/3b2_dmac.h @@ -1,6 +1,6 @@ -/* 3b2_dmac.h: AT&T 3B2 Model 400 AM9517A DMA Controller Header +/* 3b2_dmac.h: AT&T 3B2 DMA Controller Header - Copyright (c) 2017, Seth J. Morabito + 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 @@ -40,7 +40,7 @@ #define DMA_IF_READ (IFBASE + IF_DATA_REG) typedef struct { - uint8 page; + uint16 page; uint16 addr; /* Original addr */ uint16 wcount; /* Original wcount */ uint16 addr_c; /* Current addr */ diff --git a/3B2/3b2_id.h b/3B2/3b2_id.h index 589c4ab5..b32f77c0 100644 --- a/3B2/3b2_id.h +++ b/3B2/3b2_id.h @@ -32,7 +32,6 @@ #define __3B2_ID_H__ #include "sim_defs.h" -#include "sim_disk.h" #define ID0 0 #define ID1 1 diff --git a/3B2/3b2_if.c b/3B2/3b2_if.c index 445b4d2a..bf2119c2 100644 --- a/3B2/3b2_if.c +++ b/3B2/3b2_if.c @@ -63,6 +63,7 @@ UNIT if_unit = { }; REG if_reg[] = { + { HRDATAD (IRQ, if_irq, 1, "IRQ Set") }, { NULL } }; @@ -86,13 +87,17 @@ t_bool if_irq = FALSE; static SIM_INLINE void if_set_irq() { if_irq = TRUE; +#if defined(REV2) csr_data |= CSRDISK; +#endif } static SIM_INLINE void if_clear_irq() { if_irq = FALSE; +#if defined(REV2) csr_data &= ~CSRDISK; +#endif } static SIM_INLINE void if_activate(uint32 delay) @@ -289,20 +294,24 @@ void if_handle_command() case IF_WRITE_SEC: case IF_WRITE_SEC_M: if_state.cmd_type = 2; +#if defined(REV2) if (((if_state.cmd & IF_U_FLAG) >> 1) != if_state.side) { head_switch_delay = IF_HSW_DELAY; if_state.side = (if_state.cmd & IF_U_FLAG) >> 1; } +#endif break; case IF_READ_ADDR: case IF_READ_TRACK: case IF_WRITE_TRACK: if_state.cmd_type = 3; +#if defined(REV2) if (((if_state.cmd & IF_U_FLAG) >> 1) != if_state.side) { head_switch_delay = IF_HSW_DELAY; if_state.side = (if_state.cmd & IF_U_FLAG) >> 1; } +#endif break; case IF_FORCE_INT: @@ -517,7 +526,9 @@ void if_handle_command() if ((if_state.cmd & 0xf) == 0) { if_cancel_pending_irq(); +#if defined(REV2) if_clear_irq(); /* TODO: Confirm this is right */ +#endif } else if ((if_state.cmd & 0x8) == 0x8) { if_state.status |= IF_DRQ; if_set_irq(); @@ -583,6 +594,18 @@ void if_write(uint32 pa, uint32 val, size_t size) } } +#if defined(REV3) +uint32 if_csr_read(uint32 pa, size_t size) +{ + return (uint32)(if_state.csr); +} + +void if_csr_write(uint32 pa, uint32 val, size_t size) +{ + if_state.csr = val & 0xff; +} +#endif + CONST char *if_description(DEVICE *dptr) { return "Integrated Floppy Disk"; diff --git a/3B2/3b2_if.h b/3B2/3b2_if.h index 161cb634..75955998 100644 --- a/3B2/3b2_if.h +++ b/3B2/3b2_if.h @@ -44,6 +44,9 @@ typedef struct { uint8 read_addr_ptr; int8 step_dir; t_bool drq; +#if defined(REV3) + uint8 csr; +#endif } IF_STATE; /* Status Bits */ @@ -114,6 +117,10 @@ t_stat if_attach(UNIT *uptr, CONST char *cptr); t_stat if_detach(UNIT *uptr); uint32 if_read(uint32 pa, size_t size); void if_write(uint32 pa, uint32 val, size_t size); +#if defined(REV3) +uint32 if_csr_read(uint32 pa, size_t size); +void if_csr_write(uint32 pa, uint32 val, size_t size); +#endif void if_handle_command(); void if_after_dma(); CONST char *if_description(DEVICE *dptr); diff --git a/3B2/3b2_io.c b/3B2/3b2_io.c index 62c3566a..46cec99d 100644 --- a/3B2/3b2_io.c +++ b/3B2/3b2_io.c @@ -33,15 +33,34 @@ CIO_STATE cio[CIO_SLOTS] = {{0}}; -struct iolink iotable[] = { +#if defined(REV3) +iolink iotable[] = { + { MMUBASE, MMUBASE+MMUSIZE, &mmu_read, &mmu_write }, + { IFBASE, IFBASE+IFSIZE, &if_read, &if_write }, + { IFCSRBASE, IFCSRBASE+IFCSRSIZE, &if_csr_read, &if_csr_write }, + { FLTLBASE, FLTLSIZE, &flt_read, &flt_write }, + { FLTHBASE, FLTHSIZE, &flt_read, &flt_write }, + { NVRBASE, NVRBASE+NVRSIZE, &nvram_read, &nvram_write }, + { TIMERBASE, TIMERBASE+TIMERSIZE, &timer_read, &timer_write }, + { CSRBASE, CSRBASE+CSRSIZE, &csr_read, &csr_write }, + { IUBASE, IUBASE+IUSIZE, &iu_read, &iu_write }, + { DMAIUABASE, DMAIUABASE+DMAIUASIZE, &dmac_read, &dmac_write }, + { DMAIUBBASE, DMAIUBBASE+DMAIUBSIZE, &dmac_read, &dmac_write }, + { DMACBASE, DMACBASE+DMACSIZE, &dmac_read, &dmac_write }, + { DMAIFBASE, DMAIFBASE+DMAIFSIZE, &dmac_read, &dmac_write }, + { TODBASE, TODBASE+TODSIZE, &tod_read, &tod_write }, + { 0, 0, NULL, NULL} +}; +#else +iolink iotable[] = { { MMUBASE, MMUBASE+MMUSIZE, &mmu_read, &mmu_write }, { IFBASE, IFBASE+IFSIZE, &if_read, &if_write }, { IDBASE, IDBASE+IDSIZE, &id_read, &id_write }, + { DMAIDBASE, DMAIDBASE+DMAIDSIZE, &dmac_read, &dmac_write }, + { NVRBASE, NVRBASE+NVRSIZE, &nvram_read, &nvram_write }, { TIMERBASE, TIMERBASE+TIMERSIZE, &timer_read, &timer_write }, - { NVRAMBASE, NVRAMBASE+NVRAMSIZE, &nvram_read, &nvram_write }, { CSRBASE, CSRBASE+CSRSIZE, &csr_read, &csr_write }, { IUBASE, IUBASE+IUSIZE, &iu_read, &iu_write }, - { DMAIDBASE, DMAIDBASE+DMAIDSIZE, &dmac_read, &dmac_write }, { DMAIUABASE, DMAIUABASE+DMAIUASIZE, &dmac_read, &dmac_write }, { DMAIUBBASE, DMAIUBBASE+DMAIUBSIZE, &dmac_read, &dmac_write }, { DMACBASE, DMACBASE+DMACSIZE, &dmac_read, &dmac_write }, @@ -49,6 +68,7 @@ struct iolink iotable[] = { { TODBASE, TODBASE+TODSIZE, &tod_read, &tod_write }, { 0, 0, NULL, NULL} }; +#endif void cio_clear(uint8 cid) { @@ -358,10 +378,70 @@ t_bool cio_rqueue_avail(uint8 cid, uint32 qnum, uint32 esize) uint32 io_read(uint32 pa, size_t size) { - struct iolink *p; + iolink *p; uint8 cid, reg, data; - /* Special devices */ +#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) { + CSRBIT(CSRTIMO, TRUE); + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + return 0; + } + + if (pa >= BUB_BOTTOM && pa < BUB_TOP) { + CSRBIT(CSRTIMO, TRUE); + + /* TODO: I don't remember why we do this! */ + if ((pa & 0xfff) == 3) { + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + } + + /* TODO: Implement BUB */ + return 1; + } +#else if (pa == MEMSIZE_REG) { /* The following values map to memory sizes: @@ -383,6 +463,7 @@ uint32 io_read(uint32 pa, size_t size) return 0; } } +#endif /* CIO board area */ if (pa >= CIO_BOTTOM && pa < CIO_TOP) { @@ -522,7 +603,7 @@ uint32 io_read(uint32 pa, size_t size) void io_write(uint32 pa, uint32 val, size_t size) { - struct iolink *p; + iolink *p; uint8 cid, reg; /* Feature Card Area */ diff --git a/3B2/3b2_io.h b/3B2/3b2_io.h index 49e5ae74..2ffdea77 100644 --- a/3B2/3b2_io.h +++ b/3B2/3b2_io.h @@ -190,12 +190,12 @@ typedef struct { uint32 address; } cio_entry; -struct iolink { +typedef struct { uint32 low; uint32 high; uint32 (*read)(uint32 pa, size_t size); void (*write)(uint32 pa, uint32 val, size_t size); -}; +} iolink; /* Example pump structure * ---------------------- diff --git a/3B2/3b2_mem.c b/3B2/3b2_mem.c new file mode 100644 index 00000000..4cd462c1 --- /dev/null +++ b/3B2/3b2_mem.c @@ -0,0 +1,320 @@ +/* 3b2_mem.c: AT&T 3B2 memory access routines + + 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. +*/ + +#include "3b2_defs.h" +#include "3b2_mem.h" + +t_bool addr_is_rom(uint32 pa) +{ + return (pa < rom_size); +} + +t_bool addr_is_mem(uint32 pa) +{ + return (pa >= PHYS_MEM_BASE && + pa < (PHYS_MEM_BASE + MEM_SIZE)); +} + +t_bool addr_is_io(uint32 pa) +{ +#if defined(REV3) + return ((pa >= IO_BOTTOM && pa < IO_TOP) || + (pa >= CIO_BOTTOM && pa < CIO_TOP) || + (pa >= VCACHE_BOTTOM && pa < VCACHE_TOP) || + (pa >= BUB_BOTTOM && pa < BUB_TOP)); +#else + return ((pa >= IO_BOTTOM && pa < IO_TOP) || + (pa >= CIO_BOTTOM && pa < CIO_TOP)); +#endif +} + +/* Read Word (Physical Address) */ +uint32 pread_w(uint32 pa) +{ + uint32 *m; + uint32 index; + + if (pa & 3) { + sim_debug(READ_MSG, &mmu_dev, + "[%08x] Cannot read physical address. ALIGNMENT ISSUE: %08x\n", + R[NUM_PC], pa); + CSRBIT(CSRALGN, TRUE); + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + } + + if (addr_is_io(pa)) { + return io_read(pa, 32); + } + + if (addr_is_rom(pa)) { + m = ROM; + index = pa >> 2; + } else if (addr_is_mem(pa)) { + m = RAM; + index = (pa - PHYS_MEM_BASE) >> 2; + } else { + return 0; + } + + return m[index]; +} + +/* + * Write Word (Physical Address) + */ +void pwrite_w(uint32 pa, uint32 val) +{ + if (pa & 3) { + sim_debug(WRITE_MSG, &mmu_dev, + "[%08x] Cannot write physical address. ALIGNMENT ISSUE: %08x\n", + R[NUM_PC], pa); + CSRBIT(CSRALGN, TRUE); + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + } + + if (addr_is_io(pa)) { + io_write(pa, val, 32); + return; + } + + if (addr_is_mem(pa)) { + RAM[(pa - PHYS_MEM_BASE) >> 2] = val; + return; + } +} + +/* + * Read Halfword (Physical Address) + */ +uint16 pread_h(uint32 pa) +{ + uint32 *m; + uint32 index; + + if (pa & 1) { + sim_debug(READ_MSG, &mmu_dev, + "[%08x] Cannot read physical address. ALIGNMENT ISSUE %08x\n", + R[NUM_PC], pa); + CSRBIT(CSRALGN, TRUE); + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + } + + if (addr_is_io(pa)) { + return (uint16) io_read(pa, 16); + } + + if (addr_is_rom(pa)) { + m = ROM; + index = pa >> 2; + } else if (addr_is_mem(pa)) { + m = RAM; + index = (pa - PHYS_MEM_BASE) >> 2; + } else { + return 0; + } + + if (pa & 2) { + return m[index] & HALF_MASK; + } else { + return (m[index] >> 16) & HALF_MASK; + } +} + +/* + * Write Halfword (Physical Address) + */ +void pwrite_h(uint32 pa, uint16 val) +{ + uint32 *m; + uint32 index; + uint32 wval = (uint32)val; + + if (pa & 1) { + sim_debug(WRITE_MSG, &mmu_dev, + "[%08x] Cannot write physical address %08x, ALIGNMENT ISSUE\n", + R[NUM_PC], pa); + CSRBIT(CSRALGN, TRUE); + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + } + + if (addr_is_io(pa)) { + io_write(pa, val, 16); + return; + } + + if (addr_is_mem(pa)) { + m = RAM; + index = (pa - PHYS_MEM_BASE) >> 2; + } else { + return; + } + + if (pa & 2) { + m[index] = (m[index] & ~HALF_MASK) | wval; + } else { + m[index] = (m[index] & HALF_MASK) | (wval << 16); + } +} + +/* + * Read Byte (Physical Address) + */ +uint8 pread_b(uint32 pa) +{ + uint32 data; + int32 sc = (~(pa & 3) << 3) & 0x1f; + + if (addr_is_io(pa)) { + return (uint8)(io_read(pa, 8)); + } + + if (addr_is_rom(pa)) { + data = ROM[pa >> 2]; + } else if (addr_is_mem(pa)) { + data = RAM[(pa - PHYS_MEM_BASE) >> 2]; + } else { + return 0; + } + + return (data >> sc) & BYTE_MASK; +} + +/* Write Byte (Physical Address) */ +void pwrite_b(uint32 pa, uint8 val) +{ + uint32 *m; + int32 index; + int32 sc = (~(pa & 3) << 3) & 0x1f; + uint32 mask = 0xffu << sc; + + if (addr_is_io(pa)) { + io_write(pa, val, 8); + return; + } + + if (addr_is_mem(pa)) { + m = RAM; + index = (pa - PHYS_MEM_BASE) >> 2; + m[index] = (m[index] & ~mask) | (uint32) (val << sc); + return; + } +} + +/* Read Byte (Virtual Address) */ +uint8 read_b(uint32 va, uint8 r_acc) +{ + return pread_b(mmu_xlate_addr(va, r_acc)); +} + +/* Write Byte (Virtual Address) */ +void write_b(uint32 va, uint8 val) +{ + pwrite_b(mmu_xlate_addr(va, ACC_W), val); +} + +/* Read Halfword (Virtual Address) */ +uint16 read_h(uint32 va, uint8 r_acc) +{ + return pread_h(mmu_xlate_addr(va, r_acc)); +} + +/* Write Halfword (Virtual Address) */ +void write_h(uint32 va, uint16 val) +{ + pwrite_h(mmu_xlate_addr(va, ACC_W), val); +} + +/* Read Word (Virtual Address) */ +uint32 read_w(uint32 va, uint8 r_acc) +{ + return pread_w(mmu_xlate_addr(va, r_acc)); +} + +/* Write Word (Virtual Address) */ +void write_w(uint32 va, uint32 val) +{ + pwrite_w(mmu_xlate_addr(va, ACC_W), val); +} + +t_stat read_operand(uint32 va, uint8 *val) +{ + uint32 pa; + t_stat succ; + + succ = mmu_decode_va(va, ACC_IF, TRUE, &pa); + + if (succ == SCPE_OK) { + *val = pread_b(pa); + } else { + *val = 0; + } + + return succ; +} + +t_stat examine(uint32 va, uint8 *val) +{ + uint32 pa; + t_stat succ; + + succ = mmu_decode_va(va, 0, FALSE, &pa); + + if (succ == SCPE_OK) { + if (addr_is_rom(pa) || addr_is_mem(pa)) { + *val = pread_b(pa); + return SCPE_OK; + } else { + *val = 0; + return SCPE_NXM; + } + } else { + *val = 0; + return succ; + } +} + +t_stat deposit(uint32 va, uint8 val) +{ + uint32 pa; + t_stat succ; + + succ = mmu_decode_va(va, 0, FALSE, &pa); + + if (succ == SCPE_OK) { + if (addr_is_mem(pa)) { + pwrite_b(pa, val); + return SCPE_OK; + } else { + return SCPE_NXM; + } + } else { + return succ; + } +} diff --git a/3B2/3b2_mem.h b/3B2/3b2_mem.h new file mode 100644 index 00000000..4487312e --- /dev/null +++ b/3B2/3b2_mem.h @@ -0,0 +1,62 @@ +/* 3b2_mem.h: AT&T 3B2 3B2 memory access routines + + 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_MEM_H_ +#define _3B2_MEM_H_ + +#include "sim_defs.h" + +uint32 pread_w(uint32 pa); +void pwrite_w(uint32 pa, uint32 val); +uint8 pread_b(uint32 pa); +void pwrite_b(uint32 pa, uint8 val); +uint16 pread_h(uint32 pa); +void pwrite_h(uint32 pa, uint16 val); + +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_stat read_operand(uint32 va, uint8 *val); +t_stat examine(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_ */ diff --git a/3B2/3b2_rev2_csr.c b/3B2/3b2_rev2_csr.c new file mode 100644 index 00000000..29761118 --- /dev/null +++ b/3B2/3b2_rev2_csr.c @@ -0,0 +1,179 @@ +/* 3b2_rev2_csr.c: AT&T 3B2 Rev 2 Control and Status Register + + 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. +*/ + +#include "3b2_defs.h" +#include "3b2_rev2_csr.h" + +uint16 csr_data; + +BITFIELD csr_bits[] = { + BIT(IOF), + BIT(DMA), + BIT(DISK), + BIT(UART), + BIT(PIR9), + BIT(PIR8), + BIT(CLK), + BIT(IFLT), + BIT(ITIM), + BIT(FLOP), + BIT(NA), + BIT(LED), + BIT(ALGN), + BIT(RRST), + BIT(PARE), + BIT(TIMO), + ENDBITS +}; + +UNIT csr_unit = { + UDATA(NULL, UNIT_FIX, CSRSIZE) +}; + +REG csr_reg[] = { + { HRDATADF(DATA, csr_data, 16, "CSR Data", csr_bits) }, + { NULL } +}; + +DEVICE csr_dev = { + "CSR", &csr_unit, csr_reg, NULL, + 1, 16, 8, 4, 16, 32, + &csr_ex, &csr_dep, &csr_reset, + NULL, NULL, NULL, NULL, + DEV_DEBUG, 0, sys_deb_tab +}; + +t_stat csr_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ + return SCPE_OK; +} + +t_stat csr_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ + return SCPE_OK; +} + +t_stat csr_reset(DEVICE *dptr) +{ + csr_data = 0; + return SCPE_OK; +} + +uint32 csr_read(uint32 pa, size_t size) +{ + uint32 reg = pa - CSRBASE; + + sim_debug(READ_MSG, &csr_dev, + "[%08x] CSR=%04x\n", + R[NUM_PC], csr_data); + + switch (reg) { + case 0x2: + if (size == 8) { + return (csr_data >> 8) & 0xff; + } else { + return csr_data; + } + case 0x3: + return csr_data & 0xff; + default: + return 0; + } +} + +void csr_write(uint32 pa, uint32 val, size_t size) +{ + uint32 reg = pa - CSRBASE; + + switch (reg) { + case 0x03: /* Clear Bus Timeout Error */ + csr_data &= ~CSRTIMO; + break; + case 0x07: /* Clear Memory Parity Error */ + csr_data &= ~CSRPARE; + break; + case 0x0b: /* Set System Reset Request */ + full_reset(); + cpu_boot(0, &cpu_dev); + break; + case 0x0f: /* Clear Memory Alignment Fault */ + csr_data &= ~CSRALGN; + break; + case 0x13: /* Set Failure LED */ + csr_data |= CSRLED; + break; + case 0x17: /* Clear Failure LED */ + csr_data &= ~CSRLED; + break; + case 0x1b: /* Set Floppy Motor On */ + csr_data |= CSRFLOP; + break; + case 0x1f: /* Clear Floppy Motor On */ + csr_data &= ~CSRFLOP; + break; + case 0x23: /* Set Inhibit Timers */ + sim_debug(WRITE_MSG, &csr_dev, + "[%08x] SET INHIBIT TIMERS\n", R[NUM_PC]); + csr_data |= CSRITIM; + break; + case 0x27: /* Clear Inhibit Timers */ + sim_debug(WRITE_MSG, &csr_dev, + "[%08x] CLEAR INHIBIT TIMERS\n", R[NUM_PC]); + + /* 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; + break; + case 0x2b: /* Set Inhibit Faults */ + csr_data |= CSRIFLT; + break; + case 0x2f: /* Clear Inhibit Faults */ + csr_data &= ~CSRIFLT; + break; + case 0x33: /* Set PIR9 */ + csr_data |= CSRPIR9; + break; + case 0x37: /* Clear PIR9 */ + csr_data &= ~CSRPIR9; + break; + case 0x3b: /* Set PIR8 */ + csr_data |= CSRPIR8; + break; + case 0x3f: /* Clear PIR8 */ + csr_data &= ~CSRPIR8; + break; + default: + break; + } +} diff --git a/3B2/3b2_rev2_csr.h b/3B2/3b2_rev2_csr.h new file mode 100644 index 00000000..a76c3c08 --- /dev/null +++ b/3B2/3b2_rev2_csr.h @@ -0,0 +1,44 @@ +/* 3b2_rev2_csr.h: AT&T 3B2 Rev 2 Control and Status Register + + 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_REV2_CSR_H_ +#define _3B2_REV2_CSR_H_ + +#include "sim_defs.h" + +/* CSR */ +t_stat csr_svc(UNIT *uptr); +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_reset(DEVICE *dptr); +uint32 csr_read(uint32 pa, size_t size); +void csr_write(uint32 pa, uint32 val, size_t size); + +#endif /* 3B2_REV2_CSR_H_ */ diff --git a/3B2/3b2_rev2_defs.h b/3B2/3b2_rev2_defs.h index 19ab3192..879f8d3b 100644 --- a/3B2/3b2_rev2_defs.h +++ b/3B2/3b2_rev2_defs.h @@ -33,14 +33,20 @@ #include "sim_defs.h" -#define MAXMEMSIZE (1 << 22) /* 4 MB */ +#define NUM_REGISTERS 16 + +#define DEFMEMSIZE MSIZ_4M +#define MAXMEMSIZE MSIZ_4M + +#define HWORD_OP_COUNT 11 +#define CPU_VERSION 0x1A /* Version encoded in WE32100 */ #define TODBASE 0x41000 #define TODSIZE 0x40 #define TIMERBASE 0x42000 #define TIMERSIZE 0x20 -#define NVRAMBASE 0x43000 -#define NVRAMSIZE 0x1000 +#define NVRBASE 0x43000 +#define NVRSIZE 0x1000 #define CSRBASE 0x44000 #define CSRSIZE 0x100 #define IFBASE 0x4d000 @@ -75,7 +81,12 @@ #define CSRDMA 0x0002 /* DMA Interrupt */ #define CSRIOF 0x0001 /* I/O Board Fail */ +/* Memory */ #define MEMSIZE_REG 0x4C003 +#define MEMID_512K 0 +#define MEMID_1M 2 +#define MEMID_2M 1 +#define MEMID_4M 3 /* DMA Controller */ #define DMACBASE 0x48000 @@ -108,4 +119,6 @@ #define DMA_C 0x48 #define DMA_IF 0x4E +extern uint16 csr_data; + #endif /* _3B2_REV2_DEFS_H_ */ diff --git a/3B2/3b2_rev2_mau.c b/3B2/3b2_rev2_mau.c index cabae557..0b7844fe 100644 --- a/3B2/3b2_rev2_mau.c +++ b/3B2/3b2_rev2_mau.c @@ -242,10 +242,10 @@ REG mau_reg[] = { }; MTAB mau_mod[] = { - { UNIT_EXHALT, UNIT_EXHALT, "Halt on Exception", "EXHALT", - NULL, NULL, NULL, "Enables Halt on floating point exceptions" }, - { UNIT_EXHALT, 0, "No halt on Exception", "NOEXHALT", - NULL, NULL, NULL, "Disables Halt on floating point exceptions" }, + { UNIT_EXBRK, UNIT_EXBRK, "Break on exceptions", "EXBRK", + NULL, NULL, NULL, "Enables break on floating point exceptions" }, + { UNIT_EXBRK, 0, "No break on exceptions", "NOEXBRK", + NULL, NULL, NULL, "Disables break on floating point exceptions" }, { 0 } }; @@ -416,7 +416,7 @@ static SIM_INLINE void abort_on_fault() * in the CPU's PSW). */ if ((mau_state.asr & MAU_ASR_IO) && (R[NUM_PSW] & PSW_OE_MASK)) { - if (mau_unit.flags & UNIT_EXHALT) { + if (mau_unit.flags & UNIT_EXBRK) { stop_reason = STOP_EX; } sim_debug(TRACE_DBG, &mau_dev, @@ -427,7 +427,7 @@ static SIM_INLINE void abort_on_fault() /* Otherwise, check for other exceptions. */ if (mau_exception_present()) { - if (mau_unit.flags & UNIT_EXHALT) { + if (mau_unit.flags & UNIT_EXBRK) { stop_reason = STOP_EX; } sim_debug(TRACE_DBG, &mau_dev, diff --git a/3B2/3b2_rev2_mmu.c b/3B2/3b2_rev2_mmu.c index 42ebf4ee..ca068ffc 100644 --- a/3B2/3b2_rev2_mmu.c +++ b/3B2/3b2_rev2_mmu.c @@ -494,207 +494,6 @@ void mmu_write(uint32 pa, uint32 val, size_t size) } } -t_bool addr_is_rom(uint32 pa) -{ - return (pa < BOOT_CODE_SIZE); -} - -t_bool addr_is_mem(uint32 pa) -{ - return (pa >= PHYS_MEM_BASE && - pa < (PHYS_MEM_BASE + MEM_SIZE)); -} - -t_bool addr_is_io(uint32 pa) -{ - return ((pa >= IO_BOTTOM && pa < IO_TOP) || - (pa >= CIO_BOTTOM && pa < CIO_TOP)); -} - -/* - * Raw physical reads and writes. - * - * The WE32100 is a BIG-endian machine, meaning that words are - * arranged in increasing address from most-significant byte to - * least-significant byte. - */ - -/* - * Read Word (Physical Address) - */ -uint32 pread_w(uint32 pa) -{ - uint32 *m; - uint32 index; - - if (pa & 3) { - sim_debug(READ_MSG, &mmu_dev, - "[%08x] Cannot read physical address. ALIGNMENT ISSUE: %08x\n", - R[NUM_PC], pa); - csr_data |= CSRALGN; - cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); - } - - if (addr_is_io(pa)) { - return io_read(pa, 32); - } - - if (addr_is_rom(pa)) { - m = ROM; - index = pa >> 2; - } else if (addr_is_mem(pa)) { - m = RAM; - index = (pa - PHYS_MEM_BASE) >> 2; - } else { - return 0; - } - - return m[index]; -} - -/* - * Write Word (Physical Address) - */ -void pwrite_w(uint32 pa, uint32 val) -{ - if (pa & 3) { - sim_debug(WRITE_MSG, &mmu_dev, - "[%08x] Cannot write physical address. ALIGNMENT ISSUE: %08x\n", - R[NUM_PC], pa); - csr_data |= CSRALGN; - cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); - } - - if (addr_is_io(pa)) { - io_write(pa, val, 32); - return; - } - - if (addr_is_mem(pa)) { - RAM[(pa - PHYS_MEM_BASE) >> 2] = val; - return; - } -} - -/* - * Read Halfword (Physical Address) - */ -uint16 pread_h(uint32 pa) -{ - uint32 *m; - uint32 index; - - if (pa & 1) { - sim_debug(READ_MSG, &mmu_dev, - "[%08x] Cannot read physical address. ALIGNMENT ISSUE %08x\n", - R[NUM_PC], pa); - csr_data |= CSRALGN; - cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); - } - - if (addr_is_io(pa)) { - return (uint16) io_read(pa, 16); - } - - if (addr_is_rom(pa)) { - m = ROM; - index = pa >> 2; - } else if (addr_is_mem(pa)) { - m = RAM; - index = (pa - PHYS_MEM_BASE) >> 2; - } else { - return 0; - } - - if (pa & 2) { - return m[index] & HALF_MASK; - } else { - return (m[index] >> 16) & HALF_MASK; - } -} - -/* - * Write Halfword (Physical Address) - */ -void pwrite_h(uint32 pa, uint16 val) -{ - uint32 *m; - uint32 index; - uint32 wval = (uint32)val; - - if (pa & 1) { - sim_debug(WRITE_MSG, &mmu_dev, - "[%08x] Cannot write physical address %08x, ALIGNMENT ISSUE\n", - R[NUM_PC], pa); - csr_data |= CSRALGN; - cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); - } - - if (addr_is_io(pa)) { - io_write(pa, val, 16); - return; - } - - if (addr_is_mem(pa)) { - m = RAM; - index = (pa - PHYS_MEM_BASE) >> 2; - } else { - return; - } - - if (pa & 2) { - m[index] = (m[index] & ~HALF_MASK) | wval; - } else { - m[index] = (m[index] & HALF_MASK) | (wval << 16); - } -} - -/* - * Read Byte (Physical Address) - */ -uint8 pread_b(uint32 pa) -{ - uint32 data; - int32 sc = (~(pa & 3) << 3) & 0x1f; - - if (addr_is_io(pa)) { - return (uint8)(io_read(pa, 8)); - } - - if (addr_is_rom(pa)) { - data = ROM[pa >> 2]; - } else if (addr_is_mem(pa)) { - data = RAM[(pa - PHYS_MEM_BASE) >> 2]; - } else { - return 0; - } - - return (data >> sc) & BYTE_MASK; -} - -/* - * Write Byte (Physical Address) - */ -void pwrite_b(uint32 pa, uint8 val) -{ - uint32 *m; - int32 index; - int32 sc = (~(pa & 3) << 3) & 0x1f; - uint32 mask = 0xffu << sc; - - if (addr_is_io(pa)) { - io_write(pa, val, 8); - return; - } - - if (addr_is_mem(pa)) { - m = RAM; - index = (pa - PHYS_MEM_BASE) >> 2; - m[index] = (m[index] & ~mask) | (uint32) (val << sc); - return; - } -} - /* Helper functions for MMU decode. */ /* @@ -987,59 +786,6 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa) } } -t_stat examine(uint32 va, uint8 *val) { - uint32 pa; - t_stat succ; - - succ = mmu_decode_va(va, 0, FALSE, &pa); - - if (succ == SCPE_OK) { - if (addr_is_rom(pa) || addr_is_mem(pa)) { - *val = pread_b(pa); - return SCPE_OK; - } else { - *val = 0; - return SCPE_NXM; - } - } else { - *val = 0; - return succ; - } -} - -t_stat deposit(uint32 va, uint8 val) { - uint32 pa; - t_stat succ; - - succ = mmu_decode_va(va, 0, FALSE, &pa); - - if (succ == SCPE_OK) { - if (addr_is_mem(pa)) { - pwrite_b(pa, val); - return SCPE_OK; - } else { - return SCPE_NXM; - } - } else { - return succ; - } -} - -t_stat read_operand(uint32 va, uint8 *val) { - uint32 pa; - t_stat succ; - - succ = mmu_decode_va(va, ACC_OF, TRUE, &pa); - - if (succ == SCPE_OK) { - *val = pread_b(pa); - } else { - *val = 0; - } - - return succ; -} - uint32 mmu_xlate_addr(uint32 va, uint8 r_acc) { uint32 pa; @@ -1072,40 +818,6 @@ void mmu_disable() mmu_state.enabled = FALSE; } -/* - * MMU Virtual Read and Write Functions - */ - -uint8 read_b(uint32 va, uint8 r_acc) -{ - return pread_b(mmu_xlate_addr(va, r_acc)); -} - -uint16 read_h(uint32 va, uint8 r_acc) -{ - return pread_h(mmu_xlate_addr(va, r_acc)); -} - -uint32 read_w(uint32 va, uint8 r_acc) -{ - return pread_w(mmu_xlate_addr(va, r_acc)); -} - -void write_b(uint32 va, uint8 val) -{ - pwrite_b(mmu_xlate_addr(va, ACC_W), val); -} - -void write_h(uint32 va, uint16 val) -{ - pwrite_h(mmu_xlate_addr(va, ACC_W), val); -} - -void write_w(uint32 va, uint32 val) -{ - pwrite_w(mmu_xlate_addr(va, ACC_W), val); -} - CONST char *mmu_description(DEVICE *dptr) { return "WE32101"; diff --git a/3B2/3b2_rev2_mmu.h b/3B2/3b2_rev2_mmu.h index 22fcb3bf..82237a0b 100644 --- a/3B2/3b2_rev2_mmu.h +++ b/3B2/3b2_rev2_mmu.h @@ -214,12 +214,6 @@ #define ACC_IFAD 12 /* Instruction fetch after discontinuity */ #define ACC_IF 13 /* Instruction fetch */ -/* Memory access levels */ -#define L_KERNEL 0 -#define L_EXEC 1 -#define L_SUPER 2 -#define L_USER 3 - /* Pluck out Virtual Address fields */ #define SID(va) (((va) >> 30) & 3) #define SSL(va) (((va) >> 17) & 0x1fff) @@ -371,12 +365,6 @@ t_stat mmu_decode_vaddr(uint32 vaddr, uint8 r_acc, #define SHOULD_UPDATE_PD_M_BIT(pd) \ (r_acc == ACC_W && !((pd) & PD_M_MASK)) -/* Special functions for reading operands and examining memory - safely */ -t_stat read_operand(uint32 va, uint8 *val); -t_stat examine(uint32 va, uint8 *val); -t_stat deposit(uint32 va, uint8 val); - /* Dispatch to the MMU when enabled, or to physical RW when disabled */ uint8 read_b(uint32 va, uint8 r_acc); diff --git a/3B2/3b2_rev2_sys.c b/3B2/3b2_rev2_sys.c index bc0dc114..f2234669 100644 --- a/3B2/3b2_rev2_sys.c +++ b/3B2/3b2_rev2_sys.c @@ -1,4 +1,4 @@ -/* 3b2_rev2_defs.h: AT&T 3B2 Rev 2 (Model 400) system implementation +/* 3b2_rev2_sys.c: AT&T 3B2 Rev 2 (Model 400) system definition Copyright (c) 2017, Seth J. Morabito @@ -29,15 +29,8 @@ */ #include "3b2_defs.h" -#include "3b2_rev2_sys.h" -char sim_name[] = "AT&T 3B2 Model 400"; - -REG *sim_PC = &cpu_reg[0]; - -/* All opcodes are 1 or 2 bytes. Operands may be up to 6 bytes, and - there may be up to 3 operands, for a maximum of 20 bytes */ -int32 sim_emax = 20; +char sim_name[] = "AT&T 3B2/400"; DEVICE *sim_devices[] = { &cpu_dev, @@ -60,20 +53,6 @@ DEVICE *sim_devices[] = { NULL }; -const char *sim_stop_messages[SCPE_BASE] = { - "Unknown error", - "Reserved Instruction", - "Breakpoint", - "Invalid Opcode", - "IRQ", - "Exception/Trap", - "Exception Stack Too Deep", - "Unimplemented MMU Feature", - "System Powered Off", - "Infinite Loop", - "Simulator Error" -}; - void full_reset() { cpu_reset(&cpu_dev); @@ -89,109 +68,3 @@ void full_reset() ctc_reset(&ctc_dev); ni_reset(&ni_dev); } - -t_stat sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) -{ - int32 i; - uint32 addr = 0; - int32 cnt = 0; - - if ((*cptr != 0) || (flag != 0)) { - return SCPE_ARG; - } - - addr = R[NUM_PC]; - - while ((i = getc (fileref)) != EOF) { - pwrite_b(addr, (uint8)i); - addr++; - cnt++; - } - - printf ("%d Bytes loaded.\n", cnt); - - return SCPE_OK; -} - -t_stat parse_sym(CONST char *cptr, t_addr exta, UNIT *uptr, t_value *val, int32 sw) -{ - DEVICE *dptr; - t_stat r; - int32 k, num, vp; - int32 len = 4; - - if (sw & (int32) SWMASK ('B')) { - len = 1; - } else if (sw & (int32) SWMASK ('H')) { - len = 2; - } else if (sw & (int32) SWMASK ('W')) { - len = 4; - } - - // Parse cptr - num = (int32) get_uint(cptr, 16, WORD_MASK, &r); - - if (r != SCPE_OK) { - return r; - } - - if (uptr == NULL) { - uptr = &cpu_unit; - } - - dptr = find_dev_from_unit(uptr); - - if (dptr == NULL) { - return SCPE_IERR; - } - - vp = 0; - for (k = len - 1; k >= 0; k--) { - val[vp++] = (num >> (k * 8)) & 0xff; - } - - return -(vp - 1); -} - -t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) -{ - uint32 len = 4; - int32 k, vp, num; - unsigned int c; - - num = 0; - vp = 0; - - if (sw & (int32) SWMASK('M')) { - return fprint_sym_m(of, addr, val); - } - - if (sw & (int32) SWMASK ('B')) { - len = 1; - } else if (sw & (int32) SWMASK ('H')) { - len = 2; - } else if (sw & (int32) SWMASK ('W')) { - len = 4; - } - - if (sw & (int32) SWMASK('C')) { - len = 16; - for (k = (int32) len - 1; k >= 0; k--) { - c = (unsigned int)val[vp++]; - if (c >= 0x20 && c < 0x7f) { - fprintf(of, "%c", c); - } else { - fprintf(of, "."); - } - } - return -(vp - 1); - } - - for (k = len - 1; k >= 0; k--) { - num = num | (((int32) val[vp++]) << (k * 8)); - } - - fprint_val(of, (uint32) num, 16, len * 8, PV_RZRO); - - return -(vp - 1); -} diff --git a/3B2/3b2_rev3_csr.c b/3B2/3b2_rev3_csr.c new file mode 100644 index 00000000..62461720 --- /dev/null +++ b/3B2/3b2_rev3_csr.c @@ -0,0 +1,265 @@ +/* 3b2_rev3_csr.c: AT&T 3B2/600G Control and Status Register + + Copyright (c) 2020, 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. +*/ + +#include "3b2_defs.h" + +uint32 csr_data; + +BITFIELD csr_bits[] = { + BIT(UTIM), + BIT(PWDN), + BIT(OI15), + BIT(IUINT), + BIT(IUDMA), + BIT(PIR9), + BIT(PIR8), + BIT(IUTIM), + + BIT(ISTY), + BIT(IUBUS), + BIT(IFLT), + BIT(ISBER), + BIT(IBUS), + BIT(IBUB), + BIT(FECC), + BIT(THERM), + + BIT(FLED), + BIT(PSPWR), + BIT(FLSPD), + BIT(FLSD1), + BIT(FLMOT), + BIT(FLDEN), + BIT(FLSZ), + BIT(SBER), + + BIT(MBER), + BIT(UBFL), + BIT(TIMO), + BIT(FLTFR), + BIT(DALGN), + BIT(STTIM), + BIT(ABRT), + BIT(RSTR), + + ENDBITS +}; + +UNIT csr_unit = { + UDATA(NULL, UNIT_FIX, CSRSIZE) +}; + +REG csr_reg[] = { + { HRDATADF(DATA, csr_data, 32, "CSR Data", csr_bits) }, + { NULL } +}; + +DEVICE csr_dev = { + "CSR", &csr_unit, csr_reg, NULL, + 1, 16, 8, 4, 16, 32, + &csr_ex, &csr_dep, &csr_reset, + NULL, NULL, NULL, NULL, + DEV_DEBUG, 0, sys_deb_tab +}; + +t_stat csr_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ + return SCPE_OK; +} + +t_stat csr_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ + return SCPE_OK; +} + +t_stat csr_reset(DEVICE *dptr) +{ + /* Accordig to the technical reference manual, the CSR is NOT + cleared on reset */ + return SCPE_OK; +} + +uint32 csr_read(uint32 pa, size_t size) +{ + uint32 reg = (pa - CSRBASE) & 0xff; + + switch (reg & 0xf0) { + case 0x00: + return csr_data & 0xff; + case 0x20: + return (csr_data >> 8) & 0xff; + case 0x40: + return (csr_data >> 16) & 0xff; + case 0x60: + return (csr_data >> 24) & 0xff; + default: + sim_debug(WRITE_MSG, &csr_dev, + "[%08x] CSR READ. Warning, unexpected register = %02x)\n", + R[NUM_PC], reg); + return 0; + } +} + +void csr_write(uint32 pa, uint32 val, size_t size) +{ + uint32 reg = pa - CSRBASE; + + switch (reg) { + + case 0x00: + CSRBIT(CSRCLK, val); + break; + case 0x04: + CSRBIT(CSRPWRDN, val); + break; + case 0x08: + CSRBIT(CSROPINT15, val); + break; + case 0x0c: + CSRBIT(CSRUART, val); + break; + case 0x10: + CSRBIT(CSRDMA, val); + break; + case 0x14: + CSRBIT(CSRPIR9, val); + break; + case 0x18: + CSRBIT(CSRPIR8, val); + break; + case 0x1c: + CSRBIT(CSRITIM, val); + sim_debug(WRITE_MSG, &csr_dev, + "[%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; + case 0x20: + CSRBIT(CSRISTIM, val); + sim_debug(WRITE_MSG, &csr_dev, + "[%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; + case 0x24: + CSRBIT(CSRITIMO, val); + sim_debug(WRITE_MSG, &csr_dev, + "[%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; + case 0x28: + CSRBIT(CSRICPUFLT, val); + break; + case 0x2c: + CSRBIT(CSRISBERR, val); + break; + case 0x30: + CSRBIT(CSRIIOBUS, val); + break; + case 0x34: + CSRBIT(CSRIBUB, val); + break; + case 0x38: + CSRBIT(CSRFECC, val); + break; + case 0x3c: + CSRBIT(CSRTHERM, val); + cpu_nmi = val ? TRUE : FALSE; /* Immediate NMI */ + break; + case 0x40: + CSRBIT(CSRLED, val); + break; + case 0x44: + CSRBIT(CSRPWRSPDN, val); + break; + case 0x48: + CSRBIT(CSRFLPFST, val); + break; + case 0x4c: /* Floppy Side 1: Set when Cleared */ + if_state.side = (val & 1) ? 0 : 1; + CSRBIT(CSRFLPS1, val & 1); + break; + case 0x50: + CSRBIT(CSRFLPMO, val); + break; + case 0x54: + CSRBIT(CSRFLPDEN, val); + break; + case 0x58: + CSRBIT(CSRFLPSZ, val); + break; + case 0x5c: + CSRBIT(CSRSBERR, val); + break; + case 0x60: + CSRBIT(CSRMBERR, val); + break; + case 0x64: + CSRBIT(CSRUBUBF, val); + break; + case 0x68: + CSRBIT(CSRTIMO, val); + break; + case 0x6c: + CSRBIT(CSRFRF, val); + break; + case 0x70: + CSRBIT(CSRALGN, val); + break; + case 0x74: + CSRBIT(CSRSTIMO, val); + cpu_nmi = val ? TRUE : FALSE; /* Immediate NMI */ + break; + case 0x78: + CSRBIT(CSRABRT, val); + cpu_nmi = val ? TRUE : FALSE; /* Immediate NMI */ + break; + case 0x7c: + /* System reset request */ + cpu_boot(0, &cpu_dev); + break; + default: + /* Do nothing */ + break; + } +} diff --git a/3B2/3b2_rev3_csr.h b/3B2/3b2_rev3_csr.h new file mode 100644 index 00000000..d768a669 --- /dev/null +++ b/3B2/3b2_rev3_csr.h @@ -0,0 +1,43 @@ +/* 3b2_rev3_csr.h: AT&T 3B2/600G Control and Status Register + + Copyright (c) 2020, 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_400_CSR_H_ +#define _3B2_400_CSR_H_ + +#include "sim_defs.h" + +t_stat csr_svc(UNIT *uptr); +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_reset(DEVICE *dptr); +uint32 csr_read(uint32 pa, size_t size); +void csr_write(uint32 pa, uint32 val, size_t size); + +#endif diff --git a/3B2/3b2_rev3_defs.h b/3B2/3b2_rev3_defs.h new file mode 100644 index 00000000..a51fd0b8 --- /dev/null +++ b/3B2/3b2_rev3_defs.h @@ -0,0 +1,138 @@ + /* 3b2_rev3_defs.h: AT&T 3B2 Rev 3 (Model 600G) Simulator Definitions + + 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_DEFS_H_ +#define _3B2_REV3_DEFS_H_ + +#include "sim_defs.h" + +#define NUM_REGISTERS 32 + +#define DEFMEMSIZE MSIZ_4M +#define MAXMEMSIZE MSIZ_64M + +#define HWORD_OP_COUNT 12 +#define CPU_VERSION 0x1F /* Version encoded in WE32200 */ + +/* 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 IFCSRSIZE 0x100 +#define TIMERBASE 0x41000 +#define TIMERSIZE 0x20 +#define NVRBASE 0x42000 +#define NVRSIZE 0x2000 +#define CSRBASE 0x44000 +#define CSRSIZE 0x100 +#define DMAIFBASE 0x45000 +#define DMAIFSIZE 0x5 +#define DMAIUABASE 0x46000 +#define DMAIUASIZE 0x5 +#define DMAIUBBASE 0x47000 +#define DMAIUBSIZE 0x5 +#define DMACBASE 0x48000 +#define DMACSIZE 0x11 +#define IFBASE 0x4a000 +#define IFSIZE 0x10 +#define TODBASE 0x4e000 +#define TODSIZE 0x40 +#define MMUBASE 0x4f000 +#define MMUSIZE 0x1000 +#define FLTLBASE 0x4c000 +#define FLTLSIZE 0x10 +#define FLTHBASE 0x4d000 +#define FLTHSIZE 0x10 + +#define VCACHE_BOTTOM 0x1c00000 +#define VCACHE_TOP 0x2000000 + +#define BUB_BOTTOM 0x6000000 +#define BUB_TOP 0x1a000000 + +#define IF_STATUS_REG 0 +#define IF_CMD_REG 0 +#define IF_TRACK_REG 1 +#define IF_SECTOR_REG 2 +#define IF_DATA_REG 3 + +#define DMA_IF_CHAN 1 +#define DMA_IUA_CHAN 2 +#define DMA_IUB_CHAN 3 + +#define DMA_IF 0x45 +#define DMA_IUA 0x46 +#define DMA_IUB 0x47 +#define DMA_C 0x48 + +/* CSR Flags */ +#define CSRCLK 1u /* UNIX Interval Timer Timeout */ +#define CSRPWRDN (1u << 1) /* Power Down Request */ +#define CSROPINT15 (1u << 2) /* Oper. Interrupt Level 15 */ +#define CSRUART (1u << 3) /* DUART Interrupt */ +#define CSRDMA (1u << 4) /* DUART DMA Complete Interrupt */ +#define CSRPIR9 (1u << 5) /* Programmed Interrupt 9 */ +#define CSRPIR8 (1u << 6) /* Programmed Interrupt 8 */ +#define CSRITIM (1u << 7) /* Inhibit UNIX Interval Timer */ +#define CSRISTIM (1u << 8) /* Inhibit System Sanity Timer */ +#define CSRITIMO (1u << 9) /* Inhibit Bus Timer */ +#define CSRICPUFLT (1u << 10) /* Inhibit Faults to CPU */ +#define CSRISBERR (1u << 11) /* Inhibit Single Bit Error Rpt */ +#define CSRIIOBUS (1u << 12) /* Inhibit Integral 3B2 I/O Bus */ +#define CSRIBUB (1u << 13) /* Inhibit 4 BUB Slots */ +#define CSRFECC (1u << 14) /* Force ECC Syndrome */ +#define CSRTHERM (1u << 15) /* Thermal Shutdown Request */ +#define CSRLED (1u << 16) /* Failure LED */ +#define CSRPWRSPDN (1u << 17) /* Power Down -- Power Supply */ +#define CSRFLPFST (1u << 18) /* Floppy Speed Fast */ +#define CSRFLPS1 (1u << 19) /* Floppy Side 1 */ +#define CSRFLPMO (1u << 20) /* Floppy Motor On */ +#define CSRFLPDEN (1u << 21) /* Floppy Density */ +#define CSRFLPSZ (1u << 22) /* Floppy Size */ +#define CSRSBERR (1u << 23) /* Single Bit Error */ +#define CSRMBERR (1u << 24) /* Multiple Bit Error */ +#define CSRUBUBF (1u << 25) /* Ubus/BUB Received Fail */ +#define CSRTIMO (1u << 26) /* Bus Timer Timeout */ +#define CSRFRF (1u << 27) /* Fault Registers Frozen */ +#define CSRALGN (1u << 28) /* Data Alignment Error */ +#define CSRSTIMO (1u << 29) /* Sanity Timer Timeout */ +#define CSRABRT (1u << 30) /* Abort Switch Activated */ +#define CSRRRST (1u << 31) /* System Reset Request */ + +extern DEVICE flt_dev; +extern DEVICE ha_dev; +extern uint32 csr_data; + +#endif diff --git a/3B2/3b2_rev3_mau.c b/3B2/3b2_rev3_mau.c new file mode 100644 index 00000000..53fcc2b6 --- /dev/null +++ b/3B2/3b2_rev3_mau.c @@ -0,0 +1,31 @@ +/* 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. */ diff --git a/3B2/3b2_rev3_mau.h b/3B2/3b2_rev3_mau.h new file mode 100644 index 00000000..9b54bb2b --- /dev/null +++ b/3B2/3b2_rev3_mau.h @@ -0,0 +1,36 @@ +/* 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 diff --git a/3B2/3b2_rev3_mmu.c b/3B2/3b2_rev3_mmu.c new file mode 100644 index 00000000..3d70f93d --- /dev/null +++ b/3B2/3b2_rev3_mmu.c @@ -0,0 +1,1207 @@ +/* 3b2_rev3_mmu.c: AT&T 3B2/600G MMU (WE32201) + + Copyright (c) 2020, 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. +*/ + +#include "3b2_defs.h" +#include "3b2_rev3_mmu.h" +#include + +UNIT mmu_unit = { UDATA(NULL, 0, 0) }; + +MMU_STATE mmu_state; + +REG mmu_reg[] = { + { HRDATAD (ENABLE, mmu_state.enabled, 1, "Enabled?") }, + { HRDATAD (CONFIG, mmu_state.conf, 32, "Configuration") }, + { HRDATAD (VAR, mmu_state.var, 32, "Virtual Address") }, + { HRDATAD (FCODE, mmu_state.fcode, 32, "Fault Code") }, + { HRDATAD (FADDR, mmu_state.faddr, 32, "Fault Address") }, + { BRDATA (SDCL, mmu_state.sdcl, 16, 32, MMU_SDCS) }, + { BRDATA (SDCH, mmu_state.sdch, 16, 32, MMU_SDCS) }, + { BRDATA (PDCL, mmu_state.pdcl, 16, 32, MMU_PDCS) }, + { BRDATA (PDCH, mmu_state.pdch, 16, 32, MMU_PDCS) }, + { BRDATA (SRAMA, mmu_state.sra, 16, 32, MMU_SRS) }, + { BRDATA (SRAMB, mmu_state.srb, 16, 32, MMU_SRS) }, + { NULL } +}; + +#define MMU_EXEC_DBG 1 +#define MMU_TRACE_DBG 1 << 1 +#define MMU_CACHE_DBG 1 << 2 +#define MMU_FAULT_DBG 1 << 3 +#define MMU_READ_DBG 1 << 4 +#define MMU_WRITE_DBG 1 << 5 + +static DEBTAB mmu_debug[] = { + { "EXEC", MMU_EXEC_DBG, "Simple execution" }, + { "CACHE", MMU_CACHE_DBG, "Cache trace" }, + { "TRACE", MMU_TRACE_DBG, "Translation trace" }, + { "FAULT", MMU_FAULT_DBG, "Faults" }, + { "READ", MMU_READ_DBG, "Peripheral Read"}, + { "WRITE", MMU_WRITE_DBG, "Peripheral Write"}, + { NULL } +}; + +MTAB mmu_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "SDT", NULL, + NULL, &mmu_show_sdt, NULL, "Display SDT for section n [0-3]" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "SDC", NULL, + NULL, &mmu_show_sdc, NULL, "Display SD Cache" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "PDC", NULL, + NULL, &mmu_show_pdc, NULL, "Display PD Cache" }, + { 0 } +}; + +DEVICE mmu_dev = { + "MMU", /* name */ + &mmu_unit, /* units */ + mmu_reg, /* registers */ + mmu_mod, /* modifiers */ + 1, /* #units */ + 16, /* address radix */ + 8, /* address width */ + 4, /* address incr */ + 16, /* data radix */ + 32, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &mmu_init, /* reset routine */ + NULL, /* boot routine */ + NULL, /* attach routine */ + NULL, /* detach routine */ + NULL, /* context */ + DEV_DEBUG, /* flags */ + 0, /* debug control flags */ + mmu_debug, /* debug flag names */ + NULL, /* memory size change */ + NULL, /* logical name */ + NULL, /* help routine */ + NULL, /* attach help routine */ + NULL, /* help context */ + &mmu_description /* device description */ +}; + +/* + * Each bitmask corresponds to the pattern of bits used for the tag in + * the first word of a segment descriptor in the cache. The outer + * index corresponds to mode (0=Single-Context Mode, 1=Multi-Context + * Mode), the inner index corresponds to page size (0=2kB, 1=4kB, + * 2=8kB, 3=undefined) + */ +uint32 pdc_tag_masks[2][4] = { + {0x43ffffe0, 0x43ffffc0, 0x43ffff80, 0}, + {0x7fffffe0, 0x7fffffc0, 0x7fffff80, 0}, +}; + +/* + * Bitmasks for generating page addresses for contiguous segments on + * cache miss. + */ +uint32 pd_addr_masks[4] = { + 0xfffff800, 0xfffff000, 0xffffe000, 0 +}; + +uint32 pd_psl_masks[4] = { + 0x1f800, 0x1f000, 0x1e000, 0 +}; + +/* Masks used when searching the PD cache for a matching tag. */ +#define PDC_TAG_MASK (pdc_tag_masks[MMU_CONF_MCE][MMU_CONF_PS]) + +/* Mask off the bottom 10 bits of virtual address when generating PD + * cache tags */ +#define VA_TO_TAG_MASK 0xfffff800 + +/* + * Macros used to generate PD cache tags from virtual-addresses + */ +#define PDC_MTAG(VA) ((((VA) & VA_TO_TAG_MASK) >> 6) | \ + (mmu_state.cidnr[SID(VA)] << 26) | \ + (1 << 30)) + +#define PDC_STAG(VA) ((((VA) & VA_TO_TAG_MASK) >> 6) | \ + (1 << 30)) + +#define PDC_TAG(VA) (MMU_CONF_MCE ? PDC_MTAG(VA) : PDC_STAG(VA)) + +/* + * Retrieve a Segment Descriptor from the SD cache. The Segment + * Descriptor Cache entry is returned in sd_lo and sd_hi, if found. + * + * If there is a cache hit, this function returns SCPE_OK. + * If there is a cache miss, this function returns SCPE_NXM. + */ +static t_stat get_sdce(uint32 va, uint32 *sd_hi, uint32 *sd_lo) +{ + uint32 hi, lo, va_tag, sdc_tag; + + hi = mmu_state.sdch[SDC_IDX(va)]; + lo = mmu_state.sdcl[SDC_IDX(va)]; + va_tag = (va >> 20) & 0xfff; + sdc_tag = lo & 0xfff; + + if ((hi & SDC_G_MASK) && (va_tag == sdc_tag)) { + *sd_hi = SDCE_TO_SDH(hi); + *sd_lo = SDCE_TO_SDL(hi,lo); + return SCPE_OK; + } + + return SCPE_NXM; +} + +/* + * Insert a Segment Descriptor into the SD cache. + */ +static void put_sdce(uint32 va, uint32 sd_hi, uint32 sd_lo) +{ + uint8 ci = SDC_IDX(va); + + mmu_state.sdch[ci] = SD_TO_SDCH(sd_hi, sd_lo); + mmu_state.sdcl[ci] = SD_TO_SDCL(sd_lo, va); + + 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", + R[NUM_PC], ci, va, sd_hi, sd_lo, mmu_state.sdch[ci], mmu_state.sdcl[ci]); + +} + +/* + * Update the "Used" bit in the Page Descriptor cache for the given + * entry. + */ +static void set_u_bit(uint32 index) +{ + uint32 i; + + mmu_state.pdch[index] |= PDC_U_MASK; + + /* Check to see if all U bits have been set. If so, the cache will + * need to be flushed on the next put */ + for (i = 0; i < MMU_PDCS; i++) { + if ((mmu_state.pdch[i] & PDC_U_MASK) == 0) { + return; + } + } + + mmu_state.flush_u = TRUE; +} + +/* + * Retrieve a Page Descriptor Cache Entry from the Page Descriptor + * Cache. + */ +static t_stat get_pdce(uint32 va, uint32 *pd, uint8 *pd_acc, uint32 *pdc_idx) +{ + uint32 i, key_tag, target_tag; + + *pdc_idx = 0; + + /* This is a fully associative cache, so we must scan for an entry + with the correct tag. */ + key_tag = PDC_TAG(va) & PDC_TAG_MASK; + + for (i = 0; i < MMU_PDCS; i++) { + target_tag = mmu_state.pdch[i] & PDC_TAG_MASK; + if (target_tag == key_tag) { + /* Construct the PD from the cached version */ + *pd = PDCE_TO_PD(mmu_state.pdcl[i]); + *pd_acc = (mmu_state.pdcl[i] >> 24) & 0xff; + *pdc_idx = i; + sim_debug(MMU_TRACE_DBG, &mmu_dev, + "[%08x] PDC HIT. va=%08x idx=%d tag=%03x pd=%08x pdcl=%08x pdch=%08x\n", + R[NUM_PC], va, i, key_tag, *pd, + mmu_state.pdcl[i], mmu_state.pdch[i]); + set_u_bit(i); + return SCPE_OK; + } + } + + sim_debug(MMU_CACHE_DBG, &mmu_dev, + "[%08x] PDC MISS. va=%08x tag=%03x\n", + R[NUM_PC], va, key_tag); + + return SCPE_NXM; +} + +/* + * Cache a Page Descriptor in the specified slot. + */ +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.pdch[slot] = VA_TO_PDCH(va, sd_lo); + sim_debug(MMU_CACHE_DBG, &mmu_dev, + "[%08x] 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); + set_u_bit(slot); + mmu_state.last_cached = slot; +} + +/* + * Cache a Page Descriptor in the first available, least recently used + * slot. + */ +static uint32 put_pdce(uint32 va, uint32 sd_lo, uint32 pd) +{ + uint32 i; + + /* + * If all the U bits have been set, flush them all EXCEPT the most + * recently cached entry. + */ + if (mmu_state.flush_u) { + sim_debug(MMU_CACHE_DBG, &mmu_dev, + "[%08x] Flushing PDC U bits on all-set condition.\n", + R[NUM_PC]); + mmu_state.flush_u = FALSE; + for (i = 0; i < MMU_PDCS; i++) { + if (i != mmu_state.last_cached) { + mmu_state.pdch[i] &= ~(PDC_U_MASK); + } + } + } + + /* TODO: This can be done in one pass! Clean it up */ + + /* Cache the Page Descriptor in the first slot with a cleared G + * bit */ + for (i = 0; i < MMU_PDCS; i++) { + if ((mmu_state.pdch[i] & PDC_G_MASK) == 0) { + put_pdce_at(va, sd_lo, pd, i); + return i; + } + } + + /* If ALL slots had their G bit set, find the first slot with a + cleared U bit */ + for (i = 0; i < MMU_PDCS; i++) { + if ((mmu_state.pdch[i] & PDC_U_MASK) == 0) { + put_pdce_at(va, sd_lo, pd, i); + return i; + } + } + + /* This should never happen, since if all U bits become set, they + * are automatically all cleared */ + stop_reason = STOP_MMU; + + return 0; +} + +/* + * Flush the cache for an individual virtual address. + */ +static void flush_pdc(uint32 va) +{ + uint32 i, j, key_tag, target_tag; + + /* Flush the PDC. This is a fully associative cache, so we must + * scan for an entry with the correct tag. */ + + key_tag = PDC_TAG(va) & PDC_TAG_MASK; + + for (i = 0; i < MMU_PDCS; i++) { + target_tag = mmu_state.pdch[i] & PDC_TAG_MASK; + if (target_tag == key_tag) { + sim_debug(MMU_CACHE_DBG, &mmu_dev, + "[%08x] Flushing MMU PDC entry pdc_lo=%08x pdc_hi=%08x index %d (va=%08x)\n", + R[NUM_PC], + mmu_state.pdcl[i], + mmu_state.pdch[i], + i, + va); + if (mmu_state.pdch[i] & PDC_C_MASK) { + sim_debug(MMU_CACHE_DBG, &mmu_dev, + "[%08x] Flushing MMU PDC entry: CONTIGUOUS\n", + R[NUM_PC]); + /* If this PD came from a contiguous SD, we need to + * flush ALL entries belonging to the same SD. All + * pages within the same segment have the same upper + * 11 bits. */ + for (j = 0; j < MMU_PDCS; j++) { + if ((mmu_state.pdch[j] & 0x3ffc000) == + (mmu_state.pdch[i] & 0x3ffc000)) { + mmu_state.pdch[j] &= ~(PDC_G_MASK|PDC_U_MASK); + } + } + } else { + /* Otherwise, just flush the one entry */ + mmu_state.pdch[i] &= ~(PDC_G_MASK|PDC_U_MASK); + } + return; + } + } + + sim_debug(MMU_CACHE_DBG, &mmu_dev, + "[%08x] Flushing MMU PDC entry: NOT FOUND (va=%08x key_tag=%08x)\n", + R[NUM_PC], va, key_tag); + +} + +/* + * Flush all entries in both SDC and PDC. + */ +static void flush_caches() +{ + uint32 i; + + sim_debug(MMU_CACHE_DBG, &mmu_dev, + "[%08x] Flushing MMU PDC and SDC\n", + R[NUM_PC]); + + for (i = 0; i < MMU_SDCS; i++) { + mmu_state.sdch[i] &= ~SDC_G_MASK; + } + + for (i = 0; i < MMU_PDCS; i++) { + mmu_state.pdch[i] &= ~PDC_G_MASK; + mmu_state.pdch[i] &= ~PDC_U_MASK; + } +} + +/* + * Check permissions for a set of permission flags and an access type. + * + * Return SCPE_OK if permission is granted, SCPE_NXM if permission is + * not allowed. + */ +static t_stat mmu_check_perm(uint8 flags, uint8 r_acc) +{ + switch(MMU_PERM(flags)) { + case 0: /* No Access */ + return SCPE_NXM; + case 1: /* Exec Only */ + if (r_acc != ACC_IF && r_acc != ACC_IFAD) { + return SCPE_NXM; + } + return SCPE_OK; + case 2: /* Read / Execute */ + if (r_acc != ACC_IF && r_acc != ACC_IFAD && + r_acc != ACC_AF && r_acc != ACC_OF && + r_acc != ACC_MT) { + return SCPE_NXM; + } + return SCPE_OK; + default: + return SCPE_OK; + } +} + +/* + * 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 + */ +t_stat mmu_init(DEVICE *dptr) +{ + flush_caches(); + return SCPE_OK; +} + +/* + * Memory-mapped (peripheral mode) read of the MMU device + */ +uint32 mmu_read(uint32 pa, size_t size) +{ + uint8 entity, index; + uint32 data = 0; + + /* Register entity */ + entity = ((uint8)(pa >> 8)) & 0xf; + + /* Index into entity */ + index = (uint8)((pa >> 2) & 0x1f); + + switch (entity) { + case MMU_SDCL: + data = mmu_state.sdcl[index]; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_SDCL[%d] = %08x\n", + R[NUM_PC], index, data); + break; + case MMU_SDCH: + data = mmu_state.sdch[index]; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_SDCH[%d] = %08x\n", + R[NUM_PC], index, data); + break; + case MMU_PDCL: + data = mmu_state.pdcl[index]; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_PDCL[%d] = %08x\n", + R[NUM_PC], index, data); + break; + case MMU_PDCH: + data = mmu_state.pdch[index]; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_PDCH[%d] = %08x\n", + R[NUM_PC], index, data); + break; + case MMU_SRAMA: + data = mmu_state.sra[index]; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_SRAMA[%d] = %08x\n", + R[NUM_PC], index, data); + break; + case MMU_SRAMB: + data = mmu_state.srb[index]; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_SRAMB[%d] = %08x\n", + R[NUM_PC], index, data); + break; + case MMU_FC: + data = mmu_state.fcode; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_FC = %08x\n", + R[NUM_PC], data); + break; + case MMU_FA: + data = mmu_state.faddr; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_FA = %08x\n", + R[NUM_PC], data); + break; + case MMU_CONF: + data = mmu_state.conf; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_CONF = %02x (M=%d R=%d $=%d PS=%d MCE=%d DCE=%d)\n", + R[NUM_PC], data, + MMU_CONF_M, + MMU_CONF_R, + MMU_CONF_C, + MMU_CONF_PS, + MMU_CONF_MCE, + MMU_CONF_DCE); + break; + case MMU_VAR: + data = mmu_state.var; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_VAR = %08x\n", + R[NUM_PC], data); + break; + case MMU_IDC: + /* TODO: Implement */ + data = 0; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_IDC\n", R[NUM_PC]); + break; + case MMU_IDNR: + /* TODO: Implement */ + data = 0; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_IDNR\n", R[NUM_PC]); + break; + case MMU_FIDNR: + /* TODO: Implement */ + data = 0; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_FIDNR\n", R[NUM_PC]); + break; + case MMU_VR: + /* Simply not faulting here is good enough */ + data = 0; + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] MMU_VR\n", R[NUM_PC]); + break; + default: + sim_debug(MMU_READ_DBG, &mmu_dev, + "[%08x] Invalid MMU register: pa=%08x\n", + R[NUM_PC], pa); + CSRBIT(CSRTIMO, TRUE); + break; + } + + return data; +} + +void mmu_write(uint32 pa, uint32 val, size_t size) +{ + uint32 index, entity, i; + + /* Register entity */ + entity = ((uint8)(pa >> 8)) & 0xf; + + /* Index into entity */ + index = (uint8)((pa >> 2) & 0x1f); + + switch (entity) { + case MMU_SDCL: + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "MMU_SDCL[%d] = %08x\n", + index, val); + mmu_state.sdcl[index] = val; + break; + case MMU_SDCH: + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "MMU_SDCH[%d] = %08x\n", + index, val); + mmu_state.sdch[index] = val; + break; + case MMU_PDCL: + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "MMU_PDCL[%d] = %08x\n", + index, val); + mmu_state.pdcl[index] = val; + break; + case MMU_PDCH: + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "MMU_PDCH[%d] = %08x\n", + index, val); + mmu_state.pdch[index] = val; + break; + case MMU_FDCR: + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] MMU_FDCR\n", + R[NUM_PC]); + /* Data cache is not implemented */ + break; + case MMU_SRAMA: + index = index & 3; + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] MMU_SRAMA[%d] = %08x\n", + R[NUM_PC], index, val); + mmu_state.sra[index] = val; + mmu_state.sec[index].addr = val & 0xfffffffc; + + /* Flush all SDC cache entries for this section */ + for (i = 0; i < MMU_SDCS; i++) { + if (((mmu_state.sdcl[i] >> 10) & 0x3) == index) { + sim_debug(MMU_CACHE_DBG, &mmu_dev, + "[%08x] Flushing MMU SDC entry at index %d " + "(sdc_lo=%08x sdc_hi=%08x)\n", + R[NUM_PC], i, + mmu_state.sdcl[i], mmu_state.sdch[i]); + mmu_state.sdch[i] &= ~(SDC_G_MASK); + } + } + + /* Flush all PDC cache entries for this section */ + for (i = 0; i < MMU_PDCS; i++) { + if (((mmu_state.pdch[i] >> 24) & 0x3) == index) { + mmu_state.pdch[i] &= ~(PDC_G_MASK); + } + } + break; + case MMU_SRAMB: + index = index & 3; + mmu_state.srb[index] = val; + mmu_state.sec[index].len = (val >> 10) & 0x1fff; + /* We do not flush the cache on writing SRAMB */ + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] MMU_SRAMB[%d] length=%04x (%d segments)\n", + R[NUM_PC], + index, + mmu_state.sec[index].len, + mmu_state.sec[index].len + 1); + break; + case MMU_FC: + /* Set a default value */ + mmu_state.fcode = (((CPU_CM) << 5) | (0xa << 7)); + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] MMU_FC = %08x\n", + R[NUM_PC], mmu_state.fcode); + break; + case MMU_FA: + mmu_state.faddr = val; + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] MMU_FADDR = %08x\n", + R[NUM_PC], val); + break; + case MMU_CONF: + mmu_state.conf = val & 0x7f; + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] MMU_CONF = %02x (M=%d R=%d $=%d PS=%d MCE=%d DCE=%d)\n", + R[NUM_PC], val, + MMU_CONF_M, + MMU_CONF_R, + MMU_CONF_C, + MMU_CONF_PS, + MMU_CONF_MCE, + MMU_CONF_DCE); + break; + case MMU_VAR: + mmu_state.var = val; + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] MMU_VAR = %08x\n", + R[NUM_PC], val); + if ((mmu_state.sdcl[SDC_IDX(val)] & SDC_VADDR_MASK) == + ((val >> 20) & SDC_VADDR_MASK)) { + sim_debug(MMU_CACHE_DBG, &mmu_dev, + "[%08x] Flushing MMU SDC entry at index %d " + "(sdc_lo=%08x sdc_hi=%08x)\n", + R[NUM_PC], SDC_IDX(val), + mmu_state.sdcl[SDC_IDX(val)], + mmu_state.sdch[SDC_IDX(val)]); + mmu_state.sdch[SDC_IDX(val)] &= ~SDC_G_MASK; + } + flush_pdc(val); + break; + case MMU_IDC: + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] MMU_IDC = %08x\n", + R[NUM_PC], val); + break; + case MMU_IDNR: + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] MMU_IDNR = %08x\n", + R[NUM_PC], val); + break; + case MMU_FIDNR: + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] MMU_FIDNR = %08x\n", + R[NUM_PC], val); + break; + case MMU_VR: + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] MMU_VR = %08x\n", + R[NUM_PC], val); + break; + default: + sim_debug(MMU_WRITE_DBG, &mmu_dev, + "[%08x] UNHANDLED WRITE (entity=0x%x, index=0x%x, val=%08x)\n", + R[NUM_PC], entity, index, val); + break; + } +} + +/* + * Update history bits in cache and memory. + */ +static t_stat mmu_update_history(uint32 va, uint8 r_acc, uint32 pdc_idx, t_bool fc) +{ + uint32 sd_hi, sd_lo, pd; + uint32 pd_addr; + t_bool update_sdc = TRUE; + + if (get_sdce(va, &sd_hi, &sd_lo) != SCPE_OK) { + update_sdc = FALSE; + } + + sd_lo = pread_w(SD_ADDR(va)); + sd_hi = pread_w(SD_ADDR(va) + 4); + + if (MMU_CONF_M && r_acc == ACC_W && (mmu_state.sdcl[SDC_IDX(va)] & SDC_M_MASK) == 0) { + if (update_sdc) { + mmu_state.sdcl[SDC_IDX(va)] |= SDC_M_MASK; + } + + if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] MMU R&M Update Fault (M)\n", + R[NUM_PC]); + MMU_FAULT(MMU_F_RM_UPD); + return SCPE_NXM; + } + + pwrite_w(SD_ADDR(va), sd_lo | SD_M_MASK); + } + + if (MMU_CONF_R && (mmu_state.sdcl[SDC_IDX(va)] & SDC_R_MASK) == 0) { + if (update_sdc) { + mmu_state.sdcl[SDC_IDX(va)] |= SDC_R_MASK; + } + + if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] MMU R&M Update Fault (R)\n", + R[NUM_PC]); + MMU_FAULT(MMU_F_RM_UPD); + return SCPE_NXM; + } + + pwrite_w(SD_ADDR(va), sd_lo | SD_R_MASK); + } + + if (!SD_CONTIG(sd_lo)) { + pd_addr = SD_SEG_ADDR(sd_hi) + (PSL(va) * 4); + + if (r_acc == ACC_W && (mmu_state.pdcl[pdc_idx] & PDC_M_MASK) == 0) { + mmu_state.pdcl[pdc_idx] |= PDC_M_MASK; + pd = pread_w(pd_addr); + pwrite_w(pd_addr, pd | PD_M_MASK); + } + + if ((mmu_state.pdcl[pdc_idx] & PDC_R_MASK) == 0) { + mmu_state.pdcl[pdc_idx] |= PDC_R_MASK; + pd = pread_w(pd_addr); + pwrite_w(pd_addr, pd | PD_R_MASK); + } + } + + return SCPE_OK; +} + +/* + * Handle a Page Descriptor cache miss. + * + * - va is the virtual address for the PD. + * - r_acc is the requested access type. + * - fc is the fault check flag. + * + * If there was a miss when reading the SDC, TRUE will be returned in + * "sd_miss". The page descriptor will be returned in "pd", and the + * segment descriptor will be returned in "sd_lo" and "sd_hi". + * + * Returns SCPE_OK on success, SCPE_NXM on failure. If SCPE_NXM is + * returned, a failure code and fault address will be set in the + * appropriate registers. + * + * As always, the flag 'fc' may be set to FALSE to avoid certain + * types of fault checking. + * + * For detailed documentation, See: + * + * "WE 32201 Memory Management Unit Information Manual", AT&T Select + * Code 307-706, February 1987; Figure 2-18, pages 2-24 through 2-25. + */ +t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc, + uint32 *pd, uint32 *pdc_idx) +{ + uint32 sd_ptr, sd_hi, sd_lo, pd_addr; + uint32 indirect_count = 0; + t_bool sdc_miss = FALSE; + + *pdc_idx = 0; + + /* If this was an instruction fetch, the actual requested level + * here will become "Instruction Fetch After Discontinuity" + * due to the page miss. */ + r_acc = (r_acc == ACC_IF ? ACC_IFAD : r_acc); + + /* We immediately do SSL bounds checking. The 'fc' flag is not + * checked because SSL out of bounds is a fatal error. */ + if (SSL(va) > SRAMB_LEN(va)) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] SDT Length Fault. sramb_len=%x ssl=%x va=%08x\n", + R[NUM_PC], SRAMB_LEN(va), SSL(va), va); + MMU_FAULT(MMU_F_SDTLEN); + return SCPE_NXM; + } + + /* This loop handles segment descriptor indirection (if any) */ + sd_ptr = SD_ADDR(va); + while (1) { + /* Try to find the SD in the cache */ + if (get_sdce(va, &sd_hi, &sd_lo) != SCPE_OK) { + /* This was a miss, so we need to load the SD out of + * memory. */ + sdc_miss = TRUE; + + sd_lo = pread_w(sd_ptr); /* Control Bits */ + sd_hi = pread_w(sd_ptr + 4); /* Address Bits */ + sim_debug(MMU_CACHE_DBG, &mmu_dev, + "[%08x] 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); + } + + if (!SD_VALID(sd_lo)) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] Invalid Segment Descriptor. va=%08x sd_hi=%08x sd_lo=%08x\n", + R[NUM_PC], va, sd_hi, sd_lo); + MMU_FAULT(MMU_F_INV_SD); + return SCPE_NXM; + } + + if (SD_INDIRECT(sd_lo)) { + if (++indirect_count > MAX_INDIRECTS) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] Max Indirects Fault. va=%08x sd_hi=%08x sd_lo=%08x\n", + R[NUM_PC], va, sd_hi, sd_lo); + MMU_FAULT(MMU_F_INDIRECT); + return SCPE_NXM; + } + + /* 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) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] 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); + MMU_FAULT(MMU_F_MISS_MEM); + return SCPE_NXM; + } + + /* sd_hi is a pointer to a new segment descriptor */ + sd_ptr = sd_hi; + + sd_lo = pread_w(sd_ptr); + sd_hi = pread_w(sd_ptr + 4); + } else { + /* If it's not an indirection, we're done. */ + break; + } + } + + /* Fault if the segment descriptor P bit isn't set */ + if (!SD_PRESENT(sd_lo)) { + /* If the C bit is set, this is a SEGMENT NOT PRESENT + fault; otherwise, it's a PDT NOT PRESENT fault. */ + if (SD_CONTIG(sd_lo)) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] Segment Not Present. va=%08x\n", + R[NUM_PC], va); + MMU_FAULT(MMU_F_SEG_NOT_PRES); + return SCPE_NXM; + } else { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] PDT Not Present. va=%08x\n", + R[NUM_PC], va); + MMU_FAULT(MMU_F_PDT_NOT_PRES); + return SCPE_NXM; + } + } + + /* Check to see if the segment is too long. */ + if (SD_CONTIG(sd_lo)) { + if (PSL(va) > SD_MAX_OFF(sd_lo)) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] Segment Offset Fault. va=%08x\n", + R[NUM_PC], va); + MMU_FAULT(MMU_F_SEG_OFFSET); + return SCPE_NXM; + } + } else { + if ((va & 0x1ffff) > MAX_SEG_OFF(sd_lo)) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] PDT Length Fault. va=%08x max_seg_off=0x%x\n", + R[NUM_PC], va, MAX_SEG_OFF(sd_lo)); + MMU_FAULT(MMU_F_PDTLEN); + return SCPE_NXM; + } + } + + /* Either load or construct the PD */ + if (SD_CONTIG(sd_lo)) { + /* TODO: VERIFY */ + if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) { + 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", + R[NUM_PC], va, CPU_CM, SD_ACC(sd_lo), r_acc); + MMU_FAULT(MMU_F_ACC); + return SCPE_NXM; + } + + /* We have to construct a PD for this SD. */ + *pd = (((sd_hi & pd_addr_masks[MMU_CONF_PS]) + PSL_C(va)) | + ((sd_lo & 0x800000) >> 18) | /* Copy R bit */ + ((sd_lo & 0x400000) >> 21) | /* Copy M bit */ + 1); /* P bit */ + + 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", + R[NUM_PC], MMU_CONF_PS, va, sd_hi, sd_lo, *pd); + } else { + /* We can find the PD in main memory */ + pd_addr = SD_SEG_ADDR(sd_hi) + (PSL(va) * 4); + + *pd = pread_w(pd_addr); + + 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", + R[NUM_PC], va, sd_hi, sd_lo, pd_addr, *pd); + } + + if (r_acc == ACC_W && (*pd & PD_W_MASK)) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] Page Write Fault, pd=%08x va=%08x\n", + R[NUM_PC], *pd, va); + MMU_FAULT(MMU_F_PW); + return SCPE_NXM; + } + + if ((*pd & PD_P_MASK) != PD_P_MASK) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] Page Not Present Fault. pd=%08x va=%08x\n", + R[NUM_PC], *pd, va); + MMU_FAULT(MMU_F_PAGE_NOT_PRES); + return SCPE_NXM; + } + + /* Finally, cache the PD */ + + if (sdc_miss) { + put_sdce(va, sd_hi, sd_lo); + } + + *pdc_idx = put_pdce(va, sd_lo, *pd); + + return SCPE_OK; +} + +/* + * Translate a virtual address into a physical address. + * + * Note that unlike 'mmu_xlate_addr', this function will _not_ abort + * on failure. The decoded physical address is returned in "pa". If + * the argument "fc" is FALSE, this function will bypass: + * + * - Access flag checks, + * - Cache insertion, + * - Setting MMU fault registers, + * - Modifying segment and page descriptor bits. + * + * In other words, setting "fc" to FALSE does the minimum work + * necessary to translate a virtual address without changing any MMU + * state. The primary use case for this flag is to provide simulator + * debugging access to memory translation while avoiding that access + * undermining the currently running operating system (if any). + * + * This function returns SCPE_OK if translation succeeded, and + * SCPE_NXM if translation failed. + * + */ +t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa) +{ + uint32 pd, pdc_idx; + uint8 pd_acc; + t_stat succ; + + /* + * If the MMU is disabled, virtual == physical. + */ + if (!mmu_state.enabled) { + *pa = va; + return SCPE_OK; + } + + /* + * 1. Check PDC for an entry. + */ + if (get_pdce(va, &pd, &pd_acc, &pdc_idx) == SCPE_OK) { + if (mmu_check_perm(pd_acc, r_acc) != SCPE_OK) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] 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); + MMU_FAULT(MMU_F_ACC); + return SCPE_NXM; + } + + if (r_acc == ACC_W && (pd & PD_W_MASK)) { + sim_debug(MMU_FAULT_DBG, &mmu_dev, + "[%08x] Page Write Fault, pd=%08x va=%08x\n", + R[NUM_PC], pd, va); + MMU_FAULT(MMU_F_PW); + return SCPE_NXM; + } + } else { + /* Do miss processing. This will cache the PD if necessary. */ + succ = mmu_pdc_miss(va, r_acc, fc, &pd, &pdc_idx); + if (succ != SCPE_OK) { + return succ; + } + } + + /* + * 2. Update history bits + */ + succ = mmu_update_history(va, r_acc, pdc_idx, fc); + if (succ != SCPE_OK) { + return succ; + } + + /* + * 3. Translation from Page Descriptor + */ + *pa = PD_ADDR(pd) + POT(va); + + sim_debug(MMU_TRACE_DBG, &mmu_dev, + "[%08x] XLATE DONE. r_acc=%d va=%08x pa=%08x\n", + R[NUM_PC], r_acc, va, *pa); + + return SCPE_OK; +} + +/* + * Translate a virtual address into a physical address. + * + * This function returns the translated virtual address, and aborts + * without returning if translation failed. + */ +uint32 mmu_xlate_addr(uint32 va, uint8 r_acc) +{ + uint32 pa; + t_stat succ; + + succ = mmu_decode_va(va, r_acc, TRUE, &pa); + + if (succ == SCPE_OK) { + mmu_state.var = va; + return pa; + } else { + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + return 0; + } +} + +/* + * Enable the MMU and allow virtual address translation. + */ +void mmu_enable() +{ + mmu_state.enabled = TRUE; +} + +/* + * Disable the MMU. All memory access will be through physical addresses only. + */ +void mmu_disable() +{ + mmu_state.enabled = FALSE; +} + +CONST char *mmu_description(DEVICE *dptr) +{ + return "WE32201 MMU"; +} + +/* + * Display the segment descriptor cache + */ +t_stat mmu_show_sdc(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + uint32 sd_lo, sd_hi, base, pages, i; + + fprintf(st, "\nSegment Descriptor Cache\n\n"); + + fprintf(st, "start sdc (lo) sdc (hi) sd (lo) sd (hi) C/P seg start pages\n"); + fprintf(st, "-------- -------- -------- -------- -------- --- --------- -----\n"); + + for (i = 0; i < MMU_SDCS; i++) { + sd_lo = SDCE_TO_SDL(mmu_state.sdch[i], mmu_state.sdcl[i]); + sd_hi = SDCE_TO_SDH(mmu_state.sdch[i]); + base = ((mmu_state.sdcl[i] & 0xfff) << 20) | ((i & 7) << 17); + pages = ((sd_lo & SD_MAX_OFF_MASK) >> 18) + 1; + + fprintf(st, "%08x %08x %08x %08x %08x %s %08x %d\n", + base, + mmu_state.sdcl[i], + mmu_state.sdch[i], + sd_lo, sd_hi, + SD_CONTIG(sd_lo) ? "C" : "P", + sd_hi & SD_ADDR_MASK, + pages); + + } + + return SCPE_OK; +} + +t_stat mmu_show_pdc(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + uint32 i, pdc_hi, pdc_lo; + + fprintf(st, "\nPage Descriptor Cache\n\n"); + fprintf(st, "IDX pdc (hi) pdc (lo) U G C W vaddr pd addr\n"); + fprintf(st, "---- -------- -------- - - - - -------- --------\n"); + + for (i = 0; i < MMU_PDCS; i++) { + pdc_hi = mmu_state.pdch[i]; + pdc_lo = mmu_state.pdcl[i]; + + fprintf(st, "%02d %08x %08x %s %s %s %s %08x %08x\n", + i, + pdc_hi, pdc_lo, + pdc_hi & PDC_U_MASK ? "U" : " ", + pdc_hi & PDC_G_MASK ? "G" : " ", + pdc_hi & PDC_C_MASK ? "C" : "P", + pdc_lo & PDC_W_MASK ? "W" : " ", + (pdc_hi & PDC_TAG_MASK) << 6, + PDCE_TO_PD(pdc_lo)); + } + + return SCPE_OK; +} + +/* + * Display the segment table for a section. + */ +t_stat mmu_show_sdt(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + uint32 addr, len, sd_lo, sd_hi, base, pages, i; + uint8 sec; + char *cptr = (char *) desc; + t_stat result; + + if (cptr == NULL) { + fprintf(st, "Missing section number\n"); + return SCPE_ARG; + } + + sec = (uint8) get_uint(cptr, 10, 3, &result); + if (result != SCPE_OK) { + fprintf(st, "Please specify a section from 0-3\n"); + return SCPE_ARG; + } + + addr = mmu_state.sec[sec].addr; + len = mmu_state.sec[sec].len + 1; + + fprintf(st, "\nSection %d SDT\n\n", sec); + fprintf(st, "start end sd (lo) sd (hi) C/P seg start pages\n"); + fprintf(st, "-------- -------- -------- -------- --- --------- ------\n"); + + for (i = 0; i < len; i++) { + sd_lo = pread_w(addr + (i * 8)) & SD_RES_MASK; + sd_hi = pread_w(addr + (i * 8) + 4); + base = (sec << 14 | i << 1) << 16; + pages = ((sd_lo & SD_MAX_OFF_MASK) >> 18) + 1; + + if (SD_VALID(sd_lo)) { + fprintf(st, "%08x-%08x %08x %08x %s %08x %d\n", + base, + base + (((sd_lo & SD_MAX_OFF_MASK) >> 15) * 2048) - 1, + sd_lo, sd_hi, + SD_CONTIG(sd_lo) ? "C" : "P", + sd_hi & SD_ADDR_MASK, + pages); + } + } + + return SCPE_OK; +} diff --git a/3B2/3b2_rev3_mmu.h b/3B2/3b2_rev3_mmu.h new file mode 100644 index 00000000..51b8c8c6 --- /dev/null +++ b/3B2/3b2_rev3_mmu.h @@ -0,0 +1,356 @@ +/* 3b2_rev3_mmu.h: AT&T 3B2/600G MMU (WE32201) Header + + Copyright (c) 2020, 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_1000_MMU_H_ +#define _3B2_1000_MMU_H_ + +#include "sim_defs.h" + +#define MMU_SRS 4 /* Section RAM array size (words) */ +#define MMU_SDCS 8 /* SD Cache H/L array size */ +#define MMU_PDCS 64 /* PD Cache H/L array size */ +#define MMU_IDNCS 16 /* ID Number Cache array size */ + +/* Register address offsets */ +#define MMU_SDCL 0 /* SDC - Low Bits */ +#define MMU_SDCH 1 /* SDC - High Bits */ +#define MMU_PDCL 2 /* PDC - Low Bits */ +#define MMU_PDCH 3 /* PDC - High Bits */ +#define MMU_FDCR 4 /* Flush Data Cache Register */ +#define MMU_SRAMA 6 /* Section RAM A */ +#define MMU_SRAMB 7 /* Section RAM B */ +#define MMU_FC 8 /* Fault Code */ +#define MMU_FA 9 /* Fault Address */ +#define MMU_CONF 10 /* Configuration */ +#define MMU_VAR 11 /* Virtual Address Register */ +#define MMU_IDC 12 /* ID Number Cache */ +#define MMU_IDNR 13 /* ID Number Register */ +#define MMU_FIDNR 14 /* Flush ID Number Register */ +#define MMU_VR 15 /* Version Register */ + +#define MMU_CONF_M (mmu_state.conf & 0x1) +#define MMU_CONF_R ((mmu_state.conf & 0x2) >> 1) +#define MMU_CONF_C ((mmu_state.conf & 0x4) >> 1) +#define MMU_CONF_PS ((mmu_state.conf & 0x18) >> 3) +#define MMU_CONF_MCE ((mmu_state.conf & 0x20) >> 5) +#define MMU_CONF_DCE ((mmu_state.conf & 0x40) >> 6) + +/* Shift and mask the flag bits for the current CPU mode */ +#define MMU_PERM(f) ((f >> ((3 - (CPU_CM)) * 2)) & 3) + +/* Codes set in the MMU Fault register */ +#define MMU_F_MISS_MEM 1 +#define MMU_F_RM_UPD 2 +#define MMU_F_SDTLEN 3 +#define MMU_F_PW 4 +#define MMU_F_PDTLEN 5 +#define MMU_F_INV_SD 6 +#define MMU_F_SEG_NOT_PRES 7 +#define MMU_F_PDT_NOT_PRES 9 +#define MMU_F_PAGE_NOT_PRES 10 +#define MMU_F_INDIRECT 11 +#define MMU_F_ACC 13 +#define MMU_F_SEG_OFFSET 14 + +/* Access Request types */ +#define ACC_MT 0 /* Move Translated */ +#define ACC_SPW 1 /* Support processor write */ +#define ACC_SPF 3 /* Support processor fetch */ +#define ACC_IR 7 /* Interlocked read */ +#define ACC_AF 8 /* Address fetch */ +#define ACC_OF 9 /* Operand fetch */ +#define ACC_W 10 /* Write */ +#define ACC_IFAD 12 /* Instruction fetch after discontinuity */ +#define ACC_IF 13 /* Instruction fetch */ + +/* Pluck out Virtual Address fields */ +#define SID(va) (((va) >> 30) & 3) +#define SSL(va) (((va) >> 17) & 0x1fff) +#define SOT(va) (va & 0x1ffff) + +/* PSL will be either: + * - Bits 11-16 (2K pages: MMU_CONF_PS = 0) + * - Bits 12-16 (4K pages: MMU_CONF_PS = 1) + * - Bits 13-16 (8K pages: MMU_CONF_PS = 2) + */ +#define PSL(va) (((va) >> (11 + MMU_CONF_PS)) & (0x3f >> MMU_CONF_PS)) +#define PSL_C(va) (((va) & pd_psl_masks[MMU_CONF_PS])) + +/* POT will be either: + * - Bits 0-10 (2K pages: MMU_CONF_PS = 0) + * - Bits 0-11 (4K pages: MMU_CONF_PS = 1) + * - Bits 0-12 (8K pages: MMU_CONF_PS = 2) + */ +#define POT(va) ((va) & (0x1fff >> (2 - (MMU_CONF_PS)))) + +/* Get the maximum length of an SSL from SRAMB */ +#define SRAMB_LEN(va) (mmu_state.sec[SID(va)].len) + +/* Pluck out Segment Descriptor fields */ +#define SD_PRESENT(sd_lo) ((sd_lo) & 1) +#define SD_MODIFIED(sd_lo) (((sd_lo) >> 1) & 1) +#define SD_CONTIG(sd_lo) (((sd_lo) >> 2) & 1) +#define SD_VALID(sd_lo) (((sd_lo) >> 6) & 1) +#define SD_INDIRECT(sd_lo) (((sd_lo) >> 7) & 1) +#define SD_MAX_OFF(sd_lo) (((sd_lo) >> 18) & 0x3f) +#define SD_ACC(sd_lo) (((sd_lo) >> 24) & 0xff) + +#define SD_SEG_ADDR(sd_hi) ((sd_hi) & 0xfffffff8u) + +#define SD_P_MASK 0x1 +#define SD_M_MASK 0x2 +#define SD_C_MASK 0x4 +#define SD_CACHE_MASK 0x8 +#define SD_R_MASK 0x20 +#define SD_V_MASK 0x40 +#define SD_MAX_OFF_MASK 0xfc0000 +#define SD_ACC_MASK 0xff000000u +#define SD_ADDR_MASK 0xfffffff8u +#define SD_VADDR_MASK 0xfff00000u +#define SD_RES_MASK 0xfffc00efu + +#define SDC_VADDR_MASK 0xfff +#define SDC_ACC_MASK 0xff000000u +#define SDC_MAX_OFF_MASK 0x001f8000 +#define SDC_G_MASK 0x1 +#define SDC_C_MASK 0x2 +#define SDC_CACHE_MASK 0x4 +#define SDC_M_MASK 0x400000 +#define SDC_R_MASK 0x800000 + +#define PD_P_MASK 0x1 +#define PD_M_MASK 0x2 +#define PD_W_MASK 0x10 +#define PD_R_MASK 0x20 +#define PD_PADDR_MASK 0xfffff800u + +#define PDC_PADDR_MASK 0x1fffff +#define PDC_C_MASK 0x2 +#define PDC_W_MASK 0x200000 +#define PDC_M_MASK 0x400000 +#define PDC_R_MASK 0x800000 +#define PDC_G_MASK 0x40000000 +#define PDC_U_MASK 0x80000000u + +#define MAX_INDIRECTS 3 + +#define PD_ADDR(pd) (pd & (pd_addr_masks[MMU_CONF_PS])) +#define SD_ADDR(va) (mmu_state.sec[SID(va)].addr + (SSL(va) * 8)) + +#define SDC_IDX(va) ((uint8)((va) >> 17) & 7) + +/* Convert from sd to sd cache entry */ +#define SD_TO_SDCH(hi,lo) ((hi) & SD_ADDR_MASK | \ + ((lo) & SD_C_MASK) >> 1 | \ + ((lo) & SD_CACHE_MASK) >> 1 | \ + (SDC_G_MASK)) +#define SD_TO_SDCL(lo,va) (((lo) & SD_ACC_MASK) | \ + ((lo) & SD_MAX_OFF_MASK) >> 3 | \ + ((lo) & SD_R_MASK) << 18 | \ + ((lo) & SD_M_MASK) << 21 | \ + ((va) & SD_VADDR_MASK) >> 20) + +/* Convert from sd cache entry to sd */ +#define SDCE_TO_SDH(hi) ((hi) & SD_ADDR_MASK) +#define SDCE_TO_SDL(hi,lo) (((lo) & SDC_ACC_MASK) | \ + ((lo) & SDC_MAX_OFF_MASK) << 3 | \ + ((lo) & SDC_R_MASK) >> 18 | \ + ((lo) & SDC_M_MASK) >> 21 | \ + ((hi) & SDC_C_MASK) << 1 | \ + ((hi) & SDC_CACHE_MASK) << 1 | \ + SD_V_MASK | SD_P_MASK) + +/* Convert from pd cache entry to pd */ +#define PDCE_TO_PD(pdcl) (((pdcl & PDC_PADDR_MASK) << 11) | \ + ((pdcl & PDC_W_MASK) >> 17) | \ + ((pdcl & PDC_M_MASK) >> 21) | \ + ((pdcl & PDC_R_MASK) >> 18) | \ + PD_P_MASK) + +/* Convert from pd to pd cache entry (low word) */ +#define PD_TO_PDCL(pd, sd_lo) (((pd & PD_PADDR_MASK) >> 11) | \ + ((pd & PD_W_MASK) << 17) | \ + ((pd & PD_M_MASK) << 21) | \ + ((pd & PD_R_MASK) << 18) | \ + (sd_lo & SD_ACC_MASK)) + +/* Convert from va to pd cache entry (high word / tag) */ +#define VA_TO_PDCH(va, sd_lo) ((1 << 30) | \ + (mmu_state.cidnr[SID(va)] << 26) | \ + ((va & 0xfffff800u) >> 6) | \ + ((sd_lo & SD_CACHE_MASK) >> 1) | \ + ((sd_lo & SD_C_MASK) >> 1)) + +/* Maximum offset (in bytes) of a paged segment */ +#define MAX_SEG_OFF(w) (((SD_MAX_OFF(w) + 1) * ((MMU_CONF_PS + 1) * 2048)) - 1) + +#define IDNC_TAG(val) ((val) & 0xfffffff8) +#define IDNC_U(val) ((val) & 0x1) + +/* Fault codes */ +#define MMU_FAULT(f) { \ + if (fc) { \ + mmu_state.fcode = ((((uint32)r_acc)<<7) | \ + (((uint32)(CPU_CM))<<5) | \ + (f & 0x1f)); \ + mmu_state.faddr = va; \ + } \ + } + +typedef struct { + uint32 addr; + uint32 len; +} mmu_sec; + + +/* + * Segment Descriptor Cache Entry Format + * ===================================== + * + * The Segment Descriptor Cache is a directly mapped cache, indexed by + * bits 19, 18, and 17 of the virtual address. Some notes: + * + * - "Acc", "R", "M", "Max Offset", "Address", "$", and "C" are all + * copied from the SD in main memory. + * - "VAddr" holds bits 20-31 of the virtual address + * - "Address" holds a pointer (word-aligned, so the top 30 bits) to + * a page descriptor table in paged mode, or a segment in + * contiguous segment mode. + * - "Max Offset" holds the number of pages minus one in the + * segment. Depending on current page size, various bits of this + * field will be ignored: + * o Bits 20-15 are used for 2K pages + * o Bits 20-16 are used for 4K pages + * o Bits 20-17 are used for 8K pages + * + * Low Word (bits 0-31) + * -------------------- + * + * 31 24 23 22 21 20 15 14 12 11 0 + * +-------+---+---+---+------------+------+--------------------------+ + * | Acc | R | M | - | Max Offset | - | VAddr | + * +-------+---+---+---+------------+------+--------------------------+ + * + * High Word (bits 32-63) + * ---------------------- + * + * 31 3 2 1 0 + * +------------------------------------------------------+---+---+---+ + * | Address | $ | C | G | + * +------------------------------------------------------+---+---+---+ + * + * + * Page Descriptor Cache Entry Format + * ================================== + * + * The Page Descriptor Cache is a fully associative cache, with a + * tag constructed from the "G" and "IDN" bits, and bits 31-11 of + * the virtual address. + * + * Depending on the current page size and access mode, various bits of + * "VAddr" are ignored. + * + * o Multi-context mode, all ops except single-entry flush: + * VAddr bits 29-11 are used. + * o Multi-context mode, single-entry flush: + * VAddr bits 31-11 are used. + * o Single-context mode, all ops: + * Vaddr bits 31-11 are used. + * o In ALL CASES: + * + 2KB Page Size: Bits 11-12 are used. + * + 4KB Page Size: Bit 11 ignored, 12 used. + * + 8KB Page Size: Bits 11-12 ignored. + * + * Low Word (bits 0-31) + * -------------------- + * + * 31 24 23 22 21 20 0 + * +-------+---+---+---+----------------------------------------------+ + * | Acc | R | M | W | Physical Address | + * +-------+---+---+---+----------------------------------------------+ + * + * + * High Word (bits 32-63) + * ---------------------- + * + * 31 30 29 26 25 5 4 3 2 1 0 + * +---+---+---------+----------------------------+-------+---+---+---+ + * | U | G | IDN | (31) VAddr (11)| - | $ | C | - | + * +---+---+---------+----------------------------+-------+---+---+---+ + * + */ + +typedef struct _mmu_state { + t_bool enabled; /* Global enabled/disabled flag */ + + t_bool flush_u; /* If true, flush all but last cached entry */ + uint32 last_cached; /* The index of the last cached PDC entry */ + + uint32 sdcl[MMU_SDCS]; /* SDC low bits (0-31) */ + uint32 sdch[MMU_SDCS]; /* SDC high bits (32-63) */ + + uint32 pdcl[MMU_PDCS]; /* PDC low bits (0-31) */ + uint32 pdch[MMU_PDCS]; /* PDC high bits (32-63) */ + + uint32 sra[4]; /* Section RAM A */ + uint32 srb[4]; /* Section RAM B */ + + uint32 cidnr[4]; /* Current ID Number Registers */ + uint32 idnc[16]; /* ID Number Cache */ + + mmu_sec sec[4]; /* Section descriptors decoded from + Section RAM A and B */ + + uint32 fcode; /* Fault Code Register */ + uint32 faddr; /* Fault Address Register */ + uint32 conf; /* Configuration Register */ + uint32 var; /* Virtual Address Register */ + +} MMU_STATE; + +t_stat mmu_init(DEVICE *dptr); +uint32 mmu_read(uint32 pa, size_t size); +void mmu_write(uint32 pa, uint32 val, size_t size); +CONST char *mmu_description(DEVICE *dptr); + +/* Virtual memory translation */ +uint32 mmu_xlate_addr(uint32 va, uint8 r_acc); +t_stat mmu_decode_vaddr(uint32 vaddr, 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_disable(); + +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_pdc(FILE *st, UNIT *uptr, int32 val, CONST void *desc); + +#endif /* _3B2_1000_MMU_H_ */ diff --git a/3B2/3b2_rev3_sys.c b/3B2/3b2_rev3_sys.c new file mode 100644 index 00000000..5b515310 --- /dev/null +++ b/3B2/3b2_rev3_sys.c @@ -0,0 +1,70 @@ +/* 3b2_rev3_sys.c: AT&T 3B2/600G system definition + + Copyright (c) 2020, 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. +*/ + +#include "3b2_defs.h" +#include "3b2_sys.h" + +char sim_name[] = "AT&T 3B2/600G"; + +DEVICE *sim_devices[] = { + &cpu_dev, + &csr_dev, + &flt_dev, + &mmu_dev, + &mau_dev, + &timer_dev, + &tod_dev, + &nvram_dev, + &tti_dev, + &tto_dev, + &contty_dev, + &iu_timer_dev, + &dmac_dev, + &if_dev, + &ha_dev, + &ports_dev, + &ni_dev, + NULL +}; + +void full_reset() +{ + cpu_reset(&cpu_dev); + mau_reset(&mau_dev); + tti_reset(&tti_dev); + contty_reset(&contty_dev); + iu_timer_reset(&iu_timer_dev); + timer_reset(&timer_dev); + if_reset(&if_dev); + ha_reset(&ha_dev); + csr_reset(&csr_dev); + ports_reset(&ports_dev); + ni_reset(&ni_dev); +} diff --git a/3B2/3b2_scsi.c b/3B2/3b2_scsi.c new file mode 100644 index 00000000..07b50924 --- /dev/null +++ b/3B2/3b2_scsi.c @@ -0,0 +1,1464 @@ +/* 3b2_scsi.h: AT&T 3B2 SCSI (CM195W) feature card + + Copyright (c) 2020, 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. +*/ + +#include "sim_scsi.h" +#include "sim_tape.h" + +#include "3b2_defs.h" +#include "3b2_scsi.h" + +#define PUMP_CRC 0x201b3617 + +static void ha_cmd(uint8 op, uint8 subdev, uint32 addr, + int32 len, t_bool express); +static void ha_build_req(uint8 subdev, t_bool express); +static void ha_ctrl(); +static void ha_write(); +static void ha_write_ext(); +static void ha_read_ext(); +static void ha_read_capacity(); + +static void dump_rep(); +static void dump_req(); +static void dump_edt(); + +/* Do not initiate host IRQ if ivec is <= this value */ +#define SCSI_MIN_IVEC 1 +#define HA_SCSI_ID 0 +#define HA_MAXFR (1u << 16) + +HA_STATE ha_state; +SCSI_BUS ha_bus; +uint8 *ha_buf; +int8 ha_subdev_tab[8]; /* Map of subdevice to SCSI target */ +uint8 ha_subdev_cnt; +uint32 ha_crc = 0; +uint32 cq_offset = 0; + +static struct scsi_dev_t ha_tab[] = { + HA_DISK(SD327), + HA_TAPE(ST120) +}; + +#define SCSI_U_FLAGS (UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_DIS+ \ + UNIT_ROABLE+(SD327_DTYPE << UNIT_V_DTYPE)) + +UNIT ha_unit[] = { + { UDATA (&ha_svc, SCSI_U_FLAGS, HA_SIZE(SD327)) }, /* SCSI ID 0 */ + { UDATA (&ha_svc, SCSI_U_FLAGS, HA_SIZE(SD327)) }, /* SCSI ID 1 */ + { UDATA (&ha_svc, SCSI_U_FLAGS, HA_SIZE(SD327)) }, /* SCSI ID 2 */ + { UDATA (&ha_svc, SCSI_U_FLAGS, HA_SIZE(SD327)) }, /* SCSI ID 3 */ + { UDATA (&ha_svc, SCSI_U_FLAGS, HA_SIZE(SD327)) }, /* SCSI ID 4 */ + { UDATA (&ha_svc, SCSI_U_FLAGS, HA_SIZE(SD327)) }, /* SCSI ID 5 */ + { UDATA (&ha_svc, SCSI_U_FLAGS, HA_SIZE(SD327)) }, /* SCSI ID 6 */ + { UDATA (&ha_svc, SCSI_U_FLAGS, HA_SIZE(SD327)) }, /* SCSI ID 7 */ + { UDATA (&ha_svc, UNIT_DIS, 0) }, /* CIO unit */ +}; + +static UNIT *cio_unit = &ha_unit[8]; + +MTAB ha_mod[] = { + { SCSI_WLK, 0, NULL, "WRITEENABLED", + &scsi_set_wlk, NULL, NULL, "Write enable disk drive" }, + { SCSI_WLK, SCSI_WLK, NULL, "LOCKED", + &scsi_set_wlk, NULL, NULL, "Write lock disk drive" }, + { MTAB_XTD|MTAB_VUN, 0, "WRITE", NULL, + NULL, &scsi_show_wlk, NULL, "Display drive writelock status" }, + { MTAB_XTD|MTAB_VUN, SD327_DTYPE, NULL, "SD327", + &ha_set_type, NULL, NULL, "Set CDC 327MB Disk Type" }, + { MTAB_XTD|MTAB_VUN, ST120_DTYPE, NULL, "ST120", + &ha_set_type, NULL, NULL, "Set Wangtek 120MB Tape Type" }, + { MTAB_XTD|MTAB_VUN, 0, "TYPE", NULL, + NULL, &ha_show_type, NULL, "Display device type" }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &scsi_set_fmt, &scsi_show_fmt, NULL, "Set/Display unit format" }, + { 0 } +}; + +#define HA_TRACE 1 + +static DEBTAB ha_debug[] = { + { "TRACE", HA_TRACE, "Call Trace" }, + { "SCMD", SCSI_DBG_CMD, "SCSI commands"}, + { "SBUS", SCSI_DBG_BUS, "SCSI bus activity" }, + { "SMSG", SCSI_DBG_MSG, "SCSI messages" }, + { "SDSK", SCSI_DBG_DSK, "SCSI disk activity" }, + { NULL } +}; + +DEVICE ha_dev = { + "SCSI", /* name */ + ha_unit, /* units */ + NULL, /* registers */ + ha_mod, /* modifiers */ + 9, /* #units */ + 16, /* address radix */ + 32, /* address width */ + 1, /* address incr. */ + 16, /* data radix */ + 8, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &ha_reset, /* reset routine */ + NULL, /* boot routine */ + &ha_attach, /* attach routine */ + &ha_detach, /* detach routine */ + NULL, /* context */ + DEV_DEBUG|DEV_DISK|DEV_SECTORS, /* flags */ + 0, /* debug control flags */ + ha_debug, /* debug flag names */ + NULL, /* memory size change */ + NULL, /* logical name */ + NULL, /* help routine */ + NULL, + NULL, + NULL, +}; + +void ha_cio_reset(uint8 cid) +{ + sim_debug(HA_TRACE, &ha_dev, + "[%08x] Handling CIO reset\n", + R[NUM_PC]); + ha_state.pump_state = PUMP_NONE; + ha_crc = 0; +} + +t_stat ha_reset(DEVICE *dptr) +{ + uint8 cid; + uint32 t, dtyp; + UNIT *uptr; + t_stat r; + + sim_debug(HA_TRACE, &ha_dev, + "[%08x] [ha_reset] Resetting SCSI device\n", + R[NUM_PC]); + + ha_state.pump_state = PUMP_NONE; + + if (ha_buf == NULL) { + ha_buf = (uint8 *)calloc(HA_MAXFR, sizeof(uint8)); + } + + r = scsi_init(&ha_bus, HA_MAXFR); + + if (r != SCPE_OK) { + return r; + } + + ha_bus.dptr = dptr; + + scsi_reset(&ha_bus); + + for (t = 0; t < 8; t++) { + uptr = dptr->units + t; + if (t == HA_SCSI_ID) { + uptr->flags = UNIT_DIS; + } + scsi_add_unit(&ha_bus, t, uptr); + dtyp = GET_DTYPE(uptr->flags); + scsi_set_unit(&ha_bus, uptr, &ha_tab[dtyp]); + scsi_reset_unit(uptr); + } + + if (dptr->flags & DEV_DIS) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_reset] REMOVING CARD\n"); + + for (cid = 0; cid < CIO_SLOTS; cid++) { + if (cio[cid].id == HA_ID) { + break; + } + } + + if (cid == CIO_SLOTS) { + /* No card was ever attached */ + return SCPE_OK; + } + + cio[cid].id = 0; + cio[cid].ipl = 0; + cio[cid].ivec = 0; + cio[cid].exp_handler = NULL; + cio[cid].full_handler = NULL; + cio[cid].sysgen = NULL; + + ha_state.initialized = FALSE; + + } else if (!ha_state.initialized) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_reset] Attaching SCSI Card\n"); + + /* Find the first available slot */ + for (cid = 0; cid < CIO_SLOTS; cid++) { + if (cio[cid].id == 0) { + break; + } + } + + if (cid == CIO_SLOTS) { + return SCPE_NXM; + } + + cio[cid].id = HA_ID; + cio[cid].ipl = HA_IPL; + cio[cid].exp_handler = &ha_express; + cio[cid].full_handler = &ha_full; + cio[cid].reset_handler = &ha_cio_reset; + cio[cid].sysgen = &ha_sysgen; + + ha_state.initialized = TRUE; + ha_state.cid = cid; + + sim_debug(HA_TRACE, &ha_dev, + "[ha_reset] SCSI Card now enabled in IO Slot %d\n", + cid); + } + + return SCPE_OK; +} + +static void ha_calc_subdevs() +{ + uint32 target; + UNIT *uptr; + SCSI_DEV *dev; + + ha_subdev_cnt = 0; + + memset(ha_subdev_tab, -1, 8); + + for (target = 0; target < 8; target++) { + uptr = &ha_unit[target]; + if (uptr->flags & UNIT_ATT) { + dev = (SCSI_DEV *)uptr->up7; + ha_subdev_tab[ha_subdev_cnt++] = target; + } + } +} + +t_stat ha_attach(UNIT *uptr, CONST char *cptr) +{ + t_stat r; + r = scsi_attach(uptr, cptr); + ha_calc_subdevs(); + return r; +} + +t_stat ha_detach(UNIT *uptr) +{ + t_stat r; + r = scsi_detach(uptr); + ha_calc_subdevs(); + return r; +} + +t_stat ha_svc(UNIT *uptr) +{ + cio_entry cqe; + uint8 capp_data[CAPP_LEN] = {0}; + + /* Finish any pending job */ + if (ha_state.reply.pending) { + ha_state.reply.pending = FALSE; + + switch(ha_state.reply.type) { + case HA_JOB_QUICK: + ha_fcm_express(); + + sim_debug(HA_TRACE, &ha_dev, + "[ha_svc] FAST MODE CQ: status=%02x op=%02x subdev=%02x ssb=%02x\n", + ha_state.reply.status, ha_state.reply.op, ha_state.reply.subdev, ha_state.reply.ssb); + break; + case HA_JOB_EXPRESS: + case HA_JOB_FULL: + cqe.byte_count = ha_state.reply.len; + cqe.opcode = ha_state.reply.status; /* Yes, status, not opcode! */ + cqe.subdevice = ha_state.reply.subdev; + cqe.address = ha_state.reply.addr; + + sim_debug(HA_TRACE, &ha_dev, + "[ha_svc] CQE: byte_count=%04x, opcode=%02x, subdevice=%02x, addr=%08x\n", + cqe.byte_count, cqe.opcode, cqe.subdevice, cqe.address); + + if (ha_state.reply.type == HA_JOB_EXPRESS) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_svc] EXPRESS MODE CQ: byte_count=%02x op=%02x subdev=%02x address=%08x\n", + cqe.byte_count, cqe.opcode, cqe.subdevice, cqe.address); + + cio_cexpress(ha_state.cid, SCQCESIZE, &cqe, capp_data); + } else { + sim_debug(HA_TRACE, &ha_dev, + "[ha_svc] FULL MODE CQ: status=%02x op=%02x subdev=%02x ssb=%02x\n", + ha_state.reply.status, ha_state.reply.op, ha_state.reply.subdev, ha_state.reply.ssb); + + cio_cqueue(ha_state.cid, 0, SCQCESIZE, &cqe, capp_data); + } + break; + } + + dump_rep(); + } + + if (cio[ha_state.cid].ivec > SCSI_MIN_IVEC) { + sim_debug(HA_TRACE, &ha_dev, + "[%08x] [ha_svc] IRQ for board %d (VEC=%d).\n", + R[NUM_PC], ha_state.cid, cio[ha_state.cid].ivec); + cio[ha_state.cid].intr = TRUE; + } + + return SCPE_OK; +} + +void ha_sysgen(uint8 cid) +{ + uint32 sysgen_p; + uint32 alert_buf_p; + + cq_offset = 0; + + sim_debug(HA_TRACE, &ha_dev, "[ha_sysgen] Handling Sysgen.\n"); + sim_debug(HA_TRACE, &ha_dev, "[ha_sysgen] rqp=%08x\n", cio[cid].rqp); + sim_debug(HA_TRACE, &ha_dev, "[ha_sysgen] cqp=%08x\n", cio[cid].cqp); + sim_debug(HA_TRACE, &ha_dev, "[ha_sysgen] rqs=%d\n", cio[cid].rqs); + sim_debug(HA_TRACE, &ha_dev, "[ha_sysgen] cqs=%d\n", cio[cid].cqs); + sim_debug(HA_TRACE, &ha_dev, "[ha_sysgen] ivec=%d\n", cio[cid].ivec); + sim_debug(HA_TRACE, &ha_dev, "[ha_sysgen] no_rque=%d\n", cio[cid].no_rque); + + sysgen_p = pread_w(SYSGEN_PTR); + alert_buf_p = pread_w(sysgen_p + 12); + sim_debug(HA_TRACE, &ha_dev, "[ha_sysgen] alert_bfr=%08x\n", alert_buf_p); + + ha_state.frq = (cio[cid].no_rque == 0); + + ha_state.reply.type = ha_state.frq ? HA_JOB_QUICK : HA_JOB_EXPRESS; + ha_state.reply.addr = 0; + ha_state.reply.len = 0; + ha_state.reply.status = 3; + ha_state.reply.op = 0; + ha_state.reply.pending = TRUE; + + if (ha_crc == PUMP_CRC) { + sim_debug(HA_TRACE, &ha_dev, + "[%08x] [ha_full] PUMP: NEW STATE = PUMP_SYSGEN\n", + R[NUM_PC]); + ha_state.pump_state = PUMP_SYSGEN; + } else { + sim_debug(HA_TRACE, &ha_dev, + "[%08x] [ha_full] PUMP: NEW STATE = PUMP_NONE\n", + R[NUM_PC]); + ha_state.pump_state = PUMP_NONE; + } + + sim_activate_abs(cio_unit, 100000); +} + +void ha_fast_queue_check() +{ + uint8 busy, op, subdev; + uint32 timeout, addr, len, rqp; + rqp = cio[ha_state.cid].rqp; + + busy = pread_b(rqp); + op = pread_b(rqp + 1); + subdev = pread_b(rqp + 2); + timeout = pread_w(rqp + 4); + addr = pread_w(rqp + 8); + len = pread_w(rqp + 12); + + if (busy == 0xff || ha_state.pump_state != PUMP_COMPLETE) { + sim_debug(HA_TRACE, &ha_dev, + "[%08x] [ha_fast_queue_check] Job pending (opcode=0x%02x subdev=%02x)\n", + R[NUM_PC], op, subdev); + pwrite_b(rqp, 0); /* Job has been taken */ + ha_cmd(op, subdev, addr, len, FALSE); + } +} + +void ha_express(uint8 cid) +{ + cio_entry rqe; + uint8 rapp_data[RAPP_LEN] = {0}; + + sim_debug(HA_TRACE, &ha_dev, + "[%08x] [ha_express] Handling Express Request\n", + R[NUM_PC]); + + if (ha_state.frq) { + ha_fast_queue_check(); + } else { + cio_rexpress(cid, SCQRESIZE, &rqe, rapp_data); + + ha_cmd(rqe.opcode, rqe.subdevice, rqe.address, + rqe.byte_count, TRUE); + } +} + +void ha_full(uint8 cid) +{ + sim_debug(HA_TRACE, &ha_dev, + "[%08x] [ha_full] Handling Full Request (INT3)\n", + R[NUM_PC]); + + if (ha_state.pump_state == PUMP_SYSGEN) { + sim_debug(HA_TRACE, &ha_dev, + "[%08x] [ha_full] PUMP: NEW STATE = PUMP_COMPLETE\n", + R[NUM_PC]); + ha_state.pump_state = PUMP_COMPLETE; + } + + if (ha_state.frq) { + ha_fast_queue_check(); + } else { + sim_debug(HA_TRACE, &ha_dev, + "[%08x] [ha_full] NON_FRQ NOT HANDLED\n", + R[NUM_PC]); + } +} + +static void dump_req() +{ + sim_debug(HA_TRACE, &ha_dev, + "[REQ] [%08x] %08x\n", + cio[ha_state.cid].rqp, + pread_w(cio[ha_state.cid].rqp)); + sim_debug(HA_TRACE, &ha_dev, + "[REQ] [%08x] %08x\n", + cio[ha_state.cid].rqp + 4, + pread_w(cio[ha_state.cid].rqp + 4)); + sim_debug(HA_TRACE, &ha_dev, + "[REQ] [%08x] %08x\n", + cio[ha_state.cid].rqp + 8, + pread_w(cio[ha_state.cid].rqp + 8)); + sim_debug(HA_TRACE, &ha_dev, + "[REQ] [%08x] %08x\n", + cio[ha_state.cid].rqp + 12, + pread_w(cio[ha_state.cid].rqp + 12)); + sim_debug(HA_TRACE, &ha_dev, + "[REQ] [%08x] %08x\n", + cio[ha_state.cid].rqp + 16, + pread_w(cio[ha_state.cid].rqp + 16)); + sim_debug(HA_TRACE, &ha_dev, + "[REQ] [%08x] %08x\n", + cio[ha_state.cid].rqp + 20, + pread_w(cio[ha_state.cid].rqp + 20)); + sim_debug(HA_TRACE, &ha_dev, + "[REQ] [%08x] %08x\n", + cio[ha_state.cid].rqp + 24, + pread_w(cio[ha_state.cid].rqp + 24)); +} + +static void dump_rep() +{ + uint32 i; + uint32 cqs = cio[ha_state.cid].cqs; + + for (i = 0; i < cqs; i++) { + sim_debug(HA_TRACE, &ha_dev, + "[CEQ] [%08x] %08x\n", + cio[ha_state.cid].cqp + (i * 4), + pread_w(cio[ha_state.cid].cqp + (i * 4))); + } +} + +static void ha_boot_disk(UNIT *uptr, uint8 target) +{ + t_seccnt sectsread; + t_stat r; + uint8 buf[HA_BLKSZ]; + uint32 i, boot_loc; + + /* Read in the Physical Descriptor (PD) block (block 0) */ + r = sim_disk_rdsect(uptr, 0, buf, §sread, 1); + + if (r != SCPE_OK) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_boot_disk] Could not read LBA 0\n"); + HA_STAT(HA_CKCON, CIO_SUCCESS); + return; + } + + /* Store the Physical Descriptor (PD) block at well-known + address 0x2004400 */ + sim_debug(HA_TRACE, &ha_dev, + "[ha_boot_disk] Storing PD block at 0x%08x.\n", + HA_PDINFO_ADDR); + for (i = 0; i < HA_BLKSZ; i++) { + pwrite_b(HA_PDINFO_ADDR + i, buf[i]); + } + + + /* The PD block points to the logical start of disk */ + boot_loc = ATOW(buf, HA_PDLS_OFF); + + sim_debug(HA_TRACE, &ha_dev, + "[ha_boot_disk] Logical Start is at 0x%x\n", + boot_loc); + + r = sim_disk_rdsect(uptr, boot_loc, buf, §sread, 1); + + sim_debug(HA_TRACE, &ha_dev, + "[ha_boot_disk] Storing boot block %d at 0x%08x.\n", + boot_loc, HA_BOOT_ADDR); + + for (i = 0; i < HA_BLKSZ; i++) { + pwrite_b(HA_BOOT_ADDR + i, buf[i]); + } + + sim_debug(HA_TRACE, &ha_dev, + "[ha_boot_disk] Done storing boot block at 0x%08x\n", + HA_BOOT_ADDR); + + HA_STAT(HA_GOOD, CIO_SUCCESS); + + ha_state.reply.addr = HA_BOOT_ADDR; + ha_state.reply.len = HA_BLKSZ; +} + +static void ha_boot_tape(UNIT *uptr) +{ + t_seccnt sectsread; + t_stat r; + uint8 buf[HA_BLKSZ]; + uint32 i; + + if (!(uptr->flags & UNIT_ATT)) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_boot_tape] Target not attached\n"); + HA_STAT(HA_CKCON, CIO_SUCCESS); + return; + } + + r = sim_tape_rewind(uptr); + + if (r != SCPE_OK) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_boot_tape] Could not rewind tape\n"); + HA_STAT(HA_CKCON, CIO_SUCCESS); + return; + } + + r = sim_tape_rdrecf(uptr, buf, §sread, HA_BLKSZ); /* Read block 0 */ + + if (r != SCPE_OK) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_boot_tape] Could not read PD block.\n"); + HA_STAT(HA_CKCON, CIO_SUCCESS); + return; + } + + for (i = 0; i < HA_BLKSZ; i++) { + pwrite_b(HA_BOOT_ADDR + i, buf[i]); + } + + sim_debug(HA_TRACE, &ha_dev, + "[ha_boot_tape] Transfered 512 bytes to 0x%08x\n", + HA_BOOT_ADDR); + + r = sim_tape_sprecf(uptr, §sread); /* Skip block 1 */ + + HA_STAT(HA_GOOD, CIO_SUCCESS); + + ha_state.reply.addr = HA_BOOT_ADDR; + ha_state.reply.len = HA_BLKSZ; +} + +static void ha_read_block_tape(UNIT *uptr, uint32 addr) +{ + t_seccnt sectsread; + t_stat r; + uint8 buf[HA_BLKSZ]; + uint32 i; + + if (!(uptr->flags & UNIT_ATT)) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_read_block_tape] Target not attached\n"); + HA_STAT(HA_CKCON, CIO_SUCCESS); + return; + } + + r = sim_tape_rdrecf(uptr, buf, §sread, HA_BLKSZ); + + if (r != SCPE_OK) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_read_block_tape] Could not read next block.\n"); + HA_STAT(HA_CKCON, CIO_SUCCESS); + return; + } + + for (i = 0; i < HA_BLKSZ; i++) { + pwrite_b(addr + i, buf[i]); + } + + sim_debug(HA_TRACE, &ha_dev, + "[ha_read_block_tape] Transfered 512 bytes to 0x%08x\n", + addr); + + HA_STAT(HA_GOOD, CIO_SUCCESS); + + ha_state.reply.addr = addr; + ha_state.reply.len = HA_BLKSZ; +} + +static void ha_read_block_disk(UNIT *uptr, uint8 target, uint32 addr, uint32 lba) +{ + t_seccnt sectsread; + t_stat r; + uint8 buf[HA_BLKSZ]; + uint32 i; + + sim_debug(HA_TRACE, &ha_dev, + "[ha_read_block_disk] Block translated from LBA 0x%X to PBA 0x%X\n", + lba, lba); + + r = sim_disk_rdsect(uptr, lba, buf, §sread, 1); + + if (r != SCPE_OK) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_read_block_disk] Could not read block %d\n", + lba); + HA_STAT(HA_CKCON, CIO_SUCCESS); + return; + } + + for (i = 0; i < HA_BLKSZ; i++) { + pwrite_b(addr + i, buf[i]); + } + + sim_debug(HA_TRACE, &ha_dev, + "[ha_read_block_disk] Transferred 512 bytes to 0x%08x\n", + addr); + + HA_STAT(HA_GOOD, CIO_SUCCESS); + + ha_state.reply.addr = addr; + ha_state.reply.len = HA_BLKSZ; +} + +static void ha_build_req(uint8 subdev, t_bool express) +{ + uint32 i, rqp, ptr, dma_lst; + uint32 len, addr; + cio_entry rqe; + uint8 rapp_data[RAPP_LEN] = {0}; + + /* + * There are two possible ways to get the SCSI command we've + * been asked to perform. + * + * 1. If this is a "fast mode" operation, then the SCSI command + * is embedded in the Fast Request Queue entry. + * + * 2. If this is a regular queue operation, then the SCSI command + * is embedded in a struct pointed to by the "address" field + * of the queue entry. + */ + + for (i = 0; i < HA_MAX_CMD; i++) { + ha_state.request.cmd[i] = 0; + } + + if (ha_state.frq) { + rqp = cio[ha_state.cid].rqp; + + subdev = pread_b(rqp + 2); + + ha_state.request.tc = FC_TC(subdev); + ha_state.request.lu = FC_LU(subdev); + ha_state.request.timeout = pread_w(rqp + 4); + ha_state.request.cmd_len = pread_h(rqp + 18); + for (i = 0; i < HA_MAX_CMD; i++) { + ha_state.request.cmd[i] = pread_b(rqp + 20 + i); + } + ha_state.request.op = ha_state.request.cmd[0]; + + /* Possible list of DMA scatter/gather addresses */ + dma_lst = pread_h(rqp + 16) / 8; + + if (dma_lst) { + t_bool link; + + /* There's a list of address / lengths. Each entry is 8 + * bytes long. */ + ptr = pread_w(rqp + 8); + link = FALSE; + + sim_debug(HA_TRACE, &ha_dev, + "[build_req] Building a list of scatter/gather addresses.\n"); + + for (i = 0; (i < dma_lst) || link; i++) { + addr = pread_w(ptr); + len = pread_w(ptr + 4); + + if (len == 0) { + sim_debug(HA_TRACE, &ha_dev, + "[build_req] Found length of 0, bailing early.\n"); + break; /* Done early */ + } + + if (len > 0x1000) { + /* There's a new pointer in town */ + ptr = pread_w(ptr); + sim_debug(HA_TRACE, &ha_dev, + "[build_req] New ptr=%08x\n", + ptr); + link = TRUE; + continue; + } + + sim_debug(HA_TRACE, &ha_dev, + "[build_req] daddr[%d]: addr=%08x, len=%d (%x)\n", + i, addr, len, len); + + ha_state.request.daddr[i].addr = addr; + ha_state.request.daddr[i].len = len; + + ptr += 8; + } + + ha_state.request.dlen = i; + } else { + /* There's only one embedded address / length */ + ha_state.request.daddr[0].addr = pread_w(rqp + 8); + ha_state.request.daddr[0].len = pread_w(rqp + 12); + ha_state.request.dlen = 1; + } + + } else { + if (express) { + cio_rexpress(ha_state.cid, SCQRESIZE, &rqe, rapp_data); + } else { + /* TODO: Find correct queue number! */ + cio_rqueue(ha_state.cid, 0, SCQRESIZE, &rqe, rapp_data); + } + + ptr = rqe.address; + + ha_state.request.tc = FC_TC(rqe.subdevice); + ha_state.request.lu = FC_LU(rqe.subdevice); + ha_state.request.cmd_len = pread_w(ptr + 4); + ha_state.request.timeout = pread_w(ptr + 8); + ha_state.request.daddr[0].addr = pread_w(ptr + 12); + ha_state.request.daddr[0].len = rqe.byte_count; + ha_state.request.dlen = 1; + + sim_debug(HA_TRACE, &ha_dev, + "[build_req] [non-fast] Building a list of 1 scatter/gather addresses.\n"); + + ptr = pread_w(ptr); + + for (i = 0; (i < ha_state.request.cmd_len) && (i < HA_MAX_CMD); i++) { + ha_state.request.cmd[i] = pread_b(ptr + i); + } + + ha_state.request.op = ha_state.request.cmd[0]; + } +} + +static void ha_cmd(uint8 op, uint8 subdev, uint32 addr, int32 len, t_bool express) +{ + int32 i, block; + UNIT *uptr; + SCSI_DEV *dev; + uint8 target; + + /* Immediately cancel any pending IRQs */ + sim_cancel(cio_unit); + + ha_state.reply.pending = TRUE; + ha_state.reply.op = op; + ha_state.reply.subdev = subdev; + ha_state.reply.status = CIO_FAILURE; + ha_state.reply.ssb = 0; + ha_state.reply.len = 0; + ha_state.reply.addr = 0; + + if (ha_state.pump_state == PUMP_COMPLETE) { + ha_state.reply.op |= 0x80; + } + + if (ha_state.frq) { + ha_state.reply.type = HA_JOB_QUICK; + } else if (express) { + ha_state.reply.type = HA_JOB_EXPRESS; + } else { + ha_state.reply.type = HA_JOB_FULL; + } + + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] --------------------------[START]---------------------------------\n"); + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] op=%02x (%d), subdev=%02x, addr=%08x, len=%d\n", + op, op, subdev, addr, len); + + dump_req(); + + switch (op) { + case CIO_DLM: + for (i = 0; i < len; i++) { + ha_crc = cio_crc32_shift(ha_crc, pread_b(addr + i)); + } + + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] SCSI Download Memory: bytecnt=%04x " + "addr=%08x return_addr=%08x subdev=%02x (CRC=%08x)\n", + len, addr, addr, subdev, ha_crc); + ha_state.reply.status = CIO_SUCCESS; + sim_activate_abs(cio_unit, 1200); + break; + case CIO_FCF: + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] SCSI Force Function Call. (CRC=%08x)\n", + ha_crc); + cio[ha_state.cid].sysgen_s = 0; + ha_state.reply.status = CIO_SUCCESS; + sim_activate_abs(cio_unit, 1200); + case CIO_DSD: + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] SCSI DSD - %d CONFIGURED DEVICES (writing to addr %08x).\n", + ha_subdev_cnt, addr); + + pwrite_h(addr, ha_subdev_cnt); + + for (i = 0; i < ha_subdev_cnt; i++) { + addr += 2; + + target = ha_subdev_tab[i]; + + if (target < 0) { + pwrite_h(addr, 0); + continue; + } + + uptr = &ha_unit[target]; + + dev = (SCSI_DEV *)uptr->up7; + + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] [DSD] Probing subdev %d, target %d, devtype %d\n", + i, target, dev->devtype); + + switch(dev->devtype) { + case SCSI_DISK: + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] [DSD] Subdev %d is DISK (writing to addr %08x)\n", + i, addr); + pwrite_h(addr, HA_DSD_DISK); + break; + case SCSI_TAPE: + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] [DSD] Subdev %d is TAPE (writing to addr %08x)\n", + i, addr); + pwrite_h(addr, HA_DSD_TAPE); + break; + default: + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] [DSD] Warning: No device type for subdev %d (Writing to addr %08x)\n", + i, addr); + pwrite_h(addr, 0); + break; + } + } + + ha_state.reply.status = CIO_SUCCESS; + + sim_activate_abs(cio_unit, 5000); + + break; + case HA_BOOT: + target = ha_subdev_tab[subdev & 7]; + + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] TARGET %d BOOTING.\n", + target); + + if (target < 0) { + ha_state.reply.status = CIO_TIMEOUT; + sim_activate_abs(cio_unit, 250); + return; + } + + uptr = &ha_unit[target]; + + if (!(uptr->flags & UNIT_ATT)) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] TARGET %d NOT ATTACHED.\n", + target); + ha_state.reply.status = CIO_TIMEOUT; + sim_activate_abs(cio_unit, 250); + return; + } + + dev = (SCSI_DEV *)uptr->up7; + + switch(dev->devtype) { + case SCSI_DISK: + ha_boot_disk(uptr, target); + break; + case SCSI_TAPE: + ha_boot_tape(uptr); + break; + default: + sim_debug(HA_TRACE, &ha_dev, + "[HA_BOOT] Cannot boot target %d (not disk or tape).\n", + target); + ha_state.reply.status = CIO_SUCCESS; + break; + } + + sim_activate_abs(cio_unit, 5000); + break; + case HA_READ_BLK: + target = ha_subdev_tab[subdev & 7]; + + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] SUBDEV %d TARGET %d READ BLOCK (BLOCK 0x%08x TO ADDR 0x%08x)\n", + subdev, target, pread_w(addr), pread_w(addr + 4)); + + sim_debug(HA_TRACE, &ha_dev, + "[boot_next] addr = %08x\n", + addr); + sim_debug(HA_TRACE, &ha_dev, + "[boot_next] %08x = %08x\n", + addr, pread_w(addr)); + sim_debug(HA_TRACE, &ha_dev, + "[boot_next] %08x = %08x\n", + addr + 4, pread_w(addr + 4)); + sim_debug(HA_TRACE, &ha_dev, + "[boot_next] %08x = %08x\n", + addr + 8, pread_w(addr + 8)); + sim_debug(HA_TRACE, &ha_dev, + "[boot_next] %08x = %08x\n", + addr + 12, pread_w(addr + 12)); + sim_debug(HA_TRACE, &ha_dev, + "[boot_next] %08x = %08x\n", + addr + 16, pread_w(addr + 16)); + + + if (target < 0) { + ha_state.reply.status = CIO_TIMEOUT; + sim_activate_abs(cio_unit, 250); + return; + } + + uptr = &ha_unit[target]; + + if (!(uptr->flags & UNIT_ATT)) { + ha_state.reply.status = CIO_TIMEOUT; + sim_activate_abs(cio_unit, 250); + return; + } + + dev = (SCSI_DEV *)uptr->up7; + block = pread_w(addr); /* Logical block we've been asked to read */ + addr = pread_w(addr + 4); /* Dereference the pointer to the destination */ + + switch(dev->devtype) { + case SCSI_TAPE: + ha_read_block_tape(uptr, addr); + break; + case SCSI_DISK: + ha_read_block_disk(uptr, target, addr, block); + break; + default: + sim_debug(HA_TRACE, &ha_dev, + "[HA_READ_BLOCK] Cannot read block %d on target %d (not disk or tape)\n", + block, target); + ha_state.reply.status = CIO_SUCCESS; + break; + } + + sim_activate_abs(cio_unit, 5000); + + break; + case HA_CNTRL: + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] SCSI CONTROL (subdev=%02x addr=%08x)\n", + subdev, addr); + + ha_build_req(subdev, express); + ha_ctrl(); + sim_activate_abs(cio_unit, 1000); + break; + case HA_VERS: + /* + * Get Host Adapter Version + */ + + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] SCSI GET VERSION (addr=%08x len=%08x)\n", + addr, len); + + pwrite_w(addr, HA_VERSION); + ha_state.reply.status = CIO_SUCCESS; + sim_activate_abs(cio_unit, 5000); + + break; + case HA_DL_EEDT: + /* + * This is a request to download the Extended Equipped Device + * Table from the host adapter to the 3B2 main memory. + */ + + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] SCSI DOWNLOAD EDT (%d bytes to address %08x)\n", + len, addr); + + for (i = 0; i < len; i++) { + pwrite_b(addr + i, ha_state.edt[i]); + } + + ha_state.reply.status = CIO_SUCCESS; + + sim_activate_abs(cio_unit, 5000); + + break; + case HA_UL_EEDT: + /* + * This is a request to upload the Extended Equipped Device + * Table from the 3B2 main memory to the host adapter + */ + + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] SCSI UPLOAD EDT (%d bytes from address %08x)\n", + len, addr); + + for (i = 0; i < len; i++) { + ha_state.edt[i] = pread_b(addr + i); + } + + ha_state.reply.status = CIO_SUCCESS; + + sim_activate_abs(cio_unit, 5000); + + break; + case HA_EDSD: + /* + * This command is used to determine which TCs are attached to + * the SCSI bus, and what LUNs they support. A "1" in a slot + * means that the target is not a direct block device. Since we + * only support direct block devices, we just put "0" in each + * slot. + */ + + sim_debug(HA_TRACE, &ha_dev, + "[%08x] [ha_cmd] SCSI EXTENDED DSD.\n", + R[NUM_PC]); + + ha_state.reply.status = CIO_SUCCESS; + ha_state.reply.addr = addr; + ha_state.reply.len = 9; + + for (i = 0; i < 8; i++) { + uptr = &ha_unit[i]; + pwrite_b(addr + i, (uptr->flags & UNIT_ATT) ? 1 : 0); + } + + pwrite_b(addr + 8, HA_SCSI_ID); /* ID of the card */ + + sim_activate_abs(cio_unit, 200); + + break; + case 0x45: + scsi_reset(&ha_bus); + + ha_state.reply.status = CIO_SUCCESS; + ha_state.reply.addr = addr; + ha_state.reply.len = 0; + + sim_activate_abs(cio_unit, 2500); + break; + default: + sim_debug(HA_TRACE, &ha_dev, + "[%08x] *** SCSI WARNING: UNHANDLED OPCODE 0x%02x\n", + R[NUM_PC], op); + + ha_state.reply.status = CIO_FAILURE; + + sim_activate_abs(cio_unit, 200); + } + + sim_debug(HA_TRACE, &ha_dev, + "[ha_cmd] ---------------------------[END]----------------------------------\n"); + +} + +/* + * Handle a raw SCSI control message. + */ +void ha_ctrl() +{ + volatile t_bool txn_done; + uint32 i, j; + uint32 plen, ha_ptr; + uint32 in_len, out_len; + uint8 lu, status; + uint8 msgi_buf[64]; + uint32 msgi_len; + uint32 to_read; + + sim_debug(HA_TRACE, &ha_dev, + "[ha_ctrl] [HA_REQ] TC=%d LU=%d TIMEOUT=%d DLEN=%d\n", + ha_state.request.tc, ha_state.request.lu, ha_state.request.timeout, ha_state.request.dlen); + + sim_debug(HA_TRACE, &ha_dev, + "[ha_ctrl] [HA_REQ] CMD_LEN=%d CMD=<%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x>\n", + ha_state.request.cmd_len, + ha_state.request.cmd[0], ha_state.request.cmd[1], + ha_state.request.cmd[2], ha_state.request.cmd[3], + ha_state.request.cmd[4], ha_state.request.cmd[5], + ha_state.request.cmd[6], ha_state.request.cmd[7], + ha_state.request.cmd[8], ha_state.request.cmd[9]); + + in_len = out_len = 0; + + /* + * These ops need special handling. + */ + switch(ha_state.request.op) { + case HA_TESTRDY: + /* Fail early if LU is set */ + if (ha_state.request.lu) { + HA_STAT(HA_CKCON, CIO_TIMEOUT); + return; + } + break; + case HA_FORMAT: /* Not yet handled by sim_scsi library */ + case HA_VERIFY: /* Not yet handled by sim_scsi library */ + /* Just mimic success */ + HA_STAT(HA_GOOD, CIO_SUCCESS); + return; + } + + /* Get the bus's attention */ + if (!scsi_arbitrate(&ha_bus, HA_SCSI_ID)) { + HA_STAT(HA_CKCON, CIO_TIMEOUT); + return; + } + + scsi_set_atn(&ha_bus); + + if (!scsi_select(&ha_bus, ha_state.request.tc)) { + HA_STAT(HA_CKCON, CIO_TIMEOUT); + scsi_release(&ha_bus); + return; + } + + /* Select the correct LU */ + lu = 0x80 | ha_state.request.lu; + scsi_write(&ha_bus, &lu, 1); + + txn_done = FALSE; + + while (!txn_done) { + switch(ha_bus.phase) { + case SCSI_CMD: + plen = scsi_write(&ha_bus, ha_state.request.cmd, ha_state.request.cmd_len); + if (plen < ha_state.request.cmd_len) { + HA_STAT(HA_CKCON, CIO_SUCCESS); + scsi_release(&ha_bus); + return; + } + break; + case SCSI_DATI: + /* This is a read */ + in_len = scsi_read(&ha_bus, ha_buf, HA_MAXFR); + + sim_debug(HA_TRACE, &ha_dev, + "[ha_ctrl] SCSI_DATI: Consumed %d (0x%X) bytes to ha_buf in SCSI read.\n", + in_len, in_len); + + /* We need special handling based on the op code */ + switch(ha_state.request.op) { + case HA_READ: + case HA_READEXT: + ha_ptr = 0; + + for (i = 0; i < ha_state.request.dlen; i++) { + /* + * Consume the lesser of: + * - The total bytes we consumed, or: + * - The length of the current block + */ + to_read = MIN(ha_state.request.daddr[i].len, in_len); + + sim_debug(HA_TRACE, &ha_dev, + "[(%02x) TC%d,LU%d] DATI: Processing %d bytes to address %08x...\n", + ha_state.request.op, ha_state.request.tc, ha_state.request.lu, to_read, ha_state.request.daddr[i].addr); + + for (j = 0; j < to_read; j++) { + pwrite_b(ha_state.request.daddr[i].addr + j, ha_buf[ha_ptr++]); + } + + if (in_len >= to_read) { + in_len -= to_read; + } else { + /* Nothing left to write */ + break; + } + } + + break; + default: + sim_debug(HA_TRACE, &ha_dev, + "[(%02x) TC%d,LU%d] DATI: Processing %d bytes to address %08x...\n", + ha_state.request.op, ha_state.request.tc, + ha_state.request.lu, in_len, + ha_state.request.daddr[0].addr); + for (i = 0; i < in_len; i++) { + sim_debug(HA_TRACE, &ha_dev, "[%04x] [DATI] 0x%02x\n", i, ha_buf[i]); + pwrite_b(ha_state.request.daddr[0].addr + i, ha_buf[i]); + } + + break; + } + + break; + case SCSI_DATO: + /* This is a write */ + ha_ptr = 0; + out_len = 0; + ha_state.reply.len = ha_state.request.dlen; + + for (i = 0; i < ha_state.request.dlen; i++) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_ctrl] [%d] DATO: Writing %d bytes to ha_buf.\n", + i, ha_state.request.daddr[i].len); + + for (j = 0; j < ha_state.request.daddr[i].len; j++) { + ha_buf[ha_ptr++] = pread_b(ha_state.request.daddr[i].addr + j); + } + + out_len += ha_state.request.daddr[i].len; + } + + if (ha_state.request.op == HA_WRITE || ha_state.request.op == HA_WRTEXT) { + /* If total len is not on a block boundary, we have to + bump it up in order to write the whole block. */ + if (out_len % HA_BLKSZ) { + out_len = out_len + (HA_BLKSZ - (out_len % HA_BLKSZ)); + } + } + + scsi_write(&ha_bus, ha_buf, out_len); + + sim_debug(HA_TRACE, &ha_dev, + "[ha_ctrl] SCSI Write of %08x (%d) bytes Complete\n", + out_len, out_len); + break; + case SCSI_STS: + scsi_read(&ha_bus, &status, 1); + sim_debug(HA_TRACE, &ha_dev, + "[ha_ctrl] STATUS BYTE: %02x\n", + status); + break; + case SCSI_MSGI: + msgi_len = scsi_read(&ha_bus, msgi_buf, 64); + sim_debug(HA_TRACE, &ha_dev, + "[ha_ctrl] MESSAGE IN LENGTH %d\n", + msgi_len); + + for (i = 0; i < msgi_len; i++) { + sim_debug(HA_TRACE, &ha_dev, + "[ha_ctrl] MSGI[%02d] = %02x\n", + i, msgi_buf[i]); + } + + txn_done = TRUE; + break; + } + } + + if (ha_bus.sense_info) { + sim_debug(HA_TRACE, &ha_dev, "[ha_ctrl] SENSE INFO=%d, CKCON.\n", ha_bus.sense_info); + HA_STAT(HA_CKCON, 0x60); + } else { + sim_debug(HA_TRACE, &ha_dev, "[ha_ctrl] NO SENSE INFO.\n"); + HA_STAT(HA_GOOD, CIO_SUCCESS); + } + + /* Release the bus */ + scsi_release(&ha_bus); +} + +void ha_fcm_express() +{ + uint32 rqp, cqp, cqs; + + rqp = cio[ha_state.cid].rqp; + cqp = cio[ha_state.cid].cqp; + cqs = cio[ha_state.cid].cqs; + + /* Write the fast completion entry. */ + pwrite_b(cqp + cq_offset, ha_state.reply.status); + pwrite_b(cqp + cq_offset + 1, ha_state.reply.op); + pwrite_b(cqp + cq_offset + 2, ha_state.reply.subdev); + pwrite_b(cqp + cq_offset + 3, ha_state.reply.ssb); + + sim_debug(HA_TRACE, &ha_dev, + "[ha_fcm_express] stat=%02x, op=%02x (%d), cq_index=%d target=%d, lun=%d, ssb=%02x\n", + ha_state.reply.status, ha_state.reply.op, ha_state.reply.op, + (cq_offset / 4), + FC_TC(ha_state.reply.subdev), FC_LU(ha_state.reply.subdev), + ha_state.reply.ssb); + + if (ha_state.pump_state == PUMP_COMPLETE && cqs > 0) { + cq_offset = (cq_offset + 4) % (cqs * 4); + } else { + cq_offset = 0; + } +} + +t_stat ha_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + if (val < 0 || cptr || val > HA_MAX_DTYPE) { + return SCPE_ARG; + } + + if (uptr->flags & UNIT_ATT) { + return SCPE_ALATT; + } + + uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (val << UNIT_V_DTYPE); + uptr->capac = (t_addr) ha_tab[val].lbn; + scsi_set_unit(&ha_bus, uptr, &ha_tab[val]); + scsi_reset_unit(uptr); + return SCPE_OK; +} + +t_stat ha_show_type(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + fprintf(st, "%s", ha_tab[GET_DTYPE(uptr->flags)].name); + + return SCPE_OK; +} + +/* Used for debugging only */ +/* TODO: Remove after testing */ +static void dump_edt() +{ + uint8 tc_size, lu_size, num_tc, num_lu, i, j; + + uint32 offset; + + char name[10]; + + sim_debug(HA_TRACE, &ha_dev, + "[EDT] Sanity: %08x\n", + ATOW(ha_state.edt, 0)); + + sim_debug(HA_TRACE, &ha_dev, + "[EDT] Version: %d\n", + ha_state.edt[4]); + + sim_debug(HA_TRACE, &ha_dev, + "[EDT] Slot: %d\n", + ha_state.edt[5]); + + sim_debug(HA_TRACE, &ha_dev, + "[EDT] Max TC: %d\n", + ha_state.edt[6]); + + sim_debug(HA_TRACE, &ha_dev, + "[EDT] TC Size: %d\n", + ha_state.edt[7]); + + sim_debug(HA_TRACE, &ha_dev, + "[EDT] Max LUs: %d\n", + ha_state.edt[8]); + + sim_debug(HA_TRACE, &ha_dev, + "[EDT] LU Size: %d\n", + ha_state.edt[9]); + + sim_debug(HA_TRACE, &ha_dev, + "[EDT] Equipped TCs: %d\n", + ha_state.edt[10]); + + tc_size = ha_state.edt[7]; + lu_size = ha_state.edt[9]; + num_tc = ha_state.edt[10] + 1; + + for (i = 0; i < num_tc; i++) { + offset = 12 + (tc_size * i); + num_lu = ha_state.edt[offset + 17]; + + strncpy(name, ((const char *)ha_state.edt + offset + 4), 10); + + sim_debug(HA_TRACE, &ha_dev, + "[EDT] -------------------------\n"); + sim_debug(HA_TRACE, &ha_dev, + "[EDT] [TC%d] Major Number: %d\n", + i, ATOW(ha_state.edt, offset)); + sim_debug(HA_TRACE, &ha_dev, + "[EDT] [TC%d] Name: %s\n", + i, name); + sim_debug(HA_TRACE, &ha_dev, + "[EDT] [TC%d] Type: %d\n", + i, ATOH(ha_state.edt, offset + 14)); + sim_debug(HA_TRACE, &ha_dev, + "[EDT] [TC%d] Equipped?: %d\n", + i, ha_state.edt[offset + 16]); + sim_debug(HA_TRACE, &ha_dev, + "[EDT] [TC%d] Equipped LUs: %d\n", + i, ha_state.edt[offset + 17]); + sim_debug(HA_TRACE, &ha_dev, + "[EDT] [TC%d] Maximum LUs: %d\n", + i, ha_state.edt[offset + 18]); + sim_debug(HA_TRACE, &ha_dev, + "[EDT] [TC%d] LU Index: %04x\n", + i, ATOH(ha_state.edt, offset + 20)); + + offset = ATOH(ha_state.edt, offset + 20); + + for (j = 0; j < num_lu; j++) { + + offset = offset + (j * lu_size); + + sim_debug(HA_TRACE, &ha_dev, + "[EDT] -------------------------\n"); + sim_debug(HA_TRACE, &ha_dev, + "[EDT] [TC%d,LU%d] LU #: %d\n", + i, j, ha_state.edt[offset]); + sim_debug(HA_TRACE, &ha_dev, + "[EDT] [TC%d,LU%d] PD Type: 0x%02x\n", + i, j, ha_state.edt[offset + 1]); + sim_debug(HA_TRACE, &ha_dev, + "[EDT] [TC%d,LU%d] Dev Type: 0x%02x\n", + i, j, ha_state.edt[offset + 2] >> 1); + sim_debug(HA_TRACE, &ha_dev, + "[EDT] [TC%d,LU%d] Removable?: %d\n", + i, j, ha_state.edt[offset + 2] & 1); + } + + } + +} diff --git a/3B2/3b2_scsi.h b/3B2/3b2_scsi.h new file mode 100644 index 00000000..b600e85f --- /dev/null +++ b/3B2/3b2_scsi.h @@ -0,0 +1,260 @@ +/* 3b2_scsi.h: AT&T 3B2 SCSI (CM195W) Host Adapter + + Copyright (c) 2020, 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_SCSI_H_ +#define _3B2_SCSI_H_ + +#include "sim_defs.h" + +/* CIO Opcodes */ +#define HA_BOOT 0x0a +#define HA_READ_BLK 0x0b +#define HA_CNTRL 0x20 +#define HA_VERS 0x40 +#define HA_DL_EEDT 0x42 +#define HA_UL_EEDT 0x43 +#define HA_EDSD 0x44 +#define HA_RESET 0x45 + +#define HA_TESTRDY 0x00 +#define HA_FORMAT 0x04 +#define HA_WRITE 0x0a +#define HA_INQUIRY 0x12 +#define HA_MODESNS 0x1a +#define HA_RDCPCTY 0x25 +#define HA_READ 0x08 +#define HA_READEXT 0x28 +#define HA_WRTEXT 0x2a +#define HA_VERIFY 0x2f + +#define HA_PDLS_OFF 0x28 + +/* CIO Status */ +#define CIO_TIMEOUT 0x65 + +#define HA_BOOT_ADDR 0x2004000 +#define HA_PDINFO_ADDR 0x2004400 + +#define HA_ID 0x0100 +#define HA_IPL 12 + +#define HA_GOOD 0x00 +#define HA_CKCON 0x02 + +#define HA_DSD_DISK 0x100 +#define HA_DSD_TAPE 0x101 + +#define HA_VERSION 0x01 + +#define SCQRESIZE 24 +#define RAPP_LEN (SCQRESIZE - 8) +#define SCQCESIZE 16 +#define CAPP_LEN (SCQCESIZE - 8) + +#define FC_TC(x) (((x) >> 3) & 7) +#define FC_LU(x) ((x) & 7) + +#define HA_EDT_LEN 1024 + +#define HA_BLKSZ 512 + +#define HA_MAX_CMD 12 +#define INQUIRY_MAX 36 + +#define HA_STAT(ha_stat,cio_stat) { \ + ha_state.reply.ssb = (ha_stat); \ + ha_state.reply.status = (cio_stat); \ + } + +#define HA_MAX_DTYPE 1 + +/* CDC Wren IV 327 MB Hard Disk (AT&T KS-23483,L3) */ +#define SD327_DTYPE 0 +#define SD327_PQUAL 0x00 +#define SD327_SCSI 1 +#define SD327_BLK 512 +#define SD327_LBN 640396 +#define SD327_MANU "AT&T" +#define SD327_DESC "KS23483" +#define SD327_REV "0001" /* TODO: Find real rev */ +#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) */ +#define ST120_DTYPE 1 +#define ST120_PQUAL 0x00 +#define ST120_SCSI 1 +#define ST120_BLK 512 +#define ST120_LBN 266004 +#define ST120_MANU "WANGTEK" +#define ST120_DESC "KS23465" +#define ST120_REV "CX17" +#define ST120_DENS 5 + +#define UNIT_V_DTYPE (SCSI_V_UF + 0) +#define UNIT_M_DTYPE 0x1f +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) + +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) +#define HA_DISK(d) { \ + SCSI_DISK, d##_PQUAL, d##_SCSI, FALSE, d##_BLK, \ + d##_LBN, d##_MANU, d##_DESC, d##_REV, #d, 0 \ + } +#define HA_TAPE(d) { \ + SCSI_TAPE, d##_PQUAL, d##_SCSI, TRUE, d##_BLK, \ + d##_LBN, d##_MANU, d##_DESC, d##_REV, #d, 0 \ + } +#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_EXPRESS 1 +#define HA_JOB_FULL 2 + +typedef uint8 ha_jobtype; + +typedef struct { + uint32 addr; + uint32 len; +} haddr; + +/* + * SCSI Command Request + */ +typedef struct { + uint8 op; /* Destructured from the cmd byte array */ + uint8 tc; + uint8 lu; + uint32 timeout; + + uint8 dlen; + haddr daddr[48]; /* Support up to 48 transfer addresses */ + + uint32 dma_lst; + uint16 cmd_len; + uint8 cmd[HA_MAX_CMD]; +} ha_req; + +/* + * SCSI Command Response + */ +typedef struct { + ha_jobtype type; /* Job type */ + t_bool pending; /* Pending or completed? */ + uint8 status; /* Result Status */ + uint8 op; /* Command Opcode */ + uint8 subdev; /* XXTTTLLL; T=Target, L=LUN */ + uint8 ssb; /* SCSI Status Byte */ + uint32 addr; /* Response address */ + uint32 len; /* Response length */ +} ha_resp; + +#define PUMP_NONE 0 +#define PUMP_SYSGEN 1 +#define PUMP_COMPLETE 2 + +/* + * General SCSI HA internal state. + */ +typedef struct { + uint8 cid; /* Card Backsplane Slot # */ + 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 */ + uint8 edt[HA_EDT_LEN]; /* Equipped Device Table */ + ha_req request; /* Current job request */ + ha_resp reply; /* Current job reply */ +} HA_STATE; + +t_stat ha_show_type(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat ha_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat ha_reset(DEVICE *dptr); +t_stat ha_svc(UNIT *uptr); +t_stat ha_rq_svc(UNIT *uptr); +t_stat ha_attach(UNIT *uptr, CONST char *cptr); +t_stat ha_detach(UNIT *uptr); + +void ha_fast_queue_check(); +void ha_sysgen(uint8 cid); +void ha_express(uint8 cid); +void ha_full(uint8 cid); + +/* Fast Completion */ + +void ha_fcm_express(); + +#endif /* _3B2_SCSI_H_ */ diff --git a/3B2/3b2_rev2_stddev.c b/3B2/3b2_stddev.c similarity index 81% rename from 3B2/3b2_rev2_stddev.c rename to 3B2/3b2_stddev.c index f496d79b..b4ceb2ee 100644 --- a/3B2/3b2_rev2_stddev.c +++ b/3B2/3b2_stddev.c @@ -39,7 +39,7 @@ */ #include "3b2_defs.h" -#include "3b2_rev2_stddev.h" +#include "3b2_stddev.h" DEBTAB sys_deb_tab[] = { { "INIT", INIT_MSG, "Init" }, @@ -57,171 +57,10 @@ uint32 *NVRAM = NULL; int32 tmxr_poll = 16667; -/* CSR */ - -uint16 csr_data; - -BITFIELD csr_bits[] = { - BIT(IOF), - BIT(DMA), - BIT(DISK), - BIT(UART), - BIT(PIR9), - BIT(PIR8), - BIT(CLK), - BIT(IFLT), - BIT(ITIM), - BIT(FLOP), - BIT(NA), - BIT(LED), - BIT(ALGN), - BIT(RRST), - BIT(PARE), - BIT(TIMO), - ENDBITS -}; - -UNIT csr_unit = { - UDATA(NULL, UNIT_FIX, CSRSIZE) -}; - -REG csr_reg[] = { - { HRDATADF(DATA, csr_data, 16, "CSR Data", csr_bits) }, - { NULL } -}; - -DEVICE csr_dev = { - "CSR", &csr_unit, csr_reg, NULL, - 1, 16, 8, 4, 16, 32, - &csr_ex, &csr_dep, &csr_reset, - NULL, NULL, NULL, NULL, - DEV_DEBUG, 0, sys_deb_tab -}; - -t_stat csr_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) -{ - return SCPE_OK; -} - -t_stat csr_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw) -{ - return SCPE_OK; -} - -t_stat csr_reset(DEVICE *dptr) -{ - csr_data = 0; - return SCPE_OK; -} - -uint32 csr_read(uint32 pa, size_t size) -{ - uint32 reg = pa - CSRBASE; - - sim_debug(READ_MSG, &csr_dev, - "[%08x] CSR=%04x\n", - R[NUM_PC], csr_data); - - switch (reg) { - case 0x2: - if (size == 8) { - return (csr_data >> 8) & 0xff; - } else { - return csr_data; - } - case 0x3: - return csr_data & 0xff; - default: - return 0; - } -} - -void csr_write(uint32 pa, uint32 val, size_t size) -{ - uint32 reg = pa - CSRBASE; - - switch (reg) { - case 0x03: /* Clear Bus Timeout Error */ - csr_data &= ~CSRTIMO; - break; - case 0x07: /* Clear Memory Parity Error */ - csr_data &= ~CSRPARE; - break; - case 0x0b: /* Set System Reset Request */ - full_reset(); - cpu_boot(0, &cpu_dev); - break; - case 0x0f: /* Clear Memory Alignment Fault */ - csr_data &= ~CSRALGN; - break; - case 0x13: /* Set Failure LED */ - csr_data |= CSRLED; - break; - case 0x17: /* Clear Failure LED */ - csr_data &= ~CSRLED; - break; - case 0x1b: /* Set Floppy Motor On */ - csr_data |= CSRFLOP; - break; - case 0x1f: /* Clear Floppy Motor On */ - csr_data &= ~CSRFLOP; - break; - case 0x23: /* Set Inhibit Timers */ - sim_debug(WRITE_MSG, &csr_dev, - "[%08x] SET INHIBIT TIMERS\n", R[NUM_PC]); - csr_data |= CSRITIM; - break; - case 0x27: /* Clear Inhibit Timers */ - sim_debug(WRITE_MSG, &csr_dev, - "[%08x] CLEAR INHIBIT TIMERS\n", R[NUM_PC]); - - /* 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. - */ - - 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; - } - - csr_data &= ~CSRITIM; - break; - case 0x2b: /* Set Inhibit Faults */ - csr_data |= CSRIFLT; - break; - case 0x2f: /* Clear Inhibit Faults */ - csr_data &= ~CSRIFLT; - break; - case 0x33: /* Set PIR9 */ - csr_data |= CSRPIR9; - break; - case 0x37: /* Clear PIR9 */ - csr_data &= ~CSRPIR9; - break; - case 0x3b: /* Set PIR8 */ - csr_data |= CSRPIR8; - break; - case 0x3f: /* Clear PIR8 */ - csr_data &= ~CSRPIR8; - break; - default: - break; - } -} - /* NVRAM */ UNIT nvram_unit = { - UDATA(NULL, UNIT_FIX+UNIT_BINK, NVRAMSIZE) + UDATA(NULL, UNIT_FIX+UNIT_BINK, NVRSIZE) }; REG nvram_reg[] = { @@ -246,7 +85,7 @@ t_stat nvram_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) return SCPE_ARG; } - if (addr >= NVRAMSIZE) { + if (addr >= NVRSIZE) { return SCPE_NXM; } @@ -263,7 +102,7 @@ t_stat nvram_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw) return SCPE_ARG; } - if (addr >= NVRAMSIZE) { + if (addr >= NVRSIZE) { return SCPE_NXM; } @@ -275,8 +114,8 @@ t_stat nvram_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw) t_stat nvram_reset(DEVICE *dptr) { if (NVRAM == NULL) { - NVRAM = (uint32 *)calloc(NVRAMSIZE >> 2, sizeof(uint32)); - memset(NVRAM, 0, sizeof(uint32) * NVRAMSIZE >> 2); + NVRAM = (uint32 *)calloc(NVRSIZE >> 2, sizeof(uint32)); + memset(NVRAM, 0, sizeof(uint32) * NVRSIZE >> 2); nvram_unit.filebuf = NVRAM; } @@ -345,7 +184,7 @@ t_stat nvram_detach(UNIT *uptr) uint32 nvram_read(uint32 pa, size_t size) { - uint32 offset = pa - NVRAMBASE; + uint32 offset = pa - NVRBASE; uint32 data = 0; uint32 sc = (~(offset & 3) << 3) & 0x1f; @@ -370,7 +209,7 @@ uint32 nvram_read(uint32 pa, size_t size) void nvram_write(uint32 pa, uint32 val, size_t size) { - uint32 offset = pa - NVRAMBASE; + uint32 offset = pa - NVRBASE; uint32 index = offset >> 2; uint32 sc, mask; @@ -464,6 +303,61 @@ t_stat timer_reset(DEVICE *dptr) { return SCPE_OK; } +static void timer_activate(uint8 ctrnum) +{ + struct timer_ctr *ctr; + + ctr = &TIMERS[ctrnum]; + + switch (ctrnum) { + case TIMER_SANITY: +#ifdef REV3 + if ((csr_data & CSRISTIM) == 0) { + 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 { + sim_debug(EXECUTE_MSG, &timer_dev, + "[%08x] SANITY TIMER: Currently disabled, not starting\n", + R[NUM_PC]); + } +#endif + 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: +#ifdef REV3 + 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]); + } +#endif + 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; @@ -478,6 +372,18 @@ t_stat timer_set_shutdown(UNIT *uptr, int32 val, CONST char* cptr, void* desc) 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 */ @@ -687,6 +593,21 @@ void timer_write(uint32 pa, uint32 val, size_t size) } } +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; + } +} + /* * MM58174A Time Of Day Clock * @@ -955,3 +876,54 @@ t_stat tod_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr return SCPE_OK; } + +#if defined(REV3) + +/* + * Fault Register + * + * The Fault Register is composed of two 32-bit registers at addresses + * 0x4C000 and 0x4D000. These latch state of the last address to cause + * a CPU fault. + * + * Bits 00-25: Physical memory address bits 00-25 + */ + +uint32 flt_1 = 0; +uint32 flt_2 = 0; + +UNIT flt_unit = { + UDATA(NULL, UNIT_FIX+UNIT_BINK, 8) +}; + +REG flt_reg[] = { + { NULL } +}; + +DEVICE flt_dev = { + "FLT", &flt_unit, flt_reg, NULL, + 1, 16, 8, 4, 16, 32, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, sys_deb_tab, NULL, NULL, + NULL, NULL, NULL, + NULL +}; + +uint32 flt_read(uint32 pa, size_t size) +{ + sim_debug(READ_MSG, &flt_dev, + "[%08x] Read from FLT Register at %x\n", + R[NUM_PC], pa); + return 0; +} + +void flt_write(uint32 pa, uint32 val, size_t size) +{ + sim_debug(WRITE_MSG, &flt_dev, + "[%08x] Write to FLT Register at %x (val=%x)\n", + R[NUM_PC], pa, val); + return; +} + +#endif diff --git a/3B2/3b2_rev2_stddev.h b/3B2/3b2_stddev.h similarity index 92% rename from 3B2/3b2_rev2_stddev.h rename to 3B2/3b2_stddev.h index f16548b7..48c10843 100644 --- a/3B2/3b2_rev2_stddev.h +++ b/3B2/3b2_stddev.h @@ -77,10 +77,13 @@ void nvram_write(uint32 pa, uint32 val, size_t size); 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); /* CSR */ t_stat csr_svc(UNIT *uptr); @@ -120,4 +123,11 @@ t_stat tod_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr uint32 tod_read(uint32 pa, size_t size); void tod_write(uint32, uint32 val, size_t size); +#if defined(REV3) +/* Fault Register */ + +uint32 flt_read(uint32 pa, size_t size); +void flt_write(uint32 pa, uint32 val, size_t size); +#endif + #endif /* _3B2_REV2_SYSDEV_H_ */ diff --git a/3B2/3b2_sys.c b/3B2/3b2_sys.c new file mode 100644 index 00000000..3f578dba --- /dev/null +++ b/3B2/3b2_sys.c @@ -0,0 +1,157 @@ +/* 3b2_sys.c: AT&T 3B2 common system definitions + + 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. +*/ + +#include "3b2_defs.h" + +REG *sim_PC = &cpu_reg[NUM_PC]; + +/* All opcodes are 1 or 2 bytes. Operands may be up to 6 bytes, and + there may be up to 3 operands, for a maximum of 20 bytes */ +int32 sim_emax = 20; + +const char *sim_stop_messages[SCPE_BASE] = { + "Unknown error", + "Reserved Instruction", + "Breakpoint", + "Invalid Opcode", + "IRQ", + "Exception/Trap", + "Exception Stack Too Deep", + "Unimplemented MMU Feature", + "System Powered Off", + "Infinite Loop", + "Simulator Error" +}; + +t_stat sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) +{ + int32 i; + uint32 addr = 0; + int32 cnt = 0; + + if ((*cptr != 0) || (flag != 0)) { + return SCPE_ARG; + } + + addr = R[NUM_PC]; + + while ((i = getc (fileref)) != EOF) { + pwrite_b(addr, (uint8)i); + addr++; + cnt++; + } + + printf ("%d Bytes loaded.\n", cnt); + + return SCPE_OK; +} + +t_stat parse_sym(CONST char *cptr, t_addr exta, UNIT *uptr, t_value *val, int32 sw) +{ + DEVICE *dptr; + t_stat r; + int32 k, num, vp; + int32 len = 4; + + if (sw & (int32) SWMASK ('B')) { + len = 1; + } else if (sw & (int32) SWMASK ('H')) { + len = 2; + } else if (sw & (int32) SWMASK ('W')) { + len = 4; + } + + // Parse cptr + num = (int32) get_uint(cptr, 16, WORD_MASK, &r); + + if (r != SCPE_OK) { + return r; + } + + if (uptr == NULL) { + uptr = &cpu_unit; + } + + dptr = find_dev_from_unit(uptr); + + if (dptr == NULL) { + return SCPE_IERR; + } + + vp = 0; + for (k = len - 1; k >= 0; k--) { + val[vp++] = (num >> (k * 8)) & 0xff; + } + + return -(vp - 1); +} + +t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) +{ + uint32 len = 4; + int32 k, vp, num; + unsigned int c; + + num = 0; + vp = 0; + + if (sw & (int32) SWMASK('M')) { + return fprint_sym_m(of, addr, val); + } + + if (sw & (int32) SWMASK ('B')) { + len = 1; + } else if (sw & (int32) SWMASK ('H')) { + len = 2; + } else if (sw & (int32) SWMASK ('W')) { + len = 4; + } + + if (sw & (int32) SWMASK('C')) { + len = 16; + for (k = (int32) len - 1; k >= 0; k--) { + c = (unsigned int)val[vp++]; + if (c >= 0x20 && c < 0x7f) { + fprintf(of, "%c", c); + } else { + fprintf(of, "."); + } + } + return -(vp - 1); + } + + for (k = len - 1; k >= 0; k--) { + num = num | (((int32) val[vp++]) << (k * 8)); + } + + fprint_val(of, (uint32) num, 16, len * 8, PV_RZRO); + + return -(vp - 1); +} diff --git a/3B2/3b2_rev2_sys.h b/3B2/3b2_sys.h similarity index 100% rename from 3B2/3b2_rev2_sys.h rename to 3B2/3b2_sys.h diff --git a/Visual Studio Projects/3B2-600.vcproj b/Visual Studio Projects/3B2-600.vcproj new file mode 100644 index 00000000..162b7054 --- /dev/null +++ b/Visual Studio Projects/3B2-600.vcproj @@ -0,0 +1,627 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/3B2.vcproj b/Visual Studio Projects/3B2.vcproj index 7f43cf8e..b011d868 100644 --- a/Visual Studio Projects/3B2.vcproj +++ b/Visual Studio Projects/3B2.vcproj @@ -41,7 +41,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../3B2/;./;../;../slirp;../slirp_glue;../slirp_glue/qemu;../slirp_glue/qemu/win32/include;../../windows-build/include;;../../windows-build/include/SDL2" - PreprocessorDefinitions="_CRT_NONSTDC_NO_WARNINGS;SIM_BUILD_TOOL=simh-Visual-Studio-Project;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;SIM_NEED_GIT_COMMIT_ID;HAVE_PCRE_H;PCRE_STATIC;USE_INT64;USE_ADDR64;USE_SHARED;PTW32_STATIC_LIB;SIM_ASYNCH_IO;USE_READER_THREAD;HAVE_SLIRP_NETWORK;USE_SIMH_SLIRP_DEBUG" + PreprocessorDefinitions="_CRT_NONSTDC_NO_WARNINGS;SIM_BUILD_TOOL=simh-Visual-Studio-Project;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;SIM_NEED_GIT_COMMIT_ID;HAVE_PCRE_H;PCRE_STATIC;USE_INT64;USE_ADDR64;USE_SHARED;PTW32_STATIC_LIB;SIM_ASYNCH_IO;USE_READER_THREAD;HAVE_SLIRP_NETWORK;USE_SIMH_SLIRP_DEBUG;REV2" KeepComments="false" BasicRuntimeChecks="0" RuntimeLibrary="1" @@ -125,7 +125,7 @@ OmitFramePointers="true" WholeProgramOptimization="true" AdditionalIncludeDirectories="../3B2/;./;../;../slirp;../slirp_glue;../slirp_glue/qemu;../slirp_glue/qemu/win32/include;../../windows-build/include;;../../windows-build/include/SDL2" - PreprocessorDefinitions="_CRT_NONSTDC_NO_WARNINGS;SIM_BUILD_TOOL=simh-Visual-Studio-Project;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;SIM_NEED_GIT_COMMIT_ID;HAVE_PCRE_H;PCRE_STATIC;USE_INT64;USE_ADDR64;USE_SHARED;PTW32_STATIC_LIB;SIM_ASYNCH_IO;USE_READER_THREAD;HAVE_SLIRP_NETWORK;USE_SIMH_SLIRP_DEBUG" + PreprocessorDefinitions="_CRT_NONSTDC_NO_WARNINGS;SIM_BUILD_TOOL=simh-Visual-Studio-Project;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;SIM_NEED_GIT_COMMIT_ID;HAVE_PCRE_H;PCRE_STATIC;USE_INT64;USE_ADDR64;USE_SHARED;PTW32_STATIC_LIB;SIM_ASYNCH_IO;USE_READER_THREAD;HAVE_SLIRP_NETWORK;USE_SIMH_SLIRP_DEBUG;REV2" StringPooling="true" RuntimeLibrary="0" EnableFunctionLevelLinking="true" @@ -219,6 +219,10 @@ RelativePath="..\3B2\3b2_iu.c" > + + @@ -227,6 +231,10 @@ RelativePath="..\3B2\3b2_rev2_mmu.c" > + + @@ -240,7 +248,11 @@ > + + + + @@ -523,6 +539,10 @@ RelativePath="..\3B2\3b2_rev2_mmu.h" > + + @@ -536,7 +556,11 @@ > + +