diff --git a/3B2/3b2_cpu.c b/3B2/3b2_cpu.c index c7799d14..b86d2df4 100644 --- a/3B2/3b2_cpu.c +++ b/3B2/3b2_cpu.c @@ -1,6 +1,6 @@ -/* 3b2_cpu.c: AT&T 3B2 CPU (WE32100 and WE32200) Implementation +/* 3b2_cpu.c: WE32100 and WE32200 CPU - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -29,12 +29,17 @@ */ /* - * 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. + * This is an implementation of the WE 32100 and WE 32200 CPUs, used + * in the Rev 2 (e.g. 3B2/400) and Rev 3 (e.g. 3B2/700) AT&T 3B2 + * computers, respectively. * - * The WE32000 series of microprocessors were a fully 32-bit general - * purpose CISC architecture purposely designed to run UNIX. + * The WE 32K series of microprocessors were fully 32-bit, general + * purpose CISC architecture microprocessors with features designed to + * support UNIX as a primary operating system. In addition to the CPU, + * other members of the WE 32K chipset included: + * + * - WE 32101 and WE 32201 Memory Management Units + * - WE 32106 and WE 32206 Math Accelerator Units * * The architecture is fully described in the following books: * @@ -46,18 +51,9 @@ #include "3b2_cpu.h" #if defined(REV3) -#include "rom_rev3_bin.h" #include "3b2_if.h" -#define ROM_ARRAY BOOT_CODE_ARRAY -#define ROM_SIZE BOOT_CODE_SIZE #else -#include "rom_rev2_bin.h" -#include "rom_rev2_demon_bin.h" #include "3b2_id.h" -#define ROM_ARRAY BOOT_CODE_ARRAY_1 -#define ROM_SIZE BOOT_CODE_SIZE_1 -#define DEMON_ROM_ARRAY BOOT_CODE_ARRAY_2 -#define DEMON_ROM_SIZE BOOT_CODE_SIZE_2 #endif /* defined(REV3) */ #include "3b2_csr.h" @@ -70,9 +66,17 @@ #include "3b2_stddev.h" #include "3b2_timer.h" +/* Up to 128KB ROM allowed */ #define MAX_SUB_RETURN_SKIP 9 -uint32 rom_size = 0; +/* Kernel-privileged registers on write */ +#if defined(REV3) +#define PRIVREG(V) ((V) == NUM_PSW || (V) == NUM_PCBP || \ + (V) == NUM_ISP || (V) == NUM_PC || (V) >= 24) +#else +#define PRIVREG(V) ((V) == NUM_PSW || (V) == NUM_PCBP || \ + (V) == NUM_ISP || (V) == NUM_PC) +#endif /* Static function declarations */ static uint32 cpu_effective_address(operand * op); @@ -109,12 +113,16 @@ static SIM_INLINE void cpu_context_switch_3(uint32 pcbp); static SIM_INLINE t_bool op_is_psw(operand *op); static SIM_INLINE void add(t_uint64 a, t_uint64 b, operand *dst); static SIM_INLINE void sub(t_uint64 a, t_uint64 b, operand *dst); +#if defined(REV3) +static SIM_INLINE uint8 add_bcd(uint8 a, uint8 b); +static SIM_INLINE uint8 sub_bcd(uint8 a, uint8 b); +#endif /* RO memory. */ -uint32 *ROM = NULL; +uint8 *ROM = NULL; /* Main memory. */ -uint32 *RAM = NULL; +uint8 *RAM = NULL; /* Save environment for setjmp/longjmp */ jmp_buf save_env; @@ -142,6 +150,8 @@ uint32 R[NUM_REGISTERS]; /* Other global CPU state */ +t_bool rom_loaded = FALSE; /* True if ROM has been loaded, false otherwise */ + /* Interrupt request bitfield */ /* Note: Only the lowest 8 bits are used by Rev 2, and only the lowest 12 bits are used by Rev 3 */ @@ -154,6 +164,9 @@ int32 pc_incr = 0; /* Length (in bytes) of instruction t_bool cpu_ex_halt = FALSE; /* Flag to halt on exceptions / traps */ t_bool cpu_km = FALSE; /* If true, kernel mode has been forced for memory access */ +uint16 cpu_int_ack; /* The most recently acknowledged + interrupt */ + CTAB sys_cmd[] = { { "BOOT", &sys_boot, RU_BOOT, "bo{ot} boot simulator\n", NULL, &run_cmd_message }, @@ -340,15 +353,6 @@ static const char *att3b2_clock_precalibrate_commands[] = { NULL }; -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", @@ -375,7 +379,7 @@ MTAB cpu_mod[] = { { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "STACK", NULL, NULL, &cpu_show_stack, NULL, "Display the current stack with optional depth" }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "CIO", NULL, - NULL, &cpu_show_cio, NULL, "Display CIO configuration" }, + NULL, &cpu_show_cio, NULL, "Display backplane 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_EXBRK, UNIT_EXBRK, "Break on exceptions", "EXBRK", @@ -448,15 +452,23 @@ mnemonic ops[256] = { {0x08, 0, OP_NONE, NA, "RET", -1, -1, -1, -1}, #if defined(REV3) {0x09, 3, OP_DESC, WD, "CASWI", 0, 1, -1, 2}, + {0x0a, 0, OP_NONE, NA, "SETX", -1, -1, -1, -1}, + {0x0b, 0, OP_NONE, NA, "CLRX", -1, -1, -1, -1}, #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}, +#endif {0x0c, 2, OP_DESC, WD, "MOVTRW", 0, -1, -1, 1}, +#if defined(REV3) + {0x0d, 2, OP_DESH, HW, "TEDTH", 1, -1, -1, 0}, + {0x0e, 2, OP_DESC, HW, "PACKB", 0, -1, -1, 1}, + {0x0f, 3, OP_DESC, HW, "UNPACKB", 0, 1, -1, 2}, +#else {0x0d, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x0e, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x0f, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0x10, 1, OP_DESC, WD, "SAVE", 0, -1, -1, -1}, {0x11, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x12, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, @@ -466,13 +478,21 @@ mnemonic ops[256] = { {0x16, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x17, 2, OP_COPR, WD, "SPOPWT", -1, -1, -1, 1}, {0x18, 1, OP_DESC, WD, "RESTORE", 0, -1, -1, -1}, +#if defined(REV3) + {0x19, 2, OP_DESH, HW, "DTH", 1, -1, -1, 0}, +#else {0x19, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0x1a, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x1b, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, - {0x1c, 1, OP_DESC, WD, "SWAPWI", -1, -1, -1, 0}, /* 3-122 252 */ + {0x1c, 1, OP_DESC, WD, "SWAPWI", -1, -1, -1, 0}, +#if defined(REV3) + {0x1d, 2, OP_DESH, HW, "TGEDTH", 1, -1, -1, 0}, +#else {0x1d, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, - {0x1e, 1, OP_DESC, HW, "SWAPHI", -1, -1, -1, 0}, /* 3-122 252 */ - {0x1f, 1, OP_DESC, BT, "SWAPBI", -1, -1, -1, 0}, /* 3-122 252 */ +#endif + {0x1e, 1, OP_DESC, HW, "SWAPHI", -1, -1, -1, 0}, + {0x1f, 1, OP_DESC, BT, "SWAPBI", -1, -1, -1, 0}, {0x20, 1, OP_DESC, WD, "POPW", -1, -1, -1, 0}, {0x21, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x22, 2, OP_COPR, WD, "SPOPRS", 1, -1, -1, -1}, @@ -482,12 +502,20 @@ mnemonic ops[256] = { {0x26, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x27, 0, OP_NONE, NA, "CFLUSH", -1, -1, -1, -1}, {0x28, 1, OP_DESC, WD, "TSTW", 0, -1, -1, -1}, +#if defined(REV3) + {0x29, 2, OP_DESB, BT, "DTB", 1, -1, -1, 0}, +#else {0x29, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0x2a, 1, OP_DESC, HW, "TSTH", 0, -1, -1, -1}, {0x2b, 1, OP_DESC, BT, "TSTB", 0, -1, -1, -1}, {0x2c, 2, OP_DESC, WD, "CALL", 0, -1, -1, 1}, +#if defined(REV3) + {0x2d, 2, OP_DESH, HW, "TGDTH", 1, -1, -1, 0}, +#else {0x2d, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, - {0x2e, 0, OP_NONE, NA, "BPT", -1, -1, -1, -1}, +#endif + {0x2e, 0, OP_NONE, NA, "BPT", -1, -1, -1, -1}, /* TODO: Verify */ {0x2f, 0, OP_NONE, NA, "WAIT", -1, -1, -1, -1}, {0x30, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, /* Two-byte instructions */ {0x31, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, @@ -502,7 +530,11 @@ mnemonic ops[256] = { {0x3a, 2, OP_DESC, HW, "BITH", 0, 1, -1, -1}, {0x3b, 2, OP_DESC, BT, "BITB", 0, 1, -1, -1}, {0x3c, 2, OP_DESC, WD, "CMPW", 0, 1, -1, -1}, +#if defined(REV3) + {0x3d, 2, OP_DESH, HW, "TNEDTH", 1, -1, -1, 0}, +#else {0x3d, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0x3e, 2, OP_DESC, HW, "CMPH", 0, 1, -1, -1}, {0x3f, 2, OP_DESC, BT, "CMPB", 0, 1, -1, -1}, {0x40, 0, OP_NONE, NA, "RGEQ", -1, -1, -1, -1}, @@ -518,39 +550,51 @@ mnemonic ops[256] = { {0x4a, 1, OP_HALF, NA, "BLH", -1, -1, -1, 0}, {0x4b, 1, OP_BYTE, NA, "BLB", -1, -1, -1, 0}, {0x4c, 0, OP_NONE, NA, "RLEQ", -1, -1, -1, -1}, +#if defined(REV3) + {0x4d, 2, OP_DESB, BT, "TEDTB", 1, -1, -1, 0}, +#else {0x4d, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0x4e, 1, OP_HALF, NA, "BLEH", -1, -1, -1, 0}, {0x4f, 1, OP_BYTE, NA, "BLEB", -1, -1, -1, 0}, - {0x50, 0, OP_NONE, NA, "BGEQU", -1, -1, -1, 0}, /* aka BCC */ + {0x50, 0, OP_NONE, NA, "RGEQU", -1, -1, -1, 0}, /* aka RCC */ {0x51, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x52, 1, OP_HALF, NA, "BGEUH", -1, -1, -1, 0}, /* aka BCCH */ {0x53, 1, OP_BYTE, NA, "BGEUB", -1, -1, -1, 0}, /* aka BCCB */ - {0x54, 0, OP_NONE, NA, "RGTRU", -1, -1, -1, -1}, + {0x54, 0, OP_NONE, NA, "RGTRU", -1, -1, -1, -1}, /* TODO: Implement */ {0x55, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x56, 1, OP_HALF, NA, "BGUH", -1, -1, -1, 0}, {0x57, 1, OP_BYTE, NA, "BGUB", -1, -1, -1, 0}, - {0x58, 0, OP_NONE, NA, "RLSSU", -1, -1, -1, 0}, /* aka BCS */ + {0x58, 0, OP_NONE, NA, "RLSSU", -1, -1, -1, 0}, /* aka RCS */ /* TODO: Implement */ {0x59, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x5a, 1, OP_HALF, NA, "BLUH", -1, -1, -1, 0}, /* aka BCSH */ {0x5b, 1, OP_BYTE, NA, "BLUB", -1, -1, -1, 0}, /* aka BCSB */ {0x5c, 0, OP_NONE, NA, "RLEQU", -1, -1, -1, -1}, +#if defined(REV3) + {0x5d, 2, OP_DESB, HW, "TGEDTB", 1, -1, -1, 0}, +#else {0x5d, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0x5e, 1, OP_HALF, NA, "BLEUH", -1, -1, -1, 0}, {0x5f, 1, OP_BYTE, NA, "BLEUB", -1, -1, -1, 0}, - {0x60, 0, OP_NONE, NA, "RVC", -1, -1, -1, -1}, + {0x60, 0, OP_NONE, NA, "RVC", -1, -1, -1, -1}, /* TODO: Implement */ {0x61, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x62, 1, OP_HALF, NA, "BVCH", -1, -1, -1, 0}, {0x63, 1, OP_BYTE, NA, "BVCB", -1, -1, -1, 0}, {0x64, 0, OP_NONE, NA, "RNEQU", -1, -1, -1, -1}, {0x65, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x66, 1, OP_HALF, NA, "BNEH", -1, -1, -1, 0}, /* duplicate of 76 */ - {0x67, 1, OP_BYTE, NA, "BNEB", -1, -1, -1, 0}, /* duplicate of 77*/ - {0x68, 0, OP_NONE, NA, "RVS", -1, -1, -1, -1}, + {0x67, 1, OP_BYTE, NA, "BNEB", -1, -1, -1, 0}, /* duplicate of 77 */ + {0x68, 0, OP_NONE, NA, "RVS", -1, -1, -1, -1}, /* TODO: Implement */ {0x69, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x6a, 1, OP_HALF, NA, "BVSH", -1, -1, -1, 0}, {0x6b, 1, OP_BYTE, NA, "BVSB", -1, -1, -1, 0}, {0x6c, 0, OP_NONE, NA, "REQLU", -1, -1, -1, -1}, +#if defined(REV3) + {0x6d, 2, OP_DESB, BT, "TGDTB", 1, -1, -1, 0}, +#else {0x6d, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0x6e, 1, OP_HALF, NA, "BEH", -1, -1, -1, 0}, /* duplicate of 7e */ {0x6f, 1, OP_BYTE, NA, "BEB", -1, -1, -1, 0}, /* duplicate of 7f */ {0x70, 0, OP_NONE, NA, "NOP", -1, -1, -1, -1}, @@ -566,7 +610,11 @@ mnemonic ops[256] = { {0x7a, 1, OP_HALF, NA, "BRH", -1, -1, -1, 0}, {0x7b, 1, OP_BYTE, NA, "BRB", -1, -1, -1, 0}, {0x7c, 0, OP_NONE, NA, "REQL", -1, -1, -1, -1}, +#if defined(REV3) + {0x7d, 2, OP_DESB, BT, "TNEDTB", 1, -1, -1, 0}, +#else {0x7d, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0x7e, 1, OP_HALF, NA, "BEH", -1, -1, -1, 0}, /* duplicate of 6e */ {0x7f, 1, OP_BYTE, NA, "BEB", -1, -1, -1, 0}, /* duplicate of 6f */ {0x80, 1, OP_DESC, WD, "CLRW", -1, -1, -1, 0}, @@ -593,10 +641,18 @@ mnemonic ops[256] = { {0x95, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x96, 1, OP_DESC, HW, "DECH", -1, -1, -1, 0}, {0x97, 1, OP_DESC, BT, "DECB", -1, -1, -1, 0}, +#if defined(REV3) + {0x98, 0, OP_NONE, NA, "RETQINT",-1, -1, -1, -1}, /* TODO: Implement */ +#else {0x98, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0x99, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x9a, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#if defined(REV3) + {0x9b, 2, OP_DESC, BT, "SUBPB2", 0, -1, -1, 1}, +#else {0x9b, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0x9c, 2, OP_DESC, WD, "ADDW2", 0, -1, -1, 1}, {0x9d, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0x9e, 2, OP_DESC, HW, "ADDH2", 0, -1, -1, 1}, @@ -604,7 +660,11 @@ mnemonic ops[256] = { {0xa0, 1, OP_DESC, WD, "PUSHW", 0, -1, -1, -1}, {0xa1, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0xa2, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#if defined(REV3) + {0xa3, 2, OP_DESC, BT, "ADDPB2", 0, -1, -1, 1}, +#else {0xa3, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0xa4, 2, OP_DESC, WD, "MODW2", 0, -1, -1, 1}, {0xa5, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0xa6, 2, OP_DESC, HW, "MODH2", 0, -1, -1, 1}, @@ -657,10 +717,14 @@ mnemonic ops[256] = { {0xd5, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0xd6, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0xd7, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, - {0xd8, 3, OP_DESC, WD, "ROTW", 0, 1, -1, 2}, /* 3-108 238 */ + {0xd8, 3, OP_DESC, WD, "ROTW", 0, 1, -1, 2}, {0xd9, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0xda, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#if defined(REV3) + {0xdb, 3, OP_DESC, BT, "SUBPB3", 0, 1, -1, 2}, +#else {0xdb, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0xdc, 3, OP_DESC, WD, "ADDW3", 0, 1, -1, 2}, {0xdd, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0xde, 3, OP_DESC, HW, "ADDH3", 0, 1, -1, 2}, @@ -668,7 +732,11 @@ mnemonic ops[256] = { {0xe0, 1, OP_DESC, WD, "PUSHAW", 0, -1, -1, -1}, {0xe1, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0xe2, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#if defined(REV3) + {0xe3, 3, OP_DESC, BT, "ADDPB3", 0, 1, -1, 2}, +#else {0xe3, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, +#endif {0xe4, 3, OP_DESC, WD, "MODW3", 0, 1, -1, 2}, {0xe5, -1, OP_NONE, NA, "???", -1, -1, -1, -1}, {0xe6, 3, OP_DESC, HW, "MODH3", 0, 1, -1, 2}, @@ -772,55 +840,21 @@ 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, j; + uint32 slot; fprintf(st, " SLOT DEVICE\n"); fprintf(st, "---------------------\n"); - for (i = 0; i < CIO_SLOTS; i++) { - /* 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); + for (slot = 0; slot < CIO_SLOTS; slot++) { + if (cio[slot].populated) { + fprintf(st, " %2d %s\n", slot, cio[slot].name); } else { - fprintf(st, " %d %s\n", i, cio_entries[j].name); + fprintf(st, " %2d -\n", slot); } } return SCPE_OK; } -t_stat cpu_load_rom(uint8 *arrayp, uint32 len) -{ - uint32 i, index, sc, mask, val; - - /* Update global state */ - rom_size = len; - - if (ROM != NULL) { - free(ROM); - } - ROM = (uint32 *) calloc((size_t)(len >> 2), sizeof(uint32)); - if (ROM == NULL) { - return SCPE_MEM; - } - - for (i = 0; i < len; i++) { - val = arrayp[i]; - sc = (~(i & 3) << 3) & 0x1f; - mask = 0xffu << sc; - index = i >> 2; - - ROM[index] = (ROM[index] & ~mask) | (val << sc); - } - - return SCPE_OK; -} - #if defined(REV3) t_stat sys_boot(int32 flag, CONST char *ptr) { @@ -840,11 +874,8 @@ t_stat sys_boot(int32 flag, CONST char *ptr) #else t_stat sys_boot(int32 flag, CONST char *ptr) { - char gbuf[CBUFSIZE] = { 0 }; - int gnum = 0; - uint8 *srcp = ROM_ARRAY; - uint32 len = ROM_SIZE; - t_stat r; + char gbuf[CBUFSIZE]; + size_t len = ROM_SIZE; if ((ptr = get_sim_sw(ptr)) == NULL) { return SCPE_INVSW; @@ -853,20 +884,11 @@ t_stat sys_boot(int32 flag, CONST char *ptr) do { ptr = get_glyph(ptr, gbuf, 0); - if (gbuf[0] && (strcmp(gbuf, "CPU") && strcmp(gbuf, "DEMON"))) { + if (gbuf[0] && (strcmp(gbuf, "CPU"))) { return SCPE_ARG; } - - if (strcmp(gbuf, "DEMON") == 0) { - srcp = DEMON_ROM_ARRAY; - len = DEMON_ROM_SIZE; - } } while (gbuf[0]); - if ((r = cpu_load_rom(srcp, len)) != SCPE_OK) { - return r; - } - return run_cmd(flag, "CPU"); } #endif /* Rev 2 boot */ @@ -889,16 +911,21 @@ t_stat cpu_boot(int32 unit_num, DEVICE *dptr) * in the PCB, if bit I in PSW is set. */ + if (!rom_loaded) { + sim_messagef(SCPE_NXM, "Cannot boot, ROM not loaded.\n"); + return SCPE_STOP; + } + sim_debug(EXECUTE_MSG, &cpu_dev, "CPU Boot/Reset Initiated. PC=%08x SP=%08x\n", R[NUM_PC], R[NUM_SP]); mmu_disable(); - R[NUM_PCBP] = pread_w(0x80); - R[NUM_PSW] = pread_w(R[NUM_PCBP]); - R[NUM_PC] = pread_w(R[NUM_PCBP] + 4); - R[NUM_SP] = pread_w(R[NUM_PCBP] + 8); + R[NUM_PCBP] = pread_w(0x80, BUS_CPU); + R[NUM_PSW] = pread_w(R[NUM_PCBP], BUS_CPU); + R[NUM_PC] = pread_w(R[NUM_PCBP] + 4, BUS_CPU); + R[NUM_SP] = pread_w(R[NUM_PCBP] + 8, BUS_CPU); if (R[NUM_PSW] & PSW_I_MASK) { R[NUM_PSW] &= ~PSW_I_MASK; @@ -927,8 +954,8 @@ t_stat cpu_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) *vptr = value; return succ; } else { - if (addr_is_rom(uaddr) || addr_is_mem(uaddr)) { - *vptr = (uint32) pread_b(uaddr); + if (IS_ROM(uaddr) || IS_RAM(uaddr)) { + *vptr = (uint32) pread_b(uaddr, BUS_CPU); return SCPE_OK; } else { *vptr = 0; @@ -944,8 +971,8 @@ t_stat cpu_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw) if (sw & EX_V_FLAG) { return deposit(uaddr, (uint8) val); } else { - if (addr_is_mem(uaddr)) { - pwrite_b(uaddr, (uint8) val); + if (IS_RAM(uaddr)) { + pwrite_b(uaddr, (uint8) val, BUS_CPU); return SCPE_OK; } else { return SCPE_NXM; @@ -964,8 +991,8 @@ static void build_int_map() for (i = 0; i < INT_MAP_LEN; i++) { #if defined(REV3) if (i & (INT_PWRDWN|INT_BUS_OP|INT_SBERR| - INT_MBERR|INT_BUS_RXF|INT_BUS_TMO| - INT_CLOCK)) { + INT_MBERR|INT_BUS_RXF|INT_BUS_TMO| + INT_CLOCK)) { ipl = CPU_IPL_15; } else if (i & (INT_UART|INT_UART_DMA)) { ipl = CPU_IPL_13; @@ -1004,19 +1031,13 @@ static void build_int_map() t_stat cpu_reset(DEVICE *dptr) { int i; - t_stat r; - /* Link in our special "boot" command so we can boot with both - * "BO{OT}" and "BO{OT} CPU" */ - sim_vm_cmd = sys_cmd; - - /* Set up the pre-calibration routine */ - sim_clock_precalibrate_commands = att3b2_clock_precalibrate_commands; - - /* Populate the interrupt->IPL map */ - build_int_map(); + sim_debug(EXECUTE_MSG, &cpu_dev, "CPU Reset.\n"); if (!sim_is_running) { + /* Populate the interrupt->IPL map */ + build_int_map(); + /* Clear registers */ for (i = 0; i < NUM_REGISTERS; i++) { R[i] = 0; @@ -1024,29 +1045,34 @@ t_stat cpu_reset(DEVICE *dptr) /* Allocate ROM if needed */ if (ROM == NULL) { - if ((r = cpu_load_rom(ROM_ARRAY, ROM_SIZE)) != SCPE_OK) { - return r; - } + ROM = (uint8 *) calloc((size_t)ROM_SIZE, sizeof(uint8)); + } + if (ROM == NULL) { + return SCPE_MEM; } - /* Always re-allocate RAM */ - if (RAM != NULL) { - free(RAM); + /* Allocate RAM if needed */ + if (RAM == NULL) { + RAM = (uint8 *) calloc((size_t)MEM_SIZE, sizeof(uint8)); } - 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; + + /* Link in our special "boot" command so we can boot with both + * "BO{OT}" and "BO{OT} CPU" */ + sim_vm_cmd = sys_cmd; + + /* Set up the pre-calibration routine */ + sim_clock_precalibrate_commands = att3b2_clock_precalibrate_commands; + + abort_context = C_NONE; + + cpu_in_wait = FALSE; } - abort_context = C_NONE; - cpu_nmi = FALSE; - - cpu_hist_p = 0; - cpu_in_wait = FALSE; - sim_brk_types = SWMASK('E'); sim_brk_dflt = SWMASK('E'); @@ -1085,6 +1111,9 @@ t_bool cpu_is_pc_a_subroutine_call (t_addr **ret_addrs) case JSB: case CALL: case CALLPS: +#if defined(REV3) + case UCALLPS: +#endif returns[0] = R[NUM_PC] + (unsigned int) (1 - fprint_sym(stdnul, R[NUM_PC], sim_eval, &cpu_unit, SWMASK ('M'))); @@ -1107,6 +1136,10 @@ t_stat fprint_sym_m(FILE *of, t_addr addr, t_value *val) int32 vp, inst, i; mnemonic *mn; char reg_name[8]; +#if defined(REV3) + uint8 reg2 = 0; + char reg2_name[8]; +#endif desc = 0; mn = NULL; @@ -1138,10 +1171,10 @@ t_stat fprint_sym_m(FILE *of, t_addr addr, t_value *val) for (i = 0; i < mn->op_count; i++) { /* Special cases for non-descriptor opcodes */ - if (mn->mode == OP_BYTE) { + if ((mn->mode == OP_BYTE) || (mn->mode == OP_DESB && i > 0)) { mode = 6; reg = 15; - } else if (mn->mode == OP_HALF) { + } else if (mn->mode == OP_HALF || (mn->mode == OP_DESH && i > 0)) { mode = 5; reg = 15; } else if (mn->mode == OP_COPR) { @@ -1149,8 +1182,49 @@ t_stat fprint_sym_m(FILE *of, t_addr addr, t_value *val) reg = 15; } else { desc = (uint8) val[vp++]; + +#if defined(REV3) /* WE 32200 only */ + switch (desc) { + case 0x5b: + /* Get next byte */ + desc = (uint8) val[vp++]; + /* + * Mode 0x10: Auto pre-decrement -(%rx) + * Mode 0x12: Auto post-decrement (%rx)- + * Mode 0x14: Auto pre-increment +(%rx) + * Mode 0x16: Auto post-increment (%rx)- + */ + mode = ((desc >> 5) & 0x7) | 0x10; + reg = desc & 0x1f; + break; + case 0xab: + case 0xbb: + mode = 0xab; + desc = (uint8) val[vp++]; + reg = (desc >> 4) & 0xf; + reg2 = (desc & 0xf) + 16; + break; + case 0xcb: + /* Get next byte */ + desc = (uint8) val[vp++]; + mode = (desc >> 4) & 0xf; + reg = (desc & 0xf) + 16; + break; + case 0xdb: + mode = 0xdb; + desc = (uint8) val[vp++]; + reg = (desc >> 4) & 0xf; + reg2 = (desc & 0xf) + 16; + break; + default: + mode = (desc >> 4) & 0xf; + reg = desc & 0xf; + break; + } +#else /* WE 32100 only */ mode = (desc >> 4) & 0xf; reg = desc & 0xf; +#endif /* Find the expanded data type, if any */ if (mode == 14 && @@ -1280,6 +1354,41 @@ t_stat fprint_sym_m(FILE *of, t_addr addr, t_value *val) fprintf(of, "*$0x%x", w); } break; +#if defined(REV3) + case 0x10: /* Auto pre-decrement */ + cpu_register_name(reg, reg_name, 8); + fprintf(of, "-(%s)", reg_name); + break; + case 0x12: /* Auto post-decrement */ + cpu_register_name(reg, reg_name, 8); + fprintf(of, "(%s)-", reg_name); + break; + case 0x14: /* Auto pre-increment */ + cpu_register_name(reg, reg_name, 8); + fprintf(of, "+(%s)", reg_name); + break; + case 0x16: /* Auto post-increment */ + cpu_register_name(reg, reg_name, 8); + fprintf(of, "(%s)+", reg_name); + break; + case 0xab: + OP_R_B(w, val, vp); + cpu_register_name(reg, reg_name, 8); + cpu_register_name(reg2, reg2_name, 8); + fprintf(of, "%d(%s,%s)", (int8) w, reg2_name, reg_name); + break; + case 0xbb: + OP_R_H(w, val, vp); + cpu_register_name(reg, reg_name, 8); + cpu_register_name(reg2, reg2_name, 8); + fprintf(of, "0x%x(%s,%s)", w, reg2_name, reg_name); + break; + case 0xdb: + cpu_register_name(reg, reg_name, 8); + cpu_register_name(reg2, reg2_name, 8); + fprintf(of, "%s[%s]", reg2_name, reg_name); + break; +#endif default: fprintf(of, ""); break; @@ -1485,6 +1594,9 @@ void cpu_register_name(uint8 reg, char *buf, size_t len) { void cpu_show_operand(FILE *st, operand *op) { char reg_name[8]; +#if defined(REV3) + char reg2_name[8]; +#endif if (op->etype != -1) { switch(op->etype) { @@ -1578,21 +1690,53 @@ void cpu_show_operand(FILE *st, operand *op) case 15: fprintf(st, "&0x%x", (int32)op->embedded.w); break; +#if defined(REV3) + case 0x10: /* Auto pre-decrement */ + cpu_register_name(op->reg, reg_name, 8); + fprintf(st, "-(%s)", reg_name); + break; + case 0x12: /* Auto post-decrement */ + cpu_register_name(op->reg, reg_name, 8); + fprintf(st, "(%s)-", reg_name); + break; + case 0x14: /* Auto pre-increment */ + cpu_register_name(op->reg, reg_name, 8); + fprintf(st, "+(%s)", reg_name); + break; + case 0x16: /* Auto post-increment */ + cpu_register_name(op->reg, reg_name, 8); + fprintf(st, "(%s)+", reg_name); + break; + case 0xab: + cpu_register_name(op->reg, reg_name, 8); + cpu_register_name(op->reg2, reg2_name, 8); + fprintf(st, "%d(%s,%s)", (int8)op->embedded.b, reg2_name, reg_name); + break; + case 0xbb: + cpu_register_name(op->reg, reg_name, 8); + cpu_register_name(op->reg2, reg2_name, 8); + fprintf(st, "0x%x(%s,%s)", op->embedded.h, reg2_name, reg_name); + break; + case 0xdb: + cpu_register_name(op->reg, reg_name, 8); + cpu_register_name(op->reg2, reg2_name, 8); + fprintf(st, "%s[%s]", reg2_name, reg_name); + break; +#endif } } t_stat cpu_set_size(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { - uint32 *nRAM = NULL; uint32 uval = (uint32) val; + uint8 *nRAM = NULL; if ((val <= 0) || (val > MAXMEMSIZE)) { return SCPE_ARG; } /* Do (re-)allocation for memory. */ - - nRAM = (uint32 *) calloc(uval >> 2, sizeof(uint32)); + nRAM = (uint8 *) calloc(uval, sizeof(uint32)); if (nRAM == NULL) { return SCPE_MEM; @@ -1600,11 +1744,10 @@ t_stat cpu_set_size(UNIT *uptr, int32 val, CONST char *cptr, void *desc) free(RAM); RAM = nRAM; + memset(RAM, 0, (size_t)uval * sizeof(uint32)); MEM_SIZE = uval; - memset(RAM, 0, (size_t)(MEM_SIZE >> 2)); - return SCPE_OK; } @@ -1629,7 +1772,7 @@ static SIM_INLINE void clear_instruction(instr *inst) /* * Decode a single descriptor-defined operand from the instruction - * stream. Returns the number of bytes consumed during decode.type + * stream. Returns the number of bytes consumed during decode. */ static uint8 decode_operand(uint32 pa, instr *instr, uint8 op_number, int8 *etype) { @@ -1638,10 +1781,61 @@ 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_IF); + desc = read_b(pa + offset++, ACC_IF, BUS_CPU); +#if defined(REV3) + /* + * Handle addressing modes specific to the WE 32200, not + * implemented in the WE 32100 + */ + switch (desc) { + case 0x5b: /* Auto post / pre-increment */ + desc = read_b(pa + offset++, ACC_IF, BUS_CPU); + /* + * Mode 0x10: Auto pre-decrement -(%rx) + * Mode 0x12: Auto post-decrement (%rx)- + * Mode 0x14: Auto pre-increment +(%rx) + * Mode 0x16: Auto post-increment (%rx)- + */ + oper->mode = ((desc >> 5) & 0x7) | 0x10; + oper->reg = desc & 0x1f; + break; + case 0xab: /* Indexed with byte displacement */ + oper->mode = 0xab; + desc = read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->reg = (desc >> 4) & 0xf; + oper->reg2 = (desc & 0xf) + 16; + break; + case 0xbb: /* Indexed with halfword displacement */ + oper->mode = desc; + desc = read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->reg = (desc >> 4) & 0xf; + oper->reg2 = (desc & 0xf) + 16; + break; + case 0xcb: /* Extended format 1: r16 - r31 */ + desc = read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->mode = (desc >> 4) & 0xf; + oper->reg = (desc & 0xf) + 16; + break; + case 0xdb: /* Indexed with scaling */ + oper->mode = desc; + desc = read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->reg = (desc >> 4) & 0xf; + oper->reg2 = (desc & 0xf) + 16; + break; + default: + oper->mode = (desc >> 4) & 0xf; + oper->reg = desc & 0xf; + break; + } +#else + /* + * WE 32100 only + */ oper->mode = (desc >> 4) & 0xf; oper->reg = desc & 0xf; +#endif + oper->dtype = instr->mn->dtype; oper->etype = *etype; @@ -1657,10 +1851,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_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->embedded.w = (uint32) read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 8u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 16u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 24u; oper->data = oper->embedded.w; break; default: /* Register mode */ @@ -1671,8 +1865,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_IF); - oper->embedded.h |= ((uint16) read_b(pa + offset++, ACC_IF)) << 8u; + oper->embedded.h = (uint16) read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->embedded.h |= ((uint16) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 8u; oper->data = oper->embedded.h; break; case 11: /* INVALID */ @@ -1686,7 +1880,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_IF); + oper->embedded.b = read_b(pa + offset++, ACC_IF, BUS_CPU); oper->data = oper->embedded.b; break; default: /* FP Short Offset */ @@ -1698,10 +1892,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_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->embedded.w = (uint32) read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 8u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 16u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 24u; oper->data = oper->embedded.w; break; default: /* AP Short Offset */ @@ -1712,30 +1906,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_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->embedded.w = (uint32) read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 8u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 16u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 24u; oper->data = oper->embedded.w; break; case 10: /* Halfword Displacement */ case 11: /* Halfword Displacement Deferred */ - oper->embedded.h = read_b(pa + offset++, ACC_IF); - oper->embedded.h |= ((uint16) read_b(pa + offset++, ACC_IF)) << 8u; + oper->embedded.h = read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->embedded.h |= ((uint16) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 8u; oper->data = oper->embedded.h; break; case 12: /* Byte Displacement */ case 13: /* Byte Displacement Deferred */ - oper->embedded.b = read_b(pa + offset++, ACC_IF); + oper->embedded.b = read_b(pa + offset++, ACC_IF, BUS_CPU); 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_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->embedded.w = (uint32) read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 8u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 16u; + oper->embedded.w |= ((uint32) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 24u; break; case 0: case 2: @@ -1754,6 +1948,42 @@ static uint8 decode_operand(uint32 pa, instr *instr, uint8 op_number, int8 *etyp break; } break; +#if defined(REV3) + case 0x10: /* Auto pre-decrement */ + case 0x12: /* Auto post-decrement */ + case 0x14: /* Auto pre-increment */ + case 0x16: /* Auto post-increment */ + oper->data = R[oper->reg]; + break; + case 0xab: /* Indexed with byte displacement */ + oper->embedded.b = read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->data = oper->embedded.b; + break; + case 0xbb: /* Indexed with halfword displacement */ + oper->embedded.h = read_b(pa + offset++, ACC_IF, BUS_CPU); + oper->embedded.h |= ((uint16) read_b(pa + offset++, ACC_IF, BUS_CPU)) << 8u; + oper->data = oper->embedded.h; + break; + case 0xdb: /* Indexed with scaling */ + switch (op_type(oper)) { + case BT: + case SB: + oper->data = R[oper->reg]; + break; + case HW: + case UH: + oper->data = R[oper->reg] * 2; + break; + case WD: + case UW: + oper->data = R[oper->reg] * 4; + break; + } + + oper->data += R[oper->reg2]; + + break; +#endif default: cpu_abort(NORMAL_EXCEPTION, INVALID_DESCRIPTOR); } @@ -1833,24 +2063,22 @@ uint8 decode_instruction(instr *instr) } switch (mn->mode) { - case OP_NONE: - break; case OP_BYTE: - instr->operands[0].embedded.b = read_b(pa + offset++, ACC_IF); + instr->operands[0].embedded.b = read_b(pa + offset++, ACC_IF, BUS_CPU); instr->operands[0].mode = 6; instr->operands[0].reg = 15; break; case OP_HALF: - 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].embedded.h = read_b(pa + offset++, ACC_IF, BUS_CPU); + instr->operands[0].embedded.h |= (uint16)(read_b(pa + offset++, ACC_IF, BUS_CPU)) << 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_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].embedded.w = (uint32) read_b(pa + offset++, ACC_IF, BUS_CPU); + instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_IF, BUS_CPU) << 8; + instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_IF, BUS_CPU) << 16; + instr->operands[0].embedded.w |= (uint32) read_b(pa + offset++, ACC_IF, BUS_CPU) << 24; instr->operands[0].mode = 4; instr->operands[0].reg = 15; @@ -1865,6 +2093,23 @@ uint8 decode_instruction(instr *instr) offset += decode_operand(pa + offset, instr, (uint8) i, &etype); } break; +#if defined(REV3) + case OP_DESB: + /* Operand 0 is a descriptor byte */ + offset += decode_operand(pa + offset, instr, 0, &etype); + /* Operand 1 is a signed byte offset */ + instr->operands[1].embedded.b = read_b(pa + offset++, ACC_IF, BUS_CPU); + break; + case OP_DESH: + /* Operand 0 is a descriptor byte */ + offset += decode_operand(pa + offset, instr, 0, &etype); + /* Operand 1 is a signed byte offset */ + instr->operands[1].embedded.h = read_b(pa + offset++, ACC_IF, BUS_CPU); + instr->operands[1].embedded.h |= (uint16)(read_b(pa + offset++, ACC_IF, BUS_CPU)) << 8; + break; +#endif + default: + break; } return offset; @@ -1875,22 +2120,22 @@ static SIM_INLINE void cpu_context_switch_3(uint32 new_pcbp) if (R[NUM_PSW] & PSW_R_MASK) { R[0] = R[NUM_PCBP] + 64; - R[2] = read_w(R[0], ACC_AF); + R[2] = read_w(R[0], ACC_AF, BUS_CPU); R[0] += 4; while (R[2] != 0) { - R[1] = read_w(R[0], ACC_AF); + R[1] = read_w(R[0], ACC_AF, BUS_CPU); R[0] += 4; /* Execute MOVBLW instruction inside this loop */ while (R[2] != 0) { - write_w(R[1], read_w(R[0], ACC_AF)); + write_w(R[1], read_w(R[0], ACC_AF, BUS_CPU), BUS_CPU); R[2]--; R[0] += 4; R[1] += 4; } - R[2] = read_w(R[0], ACC_AF); + R[2] = read_w(R[0], ACC_AF, BUS_CPU); R[0] += 4; } @@ -1903,10 +2148,10 @@ static SIM_INLINE void cpu_context_switch_2(uint32 new_pcbp) R[NUM_PCBP] = new_pcbp; /* Put new PSW, PC and SP values from PCB into registers */ - R[NUM_PSW] = read_w(R[NUM_PCBP], ACC_AF); + R[NUM_PSW] = read_w(R[NUM_PCBP], ACC_AF, BUS_CPU); R[NUM_PSW] &= ~PSW_TM_MASK; /* Clear TM */ - R[NUM_PC] = read_w(R[NUM_PCBP] + 4, ACC_AF); - R[NUM_SP] = read_w(R[NUM_PCBP] + 8, ACC_AF); + R[NUM_PC] = read_w(R[NUM_PCBP] + 4, ACC_AF, BUS_CPU); + R[NUM_SP] = read_w(R[NUM_PCBP] + 8, ACC_AF, BUS_CPU); /* If i-bit is set, increment PCBP past initial context area */ if (R[NUM_PSW] & PSW_I_MASK) { @@ -1918,29 +2163,29 @@ static SIM_INLINE void cpu_context_switch_2(uint32 new_pcbp) static SIM_INLINE void cpu_context_switch_1(uint32 new_pcbp) { /* Save the current PC in PCB */ - write_w(R[NUM_PCBP] + 4, R[NUM_PC]); + write_w(R[NUM_PCBP] + 4, R[NUM_PC], BUS_CPU); /* Copy the 'R' flag from the new PSW to the old PSW */ R[NUM_PSW] &= ~PSW_R_MASK; - R[NUM_PSW] |= (read_w(new_pcbp, ACC_AF) & PSW_R_MASK); + R[NUM_PSW] |= (read_w(new_pcbp, ACC_AF, BUS_CPU) & PSW_R_MASK); /* Save current PSW and SP in PCB */ - write_w(R[NUM_PCBP], R[NUM_PSW]); - write_w(R[NUM_PCBP] + 8, R[NUM_SP]); + write_w(R[NUM_PCBP], R[NUM_PSW], BUS_CPU); + write_w(R[NUM_PCBP] + 8, R[NUM_SP], BUS_CPU); /* If R is set, save current R0-R8/FP/AP in PCB */ if (R[NUM_PSW] & PSW_R_MASK) { - write_w(R[NUM_PCBP] + 24, R[NUM_FP]); - write_w(R[NUM_PCBP] + 28, R[0]); - write_w(R[NUM_PCBP] + 32, R[1]); - write_w(R[NUM_PCBP] + 36, R[2]); - write_w(R[NUM_PCBP] + 40, R[3]); - write_w(R[NUM_PCBP] + 44, R[4]); - write_w(R[NUM_PCBP] + 48, R[5]); - write_w(R[NUM_PCBP] + 52, R[6]); - write_w(R[NUM_PCBP] + 56, R[7]); - write_w(R[NUM_PCBP] + 60, R[8]); - write_w(R[NUM_PCBP] + 20, R[NUM_AP]); + write_w(R[NUM_PCBP] + 24, R[NUM_FP], BUS_CPU); + write_w(R[NUM_PCBP] + 28, R[0], BUS_CPU); + write_w(R[NUM_PCBP] + 32, R[1], BUS_CPU); + write_w(R[NUM_PCBP] + 36, R[2], BUS_CPU); + write_w(R[NUM_PCBP] + 40, R[3], BUS_CPU); + write_w(R[NUM_PCBP] + 44, R[4], BUS_CPU); + write_w(R[NUM_PCBP] + 48, R[5], BUS_CPU); + write_w(R[NUM_PCBP] + 52, R[6], BUS_CPU); + write_w(R[NUM_PCBP] + 56, R[7], BUS_CPU); + write_w(R[NUM_PCBP] + 60, R[8], BUS_CPU); + write_w(R[NUM_PCBP] + 20, R[NUM_AP], BUS_CPU); R[NUM_FP] = R[NUM_PCBP] + 52; } @@ -1948,11 +2193,17 @@ static SIM_INLINE void cpu_context_switch_1(uint32 new_pcbp) void cpu_on_interrupt(uint16 vec) { - uint32 new_pcbp, new_pcbp_ptr; + uint32 new_psw_ptr, new_psw, new_pc; /* Quick-Interrupt */ + uint32 new_pcbp, new_pcbp_ptr; /* Full-Interrupt */ + t_bool quick; + + cpu_int_ack = vec; + + quick = (R[NUM_PSW] & PSW_QIE_MASK) != 0; sim_debug(IRQ_MSG, &cpu_dev, - "[%08x] [cpu_on_interrupt] vec=%02x (%d) sbd_int_req = %x, csr_data = %x\n", - R[NUM_PC], vec, vec, sbd_int_req, csr_data); + "[%08x] [cpu_on_interrupt] vec=%02x (%d), quick=%d, sbd_int_req = %x, csr_data = %x\n", + R[NUM_PC], vec, vec, quick, sbd_int_req, csr_data); /* * "If a nonmaskable interrupt request is received, an auto-vector @@ -1962,39 +2213,93 @@ void cpu_on_interrupt(uint16 vec) */ if (cpu_nmi) { vec = 0; + cpu_nmi = FALSE; } - cpu_nmi = FALSE; cpu_km = TRUE; - if (R[NUM_PSW] & PSW_QIE_MASK) { - /* TODO: Maybe implement quick interrupts at some point, but - the 3B2 ROM and SVR3 don't appear to use them. */ - stop_reason = STOP_ERR; - return; + if (quick) { + /* + * Quick interrupt microsequence + */ + new_psw_ptr = (uint32)0x48c + (8 * (uint32)vec); + + /* Set ISC, TM, and ET to 2, 0, 0 before saving */ + R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); + R[NUM_PSW] |= (2 << PSW_ISC); + + abort_context = C_RESET_INT_STACK; + + write_w(R[NUM_ISP], R[NUM_PC], BUS_CPU); + write_w(R[NUM_ISP] + 4, R[NUM_PSW], BUS_CPU); + + /* Set ISC, TM, and ET to 1, 0, 0 */ + R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); + R[NUM_PSW] |= (2 << PSW_ISC); + + abort_context = C_RESET_SYSTEM_DATA; + + new_psw = read_w(new_psw_ptr, ACC_AF, BUS_CPU); + + /* Clear out PSW state */ + R[NUM_PSW] &= ~(QIE_PSW_MASK); + + /* Set PM to CM and clear out old CM */ + R[NUM_PSW] |= (R[NUM_PSW] & PSW_CM_MASK) >> 2; + R[NUM_PSW] &= ~(PSW_CM_MASK); + + /* Copy from new PSW */ + R[NUM_PSW] |= (new_psw & QIE_PSW_MASK); + + /* Grab the new PC */ + new_pc = read_w(new_psw_ptr + 4, ACC_AF, BUS_CPU); + + /* Finish ISP stack push */ + R[NUM_ISP] = R[NUM_ISP] + 8; + + /* Set new PSW ISC/TM/ET to 7/0/3 */ + R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); + R[NUM_PSW] |= 7 << PSW_ISC; + R[NUM_PSW] |= 3 << PSW_ET; + + /* Set new PC */ + R[NUM_PC] = new_pc; + + /* Done */ + abort_context = C_NONE; + } else { + /* + * Full interrupt microsequence + */ + new_pcbp_ptr = (uint32)0x8c + (4 * (uint32)vec); + + abort_context = C_RESET_SYSTEM_DATA; + + new_pcbp = read_w(new_pcbp_ptr, ACC_AF, BUS_CPU); + + abort_context = C_RESET_INT_STACK; + + /* Save the old PCBP */ + irq_push_word(R[NUM_PCBP]); + + /* Set ISC, TM, and ET to 0, 0, 1 before saving */ + R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); + R[NUM_PSW] |= (1 << PSW_ET); + + /* Context switch */ + cpu_context_switch_1(new_pcbp); + cpu_context_switch_2(new_pcbp); + + /* Set ISC, TM, and ET to 7, 0, 3 in new PSW */ + R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); + R[NUM_PSW] |= (7 << PSW_ISC); + R[NUM_PSW] |= (3 << PSW_ET); + + cpu_context_switch_3(new_pcbp); + + abort_context = C_NONE; } - 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]); - - /* Set ISC, TM, and ET to 0, 0, 1 before saving */ - R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); - R[NUM_PSW] |= (1 << PSW_ET); - - /* Context switch */ - cpu_context_switch_1(new_pcbp); - cpu_context_switch_2(new_pcbp); - - /* Set ISC, TM, and ET to 7, 0, 3 in new PSW */ - R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); - R[NUM_PSW] |= (7 << PSW_ISC); - R[NUM_PSW] |= (3 << PSW_ET); - - cpu_context_switch_3(new_pcbp); - cpu_km = FALSE; } @@ -2141,14 +2446,6 @@ t_stat sim_instr(void) cpu_nmi = FALSE; cpu_in_wait = FALSE; cpu_on_interrupt(0); - } else if (sbd_int_req) { - ipl = int_map[sbd_int_req]; - if (PSW_CUR_IPL < ipl) { - /* For the system board, interrupt vector is always - equal to IPL */ - cpu_in_wait = FALSE; - cpu_on_interrupt(ipl); - } } else if (cio_int_req) { for (i = 0; i < CIO_SLOTS; i++) { if ((cio_int_req & (1 << i)) && (PSW_CUR_IPL < cio[i].ipl)) { @@ -2158,12 +2455,18 @@ t_stat sim_instr(void) break; } } + } else if (sbd_int_req) { + ipl = int_map[sbd_int_req]; + if (PSW_CUR_IPL < ipl) { + /* For the system board, interrupt vector is always + equal to IPL */ + cpu_in_wait = FALSE; + cpu_on_interrupt(ipl); + } } if (cpu_in_wait) { - if (sim_idle_enab) { - sim_idle(TMR_CLK, TRUE); - } + sim_idle(TMR_CLK, TRUE); continue; } @@ -2373,9 +2676,7 @@ t_stat sim_instr(void) } break; case BPT: - case HALT: trap = BREAKPOINT_TRAP; - stop_reason = STOP_IBKPT; break; case BRH: pc_incr = sign_extend_h(dst->embedded.h); @@ -2419,32 +2720,13 @@ t_stat sim_instr(void) case CALL: a = cpu_effective_address(src1); b = cpu_effective_address(dst); - write_w(R[NUM_SP] + 4, R[NUM_AP]); - write_w(R[NUM_SP], R[NUM_PC] + pc_incr); + write_w(R[NUM_SP] + 4, R[NUM_AP], BUS_CPU); + write_w(R[NUM_SP], R[NUM_PC] + pc_incr, BUS_CPU); R[NUM_SP] += 8; R[NUM_PC] = b; 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: @@ -2469,6 +2751,7 @@ t_stat sim_instr(void) R[NUM_PSW] |= (1 << PSW_ET); cpu_context_switch_1(a); + abort_context = C_PROCESS_NEW_PCB; cpu_context_switch_2(a); R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); @@ -2769,16 +3052,16 @@ t_stat sim_instr(void) break; case GATE: cpu_km = TRUE; - if (R[NUM_SP] < read_w(R[NUM_PCBP] + 12, ACC_AF) || - R[NUM_SP] > read_w(R[NUM_PCBP] + 16, ACC_AF)) { + if (R[NUM_SP] < read_w(R[NUM_PCBP] + 12, ACC_AF, BUS_CPU) || + R[NUM_SP] > read_w(R[NUM_PCBP] + 16, ACC_AF, BUS_CPU)) { sim_debug(EXECUTE_MSG, &cpu_dev, "[%08x] STACK OUT OF BOUNDS IN GATE. " "SP=%08x, R[NUM_PCBP]+12=%08x, " "R[NUM_PCBP]+16=%08x\n", R[NUM_PC], R[NUM_SP], - read_w(R[NUM_PCBP] + 12, ACC_AF), - read_w(R[NUM_PCBP] + 16, ACC_AF)); + read_w(R[NUM_PCBP] + 12, ACC_AF, BUS_CPU), + read_w(R[NUM_PCBP] + 16, ACC_AF, BUS_CPU)); cpu_abort(STACK_EXCEPTION, STACK_BOUND); } cpu_km = FALSE; @@ -2786,7 +3069,7 @@ t_stat sim_instr(void) abort_context = C_STACK_FAULT; /* Push PC+2 onto stack */ - write_w(R[NUM_SP], R[NUM_PC] + 2); + write_w(R[NUM_SP], R[NUM_PC] + 2, BUS_CPU); /* Write 1, 0, 2 to ISC, TM, ET */ R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); @@ -2794,7 +3077,7 @@ t_stat sim_instr(void) R[NUM_PSW] |= (2 << PSW_ET); /* Push PSW onto stack */ - write_w(R[NUM_SP] + 4, R[NUM_PSW]); + write_w(R[NUM_SP] + 4, R[NUM_PSW], BUS_CPU); abort_context = C_NONE; @@ -2808,12 +3091,13 @@ t_stat sim_instr(void) #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. + * Both the WE 32100 and the WE 32200 processor manuals + * state that this is not a privileged instruction, and + * that it can be run from any processor level. This is + * true for the WE 32100, but not true for the WE + * 32200. 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); @@ -2840,15 +3124,15 @@ t_stat sim_instr(void) break; case MOVBLW: while (R[2] != 0) { - a = read_w(R[0], ACC_AF); - write_w(R[1], a); + a = read_w(R[0], ACC_AF, BUS_CPU); + write_w(R[1], a, BUS_CPU); R[2]--; R[0] += 4; R[1] += 4; } break; case STREND: - while (read_b(R[0], ACC_AF) != '\0') { + while (read_b(R[0], ACC_AF, BUS_CPU) != '\0') { R[0]++; } break; @@ -3059,7 +3343,7 @@ t_stat sim_instr(void) are indeterminate". The ordering here is important. If we decrement SP before writing the results, we end up in a weird, bad state. */ - a = read_w(R[NUM_SP] - 4, ACC_AF); + a = read_w(R[NUM_SP] - 4, ACC_AF, BUS_CPU); cpu_write_op(dst, a); R[NUM_SP] -= 4; cpu_set_nz_flags(a, dst); @@ -3107,8 +3391,8 @@ t_stat sim_instr(void) break; case RET: a = R[NUM_AP]; - b = read_w(R[NUM_SP] - 4, ACC_AF); - c = read_w(R[NUM_SP] - 8, ACC_AF); + b = read_w(R[NUM_SP] - 4, ACC_AF, BUS_CPU); + c = read_w(R[NUM_SP] - 8, ACC_AF, BUS_CPU); R[NUM_AP] = b; R[NUM_PC] = c; R[NUM_SP] = a; @@ -3116,8 +3400,8 @@ t_stat sim_instr(void) break; case RETG: abort_context = C_STACK_FAULT; - a = read_w(R[NUM_SP] - 4, ACC_AF); /* PSW */ - b = read_w(R[NUM_SP] - 8, ACC_AF); /* PC */ + a = read_w(R[NUM_SP] - 4, ACC_AF, BUS_CPU); /* PSW */ + b = read_w(R[NUM_SP] - 8, ACC_AF, BUS_CPU); /* PC */ abort_context = C_NONE; if ((a & PSW_CM_MASK) < (R[NUM_PSW] & PSW_CM_MASK)) { sim_debug(EXECUTE_MSG, &cpu_dev, @@ -3166,7 +3450,7 @@ t_stat sim_instr(void) a = irq_pop_word(); /* New process PCBP */ abort_context = C_PROCESS_OLD_PCB; - b = read_w(a, ACC_AF); /* New PSW */ + b = read_w(a, ACC_AF, BUS_CPU); /* New PSW */ abort_context = C_PROCESS_NEW_PCB; /* Copy the 'R' flag from the new PSW to the old PSW */ @@ -3181,17 +3465,17 @@ t_stat sim_instr(void) /* Restore registers if R bit is set */ if (R[NUM_PSW] & PSW_R_MASK) { - R[NUM_FP] = read_w(a + 24, ACC_AF); - R[0] = read_w(a + 28, ACC_AF); - R[1] = read_w(a + 32, ACC_AF); - R[2] = read_w(a + 36, ACC_AF); - R[3] = read_w(a + 40, ACC_AF); - R[4] = read_w(a + 44, ACC_AF); - R[5] = read_w(a + 48, ACC_AF); - R[6] = read_w(a + 52, ACC_AF); - R[7] = read_w(a + 56, ACC_AF); - R[8] = read_w(a + 60, ACC_AF); - R[NUM_AP] = read_w(a + 20, ACC_AF); + R[NUM_FP] = read_w(a + 24, ACC_AF, BUS_CPU); + R[0] = read_w(a + 28, ACC_AF, BUS_CPU); + R[1] = read_w(a + 32, ACC_AF, BUS_CPU); + R[2] = read_w(a + 36, ACC_AF, BUS_CPU); + R[3] = read_w(a + 40, ACC_AF, BUS_CPU); + R[4] = read_w(a + 44, ACC_AF, BUS_CPU); + R[5] = read_w(a + 48, ACC_AF, BUS_CPU); + R[6] = read_w(a + 52, ACC_AF, BUS_CPU); + R[7] = read_w(a + 56, ACC_AF, BUS_CPU); + R[8] = read_w(a + 60, ACC_AF, BUS_CPU); + R[NUM_AP] = read_w(a + 20, ACC_AF, BUS_CPU); } abort_context = C_NONE; @@ -3200,6 +3484,15 @@ t_stat sim_instr(void) cpu_km = FALSE; pc_incr = 0; break; + case INTACK: + R[0] = cpu_int_ack << 2; + break; + case EXTOP: + sim_debug(EXECUTE_MSG, &cpu_dev, + "[%08x] EXTOP instruction.\n", + R[NUM_PC]); + cpu_abort(NORMAL_EXCEPTION, RESERVED_OPCODE); + break; case SPOP: /* Memory fault is signaled when no support processor is active */ @@ -3248,17 +3541,23 @@ t_stat sim_instr(void) break; case RESTORE: a = R[NUM_FP] - 28; /* Old FP */ - b = read_w(a, ACC_AF); /* Old FP */ + b = read_w(a, ACC_AF, BUS_CPU); /* Old FP */ c = R[NUM_FP] - 24; /* Old save point */ for (d = src1->reg; d < NUM_FP; d++) { - R[d] = read_w(c, ACC_AF); + R[d] = read_w(c, ACC_AF, BUS_CPU); c += 4; } R[NUM_FP] = b; /* Restore FP */ R[NUM_SP] = a; /* Restore SP */ break; + case RGTRU: + if ((cpu_c_flag() & cpu_z_flag()) == 0) { + R[NUM_PC] = cpu_pop_word(); + pc_incr = 0; + } + break; case RLEQ: if ((cpu_n_flag() | cpu_z_flag()) == 1) { R[NUM_PC] = cpu_pop_word(); @@ -3277,6 +3576,12 @@ t_stat sim_instr(void) pc_incr = 0; } break; + case RLSSU: + if (cpu_c_flag() == 1) { + R[NUM_PC] = cpu_pop_word(); + pc_incr = 0; + } + break; case REQL: if (cpu_z_flag() == 1) { R[NUM_PC] = cpu_pop_word(); @@ -3293,14 +3598,26 @@ t_stat sim_instr(void) R[NUM_PC] = cpu_pop_word(); pc_incr = 0; break; + case RVC: + if (cpu_v_flag() == 0) { + R[NUM_PC] = cpu_pop_word(); + pc_incr = 0; + } + break; + case RVS: + if (cpu_v_flag() == 1) { + R[NUM_PC] = cpu_pop_word(); + pc_incr = 0; + } + break; case SAVE: /* Save the FP register */ - write_w(R[NUM_SP], R[NUM_FP]); + write_w(R[NUM_SP], R[NUM_FP], BUS_CPU); /* Save all the registers from the one identified by the src operand up to FP (exclusive) */ for (a = src1->reg, b = 4; a < NUM_FP; a++, b += 4) { - write_w(R[NUM_SP] + b, R[a]); + write_w(R[NUM_SP] + b, R[a], BUS_CPU); } R[NUM_SP] = R[NUM_SP] + 28; @@ -3312,8 +3629,8 @@ t_stat sim_instr(void) * terminator never increments the source or destination * pointer! */ while (1) { - a = read_b(R[0], ACC_AF); - write_b(R[1], (uint8) a); + a = read_b(R[0], ACC_AF, BUS_CPU); + write_b(R[1], (uint8) a, BUS_CPU); if (a == '\0') { break; } @@ -3367,15 +3684,238 @@ t_stat sim_instr(void) cpu_set_c_flag(0); cpu_set_v_flag_op(a, dst); break; - default: - if (cpu_unit.flags & UNIT_OPBRK) { - stop_reason = STOP_OPCODE; +#if defined(REV3) + case ADDPB2: + a = cpu_read_op(src1); + b = cpu_read_op(dst); + result = add_bcd(a, b); /* sets flags */ + cpu_write_op(dst, result); + break; + case ADDPB3: + a = cpu_read_op(src1); + b = cpu_read_op(src2); + result = add_bcd(a, b); /* sets flags */ + cpu_write_op(dst, result); + break; + case DTB: + a = cpu_read_op(dst); + result = a - 1; + cpu_write_op(dst, (uint32)(result & WORD_MASK)); + if ((int32)result > -1) { + pc_incr = sign_extend_b(src1->embedded.b); } sim_debug(EXECUTE_MSG, &cpu_dev, - "[%08x] Illegal Opcode 0x%x\n", - R[NUM_PC], inst.mn->opcode); - cpu_abort(NORMAL_EXCEPTION, ILLEGAL_OPCODE); + "[%08x] DTB: dst=%08x r=%08x emb=%04x\n", + R[NUM_PC], a, (uint32)(result & WORD_MASK), src1->embedded.h); break; + case DTH: + a = cpu_read_op(dst); + result = a - 1; + cpu_write_op(dst, (uint32)(result & WORD_MASK)); + if ((int32)result > -1) { + pc_incr = sign_extend_h(src1->embedded.h); + } + sim_debug(EXECUTE_MSG, &cpu_dev, + "[%08x] DTH: dst=%08x r=%08x emb=%04x\n", + R[NUM_PC], a, (uint32)(result & WORD_MASK), src1->embedded.h); + break; + case TEDTB: + if (cpu_z_flag() == 0) { + a = cpu_read_op(dst); + result = a - 1; + cpu_write_op(dst, (uint32)(result & WORD_MASK)); + if ((int32)result > -1) { + pc_incr = sign_extend_b(src1->embedded.b); + } + } + break; + case TEDTH: + if (cpu_z_flag() == 0) { + a = cpu_read_op(dst); + result = a - 1; + cpu_write_op(dst, (uint32)(result & WORD_MASK)); + if ((int32)result > -1) { + pc_incr = sign_extend_h(src1->embedded.h); + } + } + break; + case TGDTB: + if ((cpu_n_flag() | cpu_z_flag()) == 1) { + a = cpu_read_op(dst); + result = a - 1; + cpu_write_op(dst, (uint32)(result & WORD_MASK)); + if ((int32)result > -1) { + pc_incr = sign_extend_b(src1->embedded.b); + } + } + break; + case TGDTH: + if ((cpu_n_flag() | cpu_z_flag()) == 1) { + a = cpu_read_op(dst); + result = a - 1; + cpu_write_op(dst, (uint32)(result & WORD_MASK)); + if ((int32)result > -1) { + pc_incr = sign_extend_h(src1->embedded.h); + } + } + break; + case TGEDTB: + if ((cpu_n_flag() == 1) & (cpu_z_flag() == 0)) { + a = cpu_read_op(dst); + result = a - 1; + cpu_write_op(dst, (uint32)(result & WORD_MASK)); + if ((int32)result > -1) { + pc_incr = sign_extend_b(src1->embedded.b); + } + } + break; + case TGEDTH: + if ((cpu_n_flag() == 1) & (cpu_z_flag() == 0)) { + a = cpu_read_op(dst); + result = a - 1; + cpu_write_op(dst, (uint32)(result & WORD_MASK)); + if ((int32)result > -1) { + pc_incr = sign_extend_h(src1->embedded.h); + } + } + break; + case TNEDTB: + if (cpu_z_flag() == 1) { + a = cpu_read_op(dst); + result = a - 1; + cpu_write_op(dst, (uint32)(result & WORD_MASK)); + if ((int32)result > -1) { + pc_incr = sign_extend_b(src1->embedded.b); + } + } + break; + case TNEDTH: + if (cpu_z_flag() == 1) { + a = cpu_read_op(dst); + result = a - 1; + cpu_write_op(dst, (uint32)(result & WORD_MASK)); + if ((int32)result > -1) { + pc_incr = sign_extend_h(src1->embedded.h); + } + } + break; + case SUBPB2: + a = cpu_read_op(src1); + b = cpu_read_op(dst); + result = sub_bcd(b, a); /* sets flags */ + cpu_write_op(dst, result); + break; + case SUBPB3: + a = cpu_read_op(src1); + b = cpu_read_op(src2); + result = sub_bcd(b, a); /* sets flags */ + cpu_write_op(dst, result); + break; + case PACKB: + a = cpu_read_op(src1); + b = ((a & 0x0f00) >> 4) | (a & 0xf); + cpu_write_op(dst, b); + break; + case UNPACKB: + a = cpu_read_op(src1); /* d1, d0 */ + b = cpu_read_op(src2); /* d3, d2 */ + c = ((b & 0xf0) << 8) | ((a & 0xf0) << 4) | + ((b & 0xf) << 4) | ((a & 0xf)); /* d3, d1, d2, d0 */ + cpu_write_op(dst, c); + break; + 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(dst, a); + } else { + cpu_write_op(src2, c); + } + + 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; + case SETX: + R[NUM_PSW] |= (1 << PSW_X); + break; + case CLRX: + R[NUM_PSW] &= ~(1 << PSW_X); + break; + case RETQINT: + abort_context = C_RESET_INT_STACK; + + /* Get old PSW value from interrupt stack */ + a = read_w(R[NUM_ISP] - 4, ACC_AF, BUS_CPU); + + /* Update PSW */ + R[NUM_PSW] &= ~(QIE_PSW_MASK); + R[NUM_PSW] |= (a & QIE_PSW_MASK); + + /* Get old PC value from interrupt stack */ + b = read_w(R[NUM_ISP] - 8, ACC_AF, BUS_CPU); + + /* Set new PC */ + R[NUM_PC] = b; + + /* Finish interrupt stack pop */ + R[NUM_ISP] = R[NUM_ISP] - 8; + + /* Set new PSW ISC/TM/ET to 7/0/3 */ + R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); + R[NUM_PSW] |= 7 << PSW_ISC; + R[NUM_PSW] |= 3 << PSW_ET; + + /* Done */ + abort_context = C_NONE; + break; + case UCALLPS: + if ((R[NUM_PSW] & PSW_EXUC_MASK) == 0) { + cpu_abort(NORMAL_EXCEPTION, ILLEGAL_OPCODE); + } + + cpu_km = TRUE; + + /* Get the new PCBP */ + abort_context = C_RESET_SYSTEM_DATA; + a = read_w(0x488, ACC_AF, BUS_CPU); + + /* Save the existing PCBP */ + abort_context = C_RESET_INT_STACK; + irq_push_word(R[NUM_PCBP]); + + /* Prepare the new PC to be pushed on the stack */ + R[NUM_PC] = R[NUM_PC] + 2; + + /* Set ISC/TM/ET to 0/0/1 */ + R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); + R[NUM_PSW] |= 1 << PSW_ET; + + cpu_context_switch_1(a); + abort_context = C_PROCESS_NEW_PCB; + cpu_context_switch_2(a); + + /* Set ISC/TM/ET to 7/0/3 */ + R[NUM_PSW] &= ~(PSW_ISC_MASK|PSW_TM_MASK|PSW_ET_MASK); + R[NUM_PSW] |= 7 << PSW_ISC; + R[NUM_PSW] |= 3 << PSW_ET; + + cpu_context_switch_3(a); + + abort_context = C_NONE; + cpu_km = FALSE; + + break; +#endif + default: + sim_debug(EXECUTE_MSG, &cpu_dev, + "[%08x] Illegal Opcode 0x%x\n", + R[NUM_PC], cpu_instr->mn->opcode); + cpu_abort(NORMAL_EXCEPTION, ILLEGAL_OPCODE); }; /* Increment the PC appropriately */ @@ -3401,11 +3941,28 @@ t_stat sim_instr(void) static SIM_INLINE void cpu_on_process_exception(uint8 isc) { - /* TODO: Handle */ - sim_debug(ERR_MSG, &cpu_dev, - "[%08x] CPU_ON_PROCESS_EXCEPTION not yet implemented.\n", - R[NUM_PC]); - stop_reason = STOP_EX; + uint32 new_pcbp; + + sim_debug(EXECUTE_MSG, &cpu_dev, + "[cpu_on_process_exception %d] SP=%08x PCBP=%08x ISP=%08x\n", + isc, R[NUM_SP], R[NUM_PCBP], R[NUM_ISP]); + + cpu_km = TRUE; + + abort_context = C_RESET_SYSTEM_DATA; + new_pcbp = read_w(0x84, ACC_AF, BUS_CPU); + + abort_context = C_RESET_INT_STACK; + irq_push_word(R[NUM_PCBP]); + + cpu_context_switch_2(new_pcbp); + + /* Set TM and ET to 0 and 3 in new PSW */ + R[NUM_PSW] &= ~(PSW_TM_MASK|PSW_ET_MASK); + R[NUM_PSW] |= (3 << PSW_ET); + + cpu_km = FALSE; + abort_context = C_NONE; return; } @@ -3414,8 +3971,8 @@ static SIM_INLINE void cpu_on_reset_exception(uint8 isc) uint32 new_pcbp; sim_debug(EXECUTE_MSG, &cpu_dev, - "[%08x] [cpu_on_reset_exception %d] SP=%08x PCBP=%08x ISP=%08x\n", - R[NUM_PC], isc, R[NUM_SP], R[NUM_PCBP], R[NUM_ISP]); + "[cpu_on_reset_exception %d] SP=%08x PCBP=%08x ISP=%08x\n", + isc, R[NUM_SP], R[NUM_PCBP], R[NUM_ISP]); if (isc == EXTERNAL_RESET) { R[NUM_PSW] &= ~(PSW_R_MASK); @@ -3426,7 +3983,7 @@ static SIM_INLINE void cpu_on_reset_exception(uint8 isc) mmu_disable(); abort_context = C_RESET_SYSTEM_DATA; - new_pcbp = read_w(0x80, ACC_AF); + new_pcbp = read_w(0x80, ACC_AF, BUS_CPU); abort_context = C_RESET_NEW_PCB; cpu_context_switch_2(new_pcbp); @@ -3440,12 +3997,12 @@ static SIM_INLINE void cpu_on_stack_exception(uint8 isc) uint32 new_pcbp; sim_debug(EXECUTE_MSG, &cpu_dev, - "[%08x] [cpu_on_stack_exception %d] SP=%08x PCBP=%08x ISP=%08x\n", - R[NUM_PC], isc, R[NUM_SP], R[NUM_PCBP], R[NUM_ISP]); + "[cpu_on_stack_exception %d] SP=%08x PCBP=%08x ISP=%08x\n", + isc, R[NUM_SP], R[NUM_PCBP], R[NUM_ISP]); abort_context = C_RESET_SYSTEM_DATA; cpu_km = TRUE; - new_pcbp = read_w(0x88, ACC_AF); + new_pcbp = read_w(0x88, ACC_AF, BUS_CPU); abort_context = C_RESET_INT_STACK; irq_push_word(R[NUM_PCBP]); @@ -3470,20 +4027,19 @@ static SIM_INLINE void cpu_on_stack_exception(uint8 isc) static SIM_INLINE void cpu_on_normal_exception(uint8 isc) { sim_debug(EXECUTE_MSG, &cpu_dev, - "[%08x] [cpu_on_normal_exception %d] %%sp=%08x abort_context=%d\n", - R[NUM_PC], isc, R[NUM_SP], abort_context); + "[cpu_on_normal_exception %d] %%sp=%08x abort_context=%d\n", + isc, R[NUM_SP], abort_context); cpu_km = TRUE; - if (R[NUM_SP] < read_w(R[NUM_PCBP] + 12, ACC_AF) || - R[NUM_SP] > read_w(R[NUM_PCBP] + 16, ACC_AF)) { + if (R[NUM_SP] < read_w(R[NUM_PCBP] + 12, ACC_AF, BUS_CPU) || + R[NUM_SP] > read_w(R[NUM_PCBP] + 16, ACC_AF, BUS_CPU)) { sim_debug(EXECUTE_MSG, &cpu_dev, - "[%08x] STACK OUT OF BOUNDS IN EXCEPTION HANDLER. " + "STACK OUT OF BOUNDS IN EXCEPTION HANDLER. " "SP=%08x, R[NUM_PCBP]+12=%08x, " "R[NUM_PCBP]+16=%08x\n", - R[NUM_PC], R[NUM_SP], - read_w(R[NUM_PCBP] + 12, ACC_AF), - read_w(R[NUM_PCBP] + 16, ACC_AF)); + read_w(R[NUM_PCBP] + 12, ACC_AF, BUS_CPU), + read_w(R[NUM_PCBP] + 16, ACC_AF, BUS_CPU)); cpu_abort(STACK_EXCEPTION, STACK_BOUND); } cpu_km = FALSE; @@ -3491,14 +4047,14 @@ static SIM_INLINE void cpu_on_normal_exception(uint8 isc) /* Set context for STACK (FAULT) */ abort_context = C_STACK_FAULT; /* Save address of next instruction to stack */ - write_w(R[NUM_SP], R[NUM_PC]); + write_w(R[NUM_SP], R[NUM_PC], BUS_CPU); /* Write 0, 3 to TM, ET fields of PSW */ R[NUM_PSW] &= ~(PSW_TM_MASK|PSW_ET_MASK); R[NUM_PSW] |= (3 << PSW_ET); /* Save PSW to stack */ - write_w(R[NUM_SP] + 4, R[NUM_PSW]); + write_w(R[NUM_SP] + 4, R[NUM_PSW], BUS_CPU); /* Set context for RESET (GATE VECTOR) */ abort_context = C_RESET_GATE_VECTOR; @@ -3516,10 +4072,10 @@ static SIM_INLINE void cpu_perform_gate(uint32 index1, uint32 index2) abort_context = C_NORMAL_GATE_VECTOR; cpu_km = TRUE; - gate_l2 = read_w(index1, ACC_AF) + index2; + gate_l2 = read_w(index1, ACC_AF, BUS_CPU) + index2; /* Get new PSW from second-level table */ - new_psw = read_w(gate_l2, ACC_AF); + new_psw = read_w(gate_l2, ACC_AF, BUS_CPU); /* Clear state in PSW */ new_psw &= ~(PSW_PM_MASK|PSW_IPL_MASK|PSW_R_MASK| @@ -3535,7 +4091,7 @@ static SIM_INLINE void cpu_perform_gate(uint32 index1, uint32 index2) new_psw |= (1 << PSW_TM); /* TM */ new_psw |= (3 << PSW_ET); /* ET */ - R[NUM_PC] = read_w(gate_l2 + 4, ACC_AF); + R[NUM_PC] = read_w(gate_l2 + 4, ACC_AF, BUS_CPU); R[NUM_PSW] = new_psw; cpu_km = FALSE; @@ -3551,6 +4107,10 @@ static SIM_INLINE void cpu_perform_gate(uint32 index1, uint32 index2) */ static uint32 cpu_effective_address(operand *op) { +#if defined(REV3) + uint32 tmp; +#endif + /* Register Deferred */ if (op->mode == 5 && op->reg != 11) { return R[op->reg]; @@ -3564,7 +4124,7 @@ static uint32 cpu_effective_address(operand *op) /* Absolute Deferred */ if (op->mode == 14 && op->reg == 15) { /* May cause exception */ - return read_w(op->embedded.w, ACC_AF); + return read_w(op->embedded.w, ACC_AF, BUS_CPU); } /* FP Short Offset */ @@ -3584,7 +4144,7 @@ static uint32 cpu_effective_address(operand *op) /* Word Displacement Deferred */ if (op->mode == 9) { - return read_w(R[op->reg] + op->embedded.w, ACC_AF); + return read_w(R[op->reg] + op->embedded.w, ACC_AF, BUS_CPU); } /* Halfword Displacement */ @@ -3594,7 +4154,7 @@ static uint32 cpu_effective_address(operand *op) /* Halfword Displacement Deferred */ if (op->mode == 11) { - return read_w(R[op->reg] + sign_extend_h(op->embedded.h), ACC_AF); + return read_w(R[op->reg] + sign_extend_h(op->embedded.h), ACC_AF, BUS_CPU); } /* Byte Displacement */ @@ -3604,9 +4164,129 @@ static uint32 cpu_effective_address(operand *op) /* Byte Displacement Deferred */ if (op->mode == 13) { - return read_w(R[op->reg] + sign_extend_b(op->embedded.b), ACC_AF); + return read_w(R[op->reg] + sign_extend_b(op->embedded.b), ACC_AF, BUS_CPU); } +#if defined(REV3) + /* Auto pre-decrement */ + if (op->mode == 0x10) { + switch(op_type(op)) { + case BT: + case SB: + R[op->reg] -= 1; + break; + case HW: + case UH: + R[op->reg] -= 2; + break; + case WD: + case UW: + R[op->reg] -= 4; + break; + } + return R[op->reg]; + } + + /* Auto post-decrement */ + if (op->mode == 0x12) { + tmp = R[op->reg]; + switch(op_type(op)) { + case BT: + case SB: + R[op->reg] -= 1; + break; + case HW: + case UH: + R[op->reg] -= 2; + break; + case WD: + case UW: + R[op->reg] -= 4; + break; + } + return tmp; + } + + /* Auto pre-increment */ + if (op->mode == 0x14) { + switch(op_type(op)) { + case BT: + case SB: + R[op->reg] += 1; + break; + case HW: + case UH: + R[op->reg] += 2; + break; + case WD: + case UW: + R[op->reg] += 4; + break; + } + return R[op->reg]; + } + + /* Auto post-increment */ + if (op->mode == 0x16) { + tmp = R[op->reg]; + switch(op_type(op)) { + case BT: + case SB: + R[op->reg] += 1; + break; + case HW: + case UH: + R[op->reg] += 2; + break; + case WD: + case UW: + R[op->reg] += 4; + break; + } + return tmp; + } + + /* Indexed with byte displacement */ + if (op->mode == 0xab) { + tmp = sign_extend_b(op->embedded.b); + tmp += R[op->reg]; + tmp += R[op->reg2]; + return tmp; + } + + /* Indexed with halfword displacement */ + if (op->mode == 0xbb) { + tmp = sign_extend_h(op->embedded.h); + tmp += R[op->reg]; + tmp += R[op->reg2]; + return tmp; + } + + /* Indexed with scaling */ + if (op->mode == 0xdb) { + switch(op_type(op)) { + case BT: + case SB: + tmp = R[op->reg]; + break; + case HW: + case UH: + tmp = R[op->reg] * 2; + break; + case WD: + case UW: + tmp = R[op->reg] * 4; + break; + default: + /* We should never reach this */ + tmp = 0; + break; + } + + return tmp + R[op->reg2]; + } +#endif + if (cpu_unit.flags & UNIT_OPBRK) { stop_reason = STOP_OPCODE; } @@ -3685,7 +4365,7 @@ static uint32 cpu_read_op(operand * op) uint32 data; /* Register */ - if (op->mode == 4 && op->reg < 15) { + if (op->mode == 4 && op->reg != 15) { switch (op_type(op)) { case WD: case UW: @@ -3704,6 +4384,9 @@ static uint32 cpu_read_op(operand * op) data = sign_extend_b(R[op->reg] & BYTE_MASK); break; default: + sim_debug(EXECUTE_MSG, &cpu_dev, + "[%08x] cpu_read_op: unknown op type (1): %d\n", + R[NUM_PC], op_type(op)); stop_reason = STOP_ERR; data = 0; break; @@ -3748,26 +4431,29 @@ static uint32 cpu_read_op(operand * op) switch (op_type(op)) { case WD: /* Signed Word */ case UW: /* Unsigned Word */ - data = read_w(eff, ACC_OF); + data = read_w(eff, ACC_OF, BUS_CPU); op->data = data; return data; case HW: /* Signed Halfword */ - data = sign_extend_h(read_h(eff, ACC_OF)); + data = sign_extend_h(read_h(eff, ACC_OF, BUS_CPU)); op->data = data; return data; case UH: /* Unsigned Halfword */ - data = read_h(eff, ACC_OF); + data = read_h(eff, ACC_OF, BUS_CPU); op->data = data; return data; case SB: /* Signed Byte */ - data = sign_extend_b(read_b(eff, ACC_OF)); + data = sign_extend_b(read_b(eff, ACC_OF, BUS_CPU)); op->data = data; return data; case BT: /* Unsigned Byte */ - data = read_b(eff, ACC_OF); + data = read_b(eff, ACC_OF, BUS_CPU); op->data = data; return data; default: + sim_debug(EXECUTE_MSG, &cpu_dev, + "[%08x] cpu_read_op: unknown op type (2): %d\n", + R[NUM_PC], op_type(op)); stop_reason = STOP_ERR; return 0; } @@ -3780,9 +4466,8 @@ static void cpu_write_op(operand * op, t_uint64 val) op->data = (uint32) val; /* Writing to a register. */ - if (op->mode == 4 && op->reg < 15) { - if ((op->reg == NUM_PSW || op->reg == NUM_PCBP || op->reg == NUM_ISP) && - cpu_execution_level() != EX_LVL_KERN) { + if (op->mode == 4 && op->reg != 15) { + if (PRIVREG(op->reg) && cpu_execution_level() != EX_LVL_KERN) { cpu_abort(NORMAL_EXCEPTION, PRIVILEGED_REGISTER); return; } @@ -3816,17 +4501,20 @@ static void cpu_write_op(operand * op, t_uint64 val) switch (op_type(op)) { case UW: case WD: - write_w(eff, (uint32) val); + write_w(eff, (uint32) val, BUS_CPU); break; case HW: case UH: - write_h(eff, val & HALF_MASK); + write_h(eff, val & HALF_MASK, BUS_CPU); break; case SB: case BT: - write_b(eff, val & BYTE_MASK); + write_b(eff, val & BYTE_MASK, BUS_CPU); break; default: + sim_debug(EXECUTE_MSG, &cpu_dev, + "[%08x] cpu_read_op: unknown op type (3): %d\n", + R[NUM_PC], op_type(op)); stop_reason = STOP_ERR; break; } @@ -3890,6 +4578,13 @@ static SIM_INLINE t_bool cpu_v_flag() return (R[NUM_PSW] & PSW_V_MASK) != 0; } +#if defined(REV3) +static SIM_INLINE t_bool cpu_x_flag() +{ + return (R[NUM_PSW] & PSW_X_MASK) != 0; +} +#endif + static SIM_INLINE void cpu_set_z_flag(t_bool val) { if (val) { @@ -3917,6 +4612,17 @@ static SIM_INLINE void cpu_set_c_flag(t_bool val) } } +#if defined(REV3) +static SIM_INLINE void cpu_set_x_flag(t_bool val) +{ + if (val) { + R[NUM_PSW] |= PSW_X_MASK; + } else { + R[NUM_PSW] &= ~PSW_X_MASK; + } +} +#endif + static SIM_INLINE void cpu_set_v_flag_op(t_uint64 val, operand *op) { switch(op_type(op)) { @@ -3973,7 +4679,7 @@ static void cpu_set_nz_flags(t_uint64 data, operand *dst) static SIM_INLINE void cpu_push_word(uint32 val) { - write_w(R[NUM_SP], val); + write_w(R[NUM_SP], val, BUS_CPU); R[NUM_SP] += 4; } @@ -3982,21 +4688,21 @@ static SIM_INLINE uint32 cpu_pop_word() uint32 result; /* We always read fromthe stack first BEFORE decrementing, in case this causes a fault. */ - result = read_w(R[NUM_SP] - 4, ACC_AF); + result = read_w(R[NUM_SP] - 4, ACC_AF, BUS_CPU); R[NUM_SP] -= 4; return result; } static SIM_INLINE void irq_push_word(uint32 val) { - write_w(R[NUM_ISP], val); + write_w(R[NUM_ISP], val, BUS_CPU); R[NUM_ISP] += 4; } static SIM_INLINE uint32 irq_pop_word() { R[NUM_ISP] -= 4; - return read_w(R[NUM_ISP], ACC_AF); + return read_w(R[NUM_ISP], ACC_AF, BUS_CPU); } static SIM_INLINE t_bool op_is_psw(operand *op) @@ -4055,6 +4761,68 @@ static SIM_INLINE void add(t_uint64 a, t_uint64 b, operand *dst) } } +#if defined(REV3) +/* + * Return the packed BCD byte result of adding two packed BCD input + * bytes. This will set the C and X carry flags appropraitely if there + * is a carry. + */ +static SIM_INLINE uint8 add_bcd(uint8 packed_a, uint8 packed_b) +{ + uint16 l, h, result; + + l = (packed_a & 0x0f) + (packed_b & 0x0f) + (cpu_x_flag() ? 1 : 0); + if ((l & 0xff) > 9) { + l += 6; + } + + h = ((packed_a >> 4) & 0x0f) + ((packed_b >> 4) & 0x0f) + (l > 15 ? 1 : 0); + if ((h & 0xff) > 9) { + h += 6; + } + + result = ((l & 0x0f) | (h << 4)) & 0xff; + + cpu_set_c_flag(h > 15); + cpu_set_x_flag(h > 15); + cpu_set_z_flag(result == 0); + cpu_set_n_flag(0); + cpu_set_v_flag(0); + + return (uint8)result; +} + +/* + * Return the packed BCD byte result of subtracting two packed BCD + * input bytes. This will set the C and X carry flags appropraitely if + * there is a carry. + */ +static SIM_INLINE uint8 sub_bcd(uint8 packed_a, uint8 packed_b) +{ + uint16 l, h, result; + + l = (packed_a & 0x0f) - (packed_b & 0x0f) - (cpu_x_flag() ? 1 : 0); + if ((l & 0x10) != 0) { + l -= 6; + } + + h = ((packed_a >> 4) & 0x0f) - ((packed_b >> 4) & 0x0f) - ((l & 0x10) != 0 ? 1 : 0); + if ((h & 0x10) != 0) { + h -= 6; + } + + result = ((l & 0x0f)|(h << 4)) & 0xff; + + cpu_set_c_flag(h > 15); + cpu_set_x_flag(h > 15); + cpu_set_z_flag(result == 0); + cpu_set_n_flag(0); + cpu_set_v_flag(0); + + return (uint8)result; +} +#endif + /* * Set PSW's ET and ISC fields, and store global exception or fault * state appropriately. @@ -4077,7 +4845,7 @@ void cpu_abort(uint8 et, uint8 isc) CONST char *cpu_description(DEVICE *dptr) { #if defined(REV3) - return "3B2/600G CPU (WE 32200)"; + return "3B2/700 CPU (WE 32200)"; #else return "3B2/400 CPU (WE 32100)"; #endif @@ -4086,44 +4854,21 @@ CONST char *cpu_description(DEVICE *dptr) 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"); + fprintf(st, "3B2/700 CPU Help\n\n"); + fprintf(st, "The 3B2/700 CPU simulates a WE 32200 at 22 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 %3s set memory size = %sB\n", - mptr->mstring, mptr->mstring); - } - } - fprintf(st, "\n"); - } - fprintf(st, "The CPU also implements a command to display a virtual to physical address\n"); - fprintf(st, "translation:\n\n"); - fprintf(st, " sim> SHOW CPU VIRTUAL=n show translation for address n\n\n"); - fprintf(st, "The CPU attempts to detect when the simulator is idle. When idle, the\n"); - fprintf(st, "simulator does not use any resources on the host system. Idle detetion is\n"); - fprintf(st, "controlled by the SET CPU IDLE and SET CPU NOIDLE commands:\n\n"); - fprintf(st, " sim> SET CPU IDLE enable idle detection\n"); - fprintf(st, " sim> SET CPU NOIDLE disable idle detection\n\n"); - fprintf(st, "Idle detection is disabled by default.\n\n"); - fprintf(st, "The CPU can maintain a history of the most recently executed instructions.\n"); - fprintf(st, "This is controlled by the SET CPU HISTORY and SHOW CPU HISTORY commands:\n\n"); - fprintf(st, " sim> SET CPU HISTORY clear history buffer\n"); - fprintf(st, " sim> SET CPU HISTORY=0 disable history\n"); - 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"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + fprint_reg_help(st, dptr); + #if defined(REV3) - fprintf(st, "Additional documentation for the 3B2/600G Simulator is available on the web:\n\n"); + fprintf(st, "\nAdditional documentation for the 3B2/700 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"); + fprintf(st, "\nAdditional documentation for the 3B2/400 Simulator is available on the web:\n\n"); #endif fprintf(st, " https://loomcom.com/3b2/emulator.html\n\n"); diff --git a/3B2/3b2_cpu.h b/3B2/3b2_cpu.h index b6f9dfa7..d7b0314b 100644 --- a/3B2/3b2_cpu.h +++ b/3B2/3b2_cpu.h @@ -1,6 +1,6 @@ -/* 3b2_cpu.h: AT&T 3B2 CPU (WE32100 and WE32200) Header +/* 3b2_cpu.h: WE32100 and WE32200 CPU - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -127,6 +127,29 @@ #define PSW_CFD_MASK (1u << PSW_CFD) #define PSW_CUR_IPL (((R[NUM_PSW] & PSW_IPL_MASK) >> PSW_IPL) & 0xf) +#if defined(REV3) +#define PSW_X 26 +#define PSW_AR 27 +#define PSW_EXUC 28 +#define PSW_EA 29 + +#define PSW_X_MASK (1u << PSW_X) +#define PSW_AR_MASK (1u << PSW_AR) +#define PSW_EXUC_MASK (1u << PSW_EXUC) +#define PSW_EA_MASK (1u << PSW_EA) +#endif + +#if defined(REV3) +#define QIE_PSW_MASK (PSW_EA_MASK|PSW_EXUC_MASK|PSW_X_MASK| \ + PSW_CFD_MASK|PSW_QIE_MASK|PSW_CD_MASK|PSW_OE_MASK| \ + PSW_N_MASK|PSW_Z_MASK|PSW_V_MASK|PSW_C_MASK| \ + PSW_TE_MASK|PSW_IPL_MASK|PSW_PM_MASK|PSW_I_MASK) +#else +#define QIE_PSW_MASK (PSW_CFD_MASK|PSW_QIE_MASK|PSW_CD_MASK|PSW_OE_MASK| \ + PSW_N_MASK|PSW_Z_MASK|PSW_V_MASK|PSW_C_MASK| \ + PSW_TE_MASK|PSW_IPL_MASK|PSW_PM_MASK|PSW_I_MASK) +#endif + /* A helper to set the PSW, preserving read-only fields */ #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))) @@ -225,7 +248,6 @@ * Opcodes */ typedef enum { - HALT = 0x00, /* Undocumented instruction */ SPOPRD = 0x02, SPOPD2 = 0x03, MOVAW = 0x04, @@ -254,7 +276,7 @@ typedef enum { SWAPWI = 0x1C, #if defined(REV3) TGEDTH = 0x1D, -#endif +#endif SWAPHI = 0x1E, SWAPBI = 0x1F, POPW = 0x20, @@ -310,7 +332,7 @@ typedef enum { RGTRU = 0x54, BGUH = 0x56, BGUB = 0x57, - BLSSU = 0x58, + RLSSU = 0x58, BLUH = 0x5A, BLUB = 0x5B, RLEQU = 0x5C, @@ -455,6 +477,9 @@ typedef enum { RETG = 0x3045, GATE = 0x3061, CALLPS = 0x30ac, +#if defined(REV3) + UCALLPS = 0x30c0, +#endif RETPS = 0x30c8 } opcode; @@ -498,6 +523,8 @@ typedef enum { typedef enum { OP_NONE, /* NULL type */ OP_DESC, /* Descriptor byte */ + OP_DESB, /* Descriptor with byte displacement (WE32200 only) */ + OP_DESH, /* Descriptor with halfword displacement (WE32200 only) */ OP_BYTE, /* 8-bit signed value */ OP_HALF, /* 16-bit signed value */ OP_COPR /* Coprocessor instruction */ @@ -536,6 +563,9 @@ typedef struct { typedef struct { uint8 mode; /* Embedded data addressing mode */ uint8 reg; /* Operand register (0-15) */ +#if defined(REV3) + uint8 reg2; /* Operand register 2 (16-31) */ +#endif int8 dtype; /* Default type for the operand */ int8 etype; /* Expanded type (-1 if none) */ union { @@ -562,18 +592,6 @@ 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); @@ -650,13 +668,13 @@ void cpu_abort(uint8 et, uint8 isc); #define CPU_SET_INT(flags) (sbd_int_req |= flags) #define CPU_CLR_INT(flags) (sbd_int_req &= ~(flags)) +extern t_bool rom_loaded; extern volatile int32 stop_reason; extern uint16 sbd_int_req; -extern uint32 rom_size; extern instr *cpu_instr; extern t_bool cpu_nmi; -extern uint32 *ROM; -extern uint32 *RAM; +extern uint8 *ROM; +extern uint8 *RAM; extern uint32 R[NUM_REGISTERS]; extern REG cpu_reg[]; extern UNIT cpu_unit; diff --git a/3B2/3b2_csr.h b/3B2/3b2_csr.h index c7cf27a5..d3b1837a 100644 --- a/3B2/3b2_csr.h +++ b/3B2/3b2_csr.h @@ -1,6 +1,6 @@ -/* 3b2_csr.h: Common CSR header +/* 3b2_csr.h: Common CSR/CSER header - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -41,4 +41,6 @@ #define CLR_CSR(FLAGS) (csr_data &= ~(FLAGS)) #define CSR(FLAGS) ((csr_data & FLAGS) != 0) +extern CSR_DATA csr_data; + #endif /* _3B2_CSR_H_ */ diff --git a/3B2/3b2_ctc.c b/3B2/3b2_ctc.c index 3bc4b08d..657e427c 100644 --- a/3B2/3b2_ctc.c +++ b/3B2/3b2_ctc.c @@ -1,6 +1,6 @@ -/* 3b2_ctc.c: AT&T 3B2 Model 400 "CTC" feature card +/* 3b2_ctc.c: CM195H 23MB Cartridge Tape Controller CIO Card - Copyright (c) 2018, Seth J. Morabito + Copyright (c) 2018-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -61,12 +61,7 @@ #define VTOC_BLOCK 0 -/* 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); -static t_stat ctc_show_queue_common(FILE *st, UNIT *uptr, int32 val, CONST void *desc, t_bool rq); - -static uint8 int_cid; /* Interrupting card ID */ +static uint8 int_slot; /* Interrupting card ID */ static uint8 int_subdev; /* Interrupting subdevice */ static t_bool ctc_conf = FALSE; /* Has a CTC card been configured? */ static uint32 ctc_crc; /* CRC32 of downloaded memory */ @@ -101,14 +96,10 @@ UNIT ctc_unit = { }; MTAB ctc_mod[] = { - { MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED", + { MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED", &set_writelock, &show_writelock, NULL, "Write enable tape drive" }, - { MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED", + { MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED", &set_writelock, NULL, NULL, "Write lock tape drive" }, - { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "RQUEUE=n", NULL, - NULL, &ctc_show_rqueue, NULL, "Display Request Queue for card n" }, - { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "CQUEUE=n", NULL, - NULL, &ctc_show_cqueue, NULL, "Display Completion Queue for card n" }, { 0 } }; @@ -147,9 +138,9 @@ DEVICE ctc_dev = { NULL, /* device description */ }; -static void cio_irq(uint8 cid, uint8 dev, int32 delay) +static void cio_irq(uint8 slot, uint8 dev, int32 delay) { - int_cid = cid; + int_slot = slot; int_subdev = dev & 0x3f; sim_activate_after(&ctc_unit, delay); } @@ -204,49 +195,49 @@ static void ctc_update_vtoc(uint32 maxpass, { uint32 i; - pwrite_w(vtoc_addr + 12, VTOC_VALID); - pwrite_w(vtoc_addr + 16, vtoc->version); + pwrite_w(vtoc_addr + 12, VTOC_VALID, BUS_PER); + pwrite_w(vtoc_addr + 16, vtoc->version, BUS_PER); for (i = 0; i < 8; i++) { - pwrite_b(vtoc_addr + 20 + i, (uint8)(vtoc->volume[i])); + pwrite_b(vtoc_addr + 20 + i, (uint8)(vtoc->volume[i]), BUS_PER); } - pwrite_h(vtoc_addr + 28, vtoc->sectorsz); - pwrite_h(vtoc_addr + 30, vtoc->nparts); + pwrite_h(vtoc_addr + 28, vtoc->sectorsz, BUS_PER); + pwrite_h(vtoc_addr + 30, vtoc->nparts, BUS_PER); for (i = 0; i < VTOC_PART; i++) { - pwrite_h(vtoc_addr + 72 + (i * 12) + 0, vtoc_table[i].id); - pwrite_h(vtoc_addr + 72 + (i * 12) + 2, vtoc_table[i].flag); - pwrite_w(vtoc_addr + 72 + (i * 12) + 4, vtoc_table[i].sstart); - pwrite_w(vtoc_addr + 72 + (i * 12) + 8, vtoc_table[i].ssize); + pwrite_h(vtoc_addr + 72 + (i * 12) + 0, vtoc_table[i].id, BUS_PER); + pwrite_h(vtoc_addr + 72 + (i * 12) + 2, vtoc_table[i].flag, BUS_PER); + pwrite_w(vtoc_addr + 72 + (i * 12) + 4, vtoc_table[i].sstart, BUS_PER); + pwrite_w(vtoc_addr + 72 + (i * 12) + 8, vtoc_table[i].ssize, BUS_PER); } /* Write the pdinfo */ - pwrite_w(pdinfo_addr, pdinfo->driveid); - pwrite_w(pdinfo_addr + 4, pdinfo->sanity); - pwrite_w(pdinfo_addr + 8, pdinfo->version); + pwrite_w(pdinfo_addr, pdinfo->driveid, BUS_PER); + pwrite_w(pdinfo_addr + 4, pdinfo->sanity, BUS_PER); + pwrite_w(pdinfo_addr + 8, pdinfo->version, BUS_PER); for (i = 0; i < 12; i++) { - pwrite_b(pdinfo_addr + 12 + i, pdinfo->serial[i]); + pwrite_b(pdinfo_addr + 12 + i, pdinfo->serial[i], BUS_PER); } - pwrite_w(pdinfo_addr + 24, pdinfo->cyls); - pwrite_w(pdinfo_addr + 28, pdinfo->tracks); - pwrite_w(pdinfo_addr + 32, pdinfo->sectors); - pwrite_w(pdinfo_addr + 36, pdinfo->bytes); - pwrite_w(pdinfo_addr + 40, pdinfo->logicalst); - pwrite_w(pdinfo_addr + 44, pdinfo->errlogst); - pwrite_w(pdinfo_addr + 48, pdinfo->errlogsz); - pwrite_w(pdinfo_addr + 52, pdinfo->mfgst); - pwrite_w(pdinfo_addr + 56, pdinfo->mfgsz); - pwrite_w(pdinfo_addr + 60, pdinfo->defectst); - pwrite_w(pdinfo_addr + 64, pdinfo->defectsz); - pwrite_w(pdinfo_addr + 68, pdinfo->relno); - pwrite_w(pdinfo_addr + 72, pdinfo->relst); - pwrite_w(pdinfo_addr + 76, pdinfo->relsz); - pwrite_w(pdinfo_addr + 80, pdinfo->relnext); + pwrite_w(pdinfo_addr + 24, pdinfo->cyls, BUS_PER); + pwrite_w(pdinfo_addr + 28, pdinfo->tracks, BUS_PER); + pwrite_w(pdinfo_addr + 32, pdinfo->sectors, BUS_PER); + pwrite_w(pdinfo_addr + 36, pdinfo->bytes, BUS_PER); + pwrite_w(pdinfo_addr + 40, pdinfo->logicalst, BUS_PER); + pwrite_w(pdinfo_addr + 44, pdinfo->errlogst, BUS_PER); + pwrite_w(pdinfo_addr + 48, pdinfo->errlogsz, BUS_PER); + pwrite_w(pdinfo_addr + 52, pdinfo->mfgst, BUS_PER); + pwrite_w(pdinfo_addr + 56, pdinfo->mfgsz, BUS_PER); + pwrite_w(pdinfo_addr + 60, pdinfo->defectst, BUS_PER); + pwrite_w(pdinfo_addr + 64, pdinfo->defectsz, BUS_PER); + pwrite_w(pdinfo_addr + 68, pdinfo->relno, BUS_PER); + pwrite_w(pdinfo_addr + 72, pdinfo->relst, BUS_PER); + pwrite_w(pdinfo_addr + 76, pdinfo->relsz, BUS_PER); + pwrite_w(pdinfo_addr + 80, pdinfo->relnext, BUS_PER); /* Now something horrible happens. We sneak RIGHT off the end of * the pdinfo struct and reach deep into the pdsector struct that * it is part of. */ - pwrite_w(pdinfo_addr + 128, maxpass); + pwrite_w(pdinfo_addr + 128, maxpass, BUS_PER); } /* @@ -257,7 +248,7 @@ static void ctc_update_vtoc(uint32 maxpass, * expects response parameters to be placed in specific fields of the * Completion Queue entry. It can be confusing to follow. */ -static void ctc_cmd(uint8 cid, +static void ctc_cmd(uint8 slot, cio_entry *rqe, uint8 *rapp_data, cio_entry *cqe, uint8 *capp_data) { @@ -283,7 +274,7 @@ static void ctc_cmd(uint8 cid, switch(rqe->opcode) { case CIO_DLM: for (i = 0; i < rqe->byte_count; i++) { - ctc_crc = cio_crc32_shift(ctc_crc, pread_b(rqe->address + i)); + ctc_crc = cio_crc32_shift(ctc_crc, pread_b(rqe->address + i, BUS_PER)); } sim_debug(TRACE_DBG, &ctc_dev, "[ctc_cmd] CIO Download Memory: bytecnt=%04x " @@ -311,11 +302,11 @@ static void ctc_cmd(uint8 cid, if (ctc_crc == CTC_DIAG_CRC1 || ctc_crc == CTC_DIAG_CRC2 || ctc_crc == CTC_DIAG_CRC3) { - pwrite_h(0x200f000, 0x1); /* Test success */ - pwrite_h(0x200f002, 0x0); /* Test Number */ - pwrite_h(0x200f004, 0x0); /* Actual */ - pwrite_h(0x200f006, 0x0); /* Expected */ - pwrite_b(0x200f008, 0x1); /* Success flag again */ + pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */ + pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */ + pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */ + pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */ + pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */ } /* An interesting (?) side-effect of FORCE FUNCTION CALL is @@ -323,7 +314,7 @@ static void ctc_cmd(uint8 cid, * required in order for new commands to work. In fact, an * INT0/INT1 combo _without_ a RESET can sysgen the board. So, * we reset the command bits here. */ - cio[cid].sysgen_s = 0; + cio[slot].sysgen_s = 0; cqe->opcode = CTC_SUCCESS; break; case CIO_DOS: @@ -339,9 +330,9 @@ static void ctc_cmd(uint8 cid, rqe->opcode); delay = DELAY_DSD; /* Write subdevice information to the host. */ - pwrite_h(rqe->address, CTC_NUM_SD); - pwrite_h(rqe->address + 2, CTC_SD_FT25); - pwrite_h(rqe->address + 4, CTC_SD_FD5); + pwrite_h(rqe->address, CTC_NUM_SD, BUS_PER); + pwrite_h(rqe->address + 2, CTC_SD_FT25, BUS_PER); + pwrite_h(rqe->address + 4, CTC_SD_FD5, BUS_PER); cqe->opcode = CTC_SUCCESS; break; case CTC_FORMAT: @@ -503,7 +494,7 @@ static void ctc_cmd(uint8 cid, ctc_state[dev].time += 10; for (j = 0; j < VTOC_SECSZ; j++) { /* Fill the buffer */ - sec_buf[j] = pread_b(rqe->address + (b * VTOC_SECSZ) + j); + sec_buf[j] = pread_b(rqe->address + (b * VTOC_SECSZ) + j, BUS_PER); } lba = blkno + b; result = sim_disk_wrsect(&ctc_unit, lba, sec_buf, &secrw, 1); @@ -601,7 +592,7 @@ static void ctc_cmd(uint8 cid, offset = j; } c = sec_buf[offset]; - pwrite_b(dest++, c); + pwrite_b(dest++, c, BUS_PER); ctc_state[dev].bytnum++; } } else { @@ -634,10 +625,10 @@ static void ctc_cmd(uint8 cid, break; } - cio_irq(cid, rqe->subdevice, delay); + cio_irq(slot, rqe->subdevice, delay); } -void ctc_sysgen(uint8 cid) +void ctc_sysgen(uint8 slot) { cio_entry cqe = {0}; uint8 rapp_data[12] = {0}; @@ -645,23 +636,23 @@ void ctc_sysgen(uint8 cid) ctc_crc = 0; sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] Handling Sysgen.\n"); - sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqp=%08x\n", cio[cid].rqp); - sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqp=%08x\n", cio[cid].cqp); - sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqs=%d\n", cio[cid].rqs); - sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqs=%d\n", cio[cid].cqs); - sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] ivec=%d\n", cio[cid].ivec); - sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] no_rque=%d\n", cio[cid].no_rque); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqp=%08x\n", cio[slot].rqp); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqp=%08x\n", cio[slot].cqp); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqs=%d\n", cio[slot].rqs); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqs=%d\n", cio[slot].cqs); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] ivec=%d\n", cio[slot].ivec); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] no_rque=%d\n", cio[slot].no_rque); cqe.opcode = 3; /* Sysgen success! */ - cio_cexpress(cid, CTQCESIZE, &cqe, rapp_data); - cio_cqueue(cid, CIO_STAT, CTQCESIZE, &cqe, rapp_data); + cio_cexpress(slot, CTQCESIZE, &cqe, rapp_data); + cio_cqueue(slot, CIO_STAT, CTQCESIZE, &cqe, rapp_data); - int_cid = cid; + int_slot = slot; sim_activate_after(&ctc_unit, DELAY_SYSGEN); } -void ctc_express(uint8 cid) +void ctc_express(uint8 slot) { cio_entry rqe, cqe; uint8 rapp_data[12] = {0}; @@ -669,16 +660,13 @@ void ctc_express(uint8 cid) sim_debug(TRACE_DBG, &ctc_dev, "[ctc_express] Handling Express Request\n"); - cio_rexpress(cid, CTQRESIZE, &rqe, rapp_data); - ctc_cmd(cid, &rqe, rapp_data, &cqe, capp_data); + cio_rexpress(slot, CTQRESIZE, &rqe, rapp_data); + ctc_cmd(slot, &rqe, rapp_data, &cqe, capp_data); - dump_entry(TRACE_DBG, &ctc_dev, "COMPLETION", - CTQCESIZE, &cqe, capp_data); - - cio_cexpress(cid, CTQCESIZE, &cqe, capp_data); + cio_cexpress(slot, CTQCESIZE, &cqe, capp_data); } -void ctc_full(uint8 cid) +void ctc_full(uint8 slot) { cio_entry rqe, cqe; uint8 rapp_data[12] = {0}; @@ -686,69 +674,35 @@ void ctc_full(uint8 cid) sim_debug(TRACE_DBG, &ctc_dev, "[ctc_full] Handling Full Request\n"); - while (cio_cqueue_avail(cid, CTQCESIZE) && - cio_rqueue(cid, TAPE_DEV, CTQRESIZE, &rqe, rapp_data) == SCPE_OK) { - ctc_cmd(cid, &rqe, rapp_data, &cqe, capp_data); + while (cio_cqueue_avail(slot, CTQCESIZE) && + cio_rqueue(slot, TAPE_DEV, CTQRESIZE, &rqe, rapp_data) == SCPE_OK) { + ctc_cmd(slot, &rqe, rapp_data, &cqe, capp_data); } - cio_cqueue(cid, CIO_STAT, CTQCESIZE, &cqe, capp_data); + cio_cqueue(slot, CIO_STAT, CTQCESIZE, &cqe, capp_data); } t_stat ctc_reset(DEVICE *dptr) { - uint8 cid; + uint8 slot; + t_stat r; ctc_crc = 0; - sim_debug(TRACE_DBG, &ctc_dev, - "[ctc_reset] Resetting CTC device\n"); - memset(ctc_state, 0, 2 * sizeof(CTC_STATE)); if (dptr->flags & DEV_DIS) { - sim_debug(TRACE_DBG, &ctc_dev, - "[ctc_reset] REMOVING CARD\n"); - - for (cid = 0; cid < CIO_SLOTS; cid++) { - if (cio[cid].id == CTC_ID) { - break; - } - } - - if (cid == CIO_SLOTS) { - /* No card was ever attached */ - return SCPE_OK; - } - - 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; - + cio_remove_all(CTC_ID); ctc_conf = FALSE; - } else if (!ctc_conf) { - sim_debug(TRACE_DBG, &ctc_dev, - "[ctc_reset] ATTACHING CARD\n"); + return SCPE_OK; + } - /* Find the first avaialable slot */ - for (cid = 0; cid < CIO_SLOTS; cid++) { - if (cio[cid].id == 0) { - break; - } + if (!ctc_conf) { + r = cio_install(CTC_ID, "CTC", CTC_IPL, + &ctc_express, &ctc_full, &ctc_sysgen, NULL, + &slot); + if (r != SCPE_OK) { + return r; } - - /* Do we have room? */ - if (cid == CIO_SLOTS) { - return SCPE_NXM; - } - - cio[cid].id = CTC_ID; - cio[cid].ipl = CTC_IPL; - cio[cid].exp_handler = &ctc_express; - cio[cid].full_handler = &ctc_full; - cio[cid].sysgen = &ctc_sysgen; - ctc_conf = TRUE; } @@ -759,20 +713,20 @@ t_stat ctc_svc(UNIT *uptr) { uint16 lp, ulp; - if (cio[int_cid].ivec > 0) { + if (cio[int_slot].ivec > 0) { sim_debug(TRACE_DBG, &ctc_dev, "[cio_svc] IRQ for board %d (VEC=%d)\n", - int_cid, cio[int_cid].ivec); - CIO_SET_INT(int_cid); + int_slot, cio[int_slot].ivec); + CIO_SET_INT(int_slot); } /* Check to see if the completion queue has more work in it. We * need to schedule an interrupt for each job if we've fallen * behind (this should be rare) */ - lp = cio_c_lp(int_cid, CTQCESIZE); - ulp = cio_c_ulp(int_cid, CTQCESIZE); + lp = cio_c_lp(int_slot, CTQCESIZE); + ulp = cio_c_ulp(int_slot, CTQCESIZE); - if ((ulp + CTQCESIZE) % (CTQCESIZE * cio[int_cid].cqs) != lp) { + if ((ulp + CTQCESIZE) % (CTQCESIZE * cio[int_slot].cqs) != lp) { sim_debug(TRACE_DBG, &ctc_dev, "[cio_svc] Completion queue has fallen behind (lp=%04x ulp=%04x)\n", lp, ulp); @@ -792,117 +746,3 @@ t_stat ctc_detach(UNIT *uptr) { return sim_disk_detach(uptr); } - -t_stat ctc_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc) -{ - return ctc_show_queue_common(st, uptr, val, desc, TRUE); -} - -t_stat ctc_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc) -{ - return ctc_show_queue_common(st, uptr, val, desc, FALSE); -} - - -static t_stat ctc_show_queue_common(FILE *st, UNIT *uptr, int32 val, - CONST void *desc, t_bool rq) -{ - uint8 cid; - char *cptr = (char *) desc; - t_stat result; - uint32 ptr, size, no_rque, i, j; - uint8 op, dev, seq, cmdstat; - - if (cptr) { - cid = (uint8) get_uint(cptr, 10, 12, &result); - if (result != SCPE_OK) { - return SCPE_ARG; - } - } else { - return SCPE_ARG; - } - - /* If the card is not sysgen'ed, give up */ - if (cio[cid].sysgen_s != CIO_SYSGEN) { - fprintf(st, "No card in slot %d, or card has not completed sysgen\n", cid); - return SCPE_ARG; - } - - if (rq) { - ptr = cio[cid].rqp; - size = cio[cid].rqs; - no_rque = cio[cid].no_rque; - fprintf(st, "Dumping %d Request Queues\n", no_rque); - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "EXPRESS ENTRY:\n"); - fprintf(st, " Byte Count: %d\n", pread_h(ptr)); - fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2)); - fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3)); - fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); - fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); - ptr += CTQRESIZE; - - for (i = 0; i < no_rque; i++) { - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "REQUEST QUEUE %d\n", i); - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / CTQRESIZE); - fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / CTQRESIZE); - fprintf(st, "---------------------------------------------------------\n"); - ptr += 4; - for (j = 0; j < size; j++) { - dev = pread_b(ptr + 2); - op = pread_b(ptr + 3); - seq = (dev & 0x40) >> 6; - cmdstat = (dev & 0x80) >> 7; - fprintf(st, "REQUEST ENTRY %d\n", j); - fprintf(st, " Byte Count: %d\n", pread_h(ptr)); - fprintf(st, " Subdevice: %d\n", dev & 0x3f); - fprintf(st, " Cmd/Stat: %d\n", cmdstat); - fprintf(st, " Seqbit: %d\n", seq); - fprintf(st, " Opcode: 0x%02x (%d)\n", op, op); - fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); - fprintf(st, " App Data: 0x%08x 0x%08x 0x%08x\n", - pread_w(ptr + 8), pread_w(ptr + 12), pread_w(ptr + 16)); - ptr += CTQRESIZE; - } - } - } else { - ptr = cio[cid].cqp; - size = cio[cid].cqs; - no_rque = 0; /* Not used */ - fprintf(st, "Dumping Completion Queue\n"); - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "EXPRESS ENTRY:\n"); - fprintf(st, " Byte Count: %d\n", pread_h(ptr)); - fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2)); - fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3)); - fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); - fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); - ptr += CTQCESIZE; - - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / CTQCESIZE); - fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / CTQCESIZE); - fprintf(st, "---------------------------------------------------------\n"); - ptr += 4; - for (i = 0; i < size; i++) { - dev = pread_b(ptr + 2); - op = pread_b(ptr + 3); - seq = (dev & 0x40) >> 6; - cmdstat = (dev & 0x80) >> 7; - fprintf(st, "COMPLETION ENTRY %d\n", i); - fprintf(st, " Byte Count: %d\n", pread_h(ptr)); - fprintf(st, " Subdevice: %d\n", dev & 0x3f); - fprintf(st, " Cmd/Stat: %d\n", cmdstat); - fprintf(st, " Seqbit: %d\n", seq); - fprintf(st, " Opcode: 0x%02x (%d)\n", op, op); - fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); - fprintf(st, " App Data: 0x%08x 0x%08x\n", - pread_w(ptr + 8), pread_w(ptr + 12)); - ptr += CTQCESIZE; - } - } - - return SCPE_OK; -} diff --git a/3B2/3b2_ctc.h b/3B2/3b2_ctc.h index 67f0b884..0d3cb700 100644 --- a/3B2/3b2_ctc.h +++ b/3B2/3b2_ctc.h @@ -1,6 +1,6 @@ -/* 3b2_ctc.h: AT&T 3B2 Model 400 "CTC" feature card +/* 3b2_ctc.h: CM195H 23MB Cartridge Tape Controller CIO Card - Copyright (c) 2018, Seth J. Morabito + Copyright (c) 2018-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -146,8 +146,8 @@ t_stat ctc_reset(DEVICE *dptr); t_stat ctc_svc(UNIT *uptr); t_stat ctc_attach(UNIT *uptr, CONST char *cptr); t_stat ctc_detach(UNIT *uptr); -void ctc_sysgen(uint8 cid); -void ctc_express(uint8 cid); -void ctc_full(uint8 cid); +void ctc_sysgen(uint8 slot); +void ctc_express(uint8 slot); +void ctc_full(uint8 slot); #endif /* _3B2_CTC_H_ */ diff --git a/3B2/3b2_defs.h b/3B2/3b2_defs.h index 9358e4bf..88587126 100644 --- a/3B2/3b2_defs.h +++ b/3B2/3b2_defs.h @@ -1,6 +1,6 @@ /* 3b2_defs.h: AT&T 3B2 Shared Simulator Definitions - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -81,6 +81,9 @@ #define PCHAR(c) (((char) (c) >= 0x20 && (char) (c) < 0x7f) ? (char) (c) : '.') +#define ROM_SIZE (128 * 1024) +#define POLL_WAIT 70000 + #define UNIT_V_EXBRK (UNIT_V_UF + 0) #define UNIT_V_OPBRK (UNIT_V_UF + 1) #define UNIT_EXBRK (1u << UNIT_V_EXBRK) @@ -88,6 +91,7 @@ #define EX_V_FLAG 1 << 21 +#define ROM_BASE 0 #define PHYS_MEM_BASE 0x2000000 #define MSIZ_512K 0x80000 @@ -131,10 +135,8 @@ #define TIMER_INTERVAL 1 #define TIMER_BUS 2 -/* Timer */ -#define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */ -#define TPS_CLK 100 /* 100 ticks per second */ - +/* Timers */ +#define TMR_CLK 0 /* Calibrated 100Hz timer */ /* Global symbols */ @@ -159,6 +161,6 @@ extern DEVICE tto_dev; #if defined(REV3) extern DEVICE flt_dev; extern DEVICE ha_dev; -#endif +#endif /* defined(REV3) */ -#endif +#endif /* _3B2_DEFS_H_ */ diff --git a/3B2/3b2_dmac.c b/3B2/3b2_dmac.c index 08273b99..85a44aa5 100644 --- a/3B2/3b2_dmac.c +++ b/3B2/3b2_dmac.c @@ -1,6 +1,6 @@ -/* 3b2_dmac.c: AT&T 3B2 DMA Controller Implementation +/* 3b2_dmac.c: AM9517 DMA Controller - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -38,6 +38,8 @@ #include "3b2_if.h" #include "3b2_iu.h" #include "3b2_mem.h" +#include "3b2_stddev.h" +#include "3b2_csr.h" DMA_STATE dma_state; @@ -71,9 +73,13 @@ dmac_dma_handler device_dma_handlers[] = { {0, 0, NULL, NULL, NULL } }; -uint32 dma_address(uint8 channel, uint32 offset, t_bool r) { +uint32 dma_address(uint8 channel, uint32 offset) { uint32 addr, page; - addr = (PHYS_MEM_BASE + (uint32)(dma_state.channels[channel].addr) + offset); + if (DMA_DECR(channel)) { + addr = (PHYS_MEM_BASE + (uint32)(dma_state.channels[channel].addr) - offset); + } else { + addr = (PHYS_MEM_BASE + (uint32)(dma_state.channels[channel].addr) + offset); + } #if defined (REV3) page = (uint32)dma_state.channels[channel].page; #else @@ -94,6 +100,7 @@ t_stat dmac_reset(DEVICE *dptr) for (i = 0; i < 4; i++) { dma_state.channels[i].page = 0; dma_state.channels[i].addr = 0; + dma_state.channels[i].mode = 0; dma_state.channels[i].wcount = 0; dma_state.channels[i].addr_c = 0; dma_state.channels[i].wcount_c = -1; @@ -111,83 +118,83 @@ uint32 dmac_read(uint32 pa, size_t size) reg = pa & 0xff; switch (base) { - case DMA_C: /* 0x48xxx */ + case DMA_C: switch (reg) { case 0: /* channel 0 current address reg */ data = ((dma_state.channels[0].addr_c) >> (dma_state.bff * 8)) & 0xff; sim_debug(READ_MSG, &dmac_dev, - "[%08x] Reading Channel 0 Addr Reg: %08x\n", - R[NUM_PC], data); + "Reading Channel 0 Addr Reg: %08x\n", + data); dma_state.bff ^= 1; break; case 1: /* channel 0 current address reg */ data = ((dma_state.channels[0].wcount_c) >> (dma_state.bff * 8)) & 0xff; sim_debug(READ_MSG, &dmac_dev, - "[%08x] Reading Channel 0 Addr Count Reg: %08x\n", - R[NUM_PC], data); + "Reading Channel 0 Addr Count Reg: %08x\n", + data); dma_state.bff ^= 1; break; case 2: /* channel 1 current address reg */ data = ((dma_state.channels[1].addr_c) >> (dma_state.bff * 8)) & 0xff; sim_debug(READ_MSG, &dmac_dev, - "[%08x] Reading Channel 1 Addr Reg: %08x\n", - R[NUM_PC], data); + "Reading Channel 1 Addr Reg: %08x\n", + data); dma_state.bff ^= 1; break; case 3: /* channel 1 current address reg */ data = ((dma_state.channels[1].wcount_c) >> (dma_state.bff * 8)) & 0xff; sim_debug(READ_MSG, &dmac_dev, - "[%08x] Reading Channel 1 Addr Count Reg: %08x\n", - R[NUM_PC], data); + "Reading Channel 1 Addr Count Reg: %08x\n", + data); dma_state.bff ^= 1; break; case 4: /* channel 2 current address reg */ data = ((dma_state.channels[2].addr_c) >> (dma_state.bff * 8)) & 0xff; sim_debug(READ_MSG, &dmac_dev, - "[%08x] Reading Channel 2 Addr Reg: %08x\n", - R[NUM_PC], data); + "Reading Channel 2 Addr Reg: %08x\n", + data); dma_state.bff ^= 1; break; case 5: /* channel 2 current address reg */ data = ((dma_state.channels[2].wcount_c) >> (dma_state.bff * 8)) & 0xff; sim_debug(READ_MSG, &dmac_dev, - "[%08x] Reading Channel 2 Addr Count Reg: %08x\n", - R[NUM_PC], data); + "Reading Channel 2 Addr Count Reg: %08x\n", + data); dma_state.bff ^= 1; break; case 6: /* channel 3 current address reg */ data = ((dma_state.channels[3].addr_c) >> (dma_state.bff * 8)) & 0xff; sim_debug(READ_MSG, &dmac_dev, - "[%08x] Reading Channel 3 Addr Reg: %08x\n", - R[NUM_PC], data); + "Reading Channel 3 Addr Reg: %08x\n", + data); dma_state.bff ^= 1; break; case 7: /* channel 3 current address reg */ data = ((dma_state.channels[3].wcount_c) >> (dma_state.bff * 8)) & 0xff; sim_debug(READ_MSG, &dmac_dev, - "[%08x] Reading Channel 3 Addr Count Reg: %08x\n", - R[NUM_PC], data); + "Reading Channel 3 Addr Count Reg: %08x\n", + data); dma_state.bff ^= 1; break; case 8: data = dma_state.status; sim_debug(READ_MSG, &dmac_dev, - "[%08x] Reading DMAC Status %08x\n", - R[NUM_PC], data); + "Reading DMAC Status %08x\n", + data); dma_state.status = 0; break; default: sim_debug(READ_MSG, &dmac_dev, - "[%08x] DMAC READ %lu B @ %08x\n", - R[NUM_PC], size, pa); + "DMAC READ %lu B @ %08x\n", + size, pa); data = 0; } return data; default: sim_debug(READ_MSG, &dmac_dev, - "[%08x] [BASE: %08x] DMAC READ %lu B @ %08x\n", - R[NUM_PC], base, size, pa); + "[BASE: %08x] DMAC READ %lu B @ %08x\n", + base, size, pa); return 0; } } @@ -200,6 +207,12 @@ void dmac_program(uint8 reg, uint8 val) uint8 channel_id, i, chan_num; dma_channel *channel; +#if defined(REV3) + /* TODO: More general DMA interrupt clearing */ + CPU_CLR_INT(INT_UART_DMA); + CLR_CSR(CSRDMA); +#endif + if (reg < 8) { switch (reg) { case 0: @@ -259,13 +272,13 @@ void dmac_program(uint8 reg, uint8 val) case 8: /* Command */ dma_state.command = val; sim_debug(WRITE_MSG, &dmac_dev, - "[%08x] Command: val=%02x\n", - R[NUM_PC], val); + "Command: val=%02x\n", + val); break; case 9: /* Request */ sim_debug(WRITE_MSG, &dmac_dev, - "[%08x] Request set: val=%02x\n", - R[NUM_PC], val); + "Request set: val=%02x\n", + val); dma_state.request = val; break; case 10: /* Write Single Mask Register Bit */ @@ -281,14 +294,15 @@ void dmac_program(uint8 reg, uint8 val) } sim_debug(WRITE_MSG, &dmac_dev, - "[%08x] Write Single Mask Register Bit. channel=%d set/clear=%02x\n", - R[NUM_PC], channel_id, (val >> 2) & 1); + "Write Single Mask Register Bit. channel=%d set/clear=%02x\n", + channel_id, (val >> 2) & 1); break; case 11: /* Mode */ + channel_id = val & 3; sim_debug(WRITE_MSG, &dmac_dev, - "[%08x] Mode Set. val=%02x\n", - R[NUM_PC], val); - dma_state.mode = val; + "Mode Set. channel=%d val=%02x\n", + channel_id, val); + dma_state.channels[channel_id].mode = val; break; case 12: /* Clear Byte Pointer Flip/Flop */ dma_state.bff = 0; @@ -308,19 +322,19 @@ void dmac_program(uint8 reg, uint8 val) break; case 15: /* Write All Mask Register Bits */ sim_debug(WRITE_MSG, &dmac_dev, - "[%08x] Write DMAC mask (all bits). Val=%02x\n", - R[NUM_PC], val); + "Write DMAC mask (all bits). Val=%02x\n", + val); dma_state.mask = val & 0xf; break; case 16: /* Clear DMAC Interrupt */ sim_debug(WRITE_MSG, &dmac_dev, - "[%08x] Clear DMAC Interrupt in DMAC. val=%02x\n", - R[NUM_PC], val); + "Clear DMA Interrupt in DMAC. val=%02x\n", + val); break; default: sim_debug(WRITE_MSG, &dmac_dev, - "[%08x] Unhandled DMAC write. reg=%x val=%02x\n", - R[NUM_PC], reg, val); + "Unhandled DMAC write. reg=%x val=%02x\n", + reg, val); break; } } @@ -398,46 +412,45 @@ void dmac_generic_dma(uint8 channel, uint32 service_address) i = chan->wcount_c; - /* TODO: This does not handle decrement-mode transfers, - which don't seem to be used in SVR3 */ + /* TODO: This assumes every transfer is a block mode, which is not + guaranteed to be valid, but is likely safe? */ - switch ((dma_state.mode >> 2) & 0xf) { - case DMA_MODE_VERIFY: + switch (DMA_XFER(channel)) { + case DMA_XFER_VERIFY: sim_debug(EXECUTE_MSG, &dmac_dev, - "[%08x] [dmac_generic_dma channel=%d] unhandled VERIFY request.\n", - R[NUM_PC], channel); + "[dmac_generic_dma channel=%d] unhandled VERIFY request.\n", + channel); break; - case DMA_MODE_WRITE: + case DMA_XFER_WRITE: sim_debug(EXECUTE_MSG, &dmac_dev, - "[%08x] [dmac_generic_dma channel=%d] write: %d bytes to %08x from %08x (page=%04x addr=%08x)\n", - R[NUM_PC], channel, + "[dmac_generic_dma channel=%d] write: %d bytes to %08x from %08x (page=%04x addr=%08x)\n", + channel, chan->wcount + 1, - dma_address(channel, 0, TRUE), + dma_address(channel, 0), 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); - chan->addr_c = dma_state.channels[channel].addr + chan->ptr; - chan->ptr++; - data = pread_b(service_address); - write_b(addr, data); + addr = dma_address(channel, chan->ptr++); + chan->addr_c = addr; + data = pread_b(service_address, BUS_PER); + write_b(addr, data, BUS_PER); } break; - case DMA_MODE_READ: + case DMA_XFER_READ: sim_debug(EXECUTE_MSG, &dmac_dev, - "[%08x] [dmac_generic_dma channel=%d] read: %d bytes from %08x to %08x\n", - R[NUM_PC], channel, + "[dmac_generic_dma channel=%d] read: %d bytes from %08x to %08x\n", + channel, chan->wcount + 1, - dma_address(channel, 0, TRUE), + dma_address(channel, 0), service_address); for (; i >= 0; i--) { chan->wcount_c = i; - addr = dma_address(channel, chan->ptr++, TRUE); - chan->addr_c = dma_state.channels[channel].addr + chan->ptr; - data = pread_b(addr); - write_b(service_address, data); + addr = dma_address(channel, chan->ptr++); + chan->addr_c = addr; + data = pread_b(addr, BUS_PER); + write_b(service_address, data, BUS_PER); } break; } diff --git a/3B2/3b2_dmac.h b/3B2/3b2_dmac.h index a5fe8e79..cda90371 100644 --- a/3B2/3b2_dmac.h +++ b/3B2/3b2_dmac.h @@ -1,6 +1,6 @@ -/* 3b2_dmac.h: AT&T 3B2 DMA Controller Header +/* 3b2_dmac.h: AM9517 DMA Controller - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -33,14 +33,20 @@ #include "3b2_defs.h" -#define DMA_MODE_VERIFY 0 -#define DMA_MODE_WRITE 1 /* Write to memory from device */ -#define DMA_MODE_READ 2 /* Read from memory to device */ +#define DMA_XFER_VERIFY 0 +#define DMA_XFER_WRITE 1 /* Write to memory from device */ +#define DMA_XFER_READ 2 /* Read from memory to device */ #define DMA_IF_READ (IFBASE + IF_DATA_REG) +#define DMA_MODE(C) ((dma_state.channels[(C)].mode >> 6) & 3) +#define DMA_DECR(C) ((dma_state.channels[(C)].mode >> 5) & 1) +#define DMA_AUTOINIT(C) ((dma_state.channels[(C)].mode >> 4) & 1) +#define DMA_XFER(C) ((dma_state.channels[(C)].mode >> 2) & 3) + typedef struct { - uint16 page; + uint8 mode; /* Channel mode */ + uint16 page; /* Memory page */ uint16 addr; /* Original addr */ uint16 wcount; /* Original wcount */ uint16 addr_c; /* Current addr */ @@ -57,7 +63,6 @@ typedef struct { /* DMAC programmable registers */ uint8 command; - uint8 mode; uint8 request; uint8 mask; uint8 status; @@ -77,7 +82,7 @@ uint32 dmac_read(uint32 pa, size_t size); void dmac_write(uint32 pa, uint32 val, size_t size); void dmac_service_drqs(); void dmac_generic_dma(uint8 channel, uint32 service_address); -uint32 dma_address(uint8 channel, uint32 offset, t_bool r); +uint32 dma_address(uint8 channel, uint32 offset); extern DMA_STATE dma_state; diff --git a/3B2/3b2_id.c b/3B2/3b2_id.c index 71762d6d..3b83d96a 100644 --- a/3B2/3b2_id.c +++ b/3B2/3b2_id.c @@ -1,6 +1,6 @@ -/* 3b2_d.h: AT&T 3B2 Model 400 Hard Disk (uPD7261) Implementation +/* 3b2_d.c: uPD7261 Integrated Disk Controller - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -167,9 +167,9 @@ MTAB id_mod[] = { &id_set_type, NULL, NULL, "Set HD161 Disk Type" }, { MTAB_XTD|MTAB_VUN, 0, "TYPE", NULL, NULL, &id_show_type, NULL, "Display device type" }, - { ID_AUTOSIZE, ID_AUTOSIZE, "autosize", "AUTOSIZE", + { ID_AUTOSIZE, ID_AUTOSIZE, "autosize", "AUTOSIZE", NULL, NULL, NULL, "Set type based on file size at attach" }, - { ID_AUTOSIZE, 0, "noautosize", "NOAUTOSIZE", + { ID_AUTOSIZE, 0, "noautosize", "NOAUTOSIZE", NULL, NULL, NULL, "Disable disk autosize on attach" }, { 0 } }; @@ -245,15 +245,14 @@ t_stat id_ctlr_svc(UNIT *uptr) switch (cmd) { case ID_CMD_SIS: sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tINTR\t\tCOMPLETING Sense Interrupt Status.\n", - R[NUM_PC]); + "INTR\t\tCOMPLETING Sense Interrupt Status.\n"); id_data[0] = id_int_status; id_int_status = 0; break; default: sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tINTR\t\tCOMPLETING OTHER COMMAND 0x%x (CONTROLLER)\n", - R[NUM_PC], cmd); + "INTR\t\tCOMPLETING OTHER COMMAND 0x%x (CONTROLLER)\n", + cmd); break; } @@ -299,15 +298,15 @@ t_stat id_unit_svc(UNIT *uptr) case ID_SEEK_0: id_set_status(ID_STAT_CEH); sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tINTR\t\tCOMPLETING Recal/Seek SEEK_0 UNIT %d\n", - R[NUM_PC], unit); + "INTR\t\tCOMPLETING Recal/Seek SEEK_0 UNIT %d\n", + unit); id_seek_state[unit] = ID_SEEK_1; id_activate(uptr, 8000); /* TODO: Correct Delay based on steps */ break; case ID_SEEK_1: sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tINTR\t\tCOMPLETING Recal/Seek SEEK_1 UNIT %d\n", - R[NUM_PC], unit); + "INTR\t\tCOMPLETING Recal/Seek SEEK_1 UNIT %d\n", + unit); id_seek_state[unit] = ID_SEEK_NONE; id_set_status(ID_STAT_SRQ); uptr->u4 = 0; /* Only clear out the command on a SEEK_1, never a SEEK_0 */ @@ -319,14 +318,14 @@ t_stat id_unit_svc(UNIT *uptr) break; default: sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tINTR\t\tERROR, NOT SEEK_0 OR SEEK_1, UNIT %d\n", - R[NUM_PC], unit); + "INTR\t\tERROR, NOT SEEK_0 OR SEEK_1, UNIT %d\n", + unit); break; } } else { sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tINTR\t\tCOMPLETING NON-POLLING Recal/Seek UNIT %d\n", - R[NUM_PC], unit); + "INTR\t\tCOMPLETING NON-POLLING Recal/Seek UNIT %d\n", + unit); id_set_status(ID_STAT_CEH); uptr->u4 = 0; if (uptr->flags & UNIT_ATT) { @@ -339,8 +338,8 @@ t_stat id_unit_svc(UNIT *uptr) break; case ID_CMD_SUS: sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tINTR\t\tCOMPLETING Sense Unit Status UNIT %d\n", - R[NUM_PC], unit); + "INTR\t\tCOMPLETING Sense Unit Status UNIT %d\n", + unit); id_set_status(ID_STAT_CEH); uptr->u4 = 0; if ((uptr->flags & UNIT_ATT) == 0) { @@ -357,8 +356,8 @@ t_stat id_unit_svc(UNIT *uptr) break; default: sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tINTR\t\tCOMPLETING OTHER COMMAND 0x%x UNIT %d\n", - R[NUM_PC], cmd, unit); + "INTR\t\tCOMPLETING OTHER COMMAND 0x%x UNIT %d\n", + cmd, unit); id_set_status(ID_STAT_CEH); uptr->u4 = 0; break; @@ -399,7 +398,7 @@ t_stat id_attach(UNIT *uptr, CONST char *cptr) { static const char *drives[] = {"HD30", "HD72", "HD72C", "HD135", "HD161", NULL}; - return sim_disk_attach_ex(uptr, cptr, 512, 1, TRUE, 0, id_dtab[ID_GET_DTYPE(uptr->flags)].name, + return sim_disk_attach_ex(uptr, cptr, 512, 1, TRUE, 0, id_dtab[ID_GET_DTYPE(uptr->flags)].name, 0, 0, (uptr->flags & ID_AUTOSIZE) ? drives : NULL); } @@ -485,8 +484,7 @@ uint32 id_read(uint32 pa, size_t size) * that's an error state. */ if (id_scnt == 0) { sim_debug(READ_MSG, &id_dev, - "[%08x] ERROR\tid_scnt = 0 but still in dma\n", - R[NUM_PC]); + "ERROR\tid_scnt = 0 but still in dma\n"); id_end_rw(ID_EST_OVR); return 0; } @@ -501,24 +499,21 @@ uint32 id_read(uint32 pa, size_t size) if (sim_disk_rdsect(id_sel_unit, lba, id_buf, §sread, 1) == SCPE_OK) { if (sectsread !=1) { sim_debug(READ_MSG, &id_dev, - "[%08x]\tERROR: ASKED TO READ ONE SECTOR, READ: %d\n", - R[NUM_PC], sectsread); + "ERROR: ASKED TO READ ONE SECTOR, READ: %d\n", + sectsread); } id_update_chs(); } else { /* Uh-oh! */ sim_debug(READ_MSG, &id_dev, - "[%08x]\tRDATA READ ERROR. Failure from sim_disk_rdsect!\n", - R[NUM_PC]); + "RDATA READ ERROR. Failure from sim_disk_rdsect!\n"); id_end_rw(ID_EST_DER); return 0; } } data = id_buf[id_buf_ptr++]; - sim_debug(READ_MSG, &id_dev, - "[%08x]\tDATA\t%02x\n", - R[NUM_PC], data); + sim_debug(READ_MSG, &id_dev, "DATA\t%02x\n", data); /* Done with this current sector, update id_scnt */ if (id_buf_ptr >= ID_SEC_SIZE) { @@ -538,8 +533,8 @@ uint32 id_read(uint32 pa, size_t size) data = id_idfield[id_idfield_ptr++]; sim_debug(READ_MSG, &id_dev, - "[%08x]\tID DATA\t%02x\n", - R[NUM_PC], data); + "ID DATA\t%02x\n", + data); if (id_idfield_ptr >= ID_IDFIELD_LEN) { if (id_scnt-- > 0) { @@ -562,13 +557,12 @@ uint32 id_read(uint32 pa, size_t size) } else { if (id_dpr < ID_FIFO_LEN) { sim_debug(READ_MSG, &id_dev, - "[%08x]\tDATA\t%02x\n", - R[NUM_PC], id_data[id_dpr]); + "DATA\t%02x\n", + id_data[id_dpr]); return id_data[id_dpr++]; } else { sim_debug(READ_MSG, &id_dev, - "[%08x] ERROR\tFIFO OVERRUN\n", - R[NUM_PC]); + "ERROR\tFIFO OVERRUN\n"); return 0; } } @@ -576,14 +570,14 @@ uint32 id_read(uint32 pa, size_t size) break; case ID_CMD_STAT_REG: /* Status Register */ sim_debug(READ_MSG, &id_dev, - "[%08x]\tSTATUS\t%02x\n", - R[NUM_PC], id_status|id_drq); + "STATUS\t%02x\n", + id_status|id_drq); return id_status|(id_drq ? 1u : 0); } sim_debug(READ_MSG, &id_dev, - "[%08x] Read of unsuported register %x\n", - R[NUM_PC], id_status); + "Read of unsuported register %x\n", + id_status); return 0; } @@ -607,8 +601,7 @@ void id_write(uint32 pa, uint32 val, size_t size) * that's an error state. */ if (id_scnt == 0) { sim_debug(WRITE_MSG, &id_dev, - "[%08x] ERROR\tid_scnt = 0 but still in dma\n", - R[NUM_PC]); + "ERROR\tid_scnt = 0 but still in dma\n"); id_end_rw(ID_EST_OVR); return; } @@ -617,12 +610,11 @@ void id_write(uint32 pa, uint32 val, size_t size) if (id_buf_ptr < ID_SEC_SIZE) { id_buf[id_buf_ptr++] = (uint8)(val & 0xff); sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tDATA\t%02x\n", - R[NUM_PC], (uint8)(val & 0xff)); + "DATA\t%02x\n", + (uint8)(val & 0xff)); } else { sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tERROR\tWDATA OVERRUN\n", - R[NUM_PC]); + "ERROR\tWDATA OVERRUN\n"); id_end_rw(ID_EST_OVR); return; } @@ -637,8 +629,8 @@ void id_write(uint32 pa, uint32 val, size_t size) if (sim_disk_wrsect(id_sel_unit, lba, id_buf, §swritten, 1) == SCPE_OK) { if (sectswritten !=1) { sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tERROR: ASKED TO WRITE ONE SECTOR, WROTE: %d\n", - R[NUM_PC], sectswritten); + "ERROR: ASKED TO WRITE ONE SECTOR, WROTE: %d\n", + sectswritten); } id_update_chs(); if (--id_scnt == 0) { @@ -647,8 +639,8 @@ void id_write(uint32 pa, uint32 val, size_t size) } else { /* Uh-oh! */ sim_debug(WRITE_MSG, &id_dev, - "[%08x] ERROR\tWDATA WRITE ERROR. lba=%04x\n", - R[NUM_PC], lba); + "ERROR\tWDATA WRITE ERROR. lba=%04x\n", + lba); id_end_rw(ID_EST_DER); return; } @@ -656,14 +648,13 @@ void id_write(uint32 pa, uint32 val, size_t size) return; } else { sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tDATA\t%02x\n", - R[NUM_PC], val); + "DATA\t%02x\n", + val); if (id_dpw < ID_FIFO_LEN) { id_data[id_dpw++] = (uint8) val; } else { sim_debug(WRITE_MSG, &id_dev, - "[%08x] ERROR\tFIFO OVERRUN\n", - R[NUM_PC]); + "ERROR\tFIFO OVERRUN\n"); } } return; @@ -691,29 +682,29 @@ void id_handle_command(uint8 val) if (aux_cmd & ID_AUX_CLCE) { sim_debug(WRITE_MSG, &id_dev, - "[%08x] \tCOMMAND\t%02x\tAUX:CLCE\n", - R[NUM_PC], val); + "COMMAND\t%02x\tAUX:CLCE\n", + val); id_clr_status(ID_STAT_CEH|ID_STAT_CEL); } if (aux_cmd & ID_AUX_HSRQ) { sim_debug(WRITE_MSG, &id_dev, - "[%08x] \tCOMMAND\t%02x\tAUX:HSRQ\n", - R[NUM_PC], val); + "COMMAND\t%02x\tAUX:HSRQ\n", + val); id_set_srqm(TRUE); } if (aux_cmd & ID_AUX_CLB) { sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tAUX:CLBUF\n", - R[NUM_PC], val); + "COMMAND\t%02x\tAUX:CLBUF\n", + val); id_clear_fifo(); } if (aux_cmd & ID_AUX_RST) { sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tAUX:RESET\n", - R[NUM_PC], val); + "COMMAND\t%02x\tAUX:RESET\n", + val); id_clear_fifo(); sim_cancel(id_sel_unit); sim_cancel(id_ctlr_unit); @@ -765,15 +756,15 @@ void id_handle_command(uint8 val) switch(cmd) { case ID_CMD_SIS: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tSense Int. Status\n", - R[NUM_PC], val); + "COMMAND\t%02x\tSense Int. Status\n", + val); id_clr_status(ID_STAT_SRQ); /* SIS immediately de-asserts SRQ */ id_activate(id_ctlr_unit, ID_SIS_WAIT); break; case ID_CMD_SPEC: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tSpecify - ETN=%02x ESN=%02x\n", - R[NUM_PC], val, id_data[3], id_data[4]); + "COMMAND\t%02x\tSpecify - ETN=%02x ESN=%02x\n", + val, id_data[3], id_data[4]); id_dtlh = id_data[1]; id_etn = id_data[3]; id_esn = id_data[4]; @@ -782,14 +773,14 @@ void id_handle_command(uint8 val) break; case ID_CMD_SUS: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tSense Unit Status - %d\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tSense Unit Status - %d\n", + val, id_ua); id_activate(id_sel_unit, ID_SUS_WAIT); break; case ID_CMD_DERR: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tDetect Error\n", - R[NUM_PC], val); + "COMMAND\t%02x\tDetect Error\n", + val); id_activate(id_ctlr_unit, ID_CMD_WAIT); break; case ID_CMD_RECAL: @@ -798,13 +789,13 @@ void id_handle_command(uint8 val) id_seek_state[id_unit_num] = ID_SEEK_0; if (id_polling) { sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tRecalibrate - %d - POLLING\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tRecalibrate - %d - POLLING\n", + val, id_ua); id_activate(id_sel_unit, 1000); } else { sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tRecalibrate - %d - NORMAL\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tRecalibrate - %d - NORMAL\n", + val, id_ua); id_activate(id_sel_unit, (ID_RECAL_WAIT + (time * ID_SEEK_WAIT))); } break; @@ -818,20 +809,20 @@ void id_handle_command(uint8 val) if (id_polling) { sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tSeek - %d - POLLING\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tSeek - %d - POLLING\n", + val, id_ua); id_activate(id_sel_unit, 4000); } else { sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tSeek - %d - NORMAL\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tSeek - %d - NORMAL\n", + val, id_ua); id_activate(id_sel_unit, ID_SEEK_BASE + (time * ID_SEEK_WAIT)); } break; case ID_CMD_FMT: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tFormat - %d\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tFormat - %d\n", + val, id_ua); id_phn = id_data[0]; id_scnt = id_data[1]; @@ -850,12 +841,12 @@ void id_handle_command(uint8 val) lba = id_lba(id_cyl[id_unit_num], id_phn, sec++); if (sim_disk_wrsect(id_sel_unit, lba, id_buf, NULL, 1) == SCPE_OK) { sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tFORMAT: PHN=%d SCNT=%d PAT=%02x LBA=%04x\n", - R[NUM_PC], id_phn, id_scnt, pattern, lba); + "FORMAT: PHN=%d SCNT=%d PAT=%02x LBA=%04x\n", + id_phn, id_scnt, pattern, lba); } else { sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tFORMAT FAILED! PHN=%d SCNT=%d PAT=%02x LBA=%04x\n", - R[NUM_PC], id_phn, id_scnt, pattern, lba); + "FORMAT FAILED! PHN=%d SCNT=%d PAT=%02x LBA=%04x\n", + id_phn, id_scnt, pattern, lba); break; } } @@ -872,16 +863,16 @@ void id_handle_command(uint8 val) break; case ID_CMD_VID: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tVerify ID - %d\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tVerify ID - %d\n", + val, id_ua); id_data[0] = 0; id_data[1] = 0x05; /* What do we put here? */ id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_RID: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tRead ID - %d\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tRead ID - %d\n", + val, id_ua); if (id_sel_unit->flags & UNIT_ATT) { id_drq = TRUE; @@ -894,21 +885,21 @@ void id_handle_command(uint8 val) id_lsn = 0; } else { sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tUNIT %d NOT ATTACHED, CANNOT READ ID.\n", - R[NUM_PC], id_ua); + "UNIT %d NOT ATTACHED, CANNOT READ ID.\n", + id_ua); } id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_RDIAG: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tRead Diag - %d\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tRead Diag - %d\n", + val, id_ua); id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_RDATA: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tRead Data - %d\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tRead Data - %d\n", + val, id_ua); if (id_sel_unit->flags & UNIT_ATT) { id_drq = TRUE; id_buf_ptr = 0; @@ -922,33 +913,33 @@ void id_handle_command(uint8 val) id_scnt = id_data[5]; } else { sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tUNIT %d NOT ATTACHED, CANNOT READ DATA.\n", - R[NUM_PC], id_ua); + "UNIT %d NOT ATTACHED, CANNOT READ DATA.\n", + id_ua); } id_activate(id_sel_unit, ID_RW_WAIT); break; case ID_CMD_CHECK: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tCheck - %d\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tCheck - %d\n", + val, id_ua); id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_SCAN: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tScan - %d\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tScan - %d\n", + val, id_ua); id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_VDATA: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tVerify Data - %d\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tVerify Data - %d\n", + val, id_ua); id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_WDATA: sim_debug(WRITE_MSG, &id_dev, - "[%08x]\tCOMMAND\t%02x\tWrite Data - %d\n", - R[NUM_PC], val, id_ua); + "COMMAND\t%02x\tWrite Data - %d\n", + val, id_ua); if (id_sel_unit->flags & UNIT_ATT) { id_drq = TRUE; id_buf_ptr = 0; @@ -962,8 +953,8 @@ void id_handle_command(uint8 val) id_scnt = id_data[5]; } else { sim_debug(EXECUTE_MSG, &id_dev, - "[%08x]\tUNIT %d NOT ATTACHED, CANNOT WRITE.\n", - R[NUM_PC], id_ua); + "UNIT %d NOT ATTACHED, CANNOT WRITE.\n", + id_ua); } id_activate(id_sel_unit, ID_RW_WAIT); break; @@ -996,5 +987,10 @@ t_stat id_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) fprintf(st, " HD161 161.4 MB 11 1224 15 18 512 Maxtor XT1190 (SVR3+)\n\n"); fprintf(st, "The drive ID and geometry values are used when low-level formatting a\n"); fprintf(st, "drive using the AT&T 'idtools' utility.\n"); + + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + fprint_reg_help(st, dptr); + return SCPE_OK; } diff --git a/3B2/3b2_id.h b/3B2/3b2_id.h index cecc4830..81f78e31 100644 --- a/3B2/3b2_id.h +++ b/3B2/3b2_id.h @@ -1,6 +1,6 @@ -/* 3b2_id.h: AT&T 3B2 Model 400 Hard Disk (uPD7261) Header +/* 3b2_id.h: uPD7261 Integrated Disk Controller - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/3B2/3b2_if.c b/3B2/3b2_if.c index 6512d5b3..cad717e1 100644 --- a/3B2/3b2_if.c +++ b/3B2/3b2_if.c @@ -1,6 +1,6 @@ -/* 3b2_if.c: AT&T 3B2 Floppy Controller (TMS2797NL) Implementation +/* 3b2_if.c: TMS2797 Integrated Floppy Controller - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -51,12 +51,12 @@ static SIM_INLINE uint32 if_lba(); * */ -#define IF_STEP_DELAY 3000 /* us */ -#define IF_R_DELAY 65000 /* us */ -#define IF_W_DELAY 70000 /* us */ -#define IF_VERIFY_DELAY 20000 /* us */ -#define IF_HLD_DELAY 60000 /* us */ -#define IF_HSW_DELAY 40000 /* us */ +#define IF_STEP_DELAY 300 /* us */ +#define IF_R_DELAY 6500 /* us */ +#define IF_W_DELAY 7000 /* us */ +#define IF_VERIFY_DELAY 2000 /* us */ +#define IF_HLD_DELAY 6000 /* us */ +#define IF_HSW_DELAY 4000 /* us */ #if defined(REV3) #define SET_INT CPU_SET_INT(INT_FLOPPY) @@ -97,9 +97,9 @@ uint32 if_sec_ptr = 0; /* Function implementation */ -static SIM_INLINE void if_activate(uint32 delay) +static SIM_INLINE void if_activate(uint32 delay_us) { - sim_activate_abs(&if_unit, delay); + sim_activate_after(&if_unit, delay_us); } t_stat if_svc(UNIT *uptr) @@ -618,6 +618,11 @@ t_stat if_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) fprintf(st, " ------ ----- ----------- ------------- -----------\n"); fprintf(st, " 720 KB 2 80 9 512\n\n"); fprintf(st, "Physical media is Double Sided/Quad Density, 96 tpi, 250kbps MFM encoding.\n"); + + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + fprint_reg_help(st, dptr); + return SCPE_OK; } diff --git a/3B2/3b2_if.h b/3B2/3b2_if.h index a6d40e58..256c084b 100644 --- a/3B2/3b2_if.h +++ b/3B2/3b2_if.h @@ -1,6 +1,6 @@ -/* 3b2_if.h: AT&T 3B2 Model Floppy Controller (TMS2797NL) Header +/* 3b2_if.h: TMS2797 Integrated Floppy Controller - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -44,7 +44,7 @@ typedef struct { uint8 read_addr_ptr; int8 step_dir; t_bool drq; -#if defined(REV3) +#if defined(REV3) uint8 csr; #endif } IF_STATE; diff --git a/3B2/3b2_io.c b/3B2/3b2_io.c index eba4bf54..80d2df51 100644 --- a/3B2/3b2_io.c +++ b/3B2/3b2_io.c @@ -1,6 +1,6 @@ -/* 3b2_io.c: AT&T 3B2 Model 400 IO and CIO feature cards +/* 3b2_io.c: Common I/O (CIO) Feature Card Support - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -45,7 +45,6 @@ #endif CIO_STATE cio[CIO_SLOTS] = {{0}}; - uint16 cio_int_req = 0; /* Bitset of card slots requesting interrupts */ #if defined(REV3) @@ -53,8 +52,8 @@ 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 }, + { FLTLBASE, FLTLBASE+FLTLSIZE, &flt_read, &flt_write }, + { FLTHBASE, 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 }, @@ -85,24 +84,73 @@ iolink iotable[] = { }; #endif -void cio_clear(uint8 cid) +/* + * Insert a CIO card into the backplane. + * + * If a space could be found, SPCE_OK is returned, and the slot the + * card was installed in is placed in `slot`. + * + * If no room is availalbe, return SCPE_NXM. + */ +t_stat cio_install(uint16 id, + CONST char *name, + uint8 ipl, + void (*exp_handler)(uint8 slot), + void (*full_handler)(uint8 slot), + void (*sysgen)(uint8 slot), + void (*reset_handler)(uint8 slot), + uint8 *slot) { - cio[cid].id = 0; - cio[cid].exp_handler = NULL; - cio[cid].full_handler = NULL; - cio[cid].reset_handler = NULL; - cio[cid].sysgen = NULL; - cio[cid].rqp = 0; - cio[cid].cqp = 0; - cio[cid].rqs = 0; - cio[cid].cqs = 0; - cio[cid].ivec = 0; - cio[cid].no_rque = 0; - cio[cid].ipl = 0; - cio[cid].sysgen_s = 0; - cio[cid].seqbit = 0; - cio[cid].op = 0; - CIO_CLR_INT(cid); + uint8 s; + + for (s = 0; s < CIO_SLOTS; s++) { + sim_debug(EXECUTE_MSG, &cpu_dev, + "[cio_install] cio[%d]: populated=%d, id=%d\n", + s, cio[s].populated, cio[s].id); + if (!cio[s].populated) { + sim_debug(EXECUTE_MSG, &cpu_dev, + "[cio_install] >>> I found a free slot! Slot #%d has nothing\n", s); + *slot = s; + /* Ensure the slot is in a clean state */ + cio_remove(s); + /* Populate the slot */ + cio[s].populated = TRUE; + cio[s].id = id; + cio[s].ipl = ipl; + strncpy(cio[s].name, name, CIO_NAME_LEN); + cio[s].exp_handler = exp_handler; + cio[s].full_handler = full_handler; + cio[s].sysgen = sysgen; + cio[s].reset_handler = reset_handler; + return SCPE_OK; + } + } + + return SCPE_NXM; +} + +/* + * Remove a CIO card from the specified backplane slot. + */ +void cio_remove(uint8 slot) +{ + memset(&cio[slot], 0, sizeof(CIO_STATE)); + /* cio[slot].populated = FALSE; */ + CIO_CLR_INT(slot); +} + +/* + * Remove all CIO cards of the matching type. + */ +void cio_remove_all(uint16 id) +{ + int i; + + for (i = 0; i < CIO_SLOTS; i++) { + if (cio[i].populated && cio[i].id == id) { + cio_remove(i); + } + } } /* @@ -130,81 +178,80 @@ uint32 cio_crc32_shift(uint32 crc, uint8 data) return ~crc; } -void cio_sysgen(uint8 cid) +void cio_sysgen(uint8 slot) { uint32 sysgen_p; - sysgen_p = pread_w(SYSGEN_PTR); + sysgen_p = pread_w(SYSGEN_PTR, BUS_PER); sim_debug(CIO_DBG, &cpu_dev, - "[%08x] [SYSGEN] Starting sysgen for card %d. sysgen_p=%08x\n", - R[NUM_PC], cid, sysgen_p); + "[SYSGEN] Starting sysgen for card %d (%s). sysgen_p=%08x\n", + slot, cio[slot].name, sysgen_p); /* seqbit is always reset to 0 on completion */ - cio[cid].seqbit = 0; + cio[slot].seqbit = 0; - cio[cid].rqp = pread_w(sysgen_p); - cio[cid].cqp = pread_w(sysgen_p + 4); - cio[cid].rqs = pread_b(sysgen_p + 8); - cio[cid].cqs = pread_b(sysgen_p + 9); - cio[cid].ivec = pread_b(sysgen_p + 10); - cio[cid].no_rque = pread_b(sysgen_p + 11); + cio[slot].rqp = pread_w(sysgen_p, BUS_PER); + cio[slot].cqp = pread_w(sysgen_p + 4, BUS_PER); + cio[slot].rqs = pread_b(sysgen_p + 8, BUS_PER); + cio[slot].cqs = pread_b(sysgen_p + 9, BUS_PER); + cio[slot].ivec = pread_b(sysgen_p + 10, BUS_PER); + cio[slot].no_rque = pread_b(sysgen_p + 11, BUS_PER); sim_debug(CIO_DBG, &cpu_dev, "[SYSGEN] sysgen rqp = %08x\n", - cio[cid].rqp); + cio[slot].rqp); sim_debug(CIO_DBG, &cpu_dev, "[SYSGEN] sysgen cqp = %08x\n", - cio[cid].cqp); + cio[slot].cqp); sim_debug(CIO_DBG, &cpu_dev, "[SYSGEN] sysgen rqs = %02x\n", - cio[cid].rqs); + cio[slot].rqs); sim_debug(CIO_DBG, &cpu_dev, "[SYSGEN] sysgen cqs = %02x\n", - cio[cid].cqs); + cio[slot].cqs); sim_debug(CIO_DBG, &cpu_dev, "[SYSGEN] sysgen ivec = %02x\n", - cio[cid].ivec); + cio[slot].ivec); sim_debug(CIO_DBG, &cpu_dev, "[SYSGEN] sysgen no_rque = %02x\n", - cio[cid].no_rque); + cio[slot].no_rque); /* If the card has a custom sysgen handler, run it */ - if (cio[cid].sysgen != NULL) { - cio[cid].sysgen(cid); + if (cio[slot].sysgen != NULL) { + cio[slot].sysgen(slot); } else { sim_debug(CIO_DBG, &cpu_dev, - "[%08x] [cio_sysgen] Not running custom sysgen.\n", - R[NUM_PC]); + "[cio_sysgen] Not running custom sysgen.\n"); } } -void cio_cexpress(uint8 cid, uint32 esize, cio_entry *cqe, uint8 *app_data) +void cio_cexpress(uint8 slot, uint32 esize, cio_entry *cqe, uint8 *app_data) { uint32 i, cqp; - cqp = cio[cid].cqp; + cqp = cio[slot].cqp; sim_debug(CIO_DBG, &cpu_dev, - "[%08x] [cio_cexpress] cqp = %08x seqbit = %d\n", - R[NUM_PC], cqp, cio[cid].seqbit); + "[cio_cexpress] [%s] cqp = %08x seqbit = %d\n", + cio[slot].name, cqp, cio[slot].seqbit); - cio[cid].seqbit ^= 1; + cio[slot].seqbit ^= 1; - cqe->subdevice |= (cio[cid].seqbit << 6); + cqe->subdevice |= (cio[slot].seqbit << 6); - pwrite_h(cqp, cqe->byte_count); - pwrite_b(cqp + 2, cqe->subdevice); - pwrite_b(cqp + 3, cqe->opcode); - pwrite_w(cqp + 4, cqe->address); + pwrite_h(cqp, cqe->byte_count, BUS_PER); + pwrite_b(cqp + 2, cqe->subdevice, BUS_PER); + pwrite_b(cqp + 3, cqe->opcode, BUS_PER); + pwrite_w(cqp + 4, cqe->address, BUS_PER); /* Write application-specific data. */ for (i = 0; i < (esize - QESIZE); i++) { - pwrite_b(cqp + 8 + i, app_data[i]); + pwrite_b(cqp + 8 + i, app_data[i], BUS_PER); } } -void cio_cqueue(uint8 cid, uint8 cmd_stat, uint32 esize, +void cio_cqueue(uint8 slot, uint8 cmd_stat, uint32 esize, cio_entry *cqe, uint8 *app_data) { uint32 i, cqp, top; @@ -215,7 +262,7 @@ void cio_cqueue(uint8 cid, uint8 cmd_stat, uint32 esize, /* Get the physical address of the completion queue * in main memory */ - cqp = cio[cid].cqp; + cqp = cio[slot].cqp; /* Get the physical address of the first entry in * the completion queue */ @@ -223,47 +270,47 @@ void cio_cqueue(uint8 cid, uint8 cmd_stat, uint32 esize, /* Get the load pointer. This is a 16-bit absolute offset * from the top of the queue to the start of the entry. */ - lp = pread_h(cqp + esize); + lp = pread_h(cqp + esize, BUS_PER); /* Load the entry at the supplied address */ - pwrite_h(top + lp, cqe->byte_count); - pwrite_b(top + lp + 2, cqe->subdevice); - pwrite_b(top + lp + 3, cqe->opcode); - pwrite_w(top + lp + 4, cqe->address); + pwrite_h(top + lp, cqe->byte_count, BUS_PER); + pwrite_b(top + lp + 2, cqe->subdevice, BUS_PER); + pwrite_b(top + lp + 3, cqe->opcode, BUS_PER); + pwrite_w(top + lp + 4, cqe->address, BUS_PER); /* Write application-specific data. */ for (i = 0; i < (esize - QESIZE); i++) { - pwrite_b(top + lp + 8 + i, app_data[i]); + pwrite_b(top + lp + 8 + i, app_data[i], BUS_PER); } /* Increment the load pointer to the next queue location. * If we go past the end of the queue, wrap around to the * start of the queue */ - if (cio[cid].cqs > 0) { - lp = (lp + esize) % (esize * cio[cid].cqs); + if (cio[slot].cqs > 0) { + lp = (lp + esize) % (esize * cio[slot].cqs); /* Store it back to the correct location */ - pwrite_h(cqp + esize, lp); + pwrite_h(cqp + esize, lp, BUS_PER); } } /* * Retrieve the Express Entry from the Request Queue */ -void cio_rexpress(uint8 cid, uint32 esize, cio_entry *rqe, uint8 *app_data) +void cio_rexpress(uint8 slot, uint32 esize, cio_entry *rqe, uint8 *app_data) { uint32 i; uint32 rqp; - rqp = cio[cid].rqp; + rqp = cio[slot].rqp; /* Unload the express entry from the request queue */ - rqe->byte_count = pread_h(rqp); - rqe->subdevice = pread_b(rqp + 2); - rqe->opcode = pread_b(rqp + 3); - rqe->address = pread_w(rqp + 4); + rqe->byte_count = pread_h(rqp, BUS_PER); + rqe->subdevice = pread_b(rqp + 2, BUS_PER); + rqe->opcode = pread_b(rqp + 3, BUS_PER); + rqe->address = pread_w(rqp + 4, BUS_PER); for (i = 0; i < (esize - QESIZE); i++) { - app_data[i] = pread_b(rqp + 8 + i); + app_data[i] = pread_b(rqp + 8 + i, BUS_PER); } } @@ -276,19 +323,19 @@ void cio_rexpress(uint8 cid, uint32 esize, cio_entry *rqe, uint8 *app_data) * Returns SCPE_OK on success, or SCPE_NXM if no entry was found. * */ -t_stat cio_rqueue(uint8 cid, uint32 qnum, uint32 esize, +t_stat cio_rqueue(uint8 slot, uint32 qnum, uint32 esize, cio_entry *rqe, uint8 *app_data) { uint32 i, rqp, top; uint16 lp, ulp; /* Get the physical address of the request queue in main memory */ - rqp = cio[cid].rqp + + rqp = cio[slot].rqp + esize + - (qnum * (LUSIZE + (esize * cio[cid].rqs))); + (qnum * (LUSIZE + (esize * cio[slot].rqs))); - lp = pread_h(rqp); - ulp = pread_h(rqp + 2); + lp = pread_h(rqp, BUS_PER); + ulp = pread_h(rqp + 2, BUS_PER); /* Check to see if the request queue is empty. If it is, there's * nothing to take. */ @@ -299,22 +346,22 @@ t_stat cio_rqueue(uint8 cid, uint32 qnum, uint32 esize, top = rqp + LUSIZE; /* Retrieve the entry at the supplied address */ - rqe->byte_count = pread_h(top + ulp); - rqe->subdevice = pread_b(top + ulp + 2); - rqe->opcode = pread_b(top + ulp + 3); - rqe->address = pread_w(top + ulp + 4); + rqe->byte_count = pread_h(top + ulp, BUS_PER); + rqe->subdevice = pread_b(top + ulp + 2, BUS_PER); + rqe->opcode = pread_b(top + ulp + 3, BUS_PER); + rqe->address = pread_w(top + ulp + 4, BUS_PER); /* Read application-specific data. */ for (i = 0; i < (esize - QESIZE); i++) { - app_data[i] = pread_b(top + ulp + 8 + i); + app_data[i] = pread_b(top + ulp + 8 + i, BUS_PER); } /* Increment the unload pointer to the next queue location. If we * go past the end of the queue, wrap around to the start of the * queue */ - if (cio[cid].rqs > 0) { - ulp = (ulp + esize) % (esize * cio[cid].rqs); - pwrite_h(rqp + 2, ulp); + if (cio[slot].rqs > 0) { + ulp = (ulp + esize) % (esize * cio[slot].rqs); + pwrite_h(rqp + 2, ulp, BUS_PER); } return SCPE_OK; @@ -323,70 +370,70 @@ t_stat cio_rqueue(uint8 cid, uint32 qnum, uint32 esize, /* * Return the Load Pointer for the given request queue */ -uint16 cio_r_lp(uint8 cid, uint32 qnum, uint32 esize) +uint16 cio_r_lp(uint8 slot, uint32 qnum, uint32 esize) { uint32 rqp; - rqp = cio[cid].rqp + + rqp = cio[slot].rqp + esize + - (qnum * (LUSIZE + (esize * cio[cid].rqs))); + (qnum * (LUSIZE + (esize * cio[slot].rqs))); - return pread_h(rqp); + return pread_h(rqp, BUS_PER); } /* * Return the Unload Pointer for the given request queue */ -uint16 cio_r_ulp(uint8 cid, uint32 qnum, uint32 esize) +uint16 cio_r_ulp(uint8 slot, uint32 qnum, uint32 esize) { uint32 rqp; - rqp = cio[cid].rqp + + rqp = cio[slot].rqp + esize + - (qnum * (LUSIZE + (esize * cio[cid].rqs))); + (qnum * (LUSIZE + (esize * cio[slot].rqs))); - return pread_h(rqp + 2); + return pread_h(rqp + 2, BUS_PER); } -uint16 cio_c_lp(uint8 cid, uint32 esize) +uint16 cio_c_lp(uint8 slot, uint32 esize) { uint32 cqp; - cqp = cio[cid].cqp + esize; - return pread_h(cqp); + cqp = cio[slot].cqp + esize; + return pread_h(cqp, BUS_PER); } -uint16 cio_c_ulp(uint8 cid, uint32 esize) +uint16 cio_c_ulp(uint8 slot, uint32 esize) { uint32 cqp; - cqp = cio[cid].cqp + esize; - return pread_h(cqp + 2); + cqp = cio[slot].cqp + esize; + return pread_h(cqp + 2, BUS_PER); } /* * Returns true if there is room in the completion queue * for a new entry. */ -t_bool cio_cqueue_avail(uint8 cid, uint32 esize) +t_bool cio_cqueue_avail(uint8 slot, uint32 esize) { uint32 lp, ulp; - lp = pread_h(cio[cid].cqp + esize); - ulp = pread_h(cio[cid].cqp + esize + 2); + lp = pread_h(cio[slot].cqp + esize, BUS_PER); + ulp = pread_h(cio[slot].cqp + esize + 2, BUS_PER); - return(((lp + esize) % (cio[cid].cqs * esize)) != ulp); + return(((lp + esize) % (cio[slot].cqs * esize)) != ulp); } -t_bool cio_rqueue_avail(uint8 cid, uint32 qnum, uint32 esize) +t_bool cio_rqueue_avail(uint8 slot, uint32 qnum, uint32 esize) { uint32 rqp, lp, ulp; /* Get the physical address of the request queue in main memory */ - rqp = cio[cid].rqp + + rqp = cio[slot].rqp + esize + - (qnum * (LUSIZE + (esize * cio[cid].rqs))); + (qnum * (LUSIZE + (esize * cio[slot].rqs))); - lp = pread_h(rqp); - ulp = pread_h(rqp + 2); + lp = pread_h(rqp, BUS_PER); + ulp = pread_h(rqp + 2, BUS_PER); return(lp != ulp); } @@ -394,67 +441,23 @@ t_bool cio_rqueue_avail(uint8 cid, uint32 qnum, uint32 esize) uint32 io_read(uint32 pa, size_t size) { iolink *p; - uint8 cid, reg, data; + uint8 slot, reg, data; #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) { + sim_debug(EXECUTE_MSG, &cpu_dev, + "[UBUB] (VCACHE) Read addr %08x\n", pa); CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return 0; } if (pa >= BUB_BOTTOM && pa < BUB_TOP) { + sim_debug(EXECUTE_MSG, &cpu_dev, + "[BUB] Read addr %08x\n", pa); 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; + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + return 0; } #else if (pa == MEMSIZE_REG) { @@ -482,14 +485,14 @@ uint32 io_read(uint32 pa, size_t size) /* CIO board area */ if (pa >= CIO_BOTTOM && pa < CIO_TOP) { - cid = CID(pa); - reg = pa - CADDR(cid); + slot = SLOT(pa); + reg = pa - CADDR(slot); - if (cio[cid].id == 0) { + if (!cio[slot].populated) { /* Nothing lives here */ sim_debug(IO_DBG, &cpu_dev, - "[READ] [%08x] No card at cid=%d reg=%d\n", - R[NUM_PC], cid, reg); + "[READ] No card at slot=%d reg=%d\n", + slot, reg); CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return 0; @@ -504,96 +507,96 @@ uint32 io_read(uint32 pa, size_t size) switch (reg) { case IOF_ID: case IOF_VEC: - switch(cio[cid].sysgen_s) { + switch(cio[slot].sysgen_s) { case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ case CIO_INT0: /* We've seen an INT0 but not an INT1. */ - cio[cid].sysgen_s |= CIO_INT0; + cio[slot].sysgen_s |= CIO_INT0; sim_debug(CIO_DBG, &cpu_dev, - "[READ] [%08x] (%d INT0) ID\n", - R[NUM_PC], cid); + "[READ] [%s] (%d INT0) ID\n", + cio[slot].name, slot); /* Return the correct byte of our board ID */ if (reg == IOF_ID) { - data = (cio[cid].id >> 8) & 0xff; + data = (cio[slot].id >> 8) & 0xff; } else { - data = (cio[cid].id & 0xff); + data = (cio[slot].id & 0xff); } break; case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */ - cio[cid].sysgen_s |= CIO_INT0; + cio[slot].sysgen_s |= CIO_INT0; sim_debug(CIO_DBG, &cpu_dev, - "[READ] [%08x] (%d INT0) SYSGEN\n", - R[NUM_PC], cid); - cio_sysgen(cid); - data = cio[cid].ivec; + "[READ] [%s] (%d INT0) SYSGEN\n", + cio[slot].name, slot); + cio_sysgen(slot); + data = cio[slot].ivec; break; case CIO_SYSGEN: /* We've already sysgen'ed */ - cio[cid].sysgen_s |= CIO_INT0; /* This must come BEFORE the exp_handler */ + cio[slot].sysgen_s |= CIO_INT0; /* This must come BEFORE the exp_handler */ sim_debug(CIO_DBG, &cpu_dev, - "[READ] [%08x] (%d INT0) EXPRESS JOB\n", - R[NUM_PC], cid); - cio[cid].exp_handler(cid); - data = cio[cid].ivec; + "[READ] [%s] (%d INT0) EXPRESS JOB\n", + cio[slot].name, slot); + cio[slot].exp_handler(slot); + data = cio[slot].ivec; break; default: /* This should never happen */ stop_reason = STOP_ERR; sim_debug(CIO_DBG, &cpu_dev, - "[READ] [%08x] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n", - R[NUM_PC], cid, cio[cid].sysgen_s); + "[READ] [%s] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n", + cio[slot].name, slot, cio[slot].sysgen_s); data = 0; break; } return data; case IOF_CTRL: - switch(cio[cid].sysgen_s) { + switch(cio[slot].sysgen_s) { case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ case CIO_INT1: /* We've seen an INT1 but not an INT0 */ /* There's nothing to do in this instance */ sim_debug(CIO_DBG, &cpu_dev, - "[READ] [%08x] (%d INT1) IGNORED\n", - R[NUM_PC], cid); - cio[cid].sysgen_s |= CIO_INT1; + "[READ] [%s] (%d INT1) IGNORED\n", + cio[slot].name, slot); + cio[slot].sysgen_s |= CIO_INT1; break; case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */ sim_debug(CIO_DBG, &cpu_dev, - "[READ] [%08x] (%d INT1) SYSGEN\n", - R[NUM_PC], cid); - cio[cid].sysgen_s |= CIO_INT1; - cio_sysgen(cid); + "[READ] [%s] (%d INT1) SYSGEN\n", + cio[slot].name, slot); + cio[slot].sysgen_s |= CIO_INT1; + cio_sysgen(slot); break; case CIO_SYSGEN: /* We've already sysgen'ed */ sim_debug(CIO_DBG, &cpu_dev, - "[READ] [%08x] (%d INT1) FULL\n", - R[NUM_PC], cid); - cio[cid].sysgen_s |= CIO_INT1; /* This must come BEFORE the full handler */ - cio[cid].full_handler(cid); + "[READ] [%s] (%d INT1) FULL\n", + cio[slot].name, slot); + cio[slot].sysgen_s |= CIO_INT1; /* This must come BEFORE the full handler */ + cio[slot].full_handler(slot); break; default: /* This should never happen */ stop_reason = STOP_ERR; sim_debug(CIO_DBG, &cpu_dev, - "[READ] [%08x] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n", - R[NUM_PC], cid, cio[cid].sysgen_s); + "[READ] [%s] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n", + cio[slot].name, slot, cio[slot].sysgen_s); break; } return 0; /* Data returned is arbitrary */ case IOF_STAT: sim_debug(CIO_DBG, &cpu_dev, - "[READ] [%08x] (%d RESET)\n", - R[NUM_PC], cid); - if (cio[cid].reset_handler) { - cio[cid].reset_handler(cid); + "[READ] [%s] (%d RESET)\n", + cio[slot].name, slot); + if (cio[slot].reset_handler) { + cio[slot].reset_handler(slot); } - cio[cid].sysgen_s = 0; + cio[slot].sysgen_s = 0; return 0; /* Data returned is arbitrary */ default: /* We should never reach here, but if we do, there's * nothing listening. */ sim_debug(CIO_DBG, &cpu_dev, - "[READ] [%08x] No card at cid=%d reg=%d\n", - R[NUM_PC], cid, reg); + "[READ] No card at slot=%d reg=%d\n", + slot, reg); CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return 0; @@ -609,8 +612,8 @@ uint32 io_read(uint32 pa, size_t size) /* Not found. */ sim_debug(IO_DBG, &cpu_dev, - "[%08x] [io_read] ADDR=%08x: No device found.\n", - R[NUM_PC], pa); + "[io_read] ADDR=%08x: No device found.\n", + pa); CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return 0; @@ -619,36 +622,38 @@ uint32 io_read(uint32 pa, size_t size) void io_write(uint32 pa, uint32 val, size_t size) { iolink *p; - uint8 cid, reg; + uint8 slot, reg; #if defined(REV3) if (pa >= VCACHE_BOTTOM && pa < VCACHE_TOP) { + sim_debug(EXECUTE_MSG, &cpu_dev, + "[UBUB] (VCACHE) Write addr %08x val 0x%x\n", + pa, val); CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return; } if (pa >= BUB_BOTTOM && pa < BUB_TOP) { + sim_debug(EXECUTE_MSG, &cpu_dev, + "[BUB] Write addr %08x val 0x%x\n", + pa, val); CSRBIT(CSRTIMO, TRUE); - /* TODO: I don't remember why we do this! */ - if ((pa & 0xfff) == 3) { - cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); - } - /* TODO: Implement BUB */ + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return; } -#endif +#endif /* Feature Card Area */ if (pa >= CIO_BOTTOM && pa < CIO_TOP) { - cid = CID(pa); - reg = pa - CADDR(cid); + slot = SLOT(pa); + reg = pa - CADDR(slot); - if (cio[cid].id == 0) { + if (!cio[slot].populated) { /* Nothing lives here */ sim_debug(CIO_DBG, &cpu_dev, - "[WRITE] [%08x] No card at cid=%d reg=%d\n", - R[NUM_PC], cid, reg); + "[WRITE] No card at slot=%d reg=%d\n", + slot, reg); CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return; @@ -663,87 +668,87 @@ void io_write(uint32 pa, uint32 val, size_t size) switch (reg) { case IOF_ID: case IOF_VEC: - switch(cio[cid].sysgen_s) { + switch(cio[slot].sysgen_s) { case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ case CIO_INT0: /* We've seen an INT0 but not an INT1. */ sim_debug(CIO_DBG, &cpu_dev, - "[WRITE] [%08x] (%d INT0) ID\n", - R[NUM_PC], cid); - cio[cid].sysgen_s |= CIO_INT0; + "[WRITE] [%s] (%d INT0) ID\n", + cio[slot].name, slot); + cio[slot].sysgen_s |= CIO_INT0; break; case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */ sim_debug(CIO_DBG, &cpu_dev, - "[WRITE] [%08x] (%d INT0) SYSGEN\n", - R[NUM_PC], cid); - cio[cid].sysgen_s |= CIO_INT0; - cio_sysgen(cid); + "[WRITE] [%s] (%d INT0) SYSGEN\n", + cio[slot].name, slot); + cio[slot].sysgen_s |= CIO_INT0; + cio_sysgen(slot); break; case CIO_SYSGEN: /* We've already sysgen'ed */ sim_debug(CIO_DBG, &cpu_dev, - "[WRITE] [%08x] (%d INT0) EXPRESS JOB\n", - R[NUM_PC], cid); - cio[cid].sysgen_s |= CIO_INT0; - cio[cid].exp_handler(cid); + "[WRITE] [%s] (%d INT0) EXPRESS JOB\n", + cio[slot].name, slot); + cio[slot].sysgen_s |= CIO_INT0; + cio[slot].exp_handler(slot); break; default: /* This should never happen */ stop_reason = STOP_ERR; sim_debug(CIO_DBG, &cpu_dev, - "[WRITE] [%08x] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n", - R[NUM_PC], cid, cio[cid].sysgen_s); + "[WRITE] [%s] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n", + cio[slot].name, slot, cio[slot].sysgen_s); break; } return; case IOF_CTRL: - switch(cio[cid].sysgen_s) { + switch(cio[slot].sysgen_s) { case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ case CIO_INT1: /* We've seen an INT1 but not an INT0 */ /* There's nothing to do in this instance */ sim_debug(CIO_DBG, &cpu_dev, - "[WRITE] [%08x] (%d INT1) IGNORED\n", - R[NUM_PC], cid); - cio[cid].sysgen_s |= CIO_INT1; + "[WRITE] [%s] (%d INT1) IGNORED\n", + cio[slot].name, slot); + cio[slot].sysgen_s |= CIO_INT1; break; case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */ sim_debug(CIO_DBG, &cpu_dev, - "[WRITE] [%08x] (%d INT1) SYSGEN\n", - R[NUM_PC], cid); - cio[cid].sysgen_s |= CIO_INT1; - cio_sysgen(cid); + "[WRITE] [%s] (%d INT1) SYSGEN\n", + cio[slot].name, slot); + cio[slot].sysgen_s |= CIO_INT1; + cio_sysgen(slot); break; case CIO_SYSGEN: /* We've already sysgen'ed */ sim_debug(CIO_DBG, &cpu_dev, - "[WRITE] [%08x] (%d INT1) FULL\n", - R[NUM_PC], cid); - cio[cid].sysgen_s |= CIO_INT1; - cio[cid].full_handler(cid); + "[WRITE] [%s] (%d INT1) FULL\n", + cio[slot].name, slot); + cio[slot].sysgen_s |= CIO_INT1; + cio[slot].full_handler(slot); break; default: /* This should never happen */ stop_reason = STOP_ERR; sim_debug(CIO_DBG, &cpu_dev, - "[WRITE] [%08x] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n", - R[NUM_PC], cid, cio[cid].sysgen_s); + "[WRITE] [%s] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n", + cio[slot].name, slot, cio[slot].sysgen_s); break; } return; case IOF_STAT: sim_debug(CIO_DBG, &cpu_dev, - "[WRITE] [%08x] (%d RESET)\n", - R[NUM_PC], cid); - if (cio[cid].reset_handler) { - cio[cid].reset_handler(cid); + "[WRITE] [%s] (%d RESET)\n", + cio[slot].name, slot); + if (cio[slot].reset_handler) { + cio[slot].reset_handler(slot); } - cio[cid].sysgen_s = 0; + cio[slot].sysgen_s = 0; return; default: /* We should never reach here, but if we do, there's * nothing listening. */ sim_debug(CIO_DBG, &cpu_dev, - "[WRITE] [%08x] No card at cid=%d reg=%d\n", - R[NUM_PC], cid, reg); + "[WRITE] No card at slot=%d reg=%d\n", + slot, reg); CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return; @@ -760,27 +765,8 @@ void io_write(uint32 pa, uint32 val, size_t size) /* Not found. */ sim_debug(IO_DBG, &cpu_dev, - "[%08x] [io_write] ADDR=%08x: No device found.\n", - R[NUM_PC], pa); + "[io_write] ADDR=%08x: No device found.\n", + pa); CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); } - - -/* For debugging only */ -void dump_entry(uint32 dbits, DEVICE *dev, CONST char *type, - uint32 esize, cio_entry *entry, uint8 *app_data) -{ - char appl[64]; - uint32 i, c_offset; - - for (i = 0, c_offset=0; i < (esize - QESIZE); i++) { - snprintf(appl + c_offset, 3, "%02x", app_data[i]); - c_offset += 2; - } - - sim_debug(dbits, dev, - "*** %s ENTRY: byte_count=%04x, subdevice=%02x, opcode=%d, address=%08x, app_data=%s\n", - type, entry->byte_count, entry->subdevice, - entry->opcode, entry->address, appl); -} diff --git a/3B2/3b2_io.h b/3B2/3b2_io.h index d06e5b90..77a1306d 100644 --- a/3B2/3b2_io.h +++ b/3B2/3b2_io.h @@ -1,6 +1,6 @@ -/* 3b2_io.h: AT&T 3B2 Model 400 IO dispatch (Header) +/* 3b2_io.h: Common I/O (CIO) Feature Card Support - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -119,9 +119,18 @@ #define IO_BOTTOM 0x40000 #define IO_TOP 0x50000 +#if defined(REV3) +#define UBUS_BOTTOM 0x1c00000 +#define UBUS_TOP 0x2000000 +#endif + /* CIO area */ #define CIO_BOTTOM 0x200000 +#if defined(REV3) +#define CIO_TOP 0x1a00000 +#else #define CIO_TOP 0x2000000 +#endif #define IOF_ID 0 #define IOF_VEC 1 @@ -143,7 +152,7 @@ #define CIO_SYSGEN_OK 3 /* Map a physical address to a card ID */ -#define CID(pa) (((((pa) >> 0x14) & 0x1f) / 2) - 1) +#define SLOT(pa) (((((pa) >> 0x14) & 0x1f) / 2) - 1) /* Map a card ID to a base address */ #define CADDR(bid) (((((bid) + 1) * 2) << 0x14)) @@ -160,15 +169,19 @@ #define CIO_INT1 2 #define CIO_SYSGEN 3 -#define CIO_SET_INT(slot) (cio_int_req |= (1 << slot)) +#define CIO_SET_INT(slot) if (cio[slot].populated && cio[slot].ivec >= 2) (cio_int_req |= (1 << slot)) #define CIO_CLR_INT(slot) (cio_int_req &= ~(1 << slot)) +#define CIO_NAME_LEN 8 + typedef struct { - uint16 id; /* Card ID */ - void (*exp_handler)(uint8 cid); /* Handler for express jobs */ - void (*full_handler)(uint8 cid); /* Handler for full jobs */ - void (*sysgen)(uint8 cid); /* Sysgen routine (optional) */ - void (*reset_handler)(uint8 cid); /* RESET request handler (optional) */ + t_bool populated; /* Populated? */ + uint16 id; /* CIO identifier */ + char name[CIO_NAME_LEN]; /* Device name */ + void (*exp_handler)(uint8 slot); /* Handler for express jobs */ + void (*full_handler)(uint8 slot); /* Handler for full jobs */ + void (*sysgen)(uint8 slot); /* Sysgen routine (optional) */ + void (*reset_handler)(uint8 slot); /* RESET request handler (optional) */ uint32 rqp; /* Request Queue Pointer */ uint32 cqp; /* Completion Queue Pointer */ uint8 rqs; /* Request queue size */ @@ -226,26 +239,32 @@ typedef struct { t_stat cio_reset(DEVICE *dptr); t_stat cio_svc(UNIT *uptr); -void cio_clear(uint8 cid); +t_stat cio_install(uint16 id, + CONST char *name, + uint8 ipl, + void (*exp_handler)(uint8 slot), + void (*full_handler)(uint8 slot), + void (*sysgen)(uint8 slot), + void (*reset_handler)(uint8 slot), + uint8 *slot); +void cio_remove(uint8 slot); +void cio_remove_all(uint16 id); uint32 cio_crc32_shift(uint32 crc, uint8 data); -void cio_cexpress(uint8 cid, uint32 esize, cio_entry *cqe, uint8 *app_data); -void cio_cqueue(uint8 cid, uint8 cmd_stat, uint32 esize, cio_entry *cqe, uint8 *app_data); -t_bool cio_cqueue_avail(uint8 cid, uint32 esize); -void cio_rexpress(uint8 cid, uint32 esize, cio_entry *rqe, uint8 *app_data); -t_stat cio_rqueue(uint8 cid, uint32 qnum, uint32 esize, cio_entry *rqe, uint8 *app_data); -t_bool cio_rqueue_avail(uint8 cid, uint32 qnum, uint32 esize); -uint16 cio_r_lp(uint8 cid, uint32 qnum, uint32 esize); -uint16 cio_r_ulp(uint8 cid, uint32 qnum, uint32 esize); -uint16 cio_c_lp(uint8 cid, uint32 esize); -uint16 cio_c_ulp(uint8 cid, uint32 esize); -void cio_sysgen(uint8 cid); +void cio_cexpress(uint8 slot, uint32 esize, cio_entry *cqe, uint8 *app_data); +void cio_cqueue(uint8 slot, uint8 cmd_stat, uint32 esize, cio_entry *cqe, uint8 *app_data); +t_bool cio_cqueue_avail(uint8 slot, uint32 esize); +void cio_rexpress(uint8 slot, uint32 esize, cio_entry *rqe, uint8 *app_data); +t_stat cio_rqueue(uint8 slot, uint32 qnum, uint32 esize, cio_entry *rqe, uint8 *app_data); +t_bool cio_rqueue_avail(uint8 slot, uint32 qnum, uint32 esize); +uint16 cio_r_lp(uint8 slot, uint32 qnum, uint32 esize); +uint16 cio_r_ulp(uint8 slot, uint32 qnum, uint32 esize); +uint16 cio_c_lp(uint8 slot, uint32 esize); +uint16 cio_c_ulp(uint8 slot, uint32 esize); +void cio_sysgen(uint8 slot); uint32 io_read(uint32 pa, size_t size); void io_write(uint32 pa, uint32 val, size_t size); -void dump_entry(uint32 dbits, DEVICE *dev, CONST char *type, - uint32 esize, cio_entry *entry, uint8 *app_data); - extern uint16 cio_int_req; extern CIO_STATE cio[CIO_SLOTS]; diff --git a/3B2/3b2_iu.c b/3B2/3b2_iu.c index b9a67aa6..e3639eec 100644 --- a/3B2/3b2_iu.c +++ b/3B2/3b2_iu.c @@ -1,6 +1,6 @@ -/* 3b2_iu.c: SCN2681A Dual UART Implementation +/* 3b2_iu.c: SCN2681A Dual UART - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -39,61 +39,62 @@ #include "3b2_stddev.h" #include "3b2_timer.h" -#define SET_INT do { \ - CPU_SET_INT(INT_UART); \ - SET_CSR(CSRUART); \ - } while (0) - -#define CLR_INT do { \ - CPU_CLR_INT(INT_UART); \ - CLR_CSR(CSRUART); \ - } while (0) - -#if defined(REV3) -#define SET_DMA_INT do { \ - CPU_SET_INT(INT_UART_DMA); \ - SET_CSR(CSRDMA); \ - } while (0) - -#define CLR_DMA_INT do { \ - CPU_CLR_INT(INT_UART_DMA); \ - CLR_CSR(CSRDMA); \ - } while (0) -#else -#define SET_DMA_INT do { \ - CPU_SET_INT(INT_DMA); \ - SET_CSR(CSRDMA); \ - } while (0) - -#define CLR_DMA_INT do { \ - CPU_CLR_INT(INT_DMA); \ - CLR_CSR(CSRDMA); \ - } while (0) -#endif - -/* Static function declarations */ -static SIM_INLINE void iu_w_cmd(uint8 portno, uint8 val); - /* - * The 3B2/400 has two on-board serial ports, labeled CONSOLE and - * CONTTY. The CONSOLE port is (naturally) the system console. The - * CONTTY port serves as a secondary serial port for an additional - * terminal. + * The 3B2/400 and 3B2/700 both have two on-board serial ports, + * labeled CONSOLE and CONTTY. The CONSOLE port is the system console. + * The CONTTY port serves as a secondary serial port for one + * additional terminal. * * These lines are driven by an SCN2681A Dual UART, with two receivers * and two transmitters. * * In addition to the two TX/RX ports, the SCN27681A also has one - * programmable timer. + * programmable timer that is used in the 3B2 for various one-shot + * timing tasks. * * The SCN2681A UART is represented here by four devices: * - * - Console TTI (Input, port A) - * - Console TTO (Output, port A) - * - Contty (I/O, port B. Terminal multiplexer with one line) + * - Console TTI (Console Input, port A) + * - Console TTO (Console Output, port A) + * - CONTTY (I/O, port B. Terminal multiplexer with one line) * - IU Timer */ +#if defined(REV3) +#define DMA_INT INT_UART_DMA +#else +#define DMA_INT INT_DMA +#endif + +#define UPDATE_IRQ do { \ + if (iu_state.imr & iu_state.isr) { \ + CPU_SET_INT(INT_UART); \ + SET_CSR(CSRUART); \ + } else { \ + CPU_CLR_INT(INT_UART); \ + CLR_CSR(CSRUART); \ + } \ + } while (0) + +#define SET_DMA_INT do { \ + CPU_SET_INT(DMA_INT); \ + SET_CSR(CSRDMA); \ + } while (0) + +#define CLR_DMA_INT do { \ + CPU_CLR_INT(DMA_INT); \ + CLR_CSR(CSRDMA); \ + } while (0) + +#define LOOPBACK(P) (((P)->mode[1] & 0xc0) == 0x80) +#define TX_ENABLED(P) ((P).conf & TX_EN) +#define PORTNO(P) ((P) == &iu_console ? PORT_A : PORT_B) + +/* Static function declarations */ +static void iu_w_cmd(IU_PORT *port, uint8 val); +static t_stat iu_tx(IU_PORT *port, uint8 val); +static void iu_rx(IU_PORT *port, uint8 val); +static uint8 iu_rx_getc(IU_PORT *port); /* * Registers @@ -113,6 +114,8 @@ IU_TIMER_STATE iu_timer_state; t_bool iu_increment_a = FALSE; t_bool iu_increment_b = FALSE; +double iu_timer_multiplier = IU_TIMER_MULTIPLIER; + BITFIELD sr_bits[] = { BIT(RXRDY), BIT(FFULL), @@ -155,15 +158,17 @@ BITFIELD conf_bits[] = { /* TTI (Console) data structures */ +UNIT tti_unit = { UDATA(&iu_svc_tti, UNIT_IDLE|TT_MODE_8B, 0), SERIAL_IN_WAIT }; + REG tti_reg[] = { - { HRDATADF(STAT, iu_console.stat, 8, "Status", sr_bits) }, - { HRDATADF(CONF, iu_console.conf, 8, "Config", conf_bits) }, - { BRDATAD(DATA, iu_console.rxbuf, 16, 8, IU_BUF_SIZE, "Data") }, + { HRDATADF(SRA, iu_console.sr, 8, "Status", sr_bits) }, + { HRDATADF(CONF, iu_console.conf, 8, "Config", conf_bits) }, + { BRDATAD(DATA, iu_console.rxbuf, 16, 8, IU_BUF_SIZE, "Data") }, + { DRDATAD(POS, tti_unit.pos, T_ADDR_W, "number of characters input"), PV_LEFT }, + { DRDATAD(TIME, tti_unit.wait, 24, "input polling interval"), PV_LEFT }, { NULL } }; -UNIT tti_unit = { UDATA(&iu_svc_tti, UNIT_IDLE, 0), TMLN_SPD_9600_BPS }; - DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, NULL, 1, 8, 32, 1, 8, 8, @@ -174,17 +179,16 @@ DEVICE tti_dev = { /* TTO (Console) data structures */ +UNIT tto_unit = { UDATA(&iu_svc_tto, UNIT_IDLE|TT_MODE_8B, 0), SERIAL_OUT_WAIT }; + REG tto_reg[] = { - { HRDATADF(STAT, iu_console.stat, 8, "Status", sr_bits) }, - { HRDATADF(ISTAT, iu_state.istat, 8, "Interrupt Status", isr_bits) }, - { HRDATAD(IMR, iu_state.imr, 8, "Interrupt Mask") }, - { HRDATADF(ACR, iu_state.acr, 8, "Auxiliary Control Register", acr_bits) }, - { HRDATAD(DATA, iu_console.txbuf, 8, "Data") }, + { HRDATADF(SRA, iu_console.sr, 8, "Status Register", sr_bits) }, + { HRDATADF(ISR, iu_state.isr, 8, "Interrupt Status", isr_bits) }, + { HRDATAD(IMR, iu_state.imr, 8, "Interrupt Mask") }, + { HRDATADF(ACR, iu_state.acr, 8, "Aux. Control Register", acr_bits) }, { NULL } }; -UNIT tto_unit = { UDATA(&iu_svc_tto, TT_MODE_8B, 0), SERIAL_OUT_WAIT }; - DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, NULL, 1, 8, 32, 1, 8, 8, @@ -204,41 +208,47 @@ DEVICE tto_dev = { * served by a TMXR multiplexer. */ -TMLN *contty_ldsc = NULL; -TMXR contty_desc = { 1, 0, 0, NULL }; +TMLN contty_ldsc[1] = { 0 }; +TMXR contty_desc = { 1, 0, 0, contty_ldsc }; /* One fixed line */ + +UNIT contty_unit[2] = { + { UDATA(&iu_svc_contty, UNIT_IDLE|UNIT_ATTABLE|TT_MODE_8B, 0), SERIAL_IN_WAIT }, + { UDATA(&iu_svc_contty_xmt, UNIT_IDLE|UNIT_DIS, 0), SERIAL_OUT_WAIT } +}; REG contty_reg[] = { - { HRDATADF(STAT, iu_contty.stat, 8, "Status", sr_bits) }, + { HRDATADF(SRB, iu_contty.sr, 8, "Status Register", sr_bits) }, { HRDATADF(CONF, iu_contty.conf, 8, "Config", conf_bits) }, { BRDATAD(RXDATA, iu_contty.rxbuf, 16, 8, IU_BUF_SIZE, "RX Data") }, - { HRDATAD(TXDATA, iu_contty.txbuf, 8, "TX Data") }, - { HRDATADF(ISTAT, iu_state.istat, 8, "Interrupt Status", isr_bits) }, + { HRDATADF(ISR, iu_state.isr, 8, "Interrupt Status", isr_bits) }, { HRDATAD(IMR, iu_state.imr, 8, "Interrupt Mask") }, { HRDATADF(ACR, iu_state.acr, 8, "Auxiliary Control Register", acr_bits) }, + { DRDATAD(TIME, contty_unit[1].wait, 24, "output character delay"), PV_LEFT }, { NULL } }; +MTAB contty_mod[] = { + { UNIT_ATT, UNIT_ATT, "summary", NULL, + NULL, &tmxr_show_summ, (void *)&contty_desc, "Display a summary of line state" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &tmxr_show_cstat, (void *)&contty_desc, "Display current connection" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &tmxr_show_cstat, (void *)&contty_desc, "Display CONTTY statistics" } +}; + CONST char *brg_rates[IU_SPEED_REGS][IU_SPEEDS] = { - {NULL, "110", NULL, NULL, - "300", NULL, NULL, "1200", - "2400", "4800", NULL, "9600", - "38400", NULL, NULL, NULL}, - {NULL, "110", NULL, NULL, - "300", NULL, "1200", NULL, - NULL, "2400", "4800", "9600", - "19200", NULL, NULL, NULL} + {"50", "110", "134.5", "200", + "300", "600", "1200", "1050", + "2400", "4800", "7200", "9600", + "38400", NULL, NULL, NULL}, + {"75", "110", "134.5", "150", + "300", "600", "1200", "2000", + "2400", "4800", "1800", "9600", + "19200", NULL, NULL, NULL} }; CONST char *parity[3] = {"O", "E", "N"}; -UNIT contty_unit[2] = { - { UDATA(&iu_svc_contty_rcv, UNIT_ATTABLE, 0) }, - { UDATA(&iu_svc_contty_xmt, TT_MODE_8B, 0), SERIAL_OUT_WAIT } -}; - -UNIT *contty_rcv_unit = &contty_unit[0]; -UNIT *contty_xmt_unit = &contty_unit[1]; - DEBTAB contty_deb_tab[] = { {"EXEC", EXECUTE_MSG, "Execute"}, {"XMT", TMXR_DBG_XMT, "Transmitted Data"}, @@ -250,10 +260,9 @@ DEBTAB contty_deb_tab[] = { {0} }; - DEVICE contty_dev = { - "CONTTY", contty_unit, contty_reg, NULL, - 1, 8, 32, 1, 8, 8, + "CONTTY", contty_unit, contty_reg, contty_mod, + 2, 8, 32, 1, 8, 8, &tmxr_ex, &tmxr_dep, &contty_reset, NULL, &contty_attach, &contty_detach, NULL, DEV_DISABLE|DEV_DEBUG|DEV_MUX, @@ -265,33 +274,77 @@ DEVICE contty_dev = { /* IU Timer data structures */ +MTAB iu_timer_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "MULT", "MULT={1|2|3|4}", + &iu_timer_set_mult, &iu_timer_show_mult, NULL, "Timer Multiplier" } +}; + REG iu_timer_reg[] = { { HRDATAD(CTR_SET, iu_timer_state.c_set, 16, "Counter Setting") }, { NULL } }; -UNIT iu_timer_unit = { UDATA(&iu_svc_timer, 0, 0) }; +UNIT iu_timer_unit = { UDATA(&iu_svc_timer, UNIT_IDLE, 0) }; DEVICE iu_timer_dev = { - "IUTIMER", &iu_timer_unit, iu_timer_reg, NULL, + "IUTIMER", &iu_timer_unit, iu_timer_reg, iu_timer_mod, 1, 8, 32, 1, 8, 8, NULL, NULL, &iu_timer_reset, NULL, NULL, NULL, NULL, DEV_DEBUG, 0, sys_deb_tab }; +t_stat iu_timer_show_mult(FILE *st, UNIT *uptr, int val, const void *desc) +{ + fprintf(st, "mult=%d", (int) iu_timer_multiplier); + return SCPE_OK; +} + +t_stat iu_timer_set_mult(UNIT *uptr, int32 val, const char *cptr, void *desc) +{ + t_stat r; + t_value v; + v = get_uint(cptr, 10, 8, &r); + if (r != SCPE_OK) { + return r; + } + if (v < 1 || v > 4) { + return SCPE_ARG; + } + iu_timer_multiplier = (uint32) v; + return SCPE_OK; + +} + uint8 brg_reg = 0; /* Selected baud-rate generator register */ uint8 brg_clk = 11; /* Selected baud-rate generator clock */ uint8 parity_sel = 1; /* Selected parity */ uint8 bits_per_char = 7; - t_stat contty_attach(UNIT *uptr, CONST char *cptr) { t_stat r; TMLN *lp; + char line_config[16]; - tmxr_set_modem_control_passthru(&contty_desc); + /* Set initial line speed */ + brg_reg = 0; + brg_clk = BRG_DEFAULT; + parity_sel = IU_PARITY_EVEN; + bits_per_char = 7; + + sprintf(line_config, "%s-%d%s1", + brg_rates[brg_reg][brg_clk], + bits_per_char, + parity[parity_sel]); + + tmxr_set_config_line(&contty_ldsc[0], line_config); + + if ((sim_switches & SWMASK('M'))) { + tmxr_set_modem_control_passthru(&contty_desc); + } + + tmxr_set_line_output_unit(&contty_desc, 0, &contty_unit[1]); r = tmxr_attach(&contty_desc, uptr, cptr); if (r != SCPE_OK) { @@ -333,85 +386,111 @@ void increment_modep_b() } } -void iu_txrdy_a_irq() { - if ((iu_state.imr & IMR_TXRA) && - (iu_console.conf & TX_EN) && - (iu_console.stat & STS_TXR)) { - sim_debug(EXECUTE_MSG, &tto_dev, - "[iu_txrdy_a_irq()] Firing IRQ after transmit of %02x (%c)\n", - (uint8) iu_console.txbuf, PCHAR(iu_console.txbuf)); - SET_INT; - } -} - -void iu_txrdy_b_irq() { - if ((iu_state.imr & IMR_TXRB) && - (iu_contty.conf & TX_EN) && - (iu_contty.stat & STS_TXR)) { - sim_debug(EXECUTE_MSG, &contty_dev, - "[iu_txrdy_b_irq()] Firing IRQ after transmit of %02x (%c)\n", - (uint8) iu_contty.txbuf, PCHAR(iu_contty.txbuf)); - SET_INT; - } -} - t_stat tti_reset(DEVICE *dptr) { memset(&iu_state, 0, sizeof(IU_STATE)); memset(&iu_console, 0, sizeof(IU_PORT)); - /* Input Port logic is inverted - 0 means set */ - iu_state.inprt = ~(IU_DCDA); + /* Input Port is active low */ + iu_state.inprt = ~IU_DCDA; + iu_state.ipcr = IU_DCDA_CH | (0xf & ~IU_DCDA); + + tmxr_set_console_units(&tti_unit, &tto_unit); /* Start the Console TTI polling loop */ - if (!sim_is_active(&tti_unit)) { - sim_activate_after(&tti_unit, tti_unit.wait); - } + sim_activate_after(&tti_unit, tti_unit.wait); return SCPE_OK; } t_stat contty_reset(DEVICE *dtpr) { - char line_config[16]; - - if (contty_ldsc == NULL) { - contty_desc.ldsc = - contty_ldsc = - (TMLN *)calloc(1, sizeof(*contty_ldsc)); - } + sim_set_uname(&contty_unit[0], "CONTTY-RCV"); + sim_set_uname(&contty_unit[1], "CONTTY-XMT"); tmxr_set_port_speed_control(&contty_desc); - tmxr_set_line_unit(&contty_desc, 0, contty_rcv_unit); - tmxr_set_line_output_unit(&contty_desc, 0, contty_xmt_unit); - tmxr_set_console_units(&tti_unit, &tto_unit); - memset(&iu_state, 0, sizeof(IU_STATE)); memset(&iu_contty, 0, sizeof(IU_PORT)); - /* DCD is off (inverted logic, 1 means off) */ - iu_state.inprt |= IU_DCDB; - - brg_reg = 0; - brg_clk = BRG_DEFAULT; - parity_sel = IU_PARITY_EVEN; - bits_per_char = 7; - - sprintf(line_config, "%s-%d%s1", - brg_rates[brg_reg][brg_clk], - bits_per_char, - parity[parity_sel]); - - tmxr_set_config_line(&contty_ldsc[0], line_config); - - /* Start the CONTTY polling loop */ - if (!sim_is_active(contty_rcv_unit)) { - sim_activate_after(contty_rcv_unit, contty_rcv_unit->wait); + if (contty_unit[0].flags & UNIT_ATT) { + sim_activate_after(&contty_unit[0], contty_unit[0].wait); + } else { + sim_cancel(&contty_unit[0]); } return SCPE_OK; } +static void iu_rx(IU_PORT *port, uint8 val) +{ + if (port->conf & RX_EN) { + if (!(port->sr & STS_FFL)) { + /* If not full, we're reading into the FIFO */ + port->rxbuf[port->w_p] = val; + port->w_p = (port->w_p + 1) % IU_BUF_SIZE; + if (port->w_p == port->r_p) { + port->sr |= STS_FFL; + } + } else { + /* FIFO is full, and now we're going to have to hold a + * character in the shift register until space is + * available */ + + /* If the register already had data, it's going to be + * overwritten, so we have to set the overflow flag */ + if (port->rxr_full) { + port->sr |= STS_OER; + } + + /* Save the character */ + port->rxr = val; + port->rxr_full = TRUE; + } + + port->sr |= STS_RXR; + iu_state.isr |= (port == &iu_console) ? ISTS_RXRA : ISTS_RXRB; + } + + UPDATE_IRQ; +} + +static uint8 iu_rx_getc(IU_PORT *port) +{ + uint8 val = 0; + + if (port->conf & RX_EN) { + val = port->rxbuf[port->r_p]; + port->r_p = (port->r_p + 1) % IU_BUF_SIZE; + /* No longer full */ + port->sr &= ~STS_FFL; + if (port->r_p == port->w_p) { + /* Empty FIFO, nothing left to read */ + port->sr &= ~STS_RXR; + iu_state.isr &= (port == &iu_console) ? ~ISTS_RXRA : ~ISTS_RXRB; + } + + if (port->rxr_full) { + /* Need to shift data from the Receive Shift Register into + the space that we just freed up */ + port->rxbuf[port->w_p] = port->rxr; + port->w_p = (port->w_p + 1) % IU_BUF_SIZE; + /* FIFO can logically never be full here, since we just + * freed up space above. */ + port->rxr_full = FALSE; + } + + if (!(port->mode[0] & 0x20)) { + /* Receiver is in "Char Error" mode, so reset SR error + bits on read */ + port->sr &= ~(STS_RXB|STS_FER|STS_PER); + } + } + + UPDATE_IRQ; + + return val; +} + t_stat iu_timer_reset(DEVICE *dptr) { memset(&iu_timer_state, 0, sizeof(IU_TIMER_STATE)); @@ -423,38 +502,15 @@ t_stat iu_timer_reset(DEVICE *dptr) t_stat iu_svc_tti(UNIT *uptr) { - int32 temp; + int32 c; tmxr_clock_coschedule(uptr, tmxr_poll); - /* TODO: - - - If there has been a change on IP0-IP3, set the corresponding - bits in IPCR, if configured to do so. We'll need to figure out - how these are wired (DCD pin, etc?) - - - Update the Output Port pins (which are logically inverted) - based on the contents of the OPR, OPCR, MR, and CR registers. - */ - - if ((temp = sim_poll_kbd()) < SCPE_KFLAG) { - return temp; + if ((c = sim_poll_kbd()) < SCPE_KFLAG) { + return c; } - if (iu_console.conf & RX_EN) { - if ((iu_console.stat & STS_FFL) == 0) { - iu_console.rxbuf[iu_console.w_p] = (temp & 0xff); - iu_console.w_p = (iu_console.w_p + 1) % IU_BUF_SIZE; - if (iu_console.w_p == iu_contty.w_p) { - iu_console.stat |= STS_FFL; - } - } - iu_console.stat |= STS_RXR; - iu_state.istat |= ISTS_RAI; - if (iu_state.imr & IMR_RXRA) { - SET_INT; - } - } + iu_rx(&iu_console, (uint8) c); return SCPE_OK; } @@ -462,60 +518,108 @@ t_stat iu_svc_tti(UNIT *uptr) t_stat iu_svc_tto(UNIT *uptr) { - /* If there's more DMA to do, do it */ - if (iu_console.dma && ((dma_state.mask >> DMA_IUA_CHAN) & 0x1) == 0) { + t_stat result; + dma_channel *chan = &dma_state.channels[DMA_IUA_CHAN]; + + /* Check for data in the transmitter shift register that's ready + * to go out to the TX line */ + if (iu_console.tx_state & T_XMIT) { + if (LOOPBACK(&iu_console)) { + /* Handle loopback mode if set */ + sim_debug(EXECUTE_MSG, &tto_dev, "iu_svc_tto: CONSOLE is in loopback.\n"); + iu_console.tx_state &= ~T_XMIT; + + iu_rx(&iu_console, iu_console.txr); + + if (TX_ENABLED(iu_console) && !(iu_console.tx_state & T_HOLD)) { + iu_console.sr |= STS_TXE; + } + } else { + /* Direct mode, no loopback */ + result = sim_putchar_s(iu_console.txr); + if (result == SCPE_STALL) { + sim_debug(EXECUTE_MSG, &tto_dev, "iu_svc_tto: CONSOLE PUTC STALL\n"); + sim_activate_after(uptr, 1000); + return SCPE_OK; + } else { + iu_console.tx_state &= ~T_XMIT; + if (TX_ENABLED(iu_console) && !(iu_console.tx_state & T_HOLD)) { + iu_console.sr |= STS_TXE; + } + } + } + } + + /* Check for data in the holding register that's ready to go + * out to the transmitter shift register */ + if (iu_console.tx_state & T_HOLD) { + iu_console.tx_state &= ~T_HOLD; + iu_console.tx_state |= T_XMIT; + iu_console.txr = iu_console.thr; + /* If the transmitter is currently enabled, we need to update + * the TxRDY and TxEMT flags */ + if (TX_ENABLED(iu_console)) { + iu_console.sr &= ~STS_TXE; + iu_console.sr |= STS_TXR; + iu_state.isr |= ISTS_TXRA; + /* DRQ is always tied to TxRDY */ + iu_console.drq = TRUE; + } + + sim_activate_after_abs(uptr, uptr->wait); + } + + UPDATE_IRQ; + + /* If we're done transmitting and there's more DMA to do, + * do it. */ + if (!iu_console.tx_state && + chan->wcount_c >= 0 && + ((dma_state.mask >> DMA_IUA_CHAN) & 0x1) == 0) { + sim_debug(EXECUTE_MSG, &tto_dev, + "iu_svc_tto: Triggering next DMA\n"); iu_dma_console(DMA_IUA_CHAN, IUBASE+IUA_DATA_REG); - } else { - /* The buffer is now empty, we've transmitted, so set TXR */ - iu_console.stat |= STS_TXR; - iu_state.istat |= 1; - iu_txrdy_a_irq(); } return SCPE_OK; } -t_stat iu_svc_contty_rcv(UNIT *uptr) +t_stat iu_svc_contty(UNIT *uptr) { - int32 temp, ln; + int32 c; if ((uptr->flags & UNIT_ATT) == 0) { return SCPE_OK; } - /* Check for connect */ - if ((ln = tmxr_poll_conn(&contty_desc)) >= 0) { - contty_ldsc[ln].rcve = 1; - iu_state.inprt &= ~(IU_DCDB); - iu_state.ipcr |= IU_DCDB; - SET_INT; + /* Check for connect of our single line */ + if (tmxr_poll_conn(&contty_desc) == 0) { + contty_ldsc[0].rcve = 1; + + iu_state.inprt &= ~IU_DCDB; + iu_state.ipcr &= ~IU_DCDB; + iu_state.ipcr |= IU_DCDB_CH; + + UPDATE_IRQ; } + tmxr_poll_tx(&contty_desc); + tmxr_poll_rx(&contty_desc); + /* Check for disconnect */ - if (!contty_ldsc[0].conn && (iu_state.inprt & IU_DCDB) == 0) { + if (!contty_ldsc[0].conn && contty_ldsc[0].rcve) { contty_ldsc[0].rcve = 0; iu_state.inprt |= IU_DCDB; - iu_state.ipcr |= IU_DCDB; - SET_INT; - } else if (iu_contty.conf & RX_EN) { - tmxr_poll_rx(&contty_desc); + iu_state.ipcr |= (IU_DCDB_CH|IU_DCDB); - if (contty_ldsc[0].conn) { - temp = tmxr_getc_ln(&contty_ldsc[0]); - if (temp && !(temp & SCPE_BREAK)) { - if ((iu_contty.stat & STS_FFL) == 0) { - iu_contty.rxbuf[iu_contty.w_p] = (temp & 0xff); - iu_contty.w_p = (iu_contty.w_p + 1) % IU_BUF_SIZE; - if (iu_contty.w_p == iu_contty.r_p) { - iu_contty.stat |= STS_FFL; - } - } - iu_contty.stat |= STS_RXR; - iu_state.istat |= ISTS_RBI; - if (iu_state.imr & IMR_RXRB) { - SET_INT; - } - } + UPDATE_IRQ; + } + + /* Check for RX */ + if ((iu_contty.conf & RX_EN) && contty_ldsc[0].conn) { + c = tmxr_getc_ln(&contty_ldsc[0]); + if (c && !(c & SCPE_BREAK)) { + iu_rx(&iu_contty, (uint8) c); } } @@ -527,17 +631,71 @@ t_stat iu_svc_contty_rcv(UNIT *uptr) t_stat iu_svc_contty_xmt(UNIT *uptr) { dma_channel *chan = &dma_state.channels[DMA_IUB_CHAN]; + TMLN *lp = &contty_ldsc[0]; + t_stat result; - tmxr_poll_tx(&contty_desc); + /* Check for data in the transmitter shift register that's ready + * to go out to the TX line */ + if (iu_contty.tx_state & T_XMIT) { + if (LOOPBACK(&iu_contty)) { + /* Handle loopback mode if set */ + sim_debug(EXECUTE_MSG, &contty_dev, "iu_svc_contty: CONTTY is in loopback.\n"); + iu_contty.tx_state &= ~T_XMIT; - /* If there's more DMA to do, do it */ - if (chan->wcount_c >= 0) { + iu_rx(&iu_contty, iu_contty.txr); + + if (TX_ENABLED(iu_contty) && !(iu_contty.tx_state & T_HOLD)) { + iu_contty.sr |= STS_TXE; + } + } else { + /* Direct mode, no loopback */ + result = tmxr_putc_ln(lp, iu_contty.txr); + if (result == SCPE_STALL) { + sim_debug(EXECUTE_MSG, &contty_dev, "iu_svc_contty: CONTTY PUTC STALL: %d\n", result); + sim_activate_after(uptr, 1000); + return SCPE_OK; + } else { + tmxr_poll_tx(&contty_desc); + iu_contty.tx_state &= ~T_XMIT; + if (TX_ENABLED(iu_contty) && !(iu_contty.tx_state & T_HOLD)) { + iu_contty.sr |= STS_TXE; + } + } + } + } + + /* Check for data in the holding register that's ready to go + * out to the transmitter shift register */ + if (iu_contty.tx_state & T_HOLD) { + sim_debug(EXECUTE_MSG, &contty_dev, + "THRB->TXRB: 0x%02x (%c)\n", + iu_contty.thr, PCHAR(iu_contty.thr)); + iu_contty.tx_state &= ~T_HOLD; + iu_contty.tx_state |= T_XMIT; + iu_contty.txr = iu_contty.thr; + /* If the transmitter is currently enabled, we need to update + * the TxRDY and TxEMT flags */ + if (TX_ENABLED(iu_contty)) { + iu_contty.sr &= ~STS_TXE; + iu_contty.sr |= STS_TXR; + iu_state.isr |= ISTS_TXRB; + /* DRQ is always tied to TxRDY */ + iu_contty.drq = TRUE; + } + + sim_activate_after_abs(uptr, uptr->wait); + } + + UPDATE_IRQ; + + /* If we're done transmitting and there's more DMA to do, + * do it. */ + if (!iu_contty.tx_state && + chan->wcount_c >= 0 && + ((dma_state.mask >> DMA_IUB_CHAN) & 0x1) == 0) { + sim_debug(EXECUTE_MSG, &tto_dev, + "iu_svc_contty_xmt: Triggering next DMA\n"); iu_dma_contty(DMA_IUB_CHAN, IUBASE+IUB_DATA_REG); - } else { - /* The buffer is now empty, we've transmitted, so set TXR */ - iu_contty.stat |= STS_TXR; - iu_state.istat |= 0x10; - iu_txrdy_b_irq(); } return SCPE_OK; @@ -545,12 +703,13 @@ t_stat iu_svc_contty_xmt(UNIT *uptr) t_stat iu_svc_timer(UNIT *uptr) { - iu_state.istat |= ISTS_CRI; + iu_state.isr |= ISTS_CRI; - if (iu_state.imr & IMR_CTR) { - SET_INT; - } + sim_debug(EXECUTE_MSG, &iu_timer_dev, + "[iu_svc_timer] IMR=%02x ISR=%02x => %02x\n", + iu_state.imr, iu_state.isr, (iu_state.imr & iu_state.isr)); + UPDATE_IRQ; return SCPE_OK; } @@ -590,30 +749,18 @@ uint32 iu_read(uint32 pa, size_t size) iu_increment_a = TRUE; break; case SRA: - data = iu_console.stat; + data = iu_console.sr; break; case RHRA: - if (iu_console.conf & RX_EN) { - data = iu_console.rxbuf[iu_console.r_p]; - iu_console.r_p = (iu_console.r_p + 1) % IU_BUF_SIZE; - /* If the FIFO is not empty, we must cause another interrupt - * to continue reading */ - if (iu_console.r_p == iu_console.w_p) { - iu_console.stat &= ~(STS_RXR|STS_FFL); - iu_state.istat &= ~ISTS_RAI; - } else if (iu_state.imr & IMR_RXRA) { - SET_INT; - } - } + data = iu_rx_getc(&iu_console); break; case IPCR: data = iu_state.ipcr; - /* Reading the port resets it */ - iu_state.ipcr = 0; - CLR_INT; + /* Reading the port resets the top 4 bits */ + iu_state.ipcr &= 0xf; break; case ISR: - data = iu_state.istat; + data = iu_state.isr; break; case CTU: data = (iu_timer_state.c_set >> 8) & 0xff; @@ -627,37 +774,29 @@ uint32 iu_read(uint32 pa, size_t size) iu_increment_b = TRUE; break; case SRB: - data = iu_contty.stat; + data = iu_contty.sr; break; case RHRB: - if (iu_contty.conf & RX_EN) { - data = iu_contty.rxbuf[iu_contty.r_p]; - iu_contty.r_p = (iu_contty.r_p + 1) % IU_BUF_SIZE; - /* If the FIFO is not empty, we must cause another interrupt - * to continue reading */ - if (iu_contty.r_p == iu_contty.w_p) { - iu_contty.stat &= ~(STS_RXR|STS_FFL); - iu_state.istat &= ~ISTS_RBI; - } else if (iu_state.imr & IMR_RXRB) { - SET_INT; - } - } + data = iu_rx_getc(&iu_contty); break; case INPRT: data = iu_state.inprt; break; case START_CTR: data = 0; - iu_state.istat &= ~ISTS_CRI; - sim_activate_abs(&iu_timer_unit, (int32)(iu_timer_state.c_set * IU_TIMER_RATE)); + iu_state.isr &= ~ISTS_CRI; + sim_debug(EXECUTE_MSG, &iu_timer_dev, + "ACR=%02x : Activating IU Timer in %d ticks / %d microseconds\n", + iu_state.acr, iu_timer_state.c_set, (int32)(iu_timer_state.c_set * iu_timer_multiplier)); + sim_activate_after(&iu_timer_unit, (int32)(iu_timer_state.c_set * iu_timer_multiplier)); break; case STOP_CTR: data = 0; - iu_state.istat &= ~ISTS_CRI; - CLR_INT; + iu_state.isr &= ~ISTS_CRI; + UPDATE_IRQ; sim_cancel(&iu_timer_unit); break; - case 17: /* Clear DMAC interrupt */ + case 17: /* Clear DMA interrupt */ data = 0; CLR_DMA_INT; break; @@ -684,25 +823,13 @@ void iu_write(uint32 pa, uint32 val, size_t size) iu_increment_a = TRUE; break; case CSRA: - if (brg_rates[brg_reg][brg_clk] != NULL) { - sprintf(line_config, "%s-%d%s1", - brg_rates[brg_reg][brg_clk], - bits_per_char, - parity[parity_sel]); - - sim_debug(EXECUTE_MSG, &contty_dev, - "Setting CONTTY line to %s\n", - line_config); - - tmxr_set_config_line(&contty_ldsc[0], line_config); - } + /* Nothing supported */ break; case CRA: /* Command A */ - iu_w_cmd(PORT_A, bval); + iu_w_cmd(&iu_console, bval); break; case THRA: /* TX/RX Buf A */ - iu_tx(PORT_A, bval); - sim_activate_abs(&tto_unit, tto_unit.wait); + iu_tx(&iu_console, bval); break; case ACR: /* Auxiliary Control Register */ iu_state.acr = bval; @@ -710,13 +837,7 @@ void iu_write(uint32 pa, uint32 val, size_t size) break; case IMR: iu_state.imr = bval; - sim_debug(EXECUTE_MSG, &tto_dev, - "[%08x] Write IMR = %x\n", - R[NUM_PC], bval); - CLR_INT; - /* Possibly cause an interrupt */ - iu_txrdy_a_irq(); - iu_txrdy_b_irq(); + UPDATE_IRQ; break; case CTUR: /* Counter/Timer Upper Preset Value */ /* Clear out high byte */ @@ -733,6 +854,7 @@ void iu_write(uint32 pa, uint32 val, size_t size) case MR12B: modep = iu_contty.modep; iu_contty.mode[modep] = bval; + sim_debug(EXECUTE_MSG, &tto_dev, "MR12B: Page %d Mode = %02x\n", modep, bval); iu_increment_b = TRUE; if (modep == 0) { if ((bval >> 4) & 1) { @@ -750,8 +872,8 @@ void iu_write(uint32 pa, uint32 val, size_t size) bits_per_char = (bval & 3) + 5; } break; - case CRB: /* Command B */ - iu_w_cmd(PORT_B, bval); + case CRB: + iu_w_cmd(&iu_contty, bval); break; case CSRB: brg_clk = (bval >> 4) & 0xf; @@ -770,183 +892,173 @@ void iu_write(uint32 pa, uint32 val, size_t size) } break; - case THRB: /* TX/RX Buf B */ - iu_tx(PORT_B, bval); - sim_activate_abs(contty_xmt_unit, contty_ldsc[0].txdeltausecs); + case THRB: + iu_tx(&iu_contty, bval); break; case OPCR: iu_state.opcr = bval; break; case SOPR: +#if defined (REV2) /* Bit 2 of the IU output register is used as a soft power * switch. When set, the machine will power down * immediately. */ if (bval & IU_KILLPWR) { stop_reason = STOP_POWER; } +#endif break; case ROPR: break; + case 17: /* Clear DMA interrupt */ + sim_debug(EXECUTE_MSG, &tto_dev, + "[WRITE] Clear DMA interrupt in UART\n"); + CLR_DMA_INT; + break; default: break; } } -t_stat iu_tx(uint8 portno, uint8 val) +/* + * Transmit a single character + */ +t_stat iu_tx(IU_PORT *port, uint8 val) { - IU_PORT *p = (portno == PORT_A) ? &iu_console : &iu_contty; - uint8 ists = (portno == PORT_A) ? ISTS_RAI : ISTS_RBI; - uint8 imr_mask = (portno == PORT_A) ? IMR_RXRA : IMR_RXRB; int32 c; - t_stat status = SCPE_OK; + uint8 tx_ists = (port == &iu_console) ? ISTS_TXRA : ISTS_TXRB; + UNIT *uptr = (port == &iu_console) ? &tto_unit : &contty_unit[1]; - if (p->conf & TX_EN) { - if ((p->mode[1] & 0xc0) == 0x80) { /* Loopback mode */ - p->txbuf = val; + sim_debug(EXECUTE_MSG, &tto_dev, + "iu_tx PORT=%d CHAR=%02x (%c)\n", + PORTNO(port), val, PCHAR(val)); - /* This is also a Receive */ - if ((p->stat & STS_FFL) == 0) { - p->rxbuf[p->w_p] = val; - p->w_p = (p->w_p + 1) % IU_BUF_SIZE; - if (p->w_p == p->r_p) { - p->stat |= STS_FFL; - } - } - - p->stat |= STS_RXR; - if (iu_state.imr & imr_mask) { - iu_state.istat |= ists; - SET_INT; - } - - return SCPE_OK; - } else { /* Direct mode */ - c = sim_tt_outcvt(val, TTUF_MODE_8B); - - if (c >= 0) { - p->txbuf = c; - p->stat &= ~(STS_TXR|STS_TXE); - iu_state.istat &= ~(1 << (portno*4)); - - if (portno == PORT_A) { - /* Write the character to the SIMH console */ - sim_debug(EXECUTE_MSG, &tto_dev, - "[iu_tx] CONSOLE transmit %02x (%c)\n", - (uint8) c, PCHAR(c)); - status = sim_putchar_s(c); - } else { - sim_debug(EXECUTE_MSG, &contty_dev, - "[iu_tx] CONTTY transmit %02x (%c)\n", - (uint8) c, PCHAR(c)); - status = tmxr_putc_ln(&contty_ldsc[0], c); - } - } - } + if (!(port->conf & TX_EN) || !(port->sr & STS_TXR)) { + sim_debug(EXECUTE_MSG, &tto_dev, + ">>> IGNORING TRANSMIT, NOT ENABLED OR NOT READY!!!\n"); + return SCPE_INCOMP; } - return status; + c = sim_tt_outcvt(val, TTUF_MODE_8B); + + if (c >= 0) { + port->tx_state |= T_HOLD; + port->sr &= ~(STS_TXR|STS_TXE); + port->drq = FALSE; + iu_state.isr &= ~(tx_ists); + port->thr = c; + sim_activate_after(uptr, uptr->wait); + } + + return SCPE_OK; } -static SIM_INLINE void iu_w_cmd(uint8 portno, uint8 cmd) +static void iu_w_cmd(IU_PORT *port, uint8 cmd) { - - IU_PORT *p; - - if (portno == 0) { - p = &iu_console; - } else { - p = &iu_contty; - } + uint8 tx_ists = (port == &iu_console) ? ISTS_TXRA : ISTS_TXRB; + uint8 dbk_ists = (port == &iu_console) ? ISTS_DBA : ISTS_DBB; /* Enable or disable transmitter */ /* Disable always wins, if both are set */ if (cmd & CMD_DTX) { - p->conf &= ~TX_EN; - p->stat &= ~STS_TXR; - p->stat &= ~STS_TXE; - p->drq = FALSE; - p->dma = FALSE; + port->conf &= ~TX_EN; + port->sr &= ~(STS_TXR|STS_TXE); + port->drq = FALSE; + iu_state.isr &= ~tx_ists; + UPDATE_IRQ; + sim_debug(EXECUTE_MSG, &tto_dev, + "DISABLE TX, PORT %d\n", PORTNO(port)); } else if (cmd & CMD_ETX) { - p->conf |= TX_EN; - /* TXE and TXR are always set by an ENABLE */ - p->stat |= STS_TXR; - p->stat |= STS_TXE; - p->drq = TRUE; - iu_state.istat |= 1 << (portno*4); - if (portno == 0) { - iu_txrdy_a_irq(); - } else { - iu_txrdy_b_irq(); + if (!(port->conf & TX_EN)) { + /* TXE and TXR are always set by an ENABLE if prior state + was DISABLED */ + port->sr |= (STS_TXR|STS_TXE); + port->drq = TRUE; } + port->conf |= TX_EN; + iu_state.isr |= tx_ists; + UPDATE_IRQ; + sim_debug(EXECUTE_MSG, &tto_dev, + "ENABLE TX, PORT %d\n", PORTNO(port)); } /* Enable or disable receiver. */ /* Disable always wins, if both are set */ if (cmd & CMD_DRX) { - p->conf &= ~RX_EN; - p->stat &= ~STS_RXR; + port->conf &= ~RX_EN; + port->sr &= ~STS_RXR; } else if (cmd & CMD_ERX) { - p->conf |= RX_EN; + port->conf |= RX_EN; } /* Command register bits 6-4 have special meaning */ switch ((cmd >> CMD_MISC_SHIFT) & CMD_MISC_MASK) { - case 1: + case CR_RST_MR: /* Causes the Channel A MR pointer to point to MR1. */ - p->modep = 0; + port->modep = 0; break; - case 2: + case CR_RST_RX: + sim_debug(EXECUTE_MSG, &tto_dev, + "PORT %d Command: RESET RX\n", PORTNO(port)); /* Reset receiver. Resets the Channel's receiver as if a hardware reset had been applied. The receiver is disabled and the FIFO is flushed. */ - p->stat &= ~STS_RXR; - p->conf &= ~RX_EN; - p->w_p = 0; - p->r_p = 0; + port->sr &= ~STS_RXR; + port->conf &= ~RX_EN; + port->w_p = 0; + port->r_p = 0; break; - case 3: + case CR_RST_TX: + sim_debug(EXECUTE_MSG, &tto_dev, + "PORT %d Command: RESET TX\n", PORTNO(port)); /* Reset transmitter. Resets the Channel's transmitter as if a hardware reset had been applied. */ - p->stat &= ~STS_TXR; - p->stat &= ~STS_TXE; - p->conf &= ~TX_EN; - p->w_p = 0; - p->r_p = 0; + port->sr &= ~STS_TXR; + port->sr &= ~STS_TXE; + port->drq = FALSE; /* drq is tied to TXR */ + port->conf &= ~TX_EN; break; - case 4: + case CR_RST_ERR: /* Reset error status. Clears the Channel's Received Break, - Parity Error, and Overrun Error bits in the status register - (SRA[7:4]). Used in character mode to clear OE status - (although RB, PE and FE bits will also be cleared) and in - block mode to clear all error status after a block of data - has been received. */ - p->stat &= ~(STS_FER|STS_PER|STS_OER); + Parity Error, Framing Error, and Overrun Error bits in the + status register (SRn[7:4]). */ + sim_debug(EXECUTE_MSG, &tto_dev, + "PORT %d Command: RESET ERROR\n", PORTNO(port)); + port->sr &= ~(STS_RXB|STS_FER|STS_PER|STS_OER); break; - case 5: + case CR_RST_BRK: /* Reset Channel's break change interrupt. Causes the Channel A break detect change bit in the interrupt status register (ISR[2] for Chan. A, ISR[6] for Chan. B) to be cleared to zero. */ - iu_state.istat &= ~(1 << (2 + portno*4)); + sim_debug(EXECUTE_MSG, &tto_dev, + "PORT %d Command: RESET BREAK IRQ\n", PORTNO(port)); + iu_state.isr &= ~dbk_ists; break; - case 6: - /* Start break. Forces the TxDA output LOW (spacing). If the - transmitter is empty the start of the break condition will - be delayed up to two bit times. If the transmitter is - active the break begins when transmission of the character - is completed. If a character is in the THR, the start of - the break will be delayed until that character, or any - other loaded subsequently are transmitted. The transmitter - must be enabled for this command to be accepted. */ - /* Not Implemented */ + case CR_START_BRK: + sim_debug(EXECUTE_MSG, &tto_dev, + "PORT %d Command: START BREAK. loopback=%d\n", + PORTNO(port), LOOPBACK(port)); + if (LOOPBACK(port)) { + /* Set "Received Break" and "Parity Error" bits in + SRA/SRB */ + port->sr |= (STS_RXB|STS_PER); + /* Set "Delta Break" bit A or B in ISR */ + iu_state.isr |= dbk_ists; + } break; - case 7: - /* Stop break. The TxDA line will go HIGH (marking) within two - bit times. TxDA will remain HIGH for one bit time before - the next character, if any, is transmitted. */ - /* Not Implemented */ + case CR_STOP_BRK: + sim_debug(EXECUTE_MSG, &tto_dev, + "PORT %d Command: STOP BREAK. loopback=%d\n", + PORTNO(port), LOOPBACK(port)); + if (LOOPBACK(port)) { + /* Set "Delta Break" bit A or B in ISR */ + iu_state.isr |= dbk_ists; + } break; } + + UPDATE_IRQ; } /* @@ -958,45 +1070,39 @@ void iu_dma_console(uint8 channel, uint32 service_address) uint32 addr; t_stat status = SCPE_OK; dma_channel *chan = &dma_state.channels[channel]; - UNIT *uptr = &tto_unit; IU_PORT *port = &iu_console; - /* Immediate acknowledge of DMA */ - port->drq = FALSE; - - if (!port->dma) { - /* Set DMA transfer type */ - port->dma = 1u << ((dma_state.mode >> 2) & 0xf); + /* If we're doing DMA and we're done, end it */ + if (iu_console.dma && chan->wcount_c < 0) { + sim_debug(EXECUTE_MSG, &tto_dev, + "iu_svc_tto: DMA Complete.\n"); + iu_console.dma = FALSE; + dma_state.mask |= (1 << DMA_IUA_CHAN); + dma_state.status |= (1 << DMA_IUA_CHAN); + SET_DMA_INT; + return; } - if (port->dma == DMA_READ) { - addr = dma_address(channel, chan->ptr, TRUE); - chan->addr_c = chan->addr + chan->ptr + 1; - data = pread_b(addr); - status = iu_tx(channel - 2, data); + /* Mark that IUA is in DMA */ + port->dma = TRUE; + + switch (DMA_XFER(DMA_IUA_CHAN)) { + case DMA_XFER_READ: + addr = dma_address(channel, chan->ptr); + chan->addr_c = addr; + data = pread_b(addr, BUS_PER); + status = iu_tx(port, data); if (status == SCPE_OK) { chan->ptr++; chan->wcount_c--; } - - sim_activate_abs(uptr, uptr->wait); - - if (chan->wcount_c >= 0) { - /* Return early so we don't finish DMA */ - return; - } + break; + default: + sim_debug(EXECUTE_MSG, &tto_dev, + "iu_dma_console: Error, transfer type %d not supported\n", + DMA_XFER(DMA_IUA_CHAN)); + break; } - - /* Cancel any pending interrupts, we're done. */ - /* TODO: I THINK THIS BREAKS EVERYTHING!!!! */ - /* sim_cancel(uptr); */ - - /* Done with DMA */ - port->dma = DMA_NONE; - - dma_state.mask |= (1 << channel); - dma_state.status |= (1 << channel); - SET_DMA_INT; } void iu_dma_contty(uint8 channel, uint32 service_address) @@ -1005,41 +1111,37 @@ void iu_dma_contty(uint8 channel, uint32 service_address) uint32 addr; t_stat status = SCPE_OK; dma_channel *chan = &dma_state.channels[channel]; - UNIT *uptr = contty_xmt_unit; IU_PORT *port = &iu_contty; - uint32 wait = 0x7fffffff; - /* Immediate acknowledge of DMA */ - port->drq = FALSE; - - if (!port->dma) { - /* Set DMA transfer type */ - port->dma = 1u << ((dma_state.mode >> 2) & 0xf); + /* If we're doing DMA and we're done, end it */ + if (iu_contty.dma && chan->wcount_c < 0) { + sim_debug(EXECUTE_MSG, &contty_dev, + "iu_svc_contty_xmt: DMA Complete.\n"); + iu_contty.dma = FALSE; + dma_state.mask |= (1 << DMA_IUB_CHAN); + dma_state.status |= (1 << DMA_IUB_CHAN); + SET_DMA_INT; + return; } - if (port->dma == DMA_READ) { - addr = dma_address(channel, chan->ptr, TRUE); - chan->addr_c = chan->addr + chan->ptr + 1; - data = pread_b(addr); - status = iu_tx(channel - 2, data); + /* Mark that IUB is in DMA */ + port->dma = TRUE; + + switch (DMA_XFER(DMA_IUB_CHAN)) { + case DMA_XFER_READ: + addr = dma_address(channel, chan->ptr); + chan->addr_c = addr; + data = pread_b(addr, BUS_PER); + status = iu_tx(port, data); if (status == SCPE_OK) { - wait = MIN(wait, contty_ldsc[0].txdeltausecs); chan->ptr++; chan->wcount_c--; } - - tmxr_activate_after(uptr, wait); - - if (chan->wcount_c >= 0) { - /* Return early so we don't finish DMA */ - return; - } + break; + default: + sim_debug(EXECUTE_MSG, &contty_dev, + "iu_dma_contty: Error, transfer type %d not supported\n", + DMA_XFER(DMA_IUB_CHAN)); + break; } - - /* Done with DMA */ - port->dma = DMA_NONE; - - dma_state.mask |= (1 << channel); - dma_state.status |= (1 << channel); - SET_DMA_INT; } diff --git a/3B2/3b2_iu.h b/3B2/3b2_iu.h index 2a718ed0..2fb61887 100644 --- a/3B2/3b2_iu.h +++ b/3B2/3b2_iu.h @@ -1,6 +1,6 @@ -/* 3b2_iu.h: SCN2681A Dual UART Header +/* 3b2_iu.h: SCN2681A Dual UART - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -56,18 +56,22 @@ #define STS_FER 0x40 /* Framing error */ #define STS_RXB 0x80 /* Received break */ -#define ISTS_TAI 0x01 /* Transmitter ready A */ -#define ISTS_RAI 0x02 /* Receiver ready A */ -#define ISTS_CBA 0x04 /* Change in break A */ +#define ISTS_TXRA 0x01 /* Transmitter ready A */ +#define ISTS_RXRA 0x02 /* Receiver ready A */ +#define ISTS_DBA 0x04 /* Delta Break A */ #define ISTS_CRI 0x08 /* Counter ready */ -#define ISTS_TBI 0x10 /* Transmitter ready B */ -#define ISTS_RBI 0x20 /* Receiver ready B */ -#define ISTS_CBB 0x40 /* Change in break B */ +#define ISTS_TXRB 0x10 /* Transmitter ready B */ +#define ISTS_RXRB 0x20 /* Receiver ready B */ +#define ISTS_DBB 0x40 /* Delta Break B */ #define ISTS_IPC 0x80 /* Interrupt port change */ #define MODE_V_CHM 6 /* Channel mode */ #define MODE_M_CHM 0x3 +/* Transmitter State bits */ +#define T_HOLD 1 +#define T_XMIT 2 + /* Used by the DMAC */ #define IUA_DATA_REG 3 #define IUB_DATA_REG 11 @@ -81,7 +85,7 @@ #define CTL 7 #define SRB 9 #define RHRB 11 -#define INPRT 13 /* Input port data */ +#define INPRT 13 #define START_CTR 14 #define STOP_CTR 15 @@ -108,16 +112,14 @@ #define TX_EN 1 #define RX_EN 2 -#define UM_CTR_EXT 0 -#define UM_CTR_TXCA 1 -#define UM_CTR_TXCB 2 -#define UM_CTR_DIV16 3 -#define UM_TMR_EXT 4 -#define UM_TMR_EXT16 5 -#define UM_TMR_XTL 6 -#define UM_TMR_XTL16 7 -#define UM_MASK 0x70 -#define UM_SHIFT 4 +/* Control Register commands */ +#define CR_RST_MR 1 +#define CR_RST_RX 2 +#define CR_RST_TX 3 +#define CR_RST_ERR 4 +#define CR_RST_BRK 5 +#define CR_START_BRK 6 +#define CR_STOP_BRK 7 /* IMR bits */ #define IMR_TXRA 0x01 @@ -139,40 +141,57 @@ #define IU_BUF_SIZE 3 -#define IU_DCDA 0x01 +/* Data Carrier Detect inputs and input change bits */ +#if defined(REV3) +#define IU_DCDB_CH 0x80 +#define IU_DCDA_CH 0x40 +#define IU_DCDB 0x08 +#define IU_DCDA 0x04 +#else +#define IU_DCDB_CH 0x20 +#define IU_DCDA_CH 0x10 #define IU_DCDB 0x02 -#define IU_DTRA 0x01 -#define IU_DTRB 0x02 - -#define DMA_NONE 0 -#define DMA_VERIFY 1 -#define DMA_WRITE 2 -#define DMA_READ 4 +#define IU_DCDA 0x01 +#endif /* Default baud rate generator (9600 baud) */ #define BRG_DEFAULT 11 -#define IU_TIMER_RATE 2.114 /* microseconds per step */ +/* The 2681 DUART includes a 16-bit timer/counter that can be used to + * trigger an interrupt after a certain amount of time has passed. + * + * The 2681 uses a crystal with a frequency of 3.686400 MHz, and the + * timer/counter uses this frequency divided by 16, giving a final + * timer/counter frequency of 230,400 Hz. There are therefore 4.34 + * microseconds of wall time per tick of the timer. + * + * The multiplier defined below is a default that can be adjusted to + * make IU timing faster, but less accurate, if desired */ +#define IU_TIMER_MULTIPLIER 4 typedef struct iu_port { - uint8 stat; /* Port Status */ uint8 cmd; /* Command */ uint8 mode[2]; /* Two mode buffers */ uint8 modep; /* Point to mode[0] or mode[1] */ uint8 conf; /* Configuration bits */ - uint8 txbuf; /* Transmit Holding Register */ + uint8 sr; /* Status Register */ + uint8 thr; /* Transmit Holding Register */ + uint8 txr; /* Transmit Shift Register */ + uint8 rxr; /* Receive Shift Register */ uint8 rxbuf[IU_BUF_SIZE]; /* Receive Holding Register (3 bytes) */ - uint8 w_p; /* Buffer Write Pointer */ - uint8 r_p; /* Buffer Read Pointer */ - uint8 dma; /* Currently active DMA mode */ + uint8 w_p; /* Receive Buffer Write Pointer */ + uint8 r_p; /* Receive Buffer Read Pointer */ + uint8 tx_state; /* Transmitting state flags (HOLD, XMIT) */ + t_bool dma; /* DMA currently active */ t_bool drq; /* DMA request enabled */ + t_bool rxr_full; /* Receive Shift Register is full */ } IU_PORT; typedef struct iu_state { - uint8 istat; /* Interrupt Status */ + uint8 isr; /* Interrupt Status Register */ uint8 imr; /* Interrupt Mask Register */ - uint8 acr; + uint8 acr; /* Aux. Control Register */ uint8 opcr; /* Output Port Configuration */ uint8 inprt; /* Input Port Data */ uint8 ipcr; /* Input Port Change Register */ @@ -189,12 +208,13 @@ t_stat contty_detach(UNIT *uptr); t_stat tti_reset(DEVICE *dptr); t_stat contty_reset(DEVICE *dptr); t_stat iu_timer_reset(DEVICE *dptr); +t_stat iu_timer_set_mult(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat iu_timer_show_mult(FILE *st, UNIT *uptr, int val, CONST void *desc); t_stat iu_svc_tti(UNIT *uptr); t_stat iu_svc_tto(UNIT *uptr); -t_stat iu_svc_contty_rcv(UNIT *uptr); +t_stat iu_svc_contty(UNIT *uptr); t_stat iu_svc_contty_xmt(UNIT *uptr); t_stat iu_svc_timer(UNIT *uptr); -t_stat iu_tx(uint8 portno, uint8 val); uint32 iu_read(uint32 pa, size_t size); void iu_write(uint32 pa, uint32 val, size_t size); void iua_drq_handled(); diff --git a/3B2/3b2_rev2_mau.c b/3B2/3b2_mau.c similarity index 91% rename from 3B2/3b2_rev2_mau.c rename to 3B2/3b2_mau.c index 2b097794..fc14b27c 100644 --- a/3B2/3b2_rev2_mau.c +++ b/3B2/3b2_mau.c @@ -1,7 +1,6 @@ -/* 3b2_rev2_mau.c: AT&T 3B2 Rev 2 (Model 400) Math Acceleration - Unit (WE32106 MAU) Implementation +/* 3b2_mau.c: WE32106 and WE32206 Math Accelerator Unit - Copyright (c) 2019, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -30,11 +29,33 @@ --------------------------------------------------------------------- - This file is part of a simulation of the WE32106 Math Acceleration - Unit. The WE32106 MAU is an IEEE-754 compabitle floating point - hardware math accelerator that was available as an optional - component on the AT&T 3B2/310 and 3B2/400, and a standard component - on the 3B2/500, 3B2/600, and 3B2/1000. + This file is part of a simulation of the WE 32106 and WE 32206 Math + Acceleration Units. The WE 32106 and WE 32206 are IEEE-754 + compabitle floating point hardware math accelerators that were + available as an optional component on the AT&T 3B2/310 and 3B2/400, + and as a standard component on the 3B2/500, 3B2/600, 3B2/700, and + 3B2/1000. + + Unimplemented Features + ====================== + + All features of the WE 32106 MAU have been implemented, but there + remain some features of the WE 32206 that are not yet implemented. + Neither System V UNIX nor the Version 3 MAU diagnostics appear to + use these features in any way, but there is no guarantee that other + software does not use them. They are: + + - The FE, UW, and WF bits of the Auxiliary Status Register + - The new operand registers f4 through f7 + - The register bank select feature of the Command Register + - The RC and RCS bits of the Command Register + - The new WE 32206 instructions: + 1. ATAN + 2. COS + 3. PI + 4. SIN + + --------------------------------------------------------------------- Portions of this code are derived from the SoftFloat 2c library by John R. Hauser. Functions derived from SoftFloat 2c are clearly @@ -81,7 +102,7 @@ --------------------------------------------------------------------- */ -#include "3b2_rev2_mau.h" +#include "3b2_mau.h" #include @@ -123,8 +144,6 @@ static void sub_128(t_uint64 a0, t_uint64 a1, static void mul_64_to_128(t_uint64 a, t_uint64 b, t_uint64 *r_low, t_uint64 *r_high); static void mul_64_by_shifted_32_to_128(t_uint64 a, uint32 b, t_mau_128 *result); static t_uint64 estimate_div_128_to_64(t_uint64 a0, t_uint64 a1, t_uint64 b); -static uint32 estimate_sqrt_32(int16 a_exp, uint32 a); - static uint32 round_pack_int(t_bool sign, t_uint64 frac, RM rounding_mode); static t_int64 round_pack_int64(t_bool sign, t_uint64 abs_0, t_uint64 abs_1, @@ -208,7 +227,13 @@ UNIT mau_unit = { UDATA(NULL, 0, 0) }; MAU_STATE mau_state; BITFIELD asr_bits[] = { +#if defined(REV3) + BIT(FE), + BITFFMT(VER,3,%d), + BIT(UW), +#else BITNCF(5), +#endif BIT(PR), BIT(QS), BIT(US), @@ -219,7 +244,11 @@ BITFIELD asr_bits[] = { BIT(UM), BIT(OM), BIT(IM), +#if defined(REV3) + BIT(WF), +#else BITNCF(1), +#endif BIT(UO), BIT(CSC), BIT(PS), @@ -279,7 +308,7 @@ DEVICE mau_dev = { #ifdef REV3 DEV_DEBUG, /* Rev 3 flags: Always required */ #else - DEV_DISABLE|DEV_DIS|DEV_DEBUG, /* Rev 2 flags */ + DEV_DISABLE|DEV_DEBUG, /* Rev 2 flags: Can be disabled */ #endif 0, /* debug control flags */ mau_debug, /* debug flag names */ @@ -314,10 +343,18 @@ XFP GEN_NONTRAPPING_NAN = { }; CONST char *mau_op_names[32] = { - "0x00", "0x01", "ADD", "SUB", "DIV", "REM", "MUL", "MOVE", /* 00-07 */ - "RDASR", "WRASR", "CMP", "CMPE", "ABS", "SQRT", "RTOI", "FTOI", /* 08-0F */ - "ITOF", "DTOF", "FTOD", "NOP", "EROF", "0x15", "0x16", "NEG", /* 10-17 */ - "LDR", "0x19", "CMPS", "CMPES", "0x1C", "0x1D", "0x1E", "0x1F" /* 18-1F */ + "0x00", "0x01", "ADD", "SUB", + "DIV", "REM", "MUL", "MOVE", + "RDASR", "WRASR", "CMP", "CMPE", + "ABS", "SQRT", "RTOI", "FTOI", + "ITOF", "DTOF", "FTOD", "NOP", + "EROF", "0x15", "0x16", "NEG", + "LDR", "0x19", "CMPS", "CMPES", +#if defined(REV3) + "SIN", "COS", "ATAN", "PI" +#else + "0x1C", "0x1D", "0x1E", "0x1F" +#endif }; CONST char *src_op_names[8] = { @@ -366,8 +403,8 @@ static SIM_INLINE void mau_case_div_zero(XFP *op1, XFP *op2, XFP *result) static SIM_INLINE void mau_exc(uint32 flag, uint32 mask) { sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [mau_exc] asr=%08x flag=%08x mask=%08x\n", - R[NUM_PC], mau_state.asr, flag, mask); + "[mau_exc] asr=%08x flag=%08x mask=%08x\n", + mau_state.asr, flag, mask); mau_state.asr |= flag; @@ -427,8 +464,8 @@ static SIM_INLINE void abort_on_fault() stop_reason = STOP_EX; } sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [abort_on_fault] Aborting on un-maskable overflow fault. ASR=%08x\n", - R[NUM_PC], mau_state.asr); + "[abort_on_fault] Aborting on un-maskable overflow fault. ASR=%08x\n", + mau_state.asr); cpu_abort(NORMAL_EXCEPTION, INTEGER_OVERFLOW); } @@ -438,8 +475,8 @@ static SIM_INLINE void abort_on_fault() stop_reason = STOP_EX; } sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [abort_on_fault] Aborting on ECP fault. ASR=%08x\n", - R[NUM_PC], mau_state.asr); + "[abort_on_fault] Aborting on ECP fault. ASR=%08x\n", + mau_state.asr); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); } @@ -475,7 +512,6 @@ static void clear_asr() */ static t_bool set_nz() { - switch(mau_state.opcode) { case M_NOP: case M_RDASR: @@ -490,6 +526,9 @@ static t_bool set_nz() t_stat mau_reset(DEVICE *dptr) { memset(&mau_state, 0, sizeof(MAU_STATE)); +#if defined(REV3) + mau_state.asr |= MAU_ASR_VER; /* Version 1 MAU */ +#endif return SCPE_OK; } @@ -846,44 +885,6 @@ static t_uint64 estimate_div_128_to_64(t_uint64 a0, t_uint64 a1, t_uint64 b) return z; } -/* - * Returns an approximation of the square root of the 32-bit - * value 'a'. - * - * Derived from the SoftFloat 2c package (see copyright notice above) - */ -static uint32 estimate_sqrt_32(int16 a_exp, uint32 a) -{ - static const uint16 sqrt_odd_adjust[] = { - 0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0, - 0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67 - }; - - static const uint16 sqrt_even_adjust[] = { - 0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E, - 0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002 - }; - - int8 index; - uint32 z; - - index = (a >> 27) & 0xf; - - if (a_exp & 1) { - z = 0x4000 + (a >> 17) - sqrt_odd_adjust[index]; - z = ((a / z) << 14) + (z << 15); - a >>= 1; - } - else { - z = 0x8000 + (a >> 17) - sqrt_even_adjust[index]; - z = a / z + z; - z = (0x20000 <= z) ? 0xFFFF8000 : ( z<<15 ); - if ( z <= a ) return (uint32) (((int32) a) >> 1); - } - - return ((uint32) ((((t_uint64) a )<<31 ) / z)) + (z >> 1); -} - static uint32 approx_recip_sqrt_32(uint32 oddExpA, uint32 a) { int index; @@ -1833,9 +1834,8 @@ void xfp_to_decimal(XFP *a, DEC *d, RM rounding_mode) d->h |= (uint32)digits[15] << 8; sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [xfp_to_decimal] " + "[xfp_to_decimal] " "Digits: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d 0x%x\n", - R[NUM_PC], digits[17], digits[16], digits[15], digits[14], digits[13], digits[12], digits[11], digits[10], digits[9], digits[8], digits[7], digits[6], digits[5], digits[4], digits[3], digits[2], digits[1], digits[0], @@ -1855,8 +1855,8 @@ void mau_decimal_to_xfp(DEC *d, XFP *a) t_int64 signed_tmp; sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [mau_decimal_to_xfp] DEC input: %08x %08x %08x\n", - R[NUM_PC], d->h, (uint32)(d->l >> 32), (uint32)(d->l)); + "[mau_decimal_to_xfp] DEC input: %08x %08x %08x\n", + d->h, (uint32)(d->l >> 32), (uint32)(d->l)); sign = (d->l) & 15; digits[0] = (d->l >> 4) & 15; @@ -1879,9 +1879,8 @@ void mau_decimal_to_xfp(DEC *d, XFP *a) digits[17] = (d->h >> 8) & 15; sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [mau_decimal_to_xfp] " + "[mau_decimal_to_xfp] " "Digits: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d 0x%x\n", - R[NUM_PC], digits[17], digits[16], digits[15], digits[14], digits[13], digits[12], digits[11], digits[10], digits[9], digits[8], digits[7], digits[6], digits[5], digits[4], digits[3], digits[2], digits[1], digits[0], @@ -1906,14 +1905,14 @@ void mau_decimal_to_xfp(DEC *d, XFP *a) } sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [mau_decimal_to_xfp] tmp val = %lld\n", - R[NUM_PC], signed_tmp); + "[mau_decimal_to_xfp] tmp val = %lld\n", + signed_tmp); mau_int64_to_xfp((t_uint64) signed_tmp, a); sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [mau_decimal_to_xfp] XFP = %04x%016llx\n", - R[NUM_PC], a->sign_exp, a->frac); + "[mau_decimal_to_xfp] XFP = %04x%016llx\n", + a->sign_exp, a->frac); } @@ -2050,8 +2049,7 @@ static void xfp_add_fracs(XFP *a, XFP *b, t_bool sign, XFP *result, RM rounding_ int32 exp_diff; sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [ADD_FRACS] a=%04x%016llx b=%04x%016llx\n", - R[NUM_PC], + "[ADD_FRACS] a=%04x%016llx b=%04x%016llx\n", a->sign_exp, a->frac, b->sign_exp, b->frac); @@ -2384,8 +2382,7 @@ static void xfp_mul(XFP *a, XFP *b, XFP *result, RM rounding_mode) t_uint64 a_frac, b_frac, r_frac_0, r_frac_1; sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [MUL] op1=%04x%016llx op2=%04x%016llx\n", - R[NUM_PC], + "[MUL] op1=%04x%016llx op2=%04x%016llx\n", a->sign_exp, a->frac, b->sign_exp, b->frac); @@ -2469,8 +2466,8 @@ static void xfp_div(XFP *a, XFP *b, XFP *result, RM rounding_mode) t_uint64 rem0, rem1, rem2, term0, term1, term2; sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [DIV] op1=%04x%016llx op2=%04x%016llx\n", - R[NUM_PC], b->sign_exp, b->frac, a->sign_exp, a->frac); + "[DIV] op1=%04x%016llx op2=%04x%016llx\n", + b->sign_exp, b->frac, a->sign_exp, a->frac); a_sign = XFP_SIGN(a); a_exp = XFP_EXP(a); @@ -2525,7 +2522,7 @@ static void xfp_div(XFP *a, XFP *b, XFP *result, RM rounding_mode) } /* Divide by zero - SPECIAL CASE 4 */ sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [DIV] Divide by zero detected.\n", R[NUM_PC]); + "[DIV] Divide by zero detected.\n"); mau_case_div_zero(a, b, result); return; } @@ -2583,8 +2580,8 @@ static void xfp_sqrt(XFP *a, XFP *result, RM rounding_mode) t_mau_128 nan_128, rem, y, term; sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [SQRT] op1=%04x%016llx\n", - R[NUM_PC], a->sign_exp, a->frac); + "[SQRT] op1=%04x%016llx\n", + a->sign_exp, a->frac); a_sign = XFP_SIGN(a); a_exp = XFP_EXP(a); @@ -2844,12 +2841,12 @@ static void load_src_op(uint8 op, XFP *xfp) xfp->frac = mau_state.f3.frac; break; case M_OP_MEM_SINGLE: - sfp = read_w(mau_state.src, ACC_AF); + sfp = read_w(mau_state.src, ACC_AF, BUS_PER); sfp_to_xfp(sfp, xfp); break; case M_OP_MEM_DOUBLE: - dfp = (t_uint64) read_w(mau_state.src + 4, ACC_AF); - dfp |= ((t_uint64) read_w(mau_state.src, ACC_AF)) << 32; + dfp = (t_uint64) read_w(mau_state.src + 4, ACC_AF, BUS_PER); + dfp |= ((t_uint64) read_w(mau_state.src, ACC_AF, BUS_PER)) << 32; sim_debug(TRACE_DBG, &mau_dev, "[load_src_op][DOUBLE] Loaded %016llx\n", dfp); @@ -2859,9 +2856,9 @@ static void load_src_op(uint8 op, XFP *xfp) xfp->sign_exp, xfp->frac); break; case M_OP_MEM_TRIPLE: - xfp->frac = (t_uint64) read_w(mau_state.src + 8, ACC_AF); - xfp->frac |= ((t_uint64) read_w(mau_state.src + 4, ACC_AF)) << 32; - xfp->sign_exp = (uint32) read_w(mau_state.src, ACC_AF); + xfp->frac = (t_uint64) read_w(mau_state.src + 8, ACC_AF, BUS_PER); + xfp->frac |= ((t_uint64) read_w(mau_state.src + 4, ACC_AF, BUS_PER)) << 32; + xfp->sign_exp = (uint32) read_w(mau_state.src, ACC_AF, BUS_PER); break; default: break; @@ -2877,9 +2874,9 @@ static void load_op1_decimal(DEC *d) switch (mau_state.op1) { case M_OP_MEM_TRIPLE: - low = read_w(mau_state.src + 8, ACC_AF); - mid = read_w(mau_state.src + 4, ACC_AF); - high = read_w(mau_state.src, ACC_AF); + low = read_w(mau_state.src + 8, ACC_AF, BUS_PER); + mid = read_w(mau_state.src + 4, ACC_AF, BUS_PER); + high = read_w(mau_state.src, ACC_AF, BUS_PER); d->l = low; d->l |= ((t_uint64) mid << 32); d->h = high; @@ -2911,7 +2908,7 @@ static void store_op3_int(uint32 val) mau_state.f3.frac = (t_uint64)val; break; case M_OP3_MEM_SINGLE: - write_w(mau_state.dst, val); + write_w(mau_state.dst, val, BUS_PER); break; default: /* Indeterminate output, unsupported */ @@ -2927,9 +2924,9 @@ static void store_op3_decimal(DEC *d) switch(mau_state.op3) { case M_OP3_MEM_TRIPLE: - write_w(mau_state.dst, d->h); - write_w(mau_state.dst + 4, (uint32)((t_uint64)d->l >> 32)); - write_w(mau_state.dst + 8, (uint32)d->l); + write_w(mau_state.dst, d->h, BUS_PER); + write_w(mau_state.dst + 4, (uint32)((t_uint64)d->l >> 32), BUS_PER); + write_w(mau_state.dst + 8, (uint32)d->l, BUS_PER); break; default: /* Unsupported */ @@ -2998,8 +2995,7 @@ static void store_op3(XFP *xfp) t_bool store_dr = FALSE; sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [store_op3] op3=%04x%016llx\n", - R[NUM_PC], + "[store_op3] op3=%04x%016llx\n", xfp->sign_exp, xfp->frac); @@ -3049,7 +3045,7 @@ static void store_op3(XFP *xfp) mau_state.asr |= MAU_ASR_Z; } } - write_w(mau_state.dst, (uint32)sfp); + write_w(mau_state.dst, (uint32)sfp, BUS_PER); break; case M_OP3_MEM_DOUBLE: if (mau_state.ntnan) { @@ -3074,18 +3070,18 @@ static void store_op3(XFP *xfp) mau_state.asr |= MAU_ASR_Z; } } - write_w(mau_state.dst, (uint32)(dfp >> 32)); - write_w(mau_state.dst + 4, (uint32)(dfp)); + write_w(mau_state.dst, (uint32)(dfp >> 32), BUS_PER); + write_w(mau_state.dst + 4, (uint32)(dfp), BUS_PER); break; case M_OP3_MEM_TRIPLE: if (mau_state.ntnan) { - write_w(mau_state.dst, (uint32)(GEN_NONTRAPPING_NAN.sign_exp)); - write_w(mau_state.dst + 4, (uint32)(GEN_NONTRAPPING_NAN.frac >> 32)); - write_w(mau_state.dst + 8, (uint32)(GEN_NONTRAPPING_NAN.frac)); + write_w(mau_state.dst, (uint32)(GEN_NONTRAPPING_NAN.sign_exp), BUS_PER); + write_w(mau_state.dst + 4, (uint32)(GEN_NONTRAPPING_NAN.frac >> 32), BUS_PER); + write_w(mau_state.dst + 8, (uint32)(GEN_NONTRAPPING_NAN.frac), BUS_PER); } else { - write_w(mau_state.dst, (uint32)(xfp->sign_exp)); - write_w(mau_state.dst + 4, (uint32)(xfp->frac >> 32)); - write_w(mau_state.dst + 8, (uint32)(xfp->frac)); + write_w(mau_state.dst, (uint32)(xfp->sign_exp), BUS_PER); + write_w(mau_state.dst + 4, (uint32)(xfp->frac >> 32), BUS_PER); + write_w(mau_state.dst + 8, (uint32)(xfp->frac), BUS_PER); } if (set_nz()) { if (XFP_SIGN(xfp)) { @@ -3114,22 +3110,22 @@ static void mau_rdasr() switch (mau_state.op3) { /* Handled */ case M_OP3_MEM_SINGLE: - write_w(mau_state.dst, mau_state.asr); + write_w(mau_state.dst, mau_state.asr, BUS_PER); break; case M_OP3_MEM_DOUBLE: - write_w(mau_state.dst, mau_state.asr); - write_w(mau_state.dst + 4, mau_state.asr); + write_w(mau_state.dst, mau_state.asr, BUS_PER); + write_w(mau_state.dst + 4, mau_state.asr, BUS_PER); break; case M_OP3_MEM_TRIPLE: - write_w(mau_state.dst, mau_state.asr); - write_w(mau_state.dst + 4, mau_state.asr); - write_w(mau_state.dst + 8, mau_state.asr); + write_w(mau_state.dst, mau_state.asr, BUS_PER); + write_w(mau_state.dst + 4, mau_state.asr, BUS_PER); + write_w(mau_state.dst + 8, mau_state.asr, BUS_PER); break; /* Unhandled */ default: sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [mau_rdasr] WARNING: Unhandled source: %02x\n", - R[NUM_PC], mau_state.op3); + "[mau_rdasr] WARNING: Unhandled source: %02x\n", + mau_state.op3); break; } } @@ -3139,15 +3135,14 @@ static void mau_wrasr() switch (mau_state.op1) { /* Handled */ case M_OP_MEM_SINGLE: - mau_state.asr = read_w(mau_state.src, ACC_AF); + mau_state.asr = read_w(mau_state.src, ACC_AF, BUS_PER); sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [WRASR] Writing ASR with: %08x\n", - R[NUM_PC], mau_state.asr); + "[WRASR] Writing ASR with: %08x\n", + mau_state.asr); break; default: sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [mau_wrasr] WARNING: Unhandled source: %02x\n", - R[NUM_PC], + "[mau_wrasr] WARNING: Unhandled source: %02x\n", mau_state.op3); break; } @@ -3206,8 +3201,8 @@ static void mau_ldr() load_src_op(mau_state.op1, &xfp); sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [LDR] Loading DR with %04x%016llx\n", - R[NUM_PC], xfp.sign_exp, xfp.frac); + "[LDR] Loading DR with %04x%016llx\n", + xfp.sign_exp, xfp.frac); mau_state.dr.sign_exp = xfp.sign_exp; mau_state.dr.frac = xfp.frac; } @@ -3244,17 +3239,17 @@ static void mau_erof() return; case M_OP3_MEM_SINGLE: sfp = xfp_to_sfp(&(mau_state.dr), MAU_RM); - write_w(mau_state.dst, (uint32)sfp); + write_w(mau_state.dst, (uint32)sfp, BUS_PER); return; case M_OP3_MEM_DOUBLE: dfp = xfp_to_dfp(&(mau_state.dr), MAU_RM); - write_w(mau_state.dst + 4, (uint32)(dfp >> 32)); - write_w(mau_state.dst, (uint32)(dfp)); + write_w(mau_state.dst + 4, (uint32)(dfp >> 32), BUS_PER); + write_w(mau_state.dst, (uint32)(dfp), BUS_PER); return; case M_OP3_MEM_TRIPLE: - write_w(mau_state.dst, (uint32)(mau_state.dr.sign_exp)); - write_w(mau_state.dst + 4, (uint32)(mau_state.dr.frac >> 32)); - write_w(mau_state.dst + 8, (uint32)(mau_state.dr.frac)); + write_w(mau_state.dst, (uint32)(mau_state.dr.sign_exp), BUS_PER); + write_w(mau_state.dst + 4, (uint32)(mau_state.dr.frac >> 32), BUS_PER); + write_w(mau_state.dst + 8, (uint32)(mau_state.dr.frac), BUS_PER); return; default: sim_debug(TRACE_DBG, &mau_dev, @@ -3349,8 +3344,7 @@ static void mau_div() load_src_op(mau_state.op1, &a); load_src_op(mau_state.op2, &b); sim_debug(TRACE_DBG, &mau_dev, - "[%08x] [DIV OP2/OP1] OP2=0x%04x%016llx OP1=0x%04x%016llx\n", - R[NUM_PC], + "[DIV OP2/OP1] OP2=0x%04x%016llx OP1=0x%04x%016llx\n", b.sign_exp, b.frac, a.sign_exp, a.frac); xfp_div(&b, &a, &result, MAU_RM); @@ -3414,13 +3408,13 @@ static void mau_itof() mau_exc(MAU_ASR_IS, MAU_ASR_IM); return; case M_OP_MEM_SINGLE: - val = read_w(mau_state.src, ACC_AF); + val = read_w(mau_state.src, ACC_AF, BUS_PER); break; case M_OP_MEM_DOUBLE: - val = read_w(mau_state.src + 4, ACC_AF); + val = read_w(mau_state.src + 4, ACC_AF, BUS_PER); break; case M_OP_MEM_TRIPLE: - val = read_w(mau_state.src + 8, ACC_AF); + val = read_w(mau_state.src + 8, ACC_AF, BUS_PER); break; default: break; @@ -3593,5 +3587,9 @@ t_stat mau_broadcast(uint32 cmd, uint32 src, uint32 dst) CONST char *mau_description(DEVICE *dptr) { - return "WE32106"; +#if defined(REV3) + return "WE 32106 MAU"; +#else + return "WE 32206 MAU"; +#endif } diff --git a/3B2/3b2_mau.h b/3B2/3b2_mau.h index fc5589a8..811c1748 100644 --- a/3B2/3b2_mau.h +++ b/3B2/3b2_mau.h @@ -1,6 +1,6 @@ -/* 3b2_mau.h: Common CSR header +/* 3b2_mau.h: WE32106 and WE32206 Math Accelerator Unit - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -26,12 +26,364 @@ not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + + --------------------------------------------------------------------- + + This file is part of a simulation of the WE 32106 and WE 32206 Math + Acceleration Units. The WE 32106 and WE 32206 are IEEE-754 + compabitle floating point hardware math accelerators that were + available as an optional component on the AT&T 3B2/310 and 3B2/400, + and as a standard component on the 3B2/500, 3B2/600, 3B2/700, and + 3B2/1000. + + Portions of this code are derived from the SoftFloat 2c library by + John R. Hauser. Functions derived from SoftFloat 2c are clearly + marked in the comments. + + Legal Notice + ============ + + SoftFloat was written by John R. Hauser. Release 2c of SoftFloat + was made possible in part by the International Computer Science + Institute, located at Suite 600, 1947 Center Street, Berkeley, + California 94704. Funding was partially provided by the National + Science Foundation under grant MIP-9311980. The original version + of this code was written as part of a project to build a + fixed-point vector processor in collaboration with the University + of California at Berkeley, overseen by Profs. Nelson Morgan and + John Wawrzynek. + + THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable + effort has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS + THAT WILL AT TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS + SOFTWARE IS RESTRICTED TO PERSONS AND ORGANIZATIONS WHO CAN AND + WILL TOLERATE ALL LOSSES, COSTS, OR OTHER PROBLEMS THEY INCUR DUE + TO THE SOFTWARE WITHOUT RECOMPENSE FROM JOHN HAUSER OR THE + INTERNATIONAL COMPUTER SCIENCE INSTITUTE, AND WHO FURTHERMORE + EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER + SCIENCE INSTITUTE (possibly via similar legal notice) AGAINST ALL + LOSSES, COSTS, OR OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND + CLIENTS DUE TO THE SOFTWARE, OR INCURRED BY ANYONE DUE TO A + DERIVATIVE WORK THEY CREATE USING ANY PART OF THE SOFTWARE. + + The following are expressly permitted, even for commercial + purposes: + + (1) distribution of SoftFloat in whole or in part, as long as this + and other legal notices remain and are prominent, and provided also + that, for a partial distribution, prominent notice is given that it + is a subset of the original; and + + (2) inclusion or use of SoftFloat in whole or in part in a + derivative work, provided that the use restrictions above are met + and the minimal documentation requirements stated in the source + code are satisfied. + --------------------------------------------------------------------- + + Data Types + ========== + + The WE32106 MAU stores values using IEEE-754 1985 types, plus a + non-standard Decimal type. + + - Decimal Type - 18 BCD digits long. Each digit is 4 bits wide. + Sign is encoded in byte 0. + + 3322 2222 2222 1111 1111 1100 0000 0000 + 1098 7654 3210 9876 5432 1098 7654 3210 + +-------------------+----+----+----+----+ + | unused | D18| D17| D16| D15| High Word + +----+----+----+----+----+----+----+----+ + | D14| D13| D12| D11| D10| D09| D08| D07| Middle Word + +----+----+----+----+----+----+----+----+ + | D06| D05| D04| D03| D02| D01| D00|sign| Low Word + +----+----+----+----+----+----+----+----+ + + Sign: 0: Positive Infinity 10: Positive Number + 1: Negative Infinity 11: Negative Number + 2: Positive NaN 12: Positive Number + 3: Negative NaN 13: Negative Number + 4-9: Trapping NaN 14-15: Positive Number + + - Extended Precision (80-bit) - exponent biased by 16383 + + 3 322222222221111 1 111110000000000 + 1 098765432109876 5 432109876543210 + +-----------------+-+---------------+ + | unused |S| Exponent | High Word + +-+---------------+-+---------------+ + |J| Fraction (high word) | Middle Word + +-+---------------------------------+ + | Fraction (low word) | Low Word + +-----------------------------------+ + + + - Double Precision (64-bit) - exponent biased by 1023 + + 3 3222222222 211111111110000000000 + 1 0987654321 098765432109876543210 + +-+----------+---------------------+ + |S| Exponent | Fraction (high) | High Word + +-+----------+---------------------+ + | Fraction (low) | Low Word + +----------------------------------+ + + + - Single Precision (32-bit) - exponent biased by 127 + + 3 32222222 22211111111110000000000 + 1 09876543 21098765432109876543210 + +-+--------+-----------------------+ + |S| Exp | Fraction | + +-+--------+-----------------------+ + */ -#ifndef _3B2_MAU_H_ -#define _3B2_MAU_H_ +#ifndef _3B2_REV2_MAU_H_ +#define _3B2_REV2_MAU_H_ -/* TODO: Update when rev3 mau is implemented */ -#include "3b2_rev2_mau.h" +#include "sim_defs.h" -#endif /* _3B2_MAU_H */ +#define SRC_LEN_INVALID 0 +#define SRC_LEN_SINGLE 1 +#define SRC_LEN_DOUBLE 2 +#define SRC_LEN_TRIPLE 3 + +#define MAU_ASR_RC_SHIFT 22 + +#define MAU_ASR_PR 0x20u /* Partial Remainder */ +#define MAU_ASR_QS 0x40u /* Divide By Zero Sticky */ +#define MAU_ASR_US 0x80u /* Underflow Sticky */ +#define MAU_ASR_OS 0x100u /* Overflow Sticky */ +#define MAU_ASR_IS 0x200u /* Invalid Operation Sticky */ +#define MAU_ASR_PM 0x400u /* Inexact Mask */ +#define MAU_ASR_QM 0x800u /* Divide by Zero Mask */ +#define MAU_ASR_UM 0x1000u /* Underflow Mask */ +#define MAU_ASR_OM 0x2000u /* Overflow Mask */ +#define MAU_ASR_IM 0x4000u /* Invalid Operation Mask */ + +#define MAU_ASR_UO 0x10000u /* Unordered */ +#define MAU_ASR_CSC 0x20000u /* Context Switch Control */ +#define MAU_ASR_PS 0x40000u /* Inexact Sticky */ +#define MAU_ASR_IO 0x80000u /* Integer Overflow */ +#define MAU_ASR_Z 0x100000u /* Zero Flag */ +#define MAU_ASR_N 0x200000u /* Negative Flag */ +#define MAU_ASR_RC 0x400000u /* Round Control */ + +#define MAU_ASR_NTNC 0x1000000u /* Nontrapping NaN Control */ +#define MAU_ASR_ECP 0x2000000u /* Exception Condition */ + +#define MAU_ASR_RA 0x80000000u /* Result Available */ + +#if defined(REV3) +#define MAU_ASR_FE 0x1u /* Feature Enable */ +#define MAU_ASR_VER 0x2u /* Version */ +#define MAU_ASR_UW 0x10u /* Unaligned Word */ +#define MAU_ASR_WF 0x8000u /* Write Fault Indicator */ +#define MAU_RC_RN 0 /* Round toward Nearest */ +#define MAU_RC_RP 1 /* Round toward Plus Infin. */ +#define MAU_RC_RM 2 /* Round toward Neg. Infin. */ +#define MAU_RC_RZ 3 /* Round toward Zero */ +#endif + +#define SFP_SIGN(V) (((V) >> 31) & 1) +#define SFP_EXP(V) (((V) >> 23) & 0xff) +#define SFP_FRAC(V) ((V) & 0x7fffff) + +#define DFP_SIGN(V) (((V) >> 63) & 1) +#define DFP_EXP(V) (((V) >> 52) & 0x7ff) +#define DFP_FRAC(V) ((V) & 0xfffffffffffffull) + +#define XFP_SIGN(V) (((V)->sign_exp >> 15) & 1) +#define XFP_EXP(V) ((V)->sign_exp & 0x7fff) +#define XFP_FRAC(V) ((V)->frac) + +#define XFP_IS_NORMAL(V) ((V)->frac & 0x8000000000000000ull) + +#define DEFAULT_XFP_NAN_SIGN_EXP 0xffff +#define DEFAULT_XFP_NAN_FRAC 0xc000000000000000ull + +#define SFP_IS_TRAPPING_NAN(V) (((((V) >> 22) & 0x1ff) == 0x1fe) && \ + ((V) & 0x3fffff)) +#define DFP_IS_TRAPPING_NAN(V) (((((V) >> 51) & 0xfff) == 0xffe) && \ + ((V) & 0x7ffffffffffffull)) +#define XFP_IS_NAN(V) ((((V)->sign_exp & 0x7fff) == 0x7fff) && \ + (t_uint64)((V)->frac << 1)) +#define XFP_IS_TRAPPING_NAN(V) ((((V)->sign_exp) & 0x7fff) && \ + ((((V)->frac) & ~(0x4000000000000000ull)) << 1) && \ + (((V)->frac) == ((V)->frac & ~(0x4000000000000000ull)))) +#define PACK_DFP(SIGN,EXP,FRAC) ((((t_uint64)(SIGN))<<63) + \ + (((t_uint64)(EXP))<<52) + \ + ((t_uint64)(FRAC))) +#define PACK_SFP(SIGN,EXP,FRAC) (((uint32)(SIGN)<<31) + \ + ((uint32)(EXP)<<23) + \ + ((uint32)(FRAC))) +#define PACK_XFP(SIGN,EXP,FRAC,V) do { \ + (V)->frac = (FRAC); \ + (V)->sign_exp = ((uint16)(SIGN) << 15) + (EXP); \ + (V)->s = FALSE; \ + } while (0) + +#define PACK_XFP_S(SIGN,EXP,FRAC,S,V) do { \ + (V)->frac = (FRAC); \ + (V)->sign_exp = ((uint16)(SIGN) << 15) + (EXP); \ + (V)->s = (S) != 0; \ + } while (0) + +#define MAU_RM ((RM)((mau_state.asr >> 22) & 3)) + +typedef enum { + M_ADD = 0x02, + M_SUB = 0x03, + M_DIV = 0x04, + M_REM = 0x05, + M_MUL = 0x06, + M_MOVE = 0x07, + M_RDASR = 0x08, + M_WRASR = 0x09, + M_CMP = 0x0a, + M_CMPE = 0x0b, + M_ABS = 0x0c, + M_SQRT = 0x0d, + M_RTOI = 0x0e, + M_FTOI = 0x0f, + M_ITOF = 0x10, + M_DTOF = 0x11, + M_FTOD = 0x12, + M_NOP = 0x13, + M_EROF = 0x14, + M_NEG = 0x17, + M_LDR = 0x18, + M_CMPS = 0x1a, + M_CMPES = 0x1b +} mau_opcodes; + +typedef enum { + M_OP3_F0_SINGLE, + M_OP3_F1_SINGLE, + M_OP3_F2_SINGLE, + M_OP3_F3_SINGLE, + M_OP3_F0_DOUBLE, + M_OP3_F1_DOUBLE, + M_OP3_F2_DOUBLE, + M_OP3_F3_DOUBLE, + M_OP3_F0_TRIPLE, + M_OP3_F1_TRIPLE, + M_OP3_F2_TRIPLE, + M_OP3_F3_TRIPLE, + M_OP3_MEM_SINGLE, + M_OP3_MEM_DOUBLE, + M_OP3_MEM_TRIPLE, + M_OP3_NONE +} op3_spec; + +/* Specifier bytes for Operands 1 and 2 */ +typedef enum { + M_OP_F0, + M_OP_F1, + M_OP_F2, + M_OP_F3, + M_OP_MEM_SINGLE, + M_OP_MEM_DOUBLE, + M_OP_MEM_TRIPLE, + M_OP_NONE +} op_spec; + +/* + * 128-bit value + */ +typedef struct { + t_uint64 low; + t_uint64 high; +} t_mau_128; + +/* + * Not-a-Number Type + */ +typedef struct { + t_bool sign; + t_uint64 high; + t_uint64 low; +} T_NAN; + +/* + * Extended Precision (80 bits). + * + * Note that an undocumented feature of the WE32106 requires the use + * of uint32 rather than uint16 for the sign and exponent components + * of the struct. Although bits 80-95 are "unused", several + * diagnostics actually expect these bits to be moved and preserved on + * word transfers. They are ignored and discarded by math routines, + * however. + * + * The 's' field holds the Sticky bit used by rounding. + */ +typedef struct { + uint32 sign_exp; /* Sign and Exponent */ + t_uint64 frac; /* Fraction/Significand/Mantissa */ + t_bool s; /* Sticky bit */ +} XFP; + +typedef struct { + uint32 h; + t_uint64 l; +} DEC; + +/* + * Supported rounding modes. + */ +typedef enum { + ROUND_NEAREST, + ROUND_PLUS_INF, + ROUND_MINUS_INF, + ROUND_ZERO +} RM; + +/* + * Double Precision (64 bits) + */ +typedef t_uint64 DFP; + +/* + * Single Precision (32 bits) + */ +typedef uint32 SFP; + +/* + * MAU state + */ + +typedef struct { + uint32 cmd; + /* Exception */ + uint32 exception; + /* Status register */ + uint32 asr; + t_bool trapping_nan; + /* Generate a Non-Trapping NaN */ + t_bool ntnan; + /* Source (from broadcast) */ + uint32 src; + /* Destination (from broadcast) */ + uint32 dst; + uint8 opcode; + uint8 op1; + uint8 op2; + uint8 op3; + /* Data Register */ + XFP dr; + /* Operand Registers */ + XFP f0; + XFP f1; + XFP f2; + XFP f3; +} MAU_STATE; + +t_stat mau_reset(DEVICE *dptr); +t_stat mau_attach(UNIT *uptr, CONST char *cptr); +t_stat mau_detach(UNIT *uptr); +t_stat mau_broadcast(uint32 cmd, uint32 src, uint32 dst); +CONST char *mau_description(DEVICE *dptr); +t_stat mau_broadcast(uint32 cmd, uint32 src, uint32 dst); + +#endif /* _3B2_REV2_MAU_H_ */ diff --git a/3B2/3b2_mem.c b/3B2/3b2_mem.c index 90db6946..1a776d45 100644 --- a/3B2/3b2_mem.c +++ b/3B2/3b2_mem.c @@ -1,6 +1,6 @@ -/* 3b2_mem.c: AT&T 3B2 memory access routines +/* 3b2_mem.c: Memory Map Access Routines - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -34,82 +34,111 @@ #include "3b2_csr.h" #include "3b2_io.h" #include "3b2_mmu.h" +#include "3b2_stddev.h" +#include "3b2_dmac.h" -t_bool addr_is_rom(uint32 pa) -{ - return (pa < rom_size); -} +#if defined(REV3) +static uint32 ecc_addr; /* ECC address */ +static t_bool ecc_err; /* ECC multi-bit error */ +#endif -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) +/* + * ECC is simulated just enough to pass diagnostics, and no more. + * + * Checking and setting of ECC syndrome bits is a no-op for Rev 2. + */ +static SIM_INLINE void check_ecc(uint32 pa, t_bool write, uint8 src) { #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)); + /* Force ECC Syndrome mode enables a diagnostic mode on the AM2960 + data correction ICs */ + if (write && !CSR(CSRFECC)) { + sim_debug(EXECUTE_MSG, &mmu_dev, + "ECC Error on Write. pa=%08x\n", + pa); + ecc_addr = pa; + ecc_err = TRUE; + } else if (ecc_err && !write && pa == ecc_addr) { + sim_debug(EXECUTE_MSG, &mmu_dev, + "ECC Error detected on Read. pa=%08x psw=%08x cur_ipl=%d csr=%08x\n", + pa, R[NUM_PSW], PSW_CUR_IPL, csr_data); + flt[0] = ecc_addr & 0x3ffff; + flt[1] = MA_CPU_IO|MA_CPU_BU; + ecc_err = FALSE; + CSRBIT(CSRFRF, TRUE); /* Fault registers frozen */ + CSRBIT(CSRMBERR, TRUE); /* Multi-bit error */ + CPU_SET_INT(INT_MBERR); + /* Only abort if CPU is doing the read */ + if (src == BUS_CPU) { + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + } + } #endif } /* Read Word (Physical Address) */ -uint32 pread_w(uint32 pa) +uint32 pread_w(uint32 pa, uint8 src) { - uint32 *m; - uint32 index; + uint8 *m; + uint32 index = 0; +#if defined(REV3) + if ((pa & 3) && (R[NUM_PSW] & PSW_EA_MASK) == 0) { +#else if (pa & 3) { +#endif sim_debug(READ_MSG, &mmu_dev, - "[%08x] Cannot read physical address. ALIGNMENT ISSUE: %08x\n", - R[NUM_PC], pa); + "Cannot read physical address. ALIGNMENT ISSUE: %08x\n", + pa); CSRBIT(CSRALGN, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); } - if (addr_is_io(pa)) { + if (IS_IO(pa)) { return io_read(pa, 32); } - if (addr_is_rom(pa)) { + if (IS_ROM(pa)) { m = ROM; - index = pa >> 2; - } else if (addr_is_mem(pa)) { + index = pa; + } else if (IS_RAM(pa)) { + check_ecc(pa, FALSE, src); m = RAM; - index = (pa - PHYS_MEM_BASE) >> 2; + index = (pa - PHYS_MEM_BASE); } else { return 0; } - return m[index]; + return ATOW(m, index); } /* * Write Word (Physical Address) */ -void pwrite_w(uint32 pa, uint32 val) +void pwrite_w(uint32 pa, uint32 val, uint8 src) { + uint32 index; + if (pa & 3) { sim_debug(WRITE_MSG, &mmu_dev, - "[%08x] Cannot write physical address. ALIGNMENT ISSUE: %08x\n", - R[NUM_PC], pa); + "Cannot write physical address. ALIGNMENT ISSUE: %08x\n", + pa); CSRBIT(CSRALGN, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); } - if (addr_is_io(pa)) { + if (IS_IO(pa)) { io_write(pa, val, 32); return; } - if (addr_is_mem(pa)) { - RAM[(pa - PHYS_MEM_BASE) >> 2] = val; + if (IS_RAM(pa)) { + check_ecc(pa, TRUE, src); + index = pa - PHYS_MEM_BASE; + RAM[index] = (val >> 24) & 0xff; + RAM[index + 1] = (val >> 16) & 0xff; + RAM[index + 2] = (val >> 8) & 0xff; + RAM[index + 3] = val & 0xff; return; } } @@ -117,165 +146,158 @@ void pwrite_w(uint32 pa, uint32 val) /* * Read Halfword (Physical Address) */ -uint16 pread_h(uint32 pa) +uint16 pread_h(uint32 pa, uint8 src) { - uint32 *m; + uint8 *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); + "Cannot read physical address. ALIGNMENT ISSUE %08x\n", + pa); CSRBIT(CSRALGN, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); } - if (addr_is_io(pa)) { + if (IS_IO(pa)) { return (uint16) io_read(pa, 16); } - if (addr_is_rom(pa)) { + if (IS_ROM(pa)) { m = ROM; - index = pa >> 2; - } else if (addr_is_mem(pa)) { + index = pa; + } else if (IS_RAM(pa)) { + check_ecc(pa, FALSE, src); m = RAM; - index = (pa - PHYS_MEM_BASE) >> 2; + index = pa - PHYS_MEM_BASE; } else { return 0; } - if (pa & 2) { - return m[index] & HALF_MASK; - } else { - return (m[index] >> 16) & HALF_MASK; - } + return ATOH(m, index); } /* * Write Halfword (Physical Address) */ -void pwrite_h(uint32 pa, uint16 val) +void pwrite_h(uint32 pa, uint16 val, uint8 src) { - uint32 *m; uint32 index; - uint32 wval = (uint32)val; +#if defined(REV3) + if ((pa & 1) && (R[NUM_PSW] & PSW_EA_MASK) == 0) { +#else if (pa & 1) { +#endif sim_debug(WRITE_MSG, &mmu_dev, - "[%08x] Cannot write physical address %08x, ALIGNMENT ISSUE\n", - R[NUM_PC], pa); + "Cannot write physical address %08x, ALIGNMENT ISSUE\n", + pa); CSRBIT(CSRALGN, TRUE); - cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); } - if (addr_is_io(pa)) { + if (IS_IO(pa)) { io_write(pa, val, 16); return; } - if (addr_is_mem(pa)) { - m = RAM; - index = (pa - PHYS_MEM_BASE) >> 2; - } else { + if (IS_RAM(pa)) { + check_ecc(pa, TRUE, src); + index = pa - PHYS_MEM_BASE; + RAM[index] = (val >> 8) & 0xff; + RAM[index + 1] = val & 0xff; 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) +uint8 pread_b(uint32 pa, uint8 src) { - uint32 data; - int32 sc = (~(pa & 3) << 3) & 0x1f; - - if (addr_is_io(pa)) { + if (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]; + if (IS_ROM(pa)) { + return ROM[pa]; + } else if (IS_RAM(pa)) { + check_ecc(pa, FALSE, src); + return RAM[pa - PHYS_MEM_BASE]; } else { return 0; } - - return (data >> sc) & BYTE_MASK; } /* Write Byte (Physical Address) */ -void pwrite_b(uint32 pa, uint8 val) +void pwrite_b(uint32 pa, uint8 val, uint8 src) { - uint32 *m; - int32 index; - int32 sc = (~(pa & 3) << 3) & 0x1f; - uint32 mask = 0xffu << sc; + uint32 index; - if (addr_is_io(pa)) { + if (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); + if (IS_RAM(pa)) { + check_ecc(pa, TRUE, src); + index = pa - PHYS_MEM_BASE; + RAM[index] = val; return; } } +/* Write to ROM (used by ROM load) */ +void pwrite_b_rom(uint32 pa, uint8 val) { + if (IS_ROM(pa)) { + ROM[pa] = val; + } + } + /* Read Byte (Virtual Address) */ -uint8 read_b(uint32 va, uint8 r_acc) +uint8 read_b(uint32 va, uint8 r_acc, uint8 src) { - return pread_b(mmu_xlate_addr(va, r_acc)); + return pread_b(mmu_xlate_addr(va, r_acc), src); } /* Write Byte (Virtual Address) */ -void write_b(uint32 va, uint8 val) +void write_b(uint32 va, uint8 val, uint8 src) { - pwrite_b(mmu_xlate_addr(va, ACC_W), val); + pwrite_b(mmu_xlate_addr(va, ACC_W), val, src); } /* Read Halfword (Virtual Address) */ -uint16 read_h(uint32 va, uint8 r_acc) +uint16 read_h(uint32 va, uint8 r_acc, uint8 src) { - return pread_h(mmu_xlate_addr(va, r_acc)); + return pread_h(mmu_xlate_addr(va, r_acc), src); } /* Write Halfword (Virtual Address) */ -void write_h(uint32 va, uint16 val) +void write_h(uint32 va, uint16 val, uint8 src) { - pwrite_h(mmu_xlate_addr(va, ACC_W), val); + pwrite_h(mmu_xlate_addr(va, ACC_W), val, src); } /* Read Word (Virtual Address) */ -uint32 read_w(uint32 va, uint8 r_acc) +uint32 read_w(uint32 va, uint8 r_acc, uint8 src) { - return pread_w(mmu_xlate_addr(va, r_acc)); + return pread_w(mmu_xlate_addr(va, r_acc), src); } /* Write Word (Virtual Address) */ -void write_w(uint32 va, uint32 val) +void write_w(uint32 va, uint32 val, uint8 src) { - pwrite_w(mmu_xlate_addr(va, ACC_W), val); + pwrite_w(mmu_xlate_addr(va, ACC_W), val, src); } t_stat read_operand(uint32 va, uint8 *val) { uint32 pa; t_stat succ; - + succ = mmu_decode_va(va, ACC_IF, TRUE, &pa); if (succ == SCPE_OK) { - *val = pread_b(pa); + *val = pread_b(pa, BUS_CPU); } else { *val = 0; } @@ -291,8 +313,8 @@ t_stat examine(uint32 va, uint8 *val) 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); + if (IS_ROM(pa) || IS_RAM(pa)) { + *val = pread_b(pa, BUS_CPU); return SCPE_OK; } else { *val = 0; @@ -312,8 +334,8 @@ t_stat deposit(uint32 va, uint8 val) succ = mmu_decode_va(va, 0, FALSE, &pa); if (succ == SCPE_OK) { - if (addr_is_mem(pa)) { - pwrite_b(pa, val); + if (IS_RAM(pa)) { + pwrite_b(pa, val, BUS_CPU); return SCPE_OK; } else { return SCPE_NXM; diff --git a/3B2/3b2_mem.h b/3B2/3b2_mem.h index b477bfb8..0f0fab3c 100644 --- a/3B2/3b2_mem.h +++ b/3B2/3b2_mem.h @@ -1,6 +1,6 @@ -/* 3b2_mem.h: AT&T 3B2 3B2 memory access routines +/* 3b2_mem.h: Memory Map Access Routines - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -33,30 +33,47 @@ #include "3b2_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); +#define IS_ROM(PA) ((PA) < ROM_SIZE) +#define IS_RAM(PA) (((PA) >= PHYS_MEM_BASE) && ((PA) < (PHYS_MEM_BASE + MEM_SIZE))) +#if defined(REV3) +#define IS_IO(PA) (((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 +#define IS_IO(PA) (((PA >= IO_BOTTOM) && (PA < IO_TOP)) || \ + ((PA >= CIO_BOTTOM) && (PA < CIO_TOP))) +#endif -uint8 read_b(uint32 va, uint8 r_acc); -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); +#define MA_BUB3 0x100 /* BUBUS slot 3 master on fault */ +#define MA_BUB2 0x200 /* BUBUS slot 2 master on fault */ +#define MA_BUB1 0x400 /* BUBUS slot 1 master on fault */ +#define MA_CPU_BU 0x2000 /* CPU access BUBUS peripheral */ +#define MA_BUB0 0x4000 /* BUBUS slot 0 master on fault */ +#define MA_CPU_IO 0x8000 /* CPU accessing I/O peripheral */ +#define MA_IO_NLY 0x10000 /* IO Bus Master on fault */ +#define MA_IO_BM 0x80000 /* IO Bus Master or BUBUS was master on fault */ + +#define BUS_PER 0 /* Read or Write is from peripheral */ +#define BUS_CPU 1 /* Read or Write is from CPU */ + +uint32 pread_w(uint32 pa, uint8 src); +void pwrite_w(uint32 pa, uint32 val, uint8 src); +uint8 pread_b(uint32 pa, uint8 src); +void pwrite_b(uint32 pa, uint8 val, uint8 src); +void pwrite_b_rom(uint32 pa, uint8 val); +uint16 pread_h(uint32 pa, uint8 src); +void pwrite_h(uint32 pa, uint16 val, uint8 src); + +uint8 read_b(uint32 va, uint8 r_acc, uint8 src); +uint16 read_h(uint32 va, uint8 r_acc, uint8 src); +uint32 read_w(uint32 va, uint8 r_acc, uint8 src); +void write_b(uint32 va, uint8 val, uint8 src); +void write_h(uint32 va, uint16 val, uint8 src); +void write_w(uint32 va, uint32 val, uint8 src); t_stat read_operand(uint32 va, uint8 *val); t_stat 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_mmu.h b/3B2/3b2_mmu.h index 54192991..4283b230 100644 --- a/3B2/3b2_mmu.h +++ b/3B2/3b2_mmu.h @@ -1,6 +1,6 @@ /* 3b2_mmu.h: Common MMU header - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/3B2/3b2_ni.c b/3B2/3b2_ni.c index c17dccd6..36a74cfa 100644 --- a/3B2/3b2_ni.c +++ b/3B2/3b2_ni.c @@ -1,6 +1,6 @@ -/* 3b2_ni.c: AT&T 3B2 Model 400 "NI" feature card +/* 3b2_ni.c: CM195A Network Interface CIO Card - Copyright (c) 2018, Seth J. Morabito + Copyright (c) 2018-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -97,26 +97,23 @@ #include "3b2_io.h" #include "3b2_mem.h" -#include "3b2_timer.h" +#include "3b2_stddev.h" /* State container for the card */ NI_STATE ni; +t_bool ni_conf = FALSE; + /* Static Function Declarations */ static void dump_packet(const char *direction, ETH_PACK *pkt); static void ni_enable(); static void ni_disable(); -static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp); -static t_stat ni_show_queue_common(FILE *st, UNIT *uptr, int32 val, - CONST void *desc, t_bool rq); -static t_stat ni_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc); -static t_stat ni_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static void ni_cmd(uint8 slot, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp); /* - * When the NI card is pumped, its CRC depends on what slot it is - * installed in and what version of driver has been installed. + * A list of pumped code CRCs that will cause Force Function Call to + * respond with "Test Passed". Must be null-terminated. */ -#define NI_DIAG_CRCS_LEN 7 static const uint32 NI_DIAG_CRCS[] = { 0x795268a4, 0xfab1057c, @@ -125,6 +122,23 @@ static const uint32 NI_DIAG_CRCS[] = { 0x267b19a0, 0x123f36c0, 0xc04ca0ab, + 0x96d0506e, /* Rev 3 NI, SVR 3.2.3 */ + 0x9d3fafde, /* Rev 3 NI, SVR 3.2.3 */ + 0, +}; + +/* + * A list of pumped code CRCs that will cause the sysgen routine to + * respond with a full completion request instead of an express + * completion request. Must be null-terminated. + */ +static const uint32 NI_PUMP_CRCS[] = { + 0xfab1057c, /* Rev 2 NI, SVR 3.x */ + 0xf6744bed, /* Rev 2 NI, SVR 3.x */ + 0x96d0506e, /* Rev 3 NI, SVR 3.2.3 */ + 0x9d3fafde, /* Rev 3 NI, SVR 3.2.3 */ + 0x3553230a, /* Rev 3 NI, SVR 3.2.3 */ + 0, }; /* @@ -151,10 +165,6 @@ MTAB ni_mod[] = { &ni_set_stats, &ni_show_stats, NULL, "Display or reset statistics" }, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "POLL", NULL, NULL, &ni_show_poll, NULL, "Display the current polling mode" }, - { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "RQUEUE=n", NULL, - NULL, &ni_show_rqueue, NULL, "Display Request Queue for card n" }, - { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "CQUEUE=n", NULL, - NULL, &ni_show_cqueue, NULL, "Display Completion Queue for card n" }, { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", &ni_setmac, &ni_showmac, NULL, "MAC address" }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "FILTERS", NULL, @@ -291,10 +301,10 @@ static void ni_disable() sim_cancel(rq_unit); sim_cancel(cio_unit); sim_cancel(sanity_unit); - CIO_CLR_INT(ni.cid); + CIO_CLR_INT(ni.slot); } -static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp) +static void ni_cmd(uint8 slot, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp) { int i, j; int32 delay; @@ -310,14 +320,14 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp centry.subdevice = rentry->subdevice; centry.address = rentry->address; - cio[cid].op = rentry->opcode; + cio[slot].op = rentry->opcode; delay = NI_INT_DELAY; switch(rentry->opcode) { case CIO_DLM: for (i = 0; i < rentry->byte_count; i++) { - ni.crc = cio_crc32_shift(ni.crc, pread_b(rentry->address + i)); + ni.crc = cio_crc32_shift(ni.crc, pread_b(rentry->address + i, BUS_PER)); } centry.address = rentry->address + rentry->byte_count; @@ -328,9 +338,9 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp centry.address, centry.subdevice, ni.crc); if (is_exp) { - cio_cexpress(cid, NIQESIZE, ¢ry, app_data); + cio_cexpress(slot, NIQESIZE, ¢ry, app_data); } else { - cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data); + cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, app_data); } break; @@ -342,13 +352,13 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp /* If the currently running program is a diagnostics program, * we are expected to write results into memory at address * 0x200f000 */ - for (i = 0; i < NI_DIAG_CRCS_LEN; i++) { + for (i = 0; NI_DIAG_CRCS[i] != 0; i++) { if (ni.crc == NI_DIAG_CRCS[i]) { - pwrite_h(0x200f000, 0x1); /* Test success */ - pwrite_h(0x200f002, 0x0); /* Test Number */ - pwrite_h(0x200f004, 0x0); /* Actual */ - pwrite_h(0x200f006, 0x0); /* Expected */ - pwrite_b(0x200f008, 0x1); /* Success flag again */ + pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */ + pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */ + pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */ + pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */ + pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */ break; } } @@ -362,12 +372,12 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp * in the right state. */ ni_disable(); - cio[cid].sysgen_s = 0; + cio[slot].sysgen_s = 0; - if (cio[cid].ivec == 0 || cio[cid].ivec == 3) { - cio_cexpress(cid, NIQESIZE, ¢ry, app_data); + if (cio[slot].ivec == 0 || cio[slot].ivec == 3) { + cio_cexpress(slot, NIQESIZE, ¢ry, app_data); } else { - cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data); + cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, app_data); } break; @@ -378,12 +388,12 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp /* The system wants us to write sub-device structures at the * supplied address */ - pwrite_h(rentry->address, 0x0); + pwrite_h(rentry->address, 0x0, BUS_PER); if (is_exp) { - cio_cexpress(cid, NIQESIZE, ¢ry, app_data); + cio_cexpress(slot, NIQESIZE, ¢ry, app_data); } else { - cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data); + cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, app_data); } break; @@ -393,7 +403,7 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp /* Try to read the mac from memory */ for (i = 0; i < MAC_SIZE_BYTES; i++) { - ni.mac_bytes[i] = pread_b(rentry->address + i); + ni.mac_bytes[i] = pread_b(rentry->address + i, BUS_PER); } snprintf(ni.mac_str, MAC_SIZE_CHARS, "%02x:%02x:%02x:%02x:%02x:%02x", @@ -406,7 +416,7 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp status = ni_setmac(ni_dev.units, 0, ni.mac_str, NULL); - cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data); + cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, app_data); break; case NI_TURNOFF: @@ -415,7 +425,7 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp ni_disable(); - cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data); + cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, app_data); break; case NI_TURNON: @@ -424,14 +434,14 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp ni_enable(); - cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data); + cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, app_data); break; case NI_STATS: sim_debug(DBG_TRACE, &ni_dev, "[ni_cmd] NI STATS Operation\n"); - cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data); + cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, app_data); break; case NI_SEND: @@ -471,25 +481,25 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp ni.wr_buf.oversize = NULL; /* Read the size of the header */ - hdrsize = pread_h(rentry->address + EIG_TABLE_SIZE); + hdrsize = pread_h(rentry->address + EIG_TABLE_SIZE, BUS_PER); /* Read out the packet frame */ for (i = 0; i < rentry->byte_count; i++) { - ni.wr_buf.msg[i] = pread_b(rentry->address + PKT_START_OFFSET + i); + ni.wr_buf.msg[i] = pread_b(rentry->address + PKT_START_OFFSET + i, BUS_PER); } /* Get a pointer to the buffer containing the protocol data */ prot_info_offset = 0; i = 0; do { - ni.prot.addr = pread_w(rentry->address + prot_info_offset); - ni.prot.size = pread_h(rentry->address + prot_info_offset + 4); - ni.prot.last = pread_h(rentry->address + prot_info_offset + 6); + ni.prot.addr = pread_w(rentry->address + prot_info_offset, BUS_PER); + ni.prot.size = pread_h(rentry->address + prot_info_offset + 4, BUS_PER); + ni.prot.last = pread_h(rentry->address + prot_info_offset + 6, BUS_PER); prot_info_offset += 8; /* Fill in the frame from this buffer */ for (j=0; j < ni.prot.size; i++, j++) { - ni.wr_buf.msg[hdrsize + i] = pread_b(ni.prot.addr + j); + ni.wr_buf.msg[hdrsize + i] = pread_b(ni.prot.addr + j, BUS_PER); } } while (!ni.prot.last); @@ -521,7 +531,7 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp centry.byte_count <<= 8; } - cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data); + cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, app_data); delay = 0; @@ -531,7 +541,7 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp "[ni_cmd] Opcode %d Not Handled Yet\n", rentry->opcode); - cio_cqueue(cid, CIO_STAT, NIQESIZE, ¢ry, app_data); + cio_cqueue(slot, CIO_STAT, NIQESIZE, ¢ry, app_data); break; } @@ -595,8 +605,10 @@ t_stat ni_show_filters(FILE* st, UNIT* uptr, int32 val, CONST void* desc) return SCPE_OK; } -void ni_sysgen(uint8 cid) +void ni_sysgen(uint8 slot) { + int i; + t_bool pumped = FALSE; cio_entry cqe = {0}; uint8 app_data[4] = {0}; @@ -607,16 +619,21 @@ void ni_sysgen(uint8 cid) sim_debug(DBG_TRACE, &ni_dev, "[ni_sysgen] CIO SYSGEN. rqp=%08x, cqp=%08x, nrq=%d, rqs=%d cqs=%d\n", - cio[cid].rqp, cio[cid].cqp, cio[cid].no_rque, cio[cid].rqs, cio[cid].cqs); + cio[slot].rqp, cio[slot].cqp, cio[slot].no_rque, cio[slot].rqs, cio[slot].cqs); /* If the card has been successfully pumped, then we respond with * a full completion queue entry. Otherwise, an express entry is * used. */ - if (ni.crc == NI_PUMP_CRC1 || - ni.crc == NI_PUMP_CRC2) { - cio_cqueue(cid, CIO_STAT, NIQESIZE, &cqe, app_data); - } else { - cio_cexpress(cid, NIQESIZE, &cqe, app_data); + for (i = 0; NI_PUMP_CRCS[i] != 0; i++) { + if (NI_PUMP_CRCS[i] == ni.crc) { + cio_cqueue(slot, CIO_STAT, NIQESIZE, &cqe, app_data); + pumped = TRUE; + break; + } + } + + if (!pumped) { + cio_cexpress(slot, NIQESIZE, &cqe, app_data); } /* Now clear out the old CRC value, in case the card needs to be @@ -629,7 +646,7 @@ void ni_sysgen(uint8 cid) /* * Handler for CIO INT0 (express job) requests. */ -void ni_express(uint8 cid) +void ni_express(uint8 slot) { cio_entry rqe = {0}; uint8 app_data[4] = {0}; @@ -637,14 +654,14 @@ void ni_express(uint8 cid) sim_debug(DBG_TRACE, &ni_dev, "[ni_express] Handling express CIO request.\n"); - cio_rexpress(cid, NIQESIZE, &rqe, app_data); - ni_cmd(cid, &rqe, app_data, TRUE); + cio_rexpress(slot, NIQESIZE, &rqe, app_data); + ni_cmd(slot, &rqe, app_data, TRUE); } /* * Handler for CIO INT1 (full job) requests. */ -void ni_full(uint8 cid) +void ni_full(uint8 slot) { cio_entry rqe = {0}; uint8 app_data[4] = {0}; @@ -652,89 +669,49 @@ void ni_full(uint8 cid) sim_debug(DBG_TRACE, &ni_dev, "[ni_full] INT1 received. Handling full CIO request.\n"); - while (cio_cqueue_avail(cid, NIQESIZE) && - cio_rqueue(cid, GE_QUEUE, NIQESIZE, &rqe, app_data) == SCPE_OK) { - ni_cmd(cid, &rqe, app_data, FALSE); + while (cio_cqueue_avail(slot, NIQESIZE) && + cio_rqueue(slot, GE_QUEUE, NIQESIZE, &rqe, app_data) == SCPE_OK) { + ni_cmd(slot, &rqe, app_data, FALSE); } } /* * Handler for CIO RESET requests. */ -void ni_cio_reset(uint8 cid) +void ni_cio_reset(uint8 slot) { - UNUSED(cid); + UNUSED(slot); ni_disable(); } -t_stat ni_autoconfig() -{ - uint8 cid; - - /* Clear the CIO table of NI cards */ - for (cid = 0; cid < CIO_SLOTS; cid++) { - if (cio[cid].id == NI_ID) { - cio[cid].id = 0; - cio[cid].ipl = 0; - cio[cid].ivec = 0; - cio[cid].exp_handler = NULL; - cio[cid].full_handler = NULL; - cio[cid].reset_handler = NULL; - cio[cid].sysgen = NULL; - } - } - - /* Find the first avaialable slot */ - for (cid = 0; cid < CIO_SLOTS; cid++) { - if (cio[cid].id == 0) { - break; - } - } - - /* Do we have room? */ - if (cid >= CIO_SLOTS) { - return SCPE_NXM; - } - - /* Remember the card slot */ - ni.cid = cid; - - /* Set up the ni structure */ - cio[cid].id = NI_ID; - cio[cid].ipl = NI_IPL; - cio[cid].exp_handler = &ni_express; - cio[cid].full_handler = &ni_full; - cio[cid].reset_handler = &ni_cio_reset; - cio[cid].sysgen = &ni_sysgen; - - return SCPE_OK; -} - t_stat ni_reset(DEVICE *dptr) { - t_stat status; + t_stat r; + uint8 slot; char uname[16]; - sim_debug(DBG_TRACE, &ni_dev, - "[ni_reset] Resetting NI device\n"); - - /* Initial setup that should only ever be done once. */ - if (!(dptr->flags & DEV_DIS) && !ni.initialized) { - /* Autoconfiguration will select the correct backplane slot - * for the device, and enable CIO routines. This should only - * be done once. */ - status = ni_autoconfig(); - if (status != SCPE_OK) { - return status; - } - - /* Set an initial MAC address in the AT&T NI range */ - ni_setmac(ni_dev.units, 0, "80:00:10:03:00:00/32", NULL); - - ni.initialized = TRUE; + if (dptr->flags & DEV_DIS) { + cio_remove_all(NI_ID); + ni_conf = FALSE; + return SCPE_OK; } + if (!ni_conf) { + r = cio_install(NI_ID, "NI", NI_IPL, + &ni_express, &ni_full, &ni_sysgen, &ni_cio_reset, + &slot); + if (r != SCPE_OK) { + return r; + } + /* Remember the card slot */ + ni.slot = slot; + ni_conf = TRUE; + } + + /* Set an initial MAC address in the AT&T NI range */ + ni_setmac(ni_dev.units, 0, "80:00:10:03:00:00/32", NULL); + /* Set up unit names */ snprintf(uname, 16, "%s-RCV", dptr->name); sim_set_uname(rcv_unit, uname); @@ -756,9 +733,6 @@ t_stat ni_reset(DEVICE *dptr) * polling activity and interrupts are disabled. */ ni_disable(); - /* We make no attempt to autoconfig until the device - * is attached. */ - return SCPE_OK; } @@ -798,16 +772,16 @@ t_stat ni_rq_svc(UNIT *uptr) UNUSED(uptr); rq_taken = FALSE; - no_rque = cio[ni.cid].no_rque - 1; + no_rque = cio[ni.slot].no_rque - 1; for (i = 0; i < no_rque; i++) { - while (NI_CACHE_HAS_SPACE(i) && cio_rqueue(ni.cid, i+1, NIQESIZE, &rqe, slot) == SCPE_OK) { + while (NI_CACHE_HAS_SPACE(i) && cio_rqueue(ni.slot, i+1, NIQESIZE, &rqe, slot) == SCPE_OK) { sim_debug(DBG_CACHE, &ni_dev, "[cache - FILL] %s packet entry. lp=%02x ulp=%02x " "slot=%d addr=0x%08x\n", i == 0 ? "Small" : "Large", - cio_r_lp(ni.cid, i+1, NIQESIZE), - cio_r_ulp(ni.cid, i+1, NIQESIZE), + cio_r_lp(ni.slot, i+1, NIQESIZE), + cio_r_ulp(ni.slot, i+1, NIQESIZE), slot[3], rqe.address); wp = ni.job_cache[i].wp; ni.job_cache[i].req[wp].addr = rqe.address; @@ -835,11 +809,7 @@ t_stat ni_rq_svc(UNIT *uptr) if (ni.poll_rate == NI_QPOLL_FAST) { sim_activate_abs(rq_unit, NI_QPOLL_FAST); } else { - if (sim_idle_enab) { - sim_clock_coschedule(rq_unit, tmxr_poll); - } else { - sim_activate_abs(rq_unit, NI_QPOLL_SLOW); - } + sim_clock_coschedule(rq_unit, 1000); } return SCPE_OK; @@ -867,11 +837,9 @@ t_stat ni_sanity_svc(UNIT *uptr) "[ni_sanity_svc] Firing sanity timer.\n"); cqe.opcode = NI_SANITY; - cio_cqueue(ni.cid, CIO_STAT, NIQESIZE, &cqe, app_data); + cio_cqueue(ni.slot, CIO_STAT, NIQESIZE, &cqe, app_data); - if (cio[ni.cid].ivec > 0) { - CIO_SET_INT(ni.cid); - } + CIO_SET_INT(ni.slot); sim_activate_after(sanity_unit, NI_SANITY_INTERVAL_US); @@ -882,11 +850,9 @@ t_stat ni_cio_svc(UNIT *uptr) { UNUSED(uptr); - if (cio[ni.cid].ivec > 0) { - sim_debug(DBG_TRACE, &ni_dev, - "[ni_cio_svc] Handling a CIO service (Setting Interrupt) for board %d\n", ni.cid); - CIO_SET_INT(ni.cid); - } + sim_debug(DBG_TRACE, &ni_dev, + "[ni_cio_svc] Handling a CIO service (Setting Interrupt) for board %d\n", ni.slot); + CIO_SET_INT(ni.slot); return SCPE_OK; } @@ -924,13 +890,13 @@ void ni_process_packet() "[cache - DRAIN] %s packet entry. lp=%02x ulp=%02x " "slot=%d addr=0x%08x\n", que_num == 0 ? "Small" : "Large", - cio_r_lp(ni.cid, que_num+1, NIQESIZE), - cio_r_ulp(ni.cid, que_num+1, NIQESIZE), + cio_r_lp(ni.slot, que_num+1, NIQESIZE), + cio_r_ulp(ni.slot, que_num+1, NIQESIZE), slot, addr); /* Store the packet into main memory */ for (i = 0; i < len; i++) { - pwrite_b(addr + i, rbuf[i]); + pwrite_b(addr + i, rbuf[i], BUS_PER); } if (ni_dev.dctrl & DBG_DAT) { @@ -948,12 +914,10 @@ void ni_process_packet() capp_data[3] = slot; /* TODO: We should probably also check status here. */ - cio_cqueue(ni.cid, CIO_STAT, NIQESIZE, ¢ry, capp_data); + cio_cqueue(ni.slot, CIO_STAT, NIQESIZE, ¢ry, capp_data); /* Trigger an interrupt */ - if (cio[ni.cid].ivec > 0) { - CIO_SET_INT(ni.cid); - } + CIO_SET_INT(ni.slot); } t_stat ni_attach(UNIT *uptr, CONST char *cptr) @@ -1155,137 +1119,3 @@ const char *ni_description(DEVICE *dptr) return "NI 10BASE5 Ethernet controller"; } - -/* - * Useful routines for debugging request and completion queues - */ - -static t_stat ni_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc) -{ - return ni_show_queue_common(st, uptr, val, desc, TRUE); -} - -static t_stat ni_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc) -{ - return ni_show_queue_common(st, uptr, val, desc, FALSE); -} - -static t_stat ni_show_queue_common(FILE *st, UNIT *uptr, int32 val, - CONST void *desc, t_bool rq) -{ - uint8 cid; - char *cptr = (char *) desc; - t_stat result; - uint32 ptr, size, no_rque, i, j; - uint16 lp, ulp; - uint8 op, dev, seq, cmdstat; - - UNUSED(uptr); - UNUSED(val); - - if (cptr) { - cid = (uint8) get_uint(cptr, 10, 12, &result); - if (result != SCPE_OK) { - return SCPE_ARG; - } - } else { - return SCPE_ARG; - } - - /* If the card is not sysgen'ed, give up */ - if (cio[cid].sysgen_s != CIO_SYSGEN) { - fprintf(st, "No card in slot %d, or card has not completed sysgen\n", cid); - return SCPE_ARG; - } - - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "Sysgen Block:\n"); - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, " Request Queue Pointer: 0x%08x\n", cio[cid].rqp); - fprintf(st, " Completion Queue Pointer: 0x%08x\n", cio[cid].cqp); - fprintf(st, " Request Queue Size: 0x%02x\n", cio[cid].rqs); - fprintf(st, " Completion Queue Size: 0x%02x\n", cio[cid].cqs); - fprintf(st, " Interrupt Vector: %d (0x%02x)\n", cio[cid].ivec, cio[cid].ivec); - fprintf(st, " Number of Request Queues: %d\n", cio[cid].no_rque); - fprintf(st, "---------------------------------------------------------\n"); - - /* Get the top of the queue */ - if (rq) { - ptr = cio[cid].rqp; - size = cio[cid].rqs; - no_rque = cio[cid].no_rque; - } else { - ptr = cio[cid].cqp; - size = cio[cid].cqs; - no_rque = 0; /* Not used */ - } - - if (rq) { - fprintf(st, "Dumping %d Request Queues\n", no_rque); - } else { - fprintf(st, "Dumping Completion Queue\n"); - } - - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "EXPRESS ENTRY:\n"); - fprintf(st, " Byte Count: %d\n", pread_h(ptr)); - fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2)); - fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3)); - fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); - fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); - ptr += 12; - - if (rq) { - for (i = 0; i < no_rque; i++) { - lp = pread_h(ptr); - ulp = pread_h(ptr + 2); - ptr += 4; - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "REQUEST QUEUE %d\n", i); - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "Load Pointer: 0x%04x (%d)\n", lp, (lp / NIQESIZE) + 1); - fprintf(st, "Unload Pointer: 0x%04x (%d)\n", ulp, (ulp / NIQESIZE) + 1); - fprintf(st, "---------------------------------------------------------\n"); - for (j = 0; j < size; j++) { - dev = pread_b(ptr + 2); - op = pread_b(ptr + 3); - seq = (dev & 0x40) >> 6; - cmdstat = (dev & 0x80) >> 7; - fprintf(st, "REQUEST ENTRY %d (@ 0x%08x)\n", j + 1, ptr); - fprintf(st, " Byte Count: 0x%04x\n", pread_h(ptr)); - fprintf(st, " Subdevice: %d\n", dev & 0x3f); - fprintf(st, " Cmd/Stat: %d\n", cmdstat); - fprintf(st, " Seqbit: %d\n", seq); - fprintf(st, " Opcode: 0x%02x (%d)\n", op, op); - fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); - fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); - ptr += 12; - } - } - } else { - lp = pread_h(ptr); - ulp = pread_h(ptr + 2); - ptr += 4; - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "Load Pointer: 0x%04x (%d)\n", lp, (lp / NIQESIZE) + 1); - fprintf(st, "Unload Pointer: 0x%04x (%d)\n", ulp, (ulp / NIQESIZE) + 1); - fprintf(st, "---------------------------------------------------------\n"); - for (i = 0; i < size; i++) { - dev = pread_b(ptr + 2); - op = pread_b(ptr + 3); - seq = (dev & 0x40) >> 6; - cmdstat = (dev & 0x80) >> 7; - fprintf(st, "COMPLETION ENTRY %d (@ 0x%08x)\n", i + 1, ptr); - fprintf(st, " Byte Count: 0x%04x\n", pread_h(ptr)); - fprintf(st, " Subdevice: %d\n", dev & 0x3f); - fprintf(st, " Cmd/Stat: %d\n", cmdstat); - fprintf(st, " Seqbit: %d\n", seq); - fprintf(st, " Opcode: 0x%02x (%d)\n", op, op); - fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); - fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); - ptr += 12; - } - } - - return SCPE_OK; -} diff --git a/3B2/3b2_ni.h b/3B2/3b2_ni.h index b8c169a9..2e146ea8 100644 --- a/3B2/3b2_ni.h +++ b/3B2/3b2_ni.h @@ -1,6 +1,6 @@ -/* 3b2_ni.h: AT&T 3B2 Model 400 "NI" feature card +/* 3b2_ni.h: CM195A Network Interface CIO Card - Copyright (c) 2018, Seth J. Morabito + Copyright (c) 2018-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -84,9 +84,6 @@ #define NI_QPOLL_FAST 100 #define NI_QPOLL_SLOW 50000 -#define NI_PUMP_CRC1 0xfab1057c -#define NI_PUMP_CRC2 0xf6744bed - #define EIG_TABLE_SIZE 40 #define PKT_HEADER_LEN_OFFSET EIG_TABLE_SIZE #define PKT_START_OFFSET (PKT_HEADER_LEN_OFFSET + 4) @@ -168,8 +165,7 @@ typedef struct { } ni_stat_info; typedef struct { - uint8 cid; - t_bool initialized; + uint8 slot; t_bool enabled; uint32 crc; uint32 poll_rate; @@ -197,18 +193,18 @@ t_stat ni_attach(UNIT *uptr, CONST char *cptr); t_stat ni_detach(UNIT *uptr); t_stat ni_setmac(UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat ni_showmac(FILE* st, UNIT *uptr, int32 val, CONST void *desc); -t_stat ni_try_job(uint8 cid); +t_stat ni_try_job(uint8 slot); t_stat ni_show_stats(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat ni_set_stats(UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat ni_show_poll(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat ni_show_filters(FILE *st, UNIT *uptr, int32 val, CONST void *desc); const char *ni_description(DEVICE *dptr); t_stat ni_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); -void ni_cio_reset(uint8 cid); +void ni_cio_reset(uint8 slot); void ni_process_packet(); -void ni_int_ack(uint8 cid); -void ni_sysgen(uint8 cid); -void ni_express(uint8 cid); -void ni_full(uint8 cid); +void ni_int_ack(uint8 slot); +void ni_sysgen(uint8 slot); +void ni_express(uint8 slot); +void ni_full(uint8 slot); #endif /* _3B2_NI_H_ */ diff --git a/3B2/3b2_ports.c b/3B2/3b2_ports.c index b77db85b..3b5152c9 100644 --- a/3B2/3b2_ports.c +++ b/3B2/3b2_ports.c @@ -1,6 +1,6 @@ -/* 3b2_ports.c: AT&T 3B2 Model 400 "PORTS" feature card +/* 3b2_ports.c: CM195B 4-Port Serial CIO Card - Copyright (c) 2018, Seth J. Morabito + Copyright (c) 2018-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -49,11 +49,9 @@ #include "3b2_cpu.h" #include "3b2_io.h" #include "3b2_mem.h" -#include "3b2_timer.h" +#include "3b2_stddev.h" -/* Static function declarations */ -static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val, - CONST void *desc, t_bool rq); +#define IO_SCHED 1000 /* Device and units for PORTS cards * -------------------------------- @@ -78,6 +76,8 @@ static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val, * */ +#define MAX_LINES 32 + #define PPQESIZE 12 #define DELAY_ASYNC 25 #define DELAY_DLM 100 @@ -100,16 +100,31 @@ static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val, #define PORTS_DIAG_CRC5 0x4be7bccc /* Used by SVR 2.0.5 */ #define PORTS_DIAG_CRC6 0x3197f6dd /* Used by SVR 2.0.5 */ -#define LN(cid,port) ((PORTS_LINES * ((cid) - ports_base_cid)) + (port)) -#define LCID(ln) (((ln) / PORTS_LINES) + ports_base_cid) -#define LPORT(ln) ((ln) % PORTS_LINES) +#define PORTS_DFLT_LINES 4 +#define PORTS_DFLT_CARDS 1 -int8 ports_base_cid; /* First cid in our contiguous block */ -uint8 ports_int_cid; /* Interrupting card ID */ +#define LN(slot,port) (ports_slot_ln[(slot)] + (port)) +#define LSLOT(ln) (ports_ln_slot[ln]) +/* #define LN(slot,port) ((PORTS_LINES * ((slot) - ports_base_slot)) + (port)) */ +/* #define LSLOT(ln) (((ln) / PORTS_LINES) + ports_base_slot) */ +#define LPORT(ln) ((ln) % PORTS_LINES) + +int8 ports_base_slot; /* First slot in our contiguous block */ +uint8 ports_int_slot; /* Interrupting card ID */ uint8 ports_int_subdev; /* Interrupting subdevice */ t_bool ports_conf = FALSE; /* Have PORTS cards been configured? */ uint32 ports_crc; /* CRC32 of downloaded memory */ +/* Mapping of line number to CIO card slot. Up to 32 lines spread over + 8 slots are supported. */ +uint8 ports_ln_slot[MAX_LINES]; + +/* Mapping of slot number to base line number belonging to the card in + that slot. I.e., if there are two PORTS cards, one in slot 3 and + one in slot 5, index 3 will have starting line 0, index 5 will have + starting line 4. */ +uint32 ports_slot_ln[CIO_SLOTS]; + /* PORTS-specific state for each slot */ PORTS_LINE_STATE *ports_state = NULL; @@ -136,12 +151,8 @@ MTAB ports_mod[] = { { TT_MODE, TT_MODE_7B, "7b", "7B", NULL, NULL, NULL, "7 bit mode" }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL, NULL, NULL, "8 bit mode" }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL, NULL, NULL, "7 bit mode - non printing suppressed" }, - { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "RQUEUE=n", NULL, - NULL, &ports_show_rqueue, NULL, "Display Request Queue for card n" }, - { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "CQUEUE=n", NULL, - NULL, &ports_show_cqueue, NULL, "Display Completion Queue for card n" }, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n", - &ports_setnl, &tmxr_show_lines, (void *) &ports_desc, "Display number of lines" }, + &ports_setnl, &tmxr_show_lines, (void *) &ports_desc, "Show or set number of lines" }, { 0 } }; @@ -189,9 +200,9 @@ DEVICE ports_dev = { }; -static void cio_irq(uint8 cid, uint8 dev, int32 delay) +static void cio_irq(uint8 slot, uint8 dev, int32 delay) { - ports_int_cid = cid; + ports_int_slot = slot; ports_int_subdev = dev & 0xf; sim_activate(&ports_unit[2], delay); } @@ -209,16 +220,20 @@ t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc) return SCPE_ARG; } - newln = (int32) get_uint(cptr, 10, (MAX_PORTS_CARDS * PORTS_LINES), &r); + newln = (int32) get_uint(cptr, 10, (MAX_CARDS * PORTS_LINES), &r); if ((r != SCPE_OK) || (newln == ports_desc.lines)) { return r; } - if ((newln == 0) || LPORT(newln) != 0) { + if ((newln == 0) || LPORT(newln) != 0 || newln > MAX_LINES) { return SCPE_ARG; } + sim_debug(TRACE_DBG, &ports_dev, + "[ports_setnl] Setting line count to %d\n", + newln); + if (newln < ports_desc.lines) { for (i = newln, t = 0; i < ports_desc.lines; i++) { t = t | ports_ldsc[i].conn; @@ -235,10 +250,6 @@ t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc) } /* completely reset line */ tmxr_detach_ln(&ports_ldsc[i]); - if (LPORT(i) == (PORTS_LINES - 1)) { - /* Also drop the corresponding card from the CIO array */ - cio_clear(LCID(i)); - } } } @@ -258,7 +269,7 @@ t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc) } -static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data) +static void ports_cmd(uint8 slot, cio_entry *rentry, uint8 *rapp_data) { cio_entry centry = {0}; uint32 ln, i; @@ -267,37 +278,35 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data) uint8 app_data[4] = {0}; centry.address = rentry->address; - cio[cid].op = rentry->opcode; - ln = LN(cid, rentry->subdevice & 0xf); + cio[slot].op = rentry->opcode; + ln = LN(slot, rentry->subdevice & 0xf); switch(rentry->opcode) { case CIO_DLM: for (i = 0; i < rentry->byte_count; i++) { - ports_crc = cio_crc32_shift(ports_crc, pread_b(rentry->address + i)); + ports_crc = cio_crc32_shift(ports_crc, pread_b(rentry->address + i, BUS_PER)); } centry.address = rentry->address + rentry->byte_count; sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_cmd] CIO Download Memory: bytecnt=%04x " + "[ports_cmd] CIO Download Memory: bytecnt=%04x " "addr=%08x return_addr=%08x subdev=%02x (CRC=%08x)\n", - R[NUM_PC], rentry->byte_count, rentry->address, centry.address, centry.subdevice, ports_crc); /* We intentionally do not set the subdevice in * the completion entry */ - cio_cexpress(cid, PPQESIZE, ¢ry, app_data); - cio_irq(cid, rentry->subdevice, DELAY_DLM); + cio_cexpress(slot, PPQESIZE, ¢ry, app_data); + cio_irq(slot, rentry->subdevice, DELAY_DLM); break; case CIO_ULM: sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_cmd] CIO Upload Memory\n", - R[NUM_PC]); - cio_cexpress(cid, PPQESIZE, ¢ry, app_data); - cio_irq(cid, rentry->subdevice, DELAY_ULM); + "[ports_cmd] CIO Upload Memory\n"); + cio_cexpress(slot, PPQESIZE, ¢ry, app_data); + cio_irq(slot, rentry->subdevice, DELAY_ULM); break; case CIO_FCF: sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_cmd] CIO Force Function Call (CRC=%08x)\n", - R[NUM_PC], ports_crc); + "[ports_cmd] CIO Force Function Call (CRC=%08x)\n", + ports_crc); /* If the currently running program is a diagnostics program, * we are expected to write results into memory at address @@ -308,11 +317,11 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data) ports_crc == PORTS_DIAG_CRC4 || ports_crc == PORTS_DIAG_CRC5 || ports_crc == PORTS_DIAG_CRC6) { - pwrite_h(0x200f000, 0x1); /* Test success */ - pwrite_h(0x200f002, 0x0); /* Test Number */ - pwrite_h(0x200f004, 0x0); /* Actual */ - pwrite_h(0x200f006, 0x0); /* Expected */ - pwrite_b(0x200f008, 0x1); /* Success flag again */ + pwrite_h(0x200f000, 0x1, BUS_PER); /* Test success */ + pwrite_h(0x200f002, 0x0, BUS_PER); /* Test Number */ + pwrite_h(0x200f004, 0x0, BUS_PER); /* Actual */ + pwrite_h(0x200f006, 0x0, BUS_PER); /* Expected */ + pwrite_b(0x200f008, 0x1, BUS_PER); /* Success flag again */ } /* An interesting (?) side-effect of FORCE FUNCTION CALL is @@ -320,49 +329,46 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data) * required in order for new commands to work. In fact, an * INT0/INT1 combo _without_ a RESET can sysgen the board. So, * we reset the command bits here. */ - cio[cid].sysgen_s = 0; - cio_cexpress(cid, PPQESIZE, ¢ry, app_data); - cio_irq(cid, rentry->subdevice, DELAY_FCF); + cio[slot].sysgen_s = 0; + cio_cexpress(slot, PPQESIZE, ¢ry, app_data); + cio_irq(slot, rentry->subdevice, DELAY_FCF); break; case CIO_DOS: sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_cmd] CIO Determine Op Status\n", - R[NUM_PC]); - cio_cexpress(cid, PPQESIZE, ¢ry, app_data); - cio_irq(cid, rentry->subdevice, DELAY_DOS); + "[ports_cmd] CIO Determine Op Status\n"); + cio_cexpress(slot, PPQESIZE, ¢ry, app_data); + cio_irq(slot, rentry->subdevice, DELAY_DOS); break; case CIO_DSD: /* Determine Sub-Devices. We have none. */ sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_cmd] Determine Sub-Devices.\n", - R[NUM_PC]); + "[ports_cmd] Determine Sub-Devices.\n"); /* The system wants us to write sub-device structures * at the supplied address */ - pwrite_h(rentry->address, 0x0); - cio_cexpress(cid, PPQESIZE, ¢ry, app_data); - cio_irq(cid, rentry->subdevice, DELAY_DSD); + pwrite_h(rentry->address, 0x0, BUS_PER); + cio_cexpress(slot, PPQESIZE, ¢ry, app_data); + cio_irq(slot, rentry->subdevice, DELAY_DSD); break; case PPC_OPTIONS: sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_cmd] PPC Options Operation\n", - R[NUM_PC]); + "[ports_cmd] PPC Options Operation\n"); - opts.line = pread_h(rentry->address); - opts.iflag = pread_h(rentry->address + 4); - opts.oflag = pread_h(rentry->address + 6); - opts.cflag = pread_h(rentry->address + 8); - opts.lflag = pread_h(rentry->address + 10); - opts.cerase = pread_b(rentry->address + 11); - opts.ckill = pread_b(rentry->address + 12); - opts.cinter = pread_b(rentry->address + 13); - opts.cquit = pread_b(rentry->address + 14); - opts.ceof = pread_b(rentry->address + 15); - opts.ceol = pread_b(rentry->address + 16); - opts.itime = pread_b(rentry->address + 17); - opts.vtime = pread_b(rentry->address + 18); - opts.vcount = pread_b(rentry->address + 19); + opts.line = pread_h(rentry->address, BUS_PER); + opts.iflag = pread_h(rentry->address + 4, BUS_PER); + opts.oflag = pread_h(rentry->address + 6, BUS_PER); + opts.cflag = pread_h(rentry->address + 8, BUS_PER); + opts.lflag = pread_h(rentry->address + 10, BUS_PER); + opts.cerase = pread_b(rentry->address + 11, BUS_PER); + opts.ckill = pread_b(rentry->address + 12, BUS_PER); + opts.cinter = pread_b(rentry->address + 13, BUS_PER); + opts.cquit = pread_b(rentry->address + 14, BUS_PER); + opts.ceof = pread_b(rentry->address + 15, BUS_PER); + opts.ceol = pread_b(rentry->address + 16, BUS_PER); + opts.itime = pread_b(rentry->address + 17, BUS_PER); + opts.vtime = pread_b(rentry->address + 18, BUS_PER); + opts.vcount = pread_b(rentry->address + 19, BUS_PER); sim_debug(TRACE_DBG, &ports_dev, " PPC Options: iflag=%04x\n", opts.iflag); sim_debug(TRACE_DBG, &ports_dev, " PPC Options: oflag=%04x\n", opts.oflag); @@ -391,46 +397,45 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data) centry.opcode = PPC_OPTIONS; centry.subdevice = rentry->subdevice; centry.address = rentry->address; - cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data); - cio_irq(cid, rentry->subdevice, DELAY_OPTIONS); + cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, app_data); + cio_irq(slot, rentry->subdevice, DELAY_OPTIONS); break; case PPC_VERS: sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_cmd] PPC Version\n", - R[NUM_PC]); + "[ports_cmd] PPC Version\n"); /* Write the version number at the supplied address */ - pwrite_b(rentry->address, PORTS_VERSION); + pwrite_b(rentry->address, PORTS_VERSION, BUS_PER); centry.opcode = CIO_ULM; /* TODO: It's unknown what the value 0x50 means, but this * is what a real board sends. */ app_data[0] = 0x50; - cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data); - cio_irq(cid, rentry->subdevice, DELAY_VERS); + cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, app_data); + cio_irq(slot, rentry->subdevice, DELAY_VERS); break; case PPC_CONN: /* CONNECT - Full request and completion queues */ sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_cmd] PPC CONNECT - subdevice = %02x\n", - R[NUM_PC], rentry->subdevice); + "[ports_cmd] PPC CONNECT - subdevice = %02x\n", + rentry->subdevice); ports_state[ln].conn = TRUE; centry.opcode = PPC_CONN; centry.subdevice = rentry->subdevice; centry.address = rentry->address; - cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data); - cio_irq(cid, rentry->subdevice, DELAY_CONN); + cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, app_data); + cio_irq(slot, rentry->subdevice, DELAY_CONN); break; case PPC_XMIT: /* XMIT - Full request and completion queues */ /* The port being referred to is in the subdevice. */ sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_cmd] PPC XMIT - subdevice = %02x, address=%08x, byte_count=%d\n", - R[NUM_PC], rentry->subdevice, rentry->address, rentry->byte_count); + "[ports_cmd] PPC XMIT - subdevice = %02x, address=%08x, byte_count=%d\n", + rentry->subdevice, rentry->address, rentry->byte_count); /* Set state for xmit */ ports_state[ln].tx_addr = rentry->address; @@ -445,20 +450,20 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data) /* DEVICE Control - Express request and completion queues */ /* The port being referred to is in the subdevice. */ sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_cmd] PPC DEVICE - subdevice = %02x\n", - R[NUM_PC], rentry->subdevice); + "[ports_cmd] PPC DEVICE - subdevice = %02x\n", + rentry->subdevice); centry.subdevice = rentry->subdevice; centry.opcode = PPC_DEVICE; - cio_cexpress(cid, PPQESIZE, ¢ry, app_data); - cio_irq(cid, rentry->subdevice, DELAY_DEVICE); + cio_cexpress(slot, PPQESIZE, ¢ry, app_data); + cio_irq(slot, rentry->subdevice, DELAY_DEVICE); break; case PPC_RECV: /* RECV - Full request and completion queues */ /* The port being referred to is in the subdevice. */ sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_cmd] PPC RECV - subdevice = %02x addr=%08x\n", - R[NUM_PC], rentry->subdevice, rentry->address); + "[ports_cmd] PPC RECV - subdevice = %02x addr=%08x\n", + rentry->subdevice, rentry->address); break; case PPC_DISC: @@ -466,8 +471,8 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data) centry.subdevice = rentry->subdevice; centry.opcode = PPC_DISC; ports_ldsc[ln].rcve = 0; - cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data); - cio_irq(cid, rentry->subdevice, DELAY_STD); + cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, app_data); + cio_irq(slot, rentry->subdevice, DELAY_STD); break; case PPC_BRK: case PPC_CLR: @@ -476,8 +481,8 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data) ">>> Op %d Not Handled Yet\n", rentry->opcode); - cio_cexpress(cid, PPQESIZE, ¢ry, app_data); - cio_irq(cid, rentry->subdevice, DELAY_STD); + cio_cexpress(slot, PPQESIZE, ¢ry, app_data); + cio_irq(slot, rentry->subdevice, DELAY_STD); break; } } @@ -488,14 +493,14 @@ static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data) static void ports_update_conn(uint32 ln) { cio_entry centry = {0}; - uint8 cid; + uint8 slot; uint8 app_data[4] = {0}; - cid = LCID(ln); + slot = LSLOT(ln); /* If the card hasn't sysgened, there's no way to write a * completion queue entry */ - if (cio[cid].sysgen_s != CIO_SYSGEN) { + if (cio[slot].sysgen_s != CIO_SYSGEN) { return; } @@ -513,15 +518,13 @@ static void ports_update_conn(uint32 ln) centry.opcode = PPC_ASYNC; centry.subdevice = LPORT(ln); - cio_cqueue(cid, CIO_CMD, PPQESIZE, ¢ry, app_data); + cio_cqueue(slot, CIO_CMD, PPQESIZE, ¢ry, app_data); /* Interrupt */ - if (cio[cid].ivec > 0) { - CIO_SET_INT(cid); - } + CIO_SET_INT(slot); } -void ports_sysgen(uint8 cid) +void ports_sysgen(uint8 slot) { cio_entry cqe = {0}; uint8 app_data[4] = {0}; @@ -532,147 +535,98 @@ void ports_sysgen(uint8 cid) /* It's not clear why we put a response in both the express * and the full queue. */ - cio_cexpress(cid, PPQESIZE, &cqe, app_data); - cio_cqueue(cid, CIO_STAT, PPQESIZE, &cqe, app_data); + cio_cexpress(slot, PPQESIZE, &cqe, app_data); + cio_cqueue(slot, CIO_STAT, PPQESIZE, &cqe, app_data); - ports_int_cid = cid; + ports_int_slot = slot; sim_activate(&ports_unit[2], DELAY_STD); } -void ports_express(uint8 cid) +void ports_express(uint8 slot) { cio_entry rqe = {0}; uint8 app_data[4] = {0}; - cio_rexpress(cid, PPQESIZE, &rqe, app_data); - ports_cmd(cid, &rqe, app_data); + cio_rexpress(slot, PPQESIZE, &rqe, app_data); + ports_cmd(slot, &rqe, app_data); } -void ports_full(uint8 cid) +void ports_full(uint8 slot) { uint32 i; cio_entry rqe = {0}; uint8 app_data[4] = {0}; for (i = 0; i < PORTS_LINES; i++) { - if (cio_rqueue(cid, i, PPQESIZE, &rqe, app_data) == SCPE_OK) { - ports_cmd(cid, &rqe, app_data); + if (cio_rqueue(slot, i, PPQESIZE, &rqe, app_data) == SCPE_OK) { + ports_cmd(slot, &rqe, app_data); } } } t_stat ports_reset(DEVICE *dptr) { - int32 i; - uint8 cid, line, ln, end_slot; - TMLN *lp; + int32 i, j; + uint8 slot; + t_stat r; ports_crc = 0; - sim_debug(TRACE_DBG, &ports_dev, - "[ports_reset] Resetting PORTS device\n"); + if (ports_ldsc == NULL) { + sim_set_uname(&ports_unit[0], "PORTS-RCV"); + sim_set_uname(&ports_unit[1], "PORTS-XMT"); + sim_set_uname(&ports_unit[2], "PORTS-CIO"); + + ports_desc.lines = PORTS_DFLT_LINES; + ports_desc.ldsc = ports_ldsc = + (TMLN *)calloc(ports_desc.lines, sizeof(*ports_ldsc)); + } + + if (ports_state == NULL) { + ports_state = (PORTS_LINE_STATE *)calloc(ports_desc.lines, sizeof(*ports_state)); + memset(ports_state, 0, ports_desc.lines*sizeof(*ports_state)); + } + + tmxr_set_port_speed_control(&ports_desc); if ((dptr->flags & DEV_DIS)) { - for (cid = 0; cid < CIO_SLOTS; cid++) { - if (cio[cid].id == PORTS_ID) { - cio[cid].id = 0; - cio[cid].ipl = 0; - cio[cid].ivec = 0; - cio[cid].exp_handler = NULL; - cio[cid].full_handler = NULL; - cio[cid].sysgen = NULL; - } - } - + cio_remove_all(PORTS_ID); ports_conf = FALSE; - } else if (!ports_conf) { + return SCPE_OK; + } + if (!ports_conf) { /* Clear out any old cards, we're starting fresh */ - for (cid = 0; cid < CIO_SLOTS; cid++) { - if (cio[cid].id == PORTS_ID) { - cio[cid].id = 0; - cio[cid].ipl = 0; - cio[cid].ivec = 0; - cio[cid].exp_handler = NULL; - cio[cid].full_handler = NULL; - cio[cid].sysgen = NULL; + cio_remove_all(PORTS_ID); + + memset(ports_slot_ln, 0, sizeof(ports_slot_ln)); + memset(ports_ln_slot, 0, sizeof(ports_ln_slot)); + + /* Insert necessary cards into the backplane */ + j = 0; + for (i = 0; i < ports_desc.lines/PORTS_LINES; i++) { + r = cio_install(PORTS_ID, "PORTS", PORTS_IPL, + &ports_express, &ports_full, &ports_sysgen, NULL, + &slot); + if (r != SCPE_OK) { + return r; } - } - - /* Find the first avaialable slot */ - for (cid = 0; cid < CIO_SLOTS; cid++) { - if (cio[cid].id == 0) { - break; - } - } - - /* Do we have room? */ - if (cid >= CIO_SLOTS || cid > (CIO_SLOTS - (ports_desc.lines/PORTS_LINES))) { - return SCPE_NXM; - } - - /* Remember the base card slot */ - ports_base_cid = cid; - - end_slot = (cid + (ports_desc.lines/PORTS_LINES)); - - for (; cid < end_slot; cid++) { - /* Set up the ports structure */ - cio[cid].id = PORTS_ID; - cio[cid].ipl = PORTS_IPL; - cio[cid].exp_handler = &ports_express; - cio[cid].full_handler = &ports_full; - cio[cid].sysgen = &ports_sysgen; - - for (line = 0; line < PORTS_LINES; line++) { - ln = LN(cid, line); - - sim_debug(TRACE_DBG, &ports_dev, - ">>> Setting up lp %d (card %d, line %d)\n", - ln, cid, line); - - lp = &ports_ldsc[ln]; - tmxr_set_get_modem_bits(lp, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL); + /* Remember the port assignments */ + ports_slot_ln[slot] = i * PORTS_LINES; + for (; j < (i * PORTS_LINES) + PORTS_LINES; j++) { + ports_ln_slot[j] = slot; } } ports_conf = TRUE; - - if (ports_ldsc == NULL) { - ports_desc.ldsc = ports_ldsc = - (TMLN *)calloc(ports_desc.lines, sizeof(*ports_ldsc)); - } - - if (ports_state == NULL) { - sim_debug(TRACE_DBG, &ports_dev, - "[ports_reset] calloc for ports_state...\n"); - ports_state = (PORTS_LINE_STATE *)calloc(ports_desc.lines, sizeof(*ports_state)); - } - - memset(ports_state, 0, ports_desc.lines*sizeof(*ports_state)); - - tmxr_set_port_speed_control(&ports_desc); - - for (i = 0; i < ports_desc.lines; i++) { - sim_debug(TRACE_DBG, &ports_dev, - "[ports_reset] Setting up line %d...\n", i); - tmxr_set_line_unit(&ports_desc, i, &ports_unit[0]); - tmxr_set_line_output_unit(&ports_desc, i, &ports_unit[1]); - if (!ports_ldsc[i].conn) { - ports_ldsc[i].xmte = 1; - } - ports_ldsc[i].rcve = 0; - tmxr_set_config_line(&ports_ldsc[i], "9600-8N1"); - } } - if (!sim_is_active(&ports_unit[0])) { - sim_debug(TRACE_DBG, &ports_dev, - "[ports_reset] starting receive polling...\n"); - sim_activate(&ports_unit[0], ports_unit[0].wait); + /* If attached, start polling for connections */ + if (ports_unit[0].flags & UNIT_ATT) { + sim_activate_after_abs(&ports_unit[0], ports_unit[0].wait); + } else { + sim_cancel(&ports_unit[0]); } - sim_debug(TRACE_DBG, &ports_dev, - "[ports_reset] returning scpe_ok\n"); return SCPE_OK; } @@ -680,20 +634,18 @@ t_stat ports_cio_svc(UNIT *uptr) { sim_debug(TRACE_DBG, &ports_dev, "[ports_cio_svc] IRQ for board %d device %d\n", - ports_int_cid, ports_int_subdev); + ports_int_slot, ports_int_subdev); - if (cio[ports_int_cid].ivec > 0) { - CIO_SET_INT(ports_int_cid); - } + CIO_SET_INT(ports_int_slot); - switch (cio[ports_int_cid].op) { + switch (cio[ports_int_slot].op) { case PPC_CONN: - cio[ports_int_cid].op = PPC_ASYNC; - ports_ldsc[LN(ports_int_cid, ports_int_subdev)].rcve = 1; + cio[ports_int_slot].op = PPC_ASYNC; + ports_ldsc[LN(ports_int_slot, ports_int_subdev)].rcve = 1; sim_activate(&ports_unit[2], DELAY_ASYNC); break; case PPC_ASYNC: - ports_update_conn(LN(ports_int_cid, ports_int_subdev)); + ports_update_conn(LN(ports_int_slot, ports_int_subdev)); break; default: break; @@ -704,7 +656,7 @@ t_stat ports_cio_svc(UNIT *uptr) t_stat ports_rcv_svc(UNIT *uptr) { - uint8 cid; + uint8 slot; int32 temp, ln; char c; cio_entry rentry = {0}; @@ -721,10 +673,8 @@ t_stat ports_rcv_svc(UNIT *uptr) ports_update_conn(ln); } - tmxr_poll_rx(&ports_desc); - for (ln = 0; ln < ports_desc.lines; ln++) { - cid = LCID(ln); + slot = LSLOT(ln); if (!ports_ldsc[ln].conn && ports_state[ln].conn) { ports_update_conn(ln); @@ -743,24 +693,27 @@ t_stat ports_rcv_svc(UNIT *uptr) c = 0xa; } - if (cio[cid].ivec > 0 && - cio_rqueue(cid, PORTS_RCV_QUEUE, + if (cio[slot].ivec > 0 && + cio_rqueue(slot, PORTS_RCV_QUEUE, PPQESIZE, &rentry, rapp_data) == SCPE_OK) { - CIO_SET_INT(cid); + CIO_SET_INT(slot); /* Write the character to the memory address */ - pwrite_b(rentry.address, c); + pwrite_b(rentry.address, c, BUS_PER); centry.subdevice = LPORT(ln); centry.opcode = PPC_RECV; centry.address = rentry.address; capp_data[3] = RC_TMR; - cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, capp_data); + cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, capp_data); } } } } + tmxr_poll_rx(&ports_desc); + tmxr_poll_tx(&ports_desc); + tmxr_clock_coschedule(uptr, tmxr_poll); return SCPE_OK; @@ -768,7 +721,7 @@ t_stat ports_rcv_svc(UNIT *uptr) t_stat ports_xmt_svc(UNIT *uptr) { - uint8 cid, ln; + uint8 slot, ln; char c; t_bool tx = FALSE; /* Did a tx ever occur? */ cio_entry centry = {0}; @@ -777,10 +730,10 @@ t_stat ports_xmt_svc(UNIT *uptr) /* Scan all lines for output */ for (ln = 0; ln < ports_desc.lines; ln++) { - cid = LCID(ln); + slot = LSLOT(ln); if (ports_ldsc[ln].conn && ports_state[ln].tx_chars > 0) { tx = TRUE; /* Even an attempt at TX counts for rescheduling */ - c = sim_tt_outcvt(pread_b(ports_state[ln].tx_addr), + c = sim_tt_outcvt(pread_b(ports_state[ln].tx_addr, BUS_PER), TT_GET_MODE(ports_unit[0].flags)); /* The PORTS card optionally handles NL->CRLF */ @@ -790,8 +743,8 @@ t_stat ports_xmt_svc(UNIT *uptr) if (tmxr_putc_ln(&ports_ldsc[ln], 0xd) == SCPE_OK) { wait = MIN(wait, ports_ldsc[ln].txdeltausecs); sim_debug(IO_DBG, &ports_dev, - "[%08x] [ports_xmt_svc] [LINE %d] XMIT (crlf): %02x (%c)\n", - R[NUM_PC], ln, 0xd, 0xd); + "[ports_xmt_svc] [LINE %d] XMIT (crlf): %02x (%c)\n", + ln, 0xd, 0xd); /* Indicate that we're in a CRLF translation */ ports_state[ln].crlf = TRUE; } @@ -806,21 +759,21 @@ t_stat ports_xmt_svc(UNIT *uptr) ports_state[ln].tx_chars--; ports_state[ln].tx_addr++; sim_debug(IO_DBG, &ports_dev, - "[%08x] [ports_xmt_svc] [LINE %d] XMIT: %02x (%c)\n", - R[NUM_PC], ln, c, c); + "[ports_xmt_svc] [LINE %d] XMIT: %02x (%c)\n", + ln, c, c); } if (ports_state[ln].tx_chars == 0) { sim_debug(TRACE_DBG, &ports_dev, - "[%08x] [ports_xmt_svc] Done with xmit, card=%d port=%d. Interrupting.\n", - R[NUM_PC], cid, LPORT(ln)); + "[ports_xmt_svc] Done with xmit, card=%d port=%d. Interrupting.\n", + slot, LPORT(ln)); centry.byte_count = ports_state[ln].tx_req_chars; centry.subdevice = LPORT(ln); centry.opcode = PPC_XMIT; centry.address = ports_state[ln].tx_req_addr; app_data[0] = RC_FLU; - cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data); - CIO_SET_INT(cid); + cio_cqueue(slot, CIO_STAT, PPQESIZE, ¢ry, app_data); + CIO_SET_INT(slot); } } } @@ -836,11 +789,24 @@ t_stat ports_xmt_svc(UNIT *uptr) t_stat ports_attach(UNIT *uptr, CONST char *cptr) { + TMLN *lp; t_stat r; + int i; - sim_debug(TRACE_DBG, &ports_dev, "ports_attach()\n"); + if ((sim_switches & SWMASK('M'))) { + tmxr_set_modem_control_passthru(&ports_desc); + } - tmxr_set_modem_control_passthru(&ports_desc); + for (i = 0; i < ports_desc.lines; i++) { + sim_debug(TRACE_DBG, &ports_dev, + "[ports_reset] Setting up line %d...\n", i); + tmxr_set_line_output_unit(&ports_desc, i, &ports_unit[1]); + if (!ports_ldsc[i].conn) { + ports_ldsc[i].xmte = 1; + } + ports_ldsc[i].rcve = 0; + tmxr_set_config_line(&ports_ldsc[i], "9600-8N1"); + } r = tmxr_attach(&ports_desc, uptr, cptr); if (r != SCPE_OK) { @@ -848,6 +814,11 @@ t_stat ports_attach(UNIT *uptr, CONST char *cptr) return r; } + for (i = 0; i < ports_desc.lines; i++) { + lp = &ports_ldsc[i]; + tmxr_set_get_modem_bits(lp, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL); + } + return SCPE_OK; } @@ -861,129 +832,7 @@ t_stat ports_detach(UNIT *uptr) return r; } - if (sim_is_active(&ports_unit[0])) { - sim_debug(TRACE_DBG, &ports_dev, - "[ports_detach] Stopping receive polling...\n"); - sim_cancel(&ports_unit[0]); - } - tmxr_clear_modem_control_passthru(&ports_desc); return SCPE_OK; } - -/* - * Useful routines for debugging request and completion queues - */ - -t_stat ports_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc) -{ - return ports_show_queue_common(st, uptr, val, desc, TRUE); -} - -t_stat ports_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc) -{ - return ports_show_queue_common(st, uptr, val, desc, FALSE); -} - - -static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val, - CONST void *desc, t_bool rq) -{ - uint8 cid; - char *cptr = (char *) desc; - t_stat result; - uint32 ptr, size, no_rque, i, j; - uint8 op, dev, seq, cmdstat; - - if (cptr) { - cid = (uint8) get_uint(cptr, 10, 12, &result); - if (result != SCPE_OK) { - return SCPE_ARG; - } - } else { - return SCPE_ARG; - } - - /* If the card is not sysgen'ed, give up */ - if (cio[cid].sysgen_s != CIO_SYSGEN) { - fprintf(st, "No card in slot %d, or card has not completed sysgen\n", cid); - return SCPE_ARG; - } - - /* Get the top of the queue */ - if (rq) { - ptr = cio[cid].rqp; - size = cio[cid].rqs; - no_rque = cio[cid].no_rque; - } else { - ptr = cio[cid].cqp; - size = cio[cid].cqs; - no_rque = 0; /* Not used */ - } - - if (rq) { - fprintf(st, "Dumping %d Request Queues\n", no_rque); - } else { - fprintf(st, "Dumping Completion Queue\n"); - } - - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "EXPRESS ENTRY:\n"); - fprintf(st, " Byte Count: %d\n", pread_h(ptr)); - fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2)); - fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3)); - fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); - fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); - ptr += 12; - - if (rq) { - for (i = 0; i < no_rque; i++) { - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "REQUEST QUEUE %d\n", i); - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / 12); - fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / 12); - fprintf(st, "---------------------------------------------------------\n"); - ptr += 4; - for (j = 0; j < size; j++) { - dev = pread_b(ptr + 2); - op = pread_b(ptr + 3); - seq = (dev & 0x40) >> 6; - cmdstat = (dev & 0x80) >> 7; - fprintf(st, "REQUEST ENTRY %d\n", j); - fprintf(st, " Byte Count: %d\n", pread_h(ptr)); - fprintf(st, " Subdevice: %d\n", dev & 0x3f); - fprintf(st, " Cmd/Stat: %d\n", cmdstat); - fprintf(st, " Seqbit: %d\n", seq); - fprintf(st, " Opcode: 0x%02x (%d)\n", op, op); - fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); - fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); - ptr += 12; - } - } - } else { - fprintf(st, "---------------------------------------------------------\n"); - fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / 12); - fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / 12); - fprintf(st, "---------------------------------------------------------\n"); - ptr += 4; - for (i = 0; i < size; i++) { - dev = pread_b(ptr + 2); - op = pread_b(ptr + 3); - seq = (dev & 0x40) >> 6; - cmdstat = (dev & 0x80) >> 7; - fprintf(st, "COMPLETION ENTRY %d\n", i); - fprintf(st, " Byte Count: %d\n", pread_h(ptr)); - fprintf(st, " Subdevice: %d\n", dev & 0x3f); - fprintf(st, " Cmd/Stat: %d\n", cmdstat); - fprintf(st, " Seqbit: %d\n", seq); - fprintf(st, " Opcode: 0x%02x (%d)\n", op, op); - fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); - fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); - ptr += 12; - } - } - - return SCPE_OK; -} diff --git a/3B2/3b2_ports.h b/3B2/3b2_ports.h index fa9302b0..2e5d7114 100644 --- a/3B2/3b2_ports.h +++ b/3B2/3b2_ports.h @@ -1,6 +1,6 @@ -/* 3b2_ports.h: AT&T 3B2 Model 400 "PORTS" feature card +/* 3b2_ports.h: CM195B 4-Port Serial CIO Card - Copyright (c) 2018, Seth J. Morabito + Copyright (c) 2018-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -51,7 +51,8 @@ #define PORTS_IPL 10 #define PORTS_VERSION 1 -#define MAX_PORTS_CARDS 12 +#define MAX_CARDS 8 /* Up to 8 PORTS cards with 32 lines total + supported */ #define PORTS_LINES 4 #define PORTS_RCV_QUEUE 5 @@ -216,15 +217,13 @@ typedef struct { t_stat ports_reset(DEVICE *dptr); t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc); -t_stat ports_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc); -t_stat ports_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat ports_rcv_svc(UNIT *uptr); t_stat ports_xmt_svc(UNIT *uptr); t_stat ports_cio_svc(UNIT *uptr); t_stat ports_attach(UNIT *uptr, CONST char *cptr); t_stat ports_detach(UNIT *uptr); -void ports_sysgen(uint8 cid); -void ports_express(uint8 cid); -void ports_full(uint8 cid); +void ports_sysgen(uint8 slot); +void ports_express(uint8 slot); +void ports_full(uint8 slot); #endif /* _3B2_PORTS_H_ */ diff --git a/3B2/3b2_rev2_csr.c b/3B2/3b2_rev2_csr.c index aea82f1b..babc5d67 100644 --- a/3B2/3b2_rev2_csr.c +++ b/3B2/3b2_rev2_csr.c @@ -1,6 +1,6 @@ -/* 3b2_rev2_csr.c: AT&T 3B2 Rev 2 Control and Status Register +/* 3b2_rev2_csr.c: ED System Board Control and Status Register - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -33,8 +33,9 @@ #include "3b2_cpu.h" #include "3b2_sys.h" #include "3b2_timer.h" +#include "3b2_sys.h" -uint16 csr_data; +CSR_DATA csr_data; BITFIELD csr_bits[] = { BIT(IOF), @@ -94,8 +95,8 @@ 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); + "CSR=%04x\n", + csr_data); switch (reg) { case 0x2: @@ -143,20 +144,15 @@ void csr_write(uint32 pa, uint32 val, size_t size) break; case 0x23: /* Set Inhibit Timers */ sim_debug(WRITE_MSG, &csr_dev, - "[%08x] SET INHIBIT TIMERS\n", R[NUM_PC]); + "SET INHIBIT TIMERS\n"); csr_data |= CSRITIM; + timer_gate(TIMER_INTERVAL, TRUE); 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(); + "CLEAR INHIBIT TIMERS\n"); csr_data &= ~CSRITIM; + timer_gate(TIMER_INTERVAL, FALSE); break; case 0x2b: /* Set Inhibit Faults */ csr_data |= CSRIFLT; diff --git a/3B2/3b2_rev2_csr.h b/3B2/3b2_rev2_csr.h index cb873724..cd31ea2f 100644 --- a/3B2/3b2_rev2_csr.h +++ b/3B2/3b2_rev2_csr.h @@ -1,6 +1,6 @@ -/* 3b2_rev2_csr.h: AT&T 3B2 Rev 2 Control and Status Register +/* 3b2_rev2_csr.h: ED System Board Control and Status Register - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -33,6 +33,8 @@ #include "3b2_defs.h" +typedef uint16 CSR_DATA; + /* CSR */ t_stat csr_svc(UNIT *uptr); t_stat csr_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); @@ -41,6 +43,4 @@ t_stat csr_reset(DEVICE *dptr); uint32 csr_read(uint32 pa, size_t size); void csr_write(uint32 pa, uint32 val, size_t size); -extern uint16 csr_data; - #endif /* 3B2_REV2_CSR_H_ */ diff --git a/3B2/3b2_rev2_defs.h b/3B2/3b2_rev2_defs.h index 3448d92c..e2960257 100644 --- a/3B2/3b2_rev2_defs.h +++ b/3B2/3b2_rev2_defs.h @@ -1,6 +1,6 @@ -/* 3b2_rev2_defs.h: AT&T 3B2 Rev 2 (Model 400) Simulator Definitions +/* 3b2_rev2_defs.h: Version 2 (3B2/400) Common Definitions - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -83,7 +83,7 @@ #define INT_SERR 0x01 /* IPL 15 */ #define INT_CLOCK 0x02 /* IPL 15 */ #define INT_DMA 0x04 /* IPL 13 */ -#define INT_UART 0x04 /* IPL 13 */ +#define INT_UART 0x08 /* IPL 13 */ #define INT_DISK 0x10 /* IPL 11 */ #define INT_FLOPPY 0x20 /* IPL 11 */ #define INT_PIR9 0x40 /* IPL 9 */ diff --git a/3B2/3b2_rev2_mau.h b/3B2/3b2_rev2_mau.h deleted file mode 100644 index c486b3f4..00000000 --- a/3B2/3b2_rev2_mau.h +++ /dev/null @@ -1,383 +0,0 @@ -/* 3b2_rev2_mau.c: AT&T 3B2 Rev 2 (Model 400) Math Acceleration - Unit (WE32106 MAU) Header - - Copyright (c) 2019, Seth J. Morabito - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, copy, - modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - Except as contained in this notice, the name of the author shall - not be used in advertising or otherwise to promote the sale, use or - other dealings in this Software without prior written authorization - from the author. - - --------------------------------------------------------------------- - - This file is part of a simulation of the WE32106 Math Acceleration - Unit. The WE32106 MAU is an IEEE-754 compabitle floating point - hardware math accelerator that was available as an optional - component on the AT&T 3B2/310 and 3B2/400, and a standard component - on the 3B2/500, 3B2/600, and 3B2/1000. - - Portions of this code are derived from the SoftFloat 2c library by - John R. Hauser. Functions derived from SoftFloat 2c are clearly - marked in the comments. - - Legal Notice - ============ - - SoftFloat was written by John R. Hauser. Release 2c of SoftFloat - was made possible in part by the International Computer Science - Institute, located at Suite 600, 1947 Center Street, Berkeley, - California 94704. Funding was partially provided by the National - Science Foundation under grant MIP-9311980. The original version - of this code was written as part of a project to build a - fixed-point vector processor in collaboration with the University - of California at Berkeley, overseen by Profs. Nelson Morgan and - John Wawrzynek. - - THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable - effort has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS - THAT WILL AT TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS - SOFTWARE IS RESTRICTED TO PERSONS AND ORGANIZATIONS WHO CAN AND - WILL TOLERATE ALL LOSSES, COSTS, OR OTHER PROBLEMS THEY INCUR DUE - TO THE SOFTWARE WITHOUT RECOMPENSE FROM JOHN HAUSER OR THE - INTERNATIONAL COMPUTER SCIENCE INSTITUTE, AND WHO FURTHERMORE - EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER - SCIENCE INSTITUTE (possibly via similar legal notice) AGAINST ALL - LOSSES, COSTS, OR OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND - CLIENTS DUE TO THE SOFTWARE, OR INCURRED BY ANYONE DUE TO A - DERIVATIVE WORK THEY CREATE USING ANY PART OF THE SOFTWARE. - - The following are expressly permitted, even for commercial - purposes: - - (1) distribution of SoftFloat in whole or in part, as long as this - and other legal notices remain and are prominent, and provided also - that, for a partial distribution, prominent notice is given that it - is a subset of the original; and - - (2) inclusion or use of SoftFloat in whole or in part in a - derivative work, provided that the use restrictions above are met - and the minimal documentation requirements stated in the source - code are satisfied. - --------------------------------------------------------------------- - - Data Types - ========== - - The WE32106 MAU stores values using IEEE-754 1985 types, plus a - non-standard Decimal type. - - - Decimal Type - 18 BCD digits long. Each digit is 4 bits wide. - Sign is encoded in byte 0. - - 3322 2222 2222 1111 1111 1100 0000 0000 - 1098 7654 3210 9876 5432 1098 7654 3210 - +-------------------+----+----+----+----+ - | unused | D18| D17| D16| D15| High Word - +----+----+----+----+----+----+----+----+ - | D14| D13| D12| D11| D10| D09| D08| D07| Middle Word - +----+----+----+----+----+----+----+----+ - | D06| D05| D04| D03| D02| D01| D00|sign| Low Word - +----+----+----+----+----+----+----+----+ - - Sign: 0: Positive Infinity 10: Positive Number - 1: Negative Infinity 11: Negative Number - 2: Positive NaN 12: Positive Number - 3: Negative NaN 13: Negative Number - 4-9: Trapping NaN 14-15: Positive Number - - - Extended Precision (80-bit) - exponent biased by 16383 - - 3 322222222221111 1 111110000000000 - 1 098765432109876 5 432109876543210 - +-----------------+-+---------------+ - | unused |S| Exponent | High Word - +-+---------------+-+---------------+ - |J| Fraction (high word) | Middle Word - +-+---------------------------------+ - | Fraction (low word) | Low Word - +-----------------------------------+ - - - - Double Precision (64-bit) - exponent biased by 1023 - - 3 3222222222 211111111110000000000 - 1 0987654321 098765432109876543210 - +-+----------+---------------------+ - |S| Exponent | Fraction (high) | High Word - +-+----------+---------------------+ - | Fraction (low) | Low Word - +----------------------------------+ - - - - Single Precision (32-bit) - exponent biased by 127 - - 3 32222222 22211111111110000000000 - 1 09876543 21098765432109876543210 - +-+--------+-----------------------+ - |S| Exp | Fraction | - +-+--------+-----------------------+ - -*/ - -#ifndef _3B2_REV2_MAU_H_ -#define _3B2_REV2_MAU_H_ - -#include "sim_defs.h" - -#define SRC_LEN_INVALID 0 -#define SRC_LEN_SINGLE 1 -#define SRC_LEN_DOUBLE 2 -#define SRC_LEN_TRIPLE 3 - -#define MAU_ASR_RC_SHIFT 22 - -#define MAU_ASR_PR 0x20u /* Partial Remainder */ -#define MAU_ASR_QS 0x40u /* Divide By Zero Sticky */ -#define MAU_ASR_US 0x80u /* Underflow Sticky */ -#define MAU_ASR_OS 0x100u /* Overflow Sticky */ -#define MAU_ASR_IS 0x200u /* Invalid Operation Sticky */ -#define MAU_ASR_PM 0x400u /* Inexact Mask */ -#define MAU_ASR_QM 0x800u /* Divide by Zero Mask */ -#define MAU_ASR_UM 0x1000u /* Underflow Mask */ -#define MAU_ASR_OM 0x2000u /* Overflow Mask */ -#define MAU_ASR_IM 0x4000u /* Invalid Operation Mask */ - -#define MAU_ASR_UO 0x10000u /* Unordered */ -#define MAU_ASR_CSC 0x20000u /* Context Switch Control */ -#define MAU_ASR_PS 0x40000u /* Inexact Sticky */ -#define MAU_ASR_IO 0x80000u /* Integer Overflow */ -#define MAU_ASR_Z 0x100000u /* Zero Flag */ -#define MAU_ASR_N 0x200000u /* Negative Flag */ -#define MAU_ASR_RC 0x400000u /* Round Control */ - -#define MAU_ASR_NTNC 0x1000000u /* Nontrapping NaN Control */ -#define MAU_ASR_ECP 0x2000000u /* Exception Condition */ - -#define MAU_ASR_RA 0x80000000u /* Result Available */ - -#define MAU_RC_RN 0 /* Round toward Nearest */ -#define MAU_RC_RP 1 /* Round toward Plus Infin. */ -#define MAU_RC_RM 2 /* Round toward Neg. Infin. */ -#define MAU_RC_RZ 3 /* Round toward Zero */ - -#define SFP_SIGN(V) (((V) >> 31) & 1) -#define SFP_EXP(V) (((V) >> 23) & 0xff) -#define SFP_FRAC(V) ((V) & 0x7fffff) - -#define DFP_SIGN(V) (((V) >> 63) & 1) -#define DFP_EXP(V) (((V) >> 52) & 0x7ff) -#define DFP_FRAC(V) ((V) & 0xfffffffffffffull) - -#define XFP_SIGN(V) (((V)->sign_exp >> 15) & 1) -#define XFP_EXP(V) ((V)->sign_exp & 0x7fff) -#define XFP_FRAC(V) ((V)->frac) - -#define XFP_IS_NORMAL(V) ((V)->frac & 0x8000000000000000ull) - -#define DEFAULT_XFP_NAN_SIGN_EXP 0xffff -#define DEFAULT_XFP_NAN_FRAC 0xc000000000000000ull - -#define SFP_IS_TRAPPING_NAN(V) (((((V) >> 22) & 0x1ff) == 0x1fe) && \ - ((V) & 0x3fffff)) -#define DFP_IS_TRAPPING_NAN(V) (((((V) >> 51) & 0xfff) == 0xffe) && \ - ((V) & 0x7ffffffffffffull)) -#define XFP_IS_NAN(V) ((((V)->sign_exp & 0x7fff) == 0x7fff) && \ - (t_uint64)((V)->frac << 1)) -#define XFP_IS_TRAPPING_NAN(V) ((((V)->sign_exp) & 0x7fff) && \ - ((((V)->frac) & ~(0x4000000000000000ull)) << 1) && \ - (((V)->frac) == ((V)->frac & ~(0x4000000000000000ull)))) -#define PACK_DFP(SIGN,EXP,FRAC) ((((t_uint64)(SIGN))<<63) + \ - (((t_uint64)(EXP))<<52) + \ - ((t_uint64)(FRAC))) -#define PACK_SFP(SIGN,EXP,FRAC) (((uint32)(SIGN)<<31) + \ - ((uint32)(EXP)<<23) + \ - ((uint32)(FRAC))) -#define PACK_XFP(SIGN,EXP,FRAC,V) do { \ - (V)->frac = (FRAC); \ - (V)->sign_exp = ((uint16)(SIGN) << 15) + (EXP); \ - (V)->s = FALSE; \ - } while (0) - -#define PACK_XFP_S(SIGN,EXP,FRAC,S,V) do { \ - (V)->frac = (FRAC); \ - (V)->sign_exp = ((uint16)(SIGN) << 15) + (EXP); \ - (V)->s = (S) != 0; \ - } while (0) - -#define MAU_RM ((RM)((mau_state.asr >> 22) & 3)) - -typedef enum { - M_ADD = 0x02, - M_SUB = 0x03, - M_DIV = 0x04, - M_REM = 0x05, - M_MUL = 0x06, - M_MOVE = 0x07, - M_RDASR = 0x08, - M_WRASR = 0x09, - M_CMP = 0x0a, - M_CMPE = 0x0b, - M_ABS = 0x0c, - M_SQRT = 0x0d, - M_RTOI = 0x0e, - M_FTOI = 0x0f, - M_ITOF = 0x10, - M_DTOF = 0x11, - M_FTOD = 0x12, - M_NOP = 0x13, - M_EROF = 0x14, - M_NEG = 0x17, - M_LDR = 0x18, - M_CMPS = 0x1a, - M_CMPES = 0x1b -} mau_opcodes; - -typedef enum { - M_OP3_F0_SINGLE, - M_OP3_F1_SINGLE, - M_OP3_F2_SINGLE, - M_OP3_F3_SINGLE, - M_OP3_F0_DOUBLE, - M_OP3_F1_DOUBLE, - M_OP3_F2_DOUBLE, - M_OP3_F3_DOUBLE, - M_OP3_F0_TRIPLE, - M_OP3_F1_TRIPLE, - M_OP3_F2_TRIPLE, - M_OP3_F3_TRIPLE, - M_OP3_MEM_SINGLE, - M_OP3_MEM_DOUBLE, - M_OP3_MEM_TRIPLE, - M_OP3_NONE -} op3_spec; - -/* Specifier bytes for Operands 1 and 2 */ -typedef enum { - M_OP_F0, - M_OP_F1, - M_OP_F2, - M_OP_F3, - M_OP_MEM_SINGLE, - M_OP_MEM_DOUBLE, - M_OP_MEM_TRIPLE, - M_OP_NONE -} op_spec; - -/* - * 128-bit value - */ -typedef struct { - t_uint64 low; - t_uint64 high; -} t_mau_128; - -/* - * Not-a-Number Type - */ -typedef struct { - t_bool sign; - t_uint64 high; - t_uint64 low; -} T_NAN; - -/* - * Extended Precision (80 bits). - * - * Note that an undocumented feature of the WE32106 requires the use - * of uint32 rather than uint16 for the sign and exponent components - * of the struct. Although bits 80-95 are "unused", several - * diagnostics actually expect these bits to be moved and preserved on - * word transfers. They are ignored and discarded by math routines, - * however. - * - * The 's' field holds the Sticky bit used by rounding. - */ -typedef struct { - uint32 sign_exp; /* Sign and Exponent */ - t_uint64 frac; /* Fraction/Significand/Mantissa */ - t_bool s; /* Sticky bit */ -} XFP; - -typedef struct { - uint32 h; - t_uint64 l; -} DEC; - -/* - * Supported rounding modes. - */ -typedef enum { - ROUND_NEAREST, - ROUND_PLUS_INF, - ROUND_MINUS_INF, - ROUND_ZERO -} RM; - -/* - * Double Precision (64 bits) - */ -typedef t_uint64 DFP; - -/* - * Single Precision (32 bits) - */ -typedef uint32 SFP; - -/* - * MAU state - */ - -typedef struct { - uint32 cmd; - /* Exception */ - uint32 exception; - /* Status register */ - uint32 asr; - t_bool trapping_nan; - /* Generate a Non-Trapping NaN */ - t_bool ntnan; - /* Source (from broadcast) */ - uint32 src; - /* Destination (from broadcast) */ - uint32 dst; - uint8 opcode; - uint8 op1; - uint8 op2; - uint8 op3; - /* Data Register */ - XFP dr; - /* Operand Registers */ - XFP f0; - XFP f1; - XFP f2; - XFP f3; -} MAU_STATE; - -t_stat mau_reset(DEVICE *dptr); -t_stat mau_attach(UNIT *uptr, CONST char *cptr); -t_stat mau_detach(UNIT *uptr); -t_stat mau_broadcast(uint32 cmd, uint32 src, uint32 dst); -CONST char *mau_description(DEVICE *dptr); -t_stat mau_broadcast(uint32 cmd, uint32 src, uint32 dst); - -#endif /* _3B2_REV2_MAU_H_ */ diff --git a/3B2/3b2_rev2_mmu.c b/3B2/3b2_rev2_mmu.c index 9c9dc2b0..88383cb3 100644 --- a/3B2/3b2_rev2_mmu.c +++ b/3B2/3b2_rev2_mmu.c @@ -1,6 +1,6 @@ -/* 3b2_rev2_mmu.c: AT&T 3B2 Rev 2 (Model 400) MMU (WE32101) Implementation +/* 3b2_rev2_mmu.c: WE32101 MMU - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -32,6 +32,7 @@ #include "3b2_cpu.h" #include "3b2_mmu.h" #include "3b2_sys.h" +#include "3b2_mem.h" UNIT mmu_unit = { UDATA(NULL, 0, 0) }; @@ -260,13 +261,16 @@ static SIM_INLINE t_stat mmu_check_perm(uint8 flags, uint8 r_acc) case 0: /* No Access */ return SCPE_NXM; case 1: /* Exec Only */ - if (r_acc != ACC_IF && r_acc != ACC_IFAD) { + if (r_acc != ACC_IF && + r_acc != ACC_IFAD) { return SCPE_NXM; } return SCPE_OK; case 2: /* Read / Execute */ - if (r_acc != ACC_AF && r_acc != ACC_OF && - r_acc != ACC_IF && r_acc != ACC_IFAD && + if (r_acc != ACC_IF && + r_acc != ACC_IFAD && + r_acc != ACC_OF && + r_acc != ACC_AF && r_acc != ACC_MT) { return SCPE_NXM; } @@ -288,8 +292,8 @@ static SIM_INLINE void mmu_update_sd(uint32 va, uint32 mask) /* We go back to main memory to find the SD because the SD may have been loaded from cache, which is lossy. */ - sd0 = pread_w(SD_ADDR(va)); - pwrite_w(SD_ADDR(va), sd0|mask); + sd0 = pread_w(SD_ADDR(va), BUS_PER); + pwrite_w(SD_ADDR(va), sd0|mask, BUS_PER); /* There is no 'R' bit in the SD cache, only an 'M' bit. */ if (mask == SD_M_MASK) { @@ -310,8 +314,8 @@ static SIM_INLINE void mmu_update_pd(uint32 va, uint32 pd_addr, uint32 mask) /* We go back to main memory to find the PD because the PD may have been loaded from cache, which is lossy. */ - pd = pread_w(pd_addr); - pwrite_w(pd_addr, pd|mask); + pd = pread_w(pd_addr, BUS_PER); + pwrite_w(pd_addr, pd|mask, BUS_PER); /* Update in the cache */ @@ -349,50 +353,50 @@ uint32 mmu_read(uint32 pa, size_t size) case MMU_SDCL: data = mmu_state.sdcl[offset]; sim_debug(READ_MSG, &mmu_dev, - "[%08x] [pa=%08x] MMU_SDCL[%d] = %08x\n", - R[NUM_PC], pa, offset, data); + "[pa=%08x] MMU_SDCL[%d] = %08x\n", + pa, offset, data); break; case MMU_SDCH: data = mmu_state.sdch[offset]; sim_debug(READ_MSG, &mmu_dev, - "[%08x] MMU_SDCH[%d] = %08x\n", - R[NUM_PC], offset, data); + "MMU_SDCH[%d] = %08x\n", + offset, data); break; case MMU_PDCRL: data = mmu_state.pdcrl[offset]; sim_debug(READ_MSG, &mmu_dev, - "[%08x] MMU_PDCRL[%d] = %08x\n", - R[NUM_PC], offset, data); + "MMU_PDCRL[%d] = %08x\n", + offset, data); break; case MMU_PDCRH: data = mmu_state.pdcrh[offset]; sim_debug(READ_MSG, &mmu_dev, - "[%08x] MMU_PDCRH[%d] = %08x\n", - R[NUM_PC], offset, data); + "MMU_PDCRH[%d] = %08x\n", + offset, data); break; case MMU_PDCLL: data = mmu_state.pdcll[offset]; sim_debug(READ_MSG, &mmu_dev, - "[%08x] MMU_PDCLL[%d] = %08x\n", - R[NUM_PC], offset, data); + "MMU_PDCLL[%d] = %08x\n", + offset, data); break; case MMU_PDCLH: data = mmu_state.pdclh[offset]; sim_debug(READ_MSG, &mmu_dev, - "[%08x] MMU_PDCLH[%d] = %08x\n", - R[NUM_PC], offset, data); + "MMU_PDCLH[%d] = %08x\n", + offset, data); break; case MMU_SRAMA: data = mmu_state.sra[offset]; sim_debug(READ_MSG, &mmu_dev, - "[%08x] MMU_SRAMA[%d] = %08x\n", - R[NUM_PC], offset, data); + "MMU_SRAMA[%d] = %08x\n", + offset, data); break; case MMU_SRAMB: data = mmu_state.srb[offset]; sim_debug(READ_MSG, &mmu_dev, - "[%08x] MMU_SRAMB[%d] = %08x\n", - R[NUM_PC], offset, data); + "MMU_SRAMB[%d] = %08x\n", + offset, data); break; case MMU_FC: data = mmu_state.fcode; @@ -403,14 +407,14 @@ uint32 mmu_read(uint32 pa, size_t size) case MMU_CONF: data = mmu_state.conf & 0x7; sim_debug(READ_MSG, &mmu_dev, - "[%08x] MMU_CONF = %08x\n", - R[NUM_PC], data); + "MMU_CONF = %08x\n", + data); break; case MMU_VAR: data = mmu_state.var; sim_debug(READ_MSG, &mmu_dev, - "[%08x] MMU_VAR = %08x\n", - R[NUM_PC], data); + "MMU_VAR = %08x\n", + data); break; } @@ -466,8 +470,8 @@ void mmu_write(uint32 pa, uint32 val, size_t size) mmu_state.sec[offset].addr = val & 0xffffffe0; /* We flush the entire section on writing SRAMA */ sim_debug(WRITE_MSG, &mmu_dev, - "[%08x] MMU_SRAMA[%d] = %08x\n", - R[NUM_PC], offset, val); + "MMU_SRAMA[%d] = %08x\n", + offset, val); flush_cache_sec((uint8) offset); break; case MMU_SRAMB: @@ -476,8 +480,8 @@ void mmu_write(uint32 pa, uint32 val, size_t size) mmu_state.sec[offset].len = (val >> 10) & 0x1fff; /* We do not flush the cache on writing SRAMB */ sim_debug(WRITE_MSG, &mmu_dev, - "[%08x] MMU_SRAMB[%d] = %08x (len=%06x)\n", - R[NUM_PC], offset, val, mmu_state.sec[offset].len); + "MMU_SRAMB[%d] = %08x (len=%06x)\n", + offset, val, mmu_state.sec[offset].len); break; case MMU_FC: mmu_state.fcode = val; @@ -518,20 +522,20 @@ t_stat mmu_get_sd(uint32 va, uint8 r_acc, t_bool fc, if (SSL(va) > SRAMB_LEN(va)) { MMU_FAULT(MMU_F_SDTLEN); sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] SDT Length Fault. sramb_len=%x ssl=%x va=%08x\n", - R[NUM_PC], SRAMB_LEN(va), SSL(va), va); + "SDT Length Fault. sramb_len=%x ssl=%x va=%08x\n", + SRAMB_LEN(va), SSL(va), va); return SCPE_NXM; } /* sd0 contains the segment descriptor, sd1 contains a pointer to the PDT or Segment */ - *sd0 = pread_w(SD_ADDR(va)); - *sd1 = pread_w(SD_ADDR(va) + 4); + *sd0 = pread_w(SD_ADDR(va), BUS_PER); + *sd1 = pread_w(SD_ADDR(va) + 4, BUS_PER); if (!SD_VALID(*sd0)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Invalid Segment Descriptor. va=%08x sd0=%08x\n", - R[NUM_PC], va, *sd0); + "Invalid Segment Descriptor. va=%08x sd0=%08x\n", + va, *sd0); MMU_FAULT(MMU_F_INV_SD); return SCPE_NXM; } @@ -547,14 +551,14 @@ t_stat mmu_get_sd(uint32 va, uint8 r_acc, t_bool fc, if (!SD_PRESENT(*sd0)) { if (SD_CONTIG(*sd0)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Segment Not Present. va=%08x", - R[NUM_PC], va); + "Segment Not Present. va=%08x", + va); MMU_FAULT(MMU_F_SEG_NOT_PRES); return SCPE_NXM; } else { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] PDT Not Present. va=%08x", - R[NUM_PC], va); + "PDT Not Present. va=%08x", + va); MMU_FAULT(MMU_F_PDT_NOT_PRES); return SCPE_NXM; } @@ -582,15 +586,15 @@ t_stat mmu_get_pd(uint32 va, uint8 r_acc, t_bool fc, /* Bounds checking on length */ if ((PSL(va) * 4) >= MAX_OFFSET(sd0)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] PDT Length Fault. " + "PDT Length Fault. " "PDT Offset=%08x Max Offset=%08x va=%08x\n", - R[NUM_PC], (PSL(va) * 4), + (PSL(va) * 4), MAX_OFFSET(sd0), va); MMU_FAULT(MMU_F_PDTLEN); return SCPE_NXM; } - *pd = pread_w(pd_addr); + *pd = pread_w(pd_addr, BUS_PER); /* Copy the access flags from the SD */ *pd_acc = SD_ACC(sd0); @@ -614,23 +618,21 @@ t_stat mmu_decode_contig(uint32 va, uint8 r_acc, /* Update R and M bits if configured */ if (SHOULD_UPDATE_SD_R_BIT(sd0)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Updating R bit in SD\n", - R[NUM_PC]); + "Updating R bit in SD\n"); mmu_update_sd(va, SD_R_MASK); } if (SHOULD_UPDATE_SD_M_BIT(sd0)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Updating M bit in SD\n", - R[NUM_PC]); + "Updating M bit in SD\n"); mmu_update_sd(va, SD_M_MASK); } /* Generate object trap if needed */ if (SD_TRAP(sd0)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Object Trap. va=%08x", - R[NUM_PC], va); + "Object Trap. va=%08x", + va); MMU_FAULT(MMU_F_OTRAP); return SCPE_NXM; } @@ -647,9 +649,9 @@ t_stat mmu_decode_paged(uint32 va, uint8 r_acc, t_bool fc, /* If the PD is not marked present, fail */ if (!PD_PRESENT(pd)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Page Not Present. " + "Page Not Present. " "pd=%08x r_acc=%x va=%08x\n", - R[NUM_PC], pd, r_acc, va); + pd, r_acc, va); MMU_FAULT(MMU_F_PAGE_NOT_PRES); return SCPE_NXM; } @@ -659,8 +661,8 @@ t_stat mmu_decode_paged(uint32 va, uint8 r_acc, t_bool fc, the 'W' bit is set, trigger a write fault */ if ((r_acc == ACC_W || r_acc == ACC_IR) && PD_WFAULT(pd)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Page Write Fault. va=%08x\n", - R[NUM_PC], va); + "Page Write Fault. va=%08x\n", + va); MMU_FAULT(MMU_F_PW); return SCPE_NXM; } @@ -668,16 +670,14 @@ t_stat mmu_decode_paged(uint32 va, uint8 r_acc, t_bool fc, /* If this is a write, modify the M bit */ if (SHOULD_UPDATE_PD_M_BIT(pd)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Updating M bit in PD\n", - R[NUM_PC]); + "Updating M bit in PD\n"); mmu_update_pd(va, PD_LOC(sd1, va), PD_M_MASK); } /* Modify the R bit and write it back */ if (SHOULD_UPDATE_PD_R_BIT(pd)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Updating R bit in PD\n", - R[NUM_PC]); + "Updating R bit in PD\n"); mmu_update_pd(va, PD_LOC(sd1, va), PD_R_MASK); } } @@ -717,29 +717,29 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa) if (sd_cached == SCPE_OK && pd_cached != SCPE_OK) { if (SD_PAGED(sd0) && mmu_get_pd(va, r_acc, fc, sd0, sd1, &pd, &pd_acc) != SCPE_OK) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Could not get PD (partial miss). r_acc=%d, fc=%d, va=%08x\n", - R[NUM_PC], r_acc, fc, va); + "Could not get PD (partial miss). r_acc=%d, fc=%d, va=%08x\n", + r_acc, fc, va); return SCPE_NXM; } } else if (sd_cached != SCPE_OK && pd_cached == SCPE_OK) { if (mmu_get_sd(va, r_acc, fc, &sd0, &sd1) != SCPE_OK) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Could not get SD (partial miss). r_acc=%d, fc=%d, va=%08x\n", - R[NUM_PC], r_acc, fc, va); + "Could not get SD (partial miss). r_acc=%d, fc=%d, va=%08x\n", + r_acc, fc, va); return SCPE_NXM; } } else if (sd_cached != SCPE_OK && pd_cached != SCPE_OK) { if (mmu_get_sd(va, r_acc, fc, &sd0, &sd1) != SCPE_OK) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Could not get SD (full miss). r_acc=%d, fc=%d, va=%08x\n", - R[NUM_PC], r_acc, fc, va); + "Could not get SD (full miss). r_acc=%d, fc=%d, va=%08x\n", + r_acc, fc, va); return SCPE_NXM; } if (SD_PAGED(sd0) && mmu_get_pd(va, r_acc, fc, sd0, sd1, &pd, &pd_acc) != SCPE_OK) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Could not get PD (full miss). r_acc=%d, fc=%d, va=%08x\n", - R[NUM_PC], r_acc, fc, va); + "Could not get PD (full miss). r_acc=%d, fc=%d, va=%08x\n", + r_acc, fc, va); return SCPE_NXM; } } @@ -747,18 +747,17 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa) if (SD_PAGED(sd0)) { if (fc && mmu_check_perm(pd_acc, r_acc) != SCPE_OK) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] PAGED: NO ACCESS TO MEMORY AT %08x.\n" + "PAGED: NO ACCESS TO MEMORY AT %08x.\n" "\t\tcpu_cm=%d r_acc=%x pd_acc=%02x\n" "\t\tpd=%08x psw=%08x\n", - R[NUM_PC], va, CPU_CM, r_acc, pd_acc, + va, CPU_CM, r_acc, pd_acc, pd, R[NUM_PSW]); MMU_FAULT(MMU_F_ACC); return SCPE_NXM; } if (PD_LAST(pd) && (PSL_C(va) | POT(va)) >= MAX_OFFSET(sd0)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] PAGED: Segment Offset Fault.\n", - R[NUM_PC]); + "PAGED: Segment Offset Fault.\n"); MMU_FAULT(MMU_F_SEG_OFFSET); return SCPE_NXM; } @@ -766,20 +765,19 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa) } else { if (fc && mmu_check_perm(SD_ACC(sd0), r_acc) != SCPE_OK) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] CONTIGUOUS: NO ACCESS TO MEMORY AT %08x.\n" + "CONTIGUOUS: NO ACCESS TO MEMORY AT %08x.\n" "\t\tsd0=%08x sd0_addr=%08x\n" "\t\tcpu_cm=%d acc_req=%x sd_acc=%02x\n", - R[NUM_PC], va, - sd0, SD_ADDR(va), + va, sd0, SD_ADDR(va), CPU_CM, r_acc, SD_ACC(sd0)); MMU_FAULT(MMU_F_ACC); return SCPE_NXM; } if (SOT(va) >= MAX_OFFSET(sd0)) { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] CONTIGUOUS: Segment Offset Fault. " + "CONTIGUOUS: Segment Offset Fault. " "sd0=%08x sd_addr=%08x SOT=%08x len=%08x va=%08x\n", - R[NUM_PC], sd0, SD_ADDR(va), SOT(va), + sd0, SD_ADDR(va), SOT(va), MAX_OFFSET(sd0), va); MMU_FAULT(MMU_F_SEG_OFFSET); return SCPE_NXM; @@ -807,16 +805,14 @@ uint32 mmu_xlate_addr(uint32 va, uint8 r_acc) void mmu_enable() { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Enabling MMU.\n", - R[NUM_PC]); + "Enabling MMU.\n"); mmu_state.enabled = TRUE; } void mmu_disable() { sim_debug(EXECUTE_MSG, &mmu_dev, - "[%08x] Disabling MMU.\n", - R[NUM_PC]); + "Disabling MMU.\n"); mmu_state.enabled = FALSE; } diff --git a/3B2/3b2_rev2_mmu.h b/3B2/3b2_rev2_mmu.h index 6090971e..30b1d116 100644 --- a/3B2/3b2_rev2_mmu.h +++ b/3B2/3b2_rev2_mmu.h @@ -1,6 +1,6 @@ -/* 3b2_rev2_mmu.c: AT&T 3B2 Rev 2 (Model 400) MMU (WE32101) Header +/* 3b2_rev2_mmu.c: WE32101 MMU - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -326,18 +326,6 @@ uint32 mmu_read(uint32 pa, size_t size); void mmu_write(uint32 pa, uint32 val, size_t size); CONST char *mmu_description(DEVICE *dptr); -/* Physical memory read/write */ -uint8 pread_b(uint32 pa); -uint16 pread_h(uint32 pa); -uint32 pread_w(uint32 pa); -uint32 pread_w_u(uint32 pa); -void pwrite_b(uint32 pa, uint8 val); -void pwrite_h(uint32 pa, uint16 val); -void pwrite_w(uint32 pa, uint32 val); - -/* TODO: REMOVE AFTER DEBUGGING */ -uint32 safe_read_w(uint32 va); - /* Virtual memory translation */ uint32 mmu_xlate_addr(uint32 va, uint8 r_acc); t_stat mmu_decode_vaddr(uint32 vaddr, uint8 r_acc, @@ -361,19 +349,6 @@ t_stat mmu_decode_vaddr(uint32 vaddr, uint8 r_acc, #define SHOULD_UPDATE_PD_M_BIT(pd) \ (r_acc == ACC_W && !((pd) & PD_M_MASK)) -/* Dispatch to the MMU when enabled, or to physical RW when - disabled */ -uint8 read_b(uint32 va, uint8 r_acc); -uint16 read_h(uint32 va, uint8 r_acc); -uint32 read_w(uint32 va, uint8 r_acc); -void write_b(uint32 va, uint8 val); -void write_h(uint32 va, uint16 val); -void write_w(uint32 va, uint32 val); - -t_bool addr_is_rom(uint32 pa); -t_bool addr_is_mem(uint32 pa); -t_bool addr_is_io(uint32 pa); - t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa); void mmu_enable(); void mmu_disable(); diff --git a/3B2/3b2_rev2_sys.c b/3B2/3b2_rev2_sys.c index f869f4ec..c9e84b4e 100644 --- a/3B2/3b2_rev2_sys.c +++ b/3B2/3b2_rev2_sys.c @@ -1,6 +1,6 @@ -/* 3b2_rev2_sys.c: AT&T 3B2 Rev 2 (Model 400) system definition +/* 3b2_rev2_sys.c: Version 2 (3B2/400) System Definition - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/3B2/3b2_rev2_timer.c b/3B2/3b2_rev2_timer.c deleted file mode 100644 index 77c7363f..00000000 --- a/3B2/3b2_rev2_timer.c +++ /dev/null @@ -1,401 +0,0 @@ -/* 3b2_rev2_timer.c: 8253 Interval Timer - - Copyright (c) 2017, Seth J. Morabito - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, copy, - modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - Except as contained in this notice, the name of the author shall - not be used in advertising or otherwise to promote the sale, use or - other dealings in this Software without prior written authorization - from the author. -*/ - -/* - * 8253 Timer. - * - * The 8253 Timer IC has three interval timers, which we treat here as - * three units. - * - * Note that this simulation is very specific to the 3B2, and not - * usable as a general purpose 8253 simulator. - * - */ - -#include "3b2_cpu.h" -#include "3b2_csr.h" -#include "3b2_defs.h" -#include "3b2_timer.h" - -#define SET_INT do { \ - CPU_SET_INT(INT_CLOCK); \ - SET_CSR(CSRCLK); \ - } while (0) - -#define CLR_INT do { \ - CPU_CLR_INT(INT_CLOCK); \ - CLR_CSR(CSRCLK); \ - } while (0) - -struct timer_ctr TIMERS[3]; - -int32 tmxr_poll = 16667; - -/* - * The three timers, (A, B, C) run at different - * programmatially controlled frequencies, so each must be - * handled through a different service routine. - */ - -UNIT timer_unit[] = { - { UDATA(&timer0_svc, 0, 0) }, - { UDATA(&timer1_svc, UNIT_IDLE, 0) }, - { UDATA(&timer2_svc, 0, 0) }, - { NULL } -}; - -UNIT *timer_clk_unit = &timer_unit[1]; - -REG timer_reg[] = { - { HRDATAD(DIVA, TIMERS[0].divider, 16, "Divider A") }, - { HRDATAD(STA, TIMERS[0].mode, 8, "Mode A") }, - { HRDATAD(DIVB, TIMERS[1].divider, 16, "Divider B") }, - { HRDATAD(STB, TIMERS[1].mode, 8, "Mode B") }, - { HRDATAD(DIVC, TIMERS[2].divider, 16, "Divider C") }, - { HRDATAD(STC, TIMERS[2].mode, 8, "Mode C") }, - { NULL } -}; - -MTAB timer_mod[] = { - { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, NULL, "SHUTDOWN", - &timer_set_shutdown, NULL, NULL, "Soft Power Shutdown" }, - { 0 } -}; - -DEVICE timer_dev = { - "TIMER", timer_unit, timer_reg, timer_mod, - 1, 16, 8, 4, 16, 32, - NULL, NULL, &timer_reset, - NULL, NULL, NULL, NULL, - DEV_DEBUG, 0, sys_deb_tab -}; - -t_stat timer_reset(DEVICE *dptr) { - int32 i, t; - - memset(&TIMERS, 0, sizeof(struct timer_ctr) * 3); - - for (i = 0; i < 3; i++) { - timer_unit[i].tmrnum = i; - timer_unit[i].tmr = (void *)&TIMERS[i]; - } - - /* Timer 1 gate is always active */ - TIMERS[1].gate = 1; - - if (!sim_is_running) { - t = sim_rtcn_init_unit(timer_clk_unit, TPS_CLK, TMR_CLK); - sim_activate_after(timer_clk_unit, 1000000 / t); - } - - return SCPE_OK; -} - -static void timer_activate(uint8 ctrnum) -{ - struct timer_ctr *ctr; - - ctr = &TIMERS[ctrnum]; - - switch (ctrnum) { - case TIMER_SANITY: - break; - case TIMER_INTERVAL: - if ((csr_data & CSRITIM) == 0) { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] INTERVAL TIMER: Activating after %d ms\n", - R[NUM_PC], ctr->val); - sim_activate_after_abs(&timer_unit[ctrnum], ctr->val); - ctr->val--; - } else { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] INTERVAL TIMER: Currently disabled, not starting\n", - R[NUM_PC]); - } - break; - case TIMER_BUS: - break; - default: - break; - } -} - -t_stat timer_set_shutdown(UNIT *uptr, int32 val, CONST char* cptr, void* desc) -{ - struct timer_ctr *sanity = (struct timer_ctr *)timer_unit[0].tmr; - - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] Setting sanity timer to 0 for shutdown.\n", R[NUM_PC]); - - sanity->val = 0; - - CLR_INT; - - CPU_SET_INT(INT_SERR); - CSRBIT(CSRTIMO, TRUE); - - return SCPE_OK; -} - -void timer_enable(uint8 ctrnum) { - timer_activate(ctrnum); -} - -void timer_disable(uint8 ctrnum) -{ - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] Disabling timer %d\n", - R[NUM_PC], ctrnum); - sim_cancel(&timer_unit[ctrnum]); -} - -/* - * Sanity Timer - */ -t_stat timer0_svc(UNIT *uptr) -{ - struct timer_ctr *ctr; - int32 time; - - ctr = (struct timer_ctr *)uptr->tmr; - - time = ctr->divider * TIMER_STP_US; - - if (time == 0) { - time = TIMER_STP_US; - } - - sim_activate_after_abs(uptr, time); - - return SCPE_OK; -} - -/* - * Interval Timer - */ -t_stat timer1_svc(UNIT *uptr) -{ - struct timer_ctr *ctr; - int32 t; - - ctr = (struct timer_ctr *)uptr->tmr; - - if (ctr->enabled && !(csr_data & CSRITIM)) { - /* Fire the IPL 15 clock interrupt */ - SET_INT; - } - - t = sim_rtcn_calb(TPS_CLK, TMR_CLK); - sim_activate_after_abs(uptr, 1000000/TPS_CLK); - tmxr_poll = t; - - return SCPE_OK; -} - -/* - * Bus Timeout Timer - */ -t_stat timer2_svc(UNIT *uptr) -{ - struct timer_ctr *ctr; - int32 time; - - ctr = (struct timer_ctr *)uptr->tmr; - - time = ctr->divider * TIMER_STP_US; - - if (time == 0) { - time = TIMER_STP_US; - } - - sim_activate_after_abs(uptr, time); - - return SCPE_OK; -} - -uint32 timer_read(uint32 pa, size_t size) -{ - uint32 reg; - uint16 ctr_val; - uint8 ctrnum; - struct timer_ctr *ctr; - - reg = pa - TIMERBASE; - ctrnum = (reg >> 2) & 0x3; - ctr = &TIMERS[ctrnum]; - - switch (reg) { - case TIMER_REG_DIVA: - case TIMER_REG_DIVB: - case TIMER_REG_DIVC: - ctr_val = ctr->val; - - if (ctr_val != ctr->divider) { - sim_debug(READ_MSG, &timer_dev, - "[%08x] >>> ctr_val = %04x, ctr->divider = %04x\n", - R[NUM_PC], ctr_val, ctr->divider); - } - - switch (ctr->mode & CLK_RW) { - case CLK_LSB: - return ctr_val & 0xff; - case CLK_MSB: - return (ctr_val & 0xff00) >> 8; - case CLK_LMB: - if (ctr->lmb) { - ctr->lmb = FALSE; - return (ctr_val & 0xff00) >> 8; - } else { - ctr->lmb = TRUE; - return ctr_val & 0xff; - } - default: - return 0; - } - break; - case TIMER_REG_CTRL: - return ctr->mode; - case TIMER_CLR_LATCH: - /* Clearing the timer latch has a side-effect - of also clearing pending interrupts */ - CLR_INT; - return 0; - default: - /* Unhandled */ - sim_debug(READ_MSG, &timer_dev, - "[%08x] UNHANDLED TIMER READ. ADDR=%08x\n", - R[NUM_PC], pa); - return 0; - } -} - -void handle_timer_write(uint8 ctrnum, uint32 val) -{ - struct timer_ctr *ctr; - - ctr = &TIMERS[ctrnum]; - switch(ctr->mode & 0x30) { - case 0x10: - ctr->divider &= 0xff00; - ctr->divider |= val & 0xff; - ctr->val = ctr->divider; - ctr->enabled = TRUE; - ctr->stime = sim_gtime(); - sim_cancel(timer_clk_unit); - sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US); - break; - case 0x20: - ctr->divider &= 0x00ff; - ctr->divider |= (val & 0xff) << 8; - ctr->val = ctr->divider; - ctr->enabled = TRUE; - ctr->stime = sim_gtime(); - /* Kick the timer to get the new divider value */ - sim_cancel(timer_clk_unit); - sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US); - break; - case 0x30: - if (ctr->lmb) { - ctr->lmb = FALSE; - ctr->divider = (uint16) ((ctr->divider & 0x00ff) | ((val & 0xff) << 8)); - ctr->val = ctr->divider; - ctr->enabled = TRUE; - ctr->stime = sim_gtime(); - sim_debug(READ_MSG, &timer_dev, - "[%08x] Write timer %d val LMB (MSB): %02x\n", - R[NUM_PC], ctrnum, val & 0xff); - /* Kick the timer to get the new divider value */ - sim_cancel(timer_clk_unit); - sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US); - } else { - ctr->lmb = TRUE; - ctr->divider = (ctr->divider & 0xff00) | (val & 0xff); - ctr->val = ctr->divider; - } - break; - default: - break; - - } -} - -void timer_write(uint32 pa, uint32 val, size_t size) -{ - uint8 reg, ctrnum; - struct timer_ctr *ctr; - - reg = (uint8) (pa - TIMERBASE); - - switch(reg) { - case TIMER_REG_DIVA: - handle_timer_write(0, val); - break; - case TIMER_REG_DIVB: - handle_timer_write(1, val); - break; - case TIMER_REG_DIVC: - handle_timer_write(2, val); - break; - case TIMER_REG_CTRL: - /* The counter number is in bits 6 and 7 */ - ctrnum = (val >> 6) & 3; - if (ctrnum > 2) { - sim_debug(WRITE_MSG, &timer_dev, - "[%08x] WARNING: Write to invalid counter: %d\n", - R[NUM_PC], ctrnum); - return; - } - ctr = &TIMERS[ctrnum]; - ctr->mode = (uint8) val; - ctr->enabled = FALSE; - ctr->lmb = FALSE; - break; - case TIMER_CLR_LATCH: - sim_debug(WRITE_MSG, &timer_dev, - "[%08x] unexpected write to clear timer latch\n", - R[NUM_PC]); - break; - } -} - -void timer_tick() -{ - if (TIMERS[0].gate && TIMERS[0].enabled) { - TIMERS[0].val = TIMERS[0].divider - 1; - } - - if (TIMERS[1].gate && TIMERS[1].enabled) { - TIMERS[1].val = TIMERS[1].divider - 1; - } - - if (TIMERS[2].gate && TIMERS[2].enabled) { - TIMERS[2].val = TIMERS[2].divider - 1; - } -} diff --git a/3B2/3b2_rev2_timer.h b/3B2/3b2_rev2_timer.h deleted file mode 100644 index d30c0202..00000000 --- a/3B2/3b2_rev2_timer.h +++ /dev/null @@ -1,72 +0,0 @@ -/* 3b2_rev2_timer.h: 8253 Interval Timer - - Copyright (c) 2017, Seth J. Morabito - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, copy, - modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - Except as contained in this notice, the name of the author shall - not be used in advertising or otherwise to promote the sale, use or - other dealings in this Software without prior written authorization - from the author. -*/ - -#ifndef _3B2_REV2_TIMER_H_ -#define _3B2_REV2_TIMER_H_ - -#include "sim_defs.h" - -#define TIMER_STP_US 1 -#define tmrnum u3 -#define tmr up7 - -#define TIMER_REG_DIVA 0x03 -#define TIMER_REG_DIVB 0x07 -#define TIMER_REG_DIVC 0x0b -#define TIMER_REG_CTRL 0x0f -#define TIMER_CLR_LATCH 0x13 - -#define CLK_RW 0x30 -#define CLK_LSB 0x10 -#define CLK_MSB 0x20 -#define CLK_LMB 0x30 - -struct timer_ctr { - uint16 divider; - uint16 val; - uint8 mode; - t_bool lmb; - t_bool enabled; - t_bool gate; - double stime; /* Most recent start time of counter */ -}; - -t_stat timer_reset(DEVICE *dptr); -uint32 timer_read(uint32 pa, size_t size); -void timer_write(uint32 pa, uint32 val, size_t size); -void timer_tick(); -t_stat timer0_svc(UNIT *uptr); -t_stat timer1_svc(UNIT *uptr); -t_stat timer2_svc(UNIT *uptr); -t_stat timer_set_shutdown(UNIT *uptr, int32 val, CONST char *cptr, void *desc); -void timer_disable(uint8 ctrnum); -void timer_enable(uint8 ctrnum); - -#endif /* _3B2_REV2_TIMER_H_ */ diff --git a/3B2/3b2_rev3_csr.c b/3B2/3b2_rev3_csr.c index 3b6afe57..148e23f7 100644 --- a/3B2/3b2_rev3_csr.c +++ b/3B2/3b2_rev3_csr.c @@ -1,6 +1,6 @@ -/* 3b2_rev3_csr.c: AT&T 3B2/600G Control and Status Register +/* 3b2_rev3_csr.c: CM518B System Board Control, Status & Error Register - Copyright (c) 2020, Seth J. Morabito + Copyright (c) 2020-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -32,8 +32,9 @@ #include "3b2_csr.h" #include "3b2_if.h" #include "3b2_timer.h" +#include "3b2_sys.h" -uint32 csr_data; +CSR_DATA csr_data; BITFIELD csr_bits[] = { BIT(UTIM), @@ -104,8 +105,14 @@ t_stat csr_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw) t_stat csr_reset(DEVICE *dptr) { - /* Accordig to the technical reference manual, the CSR is NOT - cleared on reset */ + CSRBIT(CSRFECC, TRUE); + CSRBIT(CSRTHERM, FALSE); + CSRBIT(CSRITIM, TRUE); + CSRBIT(CSRISTIM, TRUE); + CSRBIT(CSRIBUB, TRUE); + CSRBIT(CSRPWRSPDN, FALSE); + CSRBIT(CSRFLPMO, TRUE); + return SCPE_OK; } @@ -124,8 +131,8 @@ uint32 csr_read(uint32 pa, size_t size) return (csr_data >> 24) & 0xff; default: sim_debug(WRITE_MSG, &csr_dev, - "[%08x] CSR READ. Warning, unexpected register = %02x)\n", - R[NUM_PC], reg); + "CSR READ. Warning, unexpected register = %02x)\n", + reg); return 0; } } @@ -174,36 +181,15 @@ void csr_write(uint32 pa, uint32 val, size_t size) 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); - } + timer_gate(TIMER_INTERVAL, CSR(CSRITIM)); 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); - } + timer_gate(TIMER_SANITY, CSR(CSRISTIM)); 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); - } + timer_gate(TIMER_BUS, CSR(CSRITIMO)); break; case 0x28: CSRBIT(CSRICPUFLT, val); @@ -219,6 +205,9 @@ void csr_write(uint32 pa, uint32 val, size_t size) break; case 0x38: CSRBIT(CSRFECC, val); + sim_debug(WRITE_MSG, &csr_dev, + "CSR WRITE. Force ECC Syndrome = %d\n", + val); break; case 0x3c: CSRBIT(CSRTHERM, val); @@ -229,6 +218,10 @@ void csr_write(uint32 pa, uint32 val, size_t size) break; case 0x44: CSRBIT(CSRPWRSPDN, val); + if (!val) { + /* Stop the simulator - power down */ + stop_reason = STOP_POWER; + } break; case 0x48: CSRBIT(CSRFLPFST, val); @@ -290,6 +283,7 @@ void csr_write(uint32 pa, uint32 val, size_t size) break; case 0x7c: /* System reset request */ + full_reset(); cpu_boot(0, &cpu_dev); break; default: diff --git a/3B2/3b2_rev3_csr.h b/3B2/3b2_rev3_csr.h index 292efdd9..17078bed 100644 --- a/3B2/3b2_rev3_csr.h +++ b/3B2/3b2_rev3_csr.h @@ -1,6 +1,6 @@ -/* 3b2_rev3_csr.h: AT&T 3B2/600G Control and Status Register +/* 3b2_rev3_csr.h: CM518B System Board Control, Status & Error Register - Copyright (c) 2020, Seth J. Morabito + Copyright (c) 2020-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -33,6 +33,8 @@ #include "3b2_defs.h" +typedef uint32 CSR_DATA; + 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); @@ -40,6 +42,4 @@ t_stat csr_reset(DEVICE *dptr); uint32 csr_read(uint32 pa, size_t size); void csr_write(uint32 pa, uint32 val, size_t size); -extern uint32 csr_data; - #endif diff --git a/3B2/3b2_rev3_defs.h b/3B2/3b2_rev3_defs.h index 2c5289dd..31cd7006 100644 --- a/3B2/3b2_rev3_defs.h +++ b/3B2/3b2_rev3_defs.h @@ -1,6 +1,6 @@ - /* 3b2_rev3_defs.h: AT&T 3B2 Rev 3 (Model 600G) Simulator Definitions +/* 3b2_rev3_defs.h: Veresion 3 (3B2/700) Common Definitions - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -90,14 +90,6 @@ #define INT_MAP_LEN 0x2000 -/* Memory */ -#define MEMID_4M 6 -#define MEMID_16M 7 -#define MADDR_SLOT_0 0x4d000 -#define MADDR_SLOT_1 0x4d004 -#define MADDR_SLOT_2 0x4d008 -#define MADDR_SLOT_3 0x4d00c - #define IFCSRBASE 0x40000 #define IFCSRSIZE 0x100 #define TIMERBASE 0x41000 @@ -121,9 +113,9 @@ #define MMUBASE 0x4f000 #define MMUSIZE 0x1000 #define FLTLBASE 0x4c000 -#define FLTLSIZE 0x10 +#define FLTLSIZE 0x1000 #define FLTHBASE 0x4d000 -#define FLTHSIZE 0x10 +#define FLTHSIZE 0x1000 #define VCACHE_BOTTOM 0x1c00000 #define VCACHE_TOP 0x2000000 diff --git a/3B2/3b2_rev3_mau.c b/3B2/3b2_rev3_mau.c deleted file mode 100644 index 53fcc2b6..00000000 --- a/3B2/3b2_rev3_mau.c +++ /dev/null @@ -1,31 +0,0 @@ -/* 3b2_rev3_mau.c: WE32206 Math Accelration Unit Implementation - - Copyright (c) 2021, Seth J. Morabito - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, copy, - modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - Except as contained in this notice, the name of the author shall - not be used in advertising or otherwise to promote the sale, use or - other dealings in this Software without prior written authorization - from the author. -*/ - -/* Stub file. Not Yet Implemented. */ diff --git a/3B2/3b2_rev3_mau.h b/3B2/3b2_rev3_mau.h deleted file mode 100644 index 9b54bb2b..00000000 --- a/3B2/3b2_rev3_mau.h +++ /dev/null @@ -1,36 +0,0 @@ -/* 3b2_rev3_mau.h: WE32206 Math Accelration Unit Header - - Copyright (c) 2021, Seth J. Morabito - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, copy, - modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - Except as contained in this notice, the name of the author shall - not be used in advertising or otherwise to promote the sale, use or - other dealings in this Software without prior written authorization - from the author. -*/ - -#ifndef _3B2_REV3_MAU_H_ -#define _3B2_REV3_MAU_H_ - -/* Stub file. Not Yet Implemented. */ - -#endif diff --git a/3B2/3b2_rev3_mmu.c b/3B2/3b2_rev3_mmu.c index 898be53b..ccfc6eda 100644 --- a/3B2/3b2_rev3_mmu.c +++ b/3B2/3b2_rev3_mmu.c @@ -1,6 +1,6 @@ -/* 3b2_rev3_mmu.c: AT&T 3B2/600G MMU (WE32201) +/* 3b2_rev3_mmu.c: WE32201 MMU - Copyright (c) 2020, Seth J. Morabito + Copyright (c) 2020-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -188,8 +188,8 @@ static void put_sdce(uint32 va, uint32 sd_hi, uint32 sd_lo) mmu_state.sdcl[ci] = SD_TO_SDCL(sd_lo, va); 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]); + "CACHED SD AT IDX %d. va=%08x sd_hi=%08x sd_lo=%08x sdc_hi=%08x sdc_lo=%08x\n", + ci, va, sd_hi, sd_lo, mmu_state.sdch[ci], mmu_state.sdcl[ci]); } @@ -236,8 +236,8 @@ static t_stat get_pdce(uint32 va, uint32 *pd, uint8 *pd_acc, uint32 *pdc_idx) *pd_acc = (mmu_state.pdcl[i] >> 24) & 0xff; *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, + "PDC HIT. va=%08x idx=%d tag=%03x pd=%08x pdcl=%08x pdch=%08x\n", + va, i, key_tag, *pd, mmu_state.pdcl[i], mmu_state.pdch[i]); set_u_bit(i); return SCPE_OK; @@ -245,8 +245,8 @@ static t_stat get_pdce(uint32 va, uint32 *pd, uint8 *pd_acc, uint32 *pdc_idx) } sim_debug(MMU_CACHE_DBG, &mmu_dev, - "[%08x] PDC MISS. va=%08x tag=%03x\n", - R[NUM_PC], va, key_tag); + "PDC MISS. va=%08x tag=%03x\n", + va, key_tag); return SCPE_NXM; } @@ -259,8 +259,8 @@ static void put_pdce_at(uint32 va, uint32 sd_lo, uint32 pd, uint32 slot) mmu_state.pdcl[slot] = PD_TO_PDCL(pd, sd_lo); mmu_state.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); + "Caching MMU PDC entry at index %d (pdc_hi=%08x pdc_lo=%08x va=%08x)\n", + slot, mmu_state.pdch[slot], mmu_state.pdcl[slot], va); set_u_bit(slot); mmu_state.last_cached = slot; } @@ -279,8 +279,7 @@ static uint32 put_pdce(uint32 va, uint32 sd_lo, uint32 pd) */ if (mmu_state.flush_u) { sim_debug(MMU_CACHE_DBG, &mmu_dev, - "[%08x] Flushing PDC U bits on all-set condition.\n", - R[NUM_PC]); + "Flushing PDC U bits on all-set condition.\n"); mmu_state.flush_u = FALSE; for (i = 0; i < MMU_PDCS; i++) { if (i != mmu_state.last_cached) { @@ -332,16 +331,14 @@ static void flush_pdc(uint32 va) target_tag = mmu_state.pdch[i] & PDC_TAG_MASK; 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], + "Flushing MMU PDC entry pdc_lo=%08x pdc_hi=%08x index %d (va=%08x)\n", 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]); + "Flushing MMU PDC entry: CONTIGUOUS\n"); /* 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 @@ -361,8 +358,8 @@ static void flush_pdc(uint32 va) } 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); + "Flushing MMU PDC entry: NOT FOUND (va=%08x key_tag=%08x)\n", + va, key_tag); } @@ -374,8 +371,7 @@ static void flush_caches() uint32 i; sim_debug(MMU_CACHE_DBG, &mmu_dev, - "[%08x] Flushing MMU PDC and SDC\n", - R[NUM_PC]); + "Flushing MMU PDC and SDC\n"); for (i = 0; i < MMU_SDCS; i++) { mmu_state.sdch[i] &= ~SDC_G_MASK; @@ -399,13 +395,16 @@ static t_stat mmu_check_perm(uint8 flags, uint8 r_acc) case 0: /* No Access */ return SCPE_NXM; case 1: /* Exec Only */ - if (r_acc != ACC_IF && r_acc != ACC_IFAD) { + if (r_acc != ACC_IF && + r_acc != ACC_IFAD) { return SCPE_NXM; } return SCPE_OK; case 2: /* Read / Execute */ - if (r_acc != ACC_IF && r_acc != ACC_IFAD && - r_acc != ACC_AF && r_acc != ACC_OF && + if (r_acc != ACC_IF && + r_acc != ACC_IFAD && + r_acc != ACC_OF && + r_acc != ACC_AF && r_acc != ACC_MT) { return SCPE_NXM; } @@ -415,29 +414,6 @@ static t_stat mmu_check_perm(uint8 flags, uint8 r_acc) } } -/* - * Internally, the ID Number Cache is a fully associative cache with a - * tag consisting of the upper 29 bits of the segment address, plus a - * U bit indicating that the ID is usable. The value (the ID number) - * is the fixed index of the tag within the cache. - */ -static t_stat get_idnc(uint32 va, uint8 *id) -{ - uint32 i, tag, entry; - - tag = IDNC_TAG(va); - - for (i = 0; i < MMU_IDNCS; i++) { - entry = mmu_state.idnc[i]; - if ((IDNC_TAG(entry) == tag) && IDNC_U(entry)) { - *id = ((uint8)i & 0xff); - return SCPE_OK; - } - } - - return SCPE_NXM; -} - /* * Initialize the MMU device */ @@ -465,56 +441,56 @@ uint32 mmu_read(uint32 pa, size_t size) 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); + "MMU_SDCL[%d] = %08x\n", + 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); + "MMU_SDCH[%d] = %08x\n", + 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); + "MMU_PDCL[%d] = %08x\n", + 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); + "MMU_PDCH[%d] = %08x\n", + 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); + "MMU_SRAMA[%d] = %08x\n", + 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); + "MMU_SRAMB[%d] = %08x\n", + 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); + "MMU_FC = %08x\n", + 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); + "MMU_FA = %08x\n", + 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 = %02x (M=%d R=%d $=%d PS=%d MCE=%d DCE=%d)\n", + data, MMU_CONF_M, MMU_CONF_R, MMU_CONF_C, @@ -525,37 +501,36 @@ uint32 mmu_read(uint32 pa, size_t size) case MMU_VAR: data = mmu_state.var; sim_debug(MMU_READ_DBG, &mmu_dev, - "[%08x] MMU_VAR = %08x\n", - R[NUM_PC], data); + "MMU_VAR = %08x\n", + data); break; case MMU_IDC: /* TODO: Implement */ data = 0; sim_debug(MMU_READ_DBG, &mmu_dev, - "[%08x] MMU_IDC\n", R[NUM_PC]); + "MMU_IDC\n"); break; case MMU_IDNR: /* TODO: Implement */ data = 0; sim_debug(MMU_READ_DBG, &mmu_dev, - "[%08x] MMU_IDNR\n", R[NUM_PC]); + "MMU_IDNR\n"); break; case MMU_FIDNR: /* TODO: Implement */ data = 0; sim_debug(MMU_READ_DBG, &mmu_dev, - "[%08x] MMU_FIDNR\n", R[NUM_PC]); + "MMU_FIDNR\n"); break; case MMU_VR: - /* Simply not faulting here is good enough */ - data = 0; + data = MMU_REV3_VER; sim_debug(MMU_READ_DBG, &mmu_dev, - "[%08x] MMU_VR\n", R[NUM_PC]); + "MMU_VR = 0x23\n"); break; default: sim_debug(MMU_READ_DBG, &mmu_dev, - "[%08x] Invalid MMU register: pa=%08x\n", - R[NUM_PC], pa); + "Invalid MMU register: pa=%08x\n", + pa); CSRBIT(CSRTIMO, TRUE); break; } @@ -600,15 +575,14 @@ void mmu_write(uint32 pa, uint32 val, size_t size) break; case MMU_FDCR: sim_debug(MMU_WRITE_DBG, &mmu_dev, - "[%08x] MMU_FDCR\n", - R[NUM_PC]); + "MMU_FDCR\n"); /* 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_SRAMA[%d] = %08x\n", + index, val); mmu_state.sra[index] = val; mmu_state.sec[index].addr = val & 0xfffffffc; @@ -616,10 +590,9 @@ void mmu_write(uint32 pa, uint32 val, size_t size) for (i = 0; i < MMU_SDCS; i++) { if (((mmu_state.sdcl[i] >> 10) & 0x3) == index) { sim_debug(MMU_CACHE_DBG, &mmu_dev, - "[%08x] Flushing MMU SDC entry at index %d " + "Flushing MMU SDC entry at index %d " "(sdc_lo=%08x sdc_hi=%08x)\n", - R[NUM_PC], i, - mmu_state.sdcl[i], mmu_state.sdch[i]); + i, mmu_state.sdcl[i], mmu_state.sdch[i]); mmu_state.sdch[i] &= ~(SDC_G_MASK); } } @@ -637,8 +610,7 @@ void mmu_write(uint32 pa, uint32 val, size_t size) mmu_state.sec[index].len = (val >> 10) & 0x1fff; /* 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], + "MMU_SRAMB[%d] length=%04x (%d segments)\n", index, mmu_state.sec[index].len, mmu_state.sec[index].len + 1); @@ -647,20 +619,20 @@ void mmu_write(uint32 pa, uint32 val, size_t size) /* 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); + "MMU_FC = %08x\n", + 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); + "MMU_FADDR = %08x\n", + 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 = %02x (M=%d R=%d $=%d PS=%d MCE=%d DCE=%d)\n", + val, MMU_CONF_M, MMU_CONF_R, MMU_CONF_C, @@ -671,14 +643,13 @@ void mmu_write(uint32 pa, uint32 val, size_t size) case MMU_VAR: mmu_state.var = val; sim_debug(MMU_WRITE_DBG, &mmu_dev, - "[%08x] MMU_VAR = %08x\n", - R[NUM_PC], val); + "MMU_VAR = %08x\n", 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 " + "Flushing MMU SDC entry at index %d " "(sdc_lo=%08x sdc_hi=%08x)\n", - R[NUM_PC], SDC_IDX(val), + SDC_IDX(val), mmu_state.sdcl[SDC_IDX(val)], mmu_state.sdch[SDC_IDX(val)]); mmu_state.sdch[SDC_IDX(val)] &= ~SDC_G_MASK; @@ -687,28 +658,28 @@ void mmu_write(uint32 pa, uint32 val, size_t size) break; case MMU_IDC: sim_debug(MMU_WRITE_DBG, &mmu_dev, - "[%08x] MMU_IDC = %08x\n", - R[NUM_PC], val); + "MMU_IDC = %08x\n", + val); break; case MMU_IDNR: sim_debug(MMU_WRITE_DBG, &mmu_dev, - "[%08x] MMU_IDNR = %08x\n", - R[NUM_PC], val); + "MMU_IDNR = %08x\n", + val); break; case MMU_FIDNR: sim_debug(MMU_WRITE_DBG, &mmu_dev, - "[%08x] MMU_FIDNR = %08x\n", - R[NUM_PC], val); + "MMU_FIDNR = %08x\n", + val); break; case MMU_VR: sim_debug(MMU_WRITE_DBG, &mmu_dev, - "[%08x] MMU_VR = %08x\n", - R[NUM_PC], val); + "MMU_VR = %08x\n", + 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); + "UNHANDLED WRITE (entity=0x%x, index=0x%x, val=%08x)\n", + entity, index, val); break; } } @@ -726,8 +697,8 @@ static t_stat mmu_update_history(uint32 va, uint8 r_acc, uint32 pdc_idx, t_bool update_sdc = FALSE; } - sd_lo = pread_w(SD_ADDR(va)); - sd_hi = pread_w(SD_ADDR(va) + 4); + sd_lo = pread_w(SD_ADDR(va), BUS_PER); + sd_hi = pread_w(SD_ADDR(va) + 4, BUS_PER); if (MMU_CONF_M && r_acc == ACC_W && (mmu_state.sdcl[SDC_IDX(va)] & SDC_M_MASK) == 0) { if (update_sdc) { @@ -736,13 +707,12 @@ static t_stat mmu_update_history(uint32 va, uint8 r_acc, uint32 pdc_idx, t_bool if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) { sim_debug(MMU_FAULT_DBG, &mmu_dev, - "[%08x] MMU R&M Update Fault (M)\n", - R[NUM_PC]); + "MMU R&M Update Fault (M)\n"); MMU_FAULT(MMU_F_RM_UPD); return SCPE_NXM; } - pwrite_w(SD_ADDR(va), sd_lo | SD_M_MASK); + pwrite_w(SD_ADDR(va), sd_lo | SD_M_MASK, BUS_PER); } if (MMU_CONF_R && (mmu_state.sdcl[SDC_IDX(va)] & SDC_R_MASK) == 0) { @@ -752,13 +722,12 @@ static t_stat mmu_update_history(uint32 va, uint8 r_acc, uint32 pdc_idx, t_bool if (mmu_check_perm(SD_ACC(sd_lo), r_acc) != SCPE_OK) { sim_debug(MMU_FAULT_DBG, &mmu_dev, - "[%08x] MMU R&M Update Fault (R)\n", - R[NUM_PC]); + "MMU R&M Update Fault (R)\n"); MMU_FAULT(MMU_F_RM_UPD); return SCPE_NXM; } - pwrite_w(SD_ADDR(va), sd_lo | SD_R_MASK); + pwrite_w(SD_ADDR(va), sd_lo | SD_R_MASK, BUS_PER); } if (!SD_CONTIG(sd_lo)) { @@ -766,14 +735,14 @@ static t_stat mmu_update_history(uint32 va, uint8 r_acc, uint32 pdc_idx, t_bool if (r_acc == ACC_W && (mmu_state.pdcl[pdc_idx] & PDC_M_MASK) == 0) { mmu_state.pdcl[pdc_idx] |= PDC_M_MASK; - pd = pread_w(pd_addr); - pwrite_w(pd_addr, pd | PD_M_MASK); + pd = pread_w(pd_addr, BUS_PER); + pwrite_w(pd_addr, pd | PD_M_MASK, BUS_PER); } 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); + pd = pread_w(pd_addr, BUS_PER); + pwrite_w(pd_addr, pd | PD_R_MASK, BUS_PER); } } @@ -821,8 +790,8 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc, * checked because SSL out of bounds is a fatal error. */ 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); + "SDT Length Fault. sramb_len=%x ssl=%x va=%08x\n", + SRAMB_LEN(va), SSL(va), va); MMU_FAULT(MMU_F_SDTLEN); return SCPE_NXM; } @@ -836,17 +805,17 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc, * memory. */ sdc_miss = TRUE; - sd_lo = pread_w(sd_ptr); /* Control Bits */ - sd_hi = pread_w(sd_ptr + 4); /* Address Bits */ + sd_lo = pread_w(sd_ptr, BUS_PER); /* Control Bits */ + sd_hi = pread_w(sd_ptr + 4, BUS_PER); /* 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); + "SDC miss. Read sd_ptr=%08x sd_lo=%08x sd_hi=%08x va=%08x\n", + 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); + "Invalid Segment Descriptor. va=%08x sd_hi=%08x sd_lo=%08x\n", + va, sd_hi, sd_lo); MMU_FAULT(MMU_F_INV_SD); return SCPE_NXM; } @@ -854,8 +823,8 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc, if (SD_INDIRECT(sd_lo)) { if (++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); + "Max Indirects Fault. va=%08x sd_hi=%08x sd_lo=%08x\n", + va, sd_hi, sd_lo); MMU_FAULT(MMU_F_INDIRECT); return SCPE_NXM; } @@ -863,8 +832,8 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc, /* Any permission failure at this point is actually an MMU_F_MISS_MEM */ 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 Miss Processing Memory Fault (SD Access) (ckm=%d pd_acc=%02x r_acc=%02x)\n", + CPU_CM, SD_ACC(sd_lo), r_acc); MMU_FAULT(MMU_F_MISS_MEM); return SCPE_NXM; } @@ -872,8 +841,8 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc, /* sd_hi is a pointer to a new segment descriptor */ sd_ptr = sd_hi; - sd_lo = pread_w(sd_ptr); - sd_hi = pread_w(sd_ptr + 4); + sd_lo = pread_w(sd_ptr, BUS_PER); + sd_hi = pread_w(sd_ptr + 4, BUS_PER); } else { /* If it's not an indirection, we're done. */ break; @@ -886,14 +855,14 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc, fault; otherwise, it's a PDT NOT PRESENT fault. */ if (SD_CONTIG(sd_lo)) { sim_debug(MMU_FAULT_DBG, &mmu_dev, - "[%08x] Segment Not Present. va=%08x\n", - R[NUM_PC], va); + "Segment Not Present. va=%08x\n", + 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); + "PDT Not Present. va=%08x\n", + va); MMU_FAULT(MMU_F_PDT_NOT_PRES); return SCPE_NXM; } @@ -903,16 +872,16 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc, if (SD_CONTIG(sd_lo)) { if (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); + "Segment Offset Fault. va=%08x\n", + 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)); + "PDT Length Fault. va=%08x max_seg_off=0x%x\n", + va, MAX_SEG_OFF(sd_lo)); MMU_FAULT(MMU_F_PDTLEN); return SCPE_NXM; } @@ -923,8 +892,8 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc, /* 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); + "[AFTER DISCONTINUITY] Access to Memory Denied (va=%08x ckm=%d pd_acc=%02x r_acc=%02x)\n", + va, CPU_CM, SD_ACC(sd_lo), r_acc); MMU_FAULT(MMU_F_ACC); return SCPE_NXM; } @@ -936,31 +905,31 @@ t_stat mmu_pdc_miss(uint32 va, uint8 r_acc, t_bool fc, 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); + "Contiguous Segment. Constructing PD. PSIZE=%d va=%08x sd_hi=%08x sd_lo=%08x pd=%08x\n", + 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); + *pd = pread_w(pd_addr, BUS_PER); 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); + "Paged Segment. Loaded PD. va=%08x sd_hi=%08x sd_lo=%08x pd_addr=%08x pd=%08x\n", + 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); + "Page Write Fault, pd=%08x va=%08x\n", + *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); + "Page Not Present Fault. pd=%08x va=%08x\n", + *pd, va); MMU_FAULT(MMU_F_PAGE_NOT_PRES); return SCPE_NXM; } @@ -1018,16 +987,16 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa) if (get_pdce(va, &pd, &pd_acc, &pdc_idx) == SCPE_OK) { if (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); + "Access to Memory Denied (va=%08x ckm=%d pd_acc=%02x r_acc=%02x)\n", + 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); + "Page Write Fault, pd=%08x va=%08x\n", + pd, va); MMU_FAULT(MMU_F_PW); return SCPE_NXM; } @@ -1053,8 +1022,8 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa) *pa = PD_ADDR(pd) + POT(va); 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); + "XLATE DONE. r_acc=%d va=%08x pa=%08x\n", + r_acc, va, *pa); return SCPE_OK; } @@ -1189,8 +1158,8 @@ t_stat mmu_show_sdt(FILE *st, UNIT *uptr, int32 val, CONST void *desc) 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); + sd_lo = pread_w(addr + (i * 8), BUS_PER) & SD_RES_MASK; + sd_hi = pread_w(addr + (i * 8) + 4, BUS_PER); base = (sec << 14 | i << 1) << 16; pages = ((sd_lo & SD_MAX_OFF_MASK) >> 18) + 1; diff --git a/3B2/3b2_rev3_mmu.h b/3B2/3b2_rev3_mmu.h index a946553a..e538d785 100644 --- a/3B2/3b2_rev3_mmu.h +++ b/3B2/3b2_rev3_mmu.h @@ -1,6 +1,6 @@ -/* 3b2_rev3_mmu.h: AT&T 3B2/600G MMU (WE32201) Header +/* 3b2_rev3_mmu.h: WE32201 MMU - Copyright (c) 2020, Seth J. Morabito + Copyright (c) 2020-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -28,8 +28,8 @@ from the author. */ -#ifndef _3B2_1000_MMU_H_ -#define _3B2_1000_MMU_H_ +#ifndef _3B2_REV3_MMU_H_ +#define _3B2_REV3_MMU_H_ #include "3b2_defs.h" @@ -55,6 +55,8 @@ #define MMU_FIDNR 14 /* Flush ID Number Register */ #define MMU_VR 15 /* Version Register */ +#define MMU_REV3_VER 0x23 /* Version byte returned by WE32201 MMU */ + #define MMU_CONF_M (mmu_state.conf & 0x1) #define MMU_CONF_R ((mmu_state.conf & 0x2) >> 1) #define MMU_CONF_C ((mmu_state.conf & 0x4) >> 1) @@ -353,4 +355,4 @@ t_stat mmu_show_sdt(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat mmu_show_sdc(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat mmu_show_pdc(FILE *st, UNIT *uptr, int32 val, CONST void *desc); -#endif /* _3B2_1000_MMU_H_ */ +#endif /* _3B2_REV3_MMU_H_ */ diff --git a/3B2/3b2_rev3_sys.c b/3B2/3b2_rev3_sys.c index 8c32ba46..ce1645d2 100644 --- a/3B2/3b2_rev3_sys.c +++ b/3B2/3b2_rev3_sys.c @@ -1,6 +1,6 @@ -/* 3b2_rev3_sys.c: AT&T 3B2/600G system definition +/* 3b2_rev3_sys.c: Version 3 (3B2/700) System Definition - Copyright (c) 2020, Seth J. Morabito + Copyright (c) 2020-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -41,7 +41,7 @@ #include "3b2_stddev.h" #include "3b2_timer.h" -char sim_name[] = "AT&T 3B2/600G"; +char sim_name[] = "AT&T 3B2/700"; DEVICE *sim_devices[] = { &cpu_dev, diff --git a/3B2/3b2_rev3_timer.h b/3B2/3b2_rev3_timer.h deleted file mode 100644 index c4379763..00000000 --- a/3B2/3b2_rev3_timer.h +++ /dev/null @@ -1,79 +0,0 @@ -/* 3b2_rev2_stddev.h: 82C54 Interval Timer. - - Copyright (c) 2021, Seth J. Morabito - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, copy, - modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - Except as contained in this notice, the name of the author shall - not be used in advertising or otherwise to promote the sale, use or - other dealings in this Software without prior written authorization - from the author. -*/ - -#ifndef _3B2_REV3_TIMER_H_ -#define _3B2_REV3_TIMER_H_ - -#include "3b2_defs.h" - -/* Timer definitions */ -#define TIMER_STP_US 1 -#define tmrnum u3 -#define tmr up7 - -#define TIMER_REG_DIVA 0x03 -#define TIMER_REG_DIVB 0x07 -#define TIMER_REG_DIVC 0x0b -#define TIMER_REG_CTRL 0x0f -#define TIMER_CLR_LATCH 0x13 - -#define TIMER_MODE(ctr) (((ctr->ctrl) >> 1) & 7) -#define TIMER_RW(ctr) (((ctr->ctrl) >> 4) & 3) - -#define CLK_LATCH 0 -#define CLK_LSB 1 -#define CLK_MSB 2 -#define CLK_LMB 3 - -struct timer_ctr { - uint16 divider; - uint16 val; - uint8 ctrl_latch; - uint16 cnt_latch; - uint8 ctrl; - t_bool r_lmb; - t_bool w_lmb; - t_bool enabled; - t_bool r_ctrl_latch; - t_bool r_cnt_latch; -}; - -/* 82C54 Timer */ -t_stat timer_reset(DEVICE *dptr); -uint32 timer_read(uint32 pa, size_t size); -void timer_write(uint32 pa, uint32 val, size_t size); -t_stat timer0_svc(UNIT *uptr); -t_stat timer1_svc(UNIT *uptr); -t_stat timer2_svc(UNIT *uptr); -/* void timer_tick(uint8 ctrnum); */ -void timer_disable(uint8 ctrnum); -void timer_enable(uint8 ctrnum); - -#endif /* _3B2_REV3_TIMER_H_ */ diff --git a/3B2/3b2_stddev.c b/3B2/3b2_stddev.c index 6d2dba9f..14989333 100644 --- a/3B2/3b2_stddev.c +++ b/3B2/3b2_stddev.c @@ -1,6 +1,6 @@ -/* 3b2_stddev.c: AT&T 3B2 miscellaneous system board devices. +/* 3b2_stddev.c: Miscellaneous System Board Devices - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -33,14 +33,15 @@ following 3B2 devices: - nvram Non-Volatile RAM - - tod MM58174A Real-Time-Clock - - flt Fault Register + - tod MM58174A and MM58274C Real-Time-Clock + - flt Fault Register (Rev 3 only) */ #include "3b2_stddev.h" #include "3b2_cpu.h" #include "3b2_csr.h" +#include "3b2_timer.h" DEBTAB sys_deb_tab[] = { { "INIT", INIT_MSG, "Init" }, @@ -124,23 +125,19 @@ t_stat nvram_reset(DEVICE *dptr) const char *nvram_description(DEVICE *dptr) { - return "Non-volatile memory, used to store system state between boots.\n"; + return "Non-Volatile RAM.\n"; } t_stat nvram_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { - fprintf(st, - "The NVRAM holds system state between boots. On initial startup,\n" - "if no valid NVRAM file is attached, you will see the message:\n" - "\n" - " FW ERROR 1-01: NVRAM SANITY FAILURE\n" - " DEFAULT VALUES ASSUMED\n" - " IF REPEATED, CHECK THE BATTERY\n" - "\n" - "To avoid this message on subsequent boots, attach a new NVRAM file\n" - "with the SIMH command:\n" - "\n" - " sim> ATTACH NVRAM \n"); + fprintf(st, "Non-Volatile RAM\n\n"); + fprintf(st, "The %s device is a small battery-backed, non-volatile RAM\n", dptr->name); + fprintf(st, "used by the 3B2 to hold system configuration and diagnostic data.\n\n"); + fprintf(st, "In order for the simulator to keep track of this data while not\n"); + fprintf(st, "running, the %s device may be attached to a file, e.g.\n\n", dptr->name); + fprintf(st, " sim> ATTACH NVRAM \n"); + fprint_show_help(st, dptr); + fprint_reg_help(st, dptr); return SCPE_OK; } @@ -177,7 +174,6 @@ t_stat nvram_detach(UNIT *uptr) return r; } - uint32 nvram_read(uint32 pa, size_t size) { uint32 offset = pa - NVRBASE; @@ -229,25 +225,103 @@ void nvram_write(uint32 pa, uint32 val, size_t size) } /* - * MM58174A Time Of Day Clock + * MM58174A and MM58274C Time Of Day Clock. + * + * In addition to keeping track of time of day in tenths of seconds, + * this device is also used as the simulator's primary calibrated + * real-time clock. It operates at the speed of 100Hz, with every + * tenth step incrementing the time-of-day counter. */ +#define TOD_12H(TD) (((TD)->clkset & 1) == 0) +#define TOD_BCDH(V) (((V) / 10) & 0xf) +#define TOD_BCDL(V) (((V) % 10) & 0xf) +#define TOD_LYEAR(TD) ((TD)->lyear == 0) +#define TOD_LYEAR_INC(TD) \ + do { \ + (TD)->lyear = (((TD)->lyear + 1) & 0x3); \ + (TD)->clkset &= 3; \ + (TD)->clkset |= (TD)->lyear << 2; \ + } while(0) + +#define CTRL_DISABLE 0x4 +#define CLKSET_PM 0x2 +#define FLAG_DATA_CHANGED 0x4 +#define FLAG_INTERRUPT 0x1 +#define MIN_DIFF 5l +#define MAX_DIFF 157680000l + +#define CLK_DELAY 10000 /* 10 milliseconds per tick */ +#define CLK_TPS 100l /* 100 ticks per second */ + +static void tod_resync(UNIT *uptr); +static void tod_tick(UNIT *uptr); +static t_stat tod_svc(UNIT *uptr); +static t_bool tod_enabled; + +int32 tmr_poll = CLK_DELAY; +int32 tmxr_poll = CLK_DELAY; + UNIT tod_unit = { - UDATA(NULL, UNIT_FIX+UNIT_BINK, sizeof(TOD_DATA)) + UDATA(&tod_svc, UNIT_FIX|UNIT_BINK|UNIT_IDLE, sizeof(TOD_DATA)), CLK_DELAY +}; + +REG tod_reg[] = { + { DRDATAD(POLL, tmr_poll, 24, "Calibrated poll interval") }, + { 0 } }; DEVICE tod_dev = { - "TOD", &tod_unit, NULL, NULL, + "TOD", &tod_unit, tod_reg, NULL, 1, 16, 8, 4, 16, 32, NULL, NULL, &tod_reset, NULL, &tod_attach, &tod_detach, - NULL, 0, 0, sys_deb_tab, NULL, NULL, + NULL, DEV_DEBUG, 0, sys_deb_tab, NULL, NULL, &tod_help, NULL, NULL, &tod_description }; +/* + * Attempt to re-sync the TOD by catching up (if lagging) and updating + * the current time stored in the TOD state. + * + * Because this process may be expensive when catching up following a + * very long time without the simulator running, the process will + * short-circuit if the delta is longer than 5 years, or if no + * previous time was recorded. + */ +static void tod_resync(UNIT *uptr) +{ + TOD_DATA *td; + time_t delta; + uint32 catchup_ticks; + + if (!(uptr->flags & UNIT_ATT) || uptr->filebuf == NULL) { + return; + } + + td = (TOD_DATA *)uptr->filebuf; + + if (td->time > 0) { + delta = time(NULL) - td->time; + if (delta > MIN_DIFF && delta < MAX_DIFF) { + catchup_ticks = (uint32) delta * CLK_TPS; + sim_debug(EXECUTE_MSG, &tod_dev, + "Catching up with a delta of %ld seconds (%d ticks).\n", + delta, catchup_ticks); + while (catchup_ticks-- > 0) { + tod_tick(&tod_unit); + } + } + } + + td->time = time(NULL); +} + t_stat tod_reset(DEVICE *dptr) { + int32 t; + if (tod_unit.filebuf == NULL) { tod_unit.filebuf = calloc(sizeof(TOD_DATA), 1); if (tod_unit.filebuf == NULL) { @@ -255,6 +329,14 @@ t_stat tod_reset(DEVICE *dptr) } } + /* We start in a running state */ + tod_enabled = TRUE; + + t = sim_rtcn_init_unit(&tod_unit, tod_unit.wait, TMR_CLK); + sim_activate_after(&tod_unit, 1000000/CLK_TPS); + tmr_poll = t; + tmxr_poll = t; + return SCPE_OK; } @@ -288,124 +370,191 @@ t_stat tod_detach(UNIT *uptr) return r; } -/* - * Re-set the tod_data registers based on the current simulated time. - */ -void tod_resync() +static t_stat tod_svc(UNIT *uptr) { - struct timespec now; - struct tm tm; - time_t sec; - TOD_DATA *td = (TOD_DATA *)tod_unit.filebuf; + TOD_DATA *td = (TOD_DATA *)uptr->filebuf; + int32 t; - sim_rtcn_get_time(&now, TMR_CLK); - sec = now.tv_sec - td->delta; + /* Re-sync the recorded system time once every second */ + if (tod_enabled) { + tod_tick(uptr); - /* Populate the tm struct based on current sim_time */ - tm = *gmtime(&sec); - - td->tsec = 0; - td->unit_sec = tm.tm_sec % 10; - td->ten_sec = tm.tm_sec / 10; - td->unit_min = tm.tm_min % 10; - td->ten_min = tm.tm_min / 10; - td->unit_hour = tm.tm_hour % 10; - td->ten_hour = tm.tm_hour / 10; - /* tm struct stores as 0-11, tod struct as 1-12 */ - td->unit_mon = (tm.tm_mon + 1) % 10; - td->ten_mon = (tm.tm_mon + 1) / 10; - td->unit_day = tm.tm_mday % 10; - td->ten_day = tm.tm_mday / 10; - td->year = 1 << ((tm.tm_year - 1) % 4); -} - -/* - * Re-calculate the delta between real time and simulated time - */ -void tod_update_delta() -{ - struct timespec now; - struct tm tm = {0}; - time_t ssec; - TOD_DATA *td = (TOD_DATA *)tod_unit.filebuf; - sim_rtcn_get_time(&now, TMR_CLK); - - /* Let the host decide if it is DST or not */ - tm.tm_isdst = -1; - - /* Compute the simulated seconds value */ - tm.tm_sec = (td->ten_sec * 10) + td->unit_sec; - tm.tm_min = (td->ten_min * 10) + td->unit_min; - tm.tm_hour = (td->ten_hour * 10) + td->unit_hour; - /* tm struct stores as 0-11, tod struct as 1-12 */ - tm.tm_mon = ((td->ten_mon * 10) + td->unit_mon) - 1; - tm.tm_mday = (td->ten_day * 10) + td->unit_day; - - /* We're forced to do this weird arithmetic because the TOD chip - * used by the 3B2 does not store the year. It only stores the - * offset from the nearest leap year. */ - switch(td->year) { - case 1: /* Leap Year - 3 */ - tm.tm_year = 85; - break; - case 2: /* Leap Year - 2 */ - tm.tm_year = 86; - break; - case 4: /* Leap Year - 1 */ - tm.tm_year = 87; - break; - case 8: /* Leap Year */ - tm.tm_year = 88; - break; - default: - break; + if (td->tsec == 0) { + tod_resync(uptr); + } } - ssec = mktime(&tm); - td->delta = (int32)(now.tv_sec - ssec); + t = sim_rtcn_calb(CLK_TPS, TMR_CLK); + sim_activate_after(uptr, 1000000/CLK_TPS); + tmr_poll = t; + tmxr_poll = t; + AIO_SET_INTERRUPT_LATENCY(tmr_poll * CLK_TPS); + return SCPE_OK; } +/* + * The MM58174 and MM58274 consist of a set of failry "dumb" roll-over + * counters. In an ideal world, we'd just look at the real system time + * and translate that into whatever read the host needs. + * Unfortunately, since the Day-of-Week and Leap Year registers are + * totally independent of whatever the "real" date and time should be, + * this doesn't map very well, and DGMON hardware diagnostics fail. + * + * Instead, we model the behavior of the chip accurately here. Each + * rollover is cascaded to the next highest register, using the same + * logic the chip uses. + */ +static void tod_tick(UNIT *uptr) +{ + TOD_DATA *td = (TOD_DATA *)uptr->filebuf; + + if (++td->tsec > 99) { + td->tsec = 0; + td->flags |= FLAG_DATA_CHANGED; + if (++td->sec > 59) { + td->sec = 0; + if (++td->min > 59) { + td->min = 0; + td->hour++; + + /* 12-hour clock cycles from 1-12, 24-hour clock cycles from 00-23 */ + if (TOD_12H(td)) { + if (td->hour == 12) { + td->clkset ^= CLKSET_PM; + } + if (td->hour > 12) { + td->hour = 1; + } + } else if (td->hour > 23) { + td->hour = 0; + } + + if ((TOD_12H(td) && td->hour == 12) || (!TOD_12H(td) && td->hour == 0)) { + /* Manage day-of-week */ + td->wday++; + if (td->wday > 7) { + td->wday = 1; + } + td->day++; + switch(td->mon) { + case 2: /* FEB */ + if (TOD_LYEAR(td)) { + if (td->day > 29) { + td->day = 1; + } + } else { + if (td->day > 28) { + td->day = 1; + } + } + break; + case 4: /* APR */ + case 6: /* JUN */ + case 9: /* SEP */ + case 11: /* NOV */ + if (td->day > 30) { + td->day = 1; + } + break; + case 1: /* JAN */ + case 3: /* MAR */ + case 5: /* MAY */ + case 7: /* JUL */ + case 8: /* AUG */ + case 10: /* OCT */ + case 12: /* DEC */ + if (td->day > 31) { + td->day = 1; + } + break; + } + if (td->day == 1) { + if (++td->mon > 12) { + td->mon = 1; + TOD_LYEAR_INC(td); + if (++td->year > 99) { + td->year = 0; + } + } + } + } + } + } + } +} + + uint32 tod_read(uint32 pa, size_t size) { - uint8 reg; + uint8 reg, val; TOD_DATA *td = (TOD_DATA *)(tod_unit.filebuf); - tod_resync(); - - reg = pa - TODBASE; + reg = pa & 0xfc; switch(reg) { - case 0x04: /* 1/10 Sec */ - return td->tsec; - case 0x08: /* 1 Sec */ - return td->unit_sec; - case 0x0c: /* 10 Sec */ - return td->ten_sec; - case 0x10: /* 1 Min */ - return td->unit_min; - case 0x14: /* 10 Min */ - return td->ten_min; - case 0x18: /* 1 Hour */ - return td->unit_hour; - case 0x1c: /* 10 Hour */ - return td->ten_hour; - case 0x20: /* 1 Day */ - return td->unit_day; - case 0x24: /* 10 Day */ - return td->ten_day; - case 0x28: /* Day of Week */ - return td->wday; - case 0x2c: /* 1 Month */ - return td->unit_mon; - case 0x30: /* 10 Month */ - return td->ten_mon; - case 0x34: /* Year */ - return td->year; +#if defined(REV3) + case TOD_CTRL: + val = td->flags; + td->flags &= ~(FLAG_DATA_CHANGED); + break; +#endif + case TOD_TSEC: + val = TOD_BCDH(td->tsec); + break; + case TOD_1SEC: + val = TOD_BCDL(td->sec); + break; + case TOD_10SEC: + val = TOD_BCDH(td->sec); + break; + case TOD_1MIN: + val = TOD_BCDL(td->min); + break; + case TOD_10MIN: + val = TOD_BCDH(td->min); + break; + case TOD_1HOUR: + val = TOD_BCDL(td->hour); + break; + case TOD_10HOUR: + val = TOD_BCDH(td->hour); + break; + case TOD_1DAY: + val = TOD_BCDL(td->day); + break; + case TOD_10DAY: + val = TOD_BCDH(td->day); + break; + case TOD_1MON: + val = TOD_BCDL(td->mon); + break; + case TOD_10MON: + val = TOD_BCDH(td->mon); + break; + case TOD_WDAY: + val = td->wday; + break; + case TOD_1YEAR: +#if defined(REV3) + val = TOD_BCDL(td->year); +#else + val = td->lyear; +#endif + break; +#if defined(REV3) + case TOD_10YEAR: + val = TOD_BCDH(td->year); + break; + case TOD_SET_INT: + val = td->clkset; + break; +#endif default: + val = 0; break; } - return 0; + return val; } void tod_write(uint32 pa, uint32 val, size_t size) @@ -413,52 +562,83 @@ void tod_write(uint32 pa, uint32 val, size_t size) uint32 reg; TOD_DATA *td = (TOD_DATA *)(tod_unit.filebuf); - reg = pa - TODBASE; + /* reg = pa - TODBASE; */ + reg = pa & 0xfc; switch(reg) { - case 0x04: /* 1/10 Sec */ - td->tsec = (uint8) val; - break; - case 0x08: /* 1 Sec */ - td->unit_sec = (uint8) val; - break; - case 0x0c: /* 10 Sec */ - td->ten_sec = (uint8) val; - break; - case 0x10: /* 1 Min */ - td->unit_min = (uint8) val; - break; - case 0x14: /* 10 Min */ - td->ten_min = (uint8) val; - break; - case 0x18: /* 1 Hour */ - td->unit_hour = (uint8) val; - break; - case 0x1c: /* 10 Hour */ - td->ten_hour = (uint8) val; - break; - case 0x20: /* 1 Day */ - td->unit_day = (uint8) val; - break; - case 0x24: /* 10 Day */ - td->ten_day = (uint8) val; - break; - case 0x28: /* Day of Week */ - td->wday = (uint8) val; - break; - case 0x2c: /* 1 Month */ - td->unit_mon = (uint8) val; - break; - case 0x30: /* 10 Month */ - td->ten_mon = (uint8) val; - break; - case 0x34: /* Year */ - td->year = (uint8) val; - break; - case 0x38: - if (val & 1) { - tod_update_delta(); +#if defined(REV3) + case TOD_CTRL: + td->ctrl = (uint8) val; + if (val & CTRL_DISABLE) { + tod_enabled = FALSE; + td->tsec = 0; + } else { + tod_enabled = TRUE; } +#else + case TOD_TEST: + /* test mode */ +#endif + break; + case TOD_TSEC: + td->tsec = (uint8) val * 10; + break; + case TOD_1SEC: + td->sec = ((td->sec / 10) * 10) + (uint8) val; + break; + case TOD_10SEC: + td->sec = ((uint8) val * 10) + (td->sec % 10); + break; + case TOD_1MIN: + td->min = ((td->min / 10) * 10) + (uint8) val; + break; + case TOD_10MIN: + td->min = ((uint8) val * 10) + (td->min % 10); + break; + case TOD_1HOUR: + td->hour = ((td->hour / 10) * 10) + (uint8) val; + break; + case TOD_10HOUR: + td->hour = ((uint8) val * 10) + (td->hour % 10); + break; + case TOD_1DAY: + td->day = ((td->day / 10) * 10) + (uint8) val; + break; + case TOD_10DAY: + td->day = ((uint8) val * 10) + (td->day % 10); + break; + case TOD_1MON: + td->mon = ((td->mon / 10) * 10) + (uint8) val; + break; + case TOD_10MON: + td->mon = ((uint8) val * 10) + (td->mon % 10); + break; + case TOD_1YEAR: +#if defined(REV3) + td->year = ((td->year / 10) * 10) + (uint8) val; +#else + td->lyear = (uint8) val; +#endif + break; +#if defined(REV3) + case TOD_10YEAR: + td->year = ((uint8) val * 10) + (td->year % 10); + break; + case TOD_SET_INT: + td->clkset = (uint8) val; + if (!TOD_12H(td)) { + /* The AM/PM indicator is always 0 if not in 12H mode */ + td->clkset &= ~(CLKSET_PM); + } + td->lyear = (val >> 2) & 3; + break; +#else + case TOD_STARTSTOP: + tod_enabled = val & 1; + break; +#endif + case TOD_WDAY: + td->wday = (uint8)val & 0x7; break; default: break; @@ -467,27 +647,33 @@ void tod_write(uint32 pa, uint32 val, size_t size) const char *tod_description(DEVICE *dptr) { - return "Time-of-Day clock, used to store system time between boots.\n"; +#if defined(REV3) + return("MM58274C real time clock"); +#else + return("MM58174A real time clock"); +#endif } t_stat tod_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { - fprintf(st, - "The TOD is a battery-backed time-of-day clock that holds system\n" - "time between boots. In order to store the time, a file must be\n" - "attached to the TOD device with the SIMH command:\n" - "\n" - " sim> ATTACH TOD \n" - "\n" - "On a newly installed System V Release 3 UNIX system, no system\n" - "time will be stored in the TOD clock. In order to set the system\n" - "time, run the following command from within UNIX (as root):\n" - "\n" - " # sysadm datetime\n" - "\n" - "On subsequent boots, the correct system time will restored from\n" - "from the TOD.\n"); + char dname[10]; +#if defined(REV3) + snprintf(dname, 10, "MM58274C"); +#else + snprintf(dname, 10, "MM58174A"); +#endif + + fprintf(st, "%s Time-Of-Day Clock (%s)\n\n", dname, dptr->name); + fprintf(st, "The %s controller simulates a National Semiconductor %s\n", dptr->name, dname); + fprintf(st, "real time clock. This clock keeps track of the current system time\n"); + fprintf(st, "and date.\n\n"); + fprintf(st, "In order to preserve simulated calendar time between simulator runs,\n"); + fprintf(st, "the %s clock may be attached to a file which stores its state while\n", dptr->name); + fprintf(st, "the simulator is not running, e.g.:\n\n"); + fprintf(st, " sim> ATTACH TOD \n"); + fprint_show_help(st, dptr); + fprint_reg_help(st, dptr); return SCPE_OK; } @@ -500,44 +686,130 @@ t_stat tod_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr * 0x4C000 and 0x4D000. These latch state of the last address to cause * a CPU fault. * - * Bits 00-25: Physical memory address bits 00-25 + * Bits 00-25: Physical memory address bits + * + * Fault Register 2 does double duty. It actually consists of four + * words, each of which maps to a memory slot on the system board. If + * occupied, it records the size of memory equipped in the slot, as + * well as information about any memory faults. + * + * + * Bits Active Purpose + * ---------------------------------------------------- + * 31-26 H Upper Halfword ECC Syndrome Bits + * 25-20 H Lower Halfword ECC Syndeome Bits + * 19 L I/O Bus or BUB master on Fault + * 18 H Invert low addr bit 2 (DWORD1) + * 17 H Decrement low addr by 4 + * 16 L I/O Bus Master on Fault + * 15 L CPU accessing I/O Peripheral + * 14 L BUB Slot 0 master on fault + * 13 L CPU Accessing BUB Peripheral + * 12-11 H BUB peripheral accessed by CPU + * 10 L BUB Slot 1 master on fault + * 9 L BUB Slot 2 master on fault + * 8 L BUB Slot 3 master on fault + * 7-3 N/A Not Used + * 2 L Memory Equipped + * 1-0 H Equipped Memory Size + * */ -uint32 flt_1 = 0; -uint32 flt_2 = 0; +uint32 flt[2] = {0, 0}; UNIT flt_unit = { - UDATA(NULL, UNIT_FIX+UNIT_BINK, 8) + UDATA(NULL, UNIT_FIX+UNIT_BINK, 64) }; REG flt_reg[] = { + { HRDATAD(FLT1, flt[0], 32, "Fault Register 1") }, + { HRDATAD(FLT2, flt[1], 32, "Fault Register 2") }, { NULL } }; DEVICE flt_dev = { "FLT", &flt_unit, flt_reg, NULL, - 1, 16, 8, 4, 16, 32, + 1, 16, 32, 1, 16, 32, NULL, NULL, NULL, NULL, NULL, NULL, NULL, DEV_DEBUG, 0, sys_deb_tab, NULL, NULL, - NULL, NULL, NULL, - NULL + &flt_help, NULL, NULL, + &flt_description }; +/* + * Return the configured memory size for a given backplane location. + */ +static uint32 mem_size(uint8 slot) { + switch(MEM_SIZE) { + case MSIZ_8M: + if (slot <= 1) { + return MEM_EQP|MEM_4M; + } else { + return 0; + } + case MSIZ_16M: + return MEM_EQP|MEM_4M; + case MSIZ_32M: + if (slot <= 1) { + return MEM_EQP|MEM_16M; + } else { + return 0; + } + case MSIZ_64M: + return MEM_EQP|MEM_16M; + default: + return 0; + } +} + uint32 flt_read(uint32 pa, size_t size) { - sim_debug(READ_MSG, &flt_dev, - "[%08x] Read from FLT Register at %x\n", - R[NUM_PC], pa); - return 0; + sim_debug(EXECUTE_MSG, &flt_dev, + "Read from FLT Register at %x\n", + pa); + + switch(pa) { + case FLTLBASE: + return flt[0]; + case FLTHBASE: + return (flt[1] & FLT_MSK) | mem_size(0); + case FLTHBASE + 4: + return (flt[1] & FLT_MSK) | mem_size(1); + case FLTHBASE + 8: + return (flt[1] & FLT_MSK) | mem_size(2); + case FLTHBASE + 12: + return (flt[1] & FLT_MSK) | mem_size(3); + default: + sim_debug(EXECUTE_MSG, &flt_dev, + "Read from FLT Register at %x: FAILURE, NO DATA!!!!\n", + pa); + return 0; + } } 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); + sim_debug(EXECUTE_MSG, &flt_dev, + "Write to FLT Register at %x (val=%x)\n", + pa, val); + return; } +t_stat flt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf(st, "Fault Register\n\n"); + fprintf(st, "The %s device is a pair of 32-bit registers that hold information about\n", dptr->name); + fprintf(st, "system memory faults.\n"); + fprint_show_help(st, dptr); + fprint_reg_help(st, dptr); + return SCPE_OK; +} + +const char *flt_description(DEVICE *dptr) +{ + return "Fault Register"; +} + #endif diff --git a/3B2/3b2_stddev.h b/3B2/3b2_stddev.h index c709fd00..266bdb5e 100644 --- a/3B2/3b2_stddev.h +++ b/3B2/3b2_stddev.h @@ -1,6 +1,6 @@ -/* 3b2_stddev.h: AT&T 3B2 miscellaneous system board devices. +/* 3b2_stddev.h: Miscellaneous System Board Devices - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -44,39 +44,95 @@ const char *nvram_description(DEVICE *dptr); t_stat nvram_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); void nvram_write(uint32 pa, uint32 val, size_t size); -/* TOD */ typedef struct tod_data { - int32 delta; /* Delta between simulated time and real time (sec.) */ - uint8 tsec; /* 1/10 seconds */ - uint8 unit_sec; /* 1's column seconds */ - uint8 ten_sec; /* 10's column seconds */ - uint8 unit_min; /* 1's column minutes */ - uint8 ten_min; /* 10's column minutes */ - uint8 unit_hour; /* 1's column hours */ - uint8 ten_hour; /* 10's column hours */ - uint8 unit_day; /* 1's column day of month */ - uint8 ten_day; /* 10's column day of month */ - uint8 wday; /* Day of week (0-6) */ - uint8 unit_mon; /* 1's column month */ - uint8 ten_mon; /* 10's column month */ - uint8 year; /* 1, 2, 4, 8 shift register */ - uint8 pad[3]; /* Padding to 32 bytes */ + time_t time; /* System time */ + + uint8 ctrl; /* Control register (Rev 3 only) */ + uint8 flags; /* Data Changed & Interrutpt Flags (Rev 3 only) */ + uint8 clkset; /* Clock / Setting register (Rev 3 only) */ + + uint8 tsec; /* 1/100th seconds, 00-99 */ + uint8 sec; /* Seconds, 00-59 */ + uint8 min; /* Minutes, 00-59 */ + uint8 hour; /* Hours, 00-23 */ + uint8 day; /* Days, 00-27, 28, 29, or 30 */ + uint8 mon; /* Months, 00-11 */ + uint8 year; /* Years, 00-99 (Rev 3 only) */ + uint8 wday; /* Day of Week, 0-6 */ + uint8 lyear; /* Years since last leap year */ } TOD_DATA; -void tod_resync(); +#if defined(REV2) + +#define TOD_TEST 0x00 +#define TOD_TSEC 0x04 +#define TOD_1SEC 0x08 +#define TOD_10SEC 0x0c +#define TOD_1MIN 0x10 +#define TOD_10MIN 0x14 +#define TOD_1HOUR 0x18 +#define TOD_10HOUR 0x1c +#define TOD_1DAY 0x20 +#define TOD_10DAY 0x24 +#define TOD_WDAY 0x28 +#define TOD_1MON 0x2c +#define TOD_10MON 0x30 +#define TOD_1YEAR 0x34 +#define TOD_STARTSTOP 0x38 +#define TOD_INT 0x3c + +#else + +#define TOD_FLAG_CHG 0x08 +#define TOD_FLAG_IRQ 0x01 + +#define TOD_CTRL 0x00 +#define TOD_TSEC 0x04 +#define TOD_1SEC 0x08 +#define TOD_10SEC 0x0c +#define TOD_1MIN 0x10 +#define TOD_10MIN 0x14 +#define TOD_1HOUR 0x18 +#define TOD_10HOUR 0x1c +#define TOD_1DAY 0x20 +#define TOD_10DAY 0x24 +#define TOD_1MON 0x28 +#define TOD_10MON 0x2c +#define TOD_1YEAR 0x30 +#define TOD_10YEAR 0x34 +#define TOD_WDAY 0x38 +#define TOD_SET_INT 0x3c + +#endif + void tod_update_delta(); t_stat tod_reset(DEVICE *dptr); t_stat tod_attach(UNIT *uptr, CONST char *cptr); t_stat tod_detach(UNIT *uptr); -const char *tod_description(DEVICE *dptr); t_stat tod_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *tod_description(DEVICE *dptr); uint32 tod_read(uint32 pa, size_t size); void tod_write(uint32, uint32 val, size_t size); +/* Global symbols */ + +extern int32 tmxr_poll; + #if defined(REV3) /* Fault Register */ + +#define FLT_MSK 0xffffff00 +#define MEM_EQP 0x4 +#define MEM_4M 0x2 +#define MEM_16M 0x3 + uint32 flt_read(uint32 pa, size_t size); void flt_write(uint32 pa, uint32 val, size_t size); -#endif +t_stat flt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *flt_description(DEVICE *dptr); + +extern uint32 flt[2]; + +#endif /* defined(REV3) */ #endif /* _3B2_STDDEV_H_ */ diff --git a/3B2/3b2_sys.c b/3B2/3b2_sys.c index e826d46c..68a619c6 100644 --- a/3B2/3b2_sys.c +++ b/3B2/3b2_sys.c @@ -1,6 +1,6 @@ -/* 3b2_sys.c: AT&T 3B2 common system definitions +/* 3b2_sys.c: Common System Definition - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -53,25 +53,57 @@ const char *sim_stop_messages[SCPE_BASE] = { "Simulator Error" }; +/* + * ROM and Binary loader + * + * -r load ROM + * -o for memory, specify origin + * + */ t_stat sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) { + t_stat r; int32 i; - uint32 addr = 0; + uint32 origin = 0, limit = 0; int32 cnt = 0; - if ((*cptr != 0) || (flag != 0)) { - return SCPE_ARG; + if (flag) { + return sim_messagef(SCPE_NOFNC, "Command not implemented."); } - addr = R[NUM_PC]; - - while ((i = getc (fileref)) != EOF) { - pwrite_b(addr, (uint8)i); - addr++; + if (sim_switches & SWMASK('R')) { + origin = ROM_BASE; + limit = ROM_BASE + ROM_SIZE; + } else { + origin = 0; + limit = (uint32) cpu_unit.capac; + if (sim_switches & SWMASK('O')) { + origin = (uint32) get_uint(cptr, 16, 0xffffffff, &r); + if (r != SCPE_OK) { + return SCPE_ARG; + } + } + } + + while ((i = Fgetc (fileref)) != EOF) { + if (origin >= limit) { + return SCPE_NXM; + } + if (sim_switches & SWMASK('R')) { + pwrite_b_rom(origin, (uint8)i); + } else { + pwrite_b(origin, (uint8)i, BUS_CPU); + } + origin++; cnt++; } - printf ("%d Bytes loaded.\n", cnt); + if (sim_switches & SWMASK('R')) { + rom_loaded = TRUE; + sim_messagef(SCPE_OK, "%d bytes loaded into ROM\n", cnt); + } else { + sim_messagef(SCPE_OK, "%d bytes loaded at address 0x%08x\n", cnt, origin - cnt); + } return SCPE_OK; } diff --git a/3B2/3b2_sys.h b/3B2/3b2_sys.h index d3fa72e2..7eb9f174 100644 --- a/3B2/3b2_sys.h +++ b/3B2/3b2_sys.h @@ -1,6 +1,6 @@ -/* 3b2_rev2_sys.h: AT&T 3B2 Rev 2 (Model 400) system header +/* 3b2_sys.h: Common System Definition - Copyright (c) 2017, Seth J. Morabito + Copyright (c) 2017-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/3B2/3b2_rev3_timer.c b/3B2/3b2_timer.c similarity index 55% rename from 3B2/3b2_rev3_timer.c rename to 3B2/3b2_timer.c index 085a7ffd..6c74b540 100644 --- a/3B2/3b2_rev3_timer.c +++ b/3B2/3b2_timer.c @@ -1,6 +1,6 @@ -/* 3b2_rev3_timer.c: 82C54 Interval Timer. +/* 3b2_timer.c: 8253/82C54 Interval Timer - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -29,9 +29,7 @@ */ /* - * 82C54 Timer. - * - * 82C54 (Rev3) Timer IC has three interval timers, which we treat + * The 8253/82C54 Timer IC has three interval timers, which we treat * here as three units. * * In the 3B2, the three timers are assigned specific purposes: @@ -64,10 +62,10 @@ * Implementaiton Notes * ==================== * - * In general, no attempt has been made to create an accurate - * emulation of the 82C54 timer. This implementation is truly built - * for the 3B2, and even more specifically for System V Unix, which is - * the only operating system ever to have been ported to the 3B2. + * In general, no attempt has been made to create a truly accurate + * simulation of the 8253/82C54 timer. This implementation is built + * for the 3B2, and even more specifically to pass System V timer + * "Sanity/Interval Timer" diagnostics. * * - The Bus Timeout Timer is not implemented other than a stub that * is designed to pass hardware diagnostics. The simulator IO @@ -77,9 +75,8 @@ * - The System Sanity Timer is also not implemented other than a * stub to pass diagnostics. * - * - The main Unix Interval Timer is implemented as a true SIMH clock - * when set up for the correct mode. In other modes, it likewise - * implements a stub designed to pass diagnostics. + * - The main Unix Interval Timer is more fully implemented, because + * it drives system interrupts in System V UNIX. */ #include "3b2_cpu.h" @@ -87,18 +84,50 @@ #include "3b2_defs.h" #include "3b2_timer.h" +#define MIN_DIVIDER 50 + +#if defined (REV3) +#define QUICK_DELAY 10 +#else +#define QUICK_DELAY 100 +#endif + +#define DELAY_US(C,N) ((TIMER_MODE(C) == 3) ? \ + (TIME_BASE[(N)] * (C)->divider) / 2 : \ + TIME_BASE[(N)] * (C)->divider) + +#if defined(REV3) +/* Microseconds per step (Version 3 system board): + * + * Timer 0: 10KHz time base + * Timer 1: 100KHz time base + * Timer 2: 500KHz time base + */ +static uint32 TIME_BASE[3] = { + 100, 10, 1 +}; +#else +/* Microseconds per step (Version 2 system board): + * + * Timer 0: 100Khz time base + * Timer 1: 100Khz time base + * Timer 2: 500Khz time base + */ +static uint32 TIME_BASE[3] = { + 10, 10, 2 +}; +#endif + struct timer_ctr TIMERS[3]; -int32 tmxr_poll = 16667; - UNIT timer_unit[] = { - { UDATA(&timer0_svc, 0, 0) }, - { UDATA(&timer1_svc, UNIT_IDLE, 0) }, - { UDATA(&timer2_svc, 0, 0) }, + { UDATA(&tmr_svc, UNIT_IDLE, 0) }, + { UDATA(&tmr_svc, UNIT_IDLE, 0) }, + { UDATA(&tmr_svc, UNIT_IDLE, 0) }, { NULL } }; -UNIT *timer_clk_unit = &timer_unit[1]; +UNIT *tmr_int_unit = &timer_unit[3]; REG timer_reg[] = { { HRDATAD(DIV0, TIMERS[0].divider, 16, "Divider (0)") }, @@ -114,11 +143,32 @@ REG timer_reg[] = { }; DEVICE timer_dev = { - "TIMER", timer_unit, timer_reg, NULL, - 1, 16, 8, 4, 16, 32, - NULL, NULL, &timer_reset, - NULL, NULL, NULL, NULL, - DEV_DEBUG, 0, sys_deb_tab + "TMR", + timer_unit, + timer_reg, + NULL, + 3, + 16, + 8, + 4, + 16, + 32, + NULL, + NULL, + &timer_reset, + NULL, + NULL, + NULL, + NULL, + DEV_DEBUG, + 0, + sys_deb_tab, + NULL, + NULL, + &tmr_help, + NULL, + NULL, + &tmr_description }; t_stat timer_reset(DEVICE *dptr) { @@ -126,164 +176,111 @@ t_stat timer_reset(DEVICE *dptr) { memset(&TIMERS, 0, sizeof(struct timer_ctr) * 3); + /* Store the timer/counter number in the UNIT */ for (i = 0; i < 3; i++) { - timer_unit[i].tmrnum = i; - timer_unit[i].tmr = (void *)&TIMERS[i]; + timer_unit[i].u3 = i; } - /* TODO: I don't think this is right. Verify. */ - /* - if (!sim_is_running) { - t = sim_rtcn_init_unit(timer_clk_unit, TPS_CLK, TMR_CLK); - sim_activate_after_abs(timer_clk_unit, 1000000 / t); - } - */ - return SCPE_OK; } +/* + * Inhibit or allow a timer externally. + */ +void timer_gate(uint8 ctrnum, t_bool inhibit) +{ + struct timer_ctr *ctr = &TIMERS[ctrnum]; + + if (inhibit) { + ctr->gate = FALSE; + sim_cancel(&timer_unit[ctrnum]); + } else { + ctr->gate = TRUE; + if (ctr->enabled && !sim_is_active(&timer_unit[ctrnum])) { + sim_activate_after(&timer_unit[ctrnum], DELAY_US(ctr, ctrnum)); + ctr->val--; + } + } +} + static void timer_activate(uint8 ctrnum) { - struct timer_ctr *ctr; + struct timer_ctr *ctr = &TIMERS[ctrnum]; - ctr = &TIMERS[ctrnum]; - - switch (ctrnum) { - case TIMER_SANITY: - 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--; + if (ctr->enabled && ctr->gate) { + if (ctr->divider < MIN_DIVIDER) { + /* If the timer delay is too short, we need to force a + very quick activation */ + sim_activate_abs(&timer_unit[ctrnum], QUICK_DELAY); } else { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] SANITY TIMER: Currently disabled, not starting\n", - R[NUM_PC]); + /* Otherwise, use a computed time in microseconds */ + sim_activate_after_abs(&timer_unit[ctrnum], DELAY_US(ctr, ctrnum)); } - break; - case TIMER_INTERVAL: - if ((csr_data & CSRITIM) == 0) { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] INTERVAL TIMER: Activating after %d ms\n", - R[NUM_PC], ctr->val); - sim_activate_after_abs(&timer_unit[ctrnum], ctr->val); - ctr->val--; - } else { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] INTERVAL TIMER: Currently disabled, not starting\n", - R[NUM_PC]); - } - break; - case TIMER_BUS: - if ((csr_data & CSRITIMO) == 0) { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] BUS TIMER: Activating after %d steps\n", - R[NUM_PC], ctr->val); - sim_activate_abs(&timer_unit[ctrnum], (ctr->val - 2)); - ctr->val -= 2; - } else { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] BUS TIMER: Currently disabled, not starting\n", - R[NUM_PC]); - } - break; - default: - break; } } -void timer_enable(uint8 ctrnum) -{ - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] Enabling timer %d\n", - R[NUM_PC], ctrnum); - timer_activate(ctrnum); -} - -void timer_disable(uint8 ctrnum) -{ - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] Disabling timer %d\n", - R[NUM_PC], ctrnum); - sim_cancel(&timer_unit[ctrnum]); -} - /* - * Sanity Timer + * Sanity, Non-calibrated Interval, and Bus Timeout Timer service routine */ -t_stat timer0_svc(UNIT *uptr) +t_stat tmr_svc(UNIT *uptr) { - struct timer_ctr *ctr; + int32 ctr_num = uptr->u3; + uint32 usec_delay; + struct timer_ctr *ctr = &TIMERS[ctr_num]; - ctr = (struct timer_ctr *)uptr->tmr; - - if (ctr->enabled) { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] TIMER 0 COMPLETION.\n", - R[NUM_PC]); - if (!(csr_data & CSRISTIM)) { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] TIMER 0 NMI IRQ.\n", - R[NUM_PC]); - ctr->val = 0xffff; + if (ctr == NULL) { + return SCPE_SUB; + } + + /* If the timer isn't enabled, do nothing. */ + if (!ctr->enabled) { + return SCPE_OK; + } + + sim_debug(EXECUTE_MSG, &timer_dev, + "[tmr_svc] Handling timeout for ctr number %d\n", + ctr_num); + + switch (ctr_num) { + case TMR_SANITY: +#if defined (REV3) + if (!CSR(CSRISTIM) && TIMER_MODE(ctr) != 4) { cpu_nmi = TRUE; CSRBIT(CSRSTIMO, TRUE); CPU_SET_INT(INT_BUS_TMO); - } - } - - return SCPE_OK; -} - -/* - * Interval Timer - */ -t_stat timer1_svc(UNIT *uptr) -{ - struct timer_ctr *ctr; - int32 t; - - ctr = (struct timer_ctr *)uptr->tmr; - - if (ctr->enabled && !(csr_data & CSRITIM)) { - /* Fire the IPL 15 clock interrupt */ - CSRBIT(CSRCLK, TRUE); - CPU_SET_INT(INT_CLOCK); - } - - t = sim_rtcn_calb(TPS_CLK, TMR_CLK); - sim_activate_after_abs(uptr, 1000000/TPS_CLK); - tmxr_poll = t; - - return SCPE_OK; -} - -/* - * Bus Timeout Timer - */ -t_stat timer2_svc(UNIT *uptr) -{ - struct timer_ctr *ctr; - - ctr = (struct timer_ctr *)uptr->tmr; - - if (ctr->enabled && TIMER_RW(ctr) == CLK_LSB) { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] TIMER 2 COMPLETION.\n", - R[NUM_PC]); - if (!(csr_data & CSRITIMO)) { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] TIMER 2 IRQ.\n", - R[NUM_PC]); ctr->val = 0xffff; + } +#endif + break; + case TMR_INT: + if (!CSR(CSRITIM)) { + CSRBIT(CSRCLK, TRUE); + CPU_SET_INT(INT_CLOCK); + if (ctr->enabled && ctr->gate) { + usec_delay = DELAY_US(ctr, TMR_INT); + sim_debug(EXECUTE_MSG, &timer_dev, + "[tmr_svc] Re-triggering TMR_INT in %d usec\n", usec_delay); + sim_activate_after(uptr, usec_delay); + } + ctr->val = 0xffff; + } + break; + case TMR_BUS: +#if defined (REV3) + /* Only used during diagnostics */ + if (TIMER_RW(ctr) == CLK_LSB) { + sim_debug(EXECUTE_MSG, &timer_dev, + "[tmr_svc] BUS TIMER FIRING. Setting memory fault and interrupt\n"); CSRBIT(CSRTIMO, TRUE); CPU_SET_INT(INT_BUS_TMO); - /* Also trigger a bus abort */ cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + ctr->val = 0xffff; } +#endif + break; } + return SCPE_OK; } @@ -298,6 +295,9 @@ uint32 timer_read(uint32 pa, size_t size) ctrnum = (reg >> 2) & 0x3; ctr = &TIMERS[ctrnum]; + sim_debug(EXECUTE_MSG, &timer_dev, + "timer_read: reg=%x\n", reg); + switch (reg) { case TIMER_REG_DIVA: case TIMER_REG_DIVB: @@ -307,78 +307,60 @@ uint32 timer_read(uint32 pa, size_t size) switch (TIMER_RW(ctr)) { case CLK_LSB: retval = ctr_val & 0xff; - sim_debug(READ_MSG, &timer_dev, - "[%08x] [%d] [LSB] val=%d (0x%x)\n", - R[NUM_PC], ctrnum, retval, retval); break; case CLK_MSB: retval = (ctr_val & 0xff00) >> 8; - sim_debug(READ_MSG, &timer_dev, - "[%08x] [%d] [MSB] val=%d (0x%x)\n", - R[NUM_PC], ctrnum, retval, retval); break; case CLK_LMB: if (ctr->r_ctrl_latch) { ctr->r_ctrl_latch = FALSE; retval = ctr->ctrl_latch; - sim_debug(READ_MSG, &timer_dev, - "[%08x] [%d] [LATCH CTRL] val=%d (0x%x)\n", - R[NUM_PC], ctrnum, retval, retval); } else if (ctr->r_cnt_latch) { if (ctr->r_lmb) { ctr->r_lmb = FALSE; retval = (ctr->cnt_latch & 0xff00) >> 8; ctr->r_cnt_latch = FALSE; - sim_debug(READ_MSG, &timer_dev, - "[%08x] [%d] [LATCH DATA MSB] val=%d (0x%x)\n", - R[NUM_PC], ctrnum, retval, retval); } else { ctr->r_lmb = TRUE; retval = ctr->cnt_latch & 0xff; - sim_debug(READ_MSG, &timer_dev, - "[%08x] [%d] [LATCH DATA LSB] val=%d (0x%x)\n", - R[NUM_PC], ctrnum, retval, retval); } } else if (ctr->r_lmb) { ctr->r_lmb = FALSE; retval = (ctr_val & 0xff00) >> 8; - sim_debug(READ_MSG, &timer_dev, - "[%08x] [%d] [LMB - MSB] val=%d (0x%x)\n", - R[NUM_PC], ctrnum, retval, retval); } else { ctr->r_lmb = TRUE; retval = ctr_val & 0xff; - sim_debug(READ_MSG, &timer_dev, - "[%08x] [%d] [LMB - LSB] val=%d (0x%x)\n", - R[NUM_PC], ctrnum, retval, retval); } break; default: retval = 0; } - return retval; + break; case TIMER_REG_CTRL: - return ctr->ctrl; + retval = ctr->ctrl; + break; case TIMER_CLR_LATCH: /* Clearing the timer latch has a side-effect of also clearing pending interrupts */ CSRBIT(CSRCLK, FALSE); CPU_CLR_INT(INT_CLOCK); - return 0; + /* Acknowledge a clock tick */ + sim_rtcn_tick_ack(1, TMR_CLK); + retval = 0; + break; default: /* Unhandled */ - sim_debug(READ_MSG, &timer_dev, - "[%08x] UNHANDLED TIMER READ. ADDR=%08x\n", - R[NUM_PC], pa); - return 0; + retval = 0; + break; } + + return retval; } void handle_timer_write(uint8 ctrnum, uint32 val) { struct timer_ctr *ctr; - UNIT *unit = &timer_unit[ctrnum]; ctr = &TIMERS[ctrnum]; ctr->enabled = TRUE; @@ -387,17 +369,13 @@ void handle_timer_write(uint8 ctrnum, uint32 val) case CLK_LSB: ctr->divider = val & 0xff; ctr->val = ctr->divider; - sim_debug(WRITE_MSG, &timer_dev, - "[%08x] [%d] [LSB] val=%d (0x%x)\n", - R[NUM_PC], ctrnum, val & 0xff, val & 0xff); + sim_debug(EXECUTE_MSG, &timer_dev, "TIMER_WRITE: CTR=%d LSB=%02x\n", ctrnum, val & 0xff); timer_activate(ctrnum); break; case CLK_MSB: ctr->divider = (val & 0xff) << 8; ctr->val = ctr->divider; - sim_debug(WRITE_MSG, &timer_dev, - "[%08x] [%d] [MSB] val=%d (0x%x)\n", - R[NUM_PC], ctrnum, val & 0xff, val & 0xff); + sim_debug(EXECUTE_MSG, &timer_dev, "TIMER_WRITE: CTR=%d MSB=%02x\n", ctrnum, val & 0xff); timer_activate(ctrnum); break; case CLK_LMB: @@ -405,17 +383,13 @@ void handle_timer_write(uint8 ctrnum, uint32 val) ctr->w_lmb = FALSE; ctr->divider = (uint16) ((ctr->divider & 0x00ff) | ((val & 0xff) << 8)); ctr->val = ctr->divider; - sim_debug(WRITE_MSG, &timer_dev, - "[%08x] [%d] [LMB - MSB] val=%d (0x%x)\n", - R[NUM_PC], ctrnum, val & 0xff, val & 0xff); + sim_debug(EXECUTE_MSG, &timer_dev, "TIMER_WRITE: CTR=%d (L/M) MSB=%02x\n", ctrnum, val & 0xff); timer_activate(ctrnum); } else { ctr->w_lmb = TRUE; ctr->divider = (ctr->divider & 0xff00) | (val & 0xff); ctr->val = ctr->divider; - sim_debug(WRITE_MSG, &timer_dev, - "[%08x] [%d] [LMB - LSB] val=%d (0x%x)\n", - R[NUM_PC], ctrnum, val & 0xff, val & 0xff); + sim_debug(EXECUTE_MSG, &timer_dev, "TIMER_WRITE: CTR=%d (L/M) LSB=%02x\n", ctrnum, val & 0xff); } break; default: @@ -431,6 +405,9 @@ void timer_write(uint32 pa, uint32 val, size_t size) reg = (uint8) (pa - TIMERBASE); + sim_debug(EXECUTE_MSG, &timer_dev, + "timer_write: reg=%x val=%x\n", reg, val); + switch(reg) { case TIMER_REG_DIVA: handle_timer_write(0, val); @@ -444,9 +421,6 @@ void timer_write(uint32 pa, uint32 val, size_t size) case TIMER_REG_CTRL: ctrnum = (val >> 6) & 3; if (ctrnum == 3) { - sim_debug(WRITE_MSG, &timer_dev, - "[%08x] READ BACK COMMAND. DATA=%02x\n", - R[NUM_PC], val); if (val & 2) { ctr = &TIMERS[0]; if ((val & 0x20) == 0) { @@ -481,9 +455,6 @@ void timer_write(uint32 pa, uint32 val, size_t size) } } } else { - sim_debug(WRITE_MSG, &timer_dev, - "[%08x] Timer Control Write: timer %d => %02x\n", - R[NUM_PC], ctrnum, val & 0xff); ctr = &TIMERS[ctrnum]; ctr->ctrl = (uint8) val; ctr->enabled = FALSE; @@ -495,12 +466,41 @@ void timer_write(uint32 pa, uint32 val, size_t size) break; case TIMER_CLR_LATCH: sim_debug(WRITE_MSG, &timer_dev, - "[%08x] unexpected write to clear timer latch\n", - R[NUM_PC]); + "unexpected write to clear timer latch\n"); break; default: sim_debug(WRITE_MSG, &timer_dev, - "[%08x] unknown timer register: %d\n", - R[NUM_PC], reg); + "unknown timer register: %d\n", + reg); } } + +CONST char *tmr_description(DEVICE *dptr) +{ +#if defined (REV3) + return "82C54 Programmable Interval Timer"; +#else + return "8253 Programmable Interval Timer"; +#endif +} + +t_stat tmr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +#if defined (REV3) + fprintf(st, "82C54 Programmable Interval Timer (TMR)\n\n"); + fprintf(st, "The TMR device implements three programmable timers used by the 3B2/700\n"); +#else + fprintf(st, "8253 Programmable Interval Timer (TMR)\n\n"); + fprintf(st, "The TMR device implements three programmable timers used by the 3B2/400\n"); +#endif + fprintf(st, "to perform periodic tasks and sanity checks.\n\n"); + fprintf(st, "- TMR0: Used as a system sanity timer.\n"); + fprintf(st, "- TMR1: Used as a periodic 10 millisecond interval timer.\n"); + fprintf(st, "- TMR2: Used as a bus timeout timer.\n"); + + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + fprint_reg_help(st, dptr); + + return SCPE_OK; +} diff --git a/3B2/3b2_timer.h b/3B2/3b2_timer.h index 35df9b67..7d01e4ce 100644 --- a/3B2/3b2_timer.h +++ b/3B2/3b2_timer.h @@ -1,6 +1,6 @@ -/* 3b2_timer.h: Common TIMER header +/* 3b2_timer.h: 8253/82C54 Interval Timer - Copyright (c) 2021, Seth J. Morabito + Copyright (c) 2021-2022, Seth J. Morabito Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -31,12 +31,48 @@ #ifndef _3B2_TIMER_H_ #define _3B2_TIMER_H_ -#if defined(REV3) -#include "3b2_rev3_timer.h" -#else -#include "3b2_rev2_timer.h" -#endif +#include "3b2_defs.h" -extern int32 tmxr_poll; +#define TIMER_REG_DIVA 0x03 +#define TIMER_REG_DIVB 0x07 +#define TIMER_REG_DIVC 0x0b +#define TIMER_REG_CTRL 0x0f +#define TIMER_CLR_LATCH 0x13 + +#define TIMER_MODE(ctr) (((ctr->ctrl) >> 1) & 7) +#define TIMER_RW(ctr) (((ctr->ctrl) >> 4) & 3) + +#define CLK_LATCH 0 +#define CLK_LSB 1 +#define CLK_MSB 2 +#define CLK_LMB 3 + +#define TMR_SANITY 0 +#define TMR_INT 1 +#define TMR_BUS 2 + +struct timer_ctr { + uint16 divider; + uint16 val; + uint8 ctrl_latch; + uint16 cnt_latch; + uint8 ctrl; + t_bool r_lmb; + t_bool w_lmb; + t_bool enabled; + t_bool gate; + t_bool r_ctrl_latch; + t_bool r_cnt_latch; +}; + +t_stat timer_reset(DEVICE *dptr); +uint32 timer_read(uint32 pa, size_t size); +void timer_write(uint32 pa, uint32 val, size_t size); +void timer_gate(uint8 ctrnum, t_bool inhibit); + +t_stat tmr_svc(UNIT *uptr); +t_stat tmr_int_svc(UNIT *uptr); +CONST char *tmr_description(DEVICE *dptr); +t_stat tmr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); #endif /* _3B2_TIMER_H_ */ diff --git a/3B2/README.md b/3B2/README.md index 96eb02f8..dc3fb474 100644 --- a/3B2/README.md +++ b/3B2/README.md @@ -3,26 +3,25 @@ AT&T 3B2 Simulator This module contains the source for two simulators: -1. A simulator for the AT&T 3B2 Model 400 computer (Rev. 2) -2. A simulator for the AT&T 3B2 Model 600 computer (Rev. 3) - -The 3B2/400 simulator is complete, usable, and robust. The 3B2/600 simulator -is not yet usable, however. It is under active development. +1. A simulator for the AT&T 3B2/400 computer (3b2-400 or 3B2-400.EXE) +2. A simulator for the AT&T 3B2/700 computer (3b2-700 or 3B2-700.EXE) Full documentation for the 3B2 simulator is available here: - https://loomcom.com/3b2/emulator.html -Devices -------- +3B2/400 Simulator Devices +------------------------- The following devices are simulated. The SIMH names for the simulated devices are given in parentheses: - - 3B2 Model 400 System Board with 1MB, 2MB, or 4MB RAM (CSR, NVRAM) + - 3B2 Model 400 System Board with 1MB, 2MB, or 4MB RAM + - Configuration and Status Register (CSR) - WE32100 CPU (CPU) - WE32101 MMU (MMU) - - PD8253 Interval Timer (TIMER) + - WE32106 Math Accelerator Unit (MAU) + - PD8253 Interval Timer (TMR) - AM9517 DMA controller (DMAC) - SCN2681A Integrated DUART (IU) - TMS2793 Integrated Floppy Controller (IFLOPPY) @@ -33,97 +32,23 @@ devices are given in parentheses: - CM195B 4-port Serial MUX (PORTS) - CM195H Cartridge Tape Controller (CTC) -Usage ------ +3B2/700 Simulator Devices +------------------------- -To boot the 3B2 simulator into firmware mode, simply type: +The following devices are simulated. The SIMH names for the simulated +devices are given in parentheses: - sim> BOOT - -You will be greeted with the message: - - FW ERROR 1-01: NVRAM SANITY FAILURE - DEFAULT VALUES ASSUMED - IF REPEATED, CHECK THE BATTERY - - FW ERROR 1-02: DISK SANITY FAILURE - EXECUTION HALTED - - SYSTEM FAILURE: CONSULT YOUR SYSTEM ADMINISTRATION UTILITIES GUIDE - -NVRAM and Time of Day can be saved between boots by attaching both -devices to files. - - sim> ATTACH NVRAM - sim> ATTACH TOD - -If you have no operating system installed on the hard drive, on -subsequent boots you will instead see the message - - SELF-CHECK - - - FW ERROR 1-02: DISK SANITY FAILURE - EXECUTION HALTED - - SYSTEM FAILURE: CONSULT YOUR SYSTEM ADMINISTRATION UTILITIES GUIDE - - -Once you see the `SYSTEM FAILURE` message, this is actually an -invisible prompt. To access firmware mode, type the default 3B2 -firmware password `mcp`, then press Enter or carriage return. - -You should then be prompted with: - - Enter name of program to execute [ ]: - -Here, you may type a question mark (?) and press Enter to see a list -of available firmware programs. - -Booting UNIX SVR3 ------------------ - -UNIX SVR3 is the only operating system available for the 3B2. To boot -UNIX, attach the first disk image from the 3B2 "Essential Utilities" -distribution. - - sim> ATTACH IFLOPPY - sim> BOOT - -Once you reach the `SYSTEM FAILURE` message, type `mcp` to enter -firmware mode. When prompted for the name of a program to boot, enter -`unix`, and confirm the boot device is `FD5` by pressing Enter or -carriage return. - - Enter name of program to execute [ ]: unix - Possible load devices are: - - Option Number Slot Name - --------------------------------------- - 0 0 FD5 - - Enter Load Device Option Number [0 (FD5)]: - -Installing SVR3 ---------------- - -To install SVR3 to the first hard disk, first, attach a new image -to the IDISK0 device: - - sim> ATTACH IDISK0 - -Then, boot the file `idtools` from the "3B2 Maintenance Utilities - -Issue 4.0" floppy diskette. - -From `idtools`, select the `formhard` option and low-level format -integrated disk 0. Parameters for the default 72MB hard disk are: - - Drive Id: 5 - Number cylinders: 925 - Number tracks/cyl: 9 - Number sectors/track: 18 - Number bytes/sector: 512 - -After low-level formatting integrated disk 0, boot the file `unix` -from the first diskette of the 3B2 "Essential Utilities" distribution, -and follow the prompts. + - 3B2 Model 700 System Board with 8MB, 16MB, 32MB, or 64MB RAM + - Configuration and Status Registers (CSR) + - WE32200 CPU (CPU) + - WE32201 MMU (MMU) + - WE32106 Math Accelerator Unit (MAU) + - PD8253 Interval Timer (TMR) + - AM9517 DMA controller (DMAC) + - SCN2681A Integrated DUART (IU) + - TMS2793 Integrated Floppy Controller (IFLOPPY) + - Non-Volatile Memory (NVRAM) + - MM58274C Time Of Day Clock (TOD) + - CM195W SCSI Host Adapter (SCSI) + - CM195A Ethernet Network Interface (NI) + - CM195B 4-port Serial MUX (PORTS) diff --git a/3B2/rom_rev2.bin b/3B2/rom_rev2.bin deleted file mode 100644 index 6ebc5390..00000000 Binary files a/3B2/rom_rev2.bin and /dev/null differ diff --git a/3B2/rom_rev2_demon.bin b/3B2/rom_rev2_demon.bin deleted file mode 100644 index 222eb33e..00000000 Binary files a/3B2/rom_rev2_demon.bin and /dev/null differ diff --git a/3B2/rom_rev3.bin b/3B2/rom_rev3.bin deleted file mode 100644 index 94afbfaf..00000000 Binary files a/3B2/rom_rev3.bin and /dev/null differ diff --git a/3B2/tests/3b2_test.ini b/3B2/tests/3b2_test.ini deleted file mode 100644 index 39f6a980..00000000 --- a/3B2/tests/3b2_test.ini +++ /dev/null @@ -1,34 +0,0 @@ -:: 3b2-diag.ini -:: This script will run the available 3B2/400 core diagnostics. -:: -cd %~p0 -set runlimit 2 minutes -set on -on error ignore -on runtime echof "\r\n*** Test Runtime Limit %SIM_RUNLIMIT% %SIM_RUNLIMIT_UNITS% Exceeded ***\n"; exit 1 - -set env DIAG_QUIET_MODE=0 -if ("%1" == "-v") set console notelnet -else set -qu console telnet=localhost:65432,telnet=buffered; set env -a DIAG_QUIET_MODE=1 - -:: Set maximum memory size -set cpu 4M - -if not exist rev2_diags.dsk echof "\r\nMISSING - Diagnostic disk image '%~p0rev2_diags.dsk' is missing\n"; exit 1 -attach -rq ifloppy rev2_diags.dsk - -:: Initial setup -expect "UTILITIES GUIDE" send "mcp\r"; go -q -expect "Enter name of program to execute [ ]:" send "filledt\r"; go -q -expect "Enter Load Device Option Number [0 (FD5)]:" send "0\r"; go -q -expect "Enter name of program to execute [ ]:" send "dgmon\r"; go -q -expect "Enter Load Device Option Number [0 (FD5)]:" send "0\r"; go -q -expect "Did you boot filledt? [y or n] (n)" send "y\r"; go -q -expect "DGMON > " send "DGN SBD\r"; go -q -expect "SBD 0 (IN SLOT 0) DIAGNOSTICS PASSED" echof; echof "PASSED: 3B2 DGMON SBD Diagnostics."; exit 0 -expect [4] "FAIL" echof; echof "FAILED: 3B2 DGMON SBD Diagnostics."; exit 1 - -:: Run tests -if (DIAG_QUIET_MODE) echof "\nStarting 3B2 DGMON SBD Diagnostics." -boot -q CPU -return diff --git a/3B2/tests/rev2_diags.dsk b/3B2/tests/rev2_diags.dsk deleted file mode 100644 index 1fbfe909..00000000 Binary files a/3B2/tests/rev2_diags.dsk and /dev/null differ diff --git a/Visual Studio Projects/3B2.vcproj b/Visual Studio Projects/3B2-400.vcproj similarity index 89% rename from Visual Studio Projects/3B2.vcproj rename to Visual Studio Projects/3B2-400.vcproj index 49215c6d..8b2a72f5 100644 --- a/Visual Studio Projects/3B2.vcproj +++ b/Visual Studio Projects/3B2-400.vcproj @@ -2,9 +2,9 @@ @@ -26,7 +26,7 @@ @@ -222,15 +222,7 @@ > - - - - - - + + + + + + @@ -565,6 +565,10 @@ RelativePath="..\3B2\3b2_cpu.h" > + + @@ -573,10 +577,6 @@ RelativePath="..\3B2\3b2_defs.h" > - - @@ -598,21 +598,17 @@ > - - - - + + @@ -621,22 +617,18 @@ RelativePath="..\3B2\3b2_ports.h" > - - - - - - + + + + diff --git a/Visual Studio Projects/3B2-600.vcproj b/Visual Studio Projects/3B2-700.vcproj similarity index 91% rename from Visual Studio Projects/3B2-600.vcproj rename to Visual Studio Projects/3B2-700.vcproj index 09106192..f5642cbe 100644 --- a/Visual Studio Projects/3B2-600.vcproj +++ b/Visual Studio Projects/3B2-700.vcproj @@ -2,9 +2,9 @@ @@ -26,7 +26,7 @@ @@ -213,6 +213,10 @@ RelativePath="..\3B2\3b2_iu.c" > + + @@ -225,10 +229,6 @@ RelativePath="..\3B2\3b2_ports.c" > - - @@ -241,10 +241,6 @@ RelativePath="..\3B2\3b2_rev3_sys.c" > - - @@ -257,6 +253,10 @@ RelativePath="..\3B2\3b2_sys.c" > + + @@ -566,7 +566,7 @@ > + + + + @@ -605,30 +613,6 @@ RelativePath="..\3B2\3b2_ports.h" > - - - - - - - - - - - - @@ -637,6 +621,10 @@ RelativePath="..\3B2\3b2_sys.h" > + + diff --git a/Visual Studio Projects/Simh.sln b/Visual Studio Projects/Simh.sln index 3a213c5a..b3a3b940 100755 --- a/Visual Studio Projects/Simh.sln +++ b/Visual Studio Projects/Simh.sln @@ -228,7 +228,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scelbi", "scelbi.vcproj", " {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "3B2", "3B2.vcproj", "{56178F08-8783-4ADA-820C-20C06412678E}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "3B2-400", "3B2-400.vcproj", "{56178F08-8783-4ADA-820C-20C06412678E}" ProjectSection(ProjectDependencies) = postProject {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} EndProjectSection @@ -373,7 +373,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tt2500", "tt2500.vcproj", " {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "3B2-600", "3B2-600.vcproj", "{A7AE7747-DFA0-49F5-9D6C-9094657A8EE3}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "3B2-700", "3B2-700.vcproj", "{A7AE7747-DFA0-49F5-9D6C-9094657A8EE3}" ProjectSection(ProjectDependencies) = postProject {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} EndProjectSection diff --git a/makefile b/makefile index d8129485..f6044897 100644 --- a/makefile +++ b/makefile @@ -2147,27 +2147,27 @@ KS10_OPT = -DKS=1 -DUSE_INT64 -I $(KS10D) -I $(PDP11D) ${NETWORK_OPT} ATT3B2D = ${SIMHD}/3B2 ATT3B2M400 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \ - ${ATT3B2D}/3b2_rev2_sys.c ${ATT3B2D}/3b2_rev2_mmu.c \ - ${ATT3B2D}/3b2_rev2_mau.c ${ATT3B2D}/3b2_rev2_csr.c \ - ${ATT3B2D}/3b2_rev2_timer.c ${ATT3B2D}/3b2_stddev.c \ - ${ATT3B2D}/3b2_mem.c ${ATT3B2D}/3b2_iu.c \ - ${ATT3B2D}/3b2_if.c ${ATT3B2D}/3b2_id.c \ - ${ATT3B2D}/3b2_dmac.c ${ATT3B2D}/3b2_io.c \ - ${ATT3B2D}/3b2_ports.c ${ATT3B2D}/3b2_ctc.c \ + ${ATT3B2D}/3b2_rev2_sys.c ${ATT3B2D}/3b2_rev2_mmu.c \ + ${ATT3B2D}/3b2_mau.c ${ATT3B2D}/3b2_rev2_csr.c \ + ${ATT3B2D}/3b2_timer.c ${ATT3B2D}/3b2_stddev.c \ + ${ATT3B2D}/3b2_mem.c ${ATT3B2D}/3b2_iu.c \ + ${ATT3B2D}/3b2_if.c ${ATT3B2D}/3b2_id.c \ + ${ATT3B2D}/3b2_dmac.c ${ATT3B2D}/3b2_io.c \ + ${ATT3B2D}/3b2_ports.c ${ATT3B2D}/3b2_ctc.c \ ${ATT3B2D}/3b2_ni.c \ $(NETWORK_DEPS) ATT3B2M400_OPT = -DUSE_INT64 -DUSE_ADDR64 -DREV2 -I ${ATT3B2D} ${NETWORK_OPT} -ATT3B2M600 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \ - ${ATT3B2D}/3b2_rev3_sys.c ${ATT3B2D}/3b2_rev3_mmu.c \ - ${ATT3B2D}/3b2_rev2_mau.c ${ATT3B2D}/3b2_rev3_csr.c \ - ${ATT3B2D}/3b2_rev3_timer.c ${ATT3B2D}/3b2_stddev.c \ - ${ATT3B2D}/3b2_mem.c ${ATT3B2D}/3b2_iu.c \ - ${ATT3B2D}/3b2_if.c ${ATT3B2D}/3b2_dmac.c \ - ${ATT3B2D}/3b2_io.c ${ATT3B2D}/3b2_ports.c \ +ATT3B2M700 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \ + ${ATT3B2D}/3b2_rev3_sys.c ${ATT3B2D}/3b2_rev3_mmu.c \ + ${ATT3B2D}/3b2_mau.c ${ATT3B2D}/3b2_rev3_csr.c \ + ${ATT3B2D}/3b2_timer.c ${ATT3B2D}/3b2_stddev.c \ + ${ATT3B2D}/3b2_mem.c ${ATT3B2D}/3b2_iu.c \ + ${ATT3B2D}/3b2_if.c ${ATT3B2D}/3b2_dmac.c \ + ${ATT3B2D}/3b2_io.c ${ATT3B2D}/3b2_ports.c \ ${ATT3B2D}/3b2_scsi.c ${ATT3B2D}/3b2_ni.c \ $(NETWORK_DEPS) -ATT3B2M600_OPT = -DUSE_INT64 -DUSE_ADDR64 -DREV3 -I ${ATT3B2D} ${NETWORK_OPT} +ATT3B2M700_OPT = -DUSE_INT64 -DUSE_ADDR64 -DREV3 -I ${ATT3B2D} ${NETWORK_OPT} SIGMAD = ${SIMHD}/sigma SIGMA = ${SIGMAD}/sigma_cpu.c ${SIGMAD}/sigma_sys.c ${SIGMAD}/sigma_cis.c \ @@ -2239,7 +2239,7 @@ ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \ nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri \ i7094 ibm1130 id16 id32 sds lgp h316 cdc1700 \ swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 intel-mds \ - scelbi 3b2 i701 i704 i7010 i7070 i7080 i7090 \ + scelbi 3b2 3b2-700 i701 i704 i7010 i7070 i7080 i7090 \ sigma uc15 pdp10-ka pdp10-ki pdp10-kl pdp10-ks pdp6 i650 \ imlac tt2500 sel32 @@ -2546,12 +2546,12 @@ b5500 : ${B5500} ${SIM} $(MAKEIT) OPTS="$(B5500_OPT)" INDIR1="$(B5500D)" -3b2 : ${ATT3B2M400} ${SIM} ${BUILD_ROMS} - $(MAKEIT) OPTS="$(ATT3B2M400_OPT)" INDIR1="$(ATT3B2D)" +3b2 : ${ATT3B2M400} ${SIM} + $(MAKEIT) OPTS="$(ATT3B2M400_OPT)" INDIR1="$(ATT3B2D)" ALTNAME=3b2-400 -3b2-600 : ${ATT3B2M600} ${SIM} ${SCSI} ${BUILD_ROMS} - $(MAKEIT) OPTS="$(ATT3B2M600_OPT)" INDIR1="$(ATT3B2D)" +3b2-700 : ${ATT3B2M700} ${SCSI} ${SIM} + $(MAKEIT) OPTS="$(ATT3B2M700_OPT)" INDIR1="$(ATT3B2D)" i7090 : ${I7090} ${SIM} diff --git a/sim_BuildROMs.c b/sim_BuildROMs.c index 06d89f7d..f4eb0fc7 100644 --- a/sim_BuildROMs.c +++ b/sim_BuildROMs.c @@ -70,9 +70,6 @@ struct ROM_File_Descriptor { {"PDP11/dazzledart/dazzle.lda", "PDP11/pdp11_dazzle_dart_rom.h", 6096, 0xFFF83848, "dazzle_lda"}, {"PDP11/11logo/11logo.lda", "PDP11/pdp11_11logo_rom.h", 26009, 0xFFDD77F7, "logo_lda"}, {"swtp6800/swtp6800/swtbugv10.bin", "swtp6800/swtp6800/swtp_swtbugv10_bin.h", 1024, 0xFFFE4FBC, "swtp_swtbugv10_bin"}, - {"3B2/rom_rev2.bin", "3B2/rom_rev2_bin.h", 32768, 0xFFD55762, "rom_rev2_bin"}, - {"3B2/rom_rev2_demon.bin", "3B2/rom_rev2_demon_bin.h", 65536, 0xFFB345BB, "rom_rev2_demon_bin"}, - {"3B2/rom_rev3.bin", "3B2/rom_rev3_bin.h", 131072, 0xFFDC0EB8, "rom_rev3_bin"}, };