/***************************************************************************** * Change Log * Date | Change *-----------+----------------------------------------------------------------- * 16-Nov-85 | [1.51] Created * 24-Nov-85 | [1.105] Added display-char mode * 25-Nov-85 | [1.135] Modified to allow single-cycle execution of instructions * 11-Dec-85 | [1.165] Use ~, not !, for 'not' (bitwise, not logical) * 12-Dec-85 | [1.171] Implemented adr_stop mode; just like single step * | except for checking of address * 12-Dec-85 | [1.175] Removed length display to length.c * 12-Dec-85 | [1.179] Clear instruction length light error * 12-Dec-85 | [1.179] Set and clear address light as appropriate * 13-Dec-85 | [1.181] Added bad_address_r for MRCM check * 14-Dec-85 | [1.191] in diagnostic output, print d-char using bcd_to_ascii * 15-Dec-85 | [1.199] Added logic state L, call display of it in display_state * 15-Dec-85 | [1.201] Added compare_state * 17-Dec-85 | [1.212] Added overflow indicator * 20-Dec-85 | [1.221] set_X => show_X for various X * 24-Dec-85 | [1.231] Include select.h for selective trace option; invoke * | selective trace if required. * 27-Dec-85 | [1.254] print out d_char for instrs of length 2 * 29-Dec-85 | [1.273] Always set old_diagnostics because we always reset it! * 30-Dec-85 | [1.283] After char_display mode, retain display of character * 30-Dec-85 | [1.283] Added detection for slow execution mode, partial panel * | display update * 24-Jan-86 | [1.314] check_left_mouse_button => check_mouse_halt * 25-Jan-86 | [1.347] Implemented alter-plus-one * 25-Feb-86 | [1.378] Include compare indicators in slow display * 25-Feb-86 | [1.379] include<> => include "" * 25-Feb-86 | [1.381] Call x_show instead of x_push in partial display mode * 25-Feb-86 | [1.382] "partial display" now displays everything... * 25-Feb-86 | [1.382] include logic.h and initialize compare indicator to * | B=A * 25-Feb-86 | [1.384] Do not clear I_cycle until partial display is done * 29-Jul-86 | [1.385] Added lamp_test mode * 30-Jul-86 | [1.402] Do not set I_cycle to 0 unless setting fetch_complete * | false (in reverting from single cycle mode to I/EX or RUN * | modes). Added additional diagnostics about mode reversion * 30-Jul-86 | [1.403] If diagnostics on, poll at the end of each interpreter * | cycle. * 30-Jul-86 | [1.404] Before reading card on machine load, zero out locations * | 1..80 * 31-Jul-86 | [1.405] Made char variables unsigned * 31-Jul-86 | [1.406] Changed _push calls to internal calls so internal * | state changes don't appear in CE log * 31-Jul-86 | [1.409] In addr stop mode, set run to execute result * 6-Aug-86 | [1.410] Include bcd.h * 6-Aug-86 | [1.410] bcd_to_ASCII -> bcd_to_ascii * 6-Aug-86 | [1.410] 'opcode' in 'execute' is now unsigned char * 18-Nov-91 | [1.428] memory.h => mem1401.h, avoid ANSI name * 23-Dec-91 | [1.519] added 'zip' test to suppress all active * | displays at full execute speed * 23-Dec-91 | [1.524] turn off stack checking on bad_address * 22-Dec-94 | [1.600J] JRJ initialize io data state (maybe move this later) *****************************************************************************/ /***************************************************************************** 1401 Emulator Machine Interface This module interprets control panel actions *****************************************************************************/ #include "stdio.h" #include "boolean.h" #include "btypes.h" #include "scdspmsg.h" #include "modes.h" #include "alerts.h" #include "button.h" #include "mem1401.h" #include "mach.h" #include "panel.h" #include "dispatch.h" #include "length.h" #include "enter.h" #include "ifetch.h" #include "alert.h" #include "diag.h" #include "select.h" #include "logic.h" #include "bcd.h" #include "ifetch.h" #include "data.h" #include "kb.h" #include "scan.h" #include "addr.h" #include "poll.h" #include "cdr.h" #include "pr.h" #include "print.h" #include "printers.h" #include "io.h" extern button A_button; extern button B_button; extern button I_button; extern void set_wm(); extern char * mode_decode(); extern char * single_cycle_decode(); extern boolean slow; extern boolean zip; /***************************************************************************** 1401 MACHINE STATE *****************************************************************************/ static boolean run; int A_addr; /* A-address register */ int B_addr; /* B-address register */ int I_addr; /* I-address register */ int NI_addr; /* Next-I-address register (hidden register) */ int I_cycle = 0; /* Instruction cycle counter */ unsigned char OP = 0; /* opcode */ unsigned char B = 0; /* B -data register */ unsigned char A = 0; /* A -data register */ unsigned char d_char; /* d-character modified */ unsigned char L = 0; /* Logic output */ extern int current_mode; /* MODE switch setting */ int cycle; /* Current type of cycle: cycle_I, cycle_A, cycle_B (controls which light will be displayed) */ unsigned char B_char_displayed; /* Last char displayed by char_disp mode */ int single_cycle_state; boolean reset_mandatory = false; /* if true, nothing can happen */ boolean fetch_complete = false; int compare_state = logic_BEQA; boolean overflow=false; /* overflow indicator */ int cycle_count = 0; /* used to update display every cycle_mod instructions */ boolean lamp_test = false; /* lamp test mode */ boolean io_index_inhibit = false; int io_device = 0; int io_unit = 0; #define cycle_mod 255 /* power of 2 minus 1 */ /**************************************************************************** * partial_display * Effect: * Displays a subset of the machine state; slows down execution but * adds corroborative detail to an otherwise bald and unconvincing * simulation ****************************************************************************/ void partial_display() { show_O(OP); show_A(A); show_B(B); show_compare(); show_ovf(overflow); show_logic_output(L); show_length(I_cycle); switch(cycle) { /* cycle display */ case cycle_none: break; case cycle_I: i_show(); break; case cycle_A: a_show(); break; case cycle_B: b_show(); break; default: i_show(); break; } /* cycle display */ } /**************************************************************************** * display_state * Effect: * Displays machine state ****************************************************************************/ void display_state() { show_A(A); show_O(OP); show_B(B_char_displayed); show_logic_output(L); show_ovf(overflow); show_compare(); switch(cycle) { /* cycle display */ case cycle_none: break; case cycle_I: i_show(); break; case cycle_A: a_show(); break; case cycle_B: b_show(); break; } /* cycle display */ show_length(I_cycle); } /**************************************************************************** * stop * Effect: * Handles push of STOP button ****************************************************************************/ void stop() { run = false; } /**************************************************************************** * start * Effect: * Handles all the cases of the 'start' button ****************************************************************************/ #pragma optimize("t",on) void start() { int stop_addr; /* stop address for adr stop mode */ run = true; if(reset_mandatory) { /* do nothing */ wait_for_mouse_up(); stop_execute(); return; } /* do nothing */ show_mouse_cursor(); stop_addr = get_address_switches(); /* in case adr stop mode */ while(run) { /* Run Loop */ boolean trun; if(check_mouse_halt()) break; if(reset_mandatory) { /* hit error */ break; } /* hit error */ switch(current_mode) { /* mode decode */ /* run */ case mode_run: cancel_alter_plus_one(); if(fetch_complete) { /* do it */ switch(single_cycle_state) { /* state decode */ case single_cycle_start: single_cycle_state = single_cycle_run; /* no break */ case single_cycle_run: trun = execute(); fetch_complete = false; break; default: /* We have switched state from sing cyc to run in the middle of executing an instruction. We have to complete this instruction in single-cycle mode before we can let the state revert to a run state */ if(diagnostics_on) { /* tell */ sprintf(diag_buffer,"**Switched to RUN from sing_cyc"); tell(diag_buffer); } /* tell */ while(trun = execute()) { /* execution continues */ if(check_mouse_halt()) { /* quit */ wait_for_mouse_up(); break; } /* quit */ if(single_cycle_state == single_cycle_complete) { /* done */ single_cycle_state = single_cycle_run; if(diagnostics_on) { /* resume */ sprintf(diag_buffer,"**execution stablized, reverting to RUN"); tell(diag_buffer); } /* resume */ break; } /* done */ } /* execution continues */ } /* state decode */ run &= trun; B_char_displayed = B; if(!zip && (slow || ((cycle_count++ & cycle_mod) == 0)) ) partial_display(); /* must do partial_display before I_cycle cleared */ I_cycle = 0; } /* do it */ else { /* fetch it */ I_addr = NI_addr; fetch_complete = ifetch(); single_cycle_state = single_cycle_run; NI_addr = I_addr; B_char_displayed = B; if(slow) partial_display(); } /* fetch it */ break; /*--------------------------------------------------------------------------*/ /* I/EX */ case mode_i_ex: cancel_alter_plus_one(); if(fetch_complete) { /* execute /EX */ switch(single_cycle_state) { /* state decode */ case single_cycle_start: single_cycle_state = single_cycle_run; /* no break */ case single_cycle_run: fetch_complete = false; trun = execute(); break; default: /* We have switched state from sing cyc to I/EX in the middle of executing an instruction. We have to complete this instruction in single-cycle mode before we can let the state revert to a run state */ if(diagnostics_on) { /* tell */ sprintf(diag_buffer,"**Switched to I/EX from sing_cyc"); tell(diag_buffer); } /* tell */ while(trun = execute()) { /* execution continues */ if(check_mouse_halt()) { /* quit */ wait_for_mouse_up(); break; } /* quit */ if(single_cycle_state == single_cycle_complete) { /* done */ single_cycle_state = single_cycle_run; if(diagnostics_on) { /* resume */ sprintf(diag_buffer,"**execution stablized, reverting to I/EX"); tell(diag_buffer); } /* resume */ break; } /* done */ } /* execution continues */ break; } /* state decode */ run &= trun; if(single_cycle_state == single_cycle_complete) { /* done with instr */ I_cycle = 0; fetch_complete = false; } /* done with instr */ B_char_displayed = B; } /* execute /EX */ else { /* execute I/ */ I_addr = NI_addr; fetch_complete = ifetch(); /* We set this state here in case the user switches modes to sing/cyc after the I/EX fetch has completed; in this case the state will be right for the first execute cycle under single cycle mode. If we continue in I/EX mode, the state will be set to 'single_cycle_run' just before we call execution */ single_cycle_state = single_cycle_start; NI_addr = I_addr; B_char_displayed = B; } /* execute I/ */ run = false; break; /*--------------------------------------------------------------------------*/ /* adr_stop */ case mode_adr_stop: cancel_alter_plus_one(); if(fetch_complete) { if(B_addr == stop_addr) { /* B-stop */ if(diagnostics_on) tell("B-ADDRESS STOP!"); run = false; } /* B-stop */ if(A_addr == stop_addr) { /* A-stop */ if(diagnostics_on) tell("A-ADDRESS STOP!"); run = false; } /* A-stop */ run = execute(); B_char_displayed = B; if(single_cycle_state==single_cycle_complete) { /* done */ fetch_complete = false; I_cycle = 0; } /* done */ } else { I_addr = NI_addr; if(I_addr == stop_addr) { /* stop */ if(diagnostics_on) tell("I-ADDRESS STOP!"); run = false; } /* stop */ fetch_complete = ifetch(); NI_addr = I_addr; B_char_displayed = B; single_cycle_state = single_cycle_start; } break; /*--------------------------------------------------------------------------*/ /* sing cyc */ case mode_sing_cyc: cancel_alter_plus_one(); if(fetch_complete) { execute(); B_char_displayed = B; if(single_cycle_state==single_cycle_complete) { /* done */ fetch_complete = false; I_cycle = 0; } /* done */ } else { I_addr = NI_addr; fetch_complete = ifetch(); NI_addr = I_addr; B_char_displayed = B; single_cycle_state = single_cycle_start; } run = false; break; /*--------------------------------------------------------------------------*/ /* stg prt */ case mode_stg_prt: cancel_alter_plus_one(); if(print_storage(current_printer)) { /* print OK */ wait_for_mouse_up(); stop_execute(); } /* print OK */ else { /* print failed */ alert(alert_printer); wait_for_mouse_up(); stop_execute(); } /* print failed */ run = false; break; /*--------------------------------------------------------------------------*/ /* char disp */ case mode_char_dsp: { int addr; cancel_alter_plus_one(); addr = get_address_switches(); B_char_displayed = memory[addr]; show_B(B_char_displayed); display_address(addr); if(diagnostics_on) { /* log it */ sprintf(diag_buffer,"Location %d, contents '%c'", addr,bcd_to_ascii(B_char_displayed)); tell(diag_buffer); } /* log it */ wait_for_mouse_up(); stop_execute(); run = false; break; } /*--------------------------------------------------------------------------*/ /* alter */ case mode_alter: { int addr; /* We have hit 'start' so cancel the alter-plus-one mode (it may be reset as a consequence of this */ cancel_alter_plus_one(); /* update a machine register. The contents of the address switches is stored in the register (A,B,I) whose light is on */ addr = get_address_switches(); if(A_button.active) { /* store A */ A_addr = addr; cycle = cycle_A; } /* store A */ else if(B_button.active) { /* store B */ B_addr = addr; cycle = cycle_B; /* See if we are about to go into alter-plus-one mode */ check_alter_plus_one(); } /* store B */ else if(I_button.active) { /* store I */ NI_addr = I_addr = addr; cycle = cycle_I; } /* store I */ else { /* no reg selected */ wait_for_mouse_up(); run = false; break; } /* no reg selected */ /* if we get here, one of the buttons was active */ display_address(addr); wait_for_mouse_up(); stop_execute(); } run = false; break; } /* mode decode */ if(diagnostics_on) { /* talk to user */ if(!slow) partial_display(); /* test because may have already done it above; save time */ poll(); } /* talk to user */ } /* Run Loop */ hide_mouse_cursor(); stop_execute(); display_state(); } #pragma optimize("t",off) /**************************************************************************** * load_machine * Effect: * If an execution mode is set on the console, initiate program load * and start the machine ****************************************************************************/ void load_machine() { int i; switch(current_mode) { case mode_alter: case mode_stg_prt: case mode_char_dsp: /* don't do anything in these modes */ return; } for(i=1;i<=80;i++) memory[i] = '\0'; if(read_card()) { /* card came in */ NI_addr = I_addr = 1; set_wm(1); start_execute(); } /* card came in */ else { /* reader wedged */ alert(alert_reader); return; } /* reader wedged */ } /**************************************************************************** * require_reset * Effect: * Forbids further progress until reset is hit ****************************************************************************/ void require_reset() { reset_mandatory = true; } /**************************************************************************** * do_reset * Effect: * Resets the machine (start-reset) ****************************************************************************/ void do_reset() { reset_alerts(); reset_mandatory = false; I_cycle = 0; show_length(I_cycle); fetch_complete = false; light_O(false); /* turn off bad opcode light if on */ clear_inst_length(); clear_address_light(); } /**************************************************************************** * set_wm * Inputs: * short addr: Address to set word mark * Effect: * Sets a word mark in the indicated position ****************************************************************************/ void set_wm(short addr) { memory[addr] |= word_mark; } /**************************************************************************** * clear_wm * Inputs: * int addr: address of where to clear word mark * Effect: * clears the word mark at the indicated location ****************************************************************************/ void clear_wm(short addr) { memory[addr] &= (~word_mark); } /**************************************************************************** * execute * Result: boolean * true if run should continue * false if run should halt * Effect: * executes the instruction ****************************************************************************/ #pragma check_stack(off) boolean execute() { unsigned char opcode = BA8421(OP); boolean result; boolean old_diagnostics; if(instructions[opcode] == NULL) { /* no dispatch address */ inst_illegal(); return false; } /* no dispatch address */ old_diagnostics = diagnostics_on; if(diagnostics_on) { /* trace? */ if(selective_on) diagnostics_on = select_trace[opcode]; } /* trace? */ if(diagnostics_on) { /* tell user */ sprintf(diag_buffer,"OP = %d ('%c'), instructions[op] = 0x%x, SSS= %s, mode = %s", opcode, bcd_to_ascii(opcode), instructions[opcode], single_cycle_decode(), mode_decode()); tell(diag_buffer); } /* tell user */ result = (*instructions[opcode])(); diagnostics_on = old_diagnostics; return result; } #pragma check_stack(on) /**************************************************************************** * inst_illegal * Result: boolean * false, always * Effect: * Marks instruction as illegal ****************************************************************************/ boolean inst_illegal() { light_O(true); alert(alert_process); return false; } /**************************************************************************** * single_cycle_decode * Result: char * * print string of microstate ****************************************************************************/ char * single_cycle_decode() { static char ssd[80]; int micro; int macro; char * instr; switch(single_cycle_state) { /* state decode */ case single_cycle_run: return "run"; case single_cycle_start: return "start"; case single_cycle_complete: return "complete"; } /* state decode */ micro = microstate(single_cycle_state); macro = macrostate(single_cycle_state); instr = opcodes[macro]; switch(micro) { /* microdecode */ case A_complete: sprintf(ssd,"%d:%s:A_complete",macro,instr); break; case A_s_complete: sprintf(ssd,"%d:%s:A_s_complete",macro,instr); break; case A_f_complete: sprintf(ssd,"%d:%s:A_f_complete",macro,instr); break; case B_complete: sprintf(ssd,"%d:%s:B_complete",macro,instr); break; case B_f_complete: sprintf(ssd,"%d:%s:B_f_complete",macro,instr); break; case B_s_complete: sprintf(ssd,"%d:%s:B_s_complete",macro,instr); break; default: sprintf(ssd,"%d:%s:%d",macro,instr,micro); break; } /* microdecode */ return ssd; } /**************************************************************************** * tell_new_state * Effect: * Displays help message about processor state ****************************************************************************/ void diag_state() { char * ustate; ustate = single_cycle_decode(); sprintf(diag_buffer,"after %s: I => %d, NI => %d, A => %d, B => %d, SSS = %s, mode = %s", opcodes[BA8421(OP)], I_addr, NI_addr, A_addr, B_addr, ustate, mode_decode()); tell(diag_buffer); } /**************************************************************************** * diag_op * (tell_op) * Inputs: * int mask: Mask of fields to print * Effect: * Prints out state for instructions as shown below * * length op_A op_B op_d * 1 [A] [B] [d] * 2 - - d * 4 A [B] - * 5 A [B] d * 7 A B [d] * 8 A B d ****************************************************************************/ void diag_op(short mask) { char msg[80]; char * p; p = msg; p += sprintf(p,"before %d: %s ", I_addr - I_cycle, opcodes[BA8421(OP)]); if(mask & op_A) switch(I_cycle) { /* A-decode */ case 1: p += sprintf(p," [%d]",A_addr); break; case 4: case 5: case 7: case 8: p += sprintf(p," %d",A_addr); break; } /* A-decode */ if(mask & op_B) switch(I_cycle) { /* B-decode */ case 1: case 4: p += sprintf(p,", [%d]",B_addr); break; case 7: case 8: p += sprintf(p,", %d",B_addr); break; } /* B-decode */ if(mask & op_d) switch(I_cycle) { /* d-decode */ case 1: p += sprintf(p,", [%c]",bcd_to_ascii(d_char)); break; case 2: p += sprintf(p,"%c",bcd_to_ascii(d_char)); break; case 5: case 8: p += sprintf(p,", %c",bcd_to_ascii(d_char)); break; } /* d-decode */ p += sprintf(p,"; SSS= %s, mode = %s ", single_cycle_decode(), mode_decode()); tell(msg); } /**************************************************************************** * bad_address * Inputs: * short addr: Address * Result: boolean * true if address is illegal (0 or not physical memory) * Effect: * Sets alert condition for address bad ****************************************************************************/ #pragma check_stack(off) boolean bad_address(short addr) { if(ValidNonZero(addr)) return false; alert(alert_process); set_address_light(); return true; } #pragma check_stack(on) /**************************************************************************** * bad_address_r * Inputs: * short addr: Address * Result: boolean * true if address is illegal moving right (15999 or not physical memory) * Effect: * Sets alert condition for address bad ****************************************************************************/ boolean bad_address_r(short addr) { if(addr < 15999) return false; alert(alert_process); set_address_light(); return true; } /**************************************************************************** * mode_decode * Result: char * * Current mode ****************************************************************************/ char * mode_decode() { switch(current_mode) { /* mode decode */ case mode_alter: return "alter"; case mode_stg_prt: return "str prnt"; case mode_char_dsp: return "char disp"; case mode_run: return "run"; case mode_adr_stop: return "adr stop"; case mode_i_ex: return "I/EX"; case mode_sing_cyc: return "sing cyc"; } /* mode decode */ return "??"; } /**************************************************************************** * bad_d * Inputs: * char * msg: Message to issue * unsigned char d: bcd d-character * Effect: * Issues diagnostic message ****************************************************************************/ void bad_d(char * msg, unsigned char d) { if(!diagnostics_on) return; sprintf(diag_buffer,"Illegal d-character [%s]: '%c' (%d)", msg,bcd_to_ascii(d),d); tell(diag_buffer); }