From c0beba549820a1995f8e3ed5056368c8a3ddd805 Mon Sep 17 00:00:00 2001 From: Seth Morabito Date: Mon, 9 Aug 2021 11:07:21 -0700 Subject: [PATCH] 3B2: Rev 3 Development Base This change introduces initial support for the AT&T 3B2 Rev 3 platform, based around the WE32200 CPU with up to 64MB of RAM and SCSI disk and tape support. This simulator is experimental and not yet supported. It will not be built by default, but can be built with: make 3b2-600 Or by using the 3B2-600 Windows Visual Studio project. --- 3B2/3b2_cpu.c | 385 ++++-- 3B2/3b2_cpu.h | 113 +- 3B2/3b2_ctc.c | 3 - 3B2/3b2_defs.h | 62 +- 3B2/3b2_dmac.c | 71 +- 3B2/3b2_dmac.h | 6 +- 3B2/3b2_id.h | 1 - 3B2/3b2_if.c | 23 + 3B2/3b2_if.h | 7 + 3B2/3b2_io.c | 93 +- 3B2/3b2_io.h | 4 +- 3B2/3b2_mem.c | 320 +++++ 3B2/3b2_mem.h | 62 + 3B2/3b2_rev2_csr.c | 179 +++ 3B2/3b2_rev2_csr.h | 44 + 3B2/3b2_rev2_defs.h | 19 +- 3B2/3b2_rev2_mau.c | 12 +- 3B2/3b2_rev2_mmu.c | 288 ----- 3B2/3b2_rev2_mmu.h | 12 - 3B2/3b2_rev2_sys.c | 131 +- 3B2/3b2_rev3_csr.c | 265 ++++ 3B2/3b2_rev3_csr.h | 43 + 3B2/3b2_rev3_defs.h | 138 +++ 3B2/3b2_rev3_mau.c | 31 + 3B2/3b2_rev3_mau.h | 36 + 3B2/3b2_rev3_mmu.c | 1207 +++++++++++++++++++ 3B2/3b2_rev3_mmu.h | 356 ++++++ 3B2/3b2_rev3_sys.c | 70 ++ 3B2/3b2_scsi.c | 1464 +++++++++++++++++++++++ 3B2/3b2_scsi.h | 260 ++++ 3B2/{3b2_rev2_stddev.c => 3b2_stddev.c} | 310 +++-- 3B2/{3b2_rev2_stddev.h => 3b2_stddev.h} | 10 + 3B2/3b2_sys.c | 157 +++ 3B2/{3b2_rev2_sys.h => 3b2_sys.h} | 0 Visual Studio Projects/3B2-600.vcproj | 627 ++++++++++ Visual Studio Projects/3B2.vcproj | 32 +- Visual Studio Projects/Simh.sln | 9 + makefile | 36 +- 38 files changed, 6084 insertions(+), 802 deletions(-) create mode 100644 3B2/3b2_mem.c create mode 100644 3B2/3b2_mem.h create mode 100644 3B2/3b2_rev2_csr.c create mode 100644 3B2/3b2_rev2_csr.h create mode 100644 3B2/3b2_rev3_csr.c create mode 100644 3B2/3b2_rev3_csr.h create mode 100644 3B2/3b2_rev3_defs.h create mode 100644 3B2/3b2_rev3_mau.c create mode 100644 3B2/3b2_rev3_mau.h create mode 100644 3B2/3b2_rev3_mmu.c create mode 100644 3B2/3b2_rev3_mmu.h create mode 100644 3B2/3b2_rev3_sys.c create mode 100644 3B2/3b2_scsi.c create mode 100644 3B2/3b2_scsi.h rename 3B2/{3b2_rev2_stddev.c => 3b2_stddev.c} (81%) rename 3B2/{3b2_rev2_stddev.h => 3b2_stddev.h} (92%) create mode 100644 3B2/3b2_sys.c rename 3B2/{3b2_rev2_sys.h => 3b2_sys.h} (100%) create mode 100644 Visual Studio Projects/3B2-600.vcproj 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 @@ > + +