diff --git a/HP3000/hp3000_cpu.c b/HP3000/hp3000_cpu.c index 9b4450cb..538ce5d6 100644 --- a/HP3000/hp3000_cpu.c +++ b/HP3000/hp3000_cpu.c @@ -25,6 +25,13 @@ CPU HP 3000 Series III Central Processing Unit + 29_Dec-16 JDB Changed the status mnemonic flag from REG_S to REG_T + 07-Nov-16 JDB Renamed cpu_byte_to_word_ea to cpu_byte_ea + 03-Nov-16 JDB Added zero offsets to the cpu_call_procedure calls + 01-Nov-16 JDB Added per-instruction trace capability + 24-Oct-16 JDB Renamed SEXT macro to SEXT16 + 22-Sep-16 JDB Moved byte_to_word_address from hp3000_cpu_base.c + 21-Sep-16 JDB Added CIS/NOCIS option 01-Sep-16 JDB Add power fail/power restore support 23-Aug-16 JDB Add module interrupt support 14-Jul-16 JDB Implemented the cold dump process @@ -219,7 +226,7 @@ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | 0 0 1 0 | 0 0 0 1 | firmware option op | Firmware + | 0 0 1 0 | 0 0 0 1 | option set | option subset | Firmware +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ @@ -302,11 +309,19 @@ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | 0 0 1 0 | 0 0 0 1 | 0 1 1 1 1 0 0 | x | DMUL/DDIV + | 0 0 1 0 | 0 0 0 1 | 0 1 1 1 | 1 0 0 | x | DMUL/DDIV +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | 0 0 1 0 | 0 0 0 1 | 0 0 0 0 1 | ext fp op | Extended FP + | 0 0 1 0 | 0 0 0 1 | 0 0 0 0 | 1 | ext fp op | Extended FP + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 0 1 | APL op | APL + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | COBOL op | COBOL +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ @@ -371,6 +386,12 @@ uppercase, regardless of whether or not the option is enabled. Category 4 encodings that do not correspond to instructions are displayed in octal. + All of the base set instructions are one word in length. Most of the + firmware extension instructions occupy one word as well, although some are + two words long. If the first word of a two-word instruction is valid but the + second is not, an Unimplemented Instruction trap will occur, with P pointing + to the location after the second word. + The simulator provides four stop conditions related to instruction execution that may be enabled with a SET CPU STOP= command: @@ -543,6 +564,13 @@ instruction that may alter the base registers, the program, data, and stack segment base registers are printed. + The OPND option traces operand values. Some instructions that take memory + and register operands that are difficult to decode from DATA or REG traces + present the operand values in a higher-level format. The memory bank and + address values are always those of the operands. The operand data and value + presented are specific to the instruction; see the instruction executor + comments for details. + The PSERV option traces process clock event service entries. Each trace reports whether or not the CPU was executing on the Interrupt Control Stack when the process clock ticked. Execution on the ICS implies that the @@ -604,6 +632,14 @@ | +-------------------- zero +------------------------- octal bank (DBANK) + >>CPU opnd: 00.135771 000000 DFLC '+','!' + >>CPU opnd: 00.045071 000252 target fraction 3 length 6,"002222" + ~~ ~~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | | | | + | | | +-- operand-specific value + | | +------------ operand-specific data + | +-------------------- octal address (effective address) + +------------------------- octal bank (PBANK, DBANK, or SBANK) >>CPU pserv: Process clock service entered not on the ICS @@ -676,16 +712,11 @@ #include "hp3000_defs.h" #include "hp3000_cpu.h" #include "hp3000_cpu_ims.h" +#include "hp3000_mem.h" #include "hp3000_io.h" -/* External I/O data structures */ - -extern DEVICE iop_dev; /* I/O Processor */ -extern DEVICE sel_dev; /* Selector Channel */ - - /* Program constants */ #define PCLK_PERIOD mS (1) /* 1 millisecond process clock period */ @@ -724,7 +755,7 @@ extern DEVICE sel_dev; /* Selector Channel */ DEVICE cpu_dev; /* incomplete device structure */ -REG *sim_PC; /* the pointer to the P register */ +REG *sim_PC = NULL; /* the pointer to the P register */ /* CPU global data structures */ @@ -821,20 +852,18 @@ UNIT *cpu_pclk_uptr = &cpu_unit [0]; /* a (constant) pointer to the p /* CPU local state */ -static uint32 sim_stops = 0; /* the current simulation stop flag settings */ -static uint32 cpu_speed = 1; /* the CPU speed, expressed as a multiplier of a real machine */ -static uint32 pclk_increment = 1; /* the process clock increment per event service */ -static uint32 dump_control = 0002006u; /* the cold dump control word (default CNTL = 4, DEVNO = 6 */ +static uint32 sim_stops = 0; /* the current simulation stop flag settings */ +static uint32 cpu_speed = 1; /* the CPU speed, expressed as a multiplier of a real machine */ +static uint32 pclk_increment = 1; /* the process clock increment per event service */ +static uint32 dump_control = 0002006u; /* the cold dump control word (default CNTL = 4, DEVNO = 6 */ +static uint32 debug_save = 0; /* the current debug trace flag settings */ +static HP_WORD exec_mask = 0; /* the current instruction execution trace mask */ +static HP_WORD exec_match = D16_UMAX; /* the current instruction execution trace matching value */ /* CPU local data structures */ -/* Main memory */ - -static MEMORY_WORD *M = NULL; /* the pointer to the main memory allocation */ - - /* Interrupt classification names */ static const char *const interrupt_name [] = { /* class names, indexed by IRQ_CLASS */ @@ -923,7 +952,7 @@ struct FEATURE_TABLE { static const struct FEATURE_TABLE cpu_features [] = { /* features indexed by CPU_MODEL */ { 0, /* UNIT_SERIES_III */ - 0, + UNIT_CIS, 1024 * 1024 }, { 0, /* UNIT_SERIES_II */ 0, @@ -931,45 +960,13 @@ static const struct FEATURE_TABLE cpu_features [] = { /* features indexed by C }; -/* Memory access classification table */ - -typedef struct { - HP_WORD *bank_ptr; /* a pointer to the bank register */ - DEVICE *device_ptr; /* a pointer to the accessing device */ - uint32 debug_flag; /* the debug flag for tracing */ - t_bool irq; /* TRUE if an interrupt is requested on error */ - const char *name; /* the classification name */ - } ACCESS_PROPERTIES; - - -static const ACCESS_PROPERTIES access [] = { /* indexed by ACCESS_CLASS */ -/* bank_ptr device_ptr debug_flag irq name */ -/* -------- ---------- ---------- ------ ------------------- */ - { NULL, & iop_dev, DEB_MDATA, FALSE, "absolute" }, /* absolute_iop */ - { NULL, & iop_dev, DEB_MDATA, FALSE, "dma" }, /* dma_iop */ - { NULL, & sel_dev, DEB_MDATA, FALSE, "absolute" }, /* absolute_sel */ - { NULL, & sel_dev, DEB_MDATA, FALSE, "dma" }, /* dma_sel */ - { NULL, & cpu_dev, DEB_MDATA, TRUE, "absolute" }, /* absolute */ - { NULL, & cpu_dev, DEB_MDATA, TRUE, "absolute" }, /* absolute_checked */ - { & PBANK, & cpu_dev, DEB_FETCH, TRUE, "instruction fetch" }, /* fetch */ - { & PBANK, & cpu_dev, DEB_FETCH, TRUE, "instruction fetch" }, /* fetch_checked */ - { & PBANK, & cpu_dev, DEB_MDATA, TRUE, "program" }, /* program */ - { & PBANK, & cpu_dev, DEB_MDATA, TRUE, "program" }, /* program_checked */ - { & DBANK, & cpu_dev, DEB_MDATA, TRUE, "data" }, /* data */ - { & DBANK, & cpu_dev, DEB_MDATA, TRUE, "data" }, /* data_checked */ - { & SBANK, & cpu_dev, DEB_MDATA, TRUE, "stack" }, /* stack */ - { & SBANK, & cpu_dev, DEB_MDATA, TRUE, "stack" } /* stack_checked */ - }; - - /* CPU local SCP support routine declarations */ static t_stat cpu_service (UNIT *uptr); static t_stat cpu_reset (DEVICE *dptr); -static t_stat cpu_examine (t_value *eval_array, t_addr address, UNIT *uptr, int32 switches); -static t_stat cpu_deposit (t_value value, t_addr address, UNIT *uptr, int32 switches); static t_stat set_stops (UNIT *uptr, int32 option, CONST char *cptr, void *desc); +static t_stat set_exec (UNIT *uptr, int32 option, CONST char *cptr, void *desc); static t_stat set_dump (UNIT *uptr, int32 option, CONST char *cptr, void *desc); static t_stat set_size (UNIT *uptr, int32 new_size, CONST char *cptr, void *desc); static t_stat set_model (UNIT *uptr, int32 new_model, CONST char *cptr, void *desc); @@ -977,6 +974,7 @@ static t_stat set_option (UNIT *uptr, int32 new_option, CONST char *cptr, void * static t_stat set_pfars (UNIT *uptr, int32 setting, CONST char *cptr, void *desc); static t_stat show_stops (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat show_exec (FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat show_dump (FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat show_speed (FILE *st, UNIT *uptr, int32 val, CONST void *desc); @@ -1018,7 +1016,7 @@ UNIT cpu_unit [] = { - REG_A permits any display - REG_B permits binary display - REG_M defaults to CPU instruction mnemonic display - - REG_S defaults to CPU status mnemonic display + - REG_T defaults to CPU status mnemonic display Implementation notes: @@ -1054,7 +1052,7 @@ static REG cpu_reg [] = { { ORDATA (RC, TR [2], 16), REG_A | REG_FIT }, /* top of stack - 2 register */ { ORDATA (RD, TR [3], 16), REG_A | REG_FIT }, /* top of stack - 3 register */ { ORDATA (X, X, 16), REG_A | REG_FIT }, /* index register */ - { ORDATA (STA, STA, 16), REG_S | REG_B | REG_FIT }, /* status register */ + { ORDATA (STA, STA, 16), REG_T | REG_B | REG_FIT }, /* status register */ { ORDATA (SWCH, SWCH, 16), REG_A | REG_FIT }, /* switch register */ { ORDATA (CPX1, CPX1, 16), REG_B | REG_FIT }, /* run-mode interrupt flags */ { ORDATA (CPX2, CPX2, 16), REG_B | REG_FIT }, /* halt-mode interrupt flags */ @@ -1082,6 +1080,9 @@ static MTAB cpu_mod [] = { { UNIT_EIS, UNIT_EIS, "EIS", NULL, &set_option, NULL, NULL }, { UNIT_EIS, 0, "no EIS", "NOEIS", NULL, NULL, NULL }, + { UNIT_CIS, UNIT_CIS, "CIS", "CIS", &set_option, NULL, NULL }, + { UNIT_CIS, 0, "no CIS", "NOCIS", NULL, NULL, NULL }, + { UNIT_PFARS, UNIT_PFARS, "auto-restart", "ARS", &set_pfars, NULL, NULL }, { UNIT_PFARS, 0, "no auto-restart", "NOARS", &set_pfars, NULL, NULL }, @@ -1106,6 +1107,9 @@ static MTAB cpu_mod [] = { { MTAB_XDV | MTAB_NMO, 1, "STOPS", "STOP", &set_stops, &show_stops, NULL }, { MTAB_XDV, 0, NULL, "NOSTOP", &set_stops, NULL, NULL }, + { MTAB_XDV | MTAB_NMO, 1, "EXEC", "EXEC", &set_exec, &show_exec, NULL }, + { MTAB_XDV, 0, NULL, "NOEXEC", &set_exec, NULL, NULL }, + { MTAB_XDV | MTAB_NMO, 0, "SPEED", NULL, NULL, &show_speed, NULL }, { 0 } @@ -1115,12 +1119,14 @@ static MTAB cpu_mod [] = { /* Debugging trace list */ static DEBTAB cpu_deb [] = { - { "INSTR", DEB_INSTR }, /* instruction executions */ - { "DATA", DEB_MDATA }, /* data accesses */ - { "FETCH", DEB_FETCH }, /* instruction fetches */ - { "REG", DEB_REG }, /* register values */ - { "PSERV", DEB_PSERV }, /* process clock service events */ - { NULL, 0 } + { "INSTR", DEB_INSTR }, /* instructions */ + { "DATA", DEB_MDATA }, /* memory data accesses */ + { "FETCH", DEB_MFETCH }, /* memory instruction fetches */ + { "REG", DEB_REG }, /* register values */ + { "OPND", DEB_MOPND }, /* memory operand values */ + { "EXEC", DEB_EXEC }, /* instruction execution states */ + { "PSERV", DEB_PSERV }, /* process clock service events */ + { NULL, 0 } }; /* Debugging stop list */ @@ -1147,8 +1153,8 @@ DEVICE cpu_dev = { 1, /* address increment */ 8, /* data radix */ 16, /* data width */ - &cpu_examine, /* examine routine */ - &cpu_deposit, /* deposit routine */ + &mem_examine, /* examine routine */ + &mem_deposit, /* deposit routine */ &cpu_reset, /* reset routine */ NULL, /* boot routine */ NULL, /* attach routine */ @@ -1354,6 +1360,20 @@ DEVICE cpu_dev = { call is dictated by the desire to report both the original trap and the System Halt trap, even though the placement results in the display of the incoming parameter value, rather than the stacked parameter value. + + 5. The execution trace (DEB_EXEC) match test is performed in two parts to + display the register values both before and after the instruction + execution. Consequently, the enable test is done before the register + trace, and the disable test is done after. + + 6. The execution test (exec_test) is set FALSE even though execution tracing + is not specified. This is done solely to reassure the compiler that the + value is not clobbered by a longjmp call. + + 7. The set of debug trace flags is restored in the postlude (and therefore + also saved in the instruction prelude) to ensure that a simulation stop + that occurs while an execution trace is in progress does not exit with + the flags set improperly. */ t_stat sim_instr (void) @@ -1369,11 +1389,14 @@ static const char *const stack_formats [] = { /* stack register displa int abortval; HP_WORD label, parameter, device; TRAP_CLASS trap; +t_bool exec_test; t_stat status = SCPE_OK; /* Instruction prelude */ +debug_save = cpu_dev.dctrl; /* save the current set of debug flags for later restoration */ + if (sim_switches & SWMASK ('B')) /* if a simulation stop bypass was requested */ cpu_stop_flags = SS_BYPASSED; /* then clear the stop flags for the first instruction */ else /* otherwise */ @@ -1516,7 +1539,7 @@ if (abortval) { /* if a microcode abort X = CIR; /* save the current instruction for restarting */ - cpu_call_procedure (label); /* set up PB, P, PL, and STA to call the procedure */ + cpu_call_procedure (label, 0); /* set up PB, P, PL, and STA to call the procedure */ cpu_base_changed = TRUE; /* one or more base registers have changed */ } @@ -1559,22 +1582,51 @@ while (status == SCPE_OK) { /* execute until simulat sim_interval = sim_interval + 1; /* and don't count the cycle */ } - else { /* otherwise execute the next instruction */ - if (DPRINTING (cpu_dev, DEB_REG)) { /* if register tracing is enabled */ - hp_debug (&cpu_dev, DEB_REG, /* then output the active TOS registers */ - stack_formats [SR], - SBANK, SM, SR, RA, RB, RC, RD); + else { /* otherwise execute the next instruction */ + if (DPRINTING (cpu_dev, DEB_EXEC | DEB_REG)) { /* if execution or register tracing is enabled */ + if (cpu_dev.dctrl & DEB_EXEC) /* then if tracing execution */ + if (STA & STATUS_R) /* then if the right-hand stack op is pending */ + exec_test = /* then the execution test succeeds if */ + (CIR << STACKOP_A_SHIFT /* the right-hand stack op */ + & STACKOP_A_MASK /* matches the test criteria */ + & exec_mask) == exec_match; /* for the left-hand stack op value */ + else /* otherwise */ + exec_test = /* then the execution test succeeds if */ + (NIR & exec_mask) == exec_match; /* the next instruction matches the test criteria */ + else /* otherwise */ + exec_test = FALSE; /* there is no execution test */ - fprintf (sim_deb, "X %06o, %s\n", /* output the index and status registers */ - X, fmt_status (STA)); + if (cpu_dev.dctrl & DEB_EXEC /* if execution tracing is enabled */ + && cpu_dev.dctrl != DEB_ALL /* and is currently inactive */ + && exec_test) { /* and the matching test succeeds */ + debug_save = cpu_dev.dctrl; /* then save the current trace flag set */ + cpu_dev.dctrl = DEB_ALL; /* and turn on full tracing */ + } - if (cpu_base_changed) { /* if the base registers have changed since last time */ - hp_debug (&cpu_dev, DEB_REG, /* then output the base registers */ - BOV_FORMAT " PB %06o, PL %06o, DL %06o, DB %06o, Q %06o, Z %06o\n", - DBANK, 0, STA & STATUS_CS_MASK, - PB, PL, DL, DB, Q, Z); + if (cpu_dev.dctrl & DEB_REG) { /* if register tracing is enabled */ + hp_debug (&cpu_dev, DEB_REG, /* then output the active TOS registers */ + stack_formats [SR], + SBANK, SM, SR, RA, RB, RC, RD); - cpu_base_changed = FALSE; /* clear the base register change flag */ + fprintf (sim_deb, "X %06o, %s\n", /* output the index and status registers */ + X, fmt_status (STA)); + + if (cpu_base_changed) { /* if the base registers have been altered */ + hp_debug (&cpu_dev, DEB_REG, /* then output the base register values */ + BOV_FORMAT " PB %06o, PL %06o, DL %06o, DB %06o, Q %06o, Z %06o\n", + DBANK, 0, STATUS_CS (STA), + PB, PL, DL, DB, Q, Z); + + cpu_base_changed = FALSE; /* clear the base registers changed flag */ + } + } + + if (cpu_dev.dctrl & DEB_EXEC /* if execution tracing is enabled */ + && cpu_dev.dctrl == DEB_ALL /* and is currently active */ + && ! exec_test) { /* and the matching test fails */ + cpu_dev.dctrl = debug_save; /* then restore the saved debug flag set */ + hp_debug (&cpu_dev, DEB_EXEC, /* and add a separator to the trace log */ + "*****************\n"); } } @@ -1592,7 +1644,7 @@ while (status == SCPE_OK) { /* execute until simulat hp_debug (&cpu_dev, DEB_INSTR, BOV_FORMAT, /* print the address and the instruction opcode */ PBANK, P - 2 & R_MASK, CIR); /* as an octal value */ - if (fprint_cpu (sim_deb, sim_eval, 0, SIM_SW_STOP) != SCPE_OK) /* print the mnemonic; if that fails */ + if (fprint_cpu (sim_deb, sim_eval, 0, SIM_SW_STOP) == SCPE_ARG) /* print the mnemonic; if that fails */ fprint_val (sim_deb, sim_eval [0], cpu_dev.dradix, /* then print the numeric */ cpu_dev.dwidth, PV_RZRO); /* value again */ @@ -1622,6 +1674,8 @@ while (status == SCPE_OK) { /* execute until simulat /* Instruction postlude */ +cpu_dev.dctrl = debug_save; /* restore the flag set */ + cpu_update_pclk (); /* update the process clock */ clk_update_counter (); /* and system clock counters */ @@ -1873,253 +1927,6 @@ return status; /* return the operation /* CPU global utility routines */ -/* Read a word from memory. - - Read and return a word from memory at the indicated offset and implied bank. - If the access succeeds, the routine returns TRUE. If the accessed word is - outside of physical memory, the Illegal Address interrupt flag is set for - CPU accesses, the value is set to 0, and the routine returns FALSE. If - access checking is requested, and the check fails, a Bounds Violation trap is - taken. - - On entry, "offset" is a logical offset into the memory bank implied by the - access classification, except for absolute and DMA accesses, for which - "offset" is a physical address. CPU access classifications other than fetch - may be checked or unchecked. Checked accesses must specify locations within - the corresponding segments (PB <= ea <= PL for program, or DL <= ea <= S for - data or stack) unless the CPU in is privileged mode, and those that reference - the TOS locations return values from the TOS registers instead of memory. - - For checked data and stack accesses, there are three cases, depending on the - effective address: - - - EA >= DL and EA <= SM : read from memory - - - EA > SM and EA <= SM + SR : read from a TOS register if bank = stack bank - - - EA < DL or EA > SM + SR : trap if not privileged, else read from memory - - - Implementation notes: - - 1. The physical address is formed by merging the bank and offset without - masking either value to their respective register sizes. Masking is not - necessary, as it was done when the bank registers were loaded, and it is - faster to avoid it. Primarily, though, it is not done so that an invalid - bank register value (e.g., loaded from a corrupted stack) will generate - an illegal address interrupt and so will pinpoint the problem for - debugging. - - 2. In hardware, bounds checking is performed explicitly by microcode. In - simulation, bounds checking is performed explicitly by employing the - "_checked" versions of the desired access classifications. - - 3. The "_iop" and "_sel" classifications serve only to select the correct - accessing device pointer and illegal address interrupt request state. -*/ - -t_bool cpu_read_memory (ACCESS_CLASS classification, uint32 offset, HP_WORD *value) -{ -uint32 bank, address; - -if (access [classification].bank_ptr == NULL) { /* if this is an absolute or DMA access */ - address = offset; /* then the "offset" is already a physical address */ - bank = TO_BANK (offset); /* separate the bank and offset */ - offset = TO_OFFSET (offset); /* in case tracing is active */ - } - -else { /* otherwise the bank register is implied */ - bank = *access [classification].bank_ptr; /* by the access classification */ - address = bank << LA_WIDTH | offset; /* form the physical address with the supplied offset */ - } - -if (address >= MEMSIZE) { /* if this access is beyond the memory size */ - if (access [classification].irq) /* then if an interrupt is requested */ - CPX1 |= cpx1_ILLADDR; /* then set the Illegal Address interrupt */ - - *value = 0; /* return a zero value */ - return FALSE; /* and indicate failure to the caller */ - } - -else { /* otherwise the access is within the memory range */ - switch (classification) { /* so dispatch on the access classification */ - - case absolute_iop: - case dma_iop: - case absolute_sel: - case dma_sel: - case absolute: - case fetch: - case program: - case data: - case stack: - *value = (HP_WORD) M [address]; /* unchecked access values come from memory */ - break; - - - case absolute_checked: - if (offset > SM && offset <= SM + SR && bank == SBANK) /* if the offset is within the TOS */ - *value = TR [SM + SR - offset]; /* then the value comes from a TOS register */ - else /* otherwise */ - *value = (HP_WORD) M [address]; /* the value comes from memory */ - break; - - - case fetch_checked: - if (PB <= offset && offset <= PL) /* if the offset is within the program segment bounds */ - *value = (HP_WORD) M [address]; /* then the value comes from memory */ - else /* otherwise */ - MICRO_ABORT (trap_Bounds_Violation); /* trap for a bounds violation */ - break; - - - case program_checked: - if (PB <= offset && offset <= PL || PRIV) /* if the offset is within bounds or is privileged */ - *value = (HP_WORD) M [address]; /* then the value comes from memory */ - else /* otherwise */ - MICRO_ABORT (trap_Bounds_Violation); /* trap for a bounds violation */ - break; - - - case data_checked: - case stack_checked: - if (offset > SM && offset <= SM + SR && bank == SBANK) /* if the offset is within the TOS */ - *value = TR [SM + SR - offset]; /* then the value comes from a TOS register */ - else if (DL <= offset && offset <= SM + SR || PRIV) /* if the offset is within bounds or is privileged */ - *value = (HP_WORD) M [address]; /* then the value comes from memory */ - else /* otherwise */ - MICRO_ABORT (trap_Bounds_Violation); /* trap for a bounds violation */ - break; - } /* all cases are handled */ - - dpprintf (access [classification].device_ptr, access [classification].debug_flag, - BOV_FORMAT " %s%s\n", bank, offset, *value, - access [classification].name, - access [classification].debug_flag == DEB_MDATA ? " read" : ""); - - return TRUE; /* indicate success with the returned value stored */ - } -} - - -/* Write a word to memory. - - Write a word to memory at the indicated offset and implied bank. If the - write succeeds, the routine returns TRUE. If the accessed location is outside - of physical memory, the Illegal Address interrupt flag is set for CPU - accesses, the write is ignored, and the routine returns FALSE. If access - checking is requested, and the check fails, a Bounds Violation trap is taken. - - For checked data and stack accesses, there are three cases, depending on the - effective address: - - - EA >= DL and EA <= SM + SR : write to memory - - - EA > SM and EA <= SM + SR : write to a TOS register if bank = stack bank - - - EA < DL or EA > SM + SR : trap if not privileged, else write to memory - - Note that cases 1 and 2 together imply that a write to a TOS register also - writes through to the underlying memory. - - - Implementation notes: - - 1. The physical address is formed by merging the bank and offset without - masking either value to their respective register sizes. Masking is not - necessary, as it was done when the bank registers were loaded, and it is - faster to avoid it. Primarily, though, it is not done so that an invalid - bank register value (e.g., loaded from a corrupted stack) will generate - an illegal address interrupt and so will pinpoint the problem for - debugging. - - 2. In hardware, bounds checking is performed explicitly by microcode. In - simulation, bounds checking is performed explicitly by employing the - "_checked" versions of the desired access classifications. - - 3. The Series II microcode shows that only the STOR and STD instructions - write through to memory when the effective address is in a TOS register. - However, in simulation, all (checked) stack and data writes will write - through. - - 4. The "_iop" and "_sel" classifications serve only to select the correct - accessing device pointer and illegal address interrupt request state. -*/ - -t_bool cpu_write_memory (ACCESS_CLASS classification, uint32 offset, HP_WORD value) -{ -uint32 bank, address; - -if (access [classification].bank_ptr == NULL) { /* if this is an absolute or DMA access */ - address = offset; /* then "offset" is already a physical address */ - bank = TO_BANK (offset); /* separate the bank and offset */ - offset = TO_OFFSET (offset); /* in case tracing is active */ - } - -else { /* otherwise the bank register is implied */ - bank = *access [classification].bank_ptr; /* by the access classification */ - address = bank << LA_WIDTH | offset; /* form the physical address with the supplied offset */ - } - -if (address >= MEMSIZE) { /* if this access is beyond the memory size */ - if (access [classification].irq) /* then if an interrupt is requested */ - CPX1 |= cpx1_ILLADDR; /* then set the Illegal Address interrupt */ - - return FALSE; /* indicate failure to the caller */ - } - -else { /* otherwise the access is within the memory range */ - switch (classification) { /* so dispatch on the access classification */ - - case absolute_iop: - case dma_iop: - case absolute_sel: - case dma_sel: - case absolute: - case data: - case stack: - M [address] = (MEMORY_WORD) value; /* write the value to memory */ - break; - - - case absolute_checked: - if (offset > SM && offset <= SM + SR && bank == SBANK) /* if the offset is within the TOS */ - TR [SM + SR - offset] = value; /* then write the value to a TOS register */ - else /* otherwise */ - M [address] = (MEMORY_WORD) value; /* write the value to memory */ - break; - - - case data_checked: - case stack_checked: - if (offset > SM && offset <= SM + SR && bank == SBANK) /* if the offset is within the TOS */ - TR [SM + SR - offset] = value; /* then write the value to a TOS register */ - - if (DL <= offset && offset <= SM + SR || PRIV) /* if the offset is within bounds or is privileged */ - M [address] = (MEMORY_WORD) value; /* then write the value to memory */ - else /* otherwise */ - MICRO_ABORT (trap_Bounds_Violation); /* trap for a bounds violation */ - break; - - - case fetch: - case fetch_checked: - case program: - case program_checked: /* these classes cannot be used for writing */ - CPX1 |= cpx1_ADDRPAR; /* so set an Address Parity Error interrupt */ - return FALSE; /* and indicate failure to the caller */ - - } /* all cases are handled */ - - dpprintf (access [classification].device_ptr, access [classification].debug_flag, - BOV_FORMAT " %s write\n", bank, offset, value, - access [classification].name); - - return TRUE; /* indicate success with the value written */ - } -} - - /* Process a run-mode interrupt. This routine is called when one or more of the interrupt request bits are set @@ -2677,6 +2484,11 @@ return; 3. The System Reference Manual states that byte offsets are interpreted as negative if the effective address does not lie between DL and Z. However, the Series II microcode actually uses DL and S for the limits. + + 4. This routine calculates the effective address for the memory address + instructions. These instructions always bounds-check their accesses, and + data and stack accesses are mapped to the TOS registers if the effective + address lies between SM and SM + SR within the stack bank. */ void cpu_ea (HP_WORD mode_disp, ACCESS_CLASS *classification, HP_WORD *offset, BYTE_SELECTOR *selector) @@ -2707,7 +2519,7 @@ switch ((mode_disp & MODE_MASK) >> MODE_SHIFT) { /* dispatch on the addre case 012: case 013: /* positive DB-relative displacement */ base = DB + (mode_disp & DISPL_255_MASK); /* add the displacement to the base */ - class = data_checked; /* and classify as a data reference */ + class = data_mapped_checked; /* and classify as a data reference */ break; case 014: @@ -2741,7 +2553,7 @@ else { /* otherwise the mod else if (class != program_checked) { /* otherwise if it is a data or stack reference */ base = DB; /* then DB is the base for the displacement */ - class = data_checked; /* reclassify as a data reference */ + class = data_mapped_checked; /* reclassify as a data reference */ } /* otherwise, this is a program reference */ } /* which is self-referential */ @@ -2779,6 +2591,98 @@ return; } +/* Convert a data- or program-relative byte address to a word effective address. + + The supplied byte offset from DB or PB is converted to a memory address, + bounds-checked if requested, and returned. If the supplied block length is + not zero, the converted address is assumed to be the starting address of a + block, and an ending address, based on the block length, is calculated and + bounds-checked if requested. If either address lies outside of the segment + associated with the access class, a Bounds Violation trap occurs, unless a + privileged data access is requested. + + The "class" parameter indicates whether the offset is from DB or PB and + must be "data", "data_checked", "program", or "program_checked". No other + access classes are supported. In particular, "data_mapped" and + "data_mapped_checked" are not allowed, as callers of this routine never map + accesses to the TOS registers. + + Byte offsets into data segments present problems, in that negative offsets + are permitted (to access the DL-to-DB area), but there are not enough bits to + represent all locations unambiguously in the potential -32K to +32K word + offset range. Therefore, a byte offset with bit 0 = 1 can represent either a + positive or negative word offset from DB, depending on the interpretation. + The HP 3000 adopts the convention that if the address resulting from a + positive-offset interpretation does not fall within the DL-to-S range, then + 32K is added to the address, effectively changing the interpretation from a + positive to a negative offset. If this new address does not fall within the + DL-to-S range, a Bounds Violation trap occurs if the mode is non-privileged. + + The reinterpretation as a negative offset is performed only if the CPU is not + in split-stack mode (where either DBANK is different from SBANK, or DB does + not lie between DL and Z), as extra data segments do not permit negative-DB + addressing. Reinterpretation is also not used for code segments, as negative + offsets from PB are not permitted. + + + Implementation notes: + + 1. This routine implements the DBBC microcode subroutine. +*/ + +uint32 cpu_byte_ea (ACCESS_CLASS class, uint32 byte_offset, uint32 block_length) +{ +uint32 starting_word, ending_word, increment; + +if (block_length & D16_SIGN) /* if the block length is negative */ + increment = 0177777; /* then the memory increment is negative also */ +else /* otherwise */ + increment = 1; /* the increment is positive */ + +if (class == program || class == program_checked) { /* if this is a program access */ + starting_word = PB + (byte_offset >> 1) & LA_MASK; /* then determine the starting word address */ + + if (class == program_checked /* if checking is requested */ + && starting_word < PB || starting_word > PL) /* and the starting address is out of range */ + MICRO_ABORT (trap_Bounds_Violation); /* then trap for a bounds violation */ + + if (block_length != 0) { /* if a block length was supplied */ + ending_word = /* then determine the ending address */ + starting_word + ((block_length - increment + (byte_offset & 1)) >> 1) & LA_MASK; + + if (class == program_checked /* if checking is requested */ + && ending_word < PB || ending_word > PL) /* and the ending address is out of range */ + MICRO_ABORT (trap_Bounds_Violation); /* then trap for a bounds violation */ + } + } + +else { /* otherwise this is a data address */ + starting_word = DB + (byte_offset >> 1) & LA_MASK; /* so determine the starting word address */ + + if (DBANK == SBANK && DL <= DB && DB <= Z /* if not in split-stack mode */ + && (starting_word < DL || starting_word > SM)) { /* and the word address is out of range */ + starting_word = starting_word ^ D16_SIGN; /* then add 32K and try again */ + + if (class == data_checked && NPRV /* if checking is requested and non-privileged */ + && (starting_word < DL || starting_word > SM)) /* and still out of range */ + MICRO_ABORT (trap_Bounds_Violation); /* then trap for a bounds violation */ + } + + if (block_length != 0) { /* if a block length was supplied */ + ending_word = /* then determine the ending word address */ + starting_word + ((block_length - increment + (byte_offset & 1)) >> 1) & LA_MASK; + + if (class == data_checked && NPRV /* if checking is requested and non-privileged */ + && (ending_word < DL || ending_word > SM)) /* and the address is out of range */ + MICRO_ABORT (trap_Bounds_Violation); /* then trap for a bounds violation */ + } + } + + +return starting_word; /* return the starting word address */ +} + + /* Set up the entry into an interrupt handler. This routine prepares the CPU state to execute an interrupt handling @@ -2858,7 +2762,7 @@ cpu_write_memory (stack, SM, parameter); /* and push the parame X = CIR; /* save the CIR in the index register */ -cpu_call_procedure (label); /* set up to call the interrupt handling procedure */ +cpu_call_procedure (label, 0); /* set up to call the interrupt handling procedure */ return; } @@ -3120,7 +3024,8 @@ return; /* before returning to interrupt and trap routines to set up the handler procedures. On entry, "label" contains an external program label indicating the segment number and Segment Transfer Table entry number describing the procedure, or a local - program label indicating the starting address of the procedure. On exit, the + program label indicating the starting address of the procedure, and "offset" + contains an offset to be added to the starting address. On exit, the registers are set up for execution to resume with the first instruction of the procedure. @@ -3139,13 +3044,17 @@ return; /* before returning to the appropriate traps are taken. If the STT entry contains a local label, it is used to set up the P register and NIR as above. + The "offset" parameter is used only by the XBR instruction executor. The + PCAL executor and the interrupt handlers pass an offset of zero to begin + execution at the first instruction of the designated procedure. + Implementation notes: 1. This routine implements the microcode PCL3 and PCL5 subroutines. */ -void cpu_call_procedure (HP_WORD label) +void cpu_call_procedure (HP_WORD label, HP_WORD offset) { HP_WORD new_status, new_label, new_p, cst_entry, stt_size, stt_entry; @@ -3183,17 +3092,17 @@ if (label & LABEL_EXTERNAL) { /* if the label label = 0; /* then the starting address is PB */ else /* otherwise */ label = new_label; /* the PB offset is contained in the new label */ + + cpu_base_changed = TRUE; /* the program base registers have changed for tracing */ } -new_p = PB + (label & LABEL_ADDRESS_MASK); /* get the procedure starting address */ +new_p = PB + (label + offset & LABEL_ADDRESS_MASK); /* get the procedure starting address */ cpu_read_memory (fetch_checked, new_p, &NIR); /* check the bounds and get the next instruction */ P = new_p + 1 & R_MASK; /* the bounds are valid, so set the new P value */ STA = new_status; /* set the new status value */ -cpu_base_changed = TRUE; /* one or more base registers have changed for tracing */ - return; } @@ -3281,7 +3190,7 @@ STA &= ~STATUS_I; /* turn off external int cpu_read_memory (stack, Q - 3, &X); /* read the new X value from the stack marker */ -if ((new_status & STATUS_CS_MASK) != (STA & STATUS_CS_MASK)) { /* if returning to a different segment */ +if (STATUS_CS (new_status) != STATUS_CS (STA)) { /* if returning to a different segment */ cpu_setup_code_segment (new_status, &temp_status, &cst_entry); /* then set up the new segment */ if (NPRV && (temp_status & STATUS_M)) /* if in user mode now and returning to a privileged segment */ @@ -3456,23 +3365,21 @@ return SCPE_OK; /* return the success of static t_stat cpu_reset (DEVICE *dptr) { -if (M == NULL) { /* if this is the first call after simulator start */ - M = (MEMORY_WORD *) calloc (PA_MAX, /* then allocate the maximum amount of memory needed */ - sizeof (MEMORY_WORD)); - - if (M == NULL) /* if the allocation failed */ - return SCPE_MEM; /* then report the error and abort the simulation */ - - else /* otherwise the memory was allocated */ +if (sim_PC == NULL) { /* if this is the first call after simulator start */ + if (mem_initialize (PA_MAX)) { set_model (&cpu_unit [0], UNIT_SERIES_III, /* so establish the initial CPU model */ NULL, NULL); - for (sim_PC = dptr->registers; /* find the P register entry */ - sim_PC->loc != &P && sim_PC->loc != NULL; /* in the register array */ - sim_PC++); /* for the SCP interface */ + for (sim_PC = dptr->registers; /* find the P register entry */ + sim_PC->loc != &P && sim_PC->loc != NULL; /* in the register array */ + sim_PC++); /* for the SCP interface */ - if (sim_PC == NULL) /* if the P register entry is not present */ - return SCPE_NXREG; /* then there is a serious problem! */ + if (sim_PC == NULL) /* if the P register entry is not present */ + return SCPE_NXREG; /* then there is a serious problem! */ + } + + else /* otherwise memory initialization failed */ + return SCPE_MEM; /* so report the error and abort the simulation */ } if (sim_switches & SWMASK ('P')) { /* if this is a power-on reset */ @@ -3499,60 +3406,6 @@ return SCPE_OK; /* indicate that the res } -/* Examine a memory location. - - This routine is called by the SCP to examine memory. The routine retrieves - the memory location indicated by "address" as modified by any "switches" that - were specified on the command line and returns the value in the first element - of "eval_array". - - On entry, if "switches" includes SIM_SW_STOP, then "address" is an offset - from PBANK; otherwise, it is an absolute address. If the supplied address is - beyond the current memory limit, "non-existent memory" status is returned. - Otherwise, the value is obtained from memory and returned in "eval_array." -*/ - -static t_stat cpu_examine (t_value *eval_array, t_addr address, UNIT *uptr, int32 switches) -{ -if (switches & SIM_SW_STOP) /* if entry is for a simulator stop */ - address = TO_PA (PBANK, address); /* then form a PBANK-based physical address */ - -if (address >= MEMSIZE) /* if the address is beyond memory limits */ - return SCPE_NXM; /* then return non-existent memory status */ - -else if (eval_array == NULL) /* if the value pointer was not supplied */ - return SCPE_IERR; /* then return internal error status */ - -else { /* otherwise */ - eval_array [0] = (t_value) M [address]; /* store the return value */ - return SCPE_OK; /* and return success */ - } -} - - -/* Deposit to a memory location. - - This routine is called by the SCP to deposit to memory. The routine stores - the supplied "value" into memory at the "address" location. If the supplied - address is beyond the current memory limit, "non-existent memory" status is - returned. - - The presence of any "switches" supplied on the command line does not affect - the operation of the routine. -*/ - -static t_stat cpu_deposit (t_value value, t_addr address, UNIT *uptr, int32 switches) -{ -if (address >= MEMSIZE) /* if the address is beyond memory limits */ - return SCPE_NXM; /* then return non-existent memory status */ - -else { /* otherwise */ - M [address] = value & DV_MASK; /* store the supplied value into memory */ - return SCPE_OK; /* and return success */ - } -} - - /* Set the CPU simulation stop conditions. This validation routine is called to configure the set of CPU stop @@ -3619,6 +3472,90 @@ return SCPE_OK; /* the stops were succes } +/* Change the instruction execution trace criteria. + + This validation routine is called to configure the criteria that select + instruction execution tracing. The "option" parameter is 0 to clear and 1 to + set the criteria, and "cptr" points to the first character of the match value + to be set. The unit and description pointers are not used. + + The routine processes commands of the form: + + SET CPU EXEC=[;] + SET CPU NOEXEC + + If the value is not supplied, a mask of 177777 octal is used. The + values are entered in the current CPU data radix, which defaults to octal, + unless an override switch is present on the command line. +*/ + +static t_stat set_exec (UNIT *uptr, int32 option, CONST char *cptr, void *desc) +{ +char gbuf [CBUFSIZE]; +uint32 match, mask, radix; +t_stat status; + +if (option == 0) /* if this is a NOEXEC request */ + if (cptr == NULL) { /* then if there are no arguments */ + exec_match = D16_UMAX; /* then set the match and mask values */ + exec_mask = 0; /* to prevent matching */ + return SCPE_OK; /* and return success */ + } + + else /* otherwise there are extraneous characters */ + return SCPE_2MARG; /* so report that there are too many arguments */ + +else if (cptr == NULL || *cptr == '\0') /* otherwise if the EXEC request supplies no arguments */ + return SCPE_MISVAL; /* then report a missing value */ + +else { /* otherwise at least one argument is present */ + cptr = get_glyph (cptr, gbuf, ';'); /* so get the match argument */ + + if (sim_switches & SWMASK ('O')) /* if an octal override is present */ + radix = 8; /* then parse the value in base 8 */ + else if (sim_switches & SWMASK ('D')) /* otherwise if a decimal override is present */ + radix = 10; /* then parse the value in base 10 */ + else if (sim_switches & SWMASK ('H')) /* otherwise if a hex override is present */ + radix = 16; /* then parse the value in base 16 */ + else /* otherwise */ + radix = cpu_dev.dradix; /* use the current CPU data radix */ + + match = (uint32) get_uint (gbuf, radix, D16_UMAX, &status); /* parse the match value */ + + if (status != SCPE_OK) /* if a parsing error occurred */ + return status; /* then return the error status */ + + else if (*cptr == '\0') { /* otherwise if no more characters are present */ + exec_match = match; /* then set the match value */ + exec_mask = D16_MASK; /* and default the mask value */ + return SCPE_OK; /* and return success */ + } + + else { /* otherwise another argument is present */ + cptr = get_glyph (cptr, gbuf, ';'); /* so get the mask argument */ + + mask = (uint32) get_uint (gbuf, radix, D16_UMAX, &status); /* parse the mask value */ + + if (status != SCPE_OK) /* if a parsing error occurred */ + return status; /* then return the error status */ + + else if (*cptr == '\0') /* if no more characters are present */ + if (mask == 0) /* then if the mask value is zero */ + return SCPE_ARG; /* then the match will never succeed */ + + else { /* otherwise */ + exec_match = match; /* set the match value */ + exec_mask = mask; /* and the mask value */ + return SCPE_OK; /* and return success */ + } + + else /* otherwise extraneous characters are present */ + return SCPE_2MARG; /* so report that there are too many arguments */ + } + } +} + + /* Set the CPU cold dump configuration jumpers. This validation routine is called to configure the set of jumpers on the @@ -3704,18 +3641,14 @@ static t_stat set_size (UNIT *uptr, int32 new_size, CONST char *cptr, void *desc static CONST char confirm [] = "Really truncate memory [N]?"; const uint32 model = CPU_MODEL (uptr->flags); /* the current CPU model index */ -uint32 address; if ((uint32) new_size > cpu_features [model].maxmem) /* if the new memory size is not supported on current model */ return SCPE_NOFNC; /* then report the error */ -if (!(sim_switches & SWMASK ('F'))) /* if truncation is not explicitly forced */ - for (address = new_size; address < MEMSIZE; address++) /* then check the values in truncated memory, if any */ - if (M [address] != 0) /* if this location is non-zero */ - if (get_yn (confirm, FALSE) == FALSE) /* then if the user denies confirmation */ - return SCPE_INCOMP; /* then abort the command */ - else /* otherwise we have explicit confirmation to proceed */ - break; /* so checking is no longer necessary */ +if (!(sim_switches & SWMASK ('F')) /* if truncation is not explicitly forced */ + && ! mem_is_empty (new_size) /* and the truncated part is not empty */ + && get_yn (confirm, FALSE) == FALSE) /* and the user denies confirmation */ + return SCPE_INCOMP; /* then abort the command */ MEMSIZE = new_size; /* set the new memory size */ @@ -3861,6 +3794,46 @@ return SCPE_OK; /* report the success of } +/* Show the instruction execution trace criteria. + + This display routine is called to show the criteria that select instruction + execution tracing. The "st" parameter is the open output stream. The other + parameters are not used. + + This routine services an extended modifier entry, so it must add the trailing + newline to the output before returning. +*/ + +static t_stat show_exec (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ +uint32 radix; + +if (exec_mask == 0) /* if the instruction is entirely masked */ + fputs ("Execution trace disabled\n", st); /* then report that matching is disabled */ + +else { /* otherwise */ + if (sim_switches & SWMASK ('O')) /* if an octal override is present */ + radix = 8; /* then print the value in base 8 */ + else if (sim_switches & SWMASK ('D')) /* otherwise if a decimal override is present */ + radix = 10; /* then print the value in base 10 */ + else if (sim_switches & SWMASK ('H')) /* otherwise if a hex override is present */ + radix = 16; /* then print the value in base 16 */ + else /* otherwise */ + radix = cpu_dev.dradix; /* use the current CPU data radix */ + + fputs ("Execution trace match = ", st); /* print the label */ + fprint_val (st, exec_match, radix, cpu_dev.dwidth, PV_RZRO); /* and the match value */ + + fputs (", mask = ", st); /* print a separator */ + fprint_val (st, exec_mask, radix, cpu_dev.dwidth, PV_RZRO); /* and the mask value */ + + fputc ('\n', st); /* tie off the line */ + } + +return SCPE_OK; /* report the success of the display */ +} + + /* Show the CPU cold dump configuration jumpers. This display routine is called to show the device number and control byte @@ -4204,11 +4177,10 @@ else if (CPX2 & cpx2_LOADSWCH) /* otherwise if the LOAD if (cpu_micro_state != waiting) { /* then if the load is not in progress */ reset_all (CPU_IO_RESET); /* then reset the CPU and all I/O devices */ - if ((SWCH & 000200) == 0) /* if switch register bit 8 is clear */ - for (address = 0; address < MEMSIZE; address++) /* then fill memory */ - M [address] = HALT_10; /* with HALT 10 instructions */ + if ((SWCH & 000200) == 0) /* if switch register bit 8 is clear */ + mem_fill (0, HALT_10); /* then fill all of memory with HALT 10 instructions */ - SBANK = 0; /* set the stack bank to bank 0 */ + SBANK = 0; /* set the stack bank to bank 0 */ cold_device = LOWER_BYTE (SWCH) & DEVNO_MASK; /* get the device number from the lower SWCH byte */ @@ -4405,7 +4377,7 @@ switch (SUBOP (CIR)) { /* dispatch on bits 0-3 cpu_write_memory (data, offset, operand); /* to the control variable */ } - control = SEXT (operand); /* sign-extend the control value */ + control = SEXT16 (operand); /* sign-extend the control value */ } else { /* TBX or MTBX (none; STUN, BNDV) */ @@ -4414,10 +4386,10 @@ switch (SUBOP (CIR)) { /* dispatch on bits 0-3 if (opcode == MTBX) /* if the instruction is MTBX */ X = X + RB & R_MASK; /* then add the step size to the control variable */ - control = SEXT (X); /* sign-extend the control value */ + control = SEXT16 (X); /* sign-extend the control value */ } - limit = SEXT (RA); /* sign-extend the limit value */ + limit = SEXT16 (RA); /* sign-extend the limit value */ if (RB & D16_SIGN) /* if the step size is negative */ branch = control >= limit; /* then branch if the value is not below the limit */ @@ -4534,7 +4506,7 @@ switch (SUBOP (CIR)) { /* dispatch on bits 0-3 P = offset + 1 & R_MASK; /* and increment the program counter */ } - else if (TO_CCF (STA) & (CIR << BCC_CCF_SHIFT)) /* otherwise if the BCC test succeeds */ + else if (TO_CCF (STA) & CIR << BCC_CCF_SHIFT) /* otherwise if the BCC test succeeds */ status = cpu_branch_short (TRUE); /* then branch to the target address */ break; /* otherwise continue execution at P + 1 */ diff --git a/HP3000/hp3000_cpu.h b/HP3000/hp3000_cpu.h index 9fb4e547..254bf436 100644 --- a/HP3000/hp3000_cpu.h +++ b/HP3000/hp3000_cpu.h @@ -23,6 +23,15 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 07-Nov-16 JDB Added SETR and SETR_X for SETR executor use; + renamed cpu_byte_to_word_ea to cpu_byte_ea + 03-Nov-16 JDB Added LABEL_LOCAL for PARC/XBR/ENDP executor use + 01-Nov-16 JDB Added debug flag for per-instruction trace capability + 22-Oct-16 JDB Added "cpu_interrupt_pending" global from cpu_base.c + 07-Oct-16 JDB Added "cpu_dev" external so executors can trace instructions + 28-Sep-16 JDB Added CIS definitions + 22-Sep-16 JDB Added cpu_byte_to_word_ea, STATUS_CS macro + 21-Sep-16 JDB Added STATUS_CCI, UNIT_CIS, and the CIS dispatcher 12-Sep-16 JDB Added the PCN_SERIES_II and PCN_SERIES_III constants 02-Sep-16 JDB Added the POWER_STATE enumeration type, the UNIT_PFARS flag, and the "cpu_power_state" external declaration @@ -46,28 +55,6 @@ -/* Architectural constants. - - The type used to represent a main memory word value is defined. An array of - this type is used to simulate the CPU main memory. - - - Implementation notes: - - 1. The MEMORY_WORD type is a 16-bit unsigned type, corresponding with the - 16-bit main memory in the HP 3000. Unlike the general data type, which - is a 32-bit type for speed, main memory does not benefit from the faster - 32-bit execution on IA-32 processors, as only one instruction in the - cpu_read_memory and cpu_write_memory routines has an operand override - that invokes the slower instruction fetch path. There is a negligible - difference in the Memory Pattern Test diagnostic execution speeds for the - uint32 vs. uint16 definition, whereas the VM requirements are doubled for - the former. -*/ - -typedef uint16 MEMORY_WORD; /* HP 16-bit memory word representation */ - - /* Supported breakpoint switches */ #define BP_EXEC (SWMASK ('E')) /* an execution breakpoint */ @@ -80,6 +67,7 @@ typedef uint16 MEMORY_WORD; /* HP 16-bit memory word represe #define UNIT_EIS_SHIFT (UNIT_V_UF + 1) /* the Extended Instruction Set firmware option */ #define UNIT_CALTIME_SHIFT (UNIT_V_UF + 2) /* the process clock timing mode */ #define UNIT_PFARS_SHIFT (UNIT_V_UF + 3) /* the power-fail auto-restart mode */ +#define UNIT_CIS_SHIFT (UNIT_V_UF + 4) /* the COBOL II Extended Instruction Set firmware option */ #define UNIT_MODEL_MASK 0000001u /* model ID mask */ @@ -90,6 +78,7 @@ typedef uint16 MEMORY_WORD; /* HP 16-bit memory word represe #define UNIT_EIS (1u << UNIT_EIS_SHIFT) /* the Extended Instruction Set is installed */ #define UNIT_CALTIME (1u << UNIT_CALTIME_SHIFT) /* the process clock is calibrated to wall time */ #define UNIT_PFARS (1u << UNIT_PFARS_SHIFT) /* the system will auto-restart after a power failure */ +#define UNIT_CIS (1u << UNIT_CIS_SHIFT) /* the COBOL II Extended Instruction Set is installed */ #define UNIT_CPU_MODEL (cpu_unit [0].flags & UNIT_MODEL) @@ -100,11 +89,12 @@ typedef uint16 MEMORY_WORD; /* HP 16-bit memory word represe /* CPU debug flags */ -#define DEB_MDATA (1u << 0) /* trace memory data accesses */ -#define DEB_INSTR (1u << 1) /* trace instruction execution */ -#define DEB_FETCH (1u << 2) /* trace instruction fetches */ -#define DEB_REG (1u << 3) /* trace register values */ -#define DEB_PSERV (1u << 4) /* trace PCLK service events */ +#define DEB_ALL ~0u /* trace everything */ + +#define DEB_INSTR (1u << 0) /* trace instructions */ +#define DEB_REG (1u << 1) /* trace register values */ +#define DEB_PSERV (1u << 2) /* trace PCLK service events */ +#define DEB_EXEC (1u << 3) /* trace matched instruction executions */ #define BOV_FORMAT "%02o.%06o %06o " /* bank-offset-value trace format string */ @@ -118,6 +108,13 @@ typedef uint16 MEMORY_WORD; /* HP 16-bit memory word represe #define SS_BYPASSED (1u << 31) /* stops are bypassed for this instruction */ +/* Memory access macros */ + +#define cpu_read_memory(c,o,v) mem_read (&cpu_dev, c, o, v) +#define cpu_write_memory(c,o,v) mem_write (&cpu_dev, c, o, v) + + + /* System power state. The HP 3000 power supply uses two signals to indicate its state: PON (power @@ -157,38 +154,6 @@ typedef enum { } EXEC_STATE; -/* Memory access classifications. - - The access classification determines which bank register is used with the - supplied offset to access memory, and whether or not the access is bounds - checked. - - - Implementation notes: - - 1. The "_iop" and "_sel" classifications are identical. The only difference - is which device's trace flag is checked to print debugging information. - All of the other classifications check the CPU's trace flags. -*/ - -typedef enum { - absolute_iop, /* absolute bank, IOP request */ - dma_iop, /* DMA channel bank, IOP request */ - absolute_sel, /* absolute bank, selector channel request */ - dma_sel, /* DMA channel bank, selector channel request */ - absolute, /* absolute bank */ - absolute_checked, /* absolute bank, bounds checked */ - fetch, /* program bank, instruction fetch */ - fetch_checked, /* program bank, instruction fetch, bounds checked */ - program, /* program bank, data access */ - program_checked, /* program bank, data access, bounds checked */ - data, /* data bank, data access */ - data_checked, /* data bank, data access, bounds checked */ - stack, /* stack bank, data access */ - stack_checked /* stack bank, data access, bounds checked */ - } ACCESS_CLASS; - - /* CPX register flags. The CPX1 register contains flags that designate the run-time interrupts. The @@ -435,9 +400,9 @@ typedef enum { Condition Code: - 00 = CCL (less than) - 01 = CCE (equal to) - 10 = CCG (greater than) + 00 = CCG (greater than) + 01 = CCL (less than) + 10 = CCE (equal to) 11 = invalid */ @@ -451,13 +416,18 @@ typedef enum { #define STATUS_CCG 0000000u /* condition code greater than */ #define STATUS_CCL 0000400u /* condition code less than */ #define STATUS_CCE 0001000u /* condition code equal to */ +#define STATUS_CCI 0001400u /* condition code invalid */ #define STATUS_CC_MASK 0001400u /* condition code mask */ #define STATUS_CC_SHIFT 8 /* condition code alignment */ #define STATUS_CS_MASK 0000377u /* code segment mask */ +#define STATUS_CS_SHIFT 0 /* code segment alignment */ #define STATUS_CS_WIDTH 8 /* code segment mask width */ +#define STATUS_CS(s) (((s) & STATUS_CS_MASK) >> STATUS_CS_SHIFT) + + #define STATUS_OVTRAP (STATUS_T | STATUS_O) #define STATUS_NPRV (STATUS_T | STATUS_O | STATUS_C | STATUS_CC_MASK) @@ -822,7 +792,7 @@ typedef enum { #define FMEXSUBOP(v) BITS_12_15(v) /* firmware extension suboperation accessor */ -/* Specific instruction accessors */ +/* General instruction accessors */ #define IOOP_K_MASK 0000017u /* I/O K-field mask */ #define IOOP_K_SHIFT 0000000u /* I/O K-field alignment shift */ @@ -876,18 +846,8 @@ typedef enum { #define SDEC2(v) (((v) & SDEC2_MASK) >> SDEC_SHIFT) #define SDEC3(v) (((v) & SDEC3_MASK) >> SDEC_SHIFT) -#define DB_FLAG 0000020u /* PB/DB base flag */ - -#define MVBW_CCF 0000030u /* MVBW condition code flags */ -#define MVBW_N_FLAG 0000020u /* MVBW numeric flag */ -#define MVBW_A_FLAG 0000010u /* MVBW alphabetic flag */ -#define MVBW_S_FLAG 0000004u /* MVBW upshift flag */ -#define MVBW_CCF_SHIFT 6 /* CCF alignment in MVBW instruction */ - -#define NABS_FLAG 0000100u /* CVDA negative absolute value flag */ -#define ABS_FLAG 0000040u /* CVDA absolute value flag */ - -#define EIS_SDEC_SHIFT 4 /* EIS S-decrement alignment shift */ +#define DB_FLAG 0000020u /* base set PB/DB base register flag */ +#define CIS_DB_FLAG 0000001u /* CIS set PB/DB base register flag */ /* Explicit instruction opcodes and accessors */ @@ -896,6 +856,8 @@ typedef enum { #define QASR 0015700u /* quadruple arithmetic right shift */ #define DMUL 0020570u /* double integer multiply */ #define DDIV 0020571u /* double integer divide */ +#define SETR 0027400u /* set registers (none) */ +#define SETR_X 0027404u /* set registers (index) */ #define SED_1 0030041u /* set enable interrupt */ #define HALT_10 0030370u /* halt 10 */ @@ -932,6 +894,26 @@ typedef enum { #define CMD_TO(v) (((v) & CMD_TO_MASK) >> CMD_TO_SHIFT) #define CMD_MOP(v) (((v) & CMD_MOP_MASK) >> CMD_MOP_SHIFT) +#define MVBW_CCF 0000030u /* MVBW condition code flags */ +#define MVBW_N_FLAG 0000020u /* MVBW numeric flag */ +#define MVBW_A_FLAG 0000010u /* MVBW alphabetic flag */ +#define MVBW_S_FLAG 0000004u /* MVBW upshift flag */ +#define MVBW_CCF_SHIFT 6 /* CCF alignment in MVBW instruction */ + +#define NABS_FLAG 0000100u /* CVDA negative absolute value flag */ +#define ABS_FLAG 0000040u /* CVDA absolute value flag */ + +#define CVND_SC_MASK 0000016u /* CVND sign-control mask */ +#define CVND_SC_SHIFT 1 /* CVND sign-control field alignment shift */ + +#define EIS_SDEC_MASK 0000020u /* EIS S-decrement mask */ +#define EIS_SDEC_SHIFT 4 /* EIS S-decrement alignment shift */ + +#define CIS_SDEC_MASK 0000001u /* CIS S-decrement mask */ +#define CIS_SDEC_SHIFT 0 /* CIS S-decrement alignment shift */ + +#define TCCS_CCF_SHIFT 8 /* CIS TCCS instruction CCF alignment */ + /* PSHR/SETR instruction accessors */ @@ -956,6 +938,16 @@ typedef enum { #define PCN_SERIES_III 2 /* CPU number for the Series III */ +/* EDIT instruction subprogram operation values */ + +#define EDIT_SUFS 013 /* highest opcode with an extended immediate operand */ +#define EDIT_EXOP 017 /* extended opcode prefix */ + +#define EDIT_TE (EDIT_EXOP + 000) /* first extended opcode with no operand */ +#define EDIT_MDWO (EDIT_EXOP + 004) /* last extended opcode with no operand */ +#define EDIT_DBNZ (EDIT_EXOP + 011) /* last extended opcode */ + + /* Reserved memory addresses */ #define CSTB_POINTER 0000000u /* code segment table base pointer */ @@ -1006,6 +998,7 @@ typedef enum { #define LABEL_STTN_SHIFT 8 /* STT number alignment shift */ #define LABEL_SEGMENT_SHIFT 0 /* segment number alignment shift */ +#define LABEL_LOCAL 0000000u /* local program label flag */ #define LABEL_UNCALLABLE 0040000u /* local program label uncallable flag */ #define LABEL_ADDRESS_MASK 0037777u /* local program label address mask */ @@ -1017,7 +1010,7 @@ typedef enum { #define LABEL_IRQ (LABEL_EXTERNAL | ISR_SEGMENT) /* label for interrupt requests */ #define LABEL_STTN_MAX (LABEL_STTN_MASK >> LABEL_STTN_SHIFT) /* STT number maximum value */ -#define TO_LABEL(s,n) ((s) | (n) << LABEL_STTN_SHIFT) +#define TO_LABEL(s,n) ((s) | (n) << LABEL_STTN_SHIFT) /* s = segment number, n = STT number */ /* Stack marker accessors */ @@ -1063,6 +1056,11 @@ extern HP_WORD CNTR; /* Microcode Counter */ #define RD TR [3] +/* CPU device */ + +extern DEVICE cpu_dev; /* Central Processing Unit */ + + /* CPU state */ extern jmp_buf cpu_save_env; /* saved environment for microcode aborts */ @@ -1088,14 +1086,15 @@ extern void cpu_flush (void); extern void cpu_adjust_sr (uint32 target); extern void cpu_mark_stack (void); -extern void cpu_ea (HP_WORD mode_disp, ACCESS_CLASS *classification, HP_WORD *offset, BYTE_SELECTOR *selector); +extern void cpu_ea (HP_WORD mode_disp, ACCESS_CLASS *class, HP_WORD *offset, BYTE_SELECTOR *selector); +extern uint32 cpu_byte_ea (ACCESS_CLASS class, uint32 byte_offset, uint32 block_length); extern void cpu_setup_irq_handler (IRQ_CLASS class, HP_WORD parameter); extern void cpu_setup_ics_irq (IRQ_CLASS class, TRAP_CLASS trap); extern void cpu_run_mode_interrupt (HP_WORD device_number); extern void cpu_setup_code_segment (HP_WORD label, HP_WORD* status, HP_WORD *entry_0); extern void cpu_setup_data_segment (HP_WORD segment_number, HP_WORD *bank, HP_WORD *address); -extern void cpu_call_procedure (HP_WORD label); +extern void cpu_call_procedure (HP_WORD label, HP_WORD offset); extern void cpu_exit_procedure (HP_WORD new_q, HP_WORD new_sm, HP_WORD parameter); extern void cpu_start_dispatcher (void); extern void cpu_update_pclk (void); @@ -1103,7 +1102,8 @@ extern void cpu_update_pclk (void); /* Global CPU instruction execution routines */ -extern t_stat cpu_branch_short (t_bool check_loop); +extern t_bool cpu_interrupt_pending (t_stat *status); +extern t_stat cpu_branch_short (t_bool check_loop); extern HP_WORD cpu_add_16 (HP_WORD augend, HP_WORD addend); extern HP_WORD cpu_sub_16 (HP_WORD minuend, HP_WORD subtrahend); @@ -1113,3 +1113,4 @@ extern t_stat cpu_stack_op (void); extern t_stat cpu_shift_branch_bit_op (void); extern t_stat cpu_move_spec_fw_imm_field_reg_op (void); extern t_stat cpu_io_cntl_prog_imm_mem_op (void); +extern t_stat cpu_cis_op (void); diff --git a/HP3000/hp3000_cpu_base.c b/HP3000/hp3000_cpu_base.c index f2b71d19..8fb9508d 100644 --- a/HP3000/hp3000_cpu_base.c +++ b/HP3000/hp3000_cpu_base.c @@ -1,6 +1,6 @@ /* hp3000_cpu_base.c: HP 3000 CPU base set instruction simulator - Copyright (c) 2016, J. David Bryan + Copyright (c) 2016-2017, J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,15 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 08-Jan-17 JDB Fixed bug in SCAL 0/PCAL 0 if a stack overflow occurs + 07-Nov-16 JDB SETR doesn't set cpu_base_changed if no register change; + renamed cpu_byte_to_word_ea to cpu_byte_ea + 03-Nov-16 JDB Added zero offsets to the cpu_call_procedure calls + 24-Oct-16 JDB Renamed SEXT macro to SEXT16 + 22-Oct-16 JDB Changed "interrupt_pending" to global for use by CIS + 07-Oct-16 JDB Moved "extern cpu_dev" to hp3000_cpu.h where it belongs + 22-Sep-16 JDB Moved byte_to_word_address to hp3000_cpu.c + 21-Sep-16 JDB Added the COBOL II Extended Instruction Set dispatcher 12-Sep-16 JDB Use the PCN_SERIES_II and PCN_SERIES_III constants 23-Aug-16 JDB Implement the CMD instruction and module interrupts 11-Jun-16 JDB Bit mask constants are now unsigned @@ -69,14 +78,10 @@ #include "hp3000_cpu.h" #include "hp3000_cpu_fp.h" #include "hp3000_cpu_ims.h" +#include "hp3000_mem.h" -/* External I/O data structures */ - -extern DEVICE cpu_dev; /* Central Processing Unit */ - - /* Program constants */ #define SIO_OK 0100000u /* TIO bit 0 = SIO OK */ @@ -114,9 +119,7 @@ static void shift_48_64 (HP_WORD opcode, SHIFT_TYPE shift, OPERAND_SI static void check_stack_bounds (HP_WORD new_value); static uint32 tcs_io (IO_COMMAND command); static uint32 srw_io (IO_COMMAND command, HP_WORD ready_flag); -static t_bool interrupt_pending (t_stat *status); static void decrement_stack (uint32 decrement); -static uint32 byte_to_word_address (ACCESS_CLASS class, uint32 byte_offset, uint32 block_length); static t_stat move_words (ACCESS_CLASS source_class, uint32 source_base, ACCESS_CLASS dest_class, uint32 dest_base, @@ -133,6 +136,80 @@ static t_stat io_control (void); /* CPU base set global utility routines */ +/* Test for a pending interrupt. + + This routine is called from within an executor for an interruptible + instruction to test for a pending interrupt. It counts an event tick and + returns TRUE if the instruction should yield, either for an interrupt or for + an event error, or FALSE if the instruction should continue. + + Instructions that potentially take a long time (e.g., MOVE, SCU, LLSH) test + for pending interrupts after each word or byte moved or scanned. The design + of these instructions is such that an interrupt may be serviced and the + instruction resumed without disruption. For example, the MOVE instruction + updates the source and target addresses and word count on the stack after + each word moved. If the instruction is interrupted, the values on the stack + indicate where to resume after the interrupt handler completes. + + + Implementation notes: + + 1. The routine is essentially the same sequence as is performed at the top + of the instruction execution loop in the "sim_instr" routine. The + differences are that this routine backs up P to rerun the instruction + after the interrupt is serviced, and the interrupt holdoff test necessary + for the SED instruction isn't done here, as this routine is not called by + the SED executor. + + 2. The event interval decrement that occurs in the main instruction loop + after each instruction execution is cancelled here if "sim_process_event" + returns an error code. This is done so that a STEP command does not + decrement sim_interval twice. Note that skipping the initial decrement + here does not help, as it's the sim_interval value AFTER the call to + sim_process_event that must be preserved. +*/ + +t_bool cpu_interrupt_pending (t_stat *status) +{ +uint32 device_number = 0; + +sim_interval = sim_interval - 1; /* count the cycle */ + +if (sim_interval <= 0) { /* if an event timeout expired */ + *status = sim_process_event (); /* then process the event service */ + + if (*status != SCPE_OK) { /* if the service failed */ + P = P - 1 & R_MASK; /* then back up to reenter the instruction */ + sim_interval = sim_interval + 1; /* and cancel the instruction loop increment */ + + return TRUE; /* abort the instruction and stop the simulator */ + } + } + +else /* otherwise */ + *status = SCPE_OK; /* indicate good status from the service */ + +if (sel_request) /* if a selector channel request is pending */ + sel_service (1); /* then service it */ + +if (mpx_request_set) /* if a multiplexer channel request is pending */ + mpx_service (1); /* then service it */ + +if (iop_interrupt_request_set && STA & STATUS_I) /* if a hardware interrupt request is pending and enabled */ + device_number = iop_poll (); /* then poll to acknowledge the request */ + +if (CPX1 & CPX1_IRQ_SET) { /* if an interrupt is pending */ + P = P - 1 & R_MASK; /* then back up to reenter the instruction */ + cpu_run_mode_interrupt (device_number); /* and set up the service routine */ + + return TRUE; /* abort the instruction */ + } + +else /* otherwise */ + return FALSE; /* continue with the current instruction */ +} + + /* Execute a short branch. The program counter is adjusted by the displacement specified in the CIR, and @@ -259,7 +336,7 @@ HP_WORD cpu_mpy_16 (HP_WORD multiplicand, HP_WORD multiplier) int32 product; uint32 check; -product = SEXT (multiplicand) * SEXT (multiplier); /* sign-extend the operands and multiply */ +product = SEXT16 (multiplicand) * SEXT16 (multiplier); /* sign-extend the operands and multiply */ check = (uint32) product & S16_OVFL_MASK; /* check the top 17 bits and set overflow */ SET_OVERFLOW (check != 0 && check != S16_OVFL_MASK); /* if they are not all zeros or all ones */ @@ -458,7 +535,7 @@ switch (operation) { /* dispatch the stack op case 013: /* MPYL (CCA, C, O; STUN, ARITH) */ - product = SEXT (RA) * SEXT (RB); /* sign-extend the 16-bit operands and multiply */ + product = SEXT16 (RA) * SEXT16 (RB); /* sign-extend the 16-bit operands and multiply */ RB = UPPER_WORD (product); /* split the MSW */ RA = LOWER_WORD (product); /* and the LSW of the product */ @@ -474,7 +551,7 @@ switch (operation) { /* dispatch the stack op case 014: /* DIVL (CCA, O; STUN, ARITH) */ dividend = INT32 (TO_DWORD (RC, RB)); /* convert the 32-bit dividend to a signed value */ - divisor = SEXT (RA); /* and sign-extend the 16-bit divisor */ + divisor = SEXT16 (RA); /* and sign-extend the 16-bit divisor */ RB = RA; /* delete the LSW from the stack now */ cpu_pop (); /* to conform with the microcode */ @@ -482,7 +559,7 @@ switch (operation) { /* dispatch the stack op if (RA == 0) /* if dividing by zero */ MICRO_ABORT (trap_Integer_Zero_Divide); /* then trap or set the overflow flag */ - if (abs (divisor) <= abs (SEXT (RB))) /* if the divisor is <= the MSW of the dividend */ + if (abs (divisor) <= abs (SEXT16 (RB))) /* if the divisor is <= the MSW of the dividend */ SET_OVERFLOW (TRUE); /* an overflow will occur on the division */ else { /* otherwise, the divisor might be large enough */ @@ -558,8 +635,8 @@ switch (operation) { /* dispatch the stack op if (RA == 0) /* if dividing by zero */ MICRO_ABORT (trap_Integer_Zero_Divide); /* then trap or set the overflow flag */ - dividend = SEXT (RB); /* sign-extend the 16-bit dividend */ - divisor = SEXT (RA); /* and the 16-bit divisor */ + dividend = SEXT16 (RB); /* sign-extend the 16-bit dividend */ + divisor = SEXT16 (RA); /* and the 16-bit divisor */ quotient = dividend / divisor; /* form the 32-bit signed quotient */ remainder = dividend % divisor; /* and 32-bit signed remainder */ @@ -1142,10 +1219,10 @@ switch (operation) { /* dispatch the shift/br case 026: /* CPRB (CCE, CCL, CCG; STUN, BNDV) */ - if (SEXT (X) < SEXT (RB)) /* if X is less than the lower bound */ + if (SEXT16 (X) < SEXT16 (RB)) /* if X is less than the lower bound */ SET_CCL; /* then set CCL and continue */ - else if (SEXT (X) > SEXT (RA)) /* otherwise if X is greater than the upper bound */ + else if (SEXT16 (X) > SEXT16 (RA)) /* otherwise if X is greater than the upper bound */ SET_CCG; /* then set CCG and continue */ else { /* otherwise lower bound <= X <= upper bound */ @@ -1349,7 +1426,7 @@ switch (operation) { /* dispatch the operatio if (divisor == 0) /* if dividing by zero */ MICRO_ABORT (trap_Integer_Zero_Divide); /* then trap or set the overflow flag */ - RA = SEXT (RA) / divisor & R_MASK; /* store the quotient (which cannot overflow) on the TOS */ + RA = SEXT16 (RA) / divisor & R_MASK; /* store the quotient (which cannot overflow) on the TOS */ SET_CCA (RA, 0); /* and set the condition code */ break; @@ -1549,7 +1626,7 @@ switch (operation) { /* dispatch the operatio if (CIR & PSR_SBANK) /* if SBANK is to be set */ SBANK = new_sbank & BA_MASK; /* then update the new value now */ - cpu_base_changed = TRUE; /* this instruction changed the base registers */ + cpu_base_changed = (CIR != SETR && CIR != SETR_X); /* set the flag if the base registers changed */ break; } /* all cases are handled */ @@ -1656,8 +1733,14 @@ switch (operation) { /* dispatch the operatio cpu_flush (); /* flush the TOS registers to memory */ - if (SM > Z) /* if the stack limit was exceeded */ - MICRO_ABORT (trap_Stack_Overflow); /* then trap for a stack overflow */ + if (SM > Z) { /* if the stack limit was exceeded */ + if (field == 0) { /* then if the label was on the TOS */ + cpu_push (); /* then push the stack down */ + RA = label; /* and restore the label to the TOS */ + } + + MICRO_ABORT (trap_Stack_Overflow); /* trap for a stack overflow */ + } if (label & LABEL_EXTERNAL) /* if the label is non-local */ MICRO_ABORTP (trap_STT_Violation, STA); /* then trap for an STT violation */ @@ -1685,12 +1768,18 @@ switch (operation) { /* dispatch the operatio cpu_flush (); /* flush the TOS registers to memory */ - if (SM > Z) /* if the stack limit was exceeded */ - MICRO_ABORT (trap_Stack_Overflow); /* then trap for a stack overflow */ + if (SM > Z) { /* if the stack limit was exceeded */ + if (field == 0) { /* then if the label was on the TOS */ + cpu_push (); /* then push the stack down */ + RA = label; /* and restore the label to the TOS */ + } + + MICRO_ABORT (trap_Stack_Overflow); /* trap for a stack overflow */ + } cpu_mark_stack (); /* write a stack marker */ - cpu_call_procedure (label); /* set up PB, P, PL, and STA to call the procedure */ + cpu_call_procedure (label, 0); /* set up PB, P, PL, and STA to call the procedure */ break; @@ -1702,7 +1791,7 @@ switch (operation) { /* dispatch the operatio new_sm = Q - 4 - field & R_MASK; /* compute the new stack pointer value */ - cpu_read_memory (stack_checked, Q, &operand); /* read the delta Q value from the stack marker */ + cpu_read_memory (stack, Q, &operand); /* read the delta Q value from the stack marker */ new_q = Q - operand & R_MASK; /* and determine the new Q value */ cpu_exit_procedure (new_q, new_sm, field); /* set up the return code segment and stack */ @@ -1755,7 +1844,7 @@ switch (operation) { /* dispatch the operatio else /* otherwise */ label = LABEL_EXTERNAL /* convert it to an external label */ | (field << LABEL_STTN_SHIFT) /* by merging the STT number */ - | STA & STATUS_CS_MASK; /* with the currently executing segment number */ + | STATUS_CS (STA); /* with the currently executing segment number */ cpu_push (); /* push the stack down */ RA = label; /* and store the label on the TOS */ @@ -2256,7 +2345,7 @@ if (NPRV) /* if the mode is not pr address = SM + SR - IO_K (CIR) & LA_MASK; /* get the location of the device number */ -cpu_read_memory (stack_checked, address, &device); /* read it from the stack */ +cpu_read_memory (stack, address, &device); /* read it from the stack or TOS registers */ device = LOWER_BYTE (device); /* and use only the lower byte of the value */ result = iop_direct_io (device, command, /* send the I/O order to the device */ @@ -2341,80 +2430,6 @@ else { /* otherwise the device } -/* Test for a pending interrupt. - - This routine is called from within an executor for an interruptible - instruction to test for a pending interrupt. It counts an event tick and - returns TRUE if the instruction should yield, either for an interrupt or for - an event error, or FALSE if the instruction should continue. - - Instructions that potentially take a long time (e.g., MOVE, SCU, LLSH) test - for pending interrupts after each word or byte moved or scanned. The design - of these instructions is such that an interrupt may be serviced and the - instruction resumed without disruption. For example, the MOVE instruction - updates the source and target addresses and word count on the stack after - each word moved. If the instruction is interrupted, the values on the stack - indicate where to resume after the interrupt handler completes. - - - Implementation notes: - - 1. The routine is essentially the same sequence as is performed at the top - of the instruction execution loop in the "sim_instr" routine. The - differences are that this routine backs up P to rerun the instruction - after the interrupt is serviced, and the interrupt holdoff test necessary - for the SED instruction isn't done here, as this routine is not called by - the SED executor. - - 2. The event interval decrement that occurs in the main instruction loop - after each instruction execution is cancelled here if "sim_process_event" - returns an error code. This is done so that a STEP command does not - decrement sim_interval twice. Note that skipping the initial decrement - here does not help, as it's the sim_interval value AFTER the call to - sim_process_event that must be preserved. -*/ - -static t_bool interrupt_pending (t_stat *status) -{ -uint32 device_number = 0; - -sim_interval = sim_interval - 1; /* count the cycle */ - -if (sim_interval <= 0) { /* if an event timeout expired */ - *status = sim_process_event (); /* then process the event service */ - - if (*status != SCPE_OK) { /* if the service failed */ - P = P - 1 & R_MASK; /* then back up to reenter the instruction */ - sim_interval = sim_interval + 1; /* and cancel the instruction loop increment */ - - return TRUE; /* abort the instruction and stop the simulator */ - } - } - -else /* otherwise */ - *status = SCPE_OK; /* indicate good status from the service */ - -if (sel_request) /* if a selector channel request is pending */ - sel_service (1); /* then service it */ - -if (mpx_request_set) /* if a multiplexer channel request is pending */ - mpx_service (1); /* then service it */ - -if (iop_interrupt_request_set && STA & STATUS_I) /* if a hardware interrupt request is pending and enabled */ - device_number = iop_poll (); /* then poll to acknowledge the request */ - -if (CPX1 & CPX1_IRQ_SET) { /* if an interrupt is pending */ - P = P - 1 & R_MASK; /* then back up to reenter the instruction */ - cpu_run_mode_interrupt (device_number); /* and set up the service routine */ - - return TRUE; /* abort the instruction */ - } - -else /* otherwise */ - return FALSE; /* continue with the current instruction */ -} - - /* Decrement the stack pointer. Pop values from the stack until the stack pointer has been decremented by the @@ -2442,87 +2457,6 @@ return; } -/* Convert a data- or program-relative byte address to a word address. - - The supplied byte offset from DB or PB is converted to a memory address, - bounds-checked, and then returned. If the supplied block length is not zero, - the converted address is assumed to be the starting address of a block, and - an ending address, based on the block length, is calculated and - bounds-checked. If either address lies outside of the segment associated - with the access class, a Bounds Violation trap occurs, unless a privileged - data access is requested. - - Byte offsets into data segments present problems, in that negative offsets - are permitted (to access the DL-to-DB area), but there are not enough bits to - represent all locations unambiguously in the potential -32K to +32K word - offset range. Therefore, a byte offset with bit 0 = 1 can represent either a - positive or negative word offset from DB, depending on the interpretation. - The HP 3000 adopts the convention that if the address resulting from a - positive-offset interpretation does not fall within the DL-to-S range, then - 32K is added to the address, effectively changing the interpretation from a - positive to a negative offset. If this new address does not fall within the - DL-to-S range, a Bounds Violation trap occurs if the mode is non-privileged. - - The reinterpretation as a negative offset is performed only if the CPU is not - in split-stack mode (where either DBANK is different from SBANK, or DB does - not lie between DL and Z), as extra data segments do not permit negative-DB - addressing. Reinterpretation is also not used for code segments, as negative - offsets from PB are not permitted. - - - Implementation notes: - - 1. This routine implements the DBBC microcode subroutine. -*/ - -static uint32 byte_to_word_address (ACCESS_CLASS class, uint32 byte_offset, uint32 block_length) -{ -uint32 starting_word, ending_word, increment; - -if (block_length & D16_SIGN) /* if the block length is negative */ - increment = 0177777; /* then the memory increment is negative also */ -else /* otherwise */ - increment = 1; /* the increment is positive */ - -if (class == data) { /* if this is a data access */ - starting_word = DB + (byte_offset >> 1) & LA_MASK; /* then determine the starting word address */ - - if (DBANK == SBANK && DL <= DB && DB <= Z /* if not in split-stack mode */ - && (starting_word < DL || starting_word > SM)) { /* and the word address is out of range */ - starting_word = starting_word ^ D16_SIGN; /* then add 32K and try again */ - - if (NPRV && (starting_word < DL || starting_word > SM)) /* if non-privileged and still out of range */ - MICRO_ABORT (trap_Bounds_Violation); /* then trap for a bounds violation */ - } - - if (block_length != 0) { /* if a block length was supplied */ - ending_word = /* then determine the ending word address */ - starting_word + ((block_length - increment + (byte_offset & 1)) >> 1) & LA_MASK; - - if (NPRV && (ending_word < DL || ending_word > SM)) /* if non-privileged and the address is out of range */ - MICRO_ABORT (trap_Bounds_Violation); /* then trap for a bounds violation */ - } - } - -else { /* otherwise this is a program address */ - starting_word = PB + (byte_offset >> 1) & LA_MASK; /* so determine the starting word address */ - - if (starting_word < PB || starting_word > PL) /* if the starting address is out of range */ - MICRO_ABORT (trap_Bounds_Violation); /* then trap for a bounds violation */ - - if (block_length != 0) { /* if a block length was supplied */ - ending_word = /* then determine the ending address */ - starting_word + ((block_length - increment + (byte_offset & 1)) >> 1) & LA_MASK; - - if (ending_word < PB || ending_word > PL) /* if the ending address is out of range */ - MICRO_ABORT (trap_Bounds_Violation); /* then trap for a bounds violation */ - } - } - -return starting_word; /* return the starting word address */ -} - - /* Move a block of words in memory. A block of words is moved from a source address to a destination address. If @@ -2602,7 +2536,7 @@ while (RA != 0) { /* while there are words RB = RB + increment & R_MASK; /* and the source */ *RX = *RX + increment & R_MASK; /* and destination offsets */ - if (interrupt_pending (&status)) /* if an interrupt is pending */ + if (cpu_interrupt_pending (&status)) /* if an interrupt is pending */ return status; /* then return with an interrupt set up or an error */ } @@ -2629,6 +2563,11 @@ return SCPE_OK; /* and return the succ | 0 0 1 0 | 0 0 0 0 | special op | 0 0 | sp op | Special +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + Byte move and compare instructions that specify byte counts (e.g., MVB, CMPB) + bounds-check the starting and ending addresses to avoid checking each access + separately. Instructions that do not (e.g., SCW, MVBW) must bounds-check + each access, as the counts are indeterminate. + Implementation notes: @@ -2788,13 +2727,17 @@ switch (operation) { /* dispatch the move or else /* otherwise */ increment = 1; /* the increment is positive */ - if (CIR & DB_FLAG) /* if the move is from the data segment */ - class = data; /* then classify as a data access */ - else /* otherwise the move is from the code segment */ - class = program; /* so classify as a program access */ + if (CIR & DB_FLAG) { /* if the move is from the data segment */ + class = data; /* then classify as a data access */ + source = cpu_byte_ea (data_checked, RB, RA); /* and convert and check the byte address */ + } - source = byte_to_word_address (class, RB, RA); /* convert the source byte address and check bounds */ - target = byte_to_word_address (data, RC, RA); /* convert the target byte address and check bounds */ + else { /* otherwise the move is from the code segment */ + class = program; /* so classify as a program access */ + source = cpu_byte_ea (program_checked, RB, RA); /* and convert and check the byte address */ + } + + target = cpu_byte_ea (data_checked, RC, RA); /* convert the target byte address and check the bounds */ while (RA != 0) { /* while there are bytes to move */ cpu_read_memory (class, source, &operand); /* read a source word */ @@ -2823,7 +2766,7 @@ switch (operation) { /* dispatch the move or RB = RB + increment & R_MASK; /* and the source */ RC = RC + increment & R_MASK; /* and destination offsets */ - if (interrupt_pending (&status)) /* if an interrupt is pending */ + if (cpu_interrupt_pending (&status)) /* if an interrupt is pending */ return status; /* then return with an interrupt set up or an error */ } } @@ -2872,13 +2815,13 @@ switch (operation) { /* dispatch the move or test_byte = LOWER_BYTE (RA); /* get the test byte */ terminal_byte = UPPER_BYTE (RA); /* and the terminal byte */ - source = byte_to_word_address (data, RB, 0); /* convert the source byte address and check the bounds */ + source = cpu_byte_ea (data_checked, RB, 0); /* convert the source byte address and check the bounds */ cpu_read_memory (data, source, &operand); /* read the first word */ while (TRUE) { if (RB & 1) { /* if the byte address is odd */ - if (interrupt_pending (&status)) /* then if an interrupt is pending */ + if (cpu_interrupt_pending (&status)) /* then if an interrupt is pending */ return status; /* then return with an interrupt set up or an error */ byte = LOWER_BYTE (operand); /* get the lower byte */ @@ -3000,8 +2943,8 @@ switch (operation) { /* dispatch the move or while (SR > 2) /* if more than two TOS registers are valid */ cpu_queue_down (); /* then queue them down until exactly two are left */ - source = byte_to_word_address (data, RA, 0); /* convert the source */ - target = byte_to_word_address (data, RB, 0); /* and target byte addresses and check the bounds */ + source = cpu_byte_ea (data_checked, RA, 0); /* convert the source and target */ + target = cpu_byte_ea (data_checked, RB, 0); /* byte addresses and check the starting bounds */ if (source > target) { /* if the source is closer to SM than the target */ byte_count = (int32) (SM - source + 1) * 2; /* then set the byte count from the source */ @@ -3057,7 +3000,7 @@ switch (operation) { /* dispatch the move or RA = RA + 1 & R_MASK; /* and the source */ RB = RB + 1 & R_MASK; /* and destination offsets */ - if (interrupt_pending (&status)) /* if an interrupt is pending */ + if (cpu_interrupt_pending (&status)) /* if an interrupt is pending */ return status; /* then return with an interrupt set up or an error */ } @@ -3080,13 +3023,17 @@ switch (operation) { /* dispatch the move or else /* otherwise */ increment = 1; /* the increment is positive */ - if (CIR & DB_FLAG) /* if the move is from the data segment */ - class = data; /* then classify as a data access */ - else /* otherwise the move is from the code segment */ - class = program; /* so classify as a program access */ + if (CIR & DB_FLAG) { /* if the comparison is from the data segment */ + class = data; /* then classify as a data access */ + source = cpu_byte_ea (data_checked, RB, RA); /* and convert and check the byte address */ + } - source = byte_to_word_address (class, RB, RA); /* convert the source byte address and check bounds */ - target = byte_to_word_address (data, RC, RA); /* convert the target byte address and check bounds */ + else { /* otherwise the comparison is from the code segment */ + class = program; /* so classify as a program access */ + source = cpu_byte_ea (program_checked, RB, RA); /* and convert and check the byte address */ + } + + target = cpu_byte_ea (data_checked, RC, RA); /* convert the target byte address and check the bounds */ while (RA != 0) { /* while there are bytes to compare */ cpu_read_memory (class, source, &operand); /* read a source word */ @@ -3116,7 +3063,7 @@ switch (operation) { /* dispatch the move or RB = RB + increment & R_MASK; /* and the source */ RC = RC + increment & R_MASK; /* and destination offsets */ - if (interrupt_pending (&status)) /* if an interrupt is pending */ + if (cpu_interrupt_pending (&status)) /* if an interrupt is pending */ return status; /* then return with an interrupt set up or an error */ } } @@ -3160,7 +3107,7 @@ switch (operation) { /* dispatch the move or X = X - 1 & R_MASK; /* decrement the count */ - if (interrupt_pending (&status)) /* if an interrupt is pending */ + if (cpu_interrupt_pending (&status)) /* if an interrupt is pending */ return status; /* then return with an interrupt set up or an error */ } @@ -3182,14 +3129,14 @@ switch (operation) { /* dispatch the move or if (PRIV) /* if the mode is privileged */ if (CIR & 1) { /* PSTA (none; STUN, MODE) */ PREADJUST_SR (1); /* ensure a valid TOS register */ - cpu_write_memory (absolute_checked, /* before writing the TOS to memory */ + cpu_write_memory (absolute_mapped, /* before writing the TOS to memory */ X, RA); /* at address X */ cpu_pop (); /* and popping the stack */ } else { /* PLDA (CCA; STOV, MODE) */ - cpu_read_memory (absolute_checked, /* read the value at address X */ - X, &operand); + cpu_read_memory (absolute_mapped, /* read the value at address X */ + X, &operand); cpu_push (); /* push the stack down */ RA = operand; /* and store the value on the TOS */ @@ -3213,7 +3160,7 @@ switch (operation) { /* dispatch the move or cpu_queue_down (); /* then queue them down until exactly two are left */ address = TO_PA (RB, RA); /* form the physical address */ - cpu_read_memory (absolute_checked, /* and read the word from memory */ + cpu_read_memory (absolute, /* and read the word from memory */ address, &operand); cpu_push (); /* push the stack down */ @@ -3230,7 +3177,7 @@ switch (operation) { /* dispatch the move or cpu_queue_down (); /* then queue them down until exactly three are left */ address = TO_PA (RC, RB); /* form the physical address */ - cpu_write_memory (absolute_checked, /* and write the word on the TOS to memory */ + cpu_write_memory (absolute, /* and write the word on the TOS to memory */ address, RA); cpu_pop (); /* pop the TOS */ @@ -3242,15 +3189,14 @@ switch (operation) { /* dispatch the move or cpu_queue_down (); /* then queue them down until exactly two are left */ address = TO_PA (RB, RA); /* form the physical address */ - cpu_read_memory (absolute_checked, /* and read the MSW from memory */ + cpu_read_memory (absolute, /* and read the MSW from memory */ address, &operand); cpu_push (); /* push the stack down */ RA = operand; /* and store the MSW on the TOS */ address = TO_PA (RC, RB + 1 & LA_MASK); /* increment the physical address */ - - cpu_read_memory (absolute_checked, /* read the LSW from memory */ + cpu_read_memory (absolute, /* and read the LSW from memory */ address, &operand); cpu_push (); /* push the stack down again */ @@ -3264,12 +3210,11 @@ switch (operation) { /* dispatch the move or PREADJUST_SR (4); /* ensure there are four valid TOS registers */ address = TO_PA (RD, RC); /* form the physical address */ - cpu_write_memory (absolute_checked, /* write the MSW from the NOS to memory */ + cpu_write_memory (absolute, /* write the MSW from the NOS to memory */ address, RB); address = TO_PA (RD, RC + 1 & LA_MASK); /* increment the physical address */ - - cpu_write_memory (absolute_checked, /* write the LSW on the TOS to memory */ + cpu_write_memory (absolute, /* and write the LSW on the TOS to memory */ address, RA); cpu_pop (); /* pop the TOS */ @@ -3302,7 +3247,7 @@ switch (operation) { /* dispatch the move or new_q = 0; /* but the compiler doesn't realize this and so warns */ if (!disp_active) { /* if not called by the dispatcher to start a process */ - if ((STA & STATUS_CS_MASK) > 1) { /* then if an external interrupt was serviced */ + if (STATUS_CS (STA) > 1) { /* then if an external interrupt was serviced */ cpu_read_memory (stack, /* then get the device number (parameter) */ Q + 3 & LA_MASK, &device); @@ -3465,11 +3410,15 @@ return status; /* return the execution 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | 0 0 1 0 | 0 0 0 1 | 0 1 1 1 1 0 0 | x | DMUL/DDIV + | 0 0 1 0 | 0 0 0 1 | 0 1 1 1 | 1 0 0 | x | DMUL/DDIV +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - | 0 0 1 0 | 0 0 0 1 | 0 0 0 0 1 | ext fp op | Extended FP + | 0 0 1 0 | 0 0 0 1 | 0 0 0 0 | 1 | ext fp op | Extended FP + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | COBOL op | COBOL +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ @@ -3491,19 +3440,16 @@ return status; /* return the execution W1 0000 020400-020417 Extended Instruction Set (Floating Point) W2 0001 020420-020437 32105A APL Instruction Set W3 0010 020440-020457 - W4 0011 020460-020477 32234A COBOL II Instruction Set + W4 0011 020460-020477 32234A COBOL II Extended Instruction Set W5 0100 020500-020517 W6 0101 020520-020537 W7 0110 020540-020557 -- 0111 020560-020577 Base Set (DMUL/DDIV) - W8 1000 020600-020777 Extended Instruction Set (Decimal Arith) + W8 1xxx 020600-020777 Extended Instruction Set (Decimal Arith) The range occupied by the base set has no jumper and is hardwired as - "present". - - In simulation, presence is determined by the settings of the CPU unit flags. - Currently, the only defined option flag is UNIT_EIS, although the EIS itself - has not been implemented. + "present". In simulation, presence is determined by the settings of the CPU + unit flags. Implementation notes: @@ -3525,6 +3471,14 @@ operation = FIRMEXTOP (CIR); /* get the operation fro switch (operation) { /* dispatch the operation */ + case 003: /* COBOL II Extended Instruction Set */ + if (cpu_unit [0].flags & UNIT_CIS) /* if the firmware is installed */ + status = cpu_cis_op (); /* then call the CIS dispatcher */ + else /* otherwise */ + status = STOP_UNIMPL; /* the instruction range decodes as unimplemented */ + break; + + case 007: /* base set */ suboperation = FMEXSUBOP (CIR); /* get the suboperation from the instruction */ @@ -3915,11 +3869,11 @@ switch (operation) { /* dispatch the I/O or c case 006: /* XEQ (none; BNDV) */ address = SM + SR - IO_K (CIR) & LA_MASK; /* get the address of the target instruction */ - if (address >= DB || PRIV) { /* if the address is not below DB or the mode is privileged */ - cpu_read_memory (stack_checked, address, &NIR); /* the read the word at S - K into the NIR */ + if (address >= DB || PRIV) { /* if the address is not below DB or the mode is privileged */ + cpu_read_memory (stack, address, &NIR); /* then read the word at S - K into the NIR */ - P = P - 1 & R_MASK; /* decrement P so the instruction after XEQ is next */ - sim_interval = sim_interval + 1; /* but don't count the XEQ against a STEP count */ + P = P - 1 & R_MASK; /* decrement P so the instruction after XEQ is next */ + sim_interval = sim_interval + 1; /* but don't count the XEQ against a STEP count */ } else /* otherwise the address is below DB and not privileged */ @@ -3975,8 +3929,8 @@ switch (operation) { /* dispatch the I/O or c if (NPRV) /* if the mode is not privileged */ MICRO_ABORT (trap_Privilege_Violation); /* then abort with a privilege violation */ - address = SM + SR - IO_K (CIR) & LA_MASK; /* get the location of the command word */ - cpu_read_memory (stack_checked, address, &command); /* and read it from the stack */ + address = SM + SR - IO_K (CIR) & LA_MASK; /* get the location of the command word */ + cpu_read_memory (stack, address, &command); /* and read it from the stack or TOS registers */ module = CMD_TO (command); /* get the addressed (TO) module number */ diff --git a/HP3000/hp3000_cpu_cis.c b/HP3000/hp3000_cpu_cis.c new file mode 100644 index 00000000..dd58667c --- /dev/null +++ b/HP3000/hp3000_cpu_cis.c @@ -0,0 +1,1970 @@ +/* hp3000_cpu_cis.c: HP 32234A COBOL II Instruction Set simulator + + Copyright (c) 2016, J. David Bryan + + 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 + AUTHOR 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. + + 29-Dec-16 JDB Disabled interrupt checks pending test generation + 19-Oct-16 JDB Passes the COBOL-II firmware "A" diagnostic (D441A) + 06-Oct-16 JDB Passes the COBOL-II firmware "B" diagnostic (D442A) + 21-Sep-16 JDB Created + + References: + - Machine Instruction Set Reference Manual + (30000-90022, June 1984) + - HP 3000 Series 64/68/70 Computer Systems Microcode Manual + (30140-90045, October 1986) + + + This module implements the HP 32234A COBOL II Extended Instruction + Set firmware, also known as the Language Extension Instructions. The set + contains these instructions in the firmware extension range 020460-020477: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 0 0 0 | S | ALGN + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 0 0 1 | S | ABSN + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 0 0 | B | EDIT + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 0 1 | B | CMPS + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 0 0 | XBR + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 0 1 | PARC + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 0 | ENDP + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | CMPT + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 0 0 0 0 0 0 0 0 | 0 0 0 1 1 | B | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | TCCS + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 0 0 0 0 0 0 0 0 | 0 0 1 | > | = | < | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | CVND + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 0 0 0 0 0 0 0 0 | 0 1 | sign op | S | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | LDW + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 0 0 0 0 0 0 0 0 | 1 0 0 0 0 | S | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | LDDW + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 0 0 0 0 0 0 0 0 | 1 0 0 0 1 | S | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | TR + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 0 0 0 0 0 0 0 0 | 1 0 0 1 0 | B | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | ABSD + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 0 0 0 0 0 0 0 0 | 1 0 0 1 1 | S | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | 1 1 1 1 | NEGD + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 0 0 0 0 0 0 0 0 | 1 0 1 0 0 | S | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + Where: + + S = S-decrement + B = Bank select (0/1 = PB-relative/DB-relative) + + The PARC, ENDP, and XBR instructions implement the COBOL "PERFORM" statement. + ABSD and NEGD manipulate packed decimal numbers. ALGN, ABSN, EDIT, and CVND + manipulate external decimal numbers. The LDW and LDDW instructions load + single and double-words, respectively, from byte-aligned addresses. TCCS + tests the status register for a specific condition code and loads the logical + result; operation is similar to the Bcc instruction, except that the result + is explicit rather than implied by the branch. TR translates a string using + a mapping table. CMPS and CMPT compare two strings (with translation for + CMPT) and set the condition code accordingly. CMPS is similar to CMPB, + except that a shorter string is blank-padded for comparison. + + + Packed decimal (also known as COMPUTATIONAL-3, BCD, and binary-coded decimal) + numbers contain from 1 to 28 digits that are stored in pairs in successive + memory bytes in this format: + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | unused/digit | digit | + +---+---+---+---+---+---+---+---+ + | digit | digit | + +---+---+---+---+---+---+---+---+ + + [...] + + +---+---+---+---+---+---+---+---+ + | digit | digit | + +---+---+---+---+---+---+---+---+ + | digit | sign | + +---+---+---+---+---+---+---+---+ + + The sign is always located in the lower four bits of the final byte, so + numbers with an even number of digits will not use the upper four bits of the + first byte. Digits are represented by four-bit values from 0-9 (i.e., in + Binary-Coded Decimal or BCD), with the most-significant digit first and the + least-significant digit last. The sign is given by one of these encodings: + + 1100 - the number is positive + 1101 - the number is negative + 1111 - the number is unsigned + + All other values are interpreted as meaning the number is positive; however, + only one of the three values above is generated. + + Numbers may begin at an even or odd byte address, and the size of the number + (in digits) may be even or odd, so there are four possible cases of packing + into 16-bit words: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | unused/digit | digit | digit | digit | addr even + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | digit | digit | digit | sign | size even + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | unused/digit | digit | digit | digit | addr even + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | digit | sign | ... | size odd + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | ... | unused/digit | digit | addr odd + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | digit | digit | digit | sign | size ?... + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | ... | unused/digit | digit | addr odd + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | digit | sign | ... | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + + External decimal (also known as DISPLAY, numeric display, and ASCII) values + contain contain from 1 to 28 digits that are stored as ASCII characters in + successive memory bytes in this format: + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | digit | + +---+---+---+---+---+---+---+---+ + | digit | + +---+---+---+---+---+---+---+---+ + + [...] + + +---+---+---+---+---+---+---+---+ + | digit | + +---+---+---+---+---+---+---+---+ + | digit and sign | + +---+---+---+---+---+---+---+---+ + + The number begins with the most-significant digit. The sign is combined with + the least-significant digit in the final byte. Each digit except the LSD + must be in the ASCII range "0" through "9". Leading blanks are allowed, and + the entire number may be blank, but blanks within a number are not. The + least-signifiant digit and sign are represented by either: + + "0" and "1" through "9" for an unsigned number + "{" and "A" through "I" for a positive number + "}" and "J" through "R" for a negative number + + Numbers may begin at an even or odd byte address, and the size of the number + (in digits) may be even or odd, so there are four possible cases of packing + into 16-bit words: + + - the number completely fills the words + - the number has an unused leading byte in the first word + - the number has an unused trailing byte in the last word + - the number has an unused byte at each end + + Any unused bytes are not part of the number and are not disturbed. + + The EDIT instruction moves bytes from a source string to a target string + under the control of a subprogram indicated by a PB- or DB-relative address. + The subprogram consists of 8-bit instructions, each followed by zero or more + 8-bit operands. The subprogram ends with a TE (Terminate Edit) instruction. + + The supported instructions are: + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 0 0 0 0 | imm. operand | MC - move characters + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 0 0 0 1 | imm. operand | MA - move alphabetics + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 0 0 1 0 | imm. operand | MN - move numerics + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 0 0 1 1 | imm. operand | MNS - move numerics suppressed + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 0 1 0 0 | imm. operand | MFL - move numerics with floating insertion + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 0 1 0 1 | imm. operand | IC - insert character + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + | character to insert | + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 0 1 1 0 | imm. operand | ICS - insert character suppressed + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + | character to insert | + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 0 1 1 1 | imm. operand | ICI - insert characters immediate + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + | character 1 to insert | + +---+---+---+---+---+---+---+---+ + ... + +---+---+---+---+---+---+---+---+ + { | character n to insert | } (present if operand > 1) + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 0 0 0 | imm. operand | ICSI - insert characters suppressed immediate + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + | character 1 to insert | + +---+---+---+---+---+---+---+---+ + ... + +---+---+---+---+---+---+---+---+ + { | character n to insert | } (present if operand > 1) + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 0 0 1 | imm. operand | BRIS - branch if significance + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 0 1 0 | imm. operand | SUFT - subtract from target + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 0 1 1 | imm. operand | SUFS - subtract from source + +---+---+---+---+---+---+---+---+ + { | extended operand | } (present if imm. operand = 0) + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 0 1 1 | imm. operand | ICP - insert character punctuation + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 0 0 | imm. operand | ICPS - insert character punctuation suppressed + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 1 0 | imm. operand | IS - insert character on sign + +---+---+---+---+---+---+---+---+ + | positive sign character 1 | + +---+---+---+---+---+---+---+---+ + ... + +---+---+---+---+---+---+---+---+ + { | character n to insert | } (present if operand > 1) + +---+---+---+---+---+---+---+---+ + | negative sign character 1 | + +---+---+---+---+---+---+---+---+ + ... + +---+---+---+---+---+---+---+---+ + { | character n to insert | } (present if operand > 1) + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 1 1 | 0 0 0 0 | TE - terminate edit + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 1 1 | 0 0 0 1 | ENDF - end floating point insertion + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 1 1 | 0 0 1 0 | SST1 - set significance to 1 + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 1 1 | 0 0 1 1 | SST0 - set significance to 0 + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 1 1 | 0 1 0 0 | MDWO - move digit with overpunch + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 1 1 | 0 1 0 1 | SFC - set fill character + +---+---+---+---+---+---+---+---+ + | fill character | + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 1 1 | 0 1 1 0 | SFLC - set float character + +---+---+---+---+---+---+---+---+ + | + float char | - float char | + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 1 1 | 0 1 1 1 | DFLC - define float character + +---+---+---+---+---+---+---+---+ + | positive float character | + +---+---+---+---+---+---+---+---+ + | negative float character | + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 1 1 | 1 0 0 0 | SETC - set loop count + +---+---+---+---+---+---+---+---+ + | loop count | + +---+---+---+---+---+---+---+---+ + + +---+---+---+---+---+---+---+---+ + | 1 1 1 1 | 1 0 0 1 | DBNZ - decrement loop count and branch + +---+---+---+---+---+---+---+---+ + | branch displacement | + +---+---+---+---+---+---+---+---+ + + The EDIT instruction is interruptible after each subprogram command. The TR, + CMPS, and CMPT instructions are interruptible after each byte processed. The + remaining instructions execute to completion. + + Two user traps may be taken by these instructions if the T bit is on in the + status register: + + - Word Count Overflow (parameter 17) + - Invalid ASCII Digit (parameter 14) + + The Word Count Overflow trap is taken when an instruction is given an + external decimal number with more than 28 digits. The Invalid ASCII Digit + trap occurs when an external decimal number contains characters other than + "0"-"9", a "+" or "-" sign in any position other than the first or last + position, blanks other than leading blanks, an invalid overpunch character, + or an overpunch character in any position other than the last digit. + + Enabling the OPND debug flag traces the instruction operands, including the + subprogram operations of the EDIT instruction. + + + Implementation notes: + + 1. In several cases noted below, the hardware microcode implementations + differ from the descriptions in the Machine Instruction Set manual. + Also, the comments in the microcode source sometimes do not correctly + described the microcode actions. In all cases of conflict, the simulator + follows the microcode implementation. + + 2. The Machine Instruction Set manual references trap conditions (Invalid + Alphabetic Character, Invalid Operand Length, Invalid Source Character + Count, and Invalid Digit Count) that are not defined in the Series II/III + System Reference Manual. Examination of the microcode indicates that + only the Invalid ASCII Digit and Word Count Overflow traps are taken. + + 3. Target operand tracing is not done if a trap occurred, as the result will + be invalid. + + 4. The calls to "cpu_interrupt_pending" are currently stubbed out pending + testing of interrupted instruction exits and reentries. The COBOL II + firmware diagnostics do not test interruptibility, so tests have to be + written before correct action is assured. This will be addressed in a + future release. +*/ + + + +#include "hp3000_defs.h" +#include "hp3000_cpu.h" +#include "hp3000_mem.h" + + + +/* Disable intra-instruction interrupt checking pending testing */ + +#define cpu_interrupt_pending(x) FALSE /* TEMPORARY ONLY */ + + +/* Program constants */ + +#define MAX_DIGITS 28 /* maximum number of decimal digits accepted */ + + +/* Packed decimal constants */ + +#define SIGN_MASK 0360u /* 8-bit numeric sign mask */ +#define SIGN_TEST_MASK 0017u /* 8-bit numeric sign test mask */ + +#define SIGN_PLUS 0014u /* 1100 -> the number is positive */ +#define SIGN_MINUS 0015u /* 1101 -> the number is negative */ +#define SIGN_UNSIGNED 0017u /* 1111 -> the number is unsigned */ + +#define IS_NEG(v) (((v) & SIGN_TEST_MASK) == SIGN_MINUS) + + +/* External decimal constants */ + +typedef enum { /* location and type of the numeric sign character */ + Leading_Separate = 0, /* plus or minus prefix */ + Trailing_Separate = 1, /* plus or minus suffix */ + Leading_Overpunch = 2, /* overpunched first character */ + Trailing_Overpunch = 3, /* overpunched last character */ + Absolute = 4 /* no sign character */ + } DISPLAY_MODE; + + +typedef enum { /* numeric sign values */ + Negative, + Unsigned, + Positive + } NUMERIC_SIGN; + +static uint8 overpunch [3] [10] = { /* sign overpunches, indexed by NUMERIC_SIGN */ + { '}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R' }, /* Negative */ + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }, /* Unsigned */ + { '{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I' } /* Positive */ + }; + + +/* Operand printer function */ + +typedef char * (*OP_PRINT) (uint32 byte_address, uint32 byte_length); + + +/* CIS local utility routine declarations */ + +static void branch_external (HP_WORD segment, HP_WORD offset); +static uint32 strip_overpunch (uint8 *byte, NUMERIC_SIGN *sign); +static uint32 convert (HP_WORD sba, HP_WORD tba, HP_WORD count, DISPLAY_MODE mode); +static t_bool edit (t_stat *status, uint32 *trap); +static t_bool compare (BYTE_ACCESS *source, BYTE_ACCESS *target, BYTE_ACCESS *table, t_stat *status); +static void fprint_operands (BYTE_ACCESS *source, BYTE_ACCESS *target, uint32 trap); +static void fprint_translated_operands (BYTE_ACCESS *source, BYTE_ACCESS *target, BYTE_ACCESS *table); +static void fprint_operand (BYTE_ACCESS *op, char *label, OP_PRINT operand_printer); + + + +/* CIS global routines */ + + +/* Execute a CIS operation. + + This routine is called to execute the COBOL II instruction currently in the + CIR. The instruction format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 0 0 1 0 | 0 0 0 1 | 0 0 1 1 | CIS opcode | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + Instructions 020460 - 020476 are decoded directly. Code 020477 introduces a + set of two-word instructions; the second word decodes the operation. Codes + 020464 through 020467 and code 020477 with the second word less than 000006 + or greater than 000051 are unimplemented. + + Entry is with four TOS registers preloaded (this is done for all firmware + extension instructions). Therefore, no SR preload is needed here. + Instructions that provide option bits to leave addresses on the stack do not + modify those addresses during instruction execution. + + If an invalid digit or digit count is detected, a microcode abort occurs. If + the T (trap) bit is not set in the status register, the O (overflow) bit is + set and execution continues. If the T bit is set, the O bit is not set and + the trap is taken. In either case, the stack is popped according to the + instruction. + + + Implementation notes: + + 1. If a two-word instruction has a valid first word (020477) but an invalid + second word, the CIR must retain the first-word value and P must be + incremented over the second word when the Unimplemented Instruction trap + is taken. + + 2. The MICRO_ABORT macro does a longjmp to the microcode abort handler in + the sim_instr routine. If the user trap bit (T-bit) in the status word + is set, the routine clears the overflow bit (O-bit) and invokes the trap + handler. If the T-bit is clear, the routine sets the O-bit and continues + with the next instruction. Therefore, we do not need to check the T-bit + here and can simply do an unconditional MICRO_ABORT if a trap is + indicated. + + 3. Regarding error traps, the "Instruction Commentary" for the Language + Extension Instructions section of the Machine Instruction Set manual + says, in part: + + If the User Traps bit (STA.2) is not set, the Overflow bit (STA.4) is + set, the stack is popped in accordance with the instruction, and + execution continues with the following instruction. If the Traps bit + is set, the Overflow bit is not set, the stack is not popped, and a + trap to the Traps segment, segment 1, is taken. + + However, the Series 6x microcode for, e.g., the ALGN instruction does a + "JSZ TRPR" after unconditionally popping the stack (page 334). TRPR + checks STA.2 and either starts trap processing if it is set or sets + Overflow and proceeds to the next instruction if it is clear. So the + stack is popped in either case. This implementation follows this + behavior. + + 4. In the comments for the ABSD/NEGD instructions, the Series 6x microcode + manual says, "Extract the last two bits of the sign..." and then "If the + sign bits = 01 (the sign was negative => D), then...", implying that only + the lower two bits are tested. The microcode itself, however, tests all + four bits and assumes a negative value only if they equal 1101. This + behavior agrees with the EIS commentary in the Machine Instruction Set + manual, which says that all sign values other than those defined as plus, + minus, and unsigned, are treated as plus. This implementation follows + this behavior. +*/ + +t_stat cpu_cis_op (void) +{ +BYTE_ACCESS source, target, table; +NUMERIC_SIGN sign; +HP_WORD segment, offset, sign_cntl; +HP_WORD source_rba, table_rba; +char label [64]; +uint8 byte; +uint8 source_length, source_lead, source_fraction; +uint8 target_length, target_lead, target_fraction; +uint32 opcode; +t_bool store, zero_fill; +uint32 trap = trap_None; +t_stat status = SCPE_OK; + +opcode = FMEXSUBOP (CIR); /* get the opcode from the instruction */ + +switch (opcode) { /* dispatch the opcode */ + + case 000: /* ALGN (O; INV DIG, WC OVF, STOV, STUN, BNDV) */ + case 001: + source_length = LOWER_BYTE (RA); /* get the source digit count */ + source_fraction = UPPER_BYTE (RA); /* and the source fraction count */ + + target_length = LOWER_BYTE (RC); /* get the target digit count */ + target_fraction = UPPER_BYTE (RC); /* and the target fraction count */ + + if (source_fraction > source_length /* if the source fraction count is invalid */ + || source_length > MAX_DIGITS /* or the source digit count is too large */ + || target_fraction > target_length /* or the target fraction count is invalid */ + || target_length > MAX_DIGITS) /* or the target digit count is too large */ + trap = trap_Word_Count_Overflow; /* then trap for a count overflow */ + + else if (source_length > 0 && target_length > 0) { /* otherwise if there is an alignment to do */ + sign = Unsigned; /* then assume the source is unsigned */ + store = TRUE; /* enable storing */ + zero_fill = TRUE; /* and zero fill of the target */ + + source_lead = source_length - source_fraction; /* get the counts of the leading digits */ + target_lead = target_length - target_fraction; /* of the source and target numbers */ + + mem_init_byte (&source, data_checked, &RB, source_length); /* set up byte accessors */ + mem_init_byte (&target, data_checked, &RD, target_length); /* for the source and target strings */ + + while (source_length > 0 || target_length > 0) { /* while digits remain to be aligned */ + if (source_lead < target_lead /* if the target has more leading digits */ + || source_length == 0) /* or there are no more source digits */ + byte = '0'; /* then transfer a zero */ + + else { /* otherwise */ + store = (source_lead == target_lead); /* don't store if still more leading source digits */ + + byte = mem_read_byte (&source); /* get a source digit */ + source_length = source_length - 1; /* and count it */ + + if (source_length == 0) { /* if this is the last source digit */ + trap = strip_overpunch (&byte, &sign); /* then strip the sign from the digit */ + + if (trap != trap_None) /* if the overpunch was invalid */ + break; /* then abandon the transfer */ + } + + if (source_lead > 0) /* if this is a leading digit */ + source_lead = source_lead - 1; /* then count it */ + + if (byte >= '0' && byte <= '9') /* if it is numeric */ + zero_fill = FALSE; /* then turn zero-filling off */ + + else if (byte == ' ' && zero_fill) /* otherwise if it's a blank and zero-filling is on */ + byte = '0'; /* then fill */ + + else { /* otherwise */ + trap = trap_Invalid_ASCII_Digit; /* the digit is invalid */ + break; /* so abandon the transfer */ + } + } + + if (store && target_length > 0) { /* if storing and target space is available */ + if (target_length == 1) /* then if this is the last byte to store */ + byte = overpunch [sign] [byte - '0']; /* then overpunch with the source sign */ + + mem_write_byte (&target, byte); /* store the target digit */ + target_length = target_length - 1; /* and count it */ + + if (target_lead > 0) /* if this is a leading digit */ + target_lead = target_lead - 1; /* then count it */ + } + } /* continue the alignment loop */ + + + mem_update_byte (&target); /* update the final target byte */ + + if (DPRINTING (cpu_dev, DEB_MOPND)) { + sprintf (label, "source fraction %d length", source_fraction); + fprint_operand (&source, label, &fmt_byte_operand); + + if (trap == trap_None) { + sprintf (label, "target fraction %d length", target_fraction); + fprint_operand (&target, label, &fmt_byte_operand); + } + } + } + + if (CIR & CIS_SDEC_MASK) /* if the S-decrement bit is set in the instruction */ + SR = 0; /* then pop all four operands */ + + else { /* otherwise */ + cpu_pop (); /* pop all */ + cpu_pop (); /* of the operands */ + cpu_pop (); /* except the target address */ + } + + if (trap == trap_None) /* if the instruction succeeded */ + STA &= ~STATUS_O; /* then clear overflow status */ + else /* otherwise */ + MICRO_ABORT (trap); /* abort with the indicated trap */ + break; + + + case 002: /* ABSN (CCA, O; INV DIG, WC OVF, STOV, STUN, BNDV) */ + case 003: + if (RA > MAX_DIGITS) /* if the digit count is too large */ + trap = trap_Word_Count_Overflow; /* then trap for a count overflow */ + + else if (RA > 0) { /* otherwise if there are digits to process */ + source_rba = RB; /* then use a working source byte address pointer */ + mem_init_byte (&source, data_checked, /* and set up a byte accessor for the string */ + &source_rba, RA); + + if (DPRINTING (cpu_dev, DEB_MOPND)) + fprint_operand (&source, "source", &fmt_byte_operand); + + byte = mem_read_byte (&source); /* get the first source digit */ + + while (byte == ' ' && RA > 0) { /* while there are leading blanks */ + mem_modify_byte (&source, '0'); /* replace each blank with a zero digit */ + RA = RA - 1; /* and count it */ + + if (RA > 0) /* if there are more digits */ + byte = mem_read_byte (&source); /* then get the next one */ + } + + while (RA > 1 && byte >= '0' && byte <= '9') { /* validate the digits */ + byte = mem_read_byte (&source); /* by getting and checking */ + RA = RA - 1; /* each digit until the last */ + } + + if (RA == 1) { /* if this is the last digit */ + trap = strip_overpunch (&byte, &sign); /* then strip the sign from the digit */ + + if (trap == trap_None) { /* if the overpunch was valid */ + if (sign == Negative) /* then if the number was negative */ + SET_CCL; /* then set the less-than condition code */ + else /* otherwise a positive or zero number */ + SET_CCG; /* sets the greater-than condition code */ + + mem_modify_byte (&source, byte); /* rewrite the digit without the sign overpunch */ + } + } + + else if (RA > 0) /* otherwise if we've abandoned the validation */ + trap = trap_Invalid_ASCII_Digit; /* then trap for an invalid digit */ + + mem_post_byte (&source); /* post the last byte written */ + + if (DPRINTING (cpu_dev, DEB_MOPND) && trap == trap_None) + fprint_operand (&source, "target", &fmt_byte_operand); + } + + cpu_pop (); /* pop the digit count */ + + if (CIR & CIS_SDEC_MASK) /* if the S-decrement bit is set in the instruction */ + cpu_pop (); /* then pop the address as well */ + + if (trap == trap_None) /* if the instruction succeeded */ + STA &= ~STATUS_O; /* then clear overflow status */ + else /* otherwise */ + MICRO_ABORT (trap); /* abort with the indicated trap */ + break; + + + case 010: /* EDIT (O; INV DIG, WC OVF, STUN, BNDV) */ + case 011: + if (edit (&status, &trap)) { /* process the edit subprogram; if no interrupt intervened */ + SR = 0; /* then pop all four values from the stack */ + + if (trap != trap_None) /* if a trap occurred */ + MICRO_ABORT (trap); /* then take it now */ + } + break; + + + case 012: /* CMPS (CCx; STOV, STUN, BNDV) */ + case 013: + mem_init_byte (&source, data_checked, &RB, RA); /* set up a byte accessor for the first operand */ + + if (CIR & CIS_DB_FLAG) /* if the second operand is in the data segment */ + mem_init_byte (&target, data_checked, &RD, RC); /* then set up a DB byte accessor */ + else /* otherwise */ + mem_init_byte (&target, program_checked, &RD, RC); /* set up a PB byte accessor */ + + if (compare (&source, &target, NULL, &status)) { /* compare the strings; if no interrupt intervened */ + SR = 0; /* then pop all four values from the stack */ + + if (DPRINTING (cpu_dev, DEB_MOPND)) + fprint_operands (&source, &target, trap); + } + break; + + + case 014: /* XBR (none; STUN, BNDV, CSTV, MODE, ABS CST, TRACE) */ + segment = RA; /* get the segment number */ + offset = RB; /* and PB-relative offset of the target */ + + cpu_pop (); /* pop the operands */ + cpu_pop (); + + branch_external (segment, offset); /* branch to the target location */ + break; + + + case 015: /* PARC (none; STOV, STUN, BNDV, CSTV, MODE, ABS CST, TRACE) */ + segment = RB; /* get the segment number */ + offset = RC; /* and the PB-relative offset of the target */ + + RB = STA; /* replace the segment number with the current status */ + RC = P - 1 - PB & R_MASK; /* and the target with the PB-relative return address */ + + branch_external (segment, offset); /* branch to the target location */ + break; + + + case 016: /* ENDP (none; STUN, BNDV, CSTV, MODE, ABS CST, TRACE) */ + if (RA == RB) { /* if the paragraph numbers are equal */ + SR = 0; /* then pop all of the parameters */ + branch_external (RC, RD); /* and return to the caller */ + } + + else /* otherwise the paragraph numbers are unequal */ + cpu_pop (); /* so pop the current paragraph number and continue */ + break; + + + case 017: /* double-word instructions */ + opcode = NIR; /* get the operation code from the second word */ + + cpu_read_memory (fetch, P, &NIR); /* load the next instruction */ + P = P + 1 & R_MASK; /* and point to the following instruction */ + + switch (opcode) { /* dispatch the second instruction word */ + + case 006: /* CMPT (CCx; STOV, STUN, BNDV) */ + case 007: + cpu_read_memory (stack, SM, &table_rba); /* get the byte address of the translation table */ + + mem_init_byte (&source, data_checked, &RB, RA); /* set up a byte accessor for the first operand */ + + if (opcode & CIS_DB_FLAG) /* if the second operand is in the data segment */ + mem_init_byte (&target, data_checked, &RD, RC); /* then set up a DB byte accessor */ + else /* otherwise */ + mem_init_byte (&target, program_checked, &RD, RC); /* set up a PB byte accessor */ + + mem_init_byte (&table, data, &table_rba, 0); /* set up a byte accessor for the translation table */ + + if (compare (&source, &target, &table, &status)) { /* compare the strings; if no interrupt intervened */ + SR = 0; /* then pop the first four parameters */ + cpu_pop (); /* and then the fifth parameter */ + + if (DPRINTING (cpu_dev, DEB_MOPND)) + fprint_translated_operands (&source, &target, &table); + } + break; + + + case 010: /* TCCS (Test condition code and set, bits 13-15 options) */ + case 011: + case 012: + case 013: + case 014: + case 015: + case 016: + case 017: + cpu_push (); /* push the stack down */ + + if ((STA & STATUS_CC_MASK) != STATUS_CCI /* if the condition code is not invalid */ + && TO_CCF (STA) & opcode << TCCS_CCF_SHIFT) /* and the test succeeds */ + RA = D16_UMAX; /* then set the TOS to TRUE */ + else /* otherwise */ + RA = 0; /* set the TOS to FALSE */ + break; + + + case 020: /* CVND (O; INV DIG, WC OVF, STOV, STUN, BNDV) */ + case 021: + case 022: + case 023: + case 024: + case 025: + case 026: + case 027: + case 030: + case 031: + case 032: + case 033: + case 034: + case 035: + case 036: + case 037: + if (RA > MAX_DIGITS) /* if the digit count is too large */ + trap = trap_Word_Count_Overflow; /* then trap for a count overflow */ + + else if (RA > 0) { /* otherwise if there are digits to convert */ + sign_cntl = (opcode & CVND_SC_MASK) /* then get the sign control code */ + >> CVND_SC_SHIFT; + + if (sign_cntl > Absolute) /* if the code is one of the unnormalized values */ + sign_cntl = Absolute; /* then normalize it */ + + trap = convert (RB, RC, RA, sign_cntl); /* convert the number as directed */ + } + + cpu_pop (); /* pop the source character count */ + cpu_pop (); /* and source byte address */ + + if (opcode & CIS_SDEC_MASK) /* if the S-decrement bit is set in the instruction */ + cpu_pop (); /* then pop the target byte address also */ + + if (trap == trap_None) /* if the instruction succeeded */ + STA &= ~STATUS_O; /* then clear overflow status */ + else /* otherwise */ + MICRO_ABORT (trap); /* abort with the indicated trap */ + break; + + + case 040: /* LDW (none; STOV, STUN, BNDV) */ + case 041: + source_rba = RA; /* use a working source byte address pointer */ + + if ((opcode & CIS_SDEC_MASK) == 0) /* if the S-decrement bit is clear */ + cpu_push (); /* then push the stack down for the return value */ + + mem_init_byte (&source, data_checked, &source_rba, 2); /* set up a byte accessor for the characters */ + + byte = mem_read_byte (&source); /* get the first byte */ + RA = TO_WORD (byte, mem_read_byte (&source)); /* and the second byte and merge them */ + + if (DPRINTING (cpu_dev, DEB_MOPND)) + fprint_operand (&source, "source", &fmt_byte_operand); + break; + + + case 042: /* LDDW (none; STOV, STUN, BNDV) */ + case 043: + source_rba = RA; /* use a working source byte address pointer */ + + if ((opcode & CIS_SDEC_MASK) == 0) /* if the S-decrement bit is clear */ + cpu_push (); /* then push the stack down for the return value */ + + cpu_push (); /* push again for the two-word return value */ + + mem_init_byte (&source, data_checked, &source_rba, 4); /* set up a byte accessor for the characters */ + + byte = mem_read_byte (&source); /* get the first byte */ + RA = TO_WORD (byte, mem_read_byte (&source)); /* and the second byte and merge them */ + + byte = mem_read_byte (&source); /* get the third byte */ + RB = TO_WORD (byte, mem_read_byte (&source)); /* and the fourth byte and merge them */ + + if (DPRINTING (cpu_dev, DEB_MOPND)) + fprint_operand (&source, "source", &fmt_byte_operand); + break; + + + case 044: /* TR (none; STUN, STOV, BNDV) */ + case 045: + if (RA > 0) { /* if there are bytes to translate */ + mem_init_byte (&source, data_checked, &RC, RA); /* then set up byte accessors */ + mem_init_byte (&target, data_checked, &RB, RA); /* for the source and target strings */ + + if (opcode & CIS_DB_FLAG) /* if the table is in the data segment */ + mem_init_byte (&table, data, &RD, 0); /* then set up a DB byte accessor */ + else /* otherwise */ + mem_init_byte (&table, program, &RD, 0); /* set up a PB byte accessor */ + + while (RA > 0) { /* while there are bytes to translate */ + byte = mem_read_byte (&source); /* get the next byte */ + byte = mem_lookup_byte (&table, byte); /* look up the translated value */ + mem_write_byte (&target, byte); /* and write it to the target */ + + RA = RA - 1; /* update the byte count (cannot underflow) */ + + if (cpu_interrupt_pending (&status)) { /* if an interrupt is pending */ + mem_update_byte (&target); /* then update the last word written */ + return status; /* and return with an interrupt set up or an error */ + } + } + + mem_update_byte (&target); /* update the final target byte */ + + if (DPRINTING (cpu_dev, DEB_MOPND)) + fprint_operands (&source, &target, trap); + } + + SR = 0; /* pop all four values from the stack */ + break; + + + case 046: /* ABSD (CCA, O; WC OVF, STOV, STUN, BNDV) */ + case 047: + case 050: /* NEGD (CCA, O; WC OVF, STOV, STUN, BNDV) */ + case 051: + if (RA > MAX_DIGITS) /* if the digit count is too large */ + trap = trap_Word_Count_Overflow; /* then trap for a count overflow */ + + else { /* otherwise */ + mem_init_byte (&source, data, &RB, RA); /* set up a byte accessor for the operand */ + + source_rba = RA / 2 + RB; /* index to the trailing sign digit */ + mem_init_byte (&target, data, &source_rba, 0); /* and set up a byte accessor for the sign */ + + if (DPRINTING (cpu_dev, DEB_MOPND)) + fprint_operand (&source, "source", &fmt_bcd_operand); + + byte = mem_read_byte (&target); /* get the sign byte */ + + if (opcode < 050) { /* if this is an ABSD instruction */ + if (IS_NEG (byte)) /* then if the number is negative */ + SET_CCL; /* then set the less-than condition code */ + else /* otherwise a positive or zero number */ + SET_CCG; /* sets the greater-than condition code */ + + byte |= SIGN_UNSIGNED; /* change the number to unsigned */ + } + + else /* otherwise this is a NEGD instruction */ + if (IS_NEG (byte)) { /* so if the number is negative */ + byte = byte & SIGN_MASK | SIGN_PLUS; /* then make the number positive */ + SET_CCG; /* and set the greater-than condition code */ + } + + else { /* otherwise the number is positive */ + byte = byte & SIGN_MASK | SIGN_MINUS; /* so make it negative */ + SET_CCL; /* and set the less-than condition code */ + } + + mem_modify_byte (&target, byte); /* rewrite the digit */ + mem_post_byte (&target); /* and post it */ + + if (DPRINTING (cpu_dev, DEB_MOPND)) + fprint_operand (&source, "target", &fmt_bcd_operand); + } + + cpu_pop (); /* pop the digit count */ + + if (opcode & CIS_SDEC_MASK) /* if the S-decrement bit is set in the instruction */ + cpu_pop (); /* then pop the address as well */ + + if (trap == trap_None) /* if the instruction succeeded */ + STA &= ~STATUS_O; /* then clear overflow status */ + else /* otherwise */ + MICRO_ABORT (trap); /* abort with the indicated trap */ + break; + + + default: + status = STOP_UNIMPL; /* the second instruction word is unimplemented */ + } + break; /* end of the double-word instructions */ + + + default: + status = STOP_UNIMPL; /* the firmware extension instruction is unimplemented */ + } + +return status; /* return the execution status */ +} + + + +/* CIS local utility routine declarations */ + + +/* Execute a branch to a location in a specified segment. + + If the target segment is the same as the current segment, as indicated in the + status register, then a local label is used. Otherwise, an external label is + used that specifies the target segment entry 0 of the STT, which specifies + the start of the segment. The target is then set up in the same manner as a + procedure call, with the program counter adjusted by the target offset. + + The procedure setup may abort, rather than returning, if a trap prevents the + setup from succeeding. +*/ + +static void branch_external (HP_WORD segment, HP_WORD offset) +{ +HP_WORD label; + +if (STATUS_CS (segment) == STATUS_CS (STA)) /* if the target segment is current */ + label = LABEL_LOCAL; /* then use a local label */ +else /* otherwise */ + label = LABEL_EXTERNAL /* use an external label */ + | TO_LABEL (STATUS_CS (segment), 0); /* that specifies the target segment and STT 0 */ + +cpu_call_procedure (label, offset); /* set up the segment as for a procedure call */ + +return; /* return with the branch completed */ +} + + +/* Strip the sign from an overpunched digit. + + If the supplied digit includes an overpunched sign, it is stripped and + returned separately, along with the stripped digit; trap_None is returned to + indicate success. If the digit is not a valid overpunch character, then + trap_Invalid_ASCII_Digit is returned to indicate failure. + + + Implementation notes: + + 1. A set of direct tests is faster than looping through the overpunch table + to search for the matching digit. Faster still would be a direct 256-way + reverse overpunch lookup, but the gain is not significant. +*/ + +static uint32 strip_overpunch (uint8 *byte, NUMERIC_SIGN *sign) +{ +if (*byte == '{') { /* if the digit is a zero with positive overpunch */ + *byte = '0'; /* then strip the overpunch */ + *sign = Positive; /* and set the returned sign positive */ + } + +else if (*byte >= 'A' && *byte <= 'I') { /* otherwise if the digit is a positive overpunch */ + *byte = *byte - 'A' + '1'; /* then strip the overpunch to yield a 1-9 value */ + *sign = Positive; /* and set the returned sign positive */ + } + +else if (*byte == '}') { /* otherwise if the digit is a zero with negative overpunch */ + *byte = '0'; /* then strip the overpunch */ + *sign = Negative; /* and set the returned sign negative */ + } + +else if (*byte >= 'J' && *byte <= 'R') { /* otherwise if the digit is a negative overpunch */ + *byte = *byte - 'J' + '1'; /* then strip the overpunch to yield a 1-9 value */ + *sign = Negative; /* and set the returned sign negative */ + } + +else if (*byte >= '0' && *byte <= '9') /* otherwise if the digit is not overpunched */ + *sign = Unsigned; /* then simply set the return as unsigned */ + +else /* otherwise the digit is not a valid overpunch */ + return trap_Invalid_ASCII_Digit; /* so return an Invalid Digit trap as failure */ + +return trap_None; /* return no trap for successful stripping */ +} + + +/* Convert a numeric display string. + + This routine converts a numeric display string to an external decimal number. + A display string consists of the ASCII characters "0" to "9" and optional + leading spaces. The sign may be omitted (unsigned), a separate leading or + trailing "+" or "-" sign, or an integral leading or trailing overpunch. The + result is an external decimal number, consisting of "0" to "9" digits with a + trailing sign overpunch. The routine implements the CVND instruction. + + The "sba" parameter is the DB-relative byte address of the source string, + "tba" is the DB-relative byte address of the target string, "count" is the + number of source characters (including the sign character, if separate), and + "mode" is the sign display mode of the source (leading separate, trailing + separate, leading overpunch, trailing overpunch, or unsigned). The count is + always non-zero on entry. + + The routine validates all of the source characters, even if no conversion is + needed, and returns trap_Invalid_ASCII_Digit if validation fails. trap_None + is returned if the conversion succeeds. + + The implementation starts by setting indices for the location of the sign. + Separate indices for separate and overpunched signs are kept, with the unused + index set to a value beyond the string so that it never matches. If the + input has separate sign mode and only one character, then that character is + the sign, and a zero value is implied. Leading blanks are zero-filled until + the first numeric digit (if the mode is leading overpunch, then the first + digit is numeric, so leading blanks are not permitted). + + + Implementation notes: + + 1. The "last_digit" variable is only used in Trailing_Separate mode when + there is at least one character other than the sign. Consequently, it + will never be used before it is set. However, the compiler cannot detect + this and will warn erroneously that it could be used uninitialized. + Therefore, it is set (redundantly) before the conversion loop entry. +*/ + +static uint32 convert (HP_WORD sba, HP_WORD tba, HP_WORD count, DISPLAY_MODE mode) +{ +BYTE_ACCESS source, target; +NUMERIC_SIGN sign; +HP_WORD separate_index, overpunch_index; +uint8 byte, last_digit; +uint32 trap = trap_None; +t_bool zero_fill = TRUE; +t_bool bare_sign = FALSE; + +switch (mode) { /* set up the sign flag and indices */ + + case Leading_Separate: + separate_index = count; /* the first character is the separate sign */ + overpunch_index = MAX_DIGITS + 1; /* and no character is the overpunched sign */ + + bare_sign = (count == 1); /* only one character implies a bare sign */ + break; + + case Trailing_Separate: + separate_index = 1; /* the last character is the separate sign */ + overpunch_index = MAX_DIGITS + 1; /* and no character is the overpunched sign */ + + bare_sign = (count == 1); /* only one character implies a bare sign */ + break; + + case Leading_Overpunch: + overpunch_index = count; /* the first character is the overpunched sign */ + separate_index = MAX_DIGITS + 1; /* and no character is the separate sign */ + break; + + case Trailing_Overpunch: + overpunch_index = 1; /* the last character is the overpunched sign */ + separate_index = MAX_DIGITS + 1; /* and no character is the separate sign */ + break; + + case Absolute: + default: /* quiet "potentially uninitialized local variable" warnings */ + separate_index = MAX_DIGITS + 1; /* no character is the overpunched sign */ + overpunch_index = MAX_DIGITS + 1; /* and no character is the separate sign */ + break; + } + +mem_init_byte (&source, data_checked, &sba, count); /* set up byte accessors */ +mem_init_byte (&target, data_checked, &tba, count); /* for the source and target strings */ + +sign = Unsigned; /* assume that the source is unsigned */ +byte = 0; /* shut up incorrect "may be used uninitialized" warning */ + +while (count > 0) { /* while there are characters to convert */ + last_digit = byte; /* then save any previous character */ + byte = mem_read_byte (&source); /* and get the next source character */ + + if (count == separate_index) { /* If this is the separate sign character */ + if (byte == '+' || byte == ' ') /* then a plus or blank */ + sign = Positive; /* indicates a positive number */ + + else if (byte == '-') /* otherwise a minus */ + sign = Negative; /* indicates a negative number */ + + else { /* otherwise */ + trap = trap_Invalid_ASCII_Digit; /* the character is not a valid sign */ + break; /* so abandon the conversion */ + } + + if (bare_sign) /* if this is the only character */ + byte = '0'; /* then supply a zero for overpunching */ + + else { /* otherwise */ + if (mode == Trailing_Separate) { /* if this is the trailing sign */ + byte = overpunch [sign] [last_digit - '0']; /* then overpunch the last numeric digit with the sign */ + mem_modify_byte (&target, byte); /* and update it */ + } + + count = count - 1; /* count the separate sign character */ + continue; /* and continue with the next character */ + } + } + + else if (count == overpunch_index && byte != ' ') { /* otherwise if this is the non-blank overpunched sign */ + trap = strip_overpunch (&byte, &sign); /* then strip the overpunch and set the sign */ + + if (trap == trap_None) /* if the overpunch was valid */ + zero_fill = FALSE; /* then turn zero-filling off */ + else /* otherwise the overpunch was not valid */ + break; /* so abandon the conversion */ + } + + if (byte >= '0' && byte <= '9') /* if the character is numeric */ + zero_fill = FALSE; /* then turn zero-filling off */ + + else if (byte == ' ' && zero_fill) /* otherwise if it's a blank and zero-filling is on */ + byte = '0'; /* then fill */ + + else { /* otherwise */ + trap = trap_Invalid_ASCII_Digit; /* the digit is invalid */ + break; /* so abandon the conversion */ + } + + if (count == 1 && mode != Absolute) /* if this is the last character and the value is signed */ + byte = overpunch [sign] [byte - '0']; /* then overpunch with the sign */ + + mem_write_byte (&target, byte); /* store it in the target string */ + + count = count - 1; /* count the character */ + } /* and continue */ + +mem_update_byte (&target); /* update the final target byte */ + +if (DPRINTING (cpu_dev, DEB_MOPND)) + fprint_operands (&source, &target, trap); + +return trap; /* return the trap condition */ +} + + +/* Edit a number into a formatted picture. + + This routine moves an external decimal number to a target buffer under the + control of an editing subprogram. The subprogram consists of one or more + editing operations. The routine is interruptible between operations. + + On entry, the TOS registers and the condition code in the STA register are + set as follows: + + RA = 0 on initial entry, or 177777 on reentry after an interrupt + RB = the source byte address (DB-relative) + RC = the target byte address (DB-relative) + RD = the subprogram byte address (PB or DB-relative) + CC = the sign of the source number + + On return, the "status" parameter is set to the SCP status returned by the + interrupt test, and "trap" is set to trap_Invalid_ASCII_Digit if an operation + encountered an invalid digit, or trap_None if the edit succeeded. The + routine returns TRUE if the operation ran to completion, or FALSE if the + operation was interrupted and should be resumed. + + If an interrupt is detected between operations, two words are pushed onto the + stack before the interrupt handler is called. These words hold the current + significance trigger (a flag indicating whether a significant digit has been + seen or zero filling should occur), loop count, float character, and fill + character, in this format: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | TOS + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | S | 0 0 0 0 0 0 0 | loop count | TOS - 1 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + | fill character | float character | TOS - 2 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + + Where: + + S = significance trigger (0/1) + + The loop count/significance trigger word is written over the zero that + resides at the TOS on instruction entry, and then the fill/float character + word and a word of all ones are pushed. The source, target, and subprogram + byte addresses are updated during execution, so they retain their modified + values on reentry. When reentry is detected, the fill, float, and count + values are reestablished, and two words are popped off the stack to restore + the initial entry conditions. + + If operand tracing is enabled, each subprogram operation is printed in this + format: + + >>CPU opnd: 00.136024 000000 IS 6,"AAAAAA","BBBBBB" + ~~ ~~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ + | | | | + | | | +-- subprogram operation and operands (if any) + | | +------------ octal loop counter value + | +-------------------- octal subprogram address (effective address) + +------------------------- octal bank (PBANK, DBANK, or SBANK) + + ...and source and target operands are printed in this format: + + >>CPU opnd: 00.045177 000467 source 3,"ABC" + ~~ ~~~~~~ ~~~~~~ ~~~~~~~~~~~~~~ + | | | | + | | | +-- byte operand label, length, and value + | | +------------ octal relative byte offset from base register + | +-------------------- octal address (effective address) + +------------------------- octal bank (PBANK, DBANK, or SBANK) + + + Implementation notes: + + 1. The Machine Instruction Set manual says that the DBNZ ("decrement loop + count and branch") operation adds the displacement operand. However, + the microcode manual shows that the displacement is actually subtracted. + The simulator follows the microcode implementation. + + 2. The BRIS ("branch if significance") operation adds the displacement to + the address of the displacement byte. After reading the displacement + byte, the address is incremented to the next location, so the operation + subtracts 1 before adding the displacement value. +*/ + +static t_bool edit (t_stat *status, uint32 *trap) +{ +BYTE_ACCESS source, target, prog; +ACCESS_CLASS class; +HP_WORD bank; +char fill_char, float_char; +uint8 byte, opcode, operand, count; +uint32 loop_count; +t_bool filling = TRUE; /* TRUE if zero-filling is enabled */ +t_bool terminate = FALSE; /* TRUE if the operation loop is ending */ + +*status = SCPE_OK; /* initialize the return status */ +*trap = trap_None; /* and trap condition */ + +if (RA != 0) { /* if this is a reentry after an interrupt */ + filling = ((RB & D16_SIGN) != 0); /* then reset the zero-filling flag */ + loop_count = LOWER_BYTE (RB); /* and the loop counter */ + fill_char = UPPER_BYTE (RC); /* reset the fill */ + float_char = LOWER_BYTE (RC); /* and float characters */ + + cpu_pop (); /* pop the extra words */ + cpu_pop (); /* added to save the context */ + } + +else { /* otherwise this is an initial entry */ + filling = TRUE; /* so set the zero-filling flag */ + loop_count = 0; /* and clear the loop counter */ + fill_char = ' '; /* set the fill */ + float_char = '$'; /* and float character defaults */ + } + +RA = D16_UMAX; /* set the in-process flag in case of microcode abort */ +STA &= ~STATUS_O; /* clear overflow status */ + +if (CIR & CIS_DB_FLAG) { /* if the subprogram is in the data segment */ + class = data; /* then set up for data reads */ + bank = DBANK; /* and use DBANK for traces */ + } + +else { /* otherwise it's in the program segment */ + class = program; /* so set up for program reads */ + bank = PBANK; /* and use PBANK for traces */ + } + +mem_init_byte (&source, data, &RB, 0); /* set up byte accessors */ +mem_init_byte (&target, data, &RC, 0); /* for the source and target strings */ +mem_init_byte (&prog, class, &RD, 0); /* and the subprogram */ + +do { /* process operations while "terminate" is FALSE */ + operand = mem_read_byte (&prog); /* get the next operation */ + + if (DPRINTING (cpu_dev, DEB_MOPND)) { /* if operand tracing is enabled */ + hp_debug (&cpu_dev, DEB_MOPND, BOV_FORMAT " ", /* then print the current subprogram address */ + bank, prog.word_address, loop_count); /* and loop count as octal values */ + + fprint_edit (sim_deb, NULL, 0, /* print the operation mnemonic */ + prog.initial_byte_address /* at the current physical byte address */ + + prog.count - 1); + + fputc ('\n', sim_deb); /* end the trace with a newline */ + } + + opcode = UPPER_HALF (operand); /* split the opcode */ + operand = LOWER_HALF (operand); /* from the immediate operand */ + + if (operand == 0 && opcode < 014) /* if this is an extended operand */ + operand = mem_read_byte (&prog); /* then read the full value from the next byte */ + + + switch (opcode) { /* dispatch on the opcode */ + + case 000: /* MC - move characters */ + while (operand > 0) { /* while there are characters to move */ + byte = mem_read_byte (&source); /* get the next byte */ + + mem_write_byte (&target, byte); /* move it to the target */ + operand = operand - 1; /* and count it */ + } + break; + + + case 001: /* MA - move alphabetics */ + while (operand > 0) { /* while there are characters to move */ + byte = mem_read_byte (&source); /* get the next byte */ + + if (byte >= 'A' && byte <= 'Z' /* if the character is an uppercase letter */ + || byte >= 'a' && byte <= 'z' /* or a lowercase letter */ + || byte == ' ') { /* or a space */ + mem_write_byte (&target, byte); /* then move it to the target */ + operand = operand - 1; /* and count it */ + } + + else { /* otherwise */ + *trap = trap_Invalid_ASCII_Digit; /* the character is not a valid alphabetic */ + terminate = TRUE; /* so abandon the subprogram */ + break; /* and the move */ + } + } + break; + + + case 002: /* MN - move numerics */ + while (operand > 0) { /* while there are characters to move */ + byte = mem_read_byte (&source); /* get the next byte */ + + if (byte == ' ' && filling) /* if it's a blank and zero-filling is on */ + byte = '0'; /* then fill */ + + else if (byte >= '0' && byte <= '9') /* otherwise if the character is a digit */ + filling = FALSE; /* then turn zero-filling off */ + + else { /* otherwise */ + *trap = trap_Invalid_ASCII_Digit; /* the character is not a valid numeric */ + terminate = TRUE; /* so abandon the subprogram */ + break; /* and the move */ + } + + mem_write_byte (&target, byte); /* move the character to the target */ + operand = operand - 1; /* and count it */ + } + break; + + + case 003: /* MNS - move numerics suppressed */ + while (operand > 0) { /* while there are characters to move */ + byte = mem_read_byte (&source); /* get the next byte */ + + if (filling /* if zero-filling is on */ + && (byte == ' ' || byte == '0')) /* and it's a blank or zero */ + byte = fill_char; /* then substitute the fill character */ + + else if (byte >= '0' && byte <= '9') /* otherwise if the character is a digit */ + filling = FALSE; /* then turn zero-filling off */ + + else { /* otherwise */ + *trap = trap_Invalid_ASCII_Digit; /* the character is not a valid numeric */ + terminate = TRUE; /* so abandon the subprogram */ + break; /* and the move */ + } + + mem_write_byte (&target, byte); /* move the character to the target */ + operand = operand - 1; /* and count it */ + } + break; + + + case 004: /* MFL - move numerics with floating insertion */ + while (operand > 0) { /* while there are characters to move */ + byte = mem_read_byte (&source); /* get the next byte */ + + if (filling /* if zero-filling is on */ + && (byte == ' ' || byte == '0')) /* and it's a blank or zero */ + byte = fill_char; /* then substitute the fill character */ + + else if (byte >= '0' && byte <= '9') { /* otherwise if the character is a digit */ + if (filling) { /* then if zero-filling is still on */ + filling = FALSE; /* then turn it off */ + + mem_write_byte (&target, float_char); /* insert the float character before the digit */ + } + } + + else { /* otherwise */ + *trap = trap_Invalid_ASCII_Digit; /* the character is not a valid numeric */ + terminate = TRUE; /* so abandon the subprogram */ + break; /* and the move */ + } + + mem_write_byte (&target, byte); /* move the character to the target */ + operand = operand - 1; /* and count it */ + } + break; + + + case 005: /* IC - insert character */ + byte = mem_read_byte (&prog); /* get the insertion character */ + + while (operand > 0) { /* while there are characters to insert */ + mem_write_byte (&target, byte); /* copy the character to the target */ + operand = operand - 1; /* and count it */ + } + break; + + + case 006: /* ICS - insert character suppressed */ + byte = mem_read_byte (&prog); /* get the insertion character */ + + if (filling) /* if zero-filling is on */ + byte = fill_char; /* then substitute the fill character */ + + while (operand > 0) { /* while there are characters to insert */ + mem_write_byte (&target, byte); /* copy the character to the target */ + operand = operand - 1; /* and count it */ + } + break; + + + case 007: /* ICI - insert characters immediate */ + while (operand > 0) { /* while there are characters to insert */ + byte = mem_read_byte (&prog); /* get the next byte */ + + mem_write_byte (&target, byte); /* move it to the target */ + operand = operand - 1; /* and count it */ + } + break; + + + case 010: /* ICSI - insert characters suppressed immediate */ + while (operand > 0) { /* while there are characters to insert */ + byte = mem_read_byte (&prog); /* get the next byte */ + + if (filling) /* if zero-filling is on */ + byte = fill_char; /* then substitute the fill character */ + + mem_write_byte (&target, byte); /* copy the character to the target */ + operand = operand - 1; /* and count it */ + } + break; + + + case 011: /* BRIS - branch if significance */ + if (filling == FALSE) { /* if zero-filling is off */ + RD = RD - 1 + SEXT8 (operand) & R_MASK; /* then add the signed displacement to the offset */ + mem_set_byte (&prog); /* and reset the subprogram accessor */ + } + break; + + + case 012: /* SUFT - subtract from target */ + mem_update_byte (&target); /* update the final target byte if needed */ + RC = RC - SEXT8 (operand) & R_MASK; /* subtract the signed displacement from the offset */ + mem_set_byte (&target); /* and reset the target accessor */ + break; + + + case 013: /* SUFS - subtract from source */ + RB = RB - SEXT8 (operand) & R_MASK; /* subtract the signed displacement from the offset */ + mem_set_byte (&source); /* and reset the source accessor */ + break; + + + case 014: /* ICP - insert character punctuation */ + mem_write_byte (&target, operand + ' '); /* write the punctuation character to the target */ + break; + + + case 015: /* ICPS - insert character punctuation suppressed */ + if (filling) /* if zero-filling is on */ + byte = fill_char; /* then substitute the fill character */ + else /* otherwise */ + byte = operand + ' '; /* use the supplied punctuation character */ + + mem_write_byte (&target, byte); /* write the character to the target */ + break; + + + case 016: /* IS - insert characters on sign */ + if (operand > 0) { /* if the character strings are present */ + count = operand; /* then get the character count */ + + if ((STA & STATUS_CC_MASK) == STATUS_CCL) { /* if the sign is negative */ + RD = RD + count & R_MASK; /* then index to the negative character string */ + mem_set_byte (&prog); /* and reset the subprogram accessor */ + } + + while (count > 0) { /* while there are characters to copy */ + byte = mem_read_byte (&prog); /* get the next byte */ + + mem_write_byte (&target, byte); /* copy the character to the target */ + count = count - 1; /* and count it */ + } + + if ((STA & STATUS_CC_MASK) != STATUS_CCL) { /* if the sign is positive */ + RD = RD + operand & R_MASK; /* then skip over the negative character string */ + mem_set_byte (&prog); /* and reset the subprogram accessor */ + } + } + break; + + + case 017: /* two-byte operations */ + switch (operand) { /* dispatch on the second operation byte */ + + case 000: /* TE - terminate edit */ + terminate = TRUE; /* terminate the subprogram */ + break; + + + case 001: /* ENDF - end floating point insertion */ + if (filling) /* if zero-filling is on */ + mem_write_byte (&target, float_char); /* then insert the float character */ + break; + + + case 002: /* SST1 - set significance to 1 */ + filling = FALSE; /* set zero-filling off */ + break; + + + case 003: /* SST0 - set significance to 0 */ + filling = TRUE; /* set zero-filling on */ + break; + + + case 004: /* MDWO - move digit with overpunch */ + byte = mem_read_byte (&source); /* get the digit */ + + if (byte == ' ' && filling) /* if it's a blank and zero-filling is on */ + byte = '0'; /* then fill */ + + else if (byte < '0' || byte > '9') { /* otherwise if the character is not a digit */ + *trap = trap_Invalid_ASCII_Digit; /* then the it is not a valid number */ + terminate = TRUE; /* so abandon the subprogram */ + break; + } + + if ((STA & STATUS_CC_MASK) == STATUS_CCL) /* if the number is negative */ + byte = overpunch [Negative] [byte - '0']; /* then overpunch a minus sign */ + else /* otherwise */ + byte = overpunch [Positive] [byte - '0']; /* overpunch a plus sign */ + + mem_write_byte (&target, byte); /* write the overpunched character to the target */ + break; + + + case 005: /* SFC - set fill character */ + fill_char = mem_read_byte (&prog); /* set the fill character from the next byte */ + break; + + + case 006: /* SFLC - set float character on sign */ + byte = mem_read_byte (&prog); /* get the float characters */ + + if ((STA & STATUS_CC_MASK) == STATUS_CCL) /* if the number is negative */ + float_char = LOWER_HALF (byte) + ' '; /* then use the negative float character */ + else /* otherwise */ + float_char = UPPER_HALF (byte) + ' '; /* use the positive float character */ + break; + + + case 007: /* DFLC - define float character on sign */ + float_char = mem_read_byte (&prog); /* set the positive float character */ + + if ((STA & STATUS_CC_MASK) == STATUS_CCL) /* if the number is negative */ + float_char = mem_read_byte (&prog); /* then set the negative float character */ + else /* otherwise */ + mem_read_byte (&prog); /* skip over it */ + break; + + + case 010: /* SETC - set loop count */ + loop_count = mem_read_byte (&prog); /* get the new loop count */ + break; + + + case 011: /* DBNZ - decrement loop count and branch */ + byte = mem_read_byte (&prog); /* get the displacement */ + + loop_count = loop_count - 1 & D8_MASK; /* decrement the loop count modulo 256 */ + + if (loop_count > 0) { /* if the count is not zero */ + RD = RD - 1 - SEXT8 (byte) & R_MASK; /* then subtract the signed displacement from the offset */ + mem_set_byte (&prog); /* and reset the subprogram accessor */ + } + break; + + + default: /* invalid two-word opcodes */ + break; /* are ignored */ + } /* end of two-word dispatcher */ + break; + + } /* all cases are handled */ + + + if (terminate == FALSE /* if the subprogram is continuing */ + && cpu_interrupt_pending (status)) { /* and an interrupt is pending */ + cpu_push (); /* then push the stack down twice */ + cpu_push (); /* to save the subprogram execution state */ + + RA = D16_UMAX; /* set the resumption flag */ + + RB = TO_WORD ((filling ? D16_SIGN : 0), loop_count); /* save the significance trigger and loop count */ + RC = TO_WORD (fill_char, float_char); /* save the fill and float characters */ + + mem_update_byte (&target); /* update the last word written */ + return FALSE; /* and return with an interrupt set up or a status error */ + } + } + +while (terminate == FALSE); /* continue subprogram execution until terminated */ + + +mem_update_byte (&target); /* update the final target byte */ + +if (DPRINTING (cpu_dev, DEB_MOPND)) { /* if operand tracing is enabled */ + mem_set_byte (&source); /* then reset the source and target accessors */ + mem_set_byte (&target); /* to finalize the operand extents */ + + if (source.length > 0) /* if the source operand was used */ + fprint_operands (&source, &target, *trap); /* then print both source and target operands */ + else /* otherwise */ + fprint_operand (&target, "target", &fmt_byte_operand); /* print just the target operand */ + } + +RA = 0; /* clear the resumption flag */ + +return TRUE; /* return with completion status */ +} + + +/* Compare two padded byte strings. + + This routine compares two byte strings with optional translation and sets the + condition code in the status register to indicate the result. Starting with + the first, successive pairs of bytes are compared; if the string lengths are + unequal, the shorter string is padded with blanks. The comparison stops when + the bytes are unequal or the end of the strings is reached. The condition + code is CCG if the source byte is greater than the target byte, CCL if the + source byte is less than the target byte, or CCE if the strings are equal or + both strings are of zero-length. + + On entry, the RA and RC TOS registers contain the lengths of the source and + target strings, and the "source" and "target" parameters point at the byte + accessors for the source and target strings, respectively. The "table" + parameter either points at a 256-byte translation table or is NULL if no + translation is desired. If supplied, the table is used to translate the + source bytes and the target bytes if the target string is DB-relative. If + the target is PB-relative, no target translation is performed. + + The routine is interruptible between bytes. If an interrupt is pending, the + routine will return FALSE with the TOS registers updated to reflect the + partial comparison; reentering the routine will complete the operation. If + the comparison runs to completion, the condition code will be set, and the + routine will return TRUE. In either case, the "status" parameter is set to + the SCP status returned by the interrupt test. + + This routine implements the CMPS and CMPT instructions. +*/ + +static t_bool compare (BYTE_ACCESS *source, BYTE_ACCESS *target, BYTE_ACCESS *table, t_stat *status) +{ +uint8 source_byte, target_byte; + +*status = SCPE_OK; /* initialize the return status */ + +while (RA > 0 || RC > 0) { /* while there are bytes to compare */ + if (RA == 0) /* if the source string is exhausted */ + source_byte = ' '; /* then use a blank */ + else /* otherwise */ + source_byte = mem_read_byte (source); /* get the next source byte */ + + if (RC == 0) /* if the target string is exhausted */ + target_byte = ' '; /* then use a blank */ + else /* otherwise */ + target_byte = mem_read_byte (target); /* get the next target byte */ + + if (table != NULL) { /* if the translation table was supplied */ + source_byte = mem_lookup_byte (table, source_byte); /* then translate the source byte */ + + if (target->class == data || RC == 0) /* if the target is in the data segment or is exhausted */ + target_byte = mem_lookup_byte (table, target_byte); /* then translate the target byte */ + } + + if (source_byte != target_byte) /* if the bytes do not compare */ + break; /* then terminate the loop */ + + if (RA > 0) /* if source bytes remain */ + RA = RA - 1; /* then count the byte (cannot underflow) */ + + if (RC > 0) /* if target bytes remain */ + RC = RC - 1; /* then count the byte (cannot underflow) */ + + if (cpu_interrupt_pending (status)) /* if an interrupt is pending */ + return FALSE; /* then return with an interrupt set up or a status error */ + } + +if (RA == 0 && RC == 0) /* if the counts expired together */ + SET_CCE; /* then the strings are equal */ +else if (source_byte > target_byte) /* otherwise if the source byte > the target byte */ + SET_CCG; /* set the source string is greater */ +else /* otherwise the source byte < the target byte */ + SET_CCL; /* so the source string is less */ + +return TRUE; /* return comparison completion status */ +} + + +/* Format and print byte string operands. + + This routine formats and prints source and target byte string operands. The + source operand is always printed. The target operand is printed only if the + supplied trap condition is "trap_None"; otherwise, it is omitted. Tracing + must be enabled when the routine is called. +*/ + +static void fprint_operands (BYTE_ACCESS *source, BYTE_ACCESS *target, uint32 trap) +{ +fprint_operand (source, "source", &fmt_byte_operand); + +if (trap == trap_None) + fprint_operand (target, "target", &fmt_byte_operand); + +return; +} + + +/* Format, translate, and print byte string operands. + + This routine formats, optionally translates, and prints source and target + byte string operands. The source operand is always translated. The target + operand is translated only if it resides in the data segment. Tracing must + be enabled when the routine is called; the trace format is identical to that + produced by the fprint_operand routine. +*/ + +static void fprint_translated_operands (BYTE_ACCESS *source, BYTE_ACCESS *target, BYTE_ACCESS *table) +{ +hp_debug (&cpu_dev, DEB_MOPND, BOV_FORMAT " source %d,\"%s\"\n", + TO_BANK (source->first_byte_address / 2), + TO_OFFSET (source->first_byte_address / 2), + source->first_byte_offset, source->length, + fmt_translated_byte_operand (source->first_byte_address, + source->length, + table->first_byte_address)); + +if (target->class == program) + fprint_operand (target, "target", &fmt_byte_operand); + +else + hp_debug (&cpu_dev, DEB_MOPND, BOV_FORMAT " target %d,\"%s\"\n", + TO_BANK (target->first_byte_address / 2), + TO_OFFSET (target->first_byte_address / 2), + target->first_byte_offset, target->length, + fmt_translated_byte_operand (target->first_byte_address, + target->length, + table->first_byte_address)); +return; +} + + +/* Format and print a memory operand. + + The byte operand described by the byte accessor is sent to the debug trace + log file. Tracing must be enabled when the routine is called. + + On entry, "op" points at the byte accessor describing the operand, "label" + points to text used to label the operand, and "operand_printer" points to the + routine used to print the operand. The latter may be "fprint_byte_operand" + to print operands consisting of 8-bit characters, or "fprint_bcd_operand" to + print extended-decimal (BCD) operands as character strings; the operand + length field of the trace line will indicate the number of characters or BCD + digits, respectively. + + The operand is printed in this format: + + >>CPU opnd: 00.045177 000467 source 15,"NOW IS THE TIME" + ~~ ~~~~~~ ~~~~~~ ~~~~~~ ~~ ~~~~~~~~~~~~~~~~~ + | | | | | | + | | | | | +-- operand value + | | | | +------------- operand length + | | | +------------------ operand label + | | +---------------------------- octal relative byte offset from base register + | +------------------------------------ octal operand address (effective address) + +----------------------------------------- octal operand bank (PBANK, DBANK, or SBANK) +*/ + +static void fprint_operand (BYTE_ACCESS *op, char *label, OP_PRINT operand_printer) +{ +hp_debug (&cpu_dev, DEB_MOPND, BOV_FORMAT " %s %d,\"%s\"\n", + TO_BANK (op->first_byte_address / 2), + TO_OFFSET (op->first_byte_address / 2), + op->first_byte_offset, label, op->length, + operand_printer (op->first_byte_address, op->length)); + +return; +} diff --git a/HP3000/hp3000_cpu_ims.h b/HP3000/hp3000_cpu_ims.h index f870217e..6ae4f102 100644 --- a/HP3000/hp3000_cpu_ims.h +++ b/HP3000/hp3000_cpu_ims.h @@ -23,6 +23,7 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 10-Oct-16 JDB Moved cpu_read_memory, cpu_write_memory to hp3000_cpu.h 01-Sep-16 JDB Added the cpu_cold_cmd and cpu_power_cmd routines 15-Aug-16 JDB Removed obsolete comment mentioning iop_read/write_memory 15-Jul-16 JDB Corrected the IOCW_COUNT macro to return the correct value @@ -146,16 +147,11 @@ typedef enum { cpu_cold_cmd : process the LOAD and DUMP commands cpu_power_cmd : process the POWER commands - cpu_read_memory : read a word from main memory - cpu_write_memory : write a word to main memory */ extern t_stat cpu_cold_cmd (int32 arg, CONST char *buf); extern t_stat cpu_power_cmd (int32 arg, CONST char *buf); -extern t_bool cpu_read_memory (ACCESS_CLASS classification, uint32 offset, HP_WORD *value); -extern t_bool cpu_write_memory (ACCESS_CLASS classification, uint32 offset, HP_WORD value); - /* Global SIO order structures. diff --git a/HP3000/hp3000_defs.h b/HP3000/hp3000_defs.h index 3ed39d80..d776e79a 100644 --- a/HP3000/hp3000_defs.h +++ b/HP3000/hp3000_defs.h @@ -23,6 +23,10 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 29_Dec-16 JDB Changed the status mnemonic flag from REG_S to REG_T + 20-Nov-16 JDB Added mapped memory access classes + 24-Oct-16 JDB Added half-byte definitions for CIS decoding + 10-Oct-16 JDB Moved ACCESS_CLASS definition here from hp3000_cpu.h 03-Sep-16 JDB Added the STOP_POWER and STOP_ARSINH codes 13-May-16 JDB Modified for revised SCP API function parameter types 21-Mar-16 JDB Changed uint16 types to HP_WORD @@ -107,10 +111,6 @@ -#ifndef HP3000_DEFS_H_ -#define HP3000_DEFS_H_ - - #include "sim_rev.h" #include "sim_defs.h" @@ -150,7 +150,7 @@ #define REG_A (1u << REG_V_UF + 0) /* permit any display */ #define REG_B (1u << REG_V_UF + 1) /* permit binary display */ #define REG_M (1u << REG_V_UF + 2) /* default to instruction mnemonic display */ -#define REG_S (1u << REG_V_UF + 3) /* default to status mnemonic display */ +#define REG_T (1u << REG_V_UF + 3) /* default to status mnemonic display */ /* Register macros. @@ -264,21 +264,24 @@ printf (__VA_ARGS__); \ if (sim_log) \ fprintf (sim_log, __VA_ARGS__); \ - } while (0) + } \ + while (0) #define cputs(str) \ do { \ fputs (str, stdout); \ if (sim_log) \ fputs (str, sim_log); \ - } while (0) + } \ + while (0) #define cputc(ch) \ do { \ putc (ch); \ if (sim_log) \ fputc (ch, sim_log); \ - } while (0) + } \ + while (0) /* Simulation stop codes. @@ -396,6 +399,9 @@ typedef uint32 HP_WORD; /* HP 16-bit data word r #define R_MASK 0177777u /* 16-bit register mask */ +#define D4_WIDTH 4 /* 4-bit data bit width */ +#define D4_MASK 0017u /* 4-bit data mask */ + #define D8_WIDTH 8 /* 8-bit data bit width */ #define D8_MASK 0377u /* 8-bit data mask */ #define D8_UMAX 0377u /* 8-bit unsigned maximum value */ @@ -481,6 +487,67 @@ typedef uint32 HP_WORD; /* HP 16-bit data word r #define TO_OFFSET(p) ((p) & LA_MASK) +/* Memory access classifications. + + The access classification determines which bank register is used with the + supplied offset to access memory, whether or not the access is bounds + checked, and whether or not the access is mapped to the TOS registers if the + address is between SM and SM + SR. + + Bounds checking is optionally performed on program (including instruction + fetch), data, and stack accesses when not in privileged mode. Absolute + addresses are always accessed in privileged mode, so bounds checking is never + performed. + + If the memory address lies between SM and SM + SR within the stack bank, then + access to the TOS registers may be substituted for access to memory. + Register mapping is always performed on stack accesses, optionally performed + on absolute and data accesses, and is never performed on program (and fetch) + accesses. + + To summarize bounds checking and TOS register mapping: + + Bounds TOS + Access Check Mapping + -------- ------ ------- + absolute N O + fetch O N + program O N + data O O + stack O Y + dma N N + + + Implementation notes: + + 1. The enumeration values must be ordered such that the "checked" classes + are odd and differ from their corresponding "unchecked" classes only in + the LSBs. + + 2. There is no hardware DMA bank register. The "dma" class exists only to + differentiate DMA memory accesses from CPU memory accesses when tracing. +*/ + +typedef enum { + absolute, /* absolute bank */ + absolute_mapped, /* absolute bank, TOS registers mapped */ + fetch, /* program bank, instruction fetch */ + fetch_checked, /* program bank, instruction fetch, bounds checked */ + program, /* program bank, data access */ + program_checked, /* program bank, data access, bounds checked */ + data, /* data bank, data access */ + data_checked, /* data bank, data access, bounds checked */ + data_mapped, /* data bank, data or TOS register access */ + data_mapped_checked, /* data bank, data or TOS register access, bounds checked */ + stack, /* stack bank, data or TOS register access */ + stack_checked, /* stack bank, data or TOS register access, bounds checked */ + dma /* DMA bank */ + } ACCESS_CLASS; + +#define UNCHECKED(c) ((c) & ~1u) /* reclassify a request as unchecked */ +#define INVERT_CHECK(c) ((c) ^ 1u) /* convert checked to unchecked and vice versa */ + + /* Portable conversions. SIMH is written with the assumption that the defined-size types (e.g., @@ -495,11 +562,13 @@ typedef uint32 HP_WORD; /* HP 16-bit data word r The conversions available are: - - SEXT -- int16 sign-extended to int32 - - NEG16 -- int16 negated - - NEG32 -- int32 negated - - INT16 -- uint16 to int16 - - INT32 -- uint32 to int32 + - SEXT8 -- int8 sign-extended to int32 + - SEXT16 -- int16 sign-extended to int32 + - NEG16 -- int8 negated + - NEG16 -- int16 negated + - NEG32 -- int32 negated + - INT16 -- uint16 to int16 + - INT32 -- uint32 to int32 Implementation notes: @@ -508,8 +577,10 @@ typedef uint32 HP_WORD; /* HP 16-bit data word r before invoking. */ -#define SEXT(x) (int32) ((x) & D16_SIGN ? (x) | ~D16_MASK : (x)) +#define SEXT8(x) (int32) ((x) & D8_SIGN ? (x) | ~D8_MASK : (x)) +#define SEXT16(x) (int32) ((x) & D16_SIGN ? (x) | ~D16_MASK : (x)) +#define NEG8(x) ((~(x) + 1) & D8_MASK) #define NEG16(x) ((~(x) + 1) & D16_MASK) #define NEG32(x) ((~(x) + 1) & D32_MASK) @@ -517,6 +588,12 @@ typedef uint32 HP_WORD; /* HP 16-bit data word r #define INT32(u) ((u) > D32_SMAX ? (-(int32) (D32_UMAX - (u)) - 1) : (int32) (u)) +/* Half-byte accessors */ + +#define UPPER_HALF(b) ((b) >> D4_WIDTH & D4_MASK) +#define LOWER_HALF(b) ((b) & D4_MASK) + + /* Byte accessors. These macros extract the upper and lower bytes from a word and form a word @@ -614,8 +691,8 @@ extern const BITSET_FORMAT inbound_format; /* the inbound signal format str extern const BITSET_FORMAT outbound_format; /* the outbound signal format structure */ -/* System interface global SCP support routines previously declared in scp.h */ -/* +/* System interface global SCP support routines declared in scp.h + extern t_stat sim_load (FILE *fptr, CONST char *cptr, CONST char *fnam, int flag); extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); extern t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw); @@ -629,14 +706,12 @@ extern t_stat hp_show_dib (FILE *st, UNIT *uptr, int32 code, CONST void /* System interface global utility routines */ -extern t_bool hp_device_conflict (void); -extern t_stat fprint_cpu (FILE *ofile, t_value *val, uint32 radix, int32 switches); +extern t_stat fprint_cpu (FILE *ofile, t_value *val, uint32 radix, int32 switches); +extern uint32 fprint_edit (FILE *ofile, t_value *val, uint32 radix, uint32 byte_address); extern const char *fmt_status (uint32 status); extern const char *fmt_char (uint32 charval); extern const char *fmt_bitset (uint32 bitset, const BITSET_FORMAT bitfmt); -extern void hp_debug (DEVICE *dptr, uint32 flag, ...); - - -#endif +extern void hp_debug (DEVICE *dptr, uint32 flag, ...); +extern t_bool hp_device_conflict (void); diff --git a/HP3000/hp3000_diag.txt b/HP3000/hp3000_diag.txt index 02d30c87..85bc0a64 100644 --- a/HP3000/hp3000_diag.txt +++ b/HP3000/hp3000_diag.txt @@ -1,6 +1,6 @@ SIMH/HP 3000 DIAGNOSTICS PERFORMANCE ==================================== - Last update: 2016-09-07 + Last update: 2016-12-01 The HP 32230 diagnostic suite has been run against the SIMH HP 3000 simulation. @@ -60,8 +60,8 @@ The results of the diagnostic runs are summarized below: PD435A Universal Interface 01.01 Passed PD438A Terminal Control Interface 01.00 Passed PD439A CALCOMP Plotter Interface 01.01 No simulation - PD441A COBOL-II A Firmware 00.00 No simulation - PD442A COBOL-II B Firmware 00.00 No simulation + PD441A COBOL-II A Firmware 00.00 Passed + PD442A COBOL-II B Firmware 00.00 Passed PD466A Online Line Printer Verifier 01.06 Passed The "Result" column indicates the level of success in passing the given @@ -1548,7 +1548,9 @@ CONFIGURATION: sim> set lp diagnostic,intmask=8 sim> set clk realtime sim> go -TEST REPORT: D100 UNIV. INTERFACE TEST (HP D435A.01.01) +TEST REPORT: [CR entered] + + D100 UNIV. INTERFACE TEST (HP D435A.01.01) (C)COPYRIGHT HEWLETT PACKARD COMPANY 1976. ****************** WARNING ****************** this diagnostic has tests which will produce error @@ -1852,6 +1854,239 @@ TEST RESULT: Passed. +--------------------------- +D441A - COBOL-II A Firmware +--------------------------- + +TESTED DEVICE: CPU (hp3000_cpu.c) + +CONFIGURATION: sim> set cpu cis + sim> go + +TEST REPORT: [CTRL+E] + + sim> deposit SWCH 100011 + sim> go + + [CR entered] + + COBOLIIA F/W DIAG. (D441A.00.00) + + + TESTING MFL OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + MFL OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING MC'N OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + MCN OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING IC AND SUFT OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + IC OF EDIT PASSED ALL TESTS WITHOUT ERROR + SUFT OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING MA'N OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + MA OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING ICS AND SST0 AND SST1 OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + ICS OF EDIT PASSED ALL TESTS WITHOUT ERROR + SST0 OF EDIT PASSED ALL TESTS WITHOUT ERROR + SST1 OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING ICI OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + ICI OF EDIT PASSED ALL TESTS WITHOUT ERROR + BRIS OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + TESTING MN'N OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + MN OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING SFC AND ICSI OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + SFC OF EDIT PASSED ALL TESTS WITHOUT ERROR + ICSI OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING MNS OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + MNS OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING DBNZ AND SETC OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + DBNZ OF EDIT PASSED ALL TESTS WITHOUT ERROR + SETC OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING MDWO AND SUFS OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + MDWO OF EDIT PASSED ALL TESTS WITHOUT ERROR + SUFS OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING ICP OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + ICP OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING ICPS OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + ICPS OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING "IS" OF EDIT + PB'DB'MODE= 0 + PB'DB'MODE= 1 + "IS" OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + + TESTING "ENDF" OF EDIT + + TESTING "SFLC" OF EDIT + + TESTING "DFLC" OF EDIT + + PB'DB'MODE= 0 + PB'DB'MODE= 1 + "ENDF" OF EDIT PASSED ALL TESTS WITHOUT ERROR + "SFLC" OF EDIT PASSED ALL TESTS WITHOUT ERROR + "DFLC" OF EDIT PASSED ALL TESTS WITHOUT ERROR + ******************************************************************************** + END OF PASS 0 + + Programmed halt, CIR: 030375 (HALT 15), P: 010330 (RSW) + +TEST RESULT: Passed. + + + +--------------------------- +D442A - COBOL-II B Firmware +--------------------------- + +TESTED DEVICE: CPU (hp3000_cpu.c) + +CONFIGURATION: sim> set cpu cis + sim> go + +TEST REPORT: [CTRL+E] + + sim> deposit SWCH 100011 + sim> go + + [CR entered] + + COBOLIIB FIRMWARE DIAGNOSTIC (D442A.00.00) + + + TESTING ABSD + ABSD PASSED ALL TESTS WITHOUT ERROR + + TESTING ABSN + ABSN PASSED ALL TESTS WITHOUT ERROR + + + TESTING XBR + + XBR PASSED ALL TESTS WITHOUT ERROR + + + TESTING NEGD + SDEC= 0 + + SDEC= 1 + + NEGD PASSED ALL TESTS WITHOUT ERROR + + + TESTING PARC AND ENDP + + I AM IN OUTER'BLOCK OF PARC + I AM IN PAR6 + I AM IN OUTER'BLOCK OF PARC + PARC AND ENDP PASSED ALL TESTS WITHOUT ERROR + + + + TESTING TR + TESTING DB TABLE ACCESS + TESTING PB TABLE ACCESS + TR PASSED ALL TESTS WITHOUT ERROR + + + TESTING CVND + SDEC = 0 + + SDEC = 1 + + CVND PASSED ALL TESTS WITHOUT ERROR + + + + TESTING CMPS + TESTING DB-TARGET ACCESS + + TESTING PB-TARGET ACCESS + + CMPS PASSED ALL TESTS WITHOUT ERROR + + + TESTING CMPT + TESTING TRANSLATION TABLE IN PB + + TESTING DB-TARGET ACCESS + + TESTING PB-TARGET ACCESS + + CMPT PASSED ALL TESTS WITHOUT ERRORS + + + TESTING TCCS + TCCS PASSED ALL TESTS WITHOUT ERROR + + + TESTING LDW + SDEC=0 + SDEC=1 + TESTING LDDW + SDEC=0 + SDEC=1 + LDW AND LDWW PASSED ALL TESTS WITHOUT ERROR + + TESTING ALGN + ALGN PASSED ALL TESTS WITHOUT ERROR + + END OF PASS 0 + + Programmed halt, CIR: 030375 (HALT 15), P: 010315 (RSW) + +TEST RESULT: Passed. + + + ------------------------------------ D466A - Online Line Printer Verifier ------------------------------------ diff --git a/HP3000/hp3000_iop.c b/HP3000/hp3000_iop.c index 779ac54b..bd0585c8 100644 --- a/HP3000/hp3000_iop.c +++ b/HP3000/hp3000_iop.c @@ -25,6 +25,7 @@ IOP HP 3000 Series III I/O Processor + 10-Oct-16 JDB Renumbered debug flags to start at bit 0 03-Sep-16 JDB Added "iop_assert_PFWARN" to warn devices of power loss 01-Aug-16 JDB Added "iop_reset" to initialize the IOP 30-Jun-16 JDB Changed REG type of filter array to BRDATA @@ -174,6 +175,7 @@ #include "hp3000_cpu.h" #include "hp3000_cpu_ims.h" #include "hp3000_io.h" +#include "hp3000_mem.h" @@ -188,15 +190,10 @@ the trace stream. It returns the bit in the filter array corresponding to the device number. If the bit is set, the trace will be generated; otherwise, it will be suppressed. - - - Implementation notes: - - 1. Bit 0 is reserved for the memory data trace flag. */ -#define DEB_DIO (1u << 1) /* trace direct I/O commands */ -#define DEB_IRQ (1u << 2) /* trace interrupt requests */ +#define DEB_DIO (1u << 0) /* trace direct I/O commands */ +#define DEB_IRQ (1u << 1) /* trace interrupt requests */ #define FILTER(d) (1u << (d) % 32 & filter [(d) / 32]) diff --git a/HP3000/hp3000_mem.c b/HP3000/hp3000_mem.c new file mode 100644 index 00000000..e5489e40 --- /dev/null +++ b/HP3000/hp3000_mem.c @@ -0,0 +1,1051 @@ +/* hp3000_mem.c: HP 3000 main memory simulator + + Copyright (c) 2016, J. David Bryan + + 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 + AUTHOR 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. + + MEM HP 3000 Series III Main Memory + + 10-Oct-16 JDB Created + + References: + - HP 3000 Series II/III System Reference Manual + (30000-90020, July 1978) + - HP 3000 Series III Engineering Diagrams Set + (30000-90141, April 1980) + + + The HP 3000 Memory Subsystem is an integral part of the 3000 computer. + Replacing the core memory used in the earlier 3000 CX machines, the Series II + introduced an all-semiconductor memory using 4K NMOS RAMs that provided error + detection and correction. Single-bit errors are corrected automatically, and + double-bit errors are detected. All errors are logged in hardware, and the + logs are downloaded periodically by MPE to allow preventative maintenance and + replacement of failing parts. + + The Series II supports a main memory size of 64K to 256K words in 32K + increments. It uses four types of memory PCAs: + + - 30007-60002 MCL (Memory Control and Logging, up to 128K words) + - 30008-60002 SMA (Semiconductor Memory Array, 32K words, 17 bits) + - 30009-60001 FCA (Fault Correction Array, up to 128K words, 4 bits) + - 30009-60002 FLI (Fault Logging Interface, up to 256K words) + + A 64K system uses one of each PCA. A 256K system uses 2 MCLs, 8 SMAs, 2 FCAs + and 1 FLI. Five check bits (one on the SMA, four on the FCA) are used. + + The Series III supports a main memory size of 128K to 1024K words in 128K + increments using 16K RAMs. It uses three types of memory PCAs: + + - 30007-60005 MCL (Memory Control and Logging, up to 512K words) + - 30008-60003 SMA (Semiconductor Memory Array, 128K words, 22 bits) + - 30009-60002 FLI (Fault Logging Interface, up to 1024K words) + + A 128K system uses one of each PCA. A 1024K system uses 2 MCLs, 8 SMAs, and + 1 FLI. Six check bits (all on the SMA) are used. The standalone FLI PCA may + be replaced with a 30135-60063 System Clock/Fault Logging Interface that + combines both devices on a single PCA. + + Main memory consists of from one to eight 128K word memory arrays. Memory is + divided into two 512K modules, each with its own Module Control Unit and + Memory Control and Logging PCA. The two modules respond to module numbers 0 + and 1 or 2 and 3. + + Error correction is implemented by storing five (Series II) or six (Series + III) check bits with the sixteen data bits. The Series III check bits + reflect the parity of sets of eight data bits, as follows: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 C0 C1 C2 C3 C4 C5 Parity + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ------ + X X X X X X X X X Even + X X X X X X X X X Odd + X X X X X X X X X Even + X X X X X X X X X Odd + X X X X X X X X X Even + X X X X X X X X X Even + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + 07 13 23 03 15 25 11 21 16 06 32 22 34 14 24 30 00 20 10 04 02 01 Syndrome + + The check bits are generated by setting C0-C5 to zero. When read, the parity + computations (syndrome) will result in all zeros if the data and check bits + are correct and non-zero values if one or more bits are in error. If a + single bit (either data or check) is in error, the syndrome itself will have + odd parity and will indicate the bit in error as indicated above. If the + syndrome is non-zero and has even parity, i.e., does not contain either one + or three 1-bits, then a double-bit error has occurred, and the syndrome value + is not significant. + + The MCL will correct single-bit data errors (single-bit check errors need not + be corrected). Double-bit errors will result in data parity interrupts. + + Each MCL contains one 1024 x 1 static RAM ELA (Error Logging Array). The + array stores a 1 in an address corresponding to the 4K or 16K RAM chip + containing the bit in error. The address is 10 bits wide, consisting of a + 5-bit chip-row address (2-bit SMA PCA address and 3-bit row address) and a + 5-bit bit-in-error code (the lower five bits of the 6-bit ECC syndrome). The + bit-in-error code is decoded as: + + Code Bit Code Bit Code Bit Code Bit + ---- --- ---- --- ---- --- ---- --- + 00 C0 10 C2 20 C1 30 D15 + 01 C5 11 D6 21 D7 31 -- + 02 C4 12 ** 22 D11 32 D10 + 03 D3 13 D1 23 D2 33 -- + 04 C3 14 D13 24 D14 34 D12 + 05 * 15 D4 25 D5 35 -- + 06 D9 16 D8 26 -- 36 -- + 07 D0 17 -- 27 -- 37 -- + + * Forced double-error write + ** Missing SMA + + If a parity error occurs on the data sent from the MCU to the SMA for a + write, the MCL asserts a data parity error (CPX1.6) and forces a double-bit + error into the check bits by complementing the C3 and C5 bits. This ensures + that a read of the location will always cause a data parity error interrupt. + If an addressed SMA is not present, the all-zeros data and check bits result + in a syndrome of 12, due to the odd parity of the C2 and C4 calculations. + + + Main memory is simulated by allocating an array of MEMORY_WORDs large enough + to accommodate the largest system configuration (1024 KW). Array access is + then restricted to the configured size; accesses beyond the end of configured + memory result in an Illegal Address interrupt. + + All accesses to main memory are through exported functions. Examine and + deposit routines provide for SCP interfacing, and general read and write + routines are used by the other HP 3000 simulator modules. Each general + access carries an access classification that determines how memory will be + addressed. Program, data, and stack accesses use their respective memory + bank registers to form the indices into the simulated memory array. DMA + accesses on behalf of the multiplexer and selector channels use the memory + banks supplied by the channel programs. Absolute accesses imply bank number + zero. + + Several auxiliary functions provide memory initialization, filling, and + checking that a specified range of memory has not been used. A full set of + byte-access routines is provided to emulate byte addressing on the + word-addressable HP 3000. + + The memory simulator provides the capability to trace memory reads and + writes, as well as byte and BCD operands that are stored in memory. Three + general memory debug flags are defined and can be used by the other simulator + modules to trace memory reads and writes, instruction fetches, and operand + accesses. + + + Implementation notes: + + 1. Error detection and correction is not currently simulated. +*/ + + + +#include "hp3000_defs.h" +#include "hp3000_cpu.h" +#include "hp3000_mem.h" + + + +/* Memory access classification table */ + +typedef struct { + HP_WORD *bank_ptr; /* a pointer to the bank register */ + uint32 debug_flag; /* the debug flag for tracing */ + const char *name; /* the classification name */ + } ACCESS_PROPERTIES; + + +static const ACCESS_PROPERTIES access [] = { /* indexed by ACCESS_CLASS */ +/* bank_ptr debug_flag name */ +/* -------- ----------- ------------------- */ + { NULL, DEB_MDATA, "absolute" }, /* absolute */ + { NULL, DEB_MDATA, "absolute" }, /* absolute_mapped */ + { & PBANK, DEB_MFETCH, "instruction fetch" }, /* fetch */ + { & PBANK, DEB_MFETCH, "instruction fetch" }, /* fetch_checked */ + { & PBANK, DEB_MDATA, "program" }, /* program */ + { & PBANK, DEB_MDATA, "program" }, /* program_checked */ + { & DBANK, DEB_MDATA, "data" }, /* data */ + { & DBANK, DEB_MDATA, "data" }, /* data_checked */ + { & DBANK, DEB_MDATA, "data" }, /* data_mapped */ + { & DBANK, DEB_MDATA, "data" }, /* data_mapped_checked */ + { & SBANK, DEB_MDATA, "stack" }, /* stack */ + { & SBANK, DEB_MDATA, "stack" }, /* stack_checked */ + { NULL, DEB_MDATA, "dma" } /* dma */ + }; + + +/* Memory local data structures */ + + +/* Main memory */ + +static MEMORY_WORD *M = NULL; /* the pointer to the main memory allocation */ + + + +/* Memory global SCP helpers */ + + +/* Examine a memory location. + + This routine is called by the SCP to examine memory. The routine retrieves + the memory location indicated by "address" as modified by any "switches" that + were specified on the command line and returns the value in the first element + of "eval_array". + + On entry, if "switches" includes SIM_SW_STOP, then "address" is an offset + from PBANK; otherwise, it is an absolute address. If the supplied address is + beyond the current memory limit, "non-existent memory" status is returned. + Otherwise, the value is obtained from memory and returned in "eval_array." +*/ + +t_stat mem_examine (t_value *eval_array, t_addr address, UNIT *uptr, int32 switches) +{ +if (switches & SIM_SW_STOP) /* if entry is for a simulator stop */ + address = TO_PA (PBANK, address); /* then form a PBANK-based physical address */ + +if (address >= MEMSIZE) /* if the address is beyond memory limits */ + return SCPE_NXM; /* then return non-existent memory status */ + +else if (eval_array == NULL) /* if the value pointer was not supplied */ + return SCPE_IERR; /* then return internal error status */ + +else { /* otherwise */ + *eval_array = (t_value) M [address]; /* store the return value */ + return SCPE_OK; /* and return success */ + } +} + + +/* Deposit to a memory location. + + This routine is called by the SCP to deposit to memory. The routine stores + the supplied "value" into memory at the "address" location. If the supplied + address is beyond the current memory limit, "non-existent memory" status is + returned. + + The presence of any "switches" supplied on the command line does not affect + the operation of the routine. +*/ + +t_stat mem_deposit (t_value value, t_addr address, UNIT *uptr, int32 switches) +{ +if (address >= MEMSIZE) /* if the address is beyond memory limits */ + return SCPE_NXM; /* then return non-existent memory status */ + +else { /* otherwise */ + M [address] = value & DV_MASK; /* store the supplied value into memory */ + return SCPE_OK; /* and return success */ + } +} + + + +/* Memory global routines */ + + +/* Initialize main memory. + + The array of MEMORY_WORDs that represent the main memory of the HP 3000 + system is allocated and initialized to zero if the global pointer M has not + been set. The number of words to be allocated is supplied. The routine + returns TRUE if the allocation was successful or memory had already been + allocated earlier, or FALSE if the allocation failed. +*/ + +t_bool mem_initialize (uint32 memory_size) +{ +if (M == NULL) /* if memory has not been allocated */ + M = (MEMORY_WORD *) calloc (memory_size, /* then allocate the maximum amount of memory needed */ + sizeof (MEMORY_WORD)); + +return (M != NULL); +} + + +/* Check for non-zero value in a memory address range. + + A range of memory locations is checked for the presence of a non-zero value. + The starting address of the range is supplied, and the check continues + through the end of defined memory. The routine returns TRUE if the memory + range was empty (i.e., contained only zero values) and FALSE otherwise. +*/ + +t_bool mem_is_empty (uint32 starting_address) +{ +uint32 address; + +for (address = starting_address; address < MEMSIZE; address++) /* loop through the specified address range */ + if (M [address] != NOP) /* if this location is non-zero */ + return FALSE; /* then indicate that memory is not empty */ + +return TRUE; /* return TRUE if all locations contain zero values */ +} + + +/* Fill a range of memory with a value. + + Main memory locations from a supplied starting address through the end of + defined memory are filled with the specified value. This routine is + typically used by the cold-load routine to fill memory with HALT 10 + instructions. +*/ + +void mem_fill (uint32 starting_address, HP_WORD fill_value) +{ +uint32 address; + +for (address = starting_address; address < MEMSIZE; address++) /* loop through the specified address range */ + M [address] = (MEMORY_WORD) fill_value; /* filling locations with the supplied value */ + +return; +} + + +/* Read a word from memory. + + Read and return a word from memory at the indicated offset and implied bank. + If the access succeeds, the routine returns TRUE. If the accessed word is + outside of physical memory, the Illegal Address interrupt flag is set for + CPU accesses, the value is set to 0, and the routine returns FALSE. If + access checking is requested, and the check fails, a Bounds Violation trap is + taken. + + On entry, "dptr" points to the DEVICE structure of the device requesting + access, "classification" is the type of access requested, "offset" is a + logical offset into the memory bank implied by the access classification, + except for absolute and DMA accesses, for which "offset" is a physical + address, and "value" points to the variable to receive the memory content. + + Memory accesses other than DMA accesses may be checked or unchecked. Checked + program, data, and stack accesses must specify locations within the + corresponding segments (PB <= ea <= PL for program, or DL <= ea <= S for data + or stack) unless the CPU in is privileged mode, and those that reference the + TOS locations return values from the TOS registers instead of memory. + Checked absolute accesses return TOS location values if referenced but + otherwise access memory directly with no additional restrictions. + + For data and stack accesses, there are three cases, depending on the + effective address: + + - EA >= DL and EA <= SM : read from memory + + - EA > SM and EA <= SM + SR : read from a TOS register if bank = stack bank + + - EA < DL or EA > SM + SR : trap if not privileged, else read from memory + + + Implementation notes: + + 1. The physical address is formed by merging the bank and offset without + masking either value to their respective register sizes. Masking is not + necessary, as it was done when the bank registers were loaded, and it is + faster to avoid it. Primarily, though, it is not done so that an invalid + bank register value (e.g., loaded from a corrupted stack) will generate + an illegal address interrupt and so will pinpoint the problem for + debugging. + + 2. In hardware, bounds checking is performed explicitly by microcode. In + simulation, bounds checking is performed explicitly by employing the + "_checked" versions of the desired access classifications. +*/ + +t_bool mem_read (DEVICE *dptr, ACCESS_CLASS classification, uint32 offset, HP_WORD *value) +{ +uint32 bank, address; + +if (access [classification].bank_ptr == NULL) { /* if this is an absolute or DMA access */ + address = offset; /* then the "offset" is already a physical address */ + bank = TO_BANK (offset); /* separate the bank and offset */ + offset = TO_OFFSET (offset); /* in case tracing is active */ + } + +else { /* otherwise the bank register is implied */ + bank = *access [classification].bank_ptr; /* by the access classification */ + address = bank << LA_WIDTH | offset; /* form the physical address with the supplied offset */ + } + +if (address >= MEMSIZE) { /* if this access is beyond the memory size */ + if (dptr == &cpu_dev) /* then if an interrupt is requested */ + CPX1 |= cpx1_ILLADDR; /* then set the Illegal Address interrupt */ + + *value = 0; /* return a zero value */ + return FALSE; /* and indicate failure to the caller */ + } + +else { /* otherwise the access is within the memory range */ + switch (classification) { /* so dispatch on the access classification */ + + case dma: + case absolute: + case fetch: + case program: + case data: + *value = (HP_WORD) M [address]; /* unchecked access values come from memory */ + break; + + + case absolute_mapped: + case data_mapped: + case stack: + if (offset > SM && offset <= SM + SR && bank == SBANK) /* if the offset is within the TOS */ + *value = TR [SM + SR - offset]; /* then the value comes from a TOS register */ + else /* otherwise */ + *value = (HP_WORD) M [address]; /* the value comes from memory */ + break; + + + case fetch_checked: + if (PB <= offset && offset <= PL) /* if the offset is within the program segment bounds */ + *value = (HP_WORD) M [address]; /* then the value comes from memory */ + else /* otherwise */ + MICRO_ABORT (trap_Bounds_Violation); /* trap for a bounds violation */ + break; + + + case program_checked: + if (PB <= offset && offset <= PL || PRIV) /* if the offset is within bounds or is privileged */ + *value = (HP_WORD) M [address]; /* then the value comes from memory */ + else /* otherwise */ + MICRO_ABORT (trap_Bounds_Violation); /* trap for a bounds violation */ + break; + + + case data_checked: + if (DL <= offset && offset <= SM + SR || PRIV) /* if the offset is within bounds or is privileged */ + *value = (HP_WORD) M [address]; /* then the value comes from memory */ + else /* otherwise */ + MICRO_ABORT (trap_Bounds_Violation); /* trap for a bounds violation */ + break; + + + case data_mapped_checked: + case stack_checked: + if (offset > SM && offset <= SM + SR && bank == SBANK) /* if the offset is within the TOS */ + *value = TR [SM + SR - offset]; /* then the value comes from a TOS register */ + else if (DL <= offset && offset <= SM + SR || PRIV) /* if the offset is within bounds or is privileged */ + *value = (HP_WORD) M [address]; /* then the value comes from memory */ + else /* otherwise */ + MICRO_ABORT (trap_Bounds_Violation); /* trap for a bounds violation */ + break; + } /* all cases are handled */ + + dpprintf (dptr, access [classification].debug_flag, + BOV_FORMAT " %s%s\n", bank, offset, *value, + access [classification].name, + access [classification].debug_flag == DEB_MDATA ? " read" : ""); + + return TRUE; /* indicate success with the returned value stored */ + } +} + + +/* Write a word to memory. + + Write a word to memory at the indicated offset and implied bank. If the + write succeeds, the routine returns TRUE. If the accessed location is outside + of physical memory, the Illegal Address interrupt flag is set for CPU + accesses, the write is ignored, and the routine returns FALSE. If access + checking is requested, and the check fails, a Bounds Violation trap is taken. + + For data and stack accesses, there are three cases, depending on the + effective address: + + - EA >= DL and EA <= SM + SR : write to memory + + - EA > SM and EA <= SM + SR : write to a TOS register if bank = stack bank + + - EA < DL or EA > SM + SR : trap if not privileged, else write to memory + + Note that cases 1 and 2 together imply that a write to a TOS register also + writes through to the underlying memory. + + + Implementation notes: + + 1. The physical address is formed by merging the bank and offset without + masking either value to their respective register sizes. Masking is not + necessary, as it was done when the bank registers were loaded, and it is + faster to avoid it. Primarily, though, it is not done so that an invalid + bank register value (e.g., loaded from a corrupted stack) will generate + an illegal address interrupt and so will pinpoint the problem for + debugging. + + 2. In hardware, bounds checking is performed explicitly by microcode. In + simulation, bounds checking is performed explicitly by employing the + "_checked" versions of the desired access classifications. + + 3. The Series II microcode shows that only the STOR and STD instructions + write through to memory when the effective address is in a TOS register. + However, in simulation, all (checked) stack and data writes will write + through. +*/ + +t_bool mem_write (DEVICE *dptr, ACCESS_CLASS classification, uint32 offset, HP_WORD value) +{ +uint32 bank, address; + +if (access [classification].bank_ptr == NULL) { /* if this is an absolute or DMA access */ + address = offset; /* then "offset" is already a physical address */ + bank = TO_BANK (offset); /* separate the bank and offset */ + offset = TO_OFFSET (offset); /* in case tracing is active */ + } + +else { /* otherwise the bank register is implied */ + bank = *access [classification].bank_ptr; /* by the access classification */ + address = bank << LA_WIDTH | offset; /* form the physical address with the supplied offset */ + } + +if (address >= MEMSIZE) { /* if this access is beyond the memory size */ + if (dptr == &cpu_dev) /* then if an interrupt is requested */ + CPX1 |= cpx1_ILLADDR; /* then set the Illegal Address interrupt */ + + return FALSE; /* indicate failure to the caller */ + } + +else { /* otherwise the access is within the memory range */ + switch (classification) { /* so dispatch on the access classification */ + + case dma: + case absolute: + case data: + M [address] = (MEMORY_WORD) value; /* write the value to memory */ + break; + + + case absolute_mapped: + case data_mapped: + case stack: + if (offset > SM && offset <= SM + SR && bank == SBANK) /* if the offset is within the TOS */ + TR [SM + SR - offset] = value; /* then write the value to a TOS register */ + else /* otherwise */ + M [address] = (MEMORY_WORD) value; /* write the value to memory */ + break; + + + case data_mapped_checked: + case stack_checked: + if (offset > SM && offset <= SM + SR && bank == SBANK) /* if the offset is within the TOS */ + TR [SM + SR - offset] = value; /* then write the value to a TOS register */ + + /* fall into checked cases */ + + case data_checked: + if (DL <= offset && offset <= SM + SR || PRIV) /* if the offset is within bounds or is privileged */ + M [address] = (MEMORY_WORD) value; /* then write the value to memory */ + else /* otherwise */ + MICRO_ABORT (trap_Bounds_Violation); /* trap for a bounds violation */ + break; + + + case fetch: + case fetch_checked: + case program: + case program_checked: /* these classes cannot be used for writing */ + CPX1 |= cpx1_ADDRPAR; /* so set an Address Parity Error interrupt */ + return FALSE; /* and indicate failure to the caller */ + + } /* all cases are handled */ + + dpprintf (dptr, access [classification].debug_flag, + BOV_FORMAT " %s write\n", bank, offset, value, + access [classification].name); + + return TRUE; /* indicate success with the value written */ + } +} + + +/* Initialize a byte accessor. + + The supplied byte accessor structure is initialized for the starting relative + byte offset pointer and type of access indicated. If the supplied block + length is non-zero and checked accesses are requested, then the starting and + ending word addresses are bounds-checked, and a Bounds Violation will occur + if the address range exceeds that permitted by the access. If the block + length is zero and checked accesses are requested, then only the starting + address is checked, and it is the caller's responsibility to check additional + accesses as they occur. + + The byte access routines assume that if the initial range or starting address + is checked, succeeding accesses need not be checked, and vice versa. The + implication is that if the access class passed to this routine is checked, + the routine might abort with a Bounds Violation, but succeeding read or write + accesses will not, and if the class is unchecked, this routine will not abort + but a succeeding access might. + + On return, the byte accessor is ready for use with the other byte access + routines. + + + Implementation notes: + + 1. Calling mem_set_byte with the initial_byte_address field set to zero + indicates an initialization call that should use the count field as the + block length. Zero is not a valid value for initial_byte_address, as + memory location 0 is reserved for the code segment table pointer. +*/ + +void mem_init_byte (BYTE_ACCESS *bap, ACCESS_CLASS class, HP_WORD *byte_offset, uint32 block_length) +{ +bap->class = INVERT_CHECK (class); /* invert the access check for succeeding calls */ +bap->write_needed = FALSE; /* and clear the word buffer occupation flag */ + +bap->byte_offset = byte_offset; /* save the pointer to the relative byte offset variable */ +bap->first_byte_offset = *byte_offset; /* and initialize the lowest byte offset */ + +bap->length = block_length; /* set the maximum extent length to the block length */ +bap->count = block_length; /* and pass the initial block length */ +bap->initial_byte_address = 0; /* in an initialization call */ + +mem_set_byte (bap); /* set up the access from the initial byte offset */ + +bap->first_byte_address = bap->initial_byte_address; /* save the lowest byte address */ +bap->count = 0; /* and clear the byte access count */ + +return; +} + + +/* Set a byte accessor. + + The supplied byte accessor is set to access the updated address specified by + the byte offset variable. If the variable is altered directly, this routine + must be called before calling any of the other byte access routines. It is + also called to update the first byte offset and length in preparation for + formatting an operand for tracing. + + On return, the byte accessor is ready for use with the other byte access + routines. + + + Implementation notes: + + 1. Entry with the initial_byte_address field set to zero indicates an + initialization call; the count field will contain the block length. + Entry with initial_byte_address non-zero indicates that the count field + contains the number of bytes read or written since initialization. + + 2. The operand extents are updated only if an access was made with the + current accessor. This avoids extending the bounds if the accessor was + set but never used to read or write a byte. + + 3. The class field contains the access class used when reading or writing + bytes. The initial access check uses the opposite sense. +*/ + +void mem_set_byte (BYTE_ACCESS *bap) +{ +uint32 bank; + +mem_update_byte (bap); /* flush the last byte if written */ + +if (bap->count > 0 && bap->initial_byte_address > 0) { /* if bytes have been accessed */ + if (bap->initial_byte_address < bap->first_byte_address) { /* then if the current address is lower */ + bap->length = bap->length + bap->first_byte_address /* then extend the length */ + - bap->initial_byte_address; /* by the additional amount */ + + bap->first_byte_address = bap->initial_byte_address; /* reset the lowest address seen */ + bap->first_byte_offset = bap->initial_byte_offset; /* and the lowest offset seen */ + } + + else /* otherwise the current address is higher */ + bap->count = bap->count + bap->initial_byte_address /* (or unchanged) so extend the count */ + - bap->first_byte_address; /* by the additional amount if any */ + + if (bap->length < bap->count) /* if the maximum length is less than the current count */ + bap->length = bap->count; /* then reset the maximum to the current extent */ + + bap->count = 0; /* clear the access count */ + } + +bap->initial_byte_offset = *bap->byte_offset; /* set the new starting relative byte offset */ + +bap->word_address = cpu_byte_ea (INVERT_CHECK (bap->class), /* convert the new byte offset to a word address */ + *bap->byte_offset, /* and check the bounds if originally requested */ + bap->count); + +if (access [bap->class].bank_ptr == NULL) /* if this is an absolute or DMA access */ + bank = 0; /* then the byte offset is already a physical address */ +else /* otherwise */ + bank = *access [bap->class].bank_ptr; /* the bank register is implied by the classification */ + +bap->initial_byte_address = TO_PA (bank, bap->word_address) * 2 /* save the physical starting byte address */ + + (bap->initial_byte_offset & 1); + +if ((bap->initial_byte_offset & 1) == 0) /* if the starting byte offset is even */ + bap->word_address = bap->word_address - 1 & LA_MASK; /* then bias the address for the first read */ + +return; +} + + +/* Look up a byte in a table. + + The byte located in the table designated by the byte accessor pointer "bap" + at the entry designated by the "index" parameter is returned. The table is + byte-addressable and assumed to be long enough to contain the indexed + entry. + + + Implementation notes: + + 1. Successive lookups using the same index incur only one memory read + penalty. +*/ + +uint8 mem_lookup_byte (BYTE_ACCESS *bap, uint8 index) +{ +uint32 byte_offset, word_address; + +byte_offset = *bap->byte_offset + (HP_WORD) index /* get the offset to the indexed location */ + & LA_MASK; + +word_address = cpu_byte_ea (bap->class, byte_offset, 0); /* convert to a word address and check the bounds */ + +if (word_address != bap->word_address) { /* if the address is not the same as the prior access */ + bap->word_address = word_address; /* then set the new address */ + cpu_read_memory (bap->class, word_address, /* and read the memory word */ + &bap->data_word); /* containing the target byte */ + } + +if (byte_offset & 1) /* if the byte offset is odd */ + return LOWER_BYTE (bap->data_word); /* then return the lower byte */ +else /* otherwise */ + return UPPER_BYTE (bap->data_word); /* return the upper byte */ +} + + +/* Read the next byte. + + The next byte indicated by the supplied byte accessor is returned. + + If a new memory word must be read, and a previous byte write has not written + the buffered word into memory, it is posted. Then the next word is read from + memory, and the indicated byte is returned. + + + Implementation notes: + + 1. The data_word field is not read until the first access is made. This + ensures that a Bounds Violation does not occur on an unchecked + initialization call but instead occurs when the byte is actually + accessed. +*/ + +uint8 mem_read_byte (BYTE_ACCESS *bap) +{ +uint8 byte; + +if (*bap->byte_offset & 1) { /* if the byte offset is odd */ + if (bap->count == 0) /* then if this is the first access */ + cpu_read_memory (bap->class, bap->word_address, /* then read the data word */ + &bap->data_word); /* containing the target byte */ + + byte = LOWER_BYTE (bap->data_word); /* get the lower byte */ + } + +else { /* otherwise */ + if (bap->write_needed) { /* if the buffer is occupied */ + bap->write_needed = FALSE; /* then mark it written */ + cpu_write_memory (bap->class, bap->word_address, /* and write the word back */ + bap->data_word); + } + + bap->word_address = bap->word_address + 1 & LA_MASK; /* update the word address */ + cpu_read_memory (bap->class, bap->word_address, /* read the data word */ + &bap->data_word); /* containing the target byte */ + byte = UPPER_BYTE (bap->data_word); /* and get the upper byte */ + } + +*bap->byte_offset = *bap->byte_offset + 1 & LA_MASK; /* update the byte offset */ +bap->count = bap->count + 1; /* and the access count */ + +return byte; +} + + +/* Write the next byte. + + The next byte indicated by the supplied byte accessor is written. If the + lower byte is accessed, the containing word is written to memory, and the + buffer word is marked vacant. Otherwise, the upper byte is placed in the + buffer word, and the flag is set to indicate that the word will need to be + written to memory. + + + Implementation notes: + + 1. The data_word field is not read until the first access is made. This + ensures that a Bounds Violation does not occur on an unchecked + initialization call but instead occurs when the byte is actually + accessed. +*/ + +void mem_write_byte (BYTE_ACCESS *bap, uint8 byte) +{ +if (*bap->byte_offset & 1) { /* if the byte offset is odd */ + if (bap->count == 0) /* then if this is the first access */ + cpu_read_memory (bap->class, bap->word_address, /* then read the data word */ + &bap->data_word); /* containing the target byte */ + + bap->data_word = REPLACE_LOWER (bap->data_word, byte); /* replace the lower byte */ + cpu_write_memory (bap->class, bap->word_address, /* and write the word to memory */ + bap->data_word); + bap->write_needed = FALSE; /* clear the occupancy flag */ + } + +else { /* otherwise the offset is even */ + bap->word_address = bap->word_address + 1 & LA_MASK; /* so update the word address */ + bap->data_word = REPLACE_UPPER (bap->data_word, byte); /* replace the upper byte */ + bap->write_needed = TRUE; /* and set the occupancy flag */ + } + +*bap->byte_offset = *bap->byte_offset + 1 & LA_MASK; /* update the byte offset */ +bap->count = bap->count + 1; /* and the access count */ + +return; +} + + +/* Modify the last byte accessed. + + The last byte read or written as indicated by the supplied byte accessor is + modified in-place with the new value supplied. The current byte offset will + be odd if the last byte accessed was the upper (even) byte, or it will be + even if the last byte accessed was the lower (odd) byte. The current byte + offset is not changed by this routine. +*/ + +void mem_modify_byte (BYTE_ACCESS *bap, uint8 byte) +{ +if (*bap->byte_offset & 1) { /* if the last byte offset was even */ + bap->data_word = REPLACE_UPPER (bap->data_word, byte); /* then replace the upper byte */ + bap->write_needed = TRUE; /* and set the occupancy flag */ + } + +else { /* otherwise the last offset was odd */ + bap->data_word = REPLACE_LOWER (bap->data_word, byte); /* so replace the lower byte */ + cpu_write_memory (bap->class, bap->word_address, /* write the word back */ + bap->data_word); + bap->write_needed = FALSE; /* clear the occupancy flag */ + } + +return; +} + + +/* Post the current buffer word. + + The buffer word held by the supplied byte accessor is written to memory if + the occupancy flag is set. Otherwise, no action is taken. + + This routine must be called to terminate any sequence of byte operations + that involves calls to mem_read_byte and mem_modify_byte. It ensures that + the final byte written is flushed to memory. + + + Implementation notes: + + 1. Because a preceding mem_read_byte call has been made, the data_word field + already contains the byte that was NOT modified, so a read-modify-write + access is not needed. +*/ + +void mem_post_byte (BYTE_ACCESS *bap) +{ +if (bap->write_needed) { /* if the buffer needs to be written */ + bap->write_needed = FALSE; /* then clear the occupancy flag */ + cpu_write_memory (bap->class, bap->word_address, /* and write the word to memory */ + bap->data_word); + } + +return; +} + + +/* Rewrite the current buffer word. + + The upper byte of the buffer word held by the supplied byte accessor replaces + the upper byte of the current memory word without disturbing the lower byte, + and the word is rewritten to memory if the occupancy flag is set. Otherwise, + no action is taken. + + This routine should be called to terminate any sequence of byte operations + that involves calls to mem_write_byte. It ensures that the final byte + written is flushed to memory. The read-modify-write sequence ensures that + the existing lower byte in the memory word is retained. +*/ + +void mem_update_byte (BYTE_ACCESS *bap) +{ +HP_WORD target_word; + +if (bap->write_needed) { /* if the buffer needs to be written */ + bap->write_needed = FALSE; /* then clear the occupancy flag */ + + cpu_read_memory (bap->class, bap->word_address, &target_word); /* read the data word */ + bap->data_word = REPLACE_LOWER (bap->data_word, target_word); /* and replace the lower byte */ + cpu_write_memory (bap->class, bap->word_address, bap->data_word); /* and write the word back */ + } + +return; +} + + +/* Format a byte operand. + + The byte string starting at the absolute byte address given by the + "byte_address" parameter and of "byte_count" bytes in length is copied into + a local character buffer and terminated by a NUL character. A pointer to the + buffer is returned. + + No translation of non-printable characters is performed, so if the caller + interprets the returned formatted operand as a character string, an embedded + NUL will truncate the string. + + + Implementation notes: + + 1. This routine accesses the memory array directly to avoid tracing the + memory reads if debug tracing is enabled. + + 2. The byte count is assumed to be 256 or less for convenience. +*/ + +char *fmt_byte_operand (uint32 byte_address, uint32 byte_count) +{ +static char buffer [257]; +char *cptr; +uint32 address; + +if (byte_count > 256) /* truncate the formatted operand */ + byte_count = 256; /* if it's too long */ + +address = byte_address / 2; /* convert to an absolute word address */ + +cptr = buffer; /* point at the start of the buffer */ + +while (byte_count-- > 0) /* while there are bytes to transfer */ + if (byte_address++ & 1) /* if the byte address is odd */ + *cptr++ = LOWER_BYTE (M [address++]); /* then copy the lower byte and bump the word address */ + else if (address < MEMSIZE) /* otherwise if the word address is valid */ + *cptr++ = UPPER_BYTE (M [address]); /* then copy the upper byte */ + else /* otherwise the address is beyond the end of memory */ + break; /* so terminate the operand at this point */ + +*cptr = '\0'; /* add a trailing NUL */ + +return buffer; /* return a pointer to the formatted operand */ +} + + +/* Format a translated byte operand. + + The byte string starting at the absolute byte address given by the + "byte_address" parameter and of "byte_count" bytes in length is formatted + into a NUL-terminated character string and then translated using the lookup + table given by the "table_address" parameter. A pointer to the string is + returned. + + + Implementation notes: + + 1. This routine accesses the memory array directly to avoid tracing the + memory reads if debug tracing is enabled. + + 2. The routine will not return a string longer than 256 characters. +*/ + +char *fmt_translated_byte_operand (uint32 byte_address, uint32 byte_count, uint32 table_address) +{ +char *bptr, *cptr; +uint32 index; + +bptr = fmt_byte_operand (byte_address, byte_count); /* format the byte string */ + +cptr = bptr; /* point at the start of the buffer */ + +while (byte_count-- > 0) { /* while there are bytes to translate */ + index = table_address + *cptr; /* index into the translation table */ + + if (index & 1) /* if the translated byte address is odd */ + *cptr++ = LOWER_BYTE (M [index / 2]); /* then copy the lower byte from the table */ + else /* otherwise */ + *cptr++ = UPPER_BYTE (M [index / 2]); /* copy the upper byte from the table */ + } + +return bptr; /* return a pointer to the translated operand */ +} + + +/* Format a BCD operand. + + The BCD numeric string starting at the absolute byte address given by the + "byte_address" parameter and of "digit_count" BCD digits in length is + formatted into a NUL-terminated character string and then reformatted into a + local buffer as a hexadecimal character string. A pointer to the buffer is + returned. + + The digit count does not include the numeric sign, located in the four bits + following the last digit. If the digit count is even, the left-half of the + first byte is unused, as BCD strings always end in the right-half of the last + byte. + + + Implementation notes: + + 1. The digit count is assumed to be 32 or less, as HP 3000 BCD ("packed + decimal") numbers may not contain more than 28 digits. +*/ + + +char *fmt_bcd_operand (uint32 byte_address, uint32 digit_count) +{ +static char hex [] = "0123456789ABCDEF"; +static char buffer [33]; +uint32 byte_count; +char *bptr, *cptr; + +if (digit_count > 32) /* if the operand is too long */ + return "(invalid)"; /* then return an error indication */ + +byte_count = digit_count / 2 + 1; /* convert from a digit to a byte count */ +bptr = fmt_byte_operand (byte_address, byte_count); /* and format the byte string */ + +cptr = buffer; /* point at the start of the buffer */ + +if ((digit_count & 1) == 0) { /* if the digit count is even */ + *cptr++ = hex [LOWER_HALF (*bptr++)]; /* then the BCD string starts with */ + byte_count--; /* the lower half of the first byte */ + } + +while (byte_count-- > 0) { /* while there are digits to format */ + *cptr++ = hex [UPPER_HALF (*bptr)]; /* format and copy the digit in the upper half */ + *cptr++ = hex [LOWER_HALF (*bptr++)]; /* followed by the digit in the lower half */ + } + +*cptr = '\0'; /* add a trailing NUL */ + +return buffer; /* return a pointer to the formatted operand */ +} diff --git a/HP3000/hp3000_mem.h b/HP3000/hp3000_mem.h new file mode 100644 index 00000000..b17238d2 --- /dev/null +++ b/HP3000/hp3000_mem.h @@ -0,0 +1,162 @@ +/* hp3000_mem.h: HP 3000 memory subsystem interface declarations + + Copyright (c) 2016, J. David Bryan + + 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 + AUTHOR 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. + + 10-Oct-16 JDB Created + + + This file contains declarations used by the CPU, I/O Processor, Multiplexer + Channel, and Selector Channel to interface with the HP 3000 memory subsystem. +*/ + + + +/* Debug flags. + + + Implementation notes: + + 1. Memory debug flags are allocated in descending order, as they may be used + by other modules (e.g., CPU) that allocate their own flags in ascending + order. No check is made for overlapping values. +*/ + +#define DEB_MDATA (1u << 31) /* trace memory reads and writes */ +#define DEB_MFETCH (1u << 30) /* trace memory instruction fetches */ +#define DEB_MOPND (1u << 29) /* trace memory operand accesses */ + + +/* Architectural constants. + + The type used to represent a main memory word value is defined. An array of + this type is used to simulate the CPU main memory. + + + Implementation notes: + + 1. The MEMORY_WORD type is a 16-bit unsigned type, corresponding with the + 16-bit main memory in the HP 3000. Unlike the general data type, which + is a 32-bit type for speed, main memory does not benefit from the faster + 32-bit execution on IA-32 processors, as only one instruction in the + cpu_read_memory and cpu_write_memory routines has an operand override + that invokes the slower instruction fetch path. There is a negligible + difference in the Memory Pattern Test diagnostic execution speeds for the + uint32 vs. uint16 definition, whereas the VM requirements are doubled for + the former. +*/ + +typedef uint16 MEMORY_WORD; /* HP 16-bit memory word representation */ + + +/* Byte accessors. + + The HP 3000 is a word-addressable machine. Byte addressing is implemented by + assuming that a memory of N physical words may be addressed as 2N bytes. The + "byte-capable" machine instructions use "relative byte addresses" that are + used to obtain absolute word addresses by dividing by two and then accessing + the upper or lower byte of the resulting word, depending on the LSB of the + byte address. + + In simulation, this module provides a byte access structure and a set of + routines that read or write the next byte in ascending byte-offset order. + The structure is initialized with the starting byte offset from a specified + base register value and then is passed as a parameter to the other routines, + which update the fields accordingly for the access requested. This relieves + the caller from having to manage the continual logical-to-physical address + translation, word buffering, byte selection, etc. + + Byte accessors are also used to provide debug traces of byte operands in + memory. Initializing an accessor sets a field containing the absolute byte + memory address; this address may be passed to the byte formatters to print + the operand. + + In most cases, operands are defined by starting byte addresses and byte + counts. However, some operands (e.g., EDIT instruction operands) are + delineated only by the extents of the accesses. For these operands, byte + accessors maintain the lowest byte addresses and offsets actually accessed, + as well as the lengths of the extent of the accesses. +*/ + +typedef struct { /* byte access descriptor */ + HP_WORD *byte_offset; /* relative byte offset of the next byte */ + HP_WORD data_word; /* memory data word containing the current byte */ + ACCESS_CLASS class; /* memory access classification */ + uint32 word_address; /* logical word address containing the next byte */ + t_bool write_needed; /* TRUE if the data word must be written to memory */ + uint32 count; /* current count of bytes accessed */ + uint32 length; /* (trace) length of extent of access */ + uint32 initial_byte_address; /* (trace) initial absolute byte address */ + uint32 initial_byte_offset; /* (trace) initial relative byte offset */ + uint32 first_byte_address; /* (trace) lowest absolute byte address accessed */ + uint32 first_byte_offset; /* (trace) lowest relative byte offset accessed */ + } BYTE_ACCESS; + + +/* Memory global SCP support routines */ + +t_stat mem_examine (t_value *eval_array, t_addr address, UNIT *uptr, int32 switches); +t_stat mem_deposit (t_value value, t_addr address, UNIT *uptr, int32 switches); + + +/* Global memory functions. + + mem_initialize : allocate main memory + mem_is_empty : check for a non-zero value within a range of memory locations + mem_fill : set all memory locations to a specified value + + mem_read : read a word from main memory + mem_write : write a word to main memory + + mem_init_byte : initialize a memory byte access structure + mem_set_byte : set the access structure to a new byte offset + mem_lookup_byte : return a byte at a specified index in a table + mem_read_byte : read the next byte from memory + mem_write_byte : write the next byte to memory + mem_modify_byte : replace the last byte written to memory + mem_post_byte : post the word containing the last byte modified in place to memory + mem_update_byte : rewrite the word containing the last byte written to memory + + fmt_byte_operand : format a byte operand in memory into a character string + fmt_bcd_operand : format a BCD operand in memory into a character string +*/ + +extern t_bool mem_initialize (uint32 memory_size); +extern t_bool mem_is_empty (uint32 starting_address); +extern void mem_fill (uint32 starting_address, HP_WORD fill_value); + +extern t_bool mem_read (DEVICE *dptr, ACCESS_CLASS classification, uint32 offset, HP_WORD *value); +extern t_bool mem_write (DEVICE *dptr, ACCESS_CLASS classification, uint32 offset, HP_WORD value); + +extern void mem_init_byte (BYTE_ACCESS *bap, ACCESS_CLASS class, HP_WORD *byte_offset, uint32 block_length); +extern void mem_set_byte (BYTE_ACCESS *bap); +extern uint8 mem_lookup_byte (BYTE_ACCESS *bap, uint8 index); +extern uint8 mem_read_byte (BYTE_ACCESS *bap); +extern void mem_write_byte (BYTE_ACCESS *bap, uint8 byte); +extern void mem_modify_byte (BYTE_ACCESS *bap, uint8 byte); +extern void mem_post_byte (BYTE_ACCESS *bap); +extern void mem_update_byte (BYTE_ACCESS *bap); + +extern char *fmt_byte_operand (uint32 byte_address, uint32 byte_count); +extern char *fmt_translated_byte_operand (uint32 byte_address, uint32 byte_count, uint32 table_address); +extern char *fmt_bcd_operand (uint32 byte_address, uint32 digit_count); diff --git a/HP3000/hp3000_mpx.c b/HP3000/hp3000_mpx.c index b4046140..338e1209 100644 --- a/HP3000/hp3000_mpx.c +++ b/HP3000/hp3000_mpx.c @@ -25,6 +25,7 @@ MPX HP 3000 Series III Multiplexer Channel + 24-Oct-16 JDB Renamed SEXT macro to SEXT16 12-Sep-16 JDB Changed DIB register macro usage from SRDATA to DIB_REG 15-Jul-16 JDB Fixed the word count display for DREADSTB trace 08-Jun-16 JDB Corrected %d format to %u for unsigned values @@ -322,12 +323,23 @@ #include "hp3000_defs.h" -#include "hp3000_cpu.h" #include "hp3000_cpu_ims.h" #include "hp3000_io.h" +#include "hp3000_mem.h" +/* IOP device */ + +extern DEVICE iop_dev; /* I/O Processor */ + + +/* Memory access macros */ + +#define iop_read_memory(c,o,v) mem_read (&iop_dev, c, o, v) +#define iop_write_memory(c,o,v) mem_write (&iop_dev, c, o, v) + + /* Program constants. The multiplexer channel clock period is 175 nanoseconds. The channel runs @@ -1230,8 +1242,8 @@ while (cycles > 0) { /* execute as long as cy else /* otherwise */ inbound_signals = NO_SIGNALS; /* no acknowledgement is needed */ - cpu_read_memory (absolute_iop, addr_reg, &iocw); /* fetch the IOCW from memory */ - cycles = cycles - CYCLES_PER_READ; /* and count the memory access */ + iop_read_memory (absolute, addr_reg, &iocw); /* fetch the IOCW from memory */ + cycles = cycles - CYCLES_PER_READ; /* and count the memory access */ order_reg = IOCW_ORDER (iocw); /* get the translated order from the IOCW */ @@ -1326,25 +1338,25 @@ while (cycles > 0) { /* execute as long as cy break; } - if (store_ioaw == FALSE) { /* if a fetch is needed */ - cpu_read_memory (absolute_iop, addr_reg, &ioaw); /* then load the IOAW from memory */ - cycles = cycles - CYCLES_PER_READ; /* and count the memory access */ + if (store_ioaw == FALSE) { /* if a fetch is needed */ + iop_read_memory (absolute, addr_reg, &ioaw); /* then load the IOAW from memory */ + cycles = cycles - CYCLES_PER_READ; /* and count the memory access */ dprintf (mpx_dev, DEB_PIO, "Channel SR %u loaded IOAW %06o from address %06o\n", srn, ioaw, addr_reg); } - else /* otherwise provide a dummy value */ - ioaw = 0; /* that will be overwritten */ + else /* otherwise provide a dummy value */ + ioaw = 0; /* that will be overwritten */ - if (inbound_signals) /* if there are signals to assert */ - outbound = dibptr->io_interface (dibptr, /* then pass them to the interface */ + if (inbound_signals) /* if there are signals to assert */ + outbound = dibptr->io_interface (dibptr, /* then pass them to the interface */ inbound_signals, ioaw); - if (store_ioaw == TRUE) { /* if a store is needed */ - ioaw = IODATA (outbound); /* then set the IOAW from the returned value */ - cpu_write_memory (absolute_iop, addr_reg, ioaw); /* and store it in memory */ - cycles = cycles - CYCLES_PER_WRITE; /* count the memory access */ + if (store_ioaw == TRUE) { /* if a store is needed */ + ioaw = IODATA (outbound); /* then set the IOAW from the returned value */ + iop_write_memory (absolute, addr_reg, ioaw); /* and store it in memory */ + cycles = cycles - CYCLES_PER_WRITE; /* count the memory access */ dprintf (mpx_dev, DEB_PIO, "Channel SR %u stored IOAW %06o to address %06o\n", srn, ioaw, addr_reg); @@ -1393,15 +1405,15 @@ while (cycles > 0) { /* execute as long as cy outbound = dibptr->io_interface (dibptr, inbound_signals, 0); - if (sio_order != sioJUMP /* if we're not completing */ - && (sio_order != sioJUMPC || (outbound & JMPMET) == 0)) { /* a successful jump order */ - cpu_read_memory (absolute_iop, IODATA (outbound), &addr_reg); /* then get the I/O program pointer */ - cycles = cycles - CYCLES_PER_READ; /* and count the memory access */ + if (sio_order != sioJUMP /* if we're not completing */ + && (sio_order != sioJUMPC || (outbound & JMPMET) == 0)) { /* a successful jump order */ + iop_read_memory (absolute, IODATA (outbound), &addr_reg); /* then get the I/O program pointer */ + cycles = cycles - CYCLES_PER_READ; /* and count the memory access */ } - cpu_write_memory (absolute_iop, IODATA (outbound), /* write the updated program pointer */ - addr_reg + 2 & R_MASK); /* back to the DRT */ - cycles = cycles - CYCLES_PER_WRITE; /* and count the access */ + iop_write_memory (absolute, IODATA (outbound), /* write the updated program pointer */ + addr_reg + 2 & R_MASK); /* back to the DRT */ + cycles = cycles - CYCLES_PER_WRITE; /* and count the access */ break; @@ -1409,9 +1421,9 @@ while (cycles > 0) { /* execute as long as cy case State_D: inbound_data = 0; /* assume there is no inbound data */ - if (sio_order == sioSBANK) { /* if this is a Set Bank order */ - cpu_read_memory (absolute_iop, addr_reg, &ioaw); /* then read the IOAW */ - cycles = cycles - CYCLES_PER_READ; /* and count the memory access */ + if (sio_order == sioSBANK) { /* if this is a Set Bank order */ + iop_read_memory (absolute, addr_reg, &ioaw); /* then read the IOAW */ + cycles = cycles - CYCLES_PER_READ; /* and count the memory access */ dprintf (mpx_dev, DEB_PIO, "Channel SR %u loaded IOAW %06o from address %06o\n", srn, ioaw, addr_reg); @@ -1449,7 +1461,7 @@ while (cycles > 0) { /* execute as long as cy else /* otherwise */ inbound_signals |= EOT | TOGGLEOUTXFER; /* assert EOT and end the transfer */ - if (cpu_read_memory (dma_iop, /* read the word from memory */ + if (iop_read_memory (dma, /* read the word from memory */ TO_PA (AUX_BANK (aux_reg), addr_reg), /* at the indicated bank and offset */ &inbound_data)) /* if the read succeeds */ cycles = cycles - CYCLES_PER_READ; /* then count the memory access */ @@ -1467,10 +1479,10 @@ while (cycles > 0) { /* execute as long as cy if (device_end == SET) { /* if the transfer was aborted by the interface */ outbound_data = IODATA (outbound); /* then it returned the DRT program pointer address */ - cpu_read_memory (absolute_iop, outbound_data, &addr_reg); /* do the I/O program pointer fetch here */ - cpu_write_memory (absolute_iop, outbound_data, /* so we don't have to do State C */ + iop_read_memory (absolute, outbound_data, &addr_reg); /* do the I/O program pointer fetch here */ + iop_write_memory (absolute, outbound_data, /* so we don't have to do State C */ addr_reg + 2 & R_MASK); - cycles = cycles - CYCLES_PER_READ - CYCLES_PER_WRITE; /* count the two memory accesses */ + cycles = cycles - CYCLES_PER_READ - CYCLES_PER_WRITE; /* count the two memory accesses */ if (cntr_reg == CNTR_MAX) /* if the word count is now exhausted */ if (order_reg & ORDER_DC) /* then if the order is chained */ @@ -1498,7 +1510,7 @@ while (cycles > 0) { /* execute as long as cy else { /* otherwise the transfer succeeded */ if (sio_order == sioREAD || sio_order == sioREADC) /* if this is a Read or Read Chained order */ - if (cpu_write_memory (dma_iop, /* then write the word to memory */ + if (iop_write_memory (dma, /* then write the word to memory */ TO_PA (AUX_BANK (aux_reg), addr_reg), /* at the indicated bank and offset */ IODATA (outbound))) /* if the write succeeds */ cycles = cycles - CYCLES_PER_WRITE; /* then count the memory access */ @@ -1739,7 +1751,7 @@ while (working_set) { dprintf (mpx_dev, DEB_CSRW, "Order register value %02o (%s) " "and counter register value %d returned\n", order_reg & ORDER_MASK, sio_order_name [IOCW_ORDER (outbound_value)], - SEXT (IOCW_COUNT (outbound_value))); + SEXT16 (IOCW_COUNT (outbound_value))); } if (control_word & CN_ADDR_RAM) { /* if the address register is selected */ diff --git a/HP3000/hp3000_release.txt b/HP3000/hp3000_release.txt index 3fd5131f..02d5038b 100644 --- a/HP3000/hp3000_release.txt +++ b/HP3000/hp3000_release.txt @@ -1,6 +1,6 @@ SIMH/HP 3000 RELEASE NOTES ========================== - Last update: 2016-09-20 + Last update: 2017-01-08 This file documents the release history of the Hewlett-Packard 3000 simulator. @@ -158,6 +158,149 @@ the MPE version used: +===================== +Release 4, 2017-01-08 +===================== + +This release of the HP 3000 simulator adds the following features: + + - The HP 32234A COBOL II Extended Instruction Set firmware is now available. + The new SET CPU CIS option enables the firmware. + + - Subprograms in memory associated with the EDIT instruction may be examined + symbolically with the -E switch. + + - The new CPU "OPND" trace option traces memory byte operands. + + - The new CPU "EXEC" trace option turns on full tracing for instructions + that match a value specified by the new "SET CPU EXEC={;}" + command. + + - The diagnostics coverage is extended to the COBOL II firmware. + + +-------------------- +Implementation Notes +-------------------- + + - The MPE-V/R software kit has been updated to add the COBOL II runtime + routines to the system SL and COBOL example programs to the OPERATOR.SYS + account. The startup command files also enable the COBOL II instruction set. + + - New "hp3000_cpu_cis.c" and "hp3000_mem.c" modules have been added. + + - For this release, checking for interrupts is not performed during execution + of the COBOL II EDIT, TR, CMPS, and CMPT instructions. A future release will + add interruptibility to these instructions to comply with their hardware + behavior. + + - The new OPND trace option does not currently trace byte operands for + instructions in the base set (e.g., MOVB or CMPB). Operands for the + COBOL II firmware instructions are fully covered. + + - The command-line switch for the EXAMINE command to request display in + status-register format has been changed from "-S" to "-T" to avoid conflict + with the "-S" switch used to indicate an address offset from SBANK. + + +---------- +Bugs Fixed +---------- + + 1. PROBLEM: SETR prints a base register trace when values are not changed. + + VERSION: Release 3 + + OBSERVATION: The SETR instruction may be used to change any combination of + the SBANK, DB, DL, Z, STA, X, Q, and SM register values. If the REG trace + is active, the base register values will be printed after the instruction + completes. This occurs whether or not the base register values were + actually changed. In particular, the CPU diagnostic uses the SETR + instruction to flush the stack to memory without changing any base + registers. The REG trace in this case is unnecessary. + + CAUSE: The "cpu_base_changed" flag is set unconditionally when the + instruction completes. It should be set only if the SETR instruction + specifies one or more base registers to change. + + RESOLUTION: Modify "cpu_move_spec_fw_imm_field_reg_op" (hp3000_cpu_base.c) + to set the "cpu_base_changed" flag only if one or more base register change + bits are set in the instruction operand field. + + STATUS: Fixed in Release 4. + + + 2. PROBLEM: Invalid bank and offset values are accepted for address entry. + + VERSION: Release 3 + + OBSERVATION: Bank-offset addresses with out-of-range the bank or offset + values, e.g., EXAMINE 30.0 and EXAMINE 0.1777777, are accepted without + complaint. The bank value is taken modulo 20, and the higher order bits of + the offset value are merged into the bank number. Values out of range + should be rejected with errors. + + CAUSE: Incomplete range verification. + + RESOLUTION: Modify "parse_addr" (hp3000_sys.c) to check the parsed bank + and offset values against their respective maximums and return an "Invalid + argument" error if either is exceeded. + + STATUS: Fixed in Release 4. + + + 3. PROBLEM: The "-S" (SBANK-offset) switch displays values in status-register + format. + + VERSION: Release 3 + + OBSERVATION: The HP 3000 User's Manual states that adding the "-S" switch + to the EXAMINE command implies that the offset is from the bank number in + the SBANK register. The example given, "EXAMINE -S ", should + display the memory data value at the address . in + octal format. Instead, it displays the value in status-register format. + + CAUSE: The "-S" switch is used for both SBANK and STA formats. Section + 2.1.3 says that -S means that "The implied bank number is obtained from + SBANK." Section 2.1.2 says that -S means that "A CPU status mnemonic" is + being displayed. For EXAMINE -S, the latter interpretation causes the + expected octal value to be displayed in status-register format. + + RESOLUTION: Modify "fprint_sym" (hp3000_sys.c) to use the "-T" switch to + designate status-register format. Modify hp3000_sys.c, hp3000_cpu.c, and + hp3000_defs.h to rename the "REG_S" format indicator to "REG_T" for + consistency with the switch change. + + STATUS: Fixed in Release 4. + + + 4. PROBLEM: SCAL 0 and PCAL 0 instructions fail when a stack overflow occurs. + + VERSION: Release 3 + + OBSERVATION: The SCAL 0 and PCAL 0 instructions transfer control via + subroutine or procedure calls, respectively, through program labels + residing on the top of the stack. If a stack overflow occurs during + instruction execution, the stack overflow trap handler is called to enlarge + the stack, and the instruction is reexecuted. However, the program label + has been lost, so control transfers to a random location. + + CAUSE: The instructions obtain the label and then delete the TOS, flush + the rest of the stack registers to memory, and then check that SM <= Z, + i.e., that the current top of the stack in memory does not exceed the + stack limit. If SM > Z, a stack overflow has occurred, and the trap + handler is called. However, the label has not been restored to the stack, + so when the instruction is reexecuted after the stack is enlarged, the + wrong value is pulled from the TOS. + + RESOLUTION: Modify "cpu_io_cntl_prog_imm_mem_op" SCAL and PCAL executors + (hp3000_cpu_base.c) to push the label back onto the stack before taking the + stack overflow trap. + + STATUS: Fixed in Release 4. + + + ===================== Release 3, 2016-09-20 ===================== diff --git a/HP3000/hp3000_sel.c b/HP3000/hp3000_sel.c index 4a9053a6..d38ce12f 100644 --- a/HP3000/hp3000_sel.c +++ b/HP3000/hp3000_sel.c @@ -25,6 +25,8 @@ SEL HP 3000 Series III Selector Channel + 10-Oct-16 JDB Renumbered debug flags to start at 0 + Added port_read_memory, port_write_memory macros 11-Jul-16 JDB Change "sel_unit" from a UNIT to an array of one UNIT 30-Jun-16 JDB Reestablish active_dib pointer during sel_initialize 08-Jun-16 JDB Corrected %d format to %u for unsigned values @@ -289,9 +291,9 @@ #include "hp3000_defs.h" -#include "hp3000_cpu.h" #include "hp3000_cpu_ims.h" #include "hp3000_io.h" +#include "hp3000_mem.h" @@ -364,18 +366,18 @@ static const char *const action_name [] = { /* indexed by SEQ_STATE */ }; -/* Debug flags. +/* Debug flags */ + +#define DEB_CSRW (1u << 0) /* trace channel command initiations and completions */ +#define DEB_PIO (1u << 1) /* trace programmed I/O commands */ +#define DEB_STATE (1u << 2) /* trace state changes */ +#define DEB_SR (1u << 3) /* trace service requests */ - Implementation notes: +/* Memory access macros */ - 1. Bit 0 is reserved for the memory data trace flag. -*/ - -#define DEB_CSRW (1u << 1) /* trace channel command initiations and completions */ -#define DEB_PIO (1u << 2) /* trace programmed I/O commands */ -#define DEB_STATE (1u << 3) /* trace state changes */ -#define DEB_SR (1u << 4) /* trace service requests */ +#define port_read_memory(c,o,v) mem_read (&sel_dev, c, o, v) +#define port_write_memory(c,o,v) mem_write (&sel_dev, c, o, v) /* Channel global state */ @@ -600,8 +602,8 @@ if (sel_is_idle) { /* if the channel is idl active_dib = dibptr; /* save the interface's DIB pointer */ device_number = dibptr->device_number; /* and set the device number register */ - cpu_read_memory (absolute_sel, device_number * 4, /* read the initial program counter from the DRT */ - &program_counter); + port_read_memory (absolute, device_number * 4, /* read the initial program counter from the DRT */ + &program_counter); } else { /* otherwise abort the transfer in progress */ @@ -935,7 +937,7 @@ while (sel_request && cycles > 0) { /* execute as long as a outbound_data = IODATA (outbound); /* get the status or residue to return */ return_address = program_counter - 1 & LA_MASK; /* point at the second of the program words */ - cpu_write_memory (absolute_sel, return_address, outbound_data); /* save the word */ + port_write_memory (absolute, return_address, outbound_data); /* save the word */ cycles = cycles - CYCLES_PER_WRITE; /* and count the access */ dprintf (sel_dev, DEB_PIO, "Channel stored IOAW %06o to address %06o\n", @@ -1029,9 +1031,9 @@ while (sel_request && cycles > 0) { /* execute as long as a } else { /* otherwise it's a Write or Write Chained order */ - if (cpu_read_memory (dma_sel, /* if the memory read */ - TO_PA (bank, address_word), /* from the specified bank and offset */ - &input_buffer)) { /* succeeds */ + if (port_read_memory (dma, /* if the memory read */ + TO_PA (bank, address_word), /* from the specified bank and offset */ + &input_buffer)) { /* succeeds */ cycles = cycles - CYCLES_PER_READ; /* then count the access */ inbound_data = input_buffer; /* get the word to supply */ @@ -1098,9 +1100,9 @@ while (sel_request && cycles > 0) { /* execute as long as a || order == sioREADC) { /* and if this is a Read or Read Chained order */ output_buffer = IODATA (outbound); /* then pick up the returned data word */ - if (cpu_write_memory (dma_sel, /* if the memory write */ - TO_PA (bank, address_word), /* to the specified bank and offset */ - output_buffer)) /* succeeds */ + if (port_write_memory (dma, /* if the memory write */ + TO_PA (bank, address_word), /* to the specified bank and offset */ + output_buffer)) /* succeeds */ cycles = cycles - CYCLES_PER_WRITE; /* then count the access */ else { /* otherwise the memory write failed */ @@ -1264,8 +1266,8 @@ return SCPE_OK; static void end_channel (DIB *dibptr) { -cpu_write_memory (absolute_sel, device_number * 4, /* write the program counter back to the DRT */ - program_counter); +port_write_memory (absolute, device_number * 4, /* write the program counter back to the DRT */ + program_counter); dibptr->service_request = FALSE; /* clear any outstanding device service request */ @@ -1302,7 +1304,7 @@ return active_dib->io_interface (active_dib, XFERERROR | CHANSO, 0); /* tell static void load_control (HP_WORD *value) { -cpu_read_memory (absolute_sel, program_counter, value); /* read the IOCW from memory */ +port_read_memory (absolute, program_counter, value); /* read the IOCW from memory */ dprintf (sel_dev, DEB_PIO, "Channel %s IOCW %06o (%s) from address %06o\n", action_name [sequencer], *value, @@ -1324,7 +1326,7 @@ return; static void load_address (HP_WORD *value) { -cpu_read_memory (absolute_sel, program_counter, value); /* read the IOAW from memory */ +port_read_memory (absolute, program_counter, value); /* read the IOAW from memory */ dprintf (sel_dev, DEB_PIO, "Channel %s IOAW %06o from address %06o\n", action_name [sequencer], *value, program_counter); diff --git a/HP3000/hp3000_sys.c b/HP3000/hp3000_sys.c index 8c1bcba3..f85c1faf 100644 --- a/HP3000/hp3000_sys.c +++ b/HP3000/hp3000_sys.c @@ -23,9 +23,16 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. + 29-Dec-16 JDB Changed the switch for STA format from -S to -T; + changed the status mnemonic flag from REG_S to REG_T + 28-Nov-16 JDB hp_device_conflict accumulates names of active traces only + Improved parse_addr error detection + 26-Oct-16 JDB Added "fprint_edit" to print EDIT subprogram mnemonics + 27-Sep-16 JDB Added COBOL firmware mnemonics + Modified "fprint_instruction" to handle two-word instructions 15-Sep-16 JDB Modified "one_time_init" to set aux_cmds "message" field 03-Sep-16 JDB Added the STOP_POWER and STOP_ARSINH messages - 01-Sep-16 JDB Moved the hp_cold_cmd routine to the CPU (as cpu_cold_cmd) + 01-Sep-16 JDB Moved the "hp_cold_cmd" routine to the CPU (as "cpu_cold_cmd") Added the POWER command 03-Aug-16 JDB Improved "fmt_char" and "fmt_bitset" to allow multiple calls 15-Jul-16 JDB Fixed the word count display for SIO read/write orders @@ -63,13 +70,13 @@ #include "hp3000_defs.h" #include "hp3000_cpu.h" #include "hp3000_cpu_ims.h" +#include "hp3000_mem.h" #include "hp3000_io.h" /* External I/O data structures */ -extern DEVICE cpu_dev; /* Central Processing Unit */ extern DEVICE iop_dev; /* I/O Processor */ extern DEVICE mpx_dev; /* Multiplexer Channel */ extern DEVICE sel_dev; /* Selector Channel */ @@ -124,11 +131,35 @@ typedef enum { right-justified in the instruction word, i.e., extend from bits n-15, unless otherwise noted. - 2. Operand masks for signed values must include both signs and magnitudes. + 2. Operands for one-word instructions are in the lower (first) word; + operands for two-word instructions are in the upper (second) word. + + 3. Operand masks for signed values must include both signs and magnitudes. + + 4. Operand masks are defined as the complements of the operand bits to make + it easier to see which bits will be cleared when the value is ANDed. The + complements are calculated at compile-time and so impose no run-time + penalty. + + 5. The base set "move" instructions contain S-decrement fields that indicate + the counts of parameters to remove from the stack when the instructions + complete. Certain "decimal arithmetic" instructions of the optional + Extended Instruction Set and certain "numeric conversion and load" + instructions of the optional COBOL II Extended Instruction Set contain + one or two S-decrement bits, but these encode the stack adjustment rather + than expressing the adjustment directly. + + The SPL "ASMB" statement accepts the EIS instructions with the stack + adjustment given as the encoded value, e.g., CVAD 0 and CVAD 1 are + accepted, with the former deleting two words and the latter deleting four + words from the stack. Therefore, all S-decrement operands are printed + and parsed as their direct field values, rather than decoding and + encoding the actual decrements. */ typedef enum { opNone, /* no operand */ + opB15, /* PB/DB base bit 15 */ opU1, /* unsigned value range 0-1 */ opU1515, /* unsigned value pair range 0-15 */ opU63, /* unsigned value range 0-63 */ @@ -141,8 +172,9 @@ typedef enum { opPS255, /* P +/- displacement range 0-255 */ opPU255, /* P unsigned displacement range 0-255 */ opPS255IX, /* P +/- displacement range 0-255, indirect bit 5, index bit 4 */ - opS, /* S decrement bit 11 */ - opSCS, /* sign control bits 9-10, S decrement bit 11 */ + opS11, /* S decrement selection bit 11 */ + opS15, /* S decrement selection bit 15 */ + opSCS3, /* sign control bits 9-10, S decrement bit 11 */ opSU2, /* S decrement range 0-2 bits 10-11 */ opSU3, /* S decrement range 0-3 */ opSU3B, /* S decrement range 0-3, base bit 11 */ @@ -151,34 +183,46 @@ typedef enum { opSU15, /* S decrement range 0-15 */ opD255IX, /* DB+/Q+/Q-/S- displacements, indirect bit 5, index bit 4 */ opPD255IX, /* P+/P-/DB+/Q+/Q-/S- displacements, indirect bit 5, index bit 4 */ - opX /* index bit 4 */ + opX, /* index bit 4 */ + + opB31, /* second word: PB/DB base bit 15 */ + opS31, /* second word: S decrement selection bit 15 */ + opCC7, /* second word: condition code flags bits 13-15 */ + opSCS4 /* second word: sign control bits 12-14, S decrement bit 15 */ } OP_TYPE; static const t_value op_mask [] = { /* operand masks, indexed by OP_TYPE */ - 0177777, /* opNone */ - 0177776, /* opU1 */ - 0177400, /* opU1515 */ - 0177700, /* opU63 */ - 0173700, /* opU63X */ - 0177400, /* opU255 */ - 0177760, /* opC15 */ - 0177400, /* opR255L */ - 0177400, /* opR255R */ - 0173700, /* opPS31I */ - 0177000, /* opPS255 */ - 0177400, /* opPU255 */ - 0171000, /* opPS255IX */ - 0177757, /* opS */ - 0177617, /* opSCS */ - 0177717, /* opSU2 */ - 0177774, /* opSU3 */ - 0177754, /* opSU3B */ - 0177740, /* opSU3NAS */ - 0177770, /* opSU7 */ - 0177760, /* opSU15 */ - 0171000, /* opD255IX */ - 0170000, /* opPD255IX */ - 0173777 /* opX */ + ~0000000u, /* opNone */ + ~0000001u, /* opB15 */ + ~0000001u, /* opU1 */ + ~0000377u, /* opU1515 */ + ~0000077u, /* opU63 */ + ~0004077u, /* opU63X */ + ~0000377u, /* opU255 */ + ~0000017u, /* opC15 */ + ~0000377u, /* opR255L */ + ~0000377u, /* opR255R */ + ~0004077u, /* opPS31I */ + ~0000777u, /* opPS255 */ + ~0000377u, /* opPU255 */ + ~0006777u, /* opPS255IX */ + ~0000020u, /* opS11 */ + ~0000001u, /* opS15 */ + ~0000160u, /* opSCS3 */ + ~0000060u, /* opSU2 */ + ~0000003u, /* opSU3 */ + ~0000023u, /* opSU3B */ + ~0000037u, /* opSU3NAS */ + ~0000007u, /* opSU7 */ + ~0000017u, /* opSU15 */ + ~0006777u, /* opD255IX */ + ~0007777u, /* opPD255IX */ + ~0004000u, /* opX */ + + ~TO_DWORD (0000001u, 0000000u), /* opB31 */ + ~TO_DWORD (0000001u, 0000000u), /* opS31 */ + ~TO_DWORD (0000007u, 0000000u), /* opCC7 */ + ~TO_DWORD (0000017u, 0000000u) /* opSCS4 */ }; @@ -201,18 +245,18 @@ static const t_value op_mask [] = { /* operand masks, indexed by OP_ the first (primary) part of the table contains the appropriate number of classification elements. This allows rapid access to a specific instruction based on its bit pattern. In this primary part, the reserved bits masks are - not used. + not defined or used. - If some instructions in a class have reserved bits, or if the sub-opcode - decoding is not regular, the second (secondary) part of the table contains - classification elements that specify reserved bits masks. This part is - searched linearly. + If some instructions in a class have reserved bits, if the sub-opcode + decoding is not regular, or if the instruction requires two words to decode, + then the second (secondary) part of the table contains classification + elements that specify reserved bits masks. This part is searched linearly. As an example, the stack instructions have bits 0-3 = 0. The remaining twelve bits are broken into two six-bit fields. Each field encodes one of 64 - stack operations (actually, 63 operations, as one is reserved). As the stack - operations are fully decoded, the table consists only of 64 primary elements, - corresponding one-for-one to the 64 operations. + stack operations (actually, 63 operations, as one is unassigned). As the + stack operations are fully decoded, the table consists only of 64 primary + elements, corresponding one-for-one to the 64 operations. As as contrasting example, the shift and branch operations have bits 0-3 = 1 and are fully decoded by bits 5-9, except for two instructions that have @@ -238,6 +282,22 @@ static const t_value op_mask [] = { /* operand masks, indexed by OP_ opcode mnemonic in lowercase as an indicator that it is not the canonical representation. + Two-word instructions are, by definition, never decoded by a primary opcode, + so they will always be found in the secondary entries of a classification + table with 32-bit opcode and reserved bits mask values. The opcode value + contains the first word of the instruction in the lower half and the second + word in the upper half. The lower half of the reserved bits mask value will + have all bits set, and the upper half will have all bits set except for those + corresponding to reserved bits in the second instruction word (if any). A + two-word entry, therefore, may be identified by a mask value with a non-zero + upper half. + + A secondary entry with a zero-length mnemonic may be used to match the first + word of a two-word instruction if none of the second words match earlier + entries. This ensures that unimplemented two-word instructions are printed + as a two-word octal value rather than as a one-word octal value followed by + the mnemonic for a one-word instruction. + The end of an operations table is indicated by a NULL mnemonic pointer. */ @@ -456,8 +516,12 @@ static const OP_TABLE sbb_ops = { IXIT = 0000, PCN = nnn0, LOCK = nn01, and UNLK = nn11, where n is any collective value other than 0. - 2. By convention, SIMH simulators always decode all supported instructions, - regardless of whether or not they are enabled by currents CPU firmware + 2. The secondary entry with the empty mnemonic ensures that an unimplemented + two-word instruction is printed as "020477,000001" (e.g.) rather than + "020477" followed by "NOP,DELB". + + 3. By convention, SIMH simulators always decode all supported instructions, + regardless of whether or not they are enabled by current CPU firmware configurations. So the EIS, APL, and COBOL-II instructions are present in the table and are not conditional on the CPU options set. */ @@ -506,19 +570,40 @@ static const OP_TABLE msfifr_ops = { { "pcn", 0020360, opNone, 0177761 }, /* decodes bits 12-15 as nnn0 */ { "UNLK", 0020363, opNone, 0177777 }, { "unlk", 0020363, opNone, 0177763 }, /* decodes bits 12-15 as nn11 */ + { "EADD", 0020410, opNone, 0177777 }, { "ESUB", 0020411, opNone, 0177777 }, { "EMPY", 0020412, opNone, 0177777 }, { "EDIV", 0020413, opNone, 0177777 }, { "ENEG", 0020414, opNone, 0177777 }, { "ECMP", 0020415, opNone, 0177777 }, + + { "ALGN", 0020460, opS15, 0177777 }, + { "ABSN", 0020462, opS15, 0177777 }, + { "EDIT", 0020470, opB15, 0177777 }, + { "CMPS", 0020472, opB15, 0177777 }, + { "XBR", 0020474, opNone, 0177777 }, + { "PARC", 0020475, opNone, 0177777 }, + { "ENDP", 0020476, opNone, 0177777 }, + + { "CMPT", TO_DWORD (0000006, 0020477), opB31, D32_MASK }, + { "TCCS", TO_DWORD (0000010, 0020477), opCC7, D32_MASK }, + { "CVND", TO_DWORD (0000020, 0020477), opSCS4, D32_MASK }, + { "LDW", TO_DWORD (0000040, 0020477), opS31, D32_MASK }, + { "LDDW", TO_DWORD (0000042, 0020477), opS31, D32_MASK }, + { "TR", TO_DWORD (0000044, 0020477), opB31, D32_MASK }, + { "ABSD", TO_DWORD (0000046, 0020477), opS31, D32_MASK }, + { "NEGD", TO_DWORD (0000050, 0020477), opS31, D32_MASK }, + { "", 0020477, opNone, 0177777 }, /* catch unimplemented two-word instructions */ + { "DMUL", 0020570, opNone, 0177777 }, { "DDIV", 0020571, opNone, 0177777 }, + { "DMPY", 0020601, opNone, 0177617 }, - { "CVAD", 0020602, opS, 0177637 }, - { "CVDA", 0020603, opSCS, 0177777 }, - { "CVBD", 0020604, opS, 0177637 }, - { "CVDB", 0020605, opS, 0177637 }, + { "CVAD", 0020602, opS11, 0177637 }, + { "CVDA", 0020603, opSCS3, 0177777 }, + { "CVBD", 0020604, opS11, 0177637 }, + { "CVDB", 0020605, opS11, 0177637 }, { "SLD", 0020606, opSU2, 0177677 }, { "NSLD", 0020607, opSU2, 0177677 }, { "SRD", 0020610, opSU2, 0177677 }, @@ -756,6 +841,102 @@ static const OP_TABLE mlb_ops = { }; +/* EDIT subprogram operations. + + The EDIT instruction, part of the COBOL II Extended Instruction Set, + implements the COBOL "PICTURE" clause. The formation of the edited string is + controlled by a subprogram whose address is pushed onto the stack. + Subprogram operations are encoded into variable-length sequences of bytes. + Each operation begins with an opcode of the form: + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | opcode | imm. operand | + +---+---+---+---+---+---+---+---+ + + Opcodes 0-16 directly decode to specific operations, and the lower half of + the first byte contains an immediate operand value from 1-15 or a zero value + that indicates that the immediate operand is contained in the next byte. + Opcode 17 is a prefix for the subopcode contained in the lower half of the + first byte in place of the immediate operand. Opcode bytes are followed by + zero or more operand bytes, depending on the operation. + + The opcodes and their assigned operations are: + + Opcode SubOp Mnem Movement Operation + ------ ----- ---- -------- --------------------------------------- + 00 - MC s => t move characters + 01 - MA s => t move alphabetics + 02 - MN s => t move numerics + 03 - MNS s => t move numerics suppressed + 04 - MFL s => t move numerics with floating insertion + 05 - IC c => t insert character + 06 - ICS c => t insert character suppressed + 07 - ICI p => t insert characters immediate + 10 - ICSI p => t insert characters suppressed immediate + 11 - BRIS (none) branch if significance + 12 - SUFT (none) subtract from target + 13 - SUFS (none) subtract from source + 14 - ICP c -> t insert character punctuation + 15 - ICPS c -> t insert character punctuation suppressed + 16 - IS p => t insert character on sign + 17 00 TE (none) terminate edit + 17 01 ENDF c -> t end floating point insertion + 17 02 SST1 (none) set significance to 1 + 17 03 SST0 (none) set significance to 0 + 17 04 MDWO s -> t move digit with overpunch + 17 05 SFC (none) set fill character + 17 06 SFLC (none) set float character + 17 07 DFLC (none) define float character + 17 10 SETC (none) set loop count + 17 11 DBNZ (none) decrement loop count and branch + + Where: + + s = source byte string operand + t = target byte string operand + p = program byte string operand + c = character operand + -> = move 1 byte + => = move n bytes + + + Implementation notes: + + 1. The operation name table is indexed by the sum of the opcode and + subopcode, where the latter is zero if the opcode is not extended. This + provides simple access to a single table. +*/ + +static const char *const edit_ops [] = { /* EDIT operation names */ + "MC", /* 000 - move characters */ + "MA", /* 001 - move alphabetics */ + "MN", /* 002 - move numerics */ + "MNS", /* 003 - move numerics suppressed */ + "MFL", /* 004 - move numerics with floating insertion */ + "IC", /* 005 - insert character */ + "ICS", /* 006 - insert character suppressed */ + "ICI", /* 007 - insert characters immediate */ + "ICSI", /* 010 - insert characters suppressed immediate */ + "BRIS", /* 011 - branch if significance */ + "SUFT", /* 012 - subtract from target */ + "SUFS", /* 013 - subtract from source */ + "ICP", /* 014 - insert character punctuation */ + "ICPS", /* 015 - insert character punctuation suppressed */ + "IS", /* 016 - insert character on sign */ + "TE", /* 017 (017 + 000) - terminate edit */ + "ENDF", /* 020 (017 + 001) - end floating point insertion */ + "SST1", /* 021 (017 + 002) - set significance to 1 */ + "SST0", /* 022 (017 + 003) - set significance to 0 */ + "MDWO", /* 023 (017 + 004) - move digit with overpunch */ + "SFC", /* 024 (017 + 005) - set fill character */ + "SFLC", /* 025 (017 + 006) - set float character */ + "DFLC", /* 026 (017 + 007) - define float character */ + "SETC", /* 027 (017 + 010) - set loop count */ + "DBNZ" /* 030 (017 + 011) - decrement loop count and branch */ + }; + + /* System interface local SCP support routines */ static void one_time_init (void); @@ -771,7 +952,8 @@ static t_stat hp_brk_cmd (int32 arg, CONST char *buf); static void fprint_value (FILE *ofile, t_value val, uint32 radix, uint32 width, uint32 format); static t_stat fprint_order (FILE *ofile, t_value *val, uint32 radix); -static t_stat fprint_instruction (FILE *ofile, const OP_TABLE ops, t_value *instruction, +static t_stat fprint_subop (FILE *ofile, t_value *val, uint32 radix, t_addr addr, int32 switches); +static t_stat fprint_instruction (FILE *ofile, const OP_TABLE ops, t_value *val, uint32 mask, uint32 shift, uint32 radix); static t_stat parse_cpu (CONST char *cptr, t_addr address, UNIT *uptr, t_value *value, int32 switches); @@ -1017,23 +1199,26 @@ return SCPE_ARG; /* return an error if ca The following symbolic formats are supported by the listed switches: Switch Interpretation - ------ ----------------------------------- - -a a single character in the low byte + ------ -------------------------------------------------- + -a a single character in the right-hand byte -b a 16-bit binary value -c a two-character packed string + -e an EDIT instruction subprogram mnemonic -i an I/O program instruction mnemonic -m a CPU instruction mnemonic -s a CPU status mnemonic + -o override numeric output to octal -d override numeric output to decimal -h override numeric output to hex + -r begin EDIT interpretation with the right-hand byte Memory may be displayed in any format. All registers may be overridden to display in octal, decimal, or hexadecimal numeric format. Only registers marked with the REG_A flag may be displayed in any format. Registers marked with REG_B may be displayed in binary format. Registers marked with REG_M will default to CPU instruction mnemonic display. Registers marked with - REG_S will default to CPU status mnemonic display. + REG_T will default to CPU status mnemonic display. When displaying mnemonics, operand values are displayed in a radix suitable to the type of the value. Address values are displayed in the CPU's address @@ -1098,18 +1283,21 @@ else { /* otherwise display if (sw & SWMASK ('I') && !is_reg) /* if I/O channel order memory display is requested */ return fprint_order (ofile, val, radix_override); /* then format and print it */ + else if (sw & SWMASK ('E') && !is_reg) /* otherwise if an EDIT subop memory display is requested */ + return fprint_subop (ofile, val, radix_override, addr, sw); + else if (sw & SWMASK ('M') /* otherwise if CPU instruction display is requested */ && (!is_reg || addr & (REG_A | REG_M)) /* and is permitted */ || is_reg && addr & REG_M && radix_override == 0) /* or if displaying a register that defaults to mnemonic */ return fprint_cpu (ofile, val, radix_override, sw); /* then format and print it */ - else if (sw & SWMASK ('S') /* otherwise if status display is requested */ - && (!is_reg || addr & (REG_A | REG_S)) /* and is permitted */ - || is_reg && addr & REG_S && radix_override == 0) { /* or if displaying a register that defaults to status */ + else if (sw & SWMASK ('T') /* otherwise if status display is requested */ + && (!is_reg || addr & (REG_A | REG_T)) /* and is permitted */ + || is_reg && addr & REG_T && radix_override == 0) { /* or if displaying a register that defaults to status */ fputs (fmt_status ((uint32) val [0]), ofile); /* then format the status flags and condition code */ fputc (' ', ofile); /* and add a separator */ - fprint_value (ofile, val [0] & STATUS_CS_MASK, /* print the code segment number */ + fprint_value (ofile, STATUS_CS (val [0]), /* print the code segment number */ (radix_override ? radix_override : cpu_dev.dradix), STATUS_CS_WIDTH, PV_RZRO); @@ -1389,168 +1577,6 @@ return SCPE_OK; /* return the display re /* System interface global utility routines */ -/* Check for device conflicts. - - The device information blocks (DIBs) for the set of enabled devices are - checked for consistency. Each device number, interrupt priority number, and - service request number must be unique among the enabled devices. These - requirements are checked as part of the instruction execution prelude; this - allows the user to exchange two device numbers (e.g.) simply by setting each - device to the other's device number. If conflicts were enforced instead at - the time the numbers were entered, the first device would have to be set to - an unused number before the second could be set to the first device's number. - - The routine begins by filling in a DIB value table from all of the device - DIBs to allow indexed access to the values to be checked. Unused DIB values - and values corresponding to devices that have no DIBs or are disabled are set - to the corresponding UNUSED constants. - - As part of the device scan, the sizes of the largest device name and debug - flag name among the devices enabled for debugging are accumulated for use in - printing debug tracing statements. - - After the DIB value table is filled in, a conflict check is made for each - conflict type (i.e., device number, interrupt priority, or service request - number). For each check, a conflict table is built, where each array element - is set to the count of devices that contain DIB values equal to the element - index. For example, when processing device number values, conflict table - element 6 is set to the count of devices that have dibptr->device_number set - to 6. If any conflict table element is set more than once, the "conflict_is" - variable is set to the type of conflict. - - If any conflicts exist for the current type, the conflict table is scanned. - A conflict table element value (i.e., device count) greater than 1 indicates - a conflict. For each such value, the DIB value table is scanned to find - matching values, and the device names associated with the matching values are - printed. - - This routine returns TRUE if any conflicts exist and FALSE there are none. - - - Implementation notes: - - 1. When this routine is called, the console and optional log file have - already been put into "raw" output mode. Therefore, newlines are not - translated to the correct line ends on systems that require it. Before - reporting a conflict, "sim_ttcmd" is called to restore the console and - log file translation. This is OK because a conflict will abort the run - and return to the command line anyway. - - 2. sim_dname is called instead of using dptr->name directly to ensure that - we pick up an assigned logical device name. -*/ - -t_bool hp_device_conflict (void) -{ -typedef enum { /* conflict types */ - Device, /* device number conflict */ - Interrupt, /* interrupt priority conflict */ - Service, /* service request number conflict */ - None /* no conflict */ - } CONFLICT_TYPE; - -#define CONFLICT_COUNT 3 /* the number of conflict types to check */ - -static const uint32 max_number [CONFLICT_COUNT] = { /* the last element index, in CONFLICT_TYPE order */ - DEVNO_MAX, - INTPRI_MAX, - SRNO_MAX - }; - -static const char *conflict_label [CONFLICT_COUNT] = { /* the conflict names, in CONFLICT_TYPE order */ - "Device number", - "Interrupt priority", - "Service request number" - }; - -const DIB *dibptr; -const DEBTAB *tptr; -DEVICE *dptr; -size_t name_length, flag_length; -uint32 dev, val; -CONFLICT_TYPE conf, conflict_is; -int32 count; -int32 dib_val [DEVICE_COUNT] [CONFLICT_COUNT]; -int32 conflicts [DEVNO_MAX + 1]; - -device_size = 0; /* reset the device and flag name sizes */ -flag_size = 0; /* to those of the devices actively debugging */ - -for (dev = 0; dev < DEVICE_COUNT; dev++) { /* fill in the DIB value table */ - dptr = (DEVICE *) sim_devices [dev]; /* from the device table */ - dibptr = (DIB *) dptr->ctxt; /* and their associated DIBs */ - - if (dibptr && !(dptr->flags & DEV_DIS)) { /* if the DIB is defined and the device is enabled */ - dib_val [dev] [Device] = dibptr->device_number; /* then copy the values to the DIB table */ - dib_val [dev] [Interrupt] = dibptr->interrupt_priority; - dib_val [dev] [Service] = dibptr->service_request_number; - } - - else { /* otherwise the device will not participate in I/O */ - dib_val [dev] [Device] = DEVNO_UNUSED; /* so set this table entry */ - dib_val [dev] [Interrupt] = INTPRI_UNUSED; /* to the "unused" values */ - dib_val [dev] [Service] = SRNO_UNUSED; - } - - if (sim_deb && dptr->dctrl) { /* if debugging is active for this device */ - name_length = strlen (sim_dname (dptr)); /* then get the length of the device name */ - - if (name_length > device_size) /* if it's greater than the current maximum */ - device_size = name_length; /* then reset the size */ - - if (dptr->debflags) /* if the device has a debug flags table */ - for (tptr = dptr->debflags; /* then scan the table */ - tptr->name != NULL; tptr++) { /* to check the length */ - flag_length = strlen (tptr->name); /* of each flag name */ - - if (flag_length > flag_size) /* if it's greater than the current maximum */ - flag_size = flag_length; /* then reset the size */ - } - } - } - -conflict_is = None; /* assume that no conflicts exist */ - -for (conf = Device; conf <= Service; conf++) { /* check for conflicts for each type */ - memset (conflicts, 0, sizeof conflicts); /* zero the conflict table for each check */ - - for (dev = 0; dev < DEVICE_COUNT; dev++) /* populate the conflict table from the DIB value table */ - if (dib_val [dev] [conf] >= 0) /* if this device has an assigned value */ - if (++conflicts [dib_val [dev] [conf]] > 1) /* then increment the count of references */ - conflict_is = conf; /* if there is more than one reference, a conflict occurs */ - - if (conflict_is == conf) { /* if a conflict exists for this type */ - sim_ttcmd (); /* then restore the console and log I/O mode */ - - for (val = 0; val <= max_number [conf]; val++) /* search the conflict table for the next conflict */ - if (conflicts [val] > 1) { /* if a conflict is present for this value */ - count = conflicts [val]; /* then get the number of conflicting devices */ - - cprintf ("%s %u conflict (", conflict_label [conf], val); - - dev = 0; /* search for the devices that conflict */ - - while (count > 0) { /* search the DIB value table */ - if (dib_val [dev] [conf] == (int32) val) { /* to find the conflicting entries */ - if (count < conflicts [val]) /* and report them to the console */ - cputs (" and "); - - cputs (sim_dname ((DEVICE *) sim_devices [dev])); - count = count - 1; - } - - dev = dev + 1; - } - - cputs (")\n"); - } - } - } - -return (conflict_is != None); /* return TRUE if any conflicts exist */ -} - - /* Print a CPU instruction in symbolic format. This routine is called to format and print an instruction in mnemonic form. @@ -1643,6 +1669,312 @@ return status; /* return the consum } +/* Print an EDIT instruction subprogram operation in symbolic format. + + This routine prints a single EDIT subprogram operation. The opcode name is + obtained from the name table and printed, followed by the operands. + + On entry, the "ofile" parameter is the opened output stream, "val" is NULL or + points to an array containing the first two words of the operation, "radix" + contains the desired operand radix or zero if the default radix is to be + used, and "byte_address" points at the first byte of the subprogram + operation. The return value is the number of subprogram bytes used by the + operation. + + This routine is called by the SCP "EXAMINE" command processor to display an + EDIT subprogram contained in memory in symbolic format. It is also called + by the EDIT instruction executor when tracing memory operands. In the former + case, the "EXAMINE" command has obtained two words from memory and placed + them in the array pointed to by the "val" parameter. In the latter case, + "val" will be NULL, indicating that the array must be loaded explicitly. In + either case, additional memory bytes are obtained as needed by the operation + decoder. + + For each operation, the following type of operand and the number of bytes + required are listed below: + + Opcode SubOp Mnem Operand Byte Count + ------ ----- ---- --------- ---------- + 00 - MC ux 1-2 + 01 - MA ux 1-2 + 02 - MN ux 1-2 + 03 - MNS ux 1-2 + 04 - MFL ux 1-2 + 05 - IC ux,'c' 2-3 + 06 - ICS ux,'c' 2-3 + 07 - ICI ux,"s" 2-257 + 10 - ICSI ux,"s" 2-257 + 11 - BRIS sx 1-2 + 12 - SUFT sx 1-2 + 13 - SUFS sx 1-2 + 14 - ICP 'p' 1 + 15 - ICPS 'p' 1 + 16 - IS u,"s","s" 1-31 + 17 00 TE (none) 1 + 17 01 ENDF (none) 1 + 17 02 SST1 (none) 1 + 17 03 SST0 (none) 1 + 17 04 MDWO (none) 1 + 17 05 SFC 'c' 2 + 17 06 SFLC 'p','p' 2 + 17 07 DFLC 'c','c' 3 + 17 10 SETC ui 2 + 17 11 DBNZ s 2 + + Where the operands are: + + Operand Meaning + ------- ----------------------------------------------- + u unsigned value 0 .. 15 + ux unsigned value 1 .. 15 or 0 .. 255 (extended) + ui unsigned value 1 .. 256 + s signed value -128 .. +127 + sx signed value 1 .. 15 or -128 .. +127 (extended) + 'p' punctuation character value ' ' + 0 .. 15 + 'c' character value 0 .. 255 + "s" string value of 0 .. 255 characters + + Note that in only three cases must additional memory accesses be performed. + + + Implementation notes: + + 1. Operation decoding is simplified by translating the prefix opcode (17) + and subopcodes (00-11) into extended opcodes 17+0 to 17+11 (17-30). + + 2. Branch operation displacements are printed as positive to indicate a + forward jump and negative to indicate a backward jump. The radix used is + the CPU's address radix, which is octal. + + 3. The Machine Instruction Set manual says that the DBNZ ("decrement loop + count and branch") operation adds the displacement operand. However, + the microcode manual shows that the displacement is actually subtracted. + Therefore, we print the operand as a negative value to conform with the + hardware action. + + 4. A simple GET_BYTE macro is defined for easy access to the bytes contained + in the "val" array. The macro also increments the byte address and + increments the word array pointer as needed. +*/ + +#define GET_BYTE(b) \ + if (byte_address++ & 1) \ + b = LOWER_BYTE (*val++); \ + else \ + b = UPPER_BYTE (*val) + +uint32 fprint_edit (FILE *ofile, t_value *val, uint32 radix, uint32 byte_address) +{ +uint8 byte, opcode, operand; +uint32 word_address, byte_count, disp_radix; +t_value eval_array [2]; + +if (radix == 0) /* if the supplied radix is defaulted */ + disp_radix = 10; /* then assume decimal numeric output */ +else /* otherwise */ + disp_radix = radix; /* use the radix given */ + +if (val == NULL) { /* if the "val" array has not been loaded */ + word_address = byte_address / 2; /* then get the word address of the operation */ + + mem_examine (&eval_array [0], word_address, NULL, 0); /* load the two words */ + mem_examine (&eval_array [1], word_address + 1, NULL, 0); /* containing the target operation */ + + val = eval_array; /* point at the array containing the operation */ + } + +GET_BYTE (byte); /* get the opcode byte */ +byte_count = 1; /* and count it */ + +opcode = UPPER_HALF (byte); /* separate the opcode */ +operand = LOWER_HALF (byte); /* and the immediate operand */ + +if (operand == 0 && opcode <= EDIT_SUFS) { /* if an extended immediate operand is indicated */ + GET_BYTE (operand); /* then get it */ + byte_count = 2; /* and count it */ + } + +if (opcode == EDIT_EXOP) /* if the opcode is extended */ + opcode = opcode + operand; /* then translate it */ + +if (opcode <= EDIT_DBNZ) { /* if the extended opcode is valid */ + fputs (edit_ops [opcode], ofile); /* then print the associated mnemonic */ + + if (opcode < EDIT_TE || opcode > EDIT_MDWO) /* if the operation has an operand */ + fputc (' ', ofile); /* then add a space as a separator */ + } + +switch (opcode) { /* dispatch by the extended opcode */ + + case 000: /* MC - move characters */ + case 001: /* MA - move alphabetics */ + case 002: /* MN - move numerics */ + case 003: /* MNS - move numerics suppressed */ + case 004: /* MFL - move numerics with floating insertion */ + fprint_value (ofile, operand, disp_radix, /* print the character count */ + D8_WIDTH, PV_LEFT); + break; + + + case 005: /* IC - insert character */ + case 006: /* ICS - insert character suppressed */ + GET_BYTE (byte); /* get the insertion character */ + byte_count = byte_count + 1; /* and count it */ + + fprint_value (ofile, operand, disp_radix, /* print the insertion count */ + D8_WIDTH, PV_LEFT); + + fputc (',', ofile); /* add a separator */ + fputs (fmt_char ((uint32) byte), ofile); /* and print the insertion character */ + break; + + + case 007: /* ICI - insert characters immediate */ + case 010: /* ICSI - insert characters suppressed immediate */ + fprint_value (ofile, operand, disp_radix, /* print the character count */ + D8_WIDTH, PV_LEFT); + + fputs (",\"", ofile); /* add a separator and open the quotation */ + fputs (fmt_byte_operand (byte_address, operand), ofile); /* print the string operand */ + fputc ('"', ofile); /* and close the quotation */ + + byte_count = byte_count + operand; /* count the bytes */ + break; + + + case 030: /* DBNZ - decrement loop count and branch */ + GET_BYTE (operand); /* get the signed branch displacement */ + byte_count = byte_count + 1; /* and count it */ + + if (operand == 0200) { /* if the operand is -128 */ + fputc ('+', ofile); /* then print a plus sign for display */ + fprint_value (ofile, operand, cpu_dev.aradix, /* and the displacement */ + D8_WIDTH, PV_LEFT); /* in the CPU's address radix */ + break; /* and we're done */ + } + + else /* otherwise */ + operand = NEG8 (operand); /* negate the operand for display */ + + /* fall into the BRIS case */ + + case 011: /* BRIS - branch if significance */ + if (operand & D8_SIGN) { /* if the displacement is negative */ + fputc ('-', ofile); /* then print a minus sign */ + operand = NEG8 (operand); /* and make the value positive */ + } + + else /* otherwise */ + fputc ('+', ofile); /* print a plus sign */ + + fprint_value (ofile, operand, cpu_dev.aradix, /* print the displacement */ + D8_WIDTH, PV_LEFT); /* in the CPU's address radix */ + break; + + + case 012: /* SUFT - subtract from target */ + case 013: /* SUFS - subtract from source */ + if (operand & D8_SIGN) { /* if the displacement is negative */ + fputc ('-', ofile); /* then print a minus sign */ + operand = NEG8 (operand); /* and make the value positive */ + } + + fprint_value (ofile, operand, disp_radix, /* print the displacement */ + D8_WIDTH, PV_LEFT); + break; + + + case 014: /* ICP - insert character punctuation */ + case 015: /* ICPS - insert character punctuation suppressed */ + fputs (fmt_char ((uint32) (operand + ' ')), ofile); /* print the punctuation character */ + break; + + + case 016: /* IS - insert character on sign */ + fprint_value (ofile, operand, disp_radix, /* print the character count */ + D8_WIDTH, PV_LEFT); + + fputs (",\"", ofile); /* add a separator and open the quotation */ + fputs (fmt_byte_operand (byte_address, operand), ofile); /* print the string operand */ + + fputs ("\",\"", ofile); /* close and open the second quotation */ + fputs (fmt_byte_operand (byte_address + operand, operand), ofile); /* print the string operand */ + fputc ('"', ofile); /* and close the quotation */ + + byte_count = byte_count + 2 * operand; /* count the bytes */ + break; + + + case 017: /* TE - terminate edit */ + case 020: /* ENDF - end floating point insertion */ + case 021: /* SST1 - set significance to 1 */ + case 022: /* SST0 - set significance to 0 */ + case 023: /* MDWO - move digit with overpunch */ + break; /* these opcodes have no operands */ + + + case 024: /* SFC - set fill character */ + GET_BYTE (byte); /* get the fill character */ + byte_count = byte_count + 1; /* and count it */ + + fputs (fmt_char ((uint32) byte), ofile); /* print the fill character */ + break; + + case 025: /* SFLC - set float character */ + GET_BYTE (byte); /* get the float character */ + byte_count = byte_count + 1; /* and count it */ + + fputs (fmt_char ((uint32) (UPPER_HALF (byte) + ' ')), ofile); /* print the positive float character */ + fputc (',', ofile); /* and a separator */ + fputs (fmt_char ((uint32) (LOWER_HALF (byte) + ' ')), ofile); /* and the negative float character */ + break; + + + case 026: /* DFLC - define float character */ + GET_BYTE (byte); /* get the float character */ + + fputs (fmt_char ((uint32) byte), ofile); /* print the positive float character */ + + GET_BYTE (byte); /* get the float character */ + byte_count = byte_count + 2; /* and count both characters */ + + fputc (',', ofile); /* print a separator */ + fputs (fmt_char ((uint32) byte), ofile); /* and the negative float character */ + break; + + + case 027: /* SETC - set loop count */ + GET_BYTE (byte); /* get the loop count */ + byte_count = byte_count + 1; /* and count it */ + + if (byte == 0) /* if the count is zero */ + fprint_value (ofile, 256, disp_radix, /* then the loop executes 256 times */ + D16_WIDTH, PV_LEFT); + else /* otherwise */ + fprint_value (ofile, byte, disp_radix, /* print the given loop count */ + D8_WIDTH, PV_LEFT); + break; + + + default: /* the opcode is invalid */ + if (radix == 0) /* if the supplied radix is defaulted */ + disp_radix = cpu_dev.dradix; /* then use the data radix */ + else /* otherwise */ + disp_radix = radix; /* use the radix given */ + + fprint_value (ofile, EDIT_EXOP, disp_radix, /* print the extended opcode */ + D4_WIDTH, PV_RZRO); + + fputc (',', ofile); /* print a separator */ + fprint_value (ofile, opcode - EDIT_EXOP, /* and the invalid opcode in numeric form */ + disp_radix, D4_WIDTH, PV_RZRO); + break; + } + +return byte_count; /* return the number of bytes used by the operation */ +} + + /* Format the status register flags and condition code. This routine formats the flags and condition code part of the status register @@ -2042,6 +2374,181 @@ return; } +/* Check for device conflicts. + + The device information blocks (DIBs) for the set of enabled devices are + checked for consistency. Each device number, interrupt priority number, and + service request number must be unique among the enabled devices. These + requirements are checked as part of the instruction execution prelude; this + allows the user to exchange two device numbers (e.g.) simply by setting each + device to the other's device number. If conflicts were enforced instead at + the time the numbers were entered, the first device would have to be set to + an unused number before the second could be set to the first device's number. + + The routine begins by filling in a DIB value table from all of the device + DIBs to allow indexed access to the values to be checked. Unused DIB values + and values corresponding to devices that have no DIBs or are disabled are set + to the corresponding UNUSED constants. + + As part of the device scan, the sizes of the largest device name and active + debug flag name among the devices enabled for debugging are accumulated for + use in aligning the debug tracing statements. + + After the DIB value table is filled in, a conflict check is made for each + conflict type (i.e., device number, interrupt priority, or service request + number). For each check, a conflict table is built, where each array element + is set to the count of devices that contain DIB values equal to the element + index. For example, when processing device number values, conflict table + element 6 is set to the count of devices that have dibptr->device_number set + to 6. If any conflict table element is set more than once, the "conflict_is" + variable is set to the type of conflict. + + If any conflicts exist for the current type, the conflict table is scanned. + A conflict table element value (i.e., device count) greater than 1 indicates + a conflict. For each such value, the DIB value table is scanned to find + matching values, and the device names associated with the matching values are + printed. + + This routine returns TRUE if any conflicts exist and FALSE there are none. + + + Implementation notes: + + 1. When this routine is called, the console and optional log file have + already been put into "raw" output mode. Therefore, newlines are not + translated to the correct line ends on systems that require it. Before + reporting a conflict, "sim_ttcmd" is called to restore the console and + log file translation. This is OK because a conflict will abort the run + and return to the command line anyway. + + 2. sim_dname is called instead of using dptr->name directly to ensure that + we pick up an assigned logical device name. + + 3. Only the names of active trace (debug) options are accumulated to produce + the most compact trace log. However, if the CPU device's EXEC option is + enabled, then all of the CPU option names are accumulated, as EXEC + enables all trace options for a given instruction or instruction class. + + 4. Even though the routine is called only from the sim_instr routine in the + CPU simulator module, it must be located here to use the DEVICE_COUNT + constant to allocate the dib_val matrix. If it were located in the CPU + module, the matrix would have to be allocated dynamically after a + run-time determination of the count of simulator devices. +*/ + +t_bool hp_device_conflict (void) +{ +#define CONFLICT_COUNT 3 /* the number of conflict types to check */ + +typedef enum { /* conflict types */ + Device, /* device number conflict */ + Interrupt, /* interrupt priority conflict */ + Service, /* service request number conflict */ + None /* no conflict */ + } CONFLICT_TYPE; + +static const uint32 max_number [CONFLICT_COUNT] = { /* the last element index, in CONFLICT_TYPE order */ + DEVNO_MAX, + INTPRI_MAX, + SRNO_MAX + }; + +static const char *conflict_label [CONFLICT_COUNT] = { /* the conflict names, in CONFLICT_TYPE order */ + "Device number", + "Interrupt priority", + "Service request number" + }; + +const DIB *dibptr; +const DEBTAB *tptr; +DEVICE *dptr; +size_t name_length, flag_length; +uint32 dev, val; +CONFLICT_TYPE conf, conflict_is; +int32 count; +int32 dib_val [DEVICE_COUNT] [CONFLICT_COUNT]; +int32 conflicts [DEVNO_MAX + 1]; + +device_size = 0; /* reset the device and flag name sizes */ +flag_size = 0; /* to those of the devices actively debugging */ + +for (dev = 0; dev < DEVICE_COUNT; dev++) { /* fill in the DIB value table */ + dptr = (DEVICE *) sim_devices [dev]; /* from the device table */ + dibptr = (DIB *) dptr->ctxt; /* and their associated DIBs */ + + if (dibptr && !(dptr->flags & DEV_DIS)) { /* if the DIB is defined and the device is enabled */ + dib_val [dev] [Device] = dibptr->device_number; /* then copy the values to the DIB table */ + dib_val [dev] [Interrupt] = dibptr->interrupt_priority; + dib_val [dev] [Service] = dibptr->service_request_number; + } + + else { /* otherwise the device will not participate in I/O */ + dib_val [dev] [Device] = DEVNO_UNUSED; /* so set this table entry */ + dib_val [dev] [Interrupt] = INTPRI_UNUSED; /* to the "unused" values */ + dib_val [dev] [Service] = SRNO_UNUSED; + } + + if (sim_deb && dptr->dctrl) { /* if debugging is active for this device */ + name_length = strlen (sim_dname (dptr)); /* then get the length of the device name */ + + if (name_length > device_size) /* if it's greater than the current maximum */ + device_size = name_length; /* then reset the size */ + + if (dptr->debflags) /* if the device has a debug flags table */ + for (tptr = dptr->debflags; /* then scan the table */ + tptr->name != NULL; tptr++) + if (dev == 0 && dptr->dctrl & DEB_EXEC /* if the CPU device is tracing executions */ + || tptr->mask & dptr->dctrl) { /* or this debug option is active */ + flag_length = strlen (tptr->name); /* then get the flag name length */ + + if (flag_length > flag_size) /* if it's greater than the current maximum */ + flag_size = flag_length; /* then reset the size */ + } + } + } + +conflict_is = None; /* assume that no conflicts exist */ + +for (conf = Device; conf <= Service; conf++) { /* check for conflicts for each type */ + memset (conflicts, 0, sizeof conflicts); /* zero the conflict table for each check */ + + for (dev = 0; dev < DEVICE_COUNT; dev++) /* populate the conflict table from the DIB value table */ + if (dib_val [dev] [conf] >= 0) /* if this device has an assigned value */ + if (++conflicts [dib_val [dev] [conf]] > 1) /* then increment the count of references */ + conflict_is = conf; /* if there is more than one reference, a conflict occurs */ + + if (conflict_is == conf) { /* if a conflict exists for this type */ + sim_ttcmd (); /* then restore the console and log I/O mode */ + + for (val = 0; val <= max_number [conf]; val++) /* search the conflict table for the next conflict */ + if (conflicts [val] > 1) { /* if a conflict is present for this value */ + count = conflicts [val]; /* then get the number of conflicting devices */ + + cprintf ("%s %u conflict (", conflict_label [conf], val); + + dev = 0; /* search for the devices that conflict */ + + while (count > 0) { /* search the DIB value table */ + if (dib_val [dev] [conf] == (int32) val) { /* to find the conflicting entries */ + if (count < conflicts [val]) /* and report them to the console */ + cputs (" and "); + + cputs (sim_dname ((DEVICE *) sim_devices [dev])); + count = count - 1; + } + + dev = dev + 1; + } + + cputs (")\n"); + } + } + } + +return (conflict_is != None); /* return TRUE if any conflicts exist */ +} + + /* System interface local SCP support routines */ @@ -2254,15 +2761,19 @@ else /* otherwise the if (cptr != *tptr) /* if the parse succeeded */ if (**tptr == '.') /* then if this a banked address */ - if (! (parse_config & apcBank_Offset)) /* but it is not allowed */ - *tptr = cptr; /* then report a parse error */ + if (! (parse_config & apcBank_Offset) /* but it is not allowed */ + || address > BA_MAX) /* or the bank number is out of range */ + *tptr = cptr; /* then report a parse error */ else { /* otherwise the . form is allowed */ sptr = *tptr + 1; /* point to the offset */ bank = address; /* save the first part as the bank address */ address = strtotv (sptr, tptr, dptr->aradix); /* parse the offset */ - address = TO_PA (bank, address); /* form the linear address */ + if (address > LA_MAX) /* if the offset is too large */ + *tptr = cptr; /* then report a parse error */ + else /* otherwise it is in range */ + address = TO_PA (bank, address); /* so form the linear address */ } else if (address > LA_MAX) /* otherwise if the non-banked offset is too large */ @@ -2564,7 +3075,84 @@ return SCPE_OK_2_WORDS; /* indicate that each in } -/* Print a CPU instruction opcode and operand in symbolic format. +/* Format and print an EDIT instruction subprogram operation in symbolic format. + + This routine formats and prints a set of memory bytes as an EDIT subprogram + operation. It is called when the "-E" command-line switch is included with a + memory display request. + + On entry, the "ofile" parameter is the opened output stream, "val [*]" + contains the first two words of the operation, "radix" contains the desired + operand radix or zero if the default radix is to be used, "addr" points at + the memory word containing the first subprogram operation byte, and + "switches" contains the set of command-line switches supplied with the + command. The return value is one more than the negative number of memory + words consumed by the print request (e.g., a one-word consumption returns + zero, a two-word consumption returns -1, etc.). + + The routine is called to print a single memory word. If the EXAMINE command + is given a memory range, the routine will be called multiple times, with + "val" pointing at an array containing the first two words of the range. + + An operation may encompass from 1-257 bytes. The operation is assumed to + begin in the upper byte of the first memory word unless the "-R" switch is + supplied, in which case the lower byte is assumed. If "-R" is not used, and + the operation in the upper byte consumes only a single byte, the operation + (beginning) in the lower byte is also printed. + + Full operations are always printed, so a single call on the routine prints + one or two operations and then returns the number of memory words consumed + (biased by 1). If a range is being printed, this value determines the + address presented at the following call. + + Before returning, the "R" switch is set or cleared to indicate whether the + next call in a range should start with the lower or upper byte of the word. + + + Implementation notes: + + 1. Within a single command, this routine may be called (via fprint_sym) + successively for the same address. This will occur if console logging is + enabled. The first call will be to write to the console, and then it + will be called again to write to the log file. However, sim_switches is + updated after the first call in anticipation of additional calls for the + subsequent addresses in a range. To avoid this, sim_switches is tested + only if the routine is NOT called for the log file; if it is, then the + previous setting of sim_switches is used. +*/ + +static t_stat fprint_subop (FILE *ofile, t_value *val, uint32 radix, t_addr addr, int32 switches) +{ +static uint32 odd_byte = 0; +uint32 byte_addr, bytes_used, total_bytes_used; + +if (ofile != sim_log) /* if this is not a logging call */ + odd_byte = (uint32) ((switches & SWMASK ('R')) != 0); /* then recalculate the odd-byte flag */ + +total_bytes_used = odd_byte; /* initialize the bytes used accumulator */ +byte_addr = (uint32) addr * 2 + odd_byte; /* form the initial byte address */ + +do { + bytes_used = fprint_edit (ofile, val, radix, byte_addr); /* format and print an operation */ + + byte_addr = byte_addr + bytes_used; /* point at the next operation */ + total_bytes_used = total_bytes_used + bytes_used; /* and add the byte count to the accumulator */ + + if (total_bytes_used < 2) /* if a full word has not been consumed yet */ + fputs ("\n\t\t", ofile); /* then start a new line for the second operation */ + } +while (total_bytes_used < 2); /* continue until at least one word is consumed */ + +if (byte_addr & 1) /* if the next operation begins in the lower byte */ + sim_switches |= SWMASK ('R'); /* then set the switch for the next call */ +else /* otherwise it begins in the upper byte */ + sim_switches &= ~SWMASK ('R'); /* so clear the switch */ + +return -(t_stat) (total_bytes_used / 2 - 1); /* return the (biased) negative number of words consumed */ +} + + +/* Print a CPU instruction and operand in symbolic format. This routine prints a CPU instruction and its operand, if any, using the mnemonics specified in the Machine Instruction Set and Systems Programming @@ -2574,11 +3162,11 @@ return SCPE_OK_2_WORDS; /* indicate that each in (if any). On entry, the "ofile" parameter is the opened output stream, "ops" is the - table of classifications containing the instruction, "instruction" is the - machine instruction to print, "mask" is the opcode mask to apply to get the - index bits, "shift" is the right-shift count to align the index, and "radix" - contains the desired operand radix or zero if the default radix is to be - used. + table of classifications containing the instruction, "val" contains the + word(s) of the machine instruction to print, "mask" is the opcode mask to + apply to get the index bits, "shift" is the right-shift count to align the + index, and "radix" contains the desired operand radix or zero if the default + radix is to be used. On exit, a status code is returned to the caller. SCPE_OK status is returned if the print consumed a single-word value, or the negative number of extra @@ -2589,8 +3177,10 @@ return SCPE_OK_2_WORDS; /* indicate that each in The classification table consists of a set of entries that are indexed by opcode, followed optionally by a set of entries that are searched linearly. Empty mnemonics, i.e., "", are used in the indexed part to indicate that the - linear part must be searched. A NULL mnemonic ends the array (this allows - string searches for parsing to fail without aborting). + linear part must be searched, and in the linear part to indicate a two-word + instruction whose second word did not match a defined entry. A NULL mnemonic + ends the array (this allows string searches for parsing to fail without + aborting). The supplied instruction is ANDed with the "mask" parameter and then right-shifted by the "shift" parameter to produce an index into the "ops" @@ -2600,8 +3190,7 @@ return SCPE_OK_2_WORDS; /* indicate that each in instruction is masked to remove the operand and optionally the reserved bits, and the result is compared to the base opcode. If it matches, the associated mnemonic is printed. If the table is exhausted without a match, the - instruction is undefined, and it is printed in octal, regardless of the data - radix. + instruction is undefined, and SCPE_ARG is returned. For defined instructions, the operand, if any, is printed after the mnemonic. Operand values are printed in a radix suitable to the type of the value, as @@ -2628,10 +3217,9 @@ return SCPE_OK_2_WORDS; /* indicate that each in 1. All instructions in the base set are single words. However, some extension instructions, including instructions for later-series CPUs, - e.g., Series 33, use two or more words. For example, the WIOC (write I/O - channel) instruction is the two-word sequence 020302 000003, and the SIOP - (start I/O program) sequence is 020302 000000. Currently, this routine - is not set up to handle this. + e.g., the Series 33, use two words. For example, the WIOC (write I/O + channel) instruction is the two-word sequence 020302,000003, and the SIOP + (start I/O program) sequence is 020302,000000. 2. The operand type dispatch handlers either set up operand printing by assigning the prefix, indirect, and index values, or print the operand(s) @@ -2639,6 +3227,15 @@ return SCPE_OK_2_WORDS; /* indicate that each in 3. Register flags for the PSHR and SETR instructions are printed using the SPL register names. + + 4. The "rsvd_mask" fields of secondary entries for one-word instructions + have the upper 16 bits of the values set to zero. This masks off the + second instruction word, which is arbitrary, before comparison. + + 5. A secondary entry with a zero-length mnemonic is used to match the first + word of a two-word instruction when the second word fails to match any of + the earlier entries. This causes the instruction to be printed as a pair + of numeric values. */ static const char *const register_name [] = { /* PSHR/SETR register names corresponding to bits 8-15 */ @@ -2652,40 +3249,82 @@ static const char *const register_name [] = { /* PSHR/SETR register names corr "S" /* bit 15 */ }; -static t_stat fprint_instruction (FILE *ofile, const OP_TABLE ops, t_value *instruction, +static const char *const cc_flags [] = { /* TCCS condition code names corresponding to bits 13-15 */ + " N", /* 000 = never */ + " L", /* 001 = less than */ + " E", /* 010 = equal */ + " LE", /* 011 = less than or equal */ + " G", /* 100 = greater than */ + " NE", /* 101 = not equal */ + " GE", /* 110 = greater than or equal */ + " A" /* 111 = always */ + }; + +static const char *const sign_cntl [] = { /* CVND sign control names corresponding to bits 12-14 */ + " LS", /* 000 = sign is leading separate */ + " TS", /* 001 = sign is trailing separate */ + " LO", /* 010 = sign is leading overpunch or unsigned */ + " TO", /* 011 = sign is trailing overpunch or unsigned */ + " UN", /* 100 = unsigned */ + " UN", /* 101 = unsigned */ + " UN", /* 110 = unsigned */ + " UN" /* 111 = unsigned */ + }; + +static t_stat fprint_instruction (FILE *ofile, const OP_TABLE ops, t_value *val, uint32 mask, uint32 shift, uint32 radix) { uint32 op_index, op_radix; int32 reg_index; t_bool reg_first; -t_value op_value; -const char *prefix = NULL; /* base register label to print before the operand */ +t_value instruction, op_value; +const char *prefix = NULL; /* label to print before the operand */ t_bool index = FALSE; /* TRUE if the instruction is indexed */ t_bool indirect = FALSE; /* TRUE if the instruction is indirect */ +t_stat status = SCPE_OK; /* result status */ -op_index = ((uint32) instruction [0] & mask) >> shift; /* extract the opcode index */ +instruction = TO_DWORD (val [1], val [0]); /* merge the two supplied values */ + +op_index = ((uint32) instruction & mask) >> shift; /* extract the opcode index */ if (ops [op_index].mnemonic [0]) /* if a primary entry is defined */ fputs (ops [op_index].mnemonic, ofile); /* then print the mnemonic */ else { /* otherwise search through the secondary entries */ - for (op_index = (mask >> shift) + 1; /* search the table starting after the primary entries */ - ops [op_index].mnemonic != NULL; /* until the NULL entry at the end */ + for (op_index = (mask >> shift) + 1; /* in the table starting after the primary entries */ + ops [op_index].mnemonic != NULL; /* until the NULL entry at the end */ op_index++) if (ops [op_index].opcode == /* if the opcode in this table entry */ - (instruction [0] & ops [op_index].rsvd_mask /* matches the instruction with the reserved bits */ - & op_mask [ops [op_index].operand])) { /* and operand bits masked off */ - fputs (ops [op_index].mnemonic, ofile); /* then print it */ - break; /* and terminate the search */ - } + (instruction & ops [op_index].rsvd_mask /* matches the instruction with the reserved bits */ + & op_mask [ops [op_index].operand])) /* and operand bits masked off */ + if (ops [op_index].mnemonic [0]) { /* then if the entry is defined */ + fputs (ops [op_index].mnemonic, ofile); /* then print it */ + break; /* and terminate the search */ + } + + else { /* otherwise this two-word instruction is unimplemented */ + fprint_val (ofile, val [0], cpu_dev.dradix, /* so print the first word */ + cpu_dev.dwidth, PV_RZRO); + + fputc (',', ofile); /* and a separator */ + + fprint_val (ofile, val [1], cpu_dev.dradix, /* and the second word */ + cpu_dev.dwidth, PV_RZRO); + + return SCPE_OK_2_WORDS; /* return success to indicate printing is complete */ + } if (ops [op_index].mnemonic == NULL) /* if the opcode was not found */ return SCPE_ARG; /* then return error status to print it in octal */ } -op_value = /* mask the instruction to the operand value */ - instruction [0] & ~op_mask [ops [op_index].operand]; +op_value = instruction & ~op_mask [ops [op_index].operand]; /* mask the instruction to the operand value */ + +if (ops [op_index].rsvd_mask > D16_MASK) { /* if this is a two-word instruction */ + op_value = op_value >> D16_WIDTH; /* then the operand is in the upper word */ + status = SCPE_OK_2_WORDS; /* and the routine will consume two words */ + } op_radix = cpu_dev.aradix; /* assume that operand is an address */ @@ -2697,6 +3336,13 @@ switch (ops [op_index].operand) { /* dispatch by the opera break; /* no formatting needed */ + /* condition code flags in bits 13-15 */ + + case opCC7: + fputs (cc_flags [op_value], ofile); /* print the condition code flags */ + break; + + /* unsigned value pair range 0-15 */ case opU1515: @@ -2715,7 +3361,7 @@ switch (ops [op_index].operand) { /* dispatch by the opera /* P +/- displacement range 0-31, indirect bit 4 */ case opPS31I: - indirect = (instruction [0] & I_FLAG_BIT_4) != 0; /* save the indirect condition */ + indirect = (instruction & I_FLAG_BIT_4) != 0; /* save the indirect condition */ prefix = (op_value & DISPL_31_SIGN ? " P-" : " P+"); /* set the base register and sign label */ op_value = op_value & DISPL_31_MASK; /* and remove the sign from the displacement value */ break; @@ -2724,8 +3370,8 @@ switch (ops [op_index].operand) { /* dispatch by the opera /* P +/- displacement range 0-255, indirect bit 5, index bit 4 */ case opPS255IX: - index = (instruction [0] & X_FLAG) != 0; /* save the index condition */ - indirect = (instruction [0] & I_FLAG_BIT_5) != 0; /* and the indirect condition */ + index = (instruction & X_FLAG) != 0; /* save the index condition */ + indirect = (instruction & I_FLAG_BIT_5) != 0; /* and the indirect condition */ /* fall into the P-relative displacement case */ @@ -2737,27 +3383,36 @@ switch (ops [op_index].operand) { /* dispatch by the opera break; + /* base bit 15 */ + + case opB15: + case opB31: + if (op_value == 0) /* if the base bit is 0 */ + fputs (" PB", ofile); /* then PB is the base register label */ + break; + + /* S decrement range 0-3, base register bit 11 */ case opSU3B: - prefix = (instruction [0] & DB_FLAG) ? " " : " PB,"; /* set the base register label */ - op_value = op_value & ~op_mask [opSU3]; /* and remove the base flag from the S decrement value */ + prefix = (instruction & DB_FLAG) ? " " : " PB,"; /* set the base register label */ + op_value = op_value & ~op_mask [opSU3]; /* and remove the base flag from the S decrement value */ break; /* S decrement range 0-3, N/A/S bits 11-13 */ case opSU3NAS: - if (instruction [0] & MVBW_CCF) /* if any flags are present */ + if (instruction & MVBW_CCF) /* if any flags are present */ fputc (' ', ofile); /* then print a space as a separator */ - if (instruction [0] & MVBW_A_FLAG) /* if the alphabetic flag is present */ + if (instruction & MVBW_A_FLAG) /* if the alphabetic flag is present */ fputc ('A', ofile); /* then print an "A" as the indicator */ - if (instruction [0] & MVBW_N_FLAG) /* if the numeric flag is present */ + if (instruction & MVBW_N_FLAG) /* if the numeric flag is present */ fputc ('N', ofile); /* then print an "N" as the indicator */ - if (instruction [0] & MVBW_S_FLAG) /* if the upshift flag is present */ + if (instruction & MVBW_S_FLAG) /* if the upshift flag is present */ fputc ('S', ofile); /* then print an "S" as the indicator */ prefix = ","; /* separate the value from the flags */ @@ -2816,12 +3471,12 @@ switch (ops [op_index].operand) { /* dispatch by the opera /* P+/P-/DB+/Q+/Q-/S- displacements, indirect bit 5, index bit 4 */ case opPD255IX: - if ((instruction [0] & DISPL_P_FLAG) == 0) { /* if this a P-relative displacement */ + if ((instruction & DISPL_P_FLAG) == 0) { /* if this a P-relative displacement */ prefix = (op_value & DISPL_255_SIGN ? " P-" : " P+"); /* then set the base register and sign label */ op_value = op_value & DISPL_255_MASK; /* and remove the sign from the displacement value */ - index = (instruction [0] & X_FLAG) != 0; /* save the index condition */ - indirect = (instruction [0] & I_FLAG_BIT_5) != 0; /* and the indirect condition */ + index = (instruction & X_FLAG) != 0; /* save the index condition */ + indirect = (instruction & I_FLAG_BIT_5) != 0; /* and the indirect condition */ break; } @@ -2830,19 +3485,19 @@ switch (ops [op_index].operand) { /* dispatch by the opera /* DB+/Q+/Q-/S- displacements, indirect bit 5, index bit 4 */ case opD255IX: - if ((instruction [0] & DISPL_DB_FLAG) == 0) { /* if this a DB-relative displacement */ + if ((instruction & DISPL_DB_FLAG) == 0) { /* if this a DB-relative displacement */ prefix = " DB+"; /* then set the base register label */ op_value = op_value & DISPL_255_MASK; /* and remove the base flag from the displacement value */ } - else if ((instruction [0] & DISPL_QPOS_FLAG) == 0) { /* otherwise if this a positive Q-relative displacement */ - prefix = " Q+"; /* then set the base register label */ - op_value = op_value & DISPL_127_MASK; /* and remove the base flag from the displacement value */ + else if ((instruction & DISPL_QPOS_FLAG) == 0) { /* otherwise if this a positive Q-relative displacement */ + prefix = " Q+"; /* then set the base register label */ + op_value = op_value & DISPL_127_MASK; /* and remove the base flag from the displacement value */ } - else if ((instruction [0] & DISPL_QNEG_FLAG) == 0) { /* otherwise if this a negative Q-relative displacement */ - prefix = " Q-"; /* then set the base register label */ - op_value = op_value & DISPL_63_MASK; /* and remove the base flag from the displacement value */ + else if ((instruction & DISPL_QNEG_FLAG) == 0) { /* otherwise if this a negative Q-relative displacement */ + prefix = " Q-"; /* then set the base register label */ + op_value = op_value & DISPL_63_MASK; /* and remove the base flag from the displacement value */ } else { /* otherwise it must be a negative S-relative displacement */ @@ -2850,21 +3505,21 @@ switch (ops [op_index].operand) { /* dispatch by the opera op_value = op_value & DISPL_63_MASK; /* and remove the base flag from the displacement value */ } - indirect = (instruction [0] & I_FLAG_BIT_5) != 0; /* save the indirect condition */ + indirect = (instruction & I_FLAG_BIT_5) != 0; /* save the indirect condition */ /* fall into the index case */ /* index bit 4 */ case opX: - index = (instruction [0] & X_FLAG) != 0; /* save the index condition */ + index = (instruction & X_FLAG) != 0; /* save the index condition */ break; /* unsigned value range 0-63, index bit 4 */ case opU63X: - index = (instruction [0] & X_FLAG) != 0; /* save the index condition */ + index = (instruction & X_FLAG) != 0; /* save the index condition */ op_value = op_value & DISPL_63_MASK; /* and mask to the operand value */ /* fall into the unsigned value case */ @@ -2879,13 +3534,13 @@ switch (ops [op_index].operand) { /* dispatch by the opera /* sign control bits 9-10, S decrement bit 11 */ - case opSCS: - if (instruction [0] & NABS_FLAG) { /* if the negative absolute flag is present */ + case opSCS3: + if (instruction & NABS_FLAG) { /* if the negative absolute flag is present */ fputs (" NABS", ofile); /* then print "NABS" as the indicator */ prefix = ","; /* we will need to separate the flag and value */ } - else if (instruction [0] & ABS_FLAG) { /* otherwise if the absolute flag is present */ + else if (instruction & ABS_FLAG) { /* otherwise if the absolute flag is present */ fputs (" ABS", ofile); /* then print "ABS" as the indicator */ prefix = ","; /* we will need to separate the flag and value */ } @@ -2893,15 +3548,25 @@ switch (ops [op_index].operand) { /* dispatch by the opera else /* otherwise neither flag is present */ prefix = " "; /* so just use a space to separate the value */ - op_value = (op_value & ~op_mask [opS]) >> EIS_SDEC_SHIFT; /* remove the flags from the S decrement value */ + op_value = (op_value & ~op_mask [opS11]) >> EIS_SDEC_SHIFT; /* remove the flags from the S decrement value */ op_radix = (radix ? radix : cpu_dev.dradix); /* and set the print radix */ break; + /* sign control bits 12-14, S decrement bit 15 */ + + case opSCS4: + fputs (sign_cntl [op_value >> CVND_SC_SHIFT], ofile); /* print the sign-control mnemonic */ + + prefix = ","; /* separate the flag and value */ + op_value = op_value & CIS_SDEC_MASK; /* and remove the flags from the S decrement value */ + break; + + /* S decrement bit 11 */ /* S decrement range 0-2 bits 10-11 */ - case opS: + case opS11: case opSU2: op_value = op_value >> EIS_SDEC_SHIFT; /* align the S decrement value */ @@ -2926,11 +3591,14 @@ switch (ops [op_index].operand) { /* dispatch by the opera /* P unsigned displacement range 0-255 */ + /* S decrement range 0-1 */ /* S decrement range 0-3 */ /* S decrement range 0-7 */ /* S decrement range 0-15 */ case opPU255: + case opS15: + case opS31: case opSU3: case opSU7: case opSU15: @@ -2952,7 +3620,7 @@ if (indirect) /* add an indirect indic if (index) /* add an index indicator */ fputs (",X", ofile); /* if specified by the instruction */ -return SCPE_OK; +return status; /* return the applicable status */ } diff --git a/HP3000/hp_disclib.c b/HP3000/hp_disclib.c index 75f70ad2..680539be 100644 --- a/HP3000/hp_disclib.c +++ b/HP3000/hp_disclib.c @@ -24,6 +24,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the authors. + 10-Oct-16 JDB Moved "hp3000_defs.h" inclusion from "hp_disclib.h" 03-Aug-16 JDB "fmt_bitset" now allows multiple concurrent calls 09-Jun-16 JDB Added casts for ptrdiff_t to int32 values 08-Jun-16 JDB Corrected %d format to %u for unsigned values @@ -372,21 +373,22 @@ #include +#include "hp3000_defs.h" /* this must reflect the machine used */ #include "hp_disclib.h" /* Program constants */ -#define CNTLR_UNIT (DL_MAXDRIVE + 1) /* controller unit number */ -#define MAX_UNIT 10 /* last legal unit number */ +#define CNTLR_UNIT (DL_MAXDRIVE + 1) /* controller unit number */ +#define MAX_UNIT 10 /* last legal unit number */ -#define WORDS_PER_SECTOR 128 /* data words per sector */ +#define WORDS_PER_SECTOR 128 /* data words per sector */ -#define UNTALK_DELAY 160 /* ICD untalk delay (constant instruction count) */ -#define CNTLR_TIMEOUT S (1.74) /* command and parameter wait timeout (1.74 seconds) */ +#define UNTALK_DELAY 160 /* ICD untalk delay (constant instruction count) */ +#define CNTLR_TIMEOUT S (1.74) /* command and parameter wait timeout (1.74 seconds) */ -#define NO_EVENT -1 /* do not schedule an event */ +#define NO_EVENT -1 /* do not schedule an event */ #define NO_ACTION (CNTLR_IFN_IBUS) (NO_FUNCTIONS | NO_DATA) @@ -404,9 +406,9 @@ /* Controller clear types */ typedef enum { - Hard_Clear, /* power-on/preset hard clear */ - Timeout_Clear, /* command or parameter timeout clear */ - Soft_Clear /* programmed soft clear */ + Hard_Clear, /* power-on/preset hard clear */ + Timeout_Clear, /* command or parameter timeout clear */ + Soft_Clear /* programmed soft clear */ } CNTLR_CLEAR; diff --git a/HP3000/hp_disclib.h b/HP3000/hp_disclib.h index 4414ae3a..697fd17c 100644 --- a/HP3000/hp_disclib.h +++ b/HP3000/hp_disclib.h @@ -24,6 +24,7 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the authors. + 10-Oct-16 JDB Moved "hp3000_defs.h" inclusion to "hp_disclib.c" 13-May-16 JDB Modified for revised SCP API function parameter types 24-Mar-16 JDB Added the DL_BUFFER type to define the disc buffer array 21-Mar-16 JDB Changed uint16 types to HP_WORD @@ -42,10 +43,6 @@ -#include "hp3000_defs.h" /* this must reflect the machine used */ - - - /* Architectural constants. The type of the disc buffer element is defined. This must be a 16-bit array diff --git a/HP3000/hp_tapelib.c b/HP3000/hp_tapelib.c index dda62100..a2fd04c7 100644 --- a/HP3000/hp_tapelib.c +++ b/HP3000/hp_tapelib.c @@ -24,6 +24,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the authors. + 10-Oct-16 JDB Moved "hp3000_defs.h" inclusion from "hp_tapelib.h" 01-Jul-16 JDB Changed tl_attach to reset the event delay times pointer 09-Jun-16 JDB Added casts for ptrdiff_t to int32 values 08-Jun-16 JDB Corrected %d format to %u for unsigned values @@ -326,6 +327,7 @@ +#include "hp3000_defs.h" /* this must reflect the machine used */ #include "hp_tapelib.h" diff --git a/HP3000/hp_tapelib.h b/HP3000/hp_tapelib.h index bc792ad5..eedf423c 100644 --- a/HP3000/hp_tapelib.h +++ b/HP3000/hp_tapelib.h @@ -24,6 +24,7 @@ in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the authors. + 10-Oct-16 JDB Moved "hp3000_defs.h" inclusion to "hp_tapelib.c" 13-May-16 JDB Modified for revised SCP API function parameter types 24-Mar-16 JDB Added the TL_BUFFER type to define the tape buffer array 21-Mar-16 JDB Changed uint16 types to HP_WORD @@ -38,8 +39,6 @@ -#include "hp3000_defs.h" /* this must reflect the machine used */ - #include "sim_tape.h" diff --git a/Visual Studio Projects/HP3000.vcproj b/Visual Studio Projects/HP3000.vcproj index 56827769..628d0720 100644 --- a/Visual Studio Projects/HP3000.vcproj +++ b/Visual Studio Projects/HP3000.vcproj @@ -204,6 +204,10 @@ RelativePath="..\HP3000\hp3000_cpu_base.c" > + + @@ -220,6 +224,10 @@ RelativePath="..\HP3000\hp3000_lp.c" > + + @@ -317,6 +325,10 @@ RelativePath="..\HP3000\hp3000_io.h" > + + diff --git a/descrip.mms b/descrip.mms index 4625c388..c42bbf4c 100644 --- a/descrip.mms +++ b/descrip.mms @@ -432,9 +432,10 @@ HP3000_DIR = SYS$DISK:[.HP3000] HP3000_LIB1 = $(LIB_DIR)HP3000L1-$(ARCH).OLB HP3000_SOURCE1 = $(HP3000_DIR)HP3000_ATC.C,$(HP3000_DIR)HP3000_CLK.C,\ $(HP3000_DIR)HP3000_CPU.C,$(HP3000_DIR)HP3000_CPU_BASE.C,\ - $(HP3000_DIR)HP3000_CPU_FP.C,$(HP3000_DIR)HP3000_DS.C,\ - $(HP3000_DIR)HP3000_LP.C,\ - $(HP3000_DIR)HP3000_IOP.C,$(HP3000_DIR)HP3000_MPX.C,\ + $(HP3000_DIR)HP3000_CPU_CIS.C,$(HP3000_DIR)HP3000_CPU_FP.C,\ + $(HP3000_DIR)HP3000_DS.C,$(HP3000_DIR)HP3000_LP.C,\ + $(HP3000_DIR)HP3000_IOP.C,$(HP3000_DIR)HP3000_MEM.C,\ + $(HP3000_DIR)HP3000_MPX.C,\ $(HP3000_DIR)HP3000_MS.C,$(HP3000_DIR)HP3000_SCMB.C,\ $(HP3000_DIR)HP3000_SEL.C,$(HP3000_DIR)HP3000_SYS.C HP3000_LIB2 = $(LIB_DIR)HP3000L2-$(ARCH).OLB diff --git a/doc/hp3000_doc.doc b/doc/hp3000_doc.doc index 040f8714..efa3a6a3 100644 Binary files a/doc/hp3000_doc.doc and b/doc/hp3000_doc.doc differ diff --git a/makefile b/makefile index d7a88706..c73c06da 100644 --- a/makefile +++ b/makefile @@ -1215,9 +1215,10 @@ HP2100_OPT = -DHAVE_INT64 -I ${HP2100D} HP3000D = HP3000 HP3000 = ${HP3000D}/hp_disclib.c ${HP3000D}/hp_tapelib.c ${HP3000D}/hp3000_atc.c \ ${HP3000D}/hp3000_clk.c ${HP3000D}/hp3000_cpu.c ${HP3000D}/hp3000_cpu_base.c \ - ${HP3000D}/hp3000_cpu_fp.c ${HP3000D}/hp3000_ds.c ${HP3000D}/hp3000_iop.c \ - ${HP3000D}/hp3000_lp.c ${HP3000D}/hp3000_mpx.c ${HP3000D}/hp3000_ms.c \ - ${HP3000D}/hp3000_scmb.c ${HP3000D}/hp3000_sel.c ${HP3000D}/hp3000_sys.c + ${HP3000D}/hp3000_cpu_fp.c ${HP3000D}/hp3000_cpu_cis.c ${HP3000D}/hp3000_ds.c \ + ${HP3000D}/hp3000_iop.c ${HP3000D}/hp3000_lp.c ${HP3000D}/hp3000_mem.c \ + ${HP3000D}/hp3000_mpx.c ${HP3000D}/hp3000_ms.c ${HP3000D}/hp3000_scmb.c \ + ${HP3000D}/hp3000_sel.c ${HP3000D}/hp3000_sys.c HP3000_OPT = -I ${HP3000D}