diff --git a/3B2/3b2_cpu.c b/3B2/3b2_cpu.c index 29d87fca..c7799d14 100644 --- a/3B2/3b2_cpu.c +++ b/3B2/3b2_cpu.c @@ -46,34 +46,39 @@ #include "3b2_cpu.h" #if defined(REV3) -#include "3b2_rev2_mau.h" /* TODO: Replace with Rev 3 MAU when implemented */ -#include "3b2_rev3_csr.h" -#include "3b2_rev3_mmu.h" #include "rom_rev3_bin.h" +#include "3b2_if.h" +#define ROM_ARRAY BOOT_CODE_ARRAY +#define ROM_SIZE BOOT_CODE_SIZE #else -#include "3b2_rev2_csr.h" -#include "3b2_rev2_mau.h" -#include "3b2_rev2_mmu.h" -#include "3b2_id.h" #include "rom_rev2_bin.h" -#endif +#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" #include "3b2_dmac.h" #include "3b2_io.h" #include "3b2_iu.h" +#include "3b2_mau.h" #include "3b2_mem.h" +#include "3b2_mmu.h" #include "3b2_stddev.h" +#include "3b2_timer.h" #define MAX_SUB_RETURN_SKIP 9 -uint32 rom_size = BOOT_CODE_SIZE; +uint32 rom_size = 0; /* Static function declarations */ static uint32 cpu_effective_address(operand * op); static uint32 cpu_read_op(operand * op); static void cpu_write_op(operand * op, t_uint64 val); static void cpu_set_nz_flags(t_uint64 data, operand * op); -static void cpu_calc_ints(); static SIM_INLINE void cpu_on_normal_exception(uint8 isc); static SIM_INLINE void cpu_on_stack_exception(uint8 isc); static SIM_INLINE void cpu_on_process_exception(uint8 isc); @@ -136,16 +141,19 @@ volatile uint32 abort_reason; uint32 R[NUM_REGISTERS]; /* Other global CPU state */ -uint8 cpu_int_ipl = 0; /* Interrupt IPL level */ -uint8 cpu_int_vec = 0; /* Interrupt vector */ -t_bool cpu_nmi = FALSE; /* If set, there has been an NMI */ -int32 pc_incr = 0; /* Length (in bytes) of instruction - currently being executed */ -t_bool cpu_ex_halt = FALSE; /* Flag to halt on exceptions / - traps */ -t_bool cpu_km = FALSE; /* If true, kernel mode has been forced - for memory access */ +/* 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 */ +uint16 sbd_int_req = 0; /* Currently set interrupt sources */ +uint8 int_map[INT_MAP_LEN]; /* Map of interrupt sources to highest + priority IPL */ +t_bool cpu_nmi = FALSE; /* If set, there has been an NMI */ +int32 pc_incr = 0; /* Length (in bytes) of instruction + currently being executed */ +t_bool cpu_ex_halt = FALSE; /* Flag to halt on exceptions / traps */ +t_bool cpu_km = FALSE; /* If true, kernel mode has been forced + for memory access */ CTAB sys_cmd[] = { { "BOOT", &sys_boot, RU_BOOT, "bo{ot} boot simulator\n", NULL, &run_cmd_message }, @@ -182,6 +190,37 @@ BITFIELD psw_bits[] = { ENDBITS }; +BITFIELD sbd_int_req_bits[] = { +#if defined(REV3) + BIT(CLOK), /* UNIX Interval Timer */ + BIT(PWRD), /* Power Down Request */ + BIT(BUSO), /* UBUS or BUB Operational Interrupt */ + BIT(SBER), /* Single Bit Memory Error */ + BIT(MBER), /* Multiple Bit Memory Error */ + BIT(BRXF), /* UBUS, BUB, EIO Bus Received Fail */ + BIT(BTMO), /* UBUS Timer Timeout */ + BIT(UDMA), /* UART DMA Complete */ + BIT(UART), /* UART Interrupt */ + BIT(FDMA), /* Floppy DMA Complete */ + BIT(FLOP), /* Floppy Interrupt */ + BIT(PIR9), /* PIR 9 */ + BIT(PIR8), /* PIR 8 */ + BITNCF(3), /* Unused */ + ENDBITS +#else + BIT(SERR), /* System Error */ + BIT(CLOK), /* UNIX Interval Timer */ + BIT(DMAC), /* DMA Complete */ + BIT(UART), /* UART */ + BIT(DISK), /* Integrated Disk Drive (Winchester) */ + BIT(FLOP), /* Integrated Floppy Drive */ + BIT(PIR9), /* PIR 9 */ + BIT(PIR8), /* PIR 8 */ + BITNCF(8), /* Unused */ + ENDBITS +#endif +}; + /* Registers. */ REG cpu_reg[] = { { HRDATAD (R0, R[0], 32, "General purpose register 0") }, @@ -218,8 +257,7 @@ REG cpu_reg[] = { { HRDATAD (R30, R[30], 32, "Privileged register 30")}, { HRDATAD (R31, R[31], 32, "Privileged register 31")}, #endif - { HRDATAD (IPL, cpu_int_ipl, 8, "Current CPU IPL bits")}, - { HRDATAD (VEC, cpu_int_vec, 8, "Current CPU interrupt vector")}, + { HRDATADF (SBD_INT, sbd_int_req, 16, "Interrupt Requests", sbd_int_req_bits) }, { NULL } }; @@ -317,9 +355,9 @@ MTAB cpu_mod[] = { &cpu_set_size, NULL, NULL, "Set Memory to 1M bytes" }, { UNIT_MSIZE, (1u << 21), NULL, "2M", &cpu_set_size, NULL, NULL, "Set Memory to 2M bytes" }, -#endif { UNIT_MSIZE, (1u << 22), NULL, "4M", &cpu_set_size, NULL, NULL, "Set Memory to 4M bytes" }, +#endif #if defined(REV3) { UNIT_MSIZE, (1u << 23), NULL, "8M", &cpu_set_size, NULL, NULL, "Set Memory to 8M bytes" }, @@ -756,24 +794,34 @@ t_stat cpu_show_cio(FILE *st, UNIT *uptr, int32 val, CONST void *desc) return SCPE_OK; } -void cpu_load_rom() +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; + return SCPE_MEM; } - for (i = 0; i < rom_size; i++) { - val = BOOT_CODE_ARRAY[i]; + 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) { char gbuf[CBUFSIZE]; @@ -789,6 +837,39 @@ t_stat sys_boot(int32 flag, CONST char *ptr) return run_cmd(flag, "CPU"); } +#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; + + if ((ptr = get_sim_sw(ptr)) == NULL) { + return SCPE_INVSW; + } + + do { + ptr = get_glyph(ptr, gbuf, 0); + + if (gbuf[0] && (strcmp(gbuf, "CPU") && strcmp(gbuf, "DEMON"))) { + 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 */ t_stat cpu_boot(int32 unit_num, DEVICE *dptr) { @@ -872,9 +953,58 @@ t_stat cpu_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw) } } +/* + * Pre-populate the interrupt->IPL map "int_map" + */ +static void build_int_map() +{ + int i; + uint8 ipl; + + 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)) { + ipl = CPU_IPL_15; + } else if (i & (INT_UART|INT_UART_DMA)) { + ipl = CPU_IPL_13; + } else if (i & (INT_FLOPPY|INT_FLOPPY_DMA)) { + ipl = CPU_IPL_11; + } else if (i & INT_PIR9) { + ipl = CPU_IPL_9; + } else if (i & INT_PIR8) { + ipl = CPU_IPL_8; + } else { + ipl = 0; + } +#else + if (i & (INT_CLOCK|INT_SERR)) { + ipl = CPU_IPL_15; + } else if (i & (INT_UART|INT_DMA)) { + ipl = CPU_IPL_13; + } else if (i & (INT_DISK|INT_FLOPPY)) { + ipl = CPU_IPL_11; + } else if (i & INT_PIR9) { + ipl = CPU_IPL_9; + } else if (i & INT_PIR8) { + ipl = CPU_IPL_8; + } else { + ipl = 0; + } +#endif + + int_map[i] = ipl; + } + + sim_debug(EXECUTE_MSG, &cpu_dev, + "Built interrupt->IPL map of length %d\n", INT_MAP_LEN); +} + 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" */ @@ -883,24 +1013,23 @@ t_stat cpu_reset(DEVICE *dptr) /* Set up the pre-calibration routine */ sim_clock_precalibrate_commands = att3b2_clock_precalibrate_commands; + /* Populate the interrupt->IPL map */ + build_int_map(); + if (!sim_is_running) { /* Clear registers */ for (i = 0; i < NUM_REGISTERS; i++) { R[i] = 0; } - /* Allocate ROM */ - if (ROM != NULL) { - free(ROM); - } - ROM = (uint32 *) calloc((size_t)(rom_size >> 2), sizeof(uint32)); + /* Allocate ROM if needed */ if (ROM == NULL) { - return SCPE_MEM; + if ((r = cpu_load_rom(ROM_ARRAY, ROM_SIZE)) != SCPE_OK) { + return r; + } } - cpu_load_rom(); - - /* Allocate RAM */ + /* Always re-allocate RAM */ if (RAM != NULL) { free(RAM); } @@ -1822,8 +1951,8 @@ void cpu_on_interrupt(uint16 vec) uint32 new_pcbp, new_pcbp_ptr; sim_debug(IRQ_MSG, &cpu_dev, - "[%08x] [cpu_on_interrupt] vec=%02x (%d) csr_data = %x\n", - R[NUM_PC], vec, vec, csr_data); + "[%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); /* * "If a nonmaskable interrupt request is received, an auto-vector @@ -1886,6 +2015,9 @@ t_stat sim_instr(void) /* Generic index */ uint32 i; + /* Interrupt request IPL */ + uint8 ipl; + /* Used by oprocessor instructions */ uint32 coprocessor_word; @@ -1999,26 +2131,33 @@ t_stat sim_instr(void) increment_modep_b(); } - /* Set the correct IRQ state */ - cpu_calc_ints(); - - if (cpu_nmi || (PSW_CUR_IPL < cpu_int_ipl)) { - cpu_on_interrupt(cpu_int_vec); - for (i = 0; i < CIO_SLOTS; i++) { - if (cio[i].intr && - cio[i].ipl == cpu_int_ipl && - cio[i].ivec == cpu_int_vec) { - sim_debug(CIO_DBG, &cpu_dev, - "[%08x] [IRQ] Handling CIO interrupt for card %d ivec=%02x\n", - R[NUM_PC], i, cpu_int_vec); - - cio[i].intr = FALSE; - } - } - cpu_int_ipl = 0; - cpu_int_vec = 0; + /* Interrupt Handling + * + * - NMI is always serviced first. + * - SBD interrupts are handled next in priority. + * - IO Bus boards are handled last. + */ + if (cpu_nmi) { 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)) { + cpu_in_wait = FALSE; + CIO_CLR_INT(i); + cpu_on_interrupt(cio[i].ivec); + break; + } + } } if (cpu_in_wait) { @@ -3693,45 +3832,6 @@ static void cpu_write_op(operand * op, t_uint64 val) } } -/* - * Calculate the current state of interrupts. - * TODO: This could use a refactor. It's getting code-smelly. - */ -static void cpu_calc_ints() -{ - uint32 i; - - /* First scan for a CIO interrupt */ - for (i = 0; i < CIO_SLOTS; i++) { - if (cio[i].intr) { - cpu_int_ipl = cio[i].ipl; - cpu_int_vec = cio[i].ivec; - return; - } - } - - /* If none was found, look for system board interrupts */ - -#if defined(REV3) - // TODO: Rev 3 interrupt support - cpu_int_ipl = cpu_int_vec = 0; -#else - if (csr_data & CSRPIR8) { - cpu_int_ipl = cpu_int_vec = CPU_IPL_8; - } else if (csr_data & CSRPIR9) { - cpu_int_ipl = cpu_int_vec = CPU_IPL_9; - } else if (id_int() || (csr_data & CSRDISK)) { - cpu_int_ipl = cpu_int_vec = CPU_IPL_11; - } else if ((csr_data & CSRUART) || (csr_data & CSRDMA)) { - cpu_int_ipl = cpu_int_vec = CPU_IPL_13; - } else if ((csr_data & CSRCLK) || (csr_data & CSRTIMO)) { - cpu_int_ipl = cpu_int_vec = CPU_IPL_15; - } else { - cpu_int_ipl = cpu_int_vec = 0; - } -#endif -} - /* * Returns the correct datatype for an operand -- either extended type * or default type. diff --git a/3B2/3b2_cpu.h b/3B2/3b2_cpu.h index 44b8be33..b6f9dfa7 100644 --- a/3B2/3b2_cpu.h +++ b/3B2/3b2_cpu.h @@ -604,8 +604,6 @@ instr *cpu_next_instruction(void); uint8 decode_instruction(instr *instr); void cpu_on_interrupt(uint16 vec); void cpu_abort(uint8 et, uint8 isc); -void cpu_set_irq(uint8 ipl, uint8 id, uint16 csr_flags); -void cpu_clear_irq(uint8 ipl, uint16 csr_flags); /* Helper macros */ @@ -649,7 +647,11 @@ void cpu_clear_irq(uint8 ipl, uint16 csr_flags); (d) = (uint8) (a)[(p)++]; \ } +#define CPU_SET_INT(flags) (sbd_int_req |= flags) +#define CPU_CLR_INT(flags) (sbd_int_req &= ~(flags)) + extern volatile int32 stop_reason; +extern uint16 sbd_int_req; extern uint32 rom_size; extern instr *cpu_instr; extern t_bool cpu_nmi; diff --git a/3B2/3b2_csr.h b/3B2/3b2_csr.h new file mode 100644 index 00000000..c7cf27a5 --- /dev/null +++ b/3B2/3b2_csr.h @@ -0,0 +1,44 @@ +/* 3b2_csr.h: Common CSR 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_CSR_H_ +#define _3B2_CSR_H_ + +#if defined(REV3) +#include "3b2_rev3_csr.h" +#else +#include "3b2_rev2_csr.h" +#endif + +#define SET_CSR(FLAGS) (csr_data |= (FLAGS)) +#define CLR_CSR(FLAGS) (csr_data &= ~(FLAGS)) +#define CSR(FLAGS) ((csr_data & FLAGS) != 0) + +#endif /* _3B2_CSR_H_ */ diff --git a/3B2/3b2_ctc.c b/3B2/3b2_ctc.c index 01769d85..edc15e1b 100644 --- a/3B2/3b2_ctc.c +++ b/3B2/3b2_ctc.c @@ -32,8 +32,8 @@ #include "sim_disk.h" -#include "3b2_mem.h" #include "3b2_io.h" +#include "3b2_mem.h" #define CTQRESIZE 20 #define CTQCESIZE 16 @@ -761,7 +761,7 @@ t_stat ctc_svc(UNIT *uptr) sim_debug(TRACE_DBG, &ctc_dev, "[cio_svc] IRQ for board %d (VEC=%d)\n", int_cid, cio[int_cid].ivec); - cio[int_cid].intr = TRUE; + CIO_SET_INT(int_cid); } /* Check to see if the completion queue has more work in it. We diff --git a/3B2/3b2_defs.h b/3B2/3b2_defs.h index d165c478..1b48a1b5 100644 --- a/3B2/3b2_defs.h +++ b/3B2/3b2_defs.h @@ -85,6 +85,8 @@ noret __libc_longjmp(jmp_buf buf, int val); } \ } +#define PCHAR(c) (((char) (c) >= 0x20 && (char) (c) < 0x7f) ? (char) (c) : '.') + #define UNIT_V_EXBRK (UNIT_V_UF + 0) #define UNIT_V_OPBRK (UNIT_V_UF + 1) #define UNIT_EXBRK (1u << UNIT_V_EXBRK) @@ -135,6 +137,11 @@ noret __libc_longjmp(jmp_buf buf, int val); #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 */ + + /* Global symbols */ extern DEBTAB sys_deb_tab[]; diff --git a/3B2/3b2_id.c b/3B2/3b2_id.c index 6b87ac15..71762d6d 100644 --- a/3B2/3b2_id.c +++ b/3B2/3b2_id.c @@ -186,11 +186,31 @@ DEVICE id_dev = { /* Function implementation */ -t_bool id_int() +#define UPDATE_INT { \ + if ((id_status & (ID_STAT_CEL|ID_STAT_CEH)) || \ + ((id_status & ID_STAT_SRQ) && !id_srqm)) { \ + CPU_SET_INT(INT_DISK); \ + } else { \ + CPU_CLR_INT(INT_DISK); \ + } \ + } + +static SIM_INLINE void id_set_status(uint8 flags) { - return (((id_status & ID_STAT_CEL) || - (id_status & ID_STAT_CEH) || - ((id_status & ID_STAT_SRQ) && !id_srqm))); + id_status |= flags; + UPDATE_INT; +} + +static SIM_INLINE void id_clr_status(uint8 flags) +{ + id_status &= ~(flags); + UPDATE_INT; +} + +static SIM_INLINE void id_set_srqm(t_bool state) +{ + id_srqm = state; + UPDATE_INT; } static SIM_INLINE void id_clear_fifo() @@ -217,9 +237,9 @@ t_stat id_ctlr_svc(UNIT *uptr) cmd = uptr->u4; /* The command that caused the activity */ - id_srqm = FALSE; - id_status &= ~(ID_STAT_CB); - id_status |= ID_STAT_CEH; + id_set_srqm(FALSE); + id_clr_status(ID_STAT_CB); + id_set_status(ID_STAT_CEH); uptr->u4 = 0; switch (cmd) { @@ -259,8 +279,8 @@ t_stat id_unit_svc(UNIT *uptr) return SCPE_OK; } - id_srqm = FALSE; - id_status &= ~(ID_STAT_CB); + id_set_srqm(FALSE); + id_clr_status(ID_STAT_CB); /* Note that we don't set CEH, in case this is a SEEK/RECAL ID_SEEK_1 */ switch (cmd) { @@ -277,7 +297,7 @@ t_stat id_unit_svc(UNIT *uptr) if (id_polling) { switch (id_seek_state[unit]) { case ID_SEEK_0: - id_status |= ID_STAT_CEH; + 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); @@ -289,7 +309,7 @@ t_stat id_unit_svc(UNIT *uptr) "[%08x]\tINTR\t\tCOMPLETING Recal/Seek SEEK_1 UNIT %d\n", R[NUM_PC], unit); id_seek_state[unit] = ID_SEEK_NONE; - id_status |= ID_STAT_SRQ; + id_set_status(ID_STAT_SRQ); uptr->u4 = 0; /* Only clear out the command on a SEEK_1, never a SEEK_0 */ if (uptr->flags & UNIT_ATT) { id_int_status |= (ID_IST_SEN|unit); @@ -307,7 +327,7 @@ t_stat id_unit_svc(UNIT *uptr) sim_debug(EXECUTE_MSG, &id_dev, "[%08x]\tINTR\t\tCOMPLETING NON-POLLING Recal/Seek UNIT %d\n", R[NUM_PC], unit); - id_status |= ID_STAT_CEH; + id_set_status(ID_STAT_CEH); uptr->u4 = 0; if (uptr->flags & UNIT_ATT) { id_int_status |= (ID_IST_SEN|unit); @@ -321,7 +341,7 @@ t_stat id_unit_svc(UNIT *uptr) sim_debug(EXECUTE_MSG, &id_dev, "[%08x]\tINTR\t\tCOMPLETING Sense Unit Status UNIT %d\n", R[NUM_PC], unit); - id_status |= ID_STAT_CEH; + id_set_status(ID_STAT_CEH); uptr->u4 = 0; if ((uptr->flags & UNIT_ATT) == 0) { /* If no HD is attached, SUS puts 0x00 into the data @@ -339,7 +359,7 @@ t_stat id_unit_svc(UNIT *uptr) sim_debug(EXECUTE_MSG, &id_dev, "[%08x]\tINTR\t\tCOMPLETING OTHER COMMAND 0x%x UNIT %d\n", R[NUM_PC], cmd, unit); - id_status |= ID_STAT_CEH; + id_set_status(ID_STAT_CEH); uptr->u4 = 0; break; } @@ -673,14 +693,14 @@ void id_handle_command(uint8 val) sim_debug(WRITE_MSG, &id_dev, "[%08x] \tCOMMAND\t%02x\tAUX:CLCE\n", R[NUM_PC], val); - id_status &= ~(ID_STAT_CEH|ID_STAT_CEL); + 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); - id_srqm = TRUE; + id_set_srqm(TRUE); } if (aux_cmd & ID_AUX_CLB) { @@ -699,6 +719,7 @@ void id_handle_command(uint8 val) sim_cancel(id_ctlr_unit); id_status = 0; id_srqm = FALSE; + UPDATE_INT; } /* Just return early */ @@ -715,7 +736,7 @@ void id_handle_command(uint8 val) } /* A full command always resets CEH and CEL */ - id_status &= ~(ID_STAT_CEH|ID_STAT_CEL); + id_clr_status(ID_STAT_CEH|ID_STAT_CEL); /* Save the full command byte */ id_cmd = val; @@ -739,14 +760,14 @@ void id_handle_command(uint8 val) id_sel_unit->u4 = cmd; } - id_status |= ID_STAT_CB; + id_set_status(ID_STAT_CB); switch(cmd) { case ID_CMD_SIS: sim_debug(WRITE_MSG, &id_dev, "[%08x]\tCOMMAND\t%02x\tSense Int. Status\n", R[NUM_PC], val); - id_status &= ~ID_STAT_SRQ; /* SIS immediately de-asserts SRQ */ + id_clr_status(ID_STAT_SRQ); /* SIS immediately de-asserts SRQ */ id_activate(id_ctlr_unit, ID_SIS_WAIT); break; case ID_CMD_SPEC: @@ -951,7 +972,7 @@ void id_handle_command(uint8 val) void id_after_dma() { - id_status &= ~ID_STAT_DRQ; + id_clr_status(ID_STAT_DRQ); id_drq = FALSE; } diff --git a/3B2/3b2_id.h b/3B2/3b2_id.h index fcb1bc1a..cecc4830 100644 --- a/3B2/3b2_id.h +++ b/3B2/3b2_id.h @@ -158,7 +158,6 @@ /* Function prototypes */ -t_bool id_int(); t_stat id_ctlr_svc(UNIT *uptr); t_stat id_unit_svc(UNIT *uptr); t_stat id_reset(DEVICE *dptr); diff --git a/3B2/3b2_if.c b/3B2/3b2_if.c index fb1ce64e..b965d9d3 100644 --- a/3B2/3b2_if.c +++ b/3B2/3b2_if.c @@ -32,19 +32,10 @@ #include "sim_disk.h" - -#if defined(REV3) -#include "3b2_rev3_csr.h" -#else -#include "3b2_rev2_csr.h" -#endif - #include "3b2_cpu.h" +#include "3b2_csr.h" /* Static function declarations */ -static SIM_INLINE void if_set_irq(); -static SIM_INLINE void if_clear_irq(); -static SIM_INLINE void if_cancel_pending_irq(); static SIM_INLINE uint32 if_lba(); /* @@ -67,13 +58,26 @@ static SIM_INLINE uint32 if_lba(); #define IF_HLD_DELAY 60000 /* us */ #define IF_HSW_DELAY 40000 /* us */ +#if defined(REV3) +#define SET_INT CPU_SET_INT(INT_FLOPPY) +#define CLR_INT CPU_CLR_INT(INT_FLOPPY) +#else +#define SET_INT do { \ + CPU_SET_INT(INT_FLOPPY); \ + SET_CSR(CSRDISK); \ + } while(0) +#define CLR_INT do { \ + CPU_CLR_INT(INT_FLOPPY); \ + CLR_CSR(CSRDISK); \ + } while(0) +#endif + UNIT if_unit = { UDATA (&if_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BINK+UNIT_ROABLE, IF_DSK_SIZE_SECS) }; REG if_reg[] = { - { HRDATAD (IRQ, if_irq, 1, "IRQ Set") }, { NULL } }; @@ -90,36 +94,14 @@ DEVICE if_dev = { IF_STATE if_state; uint8 if_buf[IF_SEC_SIZE]; uint32 if_sec_ptr = 0; -t_bool if_irq = FALSE; /* Function implementation */ -static SIM_INLINE void if_set_irq() -{ - if_irq = TRUE; -#if defined(REV2) - csr_data |= CSRDISK; -#endif -} - -static SIM_INLINE void if_clear_irq() -{ - if_irq = FALSE; -#if defined(REV2) - csr_data &= ~CSRDISK; -#endif -} - static SIM_INLINE void if_activate(uint32 delay) { sim_activate_abs(&if_unit, delay); } -static SIM_INLINE void if_cancel_pending_irq() -{ - sim_cancel(&if_unit); -} - t_stat if_svc(UNIT *uptr) { uint32 lba; /* Logical block address for write */ @@ -160,7 +142,7 @@ t_stat if_svc(UNIT *uptr) /* Request an interrupt */ sim_debug(IRQ_MSG, &if_dev, "\tINTR\n"); - if_set_irq(); + SET_INT; return SCPE_OK; } @@ -200,7 +182,7 @@ uint32 if_read(uint32 pa, size_t size) { data |= IF_NRDY; } /* Reading the status register always de-asserts the IRQ line */ - if_clear_irq(); + CLR_INT; sim_debug(READ_MSG, &if_dev, "\tSTATUS\t%02x\n", data); break; case IF_TRACK_REG: @@ -535,13 +517,13 @@ void if_handle_command() } if ((if_state.cmd & 0xf) == 0) { - if_cancel_pending_irq(); + sim_cancel(&if_unit); #if defined(REV2) - if_clear_irq(); /* TODO: Confirm this is right */ + CLR_INT; /* TODO: Confirm this is right */ #endif } else if ((if_state.cmd & 0x8) == 0x8) { if_state.status |= IF_DRQ; - if_set_irq(); + SET_INT; } break; @@ -563,7 +545,7 @@ void if_write(uint32 pa, uint32 val, size_t size) case IF_CMD_REG: if_state.cmd = (uint8) val; /* Writing to the command register always de-asserts the IRQ line */ - if_clear_irq(); + CLR_INT; if ((uptr->flags & UNIT_ATT) == 0) { /* If not attached, do nothing */ diff --git a/3B2/3b2_if.h b/3B2/3b2_if.h index f6378ff0..a6d40e58 100644 --- a/3B2/3b2_if.h +++ b/3B2/3b2_if.h @@ -127,6 +127,5 @@ CONST char *if_description(DEVICE *dptr); t_stat if_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); extern IF_STATE if_state; -extern t_bool if_irq; #endif diff --git a/3B2/3b2_io.c b/3B2/3b2_io.c index 259fa31c..eba4bf54 100644 --- a/3B2/3b2_io.c +++ b/3B2/3b2_io.c @@ -30,24 +30,24 @@ #include "3b2_io.h" -#if defined(REV3) -#include "3b2_rev3_csr.h" -#include "3b2_rev3_mmu.h" -#else -#include "3b2_id.h" -#include "3b2_rev2_csr.h" -#include "3b2_rev2_mmu.h" -#endif - #include "3b2_cpu.h" +#include "3b2_csr.h" #include "3b2_dmac.h" #include "3b2_if.h" #include "3b2_iu.h" #include "3b2_mem.h" +#include "3b2_mmu.h" #include "3b2_stddev.h" +#include "3b2_timer.h" + +#if defined(REV2) +#include "3b2_id.h" +#endif CIO_STATE cio[CIO_SLOTS] = {{0}}; +uint16 cio_int_req = 0; /* Bitset of card slots requesting interrupts */ + #if defined(REV3) iolink iotable[] = { { MMUBASE, MMUBASE+MMUSIZE, &mmu_read, &mmu_write }, @@ -99,10 +99,10 @@ void cio_clear(uint8 cid) cio[cid].ivec = 0; cio[cid].no_rque = 0; cio[cid].ipl = 0; - cio[cid].intr = FALSE; cio[cid].sysgen_s = 0; cio[cid].seqbit = 0; cio[cid].op = 0; + CIO_CLR_INT(cid); } /* @@ -490,7 +490,7 @@ uint32 io_read(uint32 pa, size_t size) sim_debug(IO_DBG, &cpu_dev, "[READ] [%08x] No card at cid=%d reg=%d\n", R[NUM_PC], cid, reg); - csr_data |= CSRTIMO; + CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return 0; } @@ -594,7 +594,7 @@ uint32 io_read(uint32 pa, size_t size) sim_debug(CIO_DBG, &cpu_dev, "[READ] [%08x] No card at cid=%d reg=%d\n", R[NUM_PC], cid, reg); - csr_data |= CSRTIMO; + CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return 0; } @@ -611,7 +611,7 @@ uint32 io_read(uint32 pa, size_t size) sim_debug(IO_DBG, &cpu_dev, "[%08x] [io_read] ADDR=%08x: No device found.\n", R[NUM_PC], pa); - csr_data |= CSRTIMO; + CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return 0; } @@ -621,6 +621,24 @@ void io_write(uint32 pa, uint32 val, size_t size) iolink *p; uint8 cid, reg; +#if defined(REV3) + if (pa >= VCACHE_BOTTOM && pa < VCACHE_TOP) { + CSRBIT(CSRTIMO, TRUE); + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + return; + } + + if (pa >= BUB_BOTTOM && pa < BUB_TOP) { + CSRBIT(CSRTIMO, TRUE); + /* TODO: I don't remember why we do this! */ + if ((pa & 0xfff) == 3) { + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + } + /* TODO: Implement BUB */ + return; + } +#endif + /* Feature Card Area */ if (pa >= CIO_BOTTOM && pa < CIO_TOP) { cid = CID(pa); @@ -631,7 +649,7 @@ void io_write(uint32 pa, uint32 val, size_t size) sim_debug(CIO_DBG, &cpu_dev, "[WRITE] [%08x] No card at cid=%d reg=%d\n", R[NUM_PC], cid, reg); - csr_data |= CSRTIMO; + CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return; } @@ -726,7 +744,7 @@ void io_write(uint32 pa, uint32 val, size_t size) sim_debug(CIO_DBG, &cpu_dev, "[WRITE] [%08x] No card at cid=%d reg=%d\n", R[NUM_PC], cid, reg); - csr_data |= CSRTIMO; + CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return; } @@ -744,7 +762,7 @@ void io_write(uint32 pa, uint32 val, size_t size) sim_debug(IO_DBG, &cpu_dev, "[%08x] [io_write] ADDR=%08x: No device found.\n", R[NUM_PC], pa); - csr_data |= CSRTIMO; + CSRBIT(CSRTIMO, TRUE); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); } diff --git a/3B2/3b2_io.h b/3B2/3b2_io.h index 67fbb58e..d06e5b90 100644 --- a/3B2/3b2_io.h +++ b/3B2/3b2_io.h @@ -160,6 +160,8 @@ #define CIO_INT1 2 #define CIO_SYSGEN 3 +#define CIO_SET_INT(slot) (cio_int_req |= (1 << slot)) +#define CIO_CLR_INT(slot) (cio_int_req &= ~(1 << slot)) typedef struct { uint16 id; /* Card ID */ @@ -174,7 +176,6 @@ typedef struct { uint8 ivec; /* Interrupt Vector */ uint8 no_rque; /* Number of request queues */ uint8 ipl; /* IPL that this card uses */ - t_bool intr; /* Card needs to interrupt */ uint8 sysgen_s; /* Sysgen state */ uint8 seqbit; /* Squence Bit */ uint8 op; /* Last received opcode */ @@ -245,6 +246,7 @@ 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]; #endif diff --git a/3B2/3b2_iu.c b/3B2/3b2_iu.c index 1bea7b84..b9a67aa6 100644 --- a/3B2/3b2_iu.c +++ b/3B2/3b2_iu.c @@ -32,15 +32,44 @@ #include "sim_tmxr.h" -#if defined(REV3) -#include "3b2_rev3_csr.h" -#else -#include "3b2_rev2_csr.h" -#endif #include "3b2_cpu.h" +#include "3b2_csr.h" #include "3b2_dmac.h" #include "3b2_mem.h" #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); @@ -310,8 +339,8 @@ void iu_txrdy_a_irq() { (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, (char) iu_console.txbuf); - csr_data |= CSRUART; + (uint8) iu_console.txbuf, PCHAR(iu_console.txbuf)); + SET_INT; } } @@ -321,8 +350,8 @@ void iu_txrdy_b_irq() { (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, (char) iu_contty.txbuf); - csr_data |= CSRUART; + (uint8) iu_contty.txbuf, PCHAR(iu_contty.txbuf)); + SET_INT; } } @@ -396,7 +425,7 @@ t_stat iu_svc_tti(UNIT *uptr) { int32 temp; - sim_clock_coschedule(uptr, tmxr_poll); + tmxr_clock_coschedule(uptr, tmxr_poll); /* TODO: @@ -423,7 +452,7 @@ t_stat iu_svc_tti(UNIT *uptr) iu_console.stat |= STS_RXR; iu_state.istat |= ISTS_RAI; if (iu_state.imr & IMR_RXRA) { - csr_data |= CSRUART; + SET_INT; } } @@ -459,7 +488,7 @@ t_stat iu_svc_contty_rcv(UNIT *uptr) contty_ldsc[ln].rcve = 1; iu_state.inprt &= ~(IU_DCDB); iu_state.ipcr |= IU_DCDB; - csr_data |= CSRUART; + SET_INT; } /* Check for disconnect */ @@ -467,7 +496,7 @@ t_stat iu_svc_contty_rcv(UNIT *uptr) contty_ldsc[0].rcve = 0; iu_state.inprt |= IU_DCDB; iu_state.ipcr |= IU_DCDB; - csr_data |= CSRUART; + SET_INT; } else if (iu_contty.conf & RX_EN) { tmxr_poll_rx(&contty_desc); @@ -484,7 +513,7 @@ t_stat iu_svc_contty_rcv(UNIT *uptr) iu_contty.stat |= STS_RXR; iu_state.istat |= ISTS_RBI; if (iu_state.imr & IMR_RXRB) { - csr_data |= CSRUART; + SET_INT; } } } @@ -519,7 +548,7 @@ t_stat iu_svc_timer(UNIT *uptr) iu_state.istat |= ISTS_CRI; if (iu_state.imr & IMR_CTR) { - csr_data |= CSRUART; + SET_INT; } return SCPE_OK; @@ -573,7 +602,7 @@ uint32 iu_read(uint32 pa, size_t size) iu_console.stat &= ~(STS_RXR|STS_FFL); iu_state.istat &= ~ISTS_RAI; } else if (iu_state.imr & IMR_RXRA) { - csr_data |= CSRUART; + SET_INT; } } break; @@ -581,7 +610,7 @@ uint32 iu_read(uint32 pa, size_t size) data = iu_state.ipcr; /* Reading the port resets it */ iu_state.ipcr = 0; - csr_data &= ~CSRUART; + CLR_INT; break; case ISR: data = iu_state.istat; @@ -610,7 +639,7 @@ uint32 iu_read(uint32 pa, size_t size) iu_contty.stat &= ~(STS_RXR|STS_FFL); iu_state.istat &= ~ISTS_RBI; } else if (iu_state.imr & IMR_RXRB) { - csr_data |= CSRUART; + SET_INT; } } break; @@ -625,12 +654,12 @@ uint32 iu_read(uint32 pa, size_t size) case STOP_CTR: data = 0; iu_state.istat &= ~ISTS_CRI; - csr_data &= ~CSRUART; + CLR_INT; sim_cancel(&iu_timer_unit); break; case 17: /* Clear DMAC interrupt */ data = 0; - csr_data &= ~CSRDMA; + CLR_DMA_INT; break; default: break; @@ -681,7 +710,10 @@ void iu_write(uint32 pa, uint32 val, size_t size) break; case IMR: iu_state.imr = bval; - csr_data &= ~CSRUART; + 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(); @@ -784,7 +816,7 @@ t_stat iu_tx(uint8 portno, uint8 val) p->stat |= STS_RXR; if (iu_state.imr & imr_mask) { iu_state.istat |= ists; - csr_data |= CSRUART; + SET_INT; } return SCPE_OK; @@ -800,12 +832,12 @@ t_stat iu_tx(uint8 portno, uint8 val) /* Write the character to the SIMH console */ sim_debug(EXECUTE_MSG, &tto_dev, "[iu_tx] CONSOLE transmit %02x (%c)\n", - (uint8) c, (char) c); + (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, (char) c); + (uint8) c, PCHAR(c)); status = tmxr_putc_ln(&contty_ldsc[0], c); } } @@ -955,12 +987,16 @@ void iu_dma_console(uint8 channel, uint32 service_address) } } + /* 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); - csr_data |= CSRDMA; + SET_DMA_INT; } void iu_dma_contty(uint8 channel, uint32 service_address) @@ -1005,5 +1041,5 @@ void iu_dma_contty(uint8 channel, uint32 service_address) dma_state.mask |= (1 << channel); dma_state.status |= (1 << channel); - csr_data |= CSRDMA; + SET_DMA_INT; } diff --git a/3B2/3b2_mau.h b/3B2/3b2_mau.h new file mode 100644 index 00000000..fc5589a8 --- /dev/null +++ b/3B2/3b2_mau.h @@ -0,0 +1,37 @@ +/* 3b2_mau.h: Common CSR 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_MAU_H_ +#define _3B2_MAU_H_ + +/* TODO: Update when rev3 mau is implemented */ +#include "3b2_rev2_mau.h" + +#endif /* _3B2_MAU_H */ diff --git a/3B2/3b2_mem.c b/3B2/3b2_mem.c index f3b40b0b..90db6946 100644 --- a/3B2/3b2_mem.c +++ b/3B2/3b2_mem.c @@ -30,16 +30,10 @@ #include "3b2_mem.h" -#if defined(REV3) -#include "3b2_rev3_csr.h" -#include "3b2_rev3_mmu.h" -#else -#include "3b2_rev2_csr.h" -#include "3b2_rev2_mmu.h" -#endif - #include "3b2_cpu.h" +#include "3b2_csr.h" #include "3b2_io.h" +#include "3b2_mmu.h" t_bool addr_is_rom(uint32 pa) { diff --git a/3B2/3b2_mmu.h b/3B2/3b2_mmu.h new file mode 100644 index 00000000..54192991 --- /dev/null +++ b/3B2/3b2_mmu.h @@ -0,0 +1,40 @@ +/* 3b2_mmu.h: Common MMU 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_MMU_H_ +#define _3B2_MMU_H_ + +#if defined(REV3) +#include "3b2_rev3_mmu.h" +#else +#include "3b2_rev2_mmu.h" +#endif + +#endif /* _3B2_MMU_H_ */ diff --git a/3B2/3b2_ni.c b/3B2/3b2_ni.c index 29576f18..c17dccd6 100644 --- a/3B2/3b2_ni.c +++ b/3B2/3b2_ni.c @@ -97,7 +97,7 @@ #include "3b2_io.h" #include "3b2_mem.h" -#include "3b2_stddev.h" +#include "3b2_timer.h" /* State container for the card */ NI_STATE ni; @@ -286,12 +286,12 @@ static void ni_disable() sim_debug(DBG_TRACE, &ni_dev, "[ni_disable] Disabling the interface.\n"); ni.enabled = FALSE; - cio[ni.cid].intr = FALSE; sim_cancel(ni_unit); sim_cancel(rcv_unit); sim_cancel(rq_unit); sim_cancel(cio_unit); sim_cancel(sanity_unit); + CIO_CLR_INT(ni.cid); } static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp) @@ -870,7 +870,7 @@ t_stat ni_sanity_svc(UNIT *uptr) cio_cqueue(ni.cid, CIO_STAT, NIQESIZE, &cqe, app_data); if (cio[ni.cid].ivec > 0) { - cio[ni.cid].intr = TRUE; + CIO_SET_INT(ni.cid); } sim_activate_after(sanity_unit, NI_SANITY_INTERVAL_US); @@ -885,7 +885,7 @@ t_stat ni_cio_svc(UNIT *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[ni.cid].intr = TRUE; + CIO_SET_INT(ni.cid); } return SCPE_OK; @@ -952,7 +952,7 @@ void ni_process_packet() /* Trigger an interrupt */ if (cio[ni.cid].ivec > 0) { - cio[ni.cid].intr = TRUE; + CIO_SET_INT(ni.cid); } } diff --git a/3B2/3b2_ports.c b/3B2/3b2_ports.c index 00dab2a1..4f6512a3 100644 --- a/3B2/3b2_ports.c +++ b/3B2/3b2_ports.c @@ -49,7 +49,7 @@ #include "3b2_cpu.h" #include "3b2_io.h" #include "3b2_mem.h" -#include "3b2_stddev.h" +#include "3b2_timer.h" /* Static function declarations */ static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val, @@ -511,7 +511,7 @@ static void ports_update_conn(uint32 ln) /* Interrupt */ if (cio[cid].ivec > 0) { - cio[cid].intr = TRUE; + CIO_SET_INT(cid); } } @@ -677,7 +677,7 @@ t_stat ports_cio_svc(UNIT *uptr) ports_int_cid, ports_int_subdev); if (cio[ports_int_cid].ivec > 0) { - cio[ports_int_cid].intr = TRUE; + CIO_SET_INT(ports_int_cid); } switch (cio[ports_int_cid].op) { @@ -740,7 +740,7 @@ t_stat ports_rcv_svc(UNIT *uptr) if (cio[cid].ivec > 0 && cio_rqueue(cid, PORTS_RCV_QUEUE, PPQESIZE, &rentry, rapp_data) == SCPE_OK) { - cio[cid].intr = TRUE; + CIO_SET_INT(cid); /* Write the character to the memory address */ pwrite_b(rentry.address, c); @@ -814,7 +814,7 @@ t_stat ports_xmt_svc(UNIT *uptr) centry.address = ports_state[ln].tx_req_addr; app_data[0] = RC_FLU; cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data); - cio[cid].intr = TRUE; + CIO_SET_INT(cid); } } } diff --git a/3B2/3b2_rev2_csr.c b/3B2/3b2_rev2_csr.c index 8778d207..aea82f1b 100644 --- a/3B2/3b2_rev2_csr.c +++ b/3B2/3b2_rev2_csr.c @@ -32,7 +32,7 @@ #include "3b2_cpu.h" #include "3b2_sys.h" -#include "3b2_stddev.h" +#include "3b2_timer.h" uint16 csr_data; @@ -166,15 +166,19 @@ void csr_write(uint32 pa, uint32 val, size_t size) break; case 0x33: /* Set PIR9 */ csr_data |= CSRPIR9; + CPU_SET_INT(INT_PIR9); break; case 0x37: /* Clear PIR9 */ csr_data &= ~CSRPIR9; + CPU_CLR_INT(INT_PIR9); break; case 0x3b: /* Set PIR8 */ csr_data |= CSRPIR8; + CPU_SET_INT(INT_PIR8); break; case 0x3f: /* Clear PIR8 */ csr_data &= ~CSRPIR8; + CPU_CLR_INT(INT_PIR8); break; default: break; diff --git a/3B2/3b2_rev2_defs.h b/3B2/3b2_rev2_defs.h index 4e036807..3448d92c 100644 --- a/3B2/3b2_rev2_defs.h +++ b/3B2/3b2_rev2_defs.h @@ -79,6 +79,18 @@ #define CSRDMA 0x0002 /* DMA Interrupt */ #define CSRIOF 0x0001 /* I/O Board Fail */ +/* Interrupt Sources */ +#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_DISK 0x10 /* IPL 11 */ +#define INT_FLOPPY 0x20 /* IPL 11 */ +#define INT_PIR9 0x40 /* IPL 9 */ +#define INT_PIR8 0x80 /* IPL 8 */ + +#define INT_MAP_LEN 0x100 + /* Memory */ #define MEMSIZE_REG 0x4C003 #define MEMID_512K 0 diff --git a/3B2/3b2_rev2_mau.c b/3B2/3b2_rev2_mau.c index 79cce5cd..c9755d6e 100644 --- a/3B2/3b2_rev2_mau.c +++ b/3B2/3b2_rev2_mau.c @@ -85,15 +85,9 @@ #include -/* TODO: Simplify after 3b2_rev3_mau is implemented */ -#if defined(REV3) -#include "3b2_rev3_mmu.h" -#else -#include "3b2_rev2_mmu.h" -#endif - #include "3b2_cpu.h" #include "3b2_mem.h" +#include "3b2_mmu.h" #define MAU_ID 0 /* Coprocessor ID of MAU */ @@ -282,7 +276,11 @@ DEVICE mau_dev = { NULL, /* attach routine */ NULL, /* detach routine */ NULL, /* context */ - DEV_DISABLE|DEV_DIS|DEV_DEBUG, /* flags */ +#ifdef REV3 + DEV_DEBUG, /* Rev 3 flags: Always required */ +#else + DEV_DISABLE|DEV_DIS|DEV_DEBUG, /* Rev 2 flags */ +#endif 0, /* debug control flags */ mau_debug, /* debug flag names */ NULL, /* memory size change */ diff --git a/3B2/3b2_rev2_mmu.c b/3B2/3b2_rev2_mmu.c index f3e9e8ac..9c9dc2b0 100644 --- a/3B2/3b2_rev2_mmu.c +++ b/3B2/3b2_rev2_mmu.c @@ -28,10 +28,10 @@ from the author. */ -#include "3b2_rev2_mmu.h" -#include "3b2_sys.h" #include "3b2_cpu.h" +#include "3b2_mmu.h" +#include "3b2_sys.h" UNIT mmu_unit = { UDATA(NULL, 0, 0) }; diff --git a/3B2/3b2_rev2_mmu.h b/3B2/3b2_rev2_mmu.h index 889dd9a9..6090971e 100644 --- a/3B2/3b2_rev2_mmu.h +++ b/3B2/3b2_rev2_mmu.h @@ -187,9 +187,6 @@ /* Shift and mask the flag bits for the current CPU mode */ #define MMU_PERM(f) ((f >> ((3 - CPU_CM) * 2)) & 3) -#define ROM_SIZE 0x10000 -#define BOOT_CODE_SIZE 0x8000 - /* Codes set in the MMU Fault register */ #define MMU_F_SDTLEN 0x03 #define MMU_F_PW 0x04 diff --git a/3B2/3b2_rev2_sys.c b/3B2/3b2_rev2_sys.c index c290d203..f869f4ec 100644 --- a/3B2/3b2_rev2_sys.c +++ b/3B2/3b2_rev2_sys.c @@ -31,14 +31,16 @@ #include "3b2_defs.h" #include "3b2_cpu.h" +#include "3b2_csr.h" #include "3b2_ctc.h" #include "3b2_id.h" #include "3b2_if.h" #include "3b2_iu.h" #include "3b2_ni.h" #include "3b2_ports.h" -#include "3b2_rev2_mau.h" +#include "3b2_mau.h" #include "3b2_stddev.h" +#include "3b2_timer.h" char sim_name[] = "AT&T 3B2/400"; diff --git a/3B2/3b2_rev2_timer.c b/3B2/3b2_rev2_timer.c new file mode 100644 index 00000000..77c7363f --- /dev/null +++ b/3B2/3b2_rev2_timer.c @@ -0,0 +1,401 @@ +/* 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 new file mode 100644 index 00000000..d30c0202 --- /dev/null +++ b/3B2/3b2_rev2_timer.h @@ -0,0 +1,72 @@ +/* 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 80b74c8c..3b6afe57 100644 --- a/3B2/3b2_rev3_csr.c +++ b/3B2/3b2_rev3_csr.c @@ -28,11 +28,10 @@ from the author. */ -#include "3b2_rev3_csr.h" - #include "3b2_cpu.h" +#include "3b2_csr.h" #include "3b2_if.h" -#include "3b2_stddev.h" +#include "3b2_timer.h" uint32 csr_data; @@ -131,6 +130,14 @@ uint32 csr_read(uint32 pa, size_t size) } } +#define SET_INT(flag, val) { \ + if (val) { \ + CPU_SET_INT(flag); \ + } else { \ + CPU_CLR_INT(flag); \ + } \ + } + void csr_write(uint32 pa, uint32 val, size_t size) { uint32 reg = pa - CSRBASE; @@ -139,24 +146,31 @@ void csr_write(uint32 pa, uint32 val, size_t size) case 0x00: CSRBIT(CSRCLK, val); + SET_INT(INT_CLOCK, val); break; case 0x04: CSRBIT(CSRPWRDN, val); + SET_INT(INT_PWRDWN, val); break; case 0x08: CSRBIT(CSROPINT15, val); + SET_INT(INT_BUS_OP, val); break; case 0x0c: CSRBIT(CSRUART, val); + SET_INT(INT_UART, val); break; case 0x10: CSRBIT(CSRDMA, val); + SET_INT(INT_UART_DMA, val); break; case 0x14: CSRBIT(CSRPIR9, val); + SET_INT(INT_PIR9, val); break; case 0x18: CSRBIT(CSRPIR8, val); + SET_INT(INT_PIR8, val); break; case 0x1c: CSRBIT(CSRITIM, val); @@ -234,15 +248,31 @@ void csr_write(uint32 pa, uint32 val, size_t size) break; case 0x5c: CSRBIT(CSRSBERR, val); + if (val) { + if (!(csr_data & CSRISBERR)) { + SET_INT(INT_SBERR, TRUE); + } + } else { + SET_INT(INT_SBERR, FALSE); + } break; case 0x60: CSRBIT(CSRMBERR, val); + SET_INT(INT_MBERR, val); break; case 0x64: CSRBIT(CSRUBUBF, val); + SET_INT(INT_BUS_RXF, val); break; case 0x68: CSRBIT(CSRTIMO, val); + if (val) { + if (!(csr_data & CSRITIMO)) { + SET_INT(INT_BUS_TMO, TRUE); + } + } else { + SET_INT(INT_BUS_TMO, FALSE); + } break; case 0x6c: CSRBIT(CSRFRF, val); diff --git a/3B2/3b2_rev3_defs.h b/3B2/3b2_rev3_defs.h index fe4d553f..2c5289dd 100644 --- a/3B2/3b2_rev3_defs.h +++ b/3B2/3b2_rev3_defs.h @@ -33,12 +33,63 @@ #define NUM_REGISTERS 32 -#define DEFMEMSIZE MSIZ_4M +#define DEFMEMSIZE MSIZ_16M #define MAXMEMSIZE MSIZ_64M #define HWORD_OP_COUNT 12 #define CPU_VERSION 0x1F /* Version encoded in WE32200 */ +/* CSR Flags */ +#define CSRCLK 1u /* UNIX Interval Timer Timeout */ +#define CSRPWRDN (1u << 1) /* Power Down Request */ +#define CSROPINT15 (1u << 2) /* Oper. Interrupt Level 15 */ +#define CSRUART (1u << 3) /* DUART Interrupt */ +#define CSRDMA (1u << 4) /* DUART DMA Complete Interrupt */ +#define CSRPIR9 (1u << 5) /* Programmed Interrupt 9 */ +#define CSRPIR8 (1u << 6) /* Programmed Interrupt 8 */ +#define CSRITIM (1u << 7) /* Inhibit UNIX Interval Timer */ +#define CSRISTIM (1u << 8) /* Inhibit System Sanity Timer */ +#define CSRITIMO (1u << 9) /* Inhibit Bus Timer */ +#define CSRICPUFLT (1u << 10) /* Inhibit Faults to CPU */ +#define CSRISBERR (1u << 11) /* Inhibit Single Bit Error Rpt */ +#define CSRIIOBUS (1u << 12) /* Inhibit Integral 3B2 I/O Bus */ +#define CSRIBUB (1u << 13) /* Inhibit 4 BUB Slots */ +#define CSRFECC (1u << 14) /* Force ECC Syndrome */ +#define CSRTHERM (1u << 15) /* Thermal Shutdown Request */ +#define CSRLED (1u << 16) /* Failure LED */ +#define CSRPWRSPDN (1u << 17) /* Power Down -- Power Supply */ +#define CSRFLPFST (1u << 18) /* Floppy Speed Fast */ +#define CSRFLPS1 (1u << 19) /* Floppy Side 1 */ +#define CSRFLPMO (1u << 20) /* Floppy Motor On */ +#define CSRFLPDEN (1u << 21) /* Floppy Density */ +#define CSRFLPSZ (1u << 22) /* Floppy Size */ +#define CSRSBERR (1u << 23) /* Single Bit Error */ +#define CSRMBERR (1u << 24) /* Multiple Bit Error */ +#define CSRUBUBF (1u << 25) /* Ubus/BUB Received Fail */ +#define CSRTIMO (1u << 26) /* Bus Timer Timeout */ +#define CSRFRF (1u << 27) /* Fault Registers Frozen */ +#define CSRALGN (1u << 28) /* Data Alignment Error */ +#define CSRSTIMO (1u << 29) /* Sanity Timer Timeout */ +#define CSRABRT (1u << 30) /* Abort Switch Activated */ +#define CSRRRST (1u << 31) /* System Reset Request */ + +/* Interrupt Sources */ +#define INT_CLOCK 0x0001 /* UNIX Interval Timer Timeout - IPL 15 */ +#define INT_PWRDWN 0x0002 /* Power Down Request - IPL 15 */ +#define INT_BUS_OP 0x0004 /* UBUS or BUB Operational Interrupt - IPL 15 */ +#define INT_SBERR 0x0008 /* Single Bit Memory Error - IPL 15 */ +#define INT_MBERR 0x0010 /* Multiple Bit Memory Error - IPL 15 */ +#define INT_BUS_RXF 0x0020 /* UBUS, BUB, EIO Bus Received Fail - IPL 15 */ +#define INT_BUS_TMO 0x0040 /* UBUS Timer Timeout - IPL 15 */ +#define INT_UART_DMA 0x0080 /* UART DMA Complete - IPL 13 */ +#define INT_UART 0x0100 /* UART Interrupt - IPL 13 */ +#define INT_FLOPPY_DMA 0x0200 /* Floppy DMA Complete - IPL 11 */ +#define INT_FLOPPY 0x0400 /* Floppy Interrupt - IPL 11 */ +#define INT_PIR9 0x0800 /* PIR-9 (from CSER) - IPL 9 */ +#define INT_PIR8 0x1000 /* PIR-8 (from CSER) - IPL 8 */ + +#define INT_MAP_LEN 0x2000 + /* Memory */ #define MEMID_4M 6 #define MEMID_16M 7 @@ -95,38 +146,4 @@ #define DMA_IUB 0x47 #define DMA_C 0x48 -/* CSR Flags */ -#define CSRCLK 1u /* UNIX Interval Timer Timeout */ -#define CSRPWRDN (1u << 1) /* Power Down Request */ -#define CSROPINT15 (1u << 2) /* Oper. Interrupt Level 15 */ -#define CSRUART (1u << 3) /* DUART Interrupt */ -#define CSRDMA (1u << 4) /* DUART DMA Complete Interrupt */ -#define CSRPIR9 (1u << 5) /* Programmed Interrupt 9 */ -#define CSRPIR8 (1u << 6) /* Programmed Interrupt 8 */ -#define CSRITIM (1u << 7) /* Inhibit UNIX Interval Timer */ -#define CSRISTIM (1u << 8) /* Inhibit System Sanity Timer */ -#define CSRITIMO (1u << 9) /* Inhibit Bus Timer */ -#define CSRICPUFLT (1u << 10) /* Inhibit Faults to CPU */ -#define CSRISBERR (1u << 11) /* Inhibit Single Bit Error Rpt */ -#define CSRIIOBUS (1u << 12) /* Inhibit Integral 3B2 I/O Bus */ -#define CSRIBUB (1u << 13) /* Inhibit 4 BUB Slots */ -#define CSRFECC (1u << 14) /* Force ECC Syndrome */ -#define CSRTHERM (1u << 15) /* Thermal Shutdown Request */ -#define CSRLED (1u << 16) /* Failure LED */ -#define CSRPWRSPDN (1u << 17) /* Power Down -- Power Supply */ -#define CSRFLPFST (1u << 18) /* Floppy Speed Fast */ -#define CSRFLPS1 (1u << 19) /* Floppy Side 1 */ -#define CSRFLPMO (1u << 20) /* Floppy Motor On */ -#define CSRFLPDEN (1u << 21) /* Floppy Density */ -#define CSRFLPSZ (1u << 22) /* Floppy Size */ -#define CSRSBERR (1u << 23) /* Single Bit Error */ -#define CSRMBERR (1u << 24) /* Multiple Bit Error */ -#define CSRUBUBF (1u << 25) /* Ubus/BUB Received Fail */ -#define CSRTIMO (1u << 26) /* Bus Timer Timeout */ -#define CSRFRF (1u << 27) /* Fault Registers Frozen */ -#define CSRALGN (1u << 28) /* Data Alignment Error */ -#define CSRSTIMO (1u << 29) /* Sanity Timer Timeout */ -#define CSRABRT (1u << 30) /* Abort Switch Activated */ -#define CSRRRST (1u << 31) /* System Reset Request */ - #endif diff --git a/3B2/3b2_rev3_mmu.c b/3B2/3b2_rev3_mmu.c index 211f0006..898be53b 100644 --- a/3B2/3b2_rev3_mmu.c +++ b/3B2/3b2_rev3_mmu.c @@ -28,11 +28,11 @@ from the author. */ -#include "3b2_rev3_mmu.h" #include "3b2_cpu.h" +#include "3b2_csr.h" #include "3b2_mem.h" -#include "3b2_rev3_csr.h" +#include "3b2_mmu.h" UNIT mmu_unit = { UDATA(NULL, 0, 0) }; diff --git a/3B2/3b2_rev3_mmu.h b/3B2/3b2_rev3_mmu.h index a542e504..a946553a 100644 --- a/3B2/3b2_rev3_mmu.h +++ b/3B2/3b2_rev3_mmu.h @@ -167,14 +167,14 @@ #define SDC_IDX(va) ((uint8)((va) >> 17) & 7) /* Convert from sd to sd cache entry */ -#define SD_TO_SDCH(hi,lo) ((hi) & SD_ADDR_MASK | \ - ((lo) & SD_C_MASK) >> 1 | \ - ((lo) & SD_CACHE_MASK) >> 1 | \ +#define SD_TO_SDCH(hi,lo) (((hi) & SD_ADDR_MASK) | \ + ((lo) & SD_C_MASK) >> 1 | \ + ((lo) & SD_CACHE_MASK) >> 1 | \ (SDC_G_MASK)) -#define SD_TO_SDCL(lo,va) (((lo) & SD_ACC_MASK) | \ - ((lo) & SD_MAX_OFF_MASK) >> 3 | \ - ((lo) & SD_R_MASK) << 18 | \ - ((lo) & SD_M_MASK) << 21 | \ +#define SD_TO_SDCL(lo,va) (((lo) & SD_ACC_MASK) | \ + ((lo) & SD_MAX_OFF_MASK) >> 3 | \ + ((lo) & SD_R_MASK) << 18 | \ + ((lo) & SD_M_MASK) << 21 | \ ((va) & SD_VADDR_MASK) >> 20) /* Convert from sd cache entry to sd */ @@ -188,18 +188,18 @@ SD_V_MASK | SD_P_MASK) /* Convert from pd cache entry to pd */ -#define PDCE_TO_PD(pdcl) (((pdcl & PDC_PADDR_MASK) << 11) | \ - ((pdcl & PDC_W_MASK) >> 17) | \ - ((pdcl & PDC_M_MASK) >> 21) | \ - ((pdcl & PDC_R_MASK) >> 18) | \ +#define PDCE_TO_PD(pdcl) ((((pdcl) & PDC_PADDR_MASK) << 11) | \ + (((pdcl) & PDC_W_MASK) >> 17) | \ + (((pdcl) & PDC_M_MASK) >> 21) | \ + (((pdcl) & PDC_R_MASK) >> 18) | \ PD_P_MASK) /* Convert from pd to pd cache entry (low word) */ -#define PD_TO_PDCL(pd, sd_lo) (((pd & PD_PADDR_MASK) >> 11) | \ - ((pd & PD_W_MASK) << 17) | \ - ((pd & PD_M_MASK) << 21) | \ - ((pd & PD_R_MASK) << 18) | \ - (sd_lo & SD_ACC_MASK)) +#define PD_TO_PDCL(pd, sd_lo) ((((pd) & PD_PADDR_MASK) >> 11) | \ + (((pd) & PD_W_MASK) << 17) | \ + (((pd) & PD_M_MASK) << 21) | \ + (((pd) & PD_R_MASK) << 18) | \ + ((sd_lo) & SD_ACC_MASK)) /* Convert from va to pd cache entry (high word / tag) */ #define VA_TO_PDCH(va, sd_lo) ((1 << 30) | \ diff --git a/3B2/3b2_rev3_sys.c b/3B2/3b2_rev3_sys.c index 38f51807..8c32ba46 100644 --- a/3B2/3b2_rev3_sys.c +++ b/3B2/3b2_rev3_sys.c @@ -31,13 +31,15 @@ #include "3b2_defs.h" #include "3b2_cpu.h" +#include "3b2_csr.h" #include "3b2_if.h" #include "3b2_iu.h" +#include "3b2_mau.h" #include "3b2_ni.h" #include "3b2_ports.h" -#include "3b2_rev2_mau.h" /* TODO: Replace with Rev 3 MAU when implemented */ #include "3b2_scsi.h" #include "3b2_stddev.h" +#include "3b2_timer.h" char sim_name[] = "AT&T 3B2/600G"; diff --git a/3B2/3b2_rev3_timer.c b/3B2/3b2_rev3_timer.c new file mode 100644 index 00000000..085a7ffd --- /dev/null +++ b/3B2/3b2_rev3_timer.c @@ -0,0 +1,506 @@ +/* 3b2_rev3_timer.c: 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. +*/ + +/* + * 82C54 Timer. + * + * 82C54 (Rev3) Timer IC has three interval timers, which we treat + * here as three units. + * + * In the 3B2, the three timers are assigned specific purposes: + * + * - Timer 0: SYSTEM SANITY TIMER. This timer is normally loaded with + * a short timeout and allowed to run. If it times out, it + * will generate an interrupt and cause a system + * error. Software resets the timer regularly to ensure + * that it does not time out. It is fed by a 10 kHz + * clock, so each single counting step of this timer is + * 100 microseconds. + * + * - Timer 1: UNIX INTERVAL TIMER. This is the main timer that drives + * process switching in Unix. It operates at a fixed rate, + * and the counter is set up by Unix to generate an + * interrupt once every 10 milliseconds. The timer is fed + * by a 100 kHz clock, so each single counting step of + * this timer is 10 microseconds. + * + * - Timer 2: BUS TIMEOUT TIMER. This timer is reset every time the + * IO bus is accessed, and then stopped when the IO bus + * responds. It is mainly used to determine when the IO + * bus is hung (e.g., no card is installed in a given + * slot, so nothing can respond). When it times out, it + * generates an interrupt. It is fed by a 500 kHz clock, + * so each single counting step of this timer is 2 + * microseconds. + * + * + * 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. + * + * - The Bus Timeout Timer is not implemented other than a stub that + * is designed to pass hardware diagnostics. The simulator IO + * subsystem always sets the correct interrupt directly if the bus + * will not respond. + * + * - 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. + */ + +#include "3b2_cpu.h" +#include "3b2_csr.h" +#include "3b2_defs.h" +#include "3b2_timer.h" + +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) }, + { NULL } +}; + +UNIT *timer_clk_unit = &timer_unit[1]; + +REG timer_reg[] = { + { HRDATAD(DIV0, TIMERS[0].divider, 16, "Divider (0)") }, + { HRDATAD(COUNT0, TIMERS[0].val, 16, "Count (0)") }, + { HRDATAD(CTRL0, TIMERS[0].ctrl, 8, "Control (0)") }, + { HRDATAD(DIV1, TIMERS[1].divider, 16, "Divider (1)") }, + { HRDATAD(COUNT1, TIMERS[1].val, 16, "Count (1)") }, + { HRDATAD(CTRL1, TIMERS[1].ctrl, 8, "Control (1)") }, + { HRDATAD(DIV2, TIMERS[2].divider, 16, "Divider (2)") }, + { HRDATAD(COUNT2, TIMERS[2].val, 16, "Count (2)") }, + { HRDATAD(CTRL2, TIMERS[2].ctrl, 8, "Control (2)") }, + { NULL } +}; + +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 +}; + +t_stat timer_reset(DEVICE *dptr) { + int32 i; + + 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]; + } + + /* 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; +} + +static void timer_activate(uint8 ctrnum) +{ + struct timer_ctr *ctr; + + 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--; + } else { + sim_debug(EXECUTE_MSG, &timer_dev, + "[%08x] SANITY TIMER: Currently disabled, not starting\n", + R[NUM_PC]); + } + 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 + */ +t_stat timer0_svc(UNIT *uptr) +{ + struct timer_ctr *ctr; + + 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; + 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; + CSRBIT(CSRTIMO, TRUE); + CPU_SET_INT(INT_BUS_TMO); + /* Also trigger a bus abort */ + cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); + } + } + + return SCPE_OK; +} + +uint32 timer_read(uint32 pa, size_t size) +{ + uint32 reg; + uint16 ctr_val; + uint8 ctrnum, retval; + 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; + + 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; + case TIMER_REG_CTRL: + return ctr->ctrl; + 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; + 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; + UNIT *unit = &timer_unit[ctrnum]; + + ctr = &TIMERS[ctrnum]; + ctr->enabled = TRUE; + + switch(TIMER_RW(ctr)) { + 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); + 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); + timer_activate(ctrnum); + break; + case CLK_LMB: + if (ctr->w_lmb) { + 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); + 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); + } + 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: + 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) { + ctr->ctrl_latch = (uint16) TIMERS[2].ctrl; + ctr->r_ctrl_latch = TRUE; + } + if ((val & 0x20) == 0) { + ctr->cnt_latch = ctr->val; + ctr->r_cnt_latch = TRUE; + } + } + if (val & 4) { + ctr = &TIMERS[1]; + if ((val & 0x10) == 0) { + ctr->ctrl_latch = (uint16) TIMERS[2].ctrl; + ctr->r_ctrl_latch = TRUE; + } + if ((val & 0x20) == 0) { + ctr->cnt_latch = ctr->val; + ctr->r_cnt_latch = TRUE; + } + } + if (val & 8) { + ctr = &TIMERS[2]; + if ((val & 0x10) == 0) { + ctr->ctrl_latch = (uint16) TIMERS[2].ctrl; + ctr->r_ctrl_latch = TRUE; + } + if ((val & 0x20) == 0) { + ctr->cnt_latch = ctr->val; + ctr->r_cnt_latch = TRUE; + } + } + } 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; + ctr->w_lmb = FALSE; + ctr->r_lmb = FALSE; + ctr->val = 0xffff; + ctr->divider = 0xffff; + } + break; + case TIMER_CLR_LATCH: + sim_debug(WRITE_MSG, &timer_dev, + "[%08x] unexpected write to clear timer latch\n", + R[NUM_PC]); + break; + default: + sim_debug(WRITE_MSG, &timer_dev, + "[%08x] unknown timer register: %d\n", + R[NUM_PC], reg); + } +} diff --git a/3B2/3b2_rev3_timer.h b/3B2/3b2_rev3_timer.h new file mode 100644 index 00000000..c4379763 --- /dev/null +++ b/3B2/3b2_rev3_timer.h @@ -0,0 +1,79 @@ +/* 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_scsi.c b/3B2/3b2_scsi.c index a7177e95..453563c2 100644 --- a/3B2/3b2_scsi.c +++ b/3B2/3b2_scsi.c @@ -70,7 +70,7 @@ static struct scsi_dev_t ha_tab[] = { HA_TAPE(ST120) }; -#define SCSI_U_FLAGS (UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_DIS+ \ +#define SCSI_U_FLAGS (UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ \ UNIT_ROABLE+(SD327_DTYPE << UNIT_V_DTYPE)) UNIT ha_unit[] = { @@ -335,7 +335,7 @@ t_stat ha_svc(UNIT *uptr) sim_debug(HA_TRACE, &ha_dev, "[%08x] [ha_svc] IRQ for board %d (VEC=%d).\n", R[NUM_PC], ha_state.cid, cio[ha_state.cid].ivec); - cio[ha_state.cid].intr = TRUE; + CIO_SET_INT(ha_state.cid); } return SCPE_OK; diff --git a/3B2/3b2_stddev.c b/3B2/3b2_stddev.c index d3a54bc7..6d2dba9f 100644 --- a/3B2/3b2_stddev.c +++ b/3B2/3b2_stddev.c @@ -1,4 +1,4 @@ -/* 3b2_rev2_stddev.h: AT&T 3B2 Rev 2 (Model 400) System Devices Implementation +/* 3b2_stddev.c: AT&T 3B2 miscellaneous system board devices. Copyright (c) 2017, Seth J. Morabito @@ -32,21 +32,15 @@ This file contains system-specific registers and devices for the following 3B2 devices: - - timer 8253 interval timer - nvram Non-Volatile RAM - - csr Control Status Registers - tod MM58174A Real-Time-Clock + - flt Fault Register */ #include "3b2_stddev.h" -#if defined(REV3) -#include "3b2_rev3_csr.h" -#else -#include "3b2_rev2_csr.h" -#endif - #include "3b2_cpu.h" +#include "3b2_csr.h" DEBTAB sys_deb_tab[] = { { "INIT", INIT_MSG, "Init" }, @@ -58,14 +52,9 @@ DEBTAB sys_deb_tab[] = { { NULL, 0 } }; -struct timer_ctr TIMERS[3]; - uint32 *NVRAM = NULL; -int32 tmxr_poll = 16667; - /* NVRAM */ - UNIT nvram_unit = { UDATA(NULL, UNIT_FIX+UNIT_BINK, NVRSIZE) }; @@ -239,390 +228,8 @@ void nvram_write(uint32 pa, uint32 val, size_t size) } } -/* - * 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. - * - */ - -/* - * 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: -#ifdef REV3 - if ((csr_data & CSRISTIM) == 0) { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] SANITY TIMER: Activating after %d steps\n", - R[NUM_PC], ctr->val); - sim_activate_abs(&timer_unit[ctrnum], ctr->val); - ctr->val--; - } else { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] SANITY TIMER: Currently disabled, not starting\n", - R[NUM_PC]); - } -#endif - break; - case TIMER_INTERVAL: - if ((csr_data & CSRITIM) == 0) { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] INTERVAL TIMER: Activating after %d ms\n", - R[NUM_PC], ctr->val); - sim_activate_after_abs(&timer_unit[ctrnum], ctr->val); - ctr->val--; - } else { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] INTERVAL TIMER: Currently disabled, not starting\n", - R[NUM_PC]); - } - break; - case TIMER_BUS: -#ifdef REV3 - if ((csr_data & CSRITIMO) == 0) { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] BUS TIMER: Activating after %d steps\n", - R[NUM_PC], ctr->val); - sim_activate_abs(&timer_unit[ctrnum], (ctr->val - 2)); - ctr->val -= 2; - } else { - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] BUS TIMER: Currently disabled, not starting\n", - R[NUM_PC]); - } -#endif - break; - default: - break; - } -} - -t_stat timer_set_shutdown(UNIT *uptr, int32 val, CONST char* cptr, void* desc) -{ - struct timer_ctr *sanity = (struct timer_ctr *)timer_unit[0].tmr; - - sim_debug(EXECUTE_MSG, &timer_dev, - "[%08x] Setting sanity timer to 0 for shutdown.\n", R[NUM_PC]); - - sanity->val = 0; - csr_data &= ~CSRCLK; - csr_data |= CSRTIMO; - - 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 */ - csr_data |= CSRCLK; - } - - 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 */ - csr_data &= ~CSRCLK; - 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; - } -} - /* * MM58174A Time Of Day Clock - * - * Despite its name, this device is not used by the 3B2 as a clock. It - * is only used to store the current date and time between boots. It - * is set when an operator changes the date and time. Is is read at - * boot time. Therefore, we do not need to treat it as a clock or - * timer device here. */ UNIT tod_unit = { diff --git a/3B2/3b2_stddev.h b/3B2/3b2_stddev.h index 12d8dcc8..c709fd00 100644 --- a/3B2/3b2_stddev.h +++ b/3B2/3b2_stddev.h @@ -1,4 +1,4 @@ -/* 3b2_rev2_stddev.h: AT&T 3B2 Rev 2 (Model 400) System Devices Header +/* 3b2_stddev.h: AT&T 3B2 miscellaneous system board devices. Copyright (c) 2017, Seth J. Morabito @@ -28,40 +28,11 @@ from the author. */ -#ifndef _3B2_REV2_SYSDEV_H_ -#define _3B2_REV2_SYSDEV_H_ +#ifndef _3B2_STDDEV_H_ +#define _3B2_STDDEV_H_ #include "3b2_defs.h" -/* Timer definitions */ -#define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */ -#define TPS_CLK 100 /* 100 ticks per second */ - -#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 */ -}; - /* NVRAM */ t_stat nvram_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); t_stat nvram_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw); @@ -73,28 +44,7 @@ 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); -/* 8253 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); -void timer_tick(); -t_stat timer0_svc(UNIT *uptr); -t_stat timer1_svc(UNIT *uptr); -t_stat timer2_svc(UNIT *uptr); -t_stat timer_set_shutdown(UNIT *uptr, int32 val, CONST char *cptr, void *desc); -void timer_disable(uint8 ctrnum); -void timer_enable(uint8 ctrnum); - -/* CSR */ -t_stat csr_svc(UNIT *uptr); -t_stat csr_ex(t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); -t_stat csr_dep(t_value val, t_addr exta, UNIT *uptr, int32 sw); -t_stat csr_reset(DEVICE *dptr); -uint32 csr_read(uint32 pa, size_t size); -void csr_write(uint32 pa, uint32 val, size_t size); - /* TOD */ - typedef struct tod_data { int32 delta; /* Delta between simulated time and real time (sec.) */ uint8 tsec; /* 1/10 seconds */ @@ -125,11 +75,8 @@ void tod_write(uint32, uint32 val, size_t size); #if defined(REV3) /* Fault Register */ - uint32 flt_read(uint32 pa, size_t size); void flt_write(uint32 pa, uint32 val, size_t size); #endif -extern int32 tmxr_poll; - -#endif /* _3B2_REV2_SYSDEV_H_ */ +#endif /* _3B2_STDDEV_H_ */ diff --git a/3B2/3b2_sys.h b/3B2/3b2_sys.h index 6ed6adf5..d3fa72e2 100644 --- a/3B2/3b2_sys.h +++ b/3B2/3b2_sys.h @@ -28,8 +28,8 @@ from the author. */ -#ifndef _3B2_REV2_SYS_H_ -#define _3B2_REV2_SYS_H_ +#ifndef _3B2_SYS_H_ +#define _3B2_SYS_H_ #include "3b2_defs.h" @@ -43,4 +43,4 @@ extern char sim_name[]; extern REG *sim_PC; extern int32 sim_emax; -#endif /* _3B2_REV2_SYS_H_ */ +#endif /* _3B2_SYS_H_ */ diff --git a/3B2/3b2_timer.h b/3B2/3b2_timer.h new file mode 100644 index 00000000..35df9b67 --- /dev/null +++ b/3B2/3b2_timer.h @@ -0,0 +1,42 @@ +/* 3b2_timer.h: Common TIMER 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_TIMER_H_ +#define _3B2_TIMER_H_ + +#if defined(REV3) +#include "3b2_rev3_timer.h" +#else +#include "3b2_rev2_timer.h" +#endif + +extern int32 tmxr_poll; + +#endif /* _3B2_TIMER_H_ */ diff --git a/3B2/README.md b/3B2/README.md index ab073a89..96eb02f8 100644 --- a/3B2/README.md +++ b/3B2/README.md @@ -1,7 +1,13 @@ AT&T 3B2 Simulator ================== -This module contains a simulator for the AT&T 3B2 Model 400 microcomputer. +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. Full documentation for the 3B2 simulator is available here: @@ -23,6 +29,7 @@ devices are given in parentheses: - uPD7261A Integrated MFM Fixed Disk Controller (IDISK) - Non-Volatile Memory (NVRAM) - MM58174A Time Of Day Clock (TOD) + - CM195A Ethernet Network Interface (NI) - CM195B 4-port Serial MUX (PORTS) - CM195H Cartridge Tape Controller (CTC) diff --git a/Visual Studio Projects/3B2-600.vcproj b/Visual Studio Projects/3B2-600.vcproj index 162b7054..b508f7d0 100644 --- a/Visual Studio Projects/3B2-600.vcproj +++ b/Visual Studio Projects/3B2-600.vcproj @@ -239,6 +239,10 @@ RelativePath="..\3B2\3b2_rev3_sys.c" > + + @@ -536,11 +540,11 @@ > + + + + @@ -559,6 +563,10 @@ RelativePath="..\3B2\3b2_rev2_csr.h" > + + diff --git a/makefile b/makefile index c4164178..fa48c560 100644 --- a/makefile +++ b/makefile @@ -2035,21 +2035,22 @@ 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_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 + ${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_ni.c 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_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 + ${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 \ + ${ATT3B2D}/3b2_scsi.c ${ATT3B2D}/3b2_ni.c ATT3B2M600_OPT = -DUSE_INT64 -DUSE_ADDR64 -DREV3 -I ${ATT3B2D} ${NETWORK_OPT} SIGMAD = ${SIMHD}/sigma