2/26/16 - code that implements SET ATCD CONSOLE/NOCONSOLE was removed, as it essentially duplicates the SET CONSOLE TELNET functionality. However: - we want to use ATC 0 without attaching (so ATC must poll always). - we want to be able to disable the ATC (so PCLK polls) but must stop poll before disabling. To implement our own DISABLE, we need to: - add DISABLED, ENABLED to mtab (XTD_MNO) - remove DEV_DISABLE from device (so won't print an extra set of mods) - set and clear DEV_DIS as appropriate [hp3000_io.h] /* Global ATC state */ extern t_bool atc_has_console; /* TRUE if port 0 is connected to the simulation console */ [hp3000_atc.c] typedef enum { Console, No_Console, Terminal, Diagnostic, Fast_Time, Real_Time } DEVICE_MODES; { MTAB_XDV, Console, NULL, "CONSOLE", &atc_set_mode, NULL, (void *) &atcd_dev }, { MTAB_XDV, No_Console, NULL, "NOCONSOLE", &atc_set_mode, NULL, (void *) &atcd_dev }, (atc_set_mode) 2. When CONSOLE mode is enabled, the receive flag on channel 0 is cleared to ignore reception from any connected Telnet session. If NOCONSOLE mode is subsequently reestablished, reception is reenabled. This allows a system console session to remain connected during a temporary CONSOLE/NOCONSOLE switch, if desired. case Console: /* entering console host mode */ dptr->flags |= DEV_CONSOLE; /* so set the flag */ atc_has_console = TRUE; /* indicate that we have the console */ atcd_order [0] = 1; /* set the line order to omit channel 0 */ atcd_ldsc [0].rcve = FALSE; /* and disable channel reception */ resync_poll (); /* set up and (re)start the poll */ break; case No_Console: /* leaving console host mode */ dptr->flags &= ~DEV_CONSOLE; /* so clear the flag */ atc_has_console = FALSE; /* indicate that we no longer have the console */ atcd_order [0] = 0; /* set the line order to include channel 0 */ atcd_ldsc [0].rcve = TRUE; /* and enable channel reception */ if ((poll_unit.flags & UNIT_ATT) == 0) /* if the multiplexer is not attached */ sim_cancel (&poll_unit); /* then stop the input poll */ break; (atc_show_mode) if (dptr->flags & DEV_CONSOLE) /* if the console flag is set */ fputs ("console, ", st); /* then report that we have the console */ else /* otherwise */ fputs ("non-console, ", st); /* report that we do not have the console */ (atcd_reset) if (poll_unit.flags & UNIT_ATT || atc_has_console) /* if we're attached or hosting the simulation console */ resync_poll (); /* then set up and (re)start the poll */ else /* otherwise */ sim_cancel (&poll_unit); /* cancel the poll */ (atcd_attach) If we're not hosting the console, then the poll is not running, so start it and synchronize with the process clock to enable idling. If we are hosting the console, then the poll is already running and synchronized. if (status == SCPE_OK && atc_has_console == FALSE) /* if the attach succeeded and we don't have the console */ resync_poll (); /* then set up and start the poll */ (atcd_detach) if (atc_has_console == FALSE) /* if we are not hosting the simulation console */ sim_cancel (&poll_unit); /* then stop the poll */ (line_service) if (channel == 0 && atc_has_console) /* then if we are hosting the simulation console */ result = sim_putchar_s (cvtd_data); /* then output it to the console */ else /* otherwise */ result = tmxr_putc_ln (&atcd_ldsc [channel], /* output it to the line */ cvtd_data); if (atc_has_console) { /* if we are hosting the simulation console */ status = sim_poll_kbd (); /* then poll the keyboard for input */ (receive) if (char_echo >= 0) { /* if the converted character is valid for the mode */ if (channel == 0 && atc_has_console) /* then if we are hosting the simulation console */ sim_putchar (char_echo); /* then write it back to the console */ [hp3000_cpu.c] if (atc_has_console == FALSE) { /* if the ATC is not hosting the simulation console */ status = sim_poll_kbd (); /* then we must poll for a console interrupt */ -------------------------------------------------------------------------------- [hp3000_sys.c] extern DEVICE lp_dev; /* 2613/17/18 Line Printer */ -------------------------------------------------------------------------------- [3.x] TMXR conn: - tmxr_reset_ln calls sim_close_sock (lp->conn) and sets to 0 - tmxr_poll_rx calls sim_read_sock (lp->conn) if conn AND rcve are TRUE [rcve set only by user] - tmxr_close_master calls tmxr_reset_ln() if conn TRUE [called by tmxr_detach] - tmxr_dscln calls tmxr_reset_ln() if conn TRUE [called only by user] -------------------------------------------------------------------------------- [hp3000_cpu_base.c] The standard FP instructions do: - DFLT (030): RB,RA = RB,RA - FLT (047): RB,RA = RA - FADD (051): RB,RA = RD,RC + RB,RA - FSUB (052): RB,RA = RD,RC - RB,RA - FMUL (053): RB,RA = RD,RC * RB,RA - FDIV (054): RB,RA = RD,RC / RB,RA - FIXR (070): RB,RA = RB,RA - FIXT (071): RB,RA = RB,RA All do CCA on RB,RA The EIS FP instructions do: - EADD (010): [RC] = [RB] + [RA] (three-word add is 000) - ESUB (011): [RC] = [RB] - [RA] (three-word sub is 001) - EMPY (012): [RC] = [RB] * [RA] (three-word mpy is 002) - EDIV (013): [RC] = [RB] / [RA] (three-word div is 003) All do CCA on [RC] The HP 1000 FPP simulator uses: uint32 fp_exec (uint16 opcode, OP *result, OP operand_l, OP operand_r); ...where "opcode" supplies the indications for the arithmetic operation (e.g., add or subtract), and the resulting operand length (e.g., two-word or three-word). The routine determines the operand precisions, unpacks the operands, dispatches the operation, rounds and packs the result, and returns an overflow indicator (0 or 1). Maybe: typedef enum { fp_add, fp_sub, fp_mpy, fp_div, fp_flt, fp_fixr, fp_fixt } FP_OPR; extern FP_OPD fp_exec (FP_OPR operation, FP_OPD left, FP_OPD right); Then for FADD, etc.: op_w = fp_exec ((FP_OPR) (operation - 051 + fp_add), op_u, op_v); -------------------------------------------------------------------------------- [hp3000_cpu_base.c] SCU, SCW: t_stat scan_bytes (t_bool scan_while); // TRUE for SCW, FALSE for SCU -- basically does everything for both instructions -------------------------------------------------------------------------------- [hp3000_cpu_base.c, convert_address routine debugging] #if 1 if (sim_deb) { sim_eval [0] = CIR; sim_eval [1] = NIR; fprint_cpu (sim_deb, sim_eval, 0, SIM_SW_STOP); fprintf (sim_deb, " offset %06o, length %d, start %06o, end %06o, size %d\n", byte_offset, SEXT (block_length), starting_word, ending_word, abs ((int32) ending_word - (int32) starting_word) + 1); fprintf (sim_deb, " A %06o, B %06o, C %06o, D %06o, ", RA, RB, RC, RD); fprintf (sim_deb, "PB %06o, PL %06o, DL %06o, DB %06o, SM %06o\n", PB, PL, DL, DB, SM); } #endif -------------------------------------------------------------------------------- [hp3000_cpu_base.c] This method of non-canonical decoding: // 1. For bits 12-15, IXIT = 0000, PCN = nnn0, LOCK = nn01, and UNLK = nn11. static const uint32 op_3637 [12] = { /* indexed by bits 12-15 */ IXIT, LOCK, PCN, UNLK, /* 0000 0001 0010 0011 */ PCN, LOCK, PCN, UNLK, /* 0100 0101 0110 0111 */ PCN, LOCK, PCN, UNLK, /* 1000 1001 1010 1011 */ PCN, LOCK, PCN, UNLK /* 1100 1101 1110 1111 */ }; case 036: /* IXIT, LOCK, PCN, and UNLK */ case 037: if (NPRV) /* if the operation is legal but in user mode */ MICRO_ABORT (trap_Privilege_Violation); /* then abort with a privilege violation */ operation = SPECOP (CIR); /* mask to the special operation code */ if (!(cpu_stop_flags & SS_UNDEF)) /* if UNDEF is inactive */ operation = SPECOP (op_3637 [operation]); /* then decode as in hardware */ switch (operation) { /* decode the special operation */ case 000: /* IXIT (none; MODE, STOV, CSTV, TRACE, ABS CST, BNDV) */ case 001: /* LOCK (none; MODE) */ case 002: /* PCN (none; STOV, MODE) */ case 003: /* UNLK (none; MODE) */ default: status = STOP_UNIMPL; /* stop the simulator */ break; } ...produces: testb $-128, _STA+1 je L822 movl _CIR, %eax andl $15, %eax testb $4, _cpu_stop_flags jne L935 movl _op_3637.4224(,%eax,4), %eax andl $15, %eax L935: cmpl $1, %eax je L1272 ; LOCK jb L938 ; IXIT cmpl $2, %eax je L1360 ; PCN movl $2, 108(%esp) ; UNLK jmp L796 This alternate method: if (NPRV) /* these are privileged instructions */ MICRO_ABORT (trap_Privilege_Violation); switch (CNTLOP (CIR)) { /* decode the control operation */ case 000: /* XCHD (none; STUN, MODE) */ case 005: /* these decode as PSDB in hardware */ case 011: case 015: if (cpu_stop_flags & SS_UNDEF) { /* if the undefined instruction stop is active */ status = STOP_UNIMPL; /* then stop the simulator */ break; } /* otherwise fall into the PSDB executor */ case 001: /* PSDB (none; MODE) */ case 004: /* these decode as DISP in hardware */ case 006: case 010: case 012: case 014: case 016: if (cpu_stop_flags & SS_UNDEF) { /* if the undefined instruction stop is active */ status = STOP_UNIMPL; /* then stop the simulator */ break; } /* otherwise fall into the DISP executor */ case 002: /* DISP (CCx; MODE, CSTV, TRACE, ABS CST, BNDV) */ case 007: /* these decode as PSEB in hardware */ case 013: case 017: if (cpu_stop_flags & SS_UNDEF) { /* if the undefined instruction stop is active */ status = STOP_UNIMPL; /* then stop the simulator */ break; } /* otherwise fall into the PSEB executor */ case 003: /* PSEB (CCx; MODE, CSTV, TRACE, ABS CST, BNDV) */ } /* (the switch is fully decoded) */ ...produces: testb $-128, _STA+1 je L1411 movl _CIR, %eax andl $15, %eax jmp *L1417(,%eax,4) L1417: .long L1415 .long L1416 .long L1418 .long L1419 .long L1420 .long L1421 .long L1420 .long L1422 .long L1420 .long L1421 .long L1420 .long L1422 .long L1420 .long L1421 .long L1420 .long L1422 L1422: movl $2, %eax testb $4, _cpu_stop_flags jne L1384 ; returns with STOP_UNDEF -------------------------------------------------------------------------------- [hp3000_cpu_base.c] DBBC refs: 2030 (MVBW), 2033 (MVBW), 2052 (MVB/CMPB), 2064 (MVB/CMPB), 2167 (SCU, SCW) -------------------------------------------------------------------------------- [hp3000_cpu_base.c] /* SETR 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | 0 0 1 0 | register op | SK| DB| DL| Z |STA| X | Q | S | Register +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Microcode PDF page 48 (001476). Load order is left-to-right. Must be privileged if bits 8-11 are set. If not privileged, only bits 2 and 4-7 are loaded into STA; otherwise, all bits are loaded. Microcode wants SR=4 on entry. If SBANK is to be loaded, it's done at the end after all other registers are set from the current SBANK, otherwise queue_ups would get wrong values. Bit 9 loads DB and DBANK in that order from the stack. All stack registers are QDWNed before setting S (so S = SM). Q and S are bounds-checked before setting. Microcode queues up parameters after setting, except for Q and S. Setting S is done by flushing the TOS queue (SR = 0) and setting SM. */ -------------------------------------------------------------------------------- [hp3000_cpu.c] Undefined instructions affected by STOP=UNDEF: Inst Canon Notes Reserved Defined As Decoded As ---- ------ ----- -------- ----------- ----------- IXIT 020360 1 12-13 0 0 0 0 LOCK 020361 1,2 12-13 0 0 n n PCN 020362 1 12-13 0 0 n n UNLK 020363 1,2 12-13 0 0 n n SED 030040 3 12-14 0 0 0 x x x XCHD 030060 4 12-13 0 0 0 0 PSDB 030061 4 12-13 0 0 n n DISP 030062 4 12-13 0 0 n n PSEB 030063 4 12-13 0 0 n n SMSK 030100 5 12-14 0 0 0 0 0 0 SCLK 030101 5 12-14 0 0 0 n n n RMSK 030120 5 12-14 0 0 0 0 0 0 RCLK 030121 5 12-14 0 0 0 n n n Notes: 1. For bits 12-15, IXIT = 0000, PCN = nnn0, LOCK = nn01, and UNLK = nn11. 2. Presumably, 020361 LOCK and 020363 UNLK cause Unimplemented Instruction traps on non-Series II systems, though we'd need a Series III microcode listing to be certain. 3. SED only works correctly if opcodes 030040 and 030041 are used. Opcodes 030042-030057 also decode as SED, but the status register is set improperly (the I bit is cleared, bits 12-15 are rotated right twice and then ORed into the status register). 4. For bits 12-15, XCHD = 0000, DISP = nnn0, PSDB = nn01, and PSEB = nn11. 5. For bits 12-15, SMSK and RMSK = 0000, SCLK and SMSK = nnnn. -------------------------------------------------------------------------------- In practice, a U / V = W integer division can overflow only if the quotient W cannot be represented in the size available. The microcode does: - DIVI : no overflow possible - LDIV : overflow if MSU >= V - DIV : overflow if MSU >= V or W /= 0 and sign W /= sign U XOR sign V - DIVL : overflow if MSU >= V or W /= 0 and sign W /= sign U XOR sign V - DDIV : overflow if MSW = -32768 But we could do: - LDIV : overflow if upper 16 bits of quotient /= 0 - DIV : overflow if dividend = D16_SMIN and divisor = D16_UMAX - DIVL : overflow if upper 17 bits of quotient /= all zeros or all ones - DDIV : overflow if dividend = D32_SMIN and divisor = D32_UMAX DIVI [0542] : RA / imm => RA = quotient q16 = s16 / u8; no overflow possible LDIV [0663] : RC,RB / RA => RB = quotient, RA = remainder q16, r16 = u32 / u16; o = mask16 DIV [0721] : RB / RA => RB = quotient, RA = remainder q16, r16 = s16 / s16; o = mask17 overflows only if U = -2**15 and V = -1 (quotient = +2**15) DIVL [0724] : RC,RB / RA => RB = quotient, RA = remainder q16, r16 = s32 / s16; o = mask17 DDIV [7300] : RD,RC / RB,RA => RD,RC = quotient, RB,RA = remainder q32, r32 = s32 / s32; o = mask33 overflows only if U = -2**31 and V = -1 (quotient = +2**31) -------------------------------------------------------------------------------- [hp3000_cpu_base.h] // DIV : q16, r16 = s16 / s16; o = mask17 // DIVL : q16, r16 = s32 / s16; o = mask17 // LDIV : q16, r16 = u32 / u16; o = mask16 // DDIV : q32, r32 = s32 / s32; o = mask32 // DIVI : q16 = s16 / u8; no overflow possible /* Divide a 32-bit number by a 16-bit number */ uint32 cpu_div_16 (uint32 dividend, uint32 divisor) { int32 quotient, remainder; uint32 check; if (divisor == 0) /* if dividing by zero */ MICRO_ABORT (trap_Integer_Zero_Divide); /* then trap or set the overflow flag */ else { /* not dividing by zero */ if (abs (divisor) <= UPPER_WORD (abs (dividend))) /* if the divisor is <= the MSW of the dividend */ check = 1; /* an overflow will occur on the division */ else { /* otherwise, the divisor might be large enough */ quotient = dividend / divisor; /* form a signed quotient */ remainder = dividend % divisor; /* and remainder */ check = (uint32) quotient & S16_OVFL_MASK; /* check the top 17 bits */ RA = remainder & D16_MASK; /* TOS = remainder */ RB = quotient & D16_MASK; /* NOS = quotient */ SET_CCA (RB, 0); /* set the condition code */ } SET_OVERFLOW (check != 0 && check != S16_OVFL_MASK); /* overflow if top 17 bits are NOT all zeros or all ones */ } return ...; } -------------------------------------------------------------------------------- [hp3000_cpu.h] /* CCC (comparison) sets CC = - CCG (00) if operand 1 > operand 2 - CCL (01) if operand 1 < operand 2 - CCE (10) if operand 1 = operand 2 */ #define SET_CCC(a,b) STA = (STA & ~STATUS_CC_MASK | \ ((a) == (b) ? STATUS_CCE : \ SEXT(a) < SEXT(b) ? STATUS_CCL : STATUS_CCG)) /* CCZ (arithmetic) sets CC = - CCG (00) if operand > 0 - CCE (10) if operand = 0 */ #define SET_CCZ(v) STA = (STA & ~STATUS_CC_MASK) | \ ((v) == 0 ? STATUS_CCE : STATUS_CCG) /* Status register operations */ #define SET_C(v) STA = ((v) ? STA | STATUS_C : STA & ~STATUS_C) #define SET_O(v) STA = ((v) ? STA | STATUS_O : STA & ~STATUS_O) /* #define SET_CO(r,s,t) STA &= ~(STATUS_C | STATUS_O); \ if ((t) > D16_UMAX) \ STA = STA | STATUS_C; \ if ((~(r) ^ (s)) & ((s) ^ (t)) & D16_SIGN) \ STA = STA | STATUS_O */ /* set carry and overflow status */ // // >>> NOTE THAT ARITH TRAP ALWAYS OCCURS ON OVERFLOW IF IT'S ENABLED! // #define SET_CO(r,s,t) SET_C ((t) > D16_UMAX); \ SET_O ((~(r) ^ (s)) & ((s) ^ (t)) & D16_SIGN) -------------------------------------------------------------------------------- [hp3000_cpu_base.c] /* CPU base set global utility routines */ HP_WORD cpu_branch_ea (HP_WORD instruction) HP_WORD cpu_branch_disp (HP_WORD instruction) t_stat cpu_branch_to (HP_WORD address) HP_WORD cpu_add_16 (HP_WORD augend, HP_WORD addend) HP_WORD cpu_sub_16 (HP_WORD minuend, HP_WORD subtrahend) HP_WORD cpu_mpy_16 (HP_WORD multiplicand, HP_WORD multiplier) // uint32 cpu_div_16 (uint32 dividend, uint32 divisor) /* CPU base set global instruction execution routines */ t_stat cpu_stack_op (void) t_stat cpu_shift_branch_bit_op (void) t_stat cpu_move_spec_fw_imm_field_reg_op (void) t_stat cpu_io_cntl_prog_imm_mem_op (void) /* CPU base set local utility routines */ static uint32 add_32 (uint32 augend, uint32 addend) static uint32 sub_32 (uint32 minuend, uint32 subtrahend) static void shift_16_32 (SHIFT_TYPE shift, OPERAND_SIZE op_size) static void shift_48_64 (SHIFT_TYPE shift, OPERAND_SIZE op_size) static void check_stack_bounds (HP_WORD new_value) static uint32 convert_address (uint32 byte_address, uint32 block_length) static t_bool interrupt_pending (t_stat *status) /* CPU base set local instruction execution routines */ static t_stat move_spec (void) static t_stat firmware_extension (void) static t_stat io_control (void) -------------------------------------------------------------------------------- [hp3000_cpu.c] static t_stat halt_mode_interrupt (HP_WORD device_number) [...] static UNIT *uptr; const DEVICE *dptr; const DIB *dibptr; uint32 i; [...] for (i = 0; sim_devices [i] != NULL; i++) { /* loop through all of the devices */ dptr = sim_devices [i]; /* get a pointer to the device */ dibptr = (DIB *) dptr->ctxt; /* and to that device's DIB */ if (dibptr != NULL /* if this device is the cold load device */ && cold_load_device == (HP_WORD) dibptr->device_number) { uptr = dptr->units; /* then save a pointer to unit 0 */ break; /* and wait for the channel to finish */ } } [...] else /* otherwise the load is in progress */ if (sel_is_idle && mpx_is_idle /* if both the channel */ && !sim_is_active (uptr)) /* and the device are finished */ if ((CPX1 & cpx1_EXTINTR) == 0) /* then if no interrupt is pending */ return STOP_SYSHALT; /* then a System Halt occurs */ else { /* otherwise an interrupt is pending */ CPX1 = CPX1 & ~cpx1_EXTINTR; /* so clear it */ iop_direct_io (device_number, ioRIN, 0); /* reset the device interrupt */ if (device_number == cold_load_device) { /* if the expected device interrupted */ CPX2 = CPX2 & ~cpx2_LOADSWCH; /* then reset the LOAD switch */ cpu_micro_state = running; /* clear the load-in-progress state */ MICRO_ABORT (trap_Cold_Load); /* and execute the cold load trap handler */ } } /* otherwise continue waiting forever */ -------------------------------------------------------------------------------- [hp3000_cpu.c] void cpu_segment_abort (TRAP_CLASS trap, HP_WORD status, HP_WORD parameter) { if ((trap == trap_Trace || trap == trap_CS_Absent) /* for trace or absent traps */ && (parameter & LABEL_EXTERNAL)) { /* detected during PCAL */ STA = status; /* then set the new status before trapping */ cpu_mark_stack (); /* and write a stack marker to memory */ } if (STT_SEGMENT (status) < 2 /* if the trap occurred in segment 0 or 1 */ || trap == trap_CS_Absent && (CPX1 & cpx1_ICSFLAG)) /* or an absence trap occurred while on the ICS */ MICRO_ABORT (trap_System_Halt); /* then the failure is fatal */ else /* otherwise */ MICRO_ABORT (TO_DWORD (parameter, trap)); /* continue with the indicated trap */ } cpu_segment_abort is called for these traps: - trap_STT_Violation : SYSH if segment < 2 (direct for CS 0, segchecks otherwise) - trap_CST_Violation : SYSH if segment < 2 (all segchecks) - trap_Trace : SYSH if segment < 2 - trap_CS_Absent : SYSH if segment < 2 or on ICS for trap_Trace and trap_CS_Absent, PCAL call must set new status and mark the stack -------------------------------------------------------------------------------- [hp3000_cpu.c] /* Alternate stack operations using an array and register renaming. int RN; // namer register // not sure how we'd handle these in the REG definitions... // maybe as a queue (see pcq in hp2100_cpu.c), but have to forego RA-RD for TR[0-4]. // might change REG names on the fly at sim_instr exit. #define RA TR [RN] #define RB TR [(RN + 1) & 3] #define RC TR [(RN + 2) & 3] #define RD TR [(RN + 3) & 3] void cpu_push (void) { if (SR == 4) M [++SM] = RD; else SR = SR + 1; RN = (RN - 1) & 3; return; } void cpu_pop (void) { if (SR == 0) SM = SM - 1; else { RN = (RN + 1) & 3; SR = SR - 1; } return; } void cpu_queue_up (void) { TR [(RN + SR++) & 3] = M [SM--]; return; } void cpu_queue_down (void) { M [++SM] = TR [(RN + SR--) & 3]; return; } void add_a (void) { while (SR < 2) qup_a (); RB = RA + RB; pop_a; return; } */ -------------------------------------------------------------------------------- - does interrupted instruction resume properly? diag doesn't test! - rearrange loop from "channel/interrupt-or-next+exec" to "channel/exec/interrupt-or-next" ? cleaner entry? reason not to? current: loop events channels if running or paused if runirq then interrupt else if breakpoint then stop else if paused then idle else next execute else if haltirq then interrupt decrement end loop proposed: loop events channels if running if breakpoint then stop else execute if runirq then interrupt else next else if paused if runirq then interrupt else idle else if haltirq then interrupt decrement end loop -------------------------------------------------------------------------------- Issues with stopping and restarting the simulation (e.g., via STEP or BREAK). To test, break on the following locations and then proceed as indicated. tests with CPU04: + (00.020736) breakpoint on single stackop + (00.020737) breakpoint after single stackop + (00.020743) breakpoint on dual stackop + (00.020744) breakpoint after dual stackop + (00.020736) step into single stackop + (00.020743) step into dual stackop + (00.021261) step into instruction with interrupt pending + (00.021275) step into single stackop with interrupt intervening + (00.020203) step into dual stackop with interrupt intervening + (00.023032) step into PAUS with trap + (00.021124) step into PAUS with interrupt pending + (00.020403) step into interruptable instruction with interrupt intervening (MOVE 0) + (00.024340) step into interrupt doesn't set CIR correctly on reentry tests with CPU02: + (00.012527) step into XEQ does not execute the target instruction (LDNI 1) Pressing the RUN/HALT switch with an interrupt pending causes entry into microcode label IR. The RUN switch is tested before any run-time interrupts, so the interrupt remains pending while the machine is halted. The halt uses the same microcode as a HALT instruction, except for the NPRV test. Restarting loads NIR from M [P], increments P, and then does a NEXT, which is not performed because the interrupt is pending. On entry to IR, CIR still retains its previous value of the instruction that finished executing immediately before the HALT switch was pressed. Setup: - CIR = LOAD - NIR = ADD,ADD (so BPENDING = 1) NEXT for LOAD: - [CLK 0] NEXT := 1, NIRTOCIR = 1 - [CLK 1] NXTFINH := 1, STA3 := 1, INCP = 0 - [CLK 2] ? The process is (presumably): - LOAD executes - [LOAD] NEXT - if IRQ, exit to ucode 3 - CIR = NIR - NIR = M [P] - P = P + 1 - STATUS.3 = 0 - STOR executes - [STOR] NEXT - if IRQ, exit to ucode 3 - CIR = NIR - NIR = M [P] - P = P - STATUS.3 = 1 (but in sim, STATUS.3 isn't set by NEXT but rather within left stackop processing!) - left ADD executes - [ADD,---] NEXT - if IRQ, exit to ucode 3 - P = P + 1 - STATUS.3 = 0 (but in sim, CIR/NIR/P = P + 1 occurs because STATUS.3 isn't set as part of NEXT processing above!) (a trap here gets the wrong P!) (STATUS.3 = 0 even if BPENDING for another dual-stackop follows because NIRTOCIR is inhibited) - right ADD executes - [---,ADD] NEXT - if IRQ, exit to ucode 3 - CIR = NIR - NIR = M [P] - P = P + 1 - STATUS.3 = 0 - left SUB executes - [SUB,NOP] NEXT - if IRQ, exit to ucode 3 - CIR = NIR - NIR = M [P] - P = P + 1 - STATUS.3 = 0 halt-to-run: - NIR = M [P] - P = P + 1 - (NEXT) CIR = NIR - NIR = M [P] - P = P + 1 - (execute instruction in CIR) The "increment P" function is inhibited if - an interrupt is pending - STA.3 (R) is set sim_instr entry/exit conditions: - P points at the instruction to execute - if R is set, next is execution of right-hand stack op stepping through one non-stack-op instruction: - entry: P points at the instruction to execute - NIR = M [P] - P = P + 1 - (NEXT) CIR = NIR - P = P + 1 - NIR = M [P] - execute instruction - exit: P = P - 1 stepping through one left-hand stack-op instruction: - entry: P points at the instruction to execute - NIR = M [P] - P = P + 1 - (NEXT) CIR = NIR - inhibited: P = P + 1 - inhibited: NIR = M [P] - execute left instruction; sets R - exit: P = P - 1 stepping through one right-hand stack-op instruction: - entry: P points at the instruction to execute - NIR = M [P] - P = P + 1 - (NEXT) CIR = NIR - P = P + 1 - NIR = M [P] - execute right instruction - exit: P = P - 1 -------------------------------------------------------------------------------- [hp3000_cpu.c] microcode correspondence case trap_Unimplemented: case trap_STT_Violation: case trap_CST_Violation: case trap_DST_Violation: case trap_Stack_Underflow: case trap_Privilege_Violation: case trap_Bounds_Violation: parameter = label; /* the label is the parameter for these traps */ case trap_User: case trap_DS_Absent: // INT7 cpu_flush (); /* flush the TOS registers to memory */ cpu_mark_stack (); /* write a stack marker to memory */ case trap_Trace: case trap_CS_Absent: case trap_Uncallable: // INT5 STA = STATUS_M; /* clear status and enter privileged mode */ // INT6 SM = SM + 1 & R_MASK; /* increment the stack pointer */ cpu_write_memory (stack, SM, parameter); /* and push the parameter on the stack */ X = CIR; /* save the CIR in the index register */ // PCL3, etc. cpu_call_procedure (label); /* set up PB, P, PL, and STA to call the procedure */ break; case trap_Cold_Load: // ICS status = STOP_CLOAD; /* cold load is complete */ /* fall into trap_Stack_Overflow */ case trap_Stack_Overflow: if (CPX1 & cpx1_ICSFLAG) /* if the trap occurred while on the ICS */ MICRO_ABORT (trap_System_Halt); /* then the failure is fatal */ cpu_setup_ics_irq (irq_Trap, trap); /* otherwise, set up the trap on the ICS */ // INT6 SM = SM + 1 & R_MASK; /* increment the stack pointer */ cpu_write_memory (stack, SM, parameter); /* and push the parameter on the stack */ X = CIR; /* save the CIR in the index register */ // PCL3, etc. cpu_call_procedure (label); /* set up PB, P, PL, and STA to call the procedure */ break; -------------------------------------------------------------------------------- CCC tests: CMP [0615] -- (2s) CCA on RB-RA LCMP [0642] -- (2u) CCZ on RB-RA if RB > RA, else CCL DCMP [0636] -- (4s) CCA on RD-RB; if 0, CCZ on RC-RA FCMP [1201] -- (4s) CMPI [0537] -- (2s) CMPN [0537] -- (2s) The microcode "special field" has microorders for CCA, CCB, CCE, CCG, and CCL only. Comparisons are done by subtracting and setting CC = CCA. -------------------------------------------------------------------------------- [hp3000_cpu.c] In the halt state, all CPX1 bits EXCEPT power fail are held off. "Clearing the power fail interrupt inhibits all other interrupt bits in CPX1 and CPX2" [uc p230]. So a future SET CPU POWERFAIL would set the cpx1_PFINTR bit and then RUN the simulator. The cpx2_RUN bit would indicate whether the power failure occurred while the CPU was running or halted. "Module interrupt and external interrupt POLL are enabled by STA(1)" [uc p231]. That is, cpx1_MODINTR is conditioned by STATUS_I. -------------------------------------------------------------------------------- // "ex -c X" or other register does NOT print a character string! // -a and -c are ignored, whereas -o, -d, and -h are accepted // These work for examining memory but not register values // // Fix is to add REG_VMIO to all registers that should allow -a and -c. -------------------------------------------------------------------------------- [hp3000_sys.c] /* Search table. For symbolic entry, have an array of array pointers, one for each lookup array. Scan through them in order, looking for a matching mnemonic. NULL ends each lookup array. May need a separate opDI255X (which forces I on) to allow use of 0143000 for symbolic entry of "BR DB/Q/S,I". stackop 072 may need opReserved to stop parse? or parse only if leading letter? static const OP_TABLE *search_table [] = { & stack_ops, & sbb_ops, & msfifr_ops, & ioc_ops, & pmi_ops, & mlb_ops, NULL }; */ //----- mnemonic support is not present in the first release! LOAD LDX { DB+usi255 } LRA { P+usi255 } CMPM { P-usi255 } [,I] [,X] ADDM { Q+usi127 } SUBM { Q-usi63 } MPYM { S-usi63 } -------------------------------------------------------------------------------- [hp_tapelib.c/h] /* Classify the current controller opcode. The controller opcode is classified as a read, write, control, or status command, and the class is returned to the caller. If the opcode is illegal or undefined for the indicated controller, the class is marked as invalid. */ CNTLR_CLASS tl_classify (CVPTR cvptr) { UNIT *const uptr = cvptr->device->units + cvptr->unit_selected; if (cvptr->type <= LAST_CNTLR && uptr->OPCODE <= LAST_OPCODE) /* if the controller and opcode are legal */ return cmd_props [uptr->OPCODE].class; /* then return the command class */ else /* else the type or opcode is illegal */ return Class_Invalid; /* so return an invalid class */ } /* Return the name of a command phase. A string representing the supplied phase is returned to the caller. If the phase is illegal, the string "invalid" is returned. */ const char *tl_phase_name (CNTLR_PHASE phase) { if (phase <= LAST_PHASE) /* if the phase is legal, */ return phase_names [phase]; /* return the phase name */ else /* the phase is illegal, */ return invalid_name; /* so return an error indication */ } -------------------------------------------------------------------------------- HP 3000 status word format: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | S | O | I | unit | E | P | R | L | D | W | M | err code | T | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | | | | | | | Where the fields are: c S = SIO OK (= sio ok ff * SIOENABLE from mux) c O = Odd number of bytes (= ff) c I = Interrupt request (= ms_dib.interrupt_request) c unit = Selected unit number 0-3 (= usel ffs) u E = End of tape [SET] (= sim_tape_eot) u P = Protected [SFP] (= sim_tape_wrp) u R = Ready [SR] u L = Load point [SLP] (= sim_tape_bot) u D = Density (800/1600) u W = Write status [SW] c M = Tape mark (= ff) c err code = Encoded error field (= enc tape error, tape runaway, etc. ffs) c T = Track count (9 track/7 track) (= 0 gnd) HP 1000 status word format: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | D | unit | S | O | W | U | I | F | L | E | T | R | P | D | X | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | | | | | | | 13181 0 0 0 0 13183 Where the fields are: -c D = Density (800/1600) (= +5 TRUE for 13183) -c unit = Selected unit number 0-3 (= encoded ffs) -c S = Single track error cc O = Odd number of bytes (= ff) uu W = Rewinding [SRW] uu U = Tape unit busy [-SR] (= sim_is_active) cc I = Interface busy (= ff) cc F = End of file (= gate) uu L = Load point [SLP] (= sim_tape_bot) uu E = End of tape [SET] (= sim_tape_eot) cc T = Timing error (= ff) cc R = Command rejected (= ff) uu P = Protected [SFP] (= sim_tape_wrp) cc D = Data error (= ff) uu X = Tape unit offline [-SL] (= UNIT_OFFLINE flag) -------------- HP 3000 control word format: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | x x x x x x | unsel | reserved | command code | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Unit select values range from 0-3. Command codes are as follows: 00 = Select 04 = Write record 05 = Write gap 06 = Read record 07 = Forward space record 10 = Rewind 11 = Rewind and reset 12 = Backspace record 13 = Backspace file 14 = Write record with zero parity 15 = Write file mark 16 = Read record with CRCC 17 = Forward space file Codes 01-03 are reserved, and codes 14 and 16 are used for diagnostics only. HP 1000 control word format: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | x x x | 3 | 2 | 1 | 0 | command code | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Command codes are as follows: 00003 FSR = Forward space record 00015 GAP = Write gap 00023 RDR = Read record 00031 WRR = Write record 00041 BSR = Backspace record 00061 RRR = Read record backward 00101 REW = Rewind 00105 RWS = Rewind and go offline 00110 CLR = Clear 00203 FSF = Forward space file 00211 WFM = Write file mark 00215 GFM = Gap and file mark 00223 RFF = Read file forward (diag) 00241 BSF = Backspace file 01400 SL0 = Select unit 0 02400 SL1 = Select unit 1 04400 SL2 = Select unit 2 10400 SL3 = Select unit 3 HP 3000 status word format: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | S | O | I | unit | E | P | R | L | D | W | M | err code | T | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where the fields are: S = SIO OK O = Odd number of bytes I = Interrupt request unit = Selected unit number 0-3 E = End of tape P = Protected R = Ready L = Load point D = Density (800/1600) W = Write status M = Tape mark err code = Encoded error field T = Track count (9 track/7 track) The encoded error field values are: 000 = Unit interrupt 001 = Transfer error 010 = Command reject 011 = Tape runaway 100 = Timing error 101 = Tape error 110 = (reserved) 111 = No error HP 1000 status word format: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | D | unit | S | O | W | U | I | F | L | E | T | R | P | D | X | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Where the fields are: D = Density (800/1600) unit = Selected unit number 0-3 S = Single track error O = Odd number of bytes W = Rewinding U = Tape unit busy I = Interface busy F = End of file L = Load point E = End of tape T = Timing error R = Command rejected P = Protected D = Data error X = Tape unit offline -------------- // mux_order_ram wants to be 5 bits: first 4 are REG value; 5th is extra bit for enum type! // must roll our own defn, because BRDATA always assumes offset = 0 REG mux_reg [] = { { BRDATA (STAT, mux_state_ram, 2, 4, MUX_CNTL_COUNT) }, { BRDATA (AUX, mux_aux_ram, 8, 6, MUX_CNTL_COUNT) }, { "ORDER", mux_order_ram, 8, 4, 1, MUX_CNTL_COUNT }, { BRDATA (CNTR, mux_cntr_ram, 8, 12, MUX_CNTL_COUNT) }, { BRDATA (ADDR, mux_addr_ram, 8, 16, MUX_CNTL_COUNT) }, { NULL } }; --------- /* SIO program orders */ #define SIO_ORDER_MASK 0174000 #define SIO_ORDER_SHIFT 11 #define TO_SIO_ORDER(v) (SIO_ORDER) (((v) & SIO_ORDER_MASK) >> SIO_ORDER_SHIFT) typedef enum { sioJMP = 000, /* Jump unconditionally */ sioJMPC = 001, /* Jump conditionally */ sioRR = 002, /* Return residue */ sioSBNK = 003, /* Set bank */ sioINT = 004, /* Interrupt */ sioEND = 006, /* End */ sioENDI = 007, /* End with interrupt */ sioCNTL = 010, /* Control */ sioSENS = 012, /* Sense */ sioWRIT = 014, /* Write */ sioREAD = 016 /* Read */ } SIO_ORDER; ------- 2. Adding I/O devices. These modules must be modified: hp3000_defs.h add interrupt request definition hp3000_sys.c add sim_devices table entry ---- /* CPU model definition flags */ #define CPU_MODEL_SHIFT 0 #define CPU_MODEL_MASK 001 /* CPU model mask */ #define SERIES_II (0 << CPU_MODEL_SHIFT) /* Series II */ #define SERIES_III (1 << CPU_MODEL_SHIFT) /* Series III */ /* CPU unit flags */ #define UNIT_MODEL (UNIT_V_UF + 0) /* CPU model bits */ /* PC queue */ #define PCQ_SIZE 64 /* must be 2**n */ #define PCQ_MASK (PCQ_SIZE - 1) #define PCQ_ENTRY pcq [pcq_p = (pcq_p - 1) & PCQ_MASK] = err_PC /* CPU global functions */ ------------ /* CPU local data */ uint16 pcq [PCQ_SIZE] = { 0 }; /* PC queue */ uint32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ /* CPU global data */ /* External data */ extern DEVICE *sim_devices []; extern t_bool sim_idle_enab; { BRDATA (PCQ, pcq, 8, 18, PCQ_SIZE), REG_RO | REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { UNIT_EIS, UNIT_EIS, "EIS", "EIS", &cpu_set_opt, NULL, NULL }, { UNIT_EIS, 0, "no EIS", NULL, NULL, NULL, NULL }, { MTAB_XTD | MTAB_VDV, UNIT_EIS, NULL, "NOEIS", &cpu_clr_opt, NULL, NULL }, pcq_r->qptr = pcq_p; /* update the PC queue pointer */ if (M == NULL) { /* initial call after startup? */ pcq_r = find_reg ("PCQ", NULL, dptr); /* get a pointer to the PC queue */ if (pcq_r) /* defined? */ pcq_r->qptr = 0; /* initialize the queue pointer */ else /* not defined */ return SCPE_IERR; /* internal error */ ------------ static char cpu_stack_ops [] = "NOP ,DELB,DDEL,ZROX,INCX,DECX,ZERO,DZRO,DCMP,DADD,DSUB,MPYL,DIVL,DNEG,DXCH,CMP ,ADD ,SUB ,MPY ,DIV ,NEG ,TEST,STBX,DTST,DFLT,BTST,XCH ,INCA,DECA,XAX ,ADAX,ADXA,DEL ,ZROB,LDXB,STAX,LDXA,DUP ,DDUP,FLT ,FCMP,FADD,FSUB,FMPY,FDIV,FNEG,CAB ,LCMP,LADD,LSUB,LMPY,LDIV,NOT ,OR ,XOR ,AND ,FIXR,FIXT, ,INCB,DECB,XBX ,ADBX,ADXB,"; fprintf (ofile, "%.*s,%.*s", strcspn (cpu_stack_ops + high_op * 5, " ,"), cpu_stack_ops + high_op * 5, strcspn (cpu_stack_ops + low_op * 5, " ,"), cpu_stack_ops + low_op * 5); static char *cpu_stack_ops [] = { "NOP", "DELB", "DDEL", "ZROX", "INCX", "DECX", "ZERO", "DZRO", "DCMP", "DADD", "DSUB", "MPYL", "DIVL", "DNEG", "DXCH", "CMP", "ADD", "SUB", "MPY", "DIV", "NEG", "TEST", "STBX", "DTST", "DFLT", "BTST", "XCH", "INCA", "DECA", "XAX", "ADAX", "ADXA", "DEL", "ZROB", "LDXB", "STAX", "LDXA", "DUP", "DDUP", "FLT", "FCMP", "FADD", "FSUB", "FMPY", "FDIV", "FNEG", "CAB", "LCMP", "LADD", "LSUB", "LMPY", "LDIV", "NOT", "OR", "XOR", "AND", "FIXR", "FIXT", "", "INCB", "DECB", "XBX", "ADBX", "ADXB" }; switch (operand & ~FLAGS_MASK) { case opNone: case opU15: case opU1515: case opU63: case opU255: case opP31: case opP63: case opP255: case opSdec3: case opSdec7: case opMDall: case opMDdata: typedef struct { uint32 mask; char *format; } OP_FORMAT; static const OP_FORMAT operand [] = { { 0177777, "" }, /* opNone */ { 0177760, " %d" }, /* opU15 */ { 0177400, " %d:%d" }, /* opU1515 */ { 0177700, " %d" }, /* opU63 */ { 0177400, " %d" }, /* opU255 */ { 0177700, " P%+d" }, /* opP31 */ { 0177600, " P%+d" }, /* opP63 */ { 0177000, " P%+d" }, /* opP255 */ // { 0177777, "" }, /* opPLN255 */ // { 0177777, "" }, /* opPN255 */ // { 0177777, "" }, /* opPP255 */ { 0177774, " %d" }, /* opSdec3 */ { 0177770, " %d" }, /* opSdec7 */ // { 0177400, "" }, /* opREGS8 */ { 0176000, "" }, /* opMDall */ { 0177000, "" } /* opMDdata */ }; // { 0173777, /* opIndirect4 */ // { 0175777, /* opIndirect5 */ // { 0173777, /* opIndex4 */ // { 0177757 /* opBank11 */ /* register bits for PSHR, SETR (bit 8..bit15) */ //static char reg_bits [] = "SB,DB,DL,Z ,ST,X ,Q ,S ,"; /* subopcode %05 & bit 6 = 0, index = 4:5 */ //static char loop_ops [] = "TBA ,MTBA,TBX ,MTBX,"; /* subopcode %14 and bits 5:6 = 01, index = 7:9 */ //static char bcc_ops [] = "BNOP,BL ,BE ,BLE ,BG ,BNE ,BGE ,BALL,"; /* subopcodes %04..%17, index = 0:3 & 6 */ //static char mem_ops [] = "LOAD,LOAD,----,STOR,CMPM,CMPM,ADDM,ADDM,SUBM,SUBM,MPYM,MPYM,INCM,DECM,LDX ,LDX ,BR ,BR ,LDB ,LDD ,STB ,STD ,LRA ,LRA ,"; ------------- Jump: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- B IOAW read -- C IOPP write ACKSR | DEVNODB Conditional Jump: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- B IOAW read SETJMP C ~ JMPMET IOPP read ACKSR | DEVNODB C JMPMET IOPP write ACKSR | DEVNODB Return Residue: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- B IOAW write -- C IOPP read ACKSR | DEVNODB Set Bank: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- D IOAW read -- C IOPP read ACKSR | DEVNODB Interrupt: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- B IOAW read SETINT C IOPP read ACKSR | DEVNODB End: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- B IOAW write TOGGLESIOOK | TOGGLESR | PSTATSTB idle End with Interrupt: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- B IOAW write TOGGLESIOOK | TOGGLESR | PSTATSTB | SETINT idle Control: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read PCMD1 | TOGGLESR B IOAW read ACKSR | PCONTSTB C IOPP read ACKSR | DEVNODB | TOGGLESR Sense: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- B IOAW write PSTATSTB C IOPP read ACKSR | DEVNODB Write: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- B ~ DC IOAW read TOGGLESR | TOGGLEOUTXFER B DC IOAW read TOGGLESR / D ~ TC * ~DEVEND data read ACKSR | PWRITESTB | D TC * ~DEVEND data read ACKSR | PWRITESTB | EOT | TOGGLEOUTXFER \ D DEVEND IOPP read ACKSR | EOT | TOGGLESR | TOGGLEOUTXFER / C ~DEVEND IOPP read ACKSR | DEVNODB | TOGGLESR \ A DEVEND IOCW read -- Write Chained: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- B ~ DC IOAW read TOGGLESR | TOGGLEOUTXFER ) B DC IOAW read TOGGLESR / D ~ TC * ~DEVEND data read ACKSR | PWRITESTB | D TC * ~DEVEND data read ACKSR | PWRITESTB | EOT | TOGGLESR \ D DEVEND IOPP read ACKSR | EOT | TOGGLESR / C ~DEVEND IOPP read ACKSR | DEVNODB \ A DEVEND IOCW read -- Read: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- B ~ DC IOAW read READNEXTWD | TOGGLESR | TOGGLEINXFER B DC IOAW read READNEXTWD | TOGGLESR / D ~ TC * ~DEVEND data write ACKSR | PREADSTB | READNEXTWD | D TC * ~DEVEND data write ACKSR | PREADSTB | EOT | TOGGLEINXFER \ D DEVEND IOPP read ACKSR | EOT | TOGGLESR | TOGGLEINXFER / C ~DEVEND IOPP read ACKSR | DEVNODB | TOGGLESR \ A DEVEND IOCW read -- Read Chained: State Condition Action Signals ----- -------------- ---------- ------------------------------------------ C IOPP read ACKSR | DEVNODB A IOCW read -- B ~ DC IOAW read READNEXTWD | TOGGLESR | TOGGLEINXFER B DC IOAW read READNEXTWD | TOGGLESR / D ~ TC * ~DEVEND data write ACKSR | PREADSTB | READNEXTWD | D TC * ~DEVEND data write ACKSR | PREADSTB | EOT | TOGGLESR \ D DEVEND IOPP read ACKSR | EOT | TOGGLESR / C ~DEVEND IOPP read ACKSR | DEVNODB \ A DEVEND IOCW read -- HP_TAPELIB ------------------------------------- original ms_service: switch ((CNTLR_PHASE) uptr->PHASE) { /* dispatch the phase */ case Start_Phase: cvptr->status |= ML_IFBUSY; /* set interface busy */ dprintf (ms_dev, DEB_CMDS, "Unit %d %s tape command started at position %d\n", unit, ml_opcode_name (uptr->OP), uptr->pos); switch (opcode) { /* dispatch the current operation */ case Read_Record: ms_cntlr.index = 0; result = sim_tape_rdrecf (uptr, buffer, &ms_cntlr.length, ML_BUFSIZE); /* read record */ dprintf (ms_dev, DEB_RWS, "Unit %d read %d-word record\n", unit, ms_cntlr.length / 2); if (result == MTSE_OK) /* read OK? */ result = SCPE_OK; /* convert to to SCP status and continue */ else if (result == MTSE_RECE) /* record in error? */ cvptr->status |= ML_DATAERR; /* set data error status and continue */ else { /* other errors inhibit data transfer */ result = map_status (cvptr, uptr, result); /* map status */ if (result == SCPE_OK) { /* recoverable? */ uptr->PHASE = End_Phase; /* set completion */ uptr->wait = cvptr->irg_time; /* sched IRG */ } break; } if (ms_cntlr.length & 1) /* if record length was odd */ cvptr->status |= ML_ODDLEN; /* then set the corresponding status */ else /* else clear status*/ cvptr->status &= ~ML_ODDLEN; /* for an even length */ uptr->PHASE = Data_Phase; /* set data */ uptr->wait = cvptr->data_time; /* sched data */ break; case Backspace_Record: case Forward_Space_Record: if (opcode == Backspace_Record) result = sim_tape_sprecr (uptr, &ms_cntlr.length); /* space rec rev */ else result = sim_tape_sprecf (uptr, &ms_cntlr.length); /* space rec fwd */ if (result == MTSE_OK) /* read OK? */ result = SCPE_OK; /* convert to to SCP status and continue */ else { /* other errors inhibit data transfer */ result = map_status (cvptr, uptr, result); /* map status */ if (result == SCPE_OK) { /* recoverable? */ uptr->PHASE = End_Phase; /* set completion */ uptr->wait = cvptr->irg_time; /* sched IRG */ } break; } if (ms_cntlr.length & 1) /* if record length was odd */ cvptr->status |= ML_ODDLEN; /* then set the corresponding status */ else /* else clear status*/ cvptr->status &= ~ML_ODDLEN; /* for an even length */ uptr->PHASE = End_Phase; /* set end */ // actually, time should be (ML_DATA_TIME * ms_cntlr.length / 2) + ML_IRG_TIME uptr->wait = cvptr->data_time; /* sched data */ break; case Write_Record: break; case Write_Gap: break; // rew/rwo: start rewind, set DSR, sched end; if RWO, set offline case Rewind_Offline: uptr->flags |= UNIT_OFFLINE; /* set offline */ /* fall into Rewind */ case Rewind: if (sim_tape_bot (uptr)) /* at BOT? */ uptr->wait = cvptr->irg_time; // short time else { uptr->STAT |= ML_REWIND; /* set rewinding */ uptr->wait = cvptr->rew_time; // fcn of pos? } uptr->PHASE = End_Phase; /* set end */ device_sr = SET; /* request service */ break; case Backspace_File: case Forward_Space_File: do { if (opcode == Backspace_Record) result = sim_tape_sprecr (uptr, &ms_cntlr.length); /* space rec rev */ else result = sim_tape_sprecf (uptr, &ms_cntlr.length); /* space rec fwd */ if (sim_tape_eot (uptr)) /* EOT stops */ break; } while (result == MTSE_OK); if (result == MTSE_OK) /* spacing OK? */ result = SCPE_OK; /* convert to to SCP status and continue */ else { /* other errors inhibit data transfer */ result = map_status (cvptr, uptr, result); /* map status */ if (result == SCPE_OK) { /* recoverable? */ uptr->PHASE = End_Phase; /* set completion */ uptr->wait = cvptr->irg_time; /* sched IRG */ } break; } uptr->PHASE = End_Phase; /* set end */ // actually, time should be (ML_DATA_TIME * ms_cntlr.length / 2) + ML_IRG_TIME uptr->wait = cvptr->irg_time; /* sched IRG */ break; case Write_Record_Without_Parity: break; case Write_File_Mark: break; case Read_Record_With_CRCC: break; default: /* we were entered with an invalid state */ result = SCPE_IERR; /* return an internal (programming) error */ break; } /* end of operation dispatch */ break; /* end of start phase handlers */ case End_Phase: switch (opcode) { /* dispatch the operation command */ case Rewind: case Rewind_Offline: sim_tape_rewind (uptr); /* rewind tape */ uptr->STAT = ~ML_REWIND; /* clear rewinding status */ break; /* do not issue SR; already done at start */ case Forward_Space_Record: case Read_Record: device_sr = SET; /* request service */ break; default: /* we were entered with an invalid state */ result = SCPE_IERR; /* return an internal (programming) error */ break; } /* end of operation dispatch */ cvptr->status &= ~ML_IFBUSY; /* set interface not busy */ break; /* end of end phase handlers */ } /* end of phase dispatch */ ------------- typedef struct { DENSITY_TYPE densities; /* supported densities */ int32 rew_time; /* rewind initiation time */ int32 bot_time; /* beginning of tape start delay time */ int32 move_time; /* motion command start delay time */ int32 irg_time; /* interrecord gap traversal time */ int32 data_time; /* data transfer delay time */ } DRIVE_PROPERTIES; static const DRIVE_PROPERTIES drive_props [DRIVE_TYPE_SIZE] = { /* supported rwnd BOT motion IRG data */ /* densities init start start travrs xfer */ /* ------------------- ---- ------ ------ ------ ---- */ { DEN_800, 878, 161512, 14044, 24885, 88 }, /* HP 7970B 800 bpi NRZI */ { DEN_1600, 878, 252800, 17556, 27387, 44 }, /* HP 7970E 1600 bpi PE */ { DEN_800 | DEN_1600, 0, 0, 0, 0, 0 }, /* HP 7974 800/1600 bpi NRZI/PE */ { DEN_1600 | DEN_6250, 0, 0, 0, 0, 0 } /* HP 7976 1600/6250 bpi PE/GCR */ }; HP_DISCLIB --------------------------------------------------------------- /* Per-unit STAT accessors. The STAT field of the unit structure contains a drive's status (part of the controller's Status-2 value) and an index into the properties table for the drive, as follows: bits 15- 0 = drive status bits 23-16 = drive properties array index */ #define STATUS_2_WIDTH 16 /* drive status 2 */ #define PROP_INDEX_WIDTH 8 /* drive properties array index */ #define STATUS_2_SHIFT 0 /* bits 15- 0 */ #define PROP_INDEX_SHIFT 16 /* bits 23-16 */ #define STATUS_2_MASK ((1 << STATUS_2_WIDTH) - 1 << STATUS_2_SHIFT) #define PROP_INDEX_MASK ((1 << PROP_INDEX_WIDTH) - 1 << PROP_INDEX_SHIFT) #define STATUS_2(u) (((u)->STAT & STATUS_2_MASK) >> STATUS_2_SHIFT) #define PROP_INDEX(u) (((u)->STAT & PROP_INDEX_MASK) >> PROP_INDEX_SHIFT) The library provides a unit service routine to handle all of the disc commands. The routine is called from the interface service routine to handle the common disc actions, while the interface routine handles actions specific to the operation of the interface (such as data transfer). The service routine schedules the unit to continue command execution under these conditions: 1. A Seek or Recalibrate command is waiting for the seek completion. 2. A read or write command is waiting for the first data transfer of a sector to start. 3. A read or write command is waiting for the next sector to start after the final data transfer of the preceding sector. 4. A Verify command is waiting for the end of the current sector. The library also provides controller and timer service routines for MAC emulations. All three (unit, controller, and timer) must be called from their respective interface service routines before any interface-specific actions, if any, are taken. On return from the library unit or controller service routines, the "wait" field of the UNIT structure will be set to the activation time if the unit is to be scheduled. The caller is responsible for activating the unit. If the caller uses this feature, the field should be reset to zero before the next service call. The MAC timer unit is activated by the library, and its "wait" field is not used. The timer starts when a command other than End, Seek, or Recalibrate completes, or when the controller is waiting for the interface to supply or accept a parameter during command execution. It stops when an End, Seek, or Recalibrate command completes, a command is prepared for execution, or the final parameter has been supplied or accepted by the interface during command execution. The controller maintains six variables in each drive's unit structure: wait -- the current service activation time pos -- the current byte offset into the disc image file u3 (CYL) -- the current drive cylinder u4 (STAT) -- the drive status (Status-2) u5 (OP) -- the drive operation in process u6 (PHASE) -- the current operation phase These and other definitions are in the file hp_disclib.h, which must be included in the interface simulator. /* Classify the current controller opcode. The controller opcode is classified as a read, write, control, or status command, and the classification is returned to the caller. If the opcode is illegal or undefined for the indicated controller, the classification is marked as invalid. */ CNTLR_CLASS dl_classify (CNTLR_VARS cntlr) { if (cntlr.type <= LAST_TYPE /* if the controller type is legal */ && cntlr.opcode <= LAST_OPCODE /* and the opcode is legal */ && cmd_props [cntlr.opcode].valid [cntlr.type]) /* and is defined for this controller, */ return cmd_props [cntlr.opcode].classification; /* then return the command classification */ else /* the type or opcode is illegal */ return Class_Invalid; /* so return an invalid classification */ } /* Return the name of a command phase. A string representing the supplied phase is returned to the caller. If the phase is illegal, the string "invalid" is returned. */ const char *dl_phase_name (CNTLR_PHASE phase) { if (phase <= Last_Phase) /* if the phase is legal, */ return phase_name [phase]; /* return the phase name */ else /* the phase is illegal, */ return invalid_name; /* so return an error indication */ } intro: Autosizing is supported when attaching a disc image. If enabled, the model of the drive is set to match the disc image size. For example, if a 50 MB disc image is attached to a unit set for autosizing, the unit's model will be set to a 7920(H). The controller library supports four different disc drive models with these properties: Drive Model Drive Megabytes Model ID Type per Drive ----- ----- ----- --------- 7905 0 2 15 7906 1 0 20 7920 2 1 50 7925 3 3 120 Model IDs are used in the unit flags to identify the unit's model. For the autosizing feature to work, models must be assigned ascending IDs in order of ascending drive sizes. dl_attach: If the drive is set to autosize, the size of the image file is compared to the table of drive capacities to determine which model of drive was used to create it. If the image file is new, then the previous drive model is retained. uint32 id, size; if (uptr->flags & UNIT_AUTO) { /* is autosizing enabled? */ size = sim_fsize (uptr->fileref) / sizeof (uint16); /* get the file size in words */ if (size > 0) /* a new file retains the current drive model */ for (id = 0; id < PROPS_COUNT; id++) /* find the best fit to the drive models */ if (size <= drive_props [id].words /* if the file size fits the drive capacity */ || id == PROPS_COUNT - 1) { /* or this is the largest available drive */ uptr->capac = drive_props [id].words; /* then set the capacity */ uptr->flags = (uptr->flags & ~UNIT_MODEL) /* and the model */ | SET_MODEL (id); break; } } dl_set_model: if (value != UNIT_AUTO) /* if we are not autosizing */ uptr->capac = drive_props [GET_MODEL (value)].words; /* set the capacity to the new value */ --------------------------------------------------------------- 2. The clock unit is not declared with UNIT_IDLE, as it is unnecessary. It will always appear in the event queue behind the process clock, which does have UNIT_IDLE, and sim_idle only checks the head of the queue to decide whether idling is permitted.