3B2: Interrupt Refactor and DEMON ROM

- This change introduces a full refactor of the interrupt subsystem
  for the system board (SBD) and the I/O bus (CIO). Interrupt decode
  should now be significantly faster, and not require an expensive
  calculation on every step.

- The TIMER device has been split into Rev 2 and Rev 3
  implementations.

- The optional 3B2/400 Debug Monitor ROMs can now be booted by passing
  the "DEMON" argument to the 3B2/400 simulator BOOT command. Any
  of the following will cause the Debug Monitor ROM to be booted
  instead of the standard 3B2/400 ROM:

     sim> BOOT DEMON
     sim> BOOT CPU DEMON
     sim> BOOT DEMON CPU
This commit is contained in:
Seth Morabito 2021-08-24 06:35:49 -07:00
parent d1a720debd
commit 9d849283a4
41 changed files with 1786 additions and 765 deletions

View file

@ -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.

View file

@ -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;

44
3B2/3b2_csr.h Normal file
View file

@ -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_ */

View file

@ -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

View file

@ -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[];

View file

@ -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;
}

View file

@ -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);

View file

@ -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 */

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

37
3B2/3b2_mau.h Normal file
View file

@ -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 */

View file

@ -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)
{

40
3B2/3b2_mmu.h Normal file
View file

@ -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_ */

View file

@ -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);
}
}

View file

@ -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, &centry, app_data);
cio[cid].intr = TRUE;
CIO_SET_INT(cid);
}
}
}

View file

@ -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;

View file

@ -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

View file

@ -85,15 +85,9 @@
#include <math.h>
/* 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 */

View file

@ -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) };

View file

@ -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

View file

@ -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";

401
3B2/3b2_rev2_timer.c Normal file
View file

@ -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;
}
}

72
3B2/3b2_rev2_timer.h Normal file
View file

@ -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_ */

View file

@ -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);

View file

@ -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

View file

@ -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) };

View file

@ -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) | \

View file

@ -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";

506
3B2/3b2_rev3_timer.c Normal file
View file

@ -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);
}
}

79
3B2/3b2_rev3_timer.h Normal file
View file

@ -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_ */

View file

@ -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;

View file

@ -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 = {

View file

@ -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_ */

View file

@ -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_ */

42
3B2/3b2_timer.h Normal file
View file

@ -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_ */

View file

@ -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)

View file

@ -239,6 +239,10 @@
RelativePath="..\3B2\3b2_rev3_sys.c"
>
</File>
<File
RelativePath="..\3B2\3b2_rev3_timer.c"
>
</File>
<File
RelativePath="..\3B2\3b2_scsi.c"
>
@ -536,11 +540,11 @@
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_csr.h"
RelativePath="..\3B2\3b2_rev3_csr.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_defs.h"
RelativePath="..\3B2\3b2_rev3_defs.h"
>
</File>
<File
@ -548,11 +552,15 @@
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_mmu.h"
RelativePath="..\3B2\3b2_rev3_mmu.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_sys.h"
RelativePath="..\3B2\3b2_rev3_sys.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev3_timer.h"
>
</File>
<File

View file

@ -251,6 +251,10 @@
RelativePath="..\3B2\3b2_rev2_csr.c"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_timer.c"
>
</File>
<File
RelativePath="..\3B2\3b2_stddev.c"
>
@ -559,6 +563,10 @@
RelativePath="..\3B2\3b2_rev2_csr.h"
>
</File>
<File
RelativePath="..\3B2\3b2_rev2_timer.h"
>
</File>
<File
RelativePath="..\3B2\3b2_stddev.h"
>

View file

@ -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